diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f87996cdcb..a08542df25 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -30,11 +30,11 @@ Please make sure your contributions adhere to our coding guidelines: Before you submit a feature request, please check and make sure that it isn't possible through some other means. The JavaScript-enabled console is a powerful feature in the right hands. Please check our -[Wiki page](https://github.com/ethereum/go-ethereum/wiki) for more info +[Geth documentation page](https://geth.ethereum.org/docs/) for more info and help. ## Configuration, dependencies, and tests -Please see the [Developers' Guide](https://github.com/ethereum/go-ethereum/wiki/Developers'-Guide) +Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/devguide) for more details on configuring your environment, managing project dependencies and testing procedures. diff --git a/.travis.yml b/.travis.yml index 8f800c934a..5b1154432e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ jobs: dist: xenial go: 1.16.x script: - - make test-coregeth + - travis_wait 60 make test-coregeth # Run build and tests against latest-1 Go version. - stage: build @@ -50,7 +50,7 @@ jobs: env: - GO111MODULE=on script: - - go run build/ci.go test -coverage $(printf './%s/... ' $(go list ./... | sed 's|github.com/ethereum/go-ethereum||g' | cut -d'/' -f2 | uniq | grep -E '^[a-g]')) + - go run build/ci.go test -timeout 30m -coverage $(printf './%s/... ' $(go list ./... | sed 's|github.com/ethereum/go-ethereum||g' | cut -d'/' -f2 | uniq | grep -E '^[a-g]')) - stage: build name: "ARM64/Go1.16.x: make test [H-Z]" if: type = pull_request @@ -61,7 +61,7 @@ jobs: env: - GO111MODULE=on script: - - go run build/ci.go test -coverage $(printf './%s/... ' $(go list ./... | sed 's|github.com/ethereum/go-ethereum||g' | cut -d'/' -f2 | uniq | grep -E '^[h-z]')) + - go run build/ci.go test -timeout 30m -coverage $(printf './%s/... ' $(go list ./... | sed 's|github.com/ethereum/go-ethereum||g' | cut -d'/' -f2 | uniq | grep -E '^[h-z]')) # Run build and tests with environment-aware possible artifact deployment. - stage: build diff --git a/Dockerfile b/Dockerfile index d86b776611..6e0dea11b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build Geth in a stock Go builder container -FROM golang:1.15-alpine as builder +FROM golang:1.16-alpine as builder RUN apk add --no-cache make gcc musl-dev linux-headers git diff --git a/Dockerfile.alltools b/Dockerfile.alltools index 715213c5de..483afad8c3 100644 --- a/Dockerfile.alltools +++ b/Dockerfile.alltools @@ -1,5 +1,5 @@ # Build Geth in a stock Go builder container -FROM golang:1.15-alpine as builder +FROM golang:1.16-alpine as builder RUN apk add --no-cache make gcc musl-dev linux-headers git diff --git a/Makefile b/Makefile index 6b0be5ea3d..9cf091258e 100644 --- a/Makefile +++ b/Makefile @@ -39,12 +39,14 @@ ios: test: $(GORUN) build/ci.go test +# DEPRECATED. +# No attempt will be made after the Istanbul fork to maintain +# Parity configuration support. sync-parity-chainspecs: ./params/parity.json.d/sync-parity-remote.sh test-coregeth: \ test-coregeth-features \ - test-coregeth-chainspecs \ test-coregeth-consensus \ test-coregeth-regression-condensed ## Runs all tests specific to core-geth. @@ -72,29 +74,27 @@ test-evmc: hera ssvm evmone aleth-interpreter clean-evmc: rm -rf ./build/_workspace/hera ./build/_workspace/SSVM ./build/_workspace/evmone ./build/_workspace/aleth -test-coregeth-features: test-coregeth-features-parity test-coregeth-features-coregeth test-coregeth-features-multigethv0 ## Runs tests specific to multi-geth using Fork/Feature configs. +test-coregeth-features: \ + test-coregeth-features-coregeth \ + test-coregeth-features-multigethv0 ## Runs tests specific to multi-geth using Fork/Feature configs. test-coregeth-consensus: test-coregeth-features-clique-consensus -test-coregeth-features-parity: - @echo "Testing fork/feature/datatype implementation; equivalence - OPENETHEREUM." - env COREGETH_TESTS_CHAINCONFIG_FEATURE_EQUIVALENCE_OPENETHEREUM=on go test -count=1 ./tests - test-coregeth-features-coregeth: @echo "Testing fork/feature/datatype implementation; equivalence - COREGETH." - env COREGETH_TESTS_CHAINCONFIG_FEATURE_EQUIVALENCE_COREGETH=on go test -count=1 ./tests + env COREGETH_TESTS_CHAINCONFIG_FEATURE_EQUIVALENCE_COREGETH=on go test -count=1 -timeout 60m ./tests test-coregeth-features-multigethv0: @echo "Testing fork/feature/datatype implementation; equivalence - MULTIGETHv0." - env COREGETH_TESTS_CHAINCONFIG_FEATURE_EQUIVALENCE_MULTIGETHV0=on go test -count=1 ./tests + env COREGETH_TESTS_CHAINCONFIG_FEATURE_EQUIVALENCE_MULTIGETHV0=on go test -count=1 -timeout 60m ./tests test-coregeth-features-clique-consensus: @echo "Testing fork/feature/datatype implementation; equivalence - Clique consensus" - env COREGETH_TESTS_CHAINCONFIG_CONSENSUS_EQUIVALENCE_CLIQUE=on go test -count=1 -run TestState ./tests ## Only run state tests here, since Blockchain tests will care about rewards, etc. + env COREGETH_TESTS_CHAINCONFIG_CONSENSUS_EQUIVALENCE_CLIQUE=on go test -count=1 -timeout 60m -run TestState ./tests ## Only run state tests here, since Blockchain tests will care about rewards, etc. -test-coregeth-chainspecs: ## Run tests specific to multi-geth using chainspec file configs. - @echo "Testing Parity JSON chainspec equivalence." - env COREGETH_TESTS_CHAINCONFIG_OPENETHEREUM_SPECS=on go test -count=1 ./tests +test-coregeth-chainspecs-coregeth: ## Run tests specific to core-geth using coregeth chainspec file configs. + @echo "Testing CoreGeth JSON chainspec equivalence." + env COREGETH_TESTS_CHAINCONFIG_COREGETH_SPECS=on go test -count=1 ./tests test-coregeth-regression-condensed: geth @echo "Running condensed regression tests (imports) against simulated canonical blockchains." @@ -106,15 +106,18 @@ tests-generate: tests-generate-state tests-generate-difficulty ## Generate all t tests-generate-state: ## Generate state tests. @echo "Generating state tests." - env COREGETH_TESTS_CHAINCONFIG_OPENETHEREUM_SPECS=on \ env COREGETH_TESTS_GENERATE_STATE_TESTS=on \ - go run build/ci.go test -v ./tests -run TestGenState + env COREGETH_TESTS_CHAINCONFIG_FEATURE_EQUIVALENCE_COREGETH=on \ + go test -p 1 -v -timeout 60m ./tests -run TestGenStateAll tests-generate-difficulty: ## Generate difficulty tests. + @echo "Generating difficulty tests configs." + env COREGETH_TESTS_GENERATE_DIFFICULTY_TESTS_CONFIGS=on \ + go run build/ci.go test -v -timeout 10m ./tests -run TestDifficultyTestConfigGen + @echo "Generating difficulty tests." - env COREGETH_TESTS_CHAINCONFIG_OPENETHEREUM_SPECS=on \ env COREGETH_TESTS_GENERATE_DIFFICULTY_TESTS=on \ - go run build/ci.go test -v ./tests -run TestDifficultyGen + go run build/ci.go test -v -timeout 10m ./tests -run TestDifficultyGen lint: ## Run linters. $(GORUN) build/ci.go lint diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..bdce7b8d2a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,118 @@ +# Security Policy + +## Supported Versions + +Please see [Releases](https://github.com/ethereum/go-ethereum/releases). We recommend using the [most recently released version](https://github.com/ethereum/go-ethereum/releases/latest). + +## Audit reports + +Audit reports are published in the `docs` folder: https://github.com/ethereum/go-ethereum/tree/master/docs/audits + +| Scope | Date | Report Link | +| ------- | ------- | ----------- | +| `geth` | 20170425 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2017-04-25_Geth-audit_Truesec.pdf) | +| `clef` | 20180914 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2018-09-14_Clef-audit_NCC.pdf) | + +## Reporting a Vulnerability + +**Please do not file a public ticket** mentioning the vulnerability. + +To find out how to disclose a vulnerability in Ethereum visit [https://bounty.ethereum.org](https://bounty.ethereum.org) or email bounty@ethereum.org. Please read the [disclosure page](https://github.com/ethereum/go-ethereum/security/advisories?state=published) for more information about publically disclosed security vulnerabilities. + +Use the built-in `geth version-check` feature to check whether the software is affected by any known vulnerability. This command will fetch the latest [`vulnerabilities.json`](https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities.json) file which contains known security vulnerabilities concerning `geth`, and cross-check the data against its own version number. + +The following key may be used to communicate sensitive information to developers. + +Fingerprint: `AE96 ED96 9E47 9B00 84F3 E17F E88D 3334 FA5F 6A0A` + +``` +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +mQINBFgl3tgBEAC8A1tUBkD9YV+eLrOmtgy+/JS/H9RoZvkg3K1WZ8IYfj6iIRaY +neAk3Bp182GUPVz/zhKr2g0tMXIScDR3EnaDsY+Qg+JqQl8NOG+Cikr1nnkG2on9 +L8c8yiqry1ZTCmYMqCa2acTFqnyuXJ482aZNtB4QG2BpzfhW4k8YThpegk/EoRUi +m+y7buJDtoNf7YILlhDQXN8qlHB02DWOVUihph9tUIFsPK6BvTr9SIr/eG6j6k0b +fUo9pexOn7LS4SojoJmsm/5dp6AoKlac48cZU5zwR9AYcq/nvkrfmf2WkObg/xRd +EvKZzn05jRopmAIwmoC3CiLmqCHPmT5a29vEob/yPFE335k+ujjZCPOu7OwjzDk7 +M0zMSfnNfDq8bXh16nn+ueBxJ0NzgD1oC6c2PhM+XRQCXChoyI8vbfp4dGvCvYqv +QAE1bWjqnumZ/7vUPgZN6gDfiAzG2mUxC2SeFBhacgzDvtQls+uuvm+FnQOUgg2H +h8x2zgoZ7kqV29wjaUPFREuew7e+Th5BxielnzOfVycVXeSuvvIn6cd3g/s8mX1c +2kLSXJR7+KdWDrIrR5Az0kwAqFZt6B6QTlDrPswu3mxsm5TzMbny0PsbL/HBM+GZ +EZCjMXxB8bqV2eSaktjnSlUNX1VXxyOxXA+ZG2jwpr51egi57riVRXokrQARAQAB +tDlFdGhlcmV1bSBGb3VuZGF0aW9uIFNlY3VyaXR5IFRlYW0gPHNlY3VyaXR5QGV0 +aGVyZXVtLm9yZz6JAj4EEwECACgCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheA +BQJaCWH6BQkFo2BYAAoJEOiNMzT6X2oK+DEP/3H6dxkm0hvHZKoHLVuuxcu3EHYo +k5sd3MMWPrZSN8qzZnY7ayEDMxnarWOizc+2jfOxfJlzX/g8lR1/fsHdWPFPhPoV +Qk8ygrHn1H8U8+rpw/U03BqmqHpYCDzJ+CIis9UWROniqXw1nuqu/FtWOsdWxNKh +jUo6k/0EsaXsxRPzgJv7fEUcVcQ7as/C3x9sy3muc2gvgA4/BKoGPb1/U0GuA8lV +fDIDshAggmnSUAg+TuYSAAdoFQ1sKwFMPigcLJF2eyKuK3iUyixJrec/c4LSf3wA +cGghbeuqI8INP0Y2zvXDQN2cByxsFAuoZG+m0cyKGaDH2MVUvOKKYqn/03qvrf15 +AWAsW0l0yQwOTCo3FbsNzemClm5Bj/xH0E4XuwXwChcMCMOWJrFoxyvCEI+keoQc +c08/a8/MtS7vBAABXwOziSmm6CNqmzpWrh/fDrjlJlba9U3MxzvqU3IFlTdMratv +6V+SgX+L25lCzW4NxxUavoB8fAlvo8lxpHKo24FP+RcLQ8XqkU3RiUsgRjQRFOqQ +TaJcsp8mimmiYyf24mNu6b48pi+a5c/eQR9w59emeEUZqsJU+nqv8BWIIp7o4Agh +NYnKjkhPlY5e1fLVfAHIADZFynWwRPkPMJSrBiP5EtcOFxQGHGjRxU/KjXkvE0hV +xYb1PB8pWMTu/beeiQI+BBMBAgAoBQJYJd7YAhsDBQkB4TOABgsJCAcDAgYVCAIJ +CgsEFgIDAQIeAQIXgAAKCRDojTM0+l9qCplDD/9IZ2i+m1cnqQKtiyHbyFGx32oL +fzqPylX2bOG5DPsSTorSUdJMGVfT04oVxXc4S/2DVnNvi7RAbSiLapCWSplgtBOj +j1xlblOoXxT3m7s1XHGCX5tENxI9fVSSPVKJn+fQaWpPB2MhBA+1lUI6GJ+11T7K +J8LrP/fiw1/nOb7rW61HW44Gtyox23sA/d1+DsFVaF8hxJlNj5coPKr8xWzQ8pQl +juzdjHDukjevuw4rRmRq9vozvj9keEU9XJ5dldyEVXFmdDk7KT0p0Rla9nxYhzf/ +r/Bv8Bzy0HCWRb2D31BjXXGG05oVnYmNGxGFxYja4MwgrMmne3ilEVjfUJsapsqi +w41BAyQgIdfREulYN7ahsF5PrjVAqBd9IGtE8ULelF2SQxEBQBngEkP0ahP6tRAL +i7/CBjPKOyKijtqVny7qrGOnU2ygcA88/WDibexDhrjz0Gx8WmErU7rIWZiZ5u4Y +vJYVRo0+6rBCXRPeSJfiP5h1p17Anr2l42boAYslfcrzquB8MHtrNcyn650OLtHG +nbxgIdniKrpuzGN6Opw+O2id2JhD1/1p4SOemwAmthplr1MIyOHNP3q93rEj2J7h +5zPS/AJuKkMDFUpslPNLQjCOwPXtdzL7/kUZGBSyez1T3TaW1uY6l9XaJJRaSn+v +1zPgfp4GJ3lPs4AlAbQ0RXRoZXJldW0gRm91bmRhdGlvbiBCdWcgQm91bnR5IDxi +b3VudHlAZXRoZXJldW0ub3JnPokCPgQTAQIAKAIbAwYLCQgHAwIGFQgCCQoLBBYC +AwECHgECF4AFAloJYfoFCQWjYFgACgkQ6I0zNPpfagoENg/+LnSaVeMxiGVtcjWl +b7Xd73yrEy4uxiESS1AalW9mMf7oZzfI05f7QIQlaLAkNac74vZDJbPKjtb7tpMO +RFhRZMCveq6CPKU6pd1SI8IUVUKwpEe6AJP3lHdVP57dquieFE2HlYKm6uHbCGWU +0cjyTA+uu2KbgCHGmofsPY/xOcZLGEHTHqa5w60JJAQm+BSDKnw8wTyrxGvA3EK/ +ePSvOZMYa+iw6vYuZeBIMbdiXR/A2keBi3GuvqB8tDMj7P22TrH5mVDm3zNqGYD6 +amDPeiWp4cztY3aZyLcgYotqXPpDceZzDn+HopBPzAb/llCdE7bVswKRhphVMw4b +bhL0R/TQY7Sf6TK2LKSBrjv0DWOSijikE71SJcBnJvHU7EpKrQQ0lMGclm3ynyji +Nf0YTPXQt4I+fwTmOew2GFeK3UytNWbWI7oXX7Nm4bj9bhf3IJ0kmZb/Gs73+xII +e7Rz52Mby436tWyQIQiF9ITYNGvNf53TwBBZMn0pKPiTyr3Ur7FHEotkEOFNh1// +4zQY10XxuBdLrYGyZ4V8xHJM+oKre8Eg2R9qHXVbjvErHE+7CvgnV7YUip0criPr +BlKRvuoJaSliH2JFhSjWVrkPmFGrWN0BAx10yIqMnEplfKeHf4P9Elek3oInS8WP +G1zJG6s/t5+hQK0X37+TB+6rd3GJAj4EEwECACgFAlgl4TsCGwMFCQHhM4AGCwkI +BwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEOiNMzT6X2oKzf8P/iIKd77WHTbp4pMN +8h52HyZJtDJmjA1DPZrbGl1TesW/Z9uTd12txlgqZnbG2GfN9+LSP6EOPzR6v2xC +OVhR+RdWhZDJJuQCVS7lJIqQrZgmeTZG0TyQPZdLjVFBOrrhVwYX+HXbu429IzHr +URf5InyR1QgqOXyElDYS6e28HFqvaoA0DWTWDDqOLPVl+U5fuceIE2XXdv3AGLeP +Yf8J5MPobjPiZtBqI6S6iENY2Yn35qLX+axeC/iYSCHVtFuCCIdb/QYR1ZZV8Ps/ +aI9DwC7LU+YfPw7iqCIoqxSeA3o1PORkdSigEg3jtfRv5UqVo9a0oBb9jdoADsat +F/gW0E7mto3XGOiaR0eB9SSdsM3x7Bz4A0HIGNaxpZo1RWqlO91leP4c13Px7ISv +5OGXfLg+M8qb+qxbGd1HpitGi9s1y1aVfEj1kOtZ0tN8eu+Upg5WKwPNBDX3ar7J +9NCULgVSL+E79FG+zXw62gxiQrLfKzm4wU/9L5wVkwQnm29hLJ0tokrSBZFnc/1l +7OC+GM63tYicKkY4rqmoWUeYx7IwFH9mtDtvR1RxO85RbQhZizwpZpdpRkH0DqZu +ZJRmRa5r7rPqmfa7d+VIFhz2Xs8pJMLVqxTsLKcLglmjw7aOrYG0SWeH7YraXWGD +N3SlvSBiVwcK7QUKzLLvpadLwxfsuQINBFgl3tgBEACbgq6HTN5gEBi0lkD/MafI +nmNi+59U5gRGYqk46WlfRjhHudXjDpgD0lolGb4hYontkMaKRlCg2Rvgjvk3Zve0 +PKWjKw7gr8YBa9fMFY8BhAXI32OdyI9rFhxEZFfWAfwKVmT19BdeAQRFvcfd+8w8 +f1XVc+zddULMJFBTr+xKDlIRWwTkdLPQeWbjo0eHl/g4tuLiLrTxVbnj26bf+2+1 +DbM/w5VavzPrkviHqvKe/QP/gay4QDViWvFgLb90idfAHIdsPgflp0VDS5rVHFL6 +D73rSRdIRo3I8c8mYoNjSR4XDuvgOkAKW9LR3pvouFHHjp6Fr0GesRbrbb2EG66i +PsR99MQ7FqIL9VMHPm2mtR+XvbnKkH2rYyEqaMbSdk29jGapkAWle4sIhSKk749A +4tGkHl08KZ2N9o6GrfUehP/V2eJLaph2DioFL1HxRryrKy80QQKLMJRekxigq8gr +eW8xB4zuf9Mkuou+RHNmo8PebHjFstLigiD6/zP2e+4tUmrT0/JTGOShoGMl8Rt0 +VRxdPImKun+4LOXbfOxArOSkY6i35+gsgkkSy1gTJE0BY3S9auT6+YrglY/TWPQ9 +IJxWVOKlT+3WIp5wJu2bBKQ420VLqDYzkoWytel/bM1ACUtipMiIVeUs2uFiRjpz +A1Wy0QHKPTdSuGlJPRrfcQARAQABiQIlBBgBAgAPAhsMBQJaCWIIBQkFo2BYAAoJ +EOiNMzT6X2oKgSwQAKKs7BGF8TyZeIEO2EUK7R2bdQDCdSGZY06tqLFg3IHMGxDM +b/7FVoa2AEsFgv6xpoebxBB5zkhUk7lslgxvKiSLYjxfNjTBltfiFJ+eQnf+OTs8 +KeR51lLa66rvIH2qUzkNDCCTF45H4wIDpV05AXhBjKYkrDCrtey1rQyFp5fxI+0I +Q1UKKXvzZK4GdxhxDbOUSd38MYy93nqcmclGSGK/gF8XiyuVjeifDCM6+T1NQTX0 +K9lneidcqtBDvlggJTLJtQPO33o5EHzXSiud+dKth1uUhZOFEaYRZoye1YE3yB0T +NOOE8fXlvu8iuIAMBSDL9ep6sEIaXYwoD60I2gHdWD0lkP0DOjGQpi4ouXM3Edsd +5MTi0MDRNTij431kn8T/D0LCgmoUmYYMBgbwFhXr67axPZlKjrqR0z3F/Elv0ZPP +cVg1tNznsALYQ9Ovl6b5M3cJ5GapbbvNWC7yEE1qScl9HiMxjt/H6aPastH63/7w +cN0TslW+zRBy05VNJvpWGStQXcngsSUeJtI1Gd992YNjUJq4/Lih6Z1TlwcFVap+ +cTcDptoUvXYGg/9mRNNPZwErSfIJ0Ibnx9wPVuRN6NiCLOt2mtKp2F1pM6AOQPpZ +85vEh6I8i6OaO0w/Z0UHBwvpY6jDUliaROsWUQsqz78Z34CVj4cy6vPW2EF4 +=r6KK +-----END PGP PUBLIC KEY BLOCK----- +``` diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index ad8acdf522..a022ec5f9d 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -43,6 +43,7 @@ const jsondata = ` { "type" : "function", "name" : "uint64[2]", "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] }, { "type" : "function", "name" : "uint64[]", "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] }, { "type" : "function", "name" : "int8", "inputs" : [ { "name" : "inputs", "type" : "int8" } ] }, + { "type" : "function", "name" : "bytes32", "inputs" : [ { "name" : "inputs", "type" : "bytes32" } ] }, { "type" : "function", "name" : "foo", "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] }, { "type" : "function", "name" : "bar", "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] }, { "type" : "function", "name" : "slice", "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] }, @@ -68,6 +69,7 @@ var ( String, _ = NewType("string", "", nil) Bool, _ = NewType("bool", "", nil) Bytes, _ = NewType("bytes", "", nil) + Bytes32, _ = NewType("bytes32", "", nil) Address, _ = NewType("address", "", nil) Uint64Arr, _ = NewType("uint64[]", "", nil) AddressArr, _ = NewType("address[]", "", nil) @@ -98,6 +100,7 @@ var methods = map[string]Method{ "uint64[]": NewMethod("uint64[]", "uint64[]", Function, "", false, false, []Argument{{"inputs", Uint64Arr, false}}, nil), "uint64[2]": NewMethod("uint64[2]", "uint64[2]", Function, "", false, false, []Argument{{"inputs", Uint64Arr2, false}}, nil), "int8": NewMethod("int8", "int8", Function, "", false, false, []Argument{{"inputs", Int8, false}}, nil), + "bytes32": NewMethod("bytes32", "bytes32", Function, "", false, false, []Argument{{"inputs", Bytes32, false}}, nil), "foo": NewMethod("foo", "foo", Function, "", false, false, []Argument{{"inputs", Uint32, false}}, nil), "bar": NewMethod("bar", "bar", Function, "", false, false, []Argument{{"inputs", Uint32, false}, {"string", Uint16, false}}, nil), "slice": NewMethod("slice", "slice", Function, "", false, false, []Argument{{"inputs", Uint32Arr2, false}}, nil), diff --git a/accounts/abi/bind/auth.go b/accounts/abi/bind/auth.go index c891b0a3e9..b8065e8488 100644 --- a/accounts/abi/bind/auth.go +++ b/accounts/abi/bind/auth.go @@ -21,6 +21,7 @@ import ( "errors" "io" "io/ioutil" + "math/big" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/external" @@ -28,11 +29,21 @@ 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/log" ) +// ErrNoChainID is returned whenever the user failed to specify a chain id. +var ErrNoChainID = errors.New("no chain id specified") + +// ErrNotAuthorized is returned when an account is not properly unlocked. +var ErrNotAuthorized = errors.New("not authorized to sign this account") + // NewTransactor is a utility method to easily create a transaction signer from // an encrypted json key stream and the associated passphrase. +// +// Deprecated: Use NewTransactorWithChainID instead. func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) { + log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID") json, err := ioutil.ReadAll(keyin) if err != nil { return nil, err @@ -45,13 +56,17 @@ func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) { } // NewKeyStoreTransactor is a utility method to easily create a transaction signer from -// a decrypted key from a keystore. +// an decrypted key from a keystore. +// +// Deprecated: Use NewKeyStoreTransactorWithChainID instead. func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) { + log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID") + signer := types.HomesteadSigner{} return &TransactOpts{ From: account.Address, - Signer: func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) { + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { if address != account.Address { - return nil, errors.New("not authorized to sign this account") + return nil, ErrNotAuthorized } signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes()) if err != nil { @@ -64,13 +79,17 @@ func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account // NewKeyedTransactor is a utility method to easily create a transaction signer // from a single private key. +// +// Deprecated: Use NewKeyedTransactorWithChainID instead. func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts { + log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID") keyAddr := crypto.PubkeyToAddress(key.PublicKey) + signer := types.HomesteadSigner{} return &TransactOpts{ From: keyAddr, - Signer: func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) { + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { if address != keyAddr { - return nil, errors.New("not authorized to sign this account") + return nil, ErrNotAuthorized } signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key) if err != nil { @@ -81,14 +100,73 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts { } } +// NewTransactorWithChainID is a utility method to easily create a transaction signer from +// an encrypted json key stream and the associated passphrase. +func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) { + json, err := ioutil.ReadAll(keyin) + if err != nil { + return nil, err + } + key, err := keystore.DecryptKey(json, passphrase) + if err != nil { + return nil, err + } + return NewKeyedTransactorWithChainID(key.PrivateKey, chainID) +} + +// NewKeyStoreTransactorWithChainID is a utility method to easily create a transaction signer from +// an decrypted key from a keystore. +func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accounts.Account, chainID *big.Int) (*TransactOpts, error) { + if chainID == nil { + return nil, ErrNoChainID + } + signer := types.LatestSignerForChainID(chainID) + return &TransactOpts{ + From: account.Address, + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + if address != account.Address { + return nil, ErrNotAuthorized + } + signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes()) + if err != nil { + return nil, err + } + return tx.WithSignature(signer, signature) + }, + }, nil +} + +// NewKeyedTransactorWithChainID is a utility method to easily create a transaction signer +// from a single private key. +func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) { + keyAddr := crypto.PubkeyToAddress(key.PublicKey) + if chainID == nil { + return nil, ErrNoChainID + } + signer := types.LatestSignerForChainID(chainID) + return &TransactOpts{ + From: keyAddr, + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + if address != keyAddr { + return nil, ErrNotAuthorized + } + signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key) + if err != nil { + return nil, err + } + return tx.WithSignature(signer, signature) + }, + }, nil +} + // NewClefTransactor is a utility method to easily create a transaction signer // with a clef backend. func NewClefTransactor(clef *external.ExternalSigner, account accounts.Account) *TransactOpts { return &TransactOpts{ From: account.Address, - Signer: func(signer types.Signer, address common.Address, transaction *types.Transaction) (*types.Transaction, error) { + Signer: func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) { if address != account.Address { - return nil, errors.New("not authorized to sign this account") + return nil, ErrNotAuthorized } return clef.SignTx(account, transaction, nil) // Clef enforces its own chain id }, diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 0174f8c77c..e18095be33 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -77,6 +77,7 @@ type SimulatedBackend struct { // NewSimulatedBackendWithDatabase creates a new binding backend based on the given database // and uses a simulated blockchain for testing purposes. +// A simulated backend always uses chainID 1337. func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc genesisT.GenesisAlloc, gasLimit uint64) *SimulatedBackend { genesis := genesisT.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc} core.MustCommitGenesis(database, &genesis) @@ -126,10 +127,9 @@ func (b *SimulatedBackend) Rollback() { func (b *SimulatedBackend) rollback() { blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {}) - stateDB, _ := b.blockchain.State() b.pendingBlock = blocks[0] - b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil) + b.pendingState, _ = state.New(b.pendingBlock.Root(), b.blockchain.StateCache(), nil) } // stateByBlockNumber retrieves a state by a given blocknumber. @@ -482,7 +482,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs b.pendingState.RevertToSnapshot(snapshot) if err != nil { - if err == core.ErrIntrinsicGas { + if errors.Is(err, core.ErrIntrinsicGas) { return true, nil, nil // Special case, raise gas limit } return true, nil, err // Bail out @@ -545,10 +545,11 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM // Execute the call. msg := callMsg{call} - evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil) + txContext := core.NewEVMTxContext(msg) + evmContext := core.NewEVMBlockContext(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, b.config, vm.Config{}) + vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{}) gasPool := new(core.GasPool).AddGas(math.MaxUint64) return core.NewStateTransition(vmEnv, msg, gasPool).TransitionDb() @@ -560,7 +561,10 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa b.mu.Lock() defer b.mu.Unlock() - sender, err := types.Sender(types.NewEIP155Signer(b.config.GetChainID()), tx) + // Check transaction validity. + block := b.blockchain.CurrentBlock() + signer := types.MakeSigner(b.blockchain.Config(), block.Number()) + sender, err := types.Sender(signer, tx) if err != nil { panic(fmt.Errorf("invalid transaction: %v", err)) } @@ -569,7 +573,8 @@ 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(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) { + // Include tx in chain. + blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) { for _, tx := range b.pendingBlock.Transactions() { block.AddTxWithChain(b.blockchain, tx) } @@ -735,14 +740,15 @@ type callMsg struct { ethereum.CallMsg } -func (m callMsg) From() common.Address { return m.CallMsg.From } -func (m callMsg) Nonce() uint64 { return 0 } -func (m callMsg) CheckNonce() bool { return false } -func (m callMsg) To() *common.Address { return m.CallMsg.To } -func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } -func (m callMsg) Gas() uint64 { return m.CallMsg.Gas } -func (m callMsg) Value() *big.Int { return m.CallMsg.Value } -func (m callMsg) Data() []byte { return m.CallMsg.Data } +func (m callMsg) From() common.Address { return m.CallMsg.From } +func (m callMsg) Nonce() uint64 { return 0 } +func (m callMsg) CheckNonce() bool { return false } +func (m callMsg) To() *common.Address { return m.CallMsg.To } +func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } +func (m callMsg) Gas() uint64 { return m.CallMsg.Gas } +func (m callMsg) Value() *big.Int { return m.CallMsg.Value } +func (m callMsg) Data() []byte { return m.CallMsg.Data } +func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList } // filterBackend implements filters.Backend to support filtering for logs without // taking bloom-bits acceleration structures into account. diff --git a/accounts/abi/bind/backends/simulated_test.go b/accounts/abi/bind/backends/simulated_test.go index 673d3ad2cc..c10570145c 100644 --- a/accounts/abi/bind/backends/simulated_test.go +++ b/accounts/abi/bind/backends/simulated_test.go @@ -40,7 +40,7 @@ import ( func TestSimulatedBackend(t *testing.T) { var gasLimit uint64 = 8000029 key, _ := crypto.GenerateKey() // nolint: gosec - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) genAlloc := make(genesisT.GenesisAlloc) genAlloc[auth.From] = genesisT.GenesisAccount{Balance: big.NewInt(9223372036854775807)} @@ -412,7 +412,7 @@ func TestSimulatedBackend_EstimateGas(t *testing.T) { key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey) - opts := bind.NewKeyedTransactor(key) + opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := NewSimulatedBackend(genesisT.GenesisAlloc{addr: {Balance: big.NewInt(vars.Ether)}}, 10000000) defer sim.Close() @@ -889,7 +889,7 @@ func TestSimulatedBackend_PendingCodeAt(t *testing.T) { if err != nil { t.Errorf("could not get code at test addr: %v", err) } - auth := bind.NewKeyedTransactor(testKey) + auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim) if err != nil { t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract) @@ -925,7 +925,7 @@ func TestSimulatedBackend_CodeAt(t *testing.T) { if err != nil { t.Errorf("could not get code at test addr: %v", err) } - auth := bind.NewKeyedTransactor(testKey) + auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim) if err != nil { t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract) @@ -957,7 +957,7 @@ func TestSimulatedBackend_PendingAndCallContract(t *testing.T) { if err != nil { t.Errorf("could not get code at test addr: %v", err) } - contractAuth := bind.NewKeyedTransactor(testKey) + contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(abiBin), sim) if err != nil { t.Errorf("could not deploy contract: %v", err) @@ -1044,7 +1044,7 @@ func TestSimulatedBackend_CallContractRevert(t *testing.T) { if err != nil { t.Errorf("could not get code at test addr: %v", err) } - contractAuth := bind.NewKeyedTransactor(testKey) + contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(reverterBin), sim) if err != nil { t.Errorf("could not deploy contract: %v", err) diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 7dd5b8d41a..80647ce52f 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -32,7 +32,7 @@ import ( // SignerFn is a signer function callback when a contract requires a method to // sign the transaction before submission. -type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Transaction, error) +type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error) // CallOpts is the collection of options to fine tune a contract call request. type CallOpts struct { @@ -54,6 +54,8 @@ type TransactOpts struct { GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate) Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) + + NoSend bool // Do all transact steps but do not send the transaction } // FilterOpts is the collection of options to fine tune filtering for events @@ -256,10 +258,13 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i if opts.Signer == nil { return nil, errors.New("no signer to authorize the transaction with") } - signedTx, err := opts.Signer(types.HomesteadSigner{}, opts.From, rawTx) + signedTx, err := opts.Signer(opts.From, rawTx) if err != nil { return nil, err } + if opts.NoSend { + return signedTx, nil + } if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil { return nil, err } diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index dfeb5b9e50..306d631e15 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -296,7 +296,7 @@ var bindTests = []struct { ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -351,7 +351,7 @@ var bindTests = []struct { ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -397,7 +397,7 @@ var bindTests = []struct { ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -455,7 +455,7 @@ var bindTests = []struct { ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -503,7 +503,7 @@ var bindTests = []struct { ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -529,6 +529,70 @@ var bindTests = []struct { nil, nil, }, + // Tests that structs are correctly unpacked + { + + `Structs`, + ` + pragma solidity ^0.6.5; + pragma experimental ABIEncoderV2; + contract Structs { + struct A { + bytes32 B; + } + + function F() public view returns (A[] memory a, uint256[] memory c, bool[] memory d) { + A[] memory a = new A[](2); + a[0].B = bytes32(uint256(1234) << 96); + uint256[] memory c; + bool[] memory d; + return (a, c, d); + } + + function G() public view returns (A[] memory a) { + A[] memory a = new A[](2); + a[0].B = bytes32(uint256(1234) << 96); + return a; + } + } + `, + []string{`608060405234801561001057600080fd5b50610278806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806328811f591461003b5780636fecb6231461005b575b600080fd5b610043610070565b604051610052939291906101a0565b60405180910390f35b6100636100d6565b6040516100529190610186565b604080516002808252606082810190935282918291829190816020015b610095610131565b81526020019060019003908161008d575050805190915061026960611b9082906000906100be57fe5b60209081029190910101515293606093508392509050565b6040805160028082526060828101909352829190816020015b6100f7610131565b8152602001906001900390816100ef575050805190915061026960611b90829060009061012057fe5b602090810291909101015152905090565b60408051602081019091526000815290565b815260200190565b6000815180845260208085019450808401835b8381101561017b578151518752958201959082019060010161015e565b509495945050505050565b600060208252610199602083018461014b565b9392505050565b6000606082526101b3606083018661014b565b6020838203818501528186516101c98185610239565b91508288019350845b818110156101f3576101e5838651610143565b9484019492506001016101d2565b505084810360408601528551808252908201925081860190845b8181101561022b57825115158552938301939183019160010161020d565b509298975050505050505050565b9081526020019056fea2646970667358221220eb85327e285def14230424c52893aebecec1e387a50bb6b75fc4fdbed647f45f64736f6c63430006050033`}, + []string{`[{"inputs":[],"name":"F","outputs":[{"components":[{"internalType":"bytes32","name":"B","type":"bytes32"}],"internalType":"structStructs.A[]","name":"a","type":"tuple[]"},{"internalType":"uint256[]","name":"c","type":"uint256[]"},{"internalType":"bool[]","name":"d","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"G","outputs":[{"components":[{"internalType":"bytes32","name":"B","type":"bytes32"}],"internalType":"structStructs.A[]","name":"a","type":"tuple[]"}],"stateMutability":"view","type":"function"}]`}, + ` + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params/types/genesisT" + `, + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + // Deploy a structs method invoker contract and execute its default method + _, _, structs, err := DeployStructs(auth, sim) + if err != nil { + t.Fatalf("Failed to deploy defaulter contract: %v", err) + } + sim.Commit() + opts := bind.CallOpts{} + if _, err := structs.F(&opts); err != nil { + t.Fatalf("Failed to invoke F method: %v", err) + } + if _, err := structs.G(&opts); err != nil { + t.Fatalf("Failed to invoke G method: %v", err) + } + `, + nil, + nil, + nil, + nil, + }, // Tests that non-existent contracts are reported as such (though only simulator test) { `NonExistent`, @@ -569,6 +633,45 @@ var bindTests = []struct { nil, nil, }, + { + `NonExistentStruct`, + ` + contract NonExistentStruct { + function Struct() public view returns(uint256 a, uint256 b) { + return (10, 10); + } + } + `, + []string{`6080604052348015600f57600080fd5b5060888061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063d5f6622514602d575b600080fd5b6033604c565b6040805192835260208301919091528051918290030190f35b600a809156fea264697066735822beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef64736f6c6343decafe0033`}, + []string{`[{"inputs":[],"name":"Struct","outputs":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"b","type":"uint256"}],"stateMutability":"pure","type":"function"}]`}, + ` + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params/types/genesisT" + `, + ` + // Create a simulator and wrap a non-deployed contract + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{}, uint64(10000000000)) + defer sim.Close() + + nonexistent, err := NewNonExistentStruct(common.Address{}, sim) + if err != nil { + t.Fatalf("Failed to access non-existent contract: %v", err) + } + // Ensure that contract calls fail with the appropriate error + if res, err := nonexistent.Struct(nil); err == nil { + t.Fatalf("Call succeeded on non-existent contract: %v", res) + } else if (err != bind.ErrNoCode) { + t.Fatalf("Error mismatch: have %v, want %v", err, bind.ErrNoCode) + } + `, + nil, + nil, + nil, + nil, + }, // Tests that gas estimation works for contracts with weird gas mechanics too. { `FunkyGasPattern`, @@ -592,13 +695,13 @@ var bindTests = []struct { "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" - "github.com/ethereum/go-ethereum/params/types/genesisT" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params/types/genesisT" `, ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -648,7 +751,7 @@ var bindTests = []struct { ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -723,7 +826,7 @@ var bindTests = []struct { ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -817,7 +920,7 @@ var bindTests = []struct { ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -1007,7 +1110,7 @@ var bindTests = []struct { ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -1142,7 +1245,7 @@ var bindTests = []struct { ` key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -1284,7 +1387,7 @@ var bindTests = []struct { ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -1350,7 +1453,7 @@ var bindTests = []struct { ` // Initialize test accounts key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -1444,7 +1547,7 @@ var bindTests = []struct { sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000) defer sim.Close() - transactOpts := bind.NewKeyedTransactor(key) + transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) _, _, _, err := DeployIdentifierCollision(transactOpts, sim) if err != nil { t.Fatalf("failed to deploy contract: %v", err) @@ -1506,7 +1609,7 @@ var bindTests = []struct { sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000) defer sim.Close() - transactOpts := bind.NewKeyedTransactor(key) + transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) _, _, c1, err := DeployContractOne(transactOpts, sim) if err != nil { t.Fatal("Failed to deploy contract") @@ -1563,7 +1666,7 @@ var bindTests = []struct { ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() - auth := bind.NewKeyedTransactor(key) + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) defer sim.Close() @@ -1601,11 +1704,7 @@ var bindTests = []struct { contract NewFallbacks { event Fallback(bytes data); fallback() external { - bytes memory data; - assembly { - calldatacopy(data, 0, calldatasize()) - } - emit Fallback(data); + emit Fallback(msg.data); } event Received(address addr, uint value); @@ -1614,7 +1713,7 @@ var bindTests = []struct { } } `, - []string{"60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040526004361061000d575b36610081575b7f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f885258743334604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a15b005b34801561008e5760006000fd5b505b606036600082377f9043988963722edecc2099c75b0af0ff76af14ffca42ed6bce059a20a2a9f986816040518080602001828103825283818151815260200191508051906020019080838360005b838110156100fa5780820151818401525b6020810190506100de565b50505050905090810190601f1680156101275780820380516001836020036101000a031916815260200191505b509250505060405180910390a1505b00fea26469706673582212205643ca37f40c2b352dc541f42e9e6720de065de756324b7fcc9fb1d67eda4a7d64736f6c63430006040033"}, + []string{"6080604052348015600f57600080fd5b506101078061001f6000396000f3fe608060405236605f577f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f885258743334604051808373ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a1005b348015606a57600080fd5b507f9043988963722edecc2099c75b0af0ff76af14ffca42ed6bce059a20a2a9f98660003660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a100fea26469706673582212201f994dcfbc53bf610b19176f9a361eafa77b447fd9c796fa2c615dfd0aaf3b8b64736f6c634300060c0033"}, []string{`[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"Fallback","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Received","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"stateMutability":"payable","type":"receive"}]`}, ` "bytes" @@ -1632,7 +1731,7 @@ var bindTests = []struct { sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 1000000) defer sim.Close() - opts := bind.NewKeyedTransactor(key) + opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) _, _, c, err := DeployNewFallbacks(opts, sim) if err != nil { t.Fatalf("Failed to deploy contract: %v", err) @@ -1662,6 +1761,7 @@ var bindTests = []struct { } // Test fallback function + gotEvent = false opts.Value = nil calldata := []byte{0x01, 0x02, 0x03} c.Fallback(opts, calldata) diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index 5329b3ebc3..e9bdd3d414 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -304,8 +304,11 @@ var ( err := _{{$contract.Type}}.contract.Call(opts, &out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}}) {{if .Structured}} outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} }) + if err != nil { + return *outstruct, err + } {{range $i, $t := .Normalized.Outputs}} - outstruct.{{.Name}} = out[{{$i}}].({{bindtype .Type $structs}}){{end}} + outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}} return *outstruct, err {{else}} @@ -541,6 +544,7 @@ var ( if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil { return nil, err } + event.Raw = log return event, nil } diff --git a/accounts/abi/reflect_test.go b/accounts/abi/reflect_test.go index bac4cd9530..cf13a79da8 100644 --- a/accounts/abi/reflect_test.go +++ b/accounts/abi/reflect_test.go @@ -202,12 +202,12 @@ func TestConvertType(t *testing.T) { fields = append(fields, reflect.StructField{ Name: "X", Type: reflect.TypeOf(new(big.Int)), - Tag: reflect.StructTag("json:\"" + "x" + "\""), + Tag: "json:\"" + "x" + "\"", }) fields = append(fields, reflect.StructField{ Name: "Y", Type: reflect.TypeOf(new(big.Int)), - Tag: reflect.StructTag("json:\"" + "y" + "\""), + Tag: "json:\"" + "y" + "\"", }) val := reflect.New(reflect.StructOf(fields)) val.Elem().Field(0).Set(reflect.ValueOf(big.NewInt(1))) diff --git a/accounts/abi/type_test.go b/accounts/abi/type_test.go index 48df3aa383..8c3aedca6a 100644 --- a/accounts/abi/type_test.go +++ b/accounts/abi/type_test.go @@ -255,7 +255,7 @@ func TestTypeCheck(t *testing.T) { {"bytes", nil, [2]byte{0, 1}, "abi: cannot use array as type slice as argument"}, {"bytes", nil, common.Hash{1}, "abi: cannot use array as type slice as argument"}, {"string", nil, "hello world", ""}, - {"string", nil, string(""), ""}, + {"string", nil, "", ""}, {"string", nil, []byte{}, "abi: cannot use slice as type string as argument"}, {"bytes32[]", nil, [][32]byte{{}}, ""}, {"function", nil, [24]byte{}, ""}, diff --git a/accounts/accounts.go b/accounts/accounts.go index dc85cba174..08a1f0f2b1 100644 --- a/accounts/accounts.go +++ b/accounts/accounts.go @@ -21,7 +21,7 @@ import ( "fmt" "math/big" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" diff --git a/accounts/hd.go b/accounts/hd.go index 75c4761106..54acea3b26 100644 --- a/accounts/hd.go +++ b/accounts/hd.go @@ -150,3 +150,31 @@ func (path *DerivationPath) UnmarshalJSON(b []byte) error { *path, err = ParseDerivationPath(dp) return err } + +// DefaultIterator creates a BIP-32 path iterator, which progresses by increasing the last component: +// i.e. m/44'/60'/0'/0/0, m/44'/60'/0'/0/1, m/44'/60'/0'/0/2, ... m/44'/60'/0'/0/N. +func DefaultIterator(base DerivationPath) func() DerivationPath { + path := make(DerivationPath, len(base)) + copy(path[:], base[:]) + // Set it back by one, so the first call gives the first result + path[len(path)-1]-- + return func() DerivationPath { + path[len(path)-1]++ + return path + } +} + +// LedgerLiveIterator creates a bip44 path iterator for Ledger Live. +// Ledger Live increments the third component rather than the fifth component +// i.e. m/44'/60'/0'/0/0, m/44'/60'/1'/0/0, m/44'/60'/2'/0/0, ... m/44'/60'/N'/0/0. +func LedgerLiveIterator(base DerivationPath) func() DerivationPath { + path := make(DerivationPath, len(base)) + copy(path[:], base[:]) + // Set it back by one, so the first call gives the first result + path[2]-- + return func() DerivationPath { + // ledgerLivePathIterator iterates on the third component + path[2]++ + return path + } +} diff --git a/accounts/hd_test.go b/accounts/hd_test.go index 3156a487ee..0743bbe666 100644 --- a/accounts/hd_test.go +++ b/accounts/hd_test.go @@ -17,6 +17,7 @@ package accounts import ( + "fmt" "reflect" "testing" ) @@ -77,3 +78,41 @@ func TestHDPathParsing(t *testing.T) { } } } + +func testDerive(t *testing.T, next func() DerivationPath, expected []string) { + t.Helper() + for i, want := range expected { + if have := next(); fmt.Sprintf("%v", have) != want { + t.Errorf("step %d, have %v, want %v", i, have, want) + } + } +} + +func TestHdPathIteration(t *testing.T) { + testDerive(t, DefaultIterator(DefaultBaseDerivationPath), + []string{ + "m/44'/60'/0'/0/0", "m/44'/60'/0'/0/1", + "m/44'/60'/0'/0/2", "m/44'/60'/0'/0/3", + "m/44'/60'/0'/0/4", "m/44'/60'/0'/0/5", + "m/44'/60'/0'/0/6", "m/44'/60'/0'/0/7", + "m/44'/60'/0'/0/8", "m/44'/60'/0'/0/9", + }) + + testDerive(t, DefaultIterator(LegacyLedgerBaseDerivationPath), + []string{ + "m/44'/60'/0'/0", "m/44'/60'/0'/1", + "m/44'/60'/0'/2", "m/44'/60'/0'/3", + "m/44'/60'/0'/4", "m/44'/60'/0'/5", + "m/44'/60'/0'/6", "m/44'/60'/0'/7", + "m/44'/60'/0'/8", "m/44'/60'/0'/9", + }) + + testDerive(t, LedgerLiveIterator(DefaultBaseDerivationPath), + []string{ + "m/44'/60'/0'/0/0", "m/44'/60'/1'/0/0", + "m/44'/60'/2'/0/0", "m/44'/60'/3'/0/0", + "m/44'/60'/4'/0/0", "m/44'/60'/5'/0/0", + "m/44'/60'/6'/0/0", "m/44'/60'/7'/0/0", + "m/44'/60'/8'/0/0", "m/44'/60'/9'/0/0", + }) +} diff --git a/accounts/keystore/account_cache.go b/accounts/keystore/account_cache.go index 8f660e282f..a3ec6e9c56 100644 --- a/accounts/keystore/account_cache.go +++ b/accounts/keystore/account_cache.go @@ -262,7 +262,7 @@ func (ac *accountCache) scanAccounts() error { switch { case err != nil: log.Debug("Failed to decode keystore key", "path", path, "err", err) - case (addr == common.Address{}): + case addr == common.Address{}: log.Debug("Failed to decode keystore key", "path", path, "err", "missing or zero address") default: return &accounts.Account{ diff --git a/accounts/keystore/key.go b/accounts/keystore/key.go index 84d8df0c5a..2b815ce0f9 100644 --- a/accounts/keystore/key.go +++ b/accounts/keystore/key.go @@ -32,7 +32,7 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/pborman/uuid" + "github.com/google/uuid" ) const ( @@ -110,7 +110,10 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) { } u := new(uuid.UUID) - *u = uuid.Parse(keyJSON.Id) + *u, err = uuid.Parse(keyJSON.Id) + if err != nil { + return err + } k.Id = *u addr, err := hex.DecodeString(keyJSON.Address) if err != nil { @@ -128,7 +131,10 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) { } func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key { - id := uuid.NewRandom() + id, err := uuid.NewRandom() + if err != nil { + panic(fmt.Sprintf("Could not create random uuid: %v", err)) + } key := &Key{ Id: id, Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey), diff --git a/accounts/keystore/keystore.go b/accounts/keystore/keystore.go index 9d5e2cf6a2..88dcfbeb69 100644 --- a/accounts/keystore/keystore.go +++ b/accounts/keystore/keystore.go @@ -283,11 +283,9 @@ func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *b if !found { return nil, ErrLocked } - // Depending on the presence of the chain ID, sign with EIP155 or homestead - if chainID != nil { - return types.SignTx(tx, types.NewEIP155Signer(chainID), unlockedKey.PrivateKey) - } - return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey) + // Depending on the presence of the chain ID, sign with 2718 or homestead + signer := types.LatestSignerForChainID(chainID) + return types.SignTx(tx, signer, unlockedKey.PrivateKey) } // SignHashWithPassphrase signs hash if the private key matching the given address @@ -310,12 +308,9 @@ func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string, return nil, err } defer zeroKey(key.PrivateKey) - - // Depending on the presence of the chain ID, sign with EIP155 or homestead - if chainID != nil { - return types.SignTx(tx, types.NewEIP155Signer(chainID), key.PrivateKey) - } - return types.SignTx(tx, types.HomesteadSigner{}, key.PrivateKey) + // Depending on the presence of the chain ID, sign with or without replay protection. + signer := types.LatestSignerForChainID(chainID) + return types.SignTx(tx, signer, key.PrivateKey) } // Unlock unlocks the given account indefinitely. diff --git a/accounts/keystore/passphrase.go b/accounts/keystore/passphrase.go index 89cdf0bfca..3b3e631888 100644 --- a/accounts/keystore/passphrase.go +++ b/accounts/keystore/passphrase.go @@ -42,7 +42,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" - "github.com/pborman/uuid" + "github.com/google/uuid" "golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/scrypt" ) @@ -228,9 +228,12 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) { return nil, err } key := crypto.ToECDSAUnsafe(keyBytes) - + id, err := uuid.FromBytes(keyId) + if err != nil { + return nil, err + } return &Key{ - Id: uuid.UUID(keyId), + Id: id, Address: crypto.PubkeyToAddress(key.PublicKey), PrivateKey: key, }, nil @@ -276,7 +279,11 @@ func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byt if keyProtected.Version != version { return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version) } - keyId = uuid.Parse(keyProtected.Id) + keyUUID, err := uuid.Parse(keyProtected.Id) + if err != nil { + return nil, nil, err + } + keyId = keyUUID[:] plainText, err := DecryptDataV3(keyProtected.Crypto, auth) if err != nil { return nil, nil, err @@ -285,7 +292,11 @@ func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byt } func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { - keyId = uuid.Parse(keyProtected.Id) + keyUUID, err := uuid.Parse(keyProtected.Id) + if err != nil { + return nil, nil, err + } + keyId = keyUUID[:] mac, err := hex.DecodeString(keyProtected.Crypto.MAC) if err != nil { return nil, nil, err diff --git a/accounts/keystore/presale.go b/accounts/keystore/presale.go index 03055245f5..0664dc2cdd 100644 --- a/accounts/keystore/presale.go +++ b/accounts/keystore/presale.go @@ -27,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/crypto" - "github.com/pborman/uuid" + "github.com/google/uuid" "golang.org/x/crypto/pbkdf2" ) @@ -37,7 +37,10 @@ func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accou if err != nil { return accounts.Account{}, nil, err } - key.Id = uuid.NewRandom() + key.Id, err = uuid.NewRandom() + if err != nil { + return accounts.Account{}, nil, err + } a := accounts.Account{ Address: key.Address, URL: accounts.URL{ @@ -86,7 +89,7 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error ecKey := crypto.ToECDSAUnsafe(ethPriv) key = &Key{ - Id: nil, + Id: uuid.UUID{}, Address: crypto.PubkeyToAddress(ecKey.PublicKey), PrivateKey: ecKey, } diff --git a/accounts/keystore/wallet.go b/accounts/keystore/wallet.go index 498067d497..1066095f6d 100644 --- a/accounts/keystore/wallet.go +++ b/accounts/keystore/wallet.go @@ -19,7 +19,7 @@ package keystore import ( "math/big" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -58,7 +58,7 @@ func (w *keystoreWallet) Open(passphrase string) error { return nil } func (w *keystoreWallet) Close() error { return nil } // Accounts implements accounts.Wallet, returning an account list consisting of -// a single account that the plain kestore wallet contains. +// a single account that the plain keystore wallet contains. func (w *keystoreWallet) Accounts() []accounts.Account { return []accounts.Account{w.account} } @@ -93,12 +93,12 @@ func (w *keystoreWallet) signHash(account accounts.Account, hash []byte) ([]byte return w.keystore.SignHash(account, hash) } -// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed +// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed. func (w *keystoreWallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) { return w.signHash(account, crypto.Keccak256(data)) } -// SignDataWithPassphrase signs keccak256(data). The mimetype parameter describes the type of data being signed +// SignDataWithPassphrase signs keccak256(data). The mimetype parameter describes the type of data being signed. func (w *keystoreWallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) { // Make sure the requested account is contained within if !w.Contains(account) { @@ -108,12 +108,14 @@ func (w *keystoreWallet) SignDataWithPassphrase(account accounts.Account, passph return w.keystore.SignHashWithPassphrase(account, passphrase, crypto.Keccak256(data)) } +// SignText implements accounts.Wallet, attempting to sign the hash of +// the given text with the given account. func (w *keystoreWallet) SignText(account accounts.Account, text []byte) ([]byte, error) { return w.signHash(account, accounts.TextHash(text)) } // SignTextWithPassphrase implements accounts.Wallet, attempting to sign the -// given hash with the given account using passphrase as extra authentication. +// hash of the given text with the given account using passphrase as extra authentication. func (w *keystoreWallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) { // Make sure the requested account is contained within if !w.Contains(account) { diff --git a/accounts/scwallet/README.md b/accounts/scwallet/README.md index cfca916b3a..4313d9c6b2 100644 --- a/accounts/scwallet/README.md +++ b/accounts/scwallet/README.md @@ -31,12 +31,16 @@ Write down the URL (`keycard://044def09` in this example). Then ask `geth` to open the wallet: ``` - > personal.openWallet("keycard://044def09") - Please enter the pairing password: + > personal.openWallet("keycard://044def09", "pairing password") ``` - Enter the pairing password that you have received during card initialization. Same with the PIN that you will subsequently be - asked for. + The pairing password has been generated during the card initialization process. + + The process needs to be repeated once more with the PIN: + + ``` + > personal.openWallet("keycard://044def09", "PIN number") + ``` If everything goes well, you should see your new account when typing `personal` on the console: diff --git a/accounts/scwallet/securechannel.go b/accounts/scwallet/securechannel.go index 9b70c69dcc..10887a8b43 100644 --- a/accounts/scwallet/securechannel.go +++ b/accounts/scwallet/securechannel.go @@ -20,6 +20,7 @@ import ( "bytes" "crypto/aes" "crypto/cipher" + "crypto/elliptic" "crypto/rand" "crypto/sha256" "crypto/sha512" @@ -27,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" pcsc "github.com/gballet/go-libpcsclite" - "github.com/wsddn/go-ecdh" "golang.org/x/crypto/pbkdf2" "golang.org/x/text/unicode/norm" ) @@ -63,26 +63,19 @@ type SecureChannelSession struct { // NewSecureChannelSession creates a new secure channel for the given card and public key. func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSession, error) { // Generate an ECDSA keypair for ourselves - gen := ecdh.NewEllipticECDH(crypto.S256()) - private, public, err := gen.GenerateKey(rand.Reader) + key, err := crypto.GenerateKey() if err != nil { return nil, err } - - cardPublic, ok := gen.Unmarshal(keyData) - if !ok { - return nil, fmt.Errorf("could not unmarshal public key from card") - } - - secret, err := gen.GenerateSharedSecret(private, cardPublic) + cardPublic, err := crypto.UnmarshalPubkey(keyData) if err != nil { - return nil, err + return nil, fmt.Errorf("could not unmarshal public key from card: %v", err) } - + secret, _ := key.Curve.ScalarMult(cardPublic.X, cardPublic.Y, key.D.Bytes()) return &SecureChannelSession{ card: card, - secret: secret, - publicKey: gen.Marshal(public), + secret: secret.Bytes(), + publicKey: elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y), }, nil } diff --git a/accounts/scwallet/wallet.go b/accounts/scwallet/wallet.go index 85fae8c114..b4d229bc0b 100644 --- a/accounts/scwallet/wallet.go +++ b/accounts/scwallet/wallet.go @@ -33,7 +33,7 @@ import ( "sync" "time" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -699,7 +699,7 @@ func (w *Wallet) signHash(account accounts.Account, hash []byte) ([]byte, error) // the needed details via SignTxWithPassphrase, or by other means (e.g. unlock // the account in a keystore). func (w *Wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { - signer := types.NewEIP155Signer(chainID) + signer := types.LatestSignerForChainID(chainID) hash := signer.Hash(tx) sig, err := w.signHash(account, hash[:]) if err != nil { diff --git a/accounts/usbwallet/ledger.go b/accounts/usbwallet/ledger.go index 71f0f9392f..3de3b4091c 100644 --- a/accounts/usbwallet/ledger.go +++ b/accounts/usbwallet/ledger.go @@ -52,8 +52,10 @@ const ( ledgerOpRetrieveAddress ledgerOpcode = 0x02 // Returns the public key and Ethereum address for a given BIP 32 path ledgerOpSignTransaction ledgerOpcode = 0x04 // Signs an Ethereum transaction after having the user validate the parameters ledgerOpGetConfiguration ledgerOpcode = 0x06 // Returns specific wallet application configuration + ledgerOpSignTypedMessage ledgerOpcode = 0x0c // Signs an Ethereum message following the EIP 712 specification ledgerP1DirectlyFetchAddress ledgerParam1 = 0x00 // Return address directly from the wallet + ledgerP1InitTypedMessageData ledgerParam1 = 0x00 // First chunk of Typed Message data ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address @@ -170,6 +172,24 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio return w.ledgerSign(path, tx, chainID) } +// SignTypedMessage implements usbwallet.driver, sending the message to the Ledger and +// waiting for the user to sign or deny the transaction. +// +// Note: this was introduced in the ledger 1.5.0 firmware +func (w *ledgerDriver) SignTypedMessage(path accounts.DerivationPath, domainHash []byte, messageHash []byte) ([]byte, error) { + // If the Ethereum app doesn't run, abort + if w.offline() { + return nil, accounts.ErrWalletClosed + } + // Ensure the wallet is capable of signing the given transaction + if w.version[0] < 1 && w.version[1] < 5 { + //lint:ignore ST1005 brand name displayed on the console + return nil, fmt.Errorf("Ledger version >= 1.5.0 required for EIP-712 signing (found version v%d.%d.%d)", w.version[0], w.version[1], w.version[2]) + } + // All infos gathered and metadata checks out, request signing + return w.ledgerSignTypedMessage(path, domainHash, messageHash) +} + // ledgerVersion retrieves the current version of the Ethereum wallet app running // on the Ledger wallet. // @@ -367,6 +387,68 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction return sender, signed, nil } +// ledgerSignTypedMessage sends the transaction to the Ledger wallet, and waits for the user +// to confirm or deny the transaction. +// +// The signing protocol is defined as follows: +// +// CLA | INS | P1 | P2 | Lc | Le +// ----+-----+----+-----------------------------+-----+--- +// E0 | 0C | 00 | implementation version : 00 | variable | variable +// +// Where the input is: +// +// Description | Length +// -------------------------------------------------+---------- +// Number of BIP 32 derivations to perform (max 10) | 1 byte +// First derivation index (big endian) | 4 bytes +// ... | 4 bytes +// Last derivation index (big endian) | 4 bytes +// domain hash | 32 bytes +// message hash | 32 bytes +// +// +// +// And the output data is: +// +// Description | Length +// ------------+--------- +// signature V | 1 byte +// signature R | 32 bytes +// signature S | 32 bytes +func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHash []byte, messageHash []byte) ([]byte, error) { + // Flatten the derivation path into the Ledger request + path := make([]byte, 1+4*len(derivationPath)) + path[0] = byte(len(derivationPath)) + for i, component := range derivationPath { + binary.BigEndian.PutUint32(path[1+4*i:], component) + } + // Create the 712 message + payload := append(path, domainHash...) + payload = append(payload, messageHash...) + + // Send the request and wait for the response + var ( + op = ledgerP1InitTypedMessageData + reply []byte + err error + ) + + // Send the message over, ensuring it's processed correctly + reply, err = w.ledgerExchange(ledgerOpSignTypedMessage, op, 0, payload) + + if err != nil { + return nil, err + } + + // Extract the Ethereum signature and do a sanity validation + if len(reply) != crypto.SignatureLength { + return nil, errors.New("reply lacks signature") + } + signature := append(reply[1:], reply[0]) + return signature, nil +} + // ledgerExchange performs a data exchange with the Ledger wallet, sending it a // message and retrieving the response. // diff --git a/accounts/usbwallet/trezor.go b/accounts/usbwallet/trezor.go index 1892097baf..c2182b88d0 100644 --- a/accounts/usbwallet/trezor.go +++ b/accounts/usbwallet/trezor.go @@ -185,6 +185,10 @@ func (w *trezorDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio return w.trezorSign(path, tx, chainID) } +func (w *trezorDriver) SignTypedMessage(path accounts.DerivationPath, domainHash []byte, messageHash []byte) ([]byte, error) { + return nil, accounts.ErrNotSupported +} + // trezorDerive sends a derivation request to the Trezor device and returns the // Ethereum address located on that path. func (w *trezorDriver) trezorDerive(derivationPath []uint32) (common.Address, error) { @@ -255,9 +259,11 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction if chainID == nil { signer = new(types.HomesteadSigner) } else { + // Trezor backend does not support typed transactions yet. signer = types.NewEIP155Signer(chainID) signature[64] -= byte(chainID.Uint64()*2 + 35) } + // Inject the final signature into the transaction and sanity check the sender signed, err := tx.WithSignature(signer, signature) if err != nil { diff --git a/accounts/usbwallet/wallet.go b/accounts/usbwallet/wallet.go index e39c6bdf34..b6f1814488 100644 --- a/accounts/usbwallet/wallet.go +++ b/accounts/usbwallet/wallet.go @@ -25,7 +25,7 @@ import ( "sync" "time" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -67,6 +67,8 @@ type driver interface { // SignTx sends the transaction to the USB device and waits for the user to confirm // or deny the transaction. SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) + + SignTypedMessage(path accounts.DerivationPath, messageHash []byte, domainHash []byte) ([]byte, error) } // wallet represents the common functionality shared by all USB hardware @@ -524,7 +526,46 @@ func (w *wallet) signHash(account accounts.Account, hash []byte) ([]byte, error) // SignData signs keccak256(data). The mimetype parameter describes the type of data being signed func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) { - return w.signHash(account, crypto.Keccak256(data)) + + // Unless we are doing 712 signing, simply dispatch to signHash + if !(mimeType == accounts.MimetypeTypedData && len(data) == 66 && data[0] == 0x19 && data[1] == 0x01) { + return w.signHash(account, crypto.Keccak256(data)) + } + + // dispatch to 712 signing if the mimetype is TypedData and the format matches + w.stateLock.RLock() // Comms have own mutex, this is for the state fields + defer w.stateLock.RUnlock() + + // If the wallet is closed, abort + if w.device == nil { + return nil, accounts.ErrWalletClosed + } + // Make sure the requested account is contained within + path, ok := w.paths[account.Address] + if !ok { + return nil, accounts.ErrUnknownAccount + } + // All infos gathered and metadata checks out, request signing + <-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() + }() + // Sign the transaction + signature, err := w.driver.SignTypedMessage(path, data[2:34], data[34:66]) + if err != nil { + return nil, err + } + return signature, nil } // SignDataWithPassphrase implements accounts.Wallet, attempting to sign the given diff --git a/build/aleth-interpreter.sh b/build/aleth-interpreter.sh index c8e653817c..1a61d155d1 100755 --- a/build/aleth-interpreter.sh +++ b/build/aleth-interpreter.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e diff --git a/build/checksums.txt b/build/checksums.txt index 32b376519f..d5bd4d0cd3 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -1,13 +1,17 @@ # This file contains sha256 checksums of optional build dependencies. -c1076b90cf94b73ebed62a81d802cd84d43d02dea8c07abdc922c57a071c84f1 go1.15.5.src.tar.gz -359a4334b8c8f5e3067e5a76f16419791ac3fef4613d8e8e1eac0b9719915f6d go1.15.5.darwin-amd64.tar.gz -4c8179d406136979724c71732009c7e2e7c794dbeaaa2a043c00da34d4be0559 go1.15.5.linux-386.tar.gz -9a58494e8da722c3aef248c9227b0e9c528c7318309827780f16220998180a0d go1.15.5.linux-amd64.tar.gz -a72a0b036beb4193a0214bca3fca4c5d68a38a4ccf098c909f7ce8bf08567c48 go1.15.5.linux-arm64.tar.gz -5ea6456620d3efed5dda99238c7f23866eafdd915e5348736e631bc283c0238a go1.15.5.linux-armv6l.tar.gz -d812436c7e3482ba3c97172edf26afaf35aca60a5621ff4a5f6a08386505ab9c go1.15.5.windows-386.zip -1d24be3a200201a74be25e4134fbec467750e834e84e9c7789a9fc13248c5507 go1.15.5.windows-amd64.zip +7688063d55656105898f323d90a79a39c378d86fe89ae192eb3b7fc46347c95a go1.16.src.tar.gz +6000a9522975d116bf76044967d7e69e04e982e9625330d9a539a8b45395f9a8 go1.16.darwin-amd64.tar.gz +ea435a1ac6d497b03e367fdfb74b33e961d813883468080f6e239b3b03bea6aa go1.16.linux-386.tar.gz +013a489ebb3e24ef3d915abe5b94c3286c070dfe0818d5bca8108f1d6e8440d2 go1.16.linux-amd64.tar.gz +3770f7eb22d05e25fbee8fb53c2a4e897da043eb83c69b9a14f8d98562cd8098 go1.16.linux-arm64.tar.gz +d1d9404b1dbd77afa2bdc70934e10fbfcf7d785c372efc29462bb7d83d0a32fd go1.16.linux-armv6l.tar.gz +481492a17d42193d471b93b7a06da3555331bd833b76336afc87be820c48933f go1.16.windows-386.zip +5cc88fa506b3d5c453c54c3ea218fc8dd05d7362ae1de15bb67986b72089ce93 go1.16.windows-amd64.zip +d7d6c70b05a7c2f68b48aab5ab8cb5116b8444c9ddad131673b152e7cff7c726 go1.16.freebsd-386.tar.gz +40b03216f6945fb6883a50604fc7f409a83f62171607229a9c598e701e684f8a go1.16.freebsd-amd64.tar.gz +27a1aaa988e930b7932ce459c8a63ad5b3333b3a06b016d87ff289f2a11aacd6 go1.16.linux-ppc64le.tar.gz +be4c9e4e2cf058efc4e3eb013a760cb989ddc4362f111950c990d1c63b27ccbe go1.16.linux-s390x.tar.gz d998a84eea42f2271aca792a7b027ca5c1edfcba229e8e5a844c9ac3f336df35 golangci-lint-1.27.0-linux-armv7.tar.gz bf781f05b0d393b4bf0a327d9e62926949a4f14d7774d950c4e009fc766ed1d4 golangci-lint.exe-1.27.0-windows-amd64.zip diff --git a/build/ci.go b/build/ci.go index 099660e26a..a3a4c3afaf 100644 --- a/build/ci.go +++ b/build/ci.go @@ -26,7 +26,7 @@ Available commands are: install [ -arch architecture ] [ -cc compiler ] [ packages... ] -- builds packages and executables test [ -coverage ] [ packages... ] -- runs the tests lint -- runs certain pre-selected linters - archive [ -arch architecture ] [ -type zip|tar ] [ -signer key-envvar ] [ -upload dest ] -- archives build artifacts + archive [ -arch architecture ] [ -type zip|tar ] [ -signer key-envvar ] [ -signify key-envvar ] [ -upload dest ] -- archives build artifacts importkeys -- imports signing keys from env debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package nsis -- creates a Windows NSIS installer @@ -58,6 +58,7 @@ import ( "time" "github.com/cespare/cp" + "github.com/ethereum/go-ethereum/crypto/signify" "github.com/ethereum/go-ethereum/internal/build" "github.com/ethereum/go-ethereum/params" ) @@ -74,7 +75,6 @@ var ( "COPYING", executablePath("abigen"), executablePath("bootnode"), - executablePath("echaindb"), executablePath("echainspec"), executablePath("evm"), executablePath("geth"), @@ -92,10 +92,6 @@ var ( BinaryName: "bootnode", Description: "Ethereum bootnode.", }, - { - BinaryName: "echaindb", - Description: "Developer utility to inspect and manage the chain database", - }, { BinaryName: "echainspec", Description: "Developer utility to manage chain external chain configuration", @@ -119,7 +115,6 @@ var ( } // A debian package is created for all executables listed here. - debEthereum = debPackage{ Name: "ethereum", Version: params.Version, @@ -141,11 +136,12 @@ var ( // Note: disco is unsupported because it was officially deprecated on Launchpad. // Note: eoan is unsupported because it was officially deprecated on Launchpad. debDistroGoBoots = map[string]string{ - "trusty": "golang-1.11", - "xenial": "golang-go", - "bionic": "golang-go", - "focal": "golang-go", - "groovy": "golang-go", + "trusty": "golang-1.11", + "xenial": "golang-go", + "bionic": "golang-go", + "focal": "golang-go", + "groovy": "golang-go", + "hirsute": "golang-go", } debGoBootPaths = map[string]string{ @@ -156,7 +152,7 @@ var ( // This is the version of go that will be downloaded by // // go run ci.go install -dlgo - dlgoVersion = "1.15.5" + dlgoVersion = "1.16" ) var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin")) @@ -265,10 +261,10 @@ func doInstall(cmdline []string) { gobuild.Args = append(gobuild.Args, buildFlags(env)...) /* - TODO(meowsbits): The -trimpath flag is commented because it breaks openrpc discovery, for which - reflection/AST-parsing gets broken when paths are not full. - Is there a better solve for this? Can we just turn reflection off for the geth build? - */ + TODO(meowsbits): The -trimpath flag is commented because it breaks openrpc discovery, for which + reflection/AST-parsing gets broken when paths are not full. + Is there a better solve for this? Can we just turn reflection off for the geth build? + */ // We use -trimpath to avoid leaking local paths into the built executables. // gobuild.Args = append(gobuild.Args, "-trimpath") @@ -345,6 +341,7 @@ func goToolSetEnv(cmd *exec.Cmd) { func doTest(cmdline []string) { coverage := flag.Bool("coverage", false, "Whether to record code coverage") verbose := flag.Bool("v", false, "Whether to log verbosely") + timeout := flag.String("timeout", "", "Timeout limit") flag.CommandLine.Parse(cmdline) env := build.Env() @@ -364,6 +361,9 @@ func doTest(cmdline []string) { if *verbose { gotest.Args = append(gotest.Args, "-v") } + if *timeout != "" { + gotest.Args = append(gotest.Args, "-timeout", *timeout) + } gotest.Args = append(gotest.Args, packages...) build.MustRun(gotest) @@ -406,11 +406,12 @@ func downloadLinter(cachedir string) string { // Release Packaging func doArchive(cmdline []string) { var ( - arch = flag.String("arch", runtime.GOARCH, "Architecture cross packaging") - atype = flag.String("type", "zip", "Type of archive to write (zip|tar)") - signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. LINUX_SIGNING_KEY)`) - upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`) - ext string + arch = flag.String("arch", runtime.GOARCH, "Architecture cross packaging") + atype = flag.String("type", "zip", "Type of archive to write (zip|tar)") + signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. LINUX_SIGNING_KEY)`) + signify = flag.String("signify", "", `Environment variable holding the signify key (e.g. LINUX_SIGNIFY_KEY)`) + upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`) + ext string ) flag.CommandLine.Parse(cmdline) switch *atype { @@ -437,7 +438,7 @@ func doArchive(cmdline []string) { log.Fatal(err) } for _, archive := range []string{geth, alltools} { - if err := archiveUpload(archive, *upload, *signer); err != nil { + if err := archiveUpload(archive, *upload, *signer, *signify); err != nil { log.Fatal(err) } } @@ -457,7 +458,7 @@ func archiveBasename(arch string, archiveVersion string) string { return platform + "-" + archiveVersion } -func archiveUpload(archive string, blobstore string, signer string) error { +func archiveUpload(archive string, blobstore string, signer string, signifyVar string) error { // If signing was requested, generate the signature files if signer != "" { key := getenvBase64(signer) @@ -465,6 +466,14 @@ func archiveUpload(archive string, blobstore string, signer string) error { return err } } + if signifyVar != "" { + key := os.Getenv(signifyVar) + untrustedComment := "verify with geth-release.pub" + trustedComment := fmt.Sprintf("%s (%s)", archive, time.Now().UTC().Format(time.RFC1123)) + if err := signify.SignFile(archive, archive+".sig", key, untrustedComment, trustedComment); err != nil { + return err + } + } // If uploading to Azure was requested, push the archive possibly with its signature if blobstore != "" { auth := build.AzureBlobstoreConfig{ @@ -480,6 +489,11 @@ func archiveUpload(archive string, blobstore string, signer string) error { return err } } + if signifyVar != "" { + if err := build.AzureBlobstoreUpload(archive+".sig", filepath.Base(archive+".sig"), auth); err != nil { + return err + } + } } return nil } @@ -559,16 +573,17 @@ func doDebianSource(cmdline []string) { build.MustRun(debuild) var ( - basename = fmt.Sprintf("%s_%s", meta.Name(), meta.VersionString()) - source = filepath.Join(*workdir, basename+".tar.xz") - dsc = filepath.Join(*workdir, basename+".dsc") - changes = filepath.Join(*workdir, basename+"_source.changes") + basename = fmt.Sprintf("%s_%s", meta.Name(), meta.VersionString()) + source = filepath.Join(*workdir, basename+".tar.xz") + dsc = filepath.Join(*workdir, basename+".dsc") + changes = filepath.Join(*workdir, basename+"_source.changes") + buildinfo = filepath.Join(*workdir, basename+"_source.buildinfo") ) if *signer != "" { build.MustRunCommand("debsign", changes) } if *upload != "" { - ppaUpload(*workdir, *upload, *sshUser, []string{source, dsc, changes}) + ppaUpload(*workdir, *upload, *sshUser, []string{source, dsc, changes, buildinfo}) } } } @@ -816,6 +831,7 @@ func doWindowsInstaller(cmdline []string) { var ( arch = flag.String("arch", runtime.GOARCH, "Architecture for cross build packaging") signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. WINDOWS_SIGNING_KEY)`) + signify = flag.String("signify key", "", `Environment variable holding the signify signing key (e.g. WINDOWS_SIGNIFY_KEY)`) upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`) workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`) ) @@ -877,7 +893,7 @@ func doWindowsInstaller(cmdline []string) { filepath.Join(*workdir, "geth.nsi"), ) // Sign and publish installer. - if err := archiveUpload(installer, *upload, *signer); err != nil { + if err := archiveUpload(installer, *upload, *signer, *signify); err != nil { log.Fatal(err) } } @@ -886,10 +902,11 @@ func doWindowsInstaller(cmdline []string) { func doAndroidArchive(cmdline []string) { var ( - local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`) - signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`) - deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org")`) - upload = flag.String("upload", "", `Destination to upload the archive (usually "gethstore/builds")`) + local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`) + signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`) + signify = flag.String("signify", "", `Environment variable holding the signify signing key (e.g. ANDROID_SIGNIFY_KEY)`) + deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org")`) + upload = flag.String("upload", "", `Destination to upload the archive (usually "gethstore/builds")`) ) flag.CommandLine.Parse(cmdline) env := build.Env() @@ -918,7 +935,7 @@ func doAndroidArchive(cmdline []string) { archive := "geth-" + archiveBasename("android", params.ArchiveVersion(env.Commit)) + ".aar" os.Rename("geth.aar", archive) - if err := archiveUpload(archive, *upload, *signer); err != nil { + if err := archiveUpload(archive, *upload, *signer, *signify); err != nil { log.Fatal(err) } // Sign and upload all the artifacts to Maven Central @@ -1011,10 +1028,11 @@ func newMavenMetadata(env build.Environment) mavenMetadata { func doXCodeFramework(cmdline []string) { var ( - local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`) - signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. IOS_SIGNING_KEY)`) - deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "trunk")`) - upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`) + local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`) + signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. IOS_SIGNING_KEY)`) + signify = flag.String("signify", "", `Environment variable holding the signify signing key (e.g. IOS_SIGNIFY_KEY)`) + deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "trunk")`) + upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`) ) flag.CommandLine.Parse(cmdline) env := build.Env() @@ -1042,7 +1060,7 @@ func doXCodeFramework(cmdline []string) { maybeSkipArchive(env) // Sign and upload the framework to Azure - if err := archiveUpload(archive+".tar.gz", *upload, *signer); err != nil { + if err := archiveUpload(archive+".tar.gz", *upload, *signer, *signify); err != nil { log.Fatal(err) } // Prepare and upload a PodSpec to CocoaPods diff --git a/build/evmone.sh b/build/evmone.sh index 760ccb9309..0aea9cddc4 100755 --- a/build/evmone.sh +++ b/build/evmone.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e diff --git a/build/hera.sh b/build/hera.sh index 7fb63be403..e21e6aca2f 100755 --- a/build/hera.sh +++ b/build/hera.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e diff --git a/build/ssvm.sh b/build/ssvm.sh index 9872c59903..949f2cc1cd 100755 --- a/build/ssvm.sh +++ b/build/ssvm.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index a74b0396d4..7b3b35e4e5 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -96,7 +96,7 @@ var ( } aliasFlag = cli.StringFlag{ Name: "alias", - Usage: "Comma separated aliases for function and event renaming, e.g. foo=bar", + Usage: "Comma separated aliases for function and event renaming, e.g. original1=alias1, original2=alias2", } ) diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go index 6c9ff615a1..036b968ef8 100644 --- a/cmd/bootnode/main.go +++ b/cmd/bootnode/main.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/netutil" @@ -121,17 +120,17 @@ func main() { printNotice(&nodeKey.PublicKey, *realaddr) + db, _ := enode.OpenDB("") + ln := enode.NewLocalNode(db, nodeKey) + cfg := discover.Config{ + PrivateKey: nodeKey, + NetRestrict: restrictList, + } if *runv5 { - if _, err := discv5.ListenUDP(nodeKey, conn, "", restrictList); err != nil { + if _, err := discover.ListenV5(conn, ln, cfg); err != nil { utils.Fatalf("%v", err) } } else { - db, _ := enode.OpenDB("") - ln := enode.NewLocalNode(db, nodeKey) - cfg := discover.Config{ - PrivateKey: nodeKey, - NetRestrict: restrictList, - } if _, err := discover.ListenUDP(conn, ln, cfg); err != nil { utils.Fatalf("%v", err) } diff --git a/cmd/clef/README.md b/cmd/clef/README.md index 380d1b702c..9bbbd1d63c 100644 --- a/cmd/clef/README.md +++ b/cmd/clef/README.md @@ -919,4 +919,4 @@ There are a couple of implementation for a UI. We'll try to keep this list up to | QtSigner| https://github.com/holiman/qtsigner/| Python3/QT-based| :+1:| :+1:| :+1:| :+1:| :+1:| :x: | :+1: (partially)| | GtkSigner| https://github.com/holiman/gtksigner| Python3/GTK-based| :+1:| :x:| :x:| :+1:| :+1:| :x: | :x: | | Frame | https://github.com/floating/frame/commits/go-signer| Electron-based| :x:| :x:| :x:| :x:| ?| :x: | :x: | -| Clef UI| https://github.com/kyokan/clef-ui| Golang/QT-based| :+1:| :+1:| :x:| :+1:| :+1:| :x: | :+1: (approve tx only)| +| Clef UI| https://github.com/ethereum/clef-ui| Golang/QT-based| :+1:| :+1:| :x:| :+1:| :+1:| :x: | :+1: (approve tx only)| diff --git a/cmd/clef/main.go b/cmd/clef/main.go index a6fd358f1b..9714ae179f 100644 --- a/cmd/clef/main.go +++ b/cmd/clef/main.go @@ -55,7 +55,7 @@ import ( "github.com/ethereum/go-ethereum/signer/rules" "github.com/ethereum/go-ethereum/signer/storage" - colorable "github.com/mattn/go-colorable" + "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "gopkg.in/urfave/cli.v1" ) @@ -108,11 +108,6 @@ var ( Usage: "HTTP-RPC server listening port", Value: node.DefaultHTTPPort + 5, } - legacyRPCPortFlag = cli.IntFlag{ - Name: "rpcport", - Usage: "HTTP-RPC server listening port (Deprecated, please use --http.port).", - Value: node.DefaultHTTPPort + 5, - } signerSecretFlag = cli.StringFlag{ Name: "signersecret", Usage: "A file containing the (encrypted) master seed to encrypt Clef data, e.g. keystore credentials and ruleset hash", @@ -251,12 +246,6 @@ var AppHelpFlagGroups = []flags.FlagGroup{ acceptFlag, }, }, - { - Name: "ALIASED (deprecated)", - Flags: []cli.Flag{ - legacyRPCPortFlag, - }, - }, } func init() { @@ -284,7 +273,6 @@ func init() { testFlag, advancedMode, acceptFlag, - legacyRPCPortFlag, } app.Action = signer app.Commands = []cli.Command{initCommand, @@ -678,12 +666,6 @@ func signer(c *cli.Context) error { // set port port := c.Int(rpcPortFlag.Name) - if c.GlobalIsSet(legacyRPCPortFlag.Name) { - if !c.GlobalIsSet(rpcPortFlag.Name) { - port = c.Int(legacyRPCPortFlag.Name) - } - log.Warn("The flag --rpcport is deprecated and will be removed in the future, please use --http.port") - } // start http server httpEndpoint := fmt.Sprintf("%s:%d", c.GlobalString(utils.HTTPListenAddrFlag.Name), port) @@ -748,12 +730,10 @@ func DefaultConfigDir() string { appdata := os.Getenv("APPDATA") if appdata != "" { return filepath.Join(appdata, "Signer") - } else { - return filepath.Join(home, "AppData", "Roaming", "Signer") } - } else { - return filepath.Join(home, ".clef") + return filepath.Join(home, "AppData", "Roaming", "Signer") } + return filepath.Join(home, ".clef") } // As we cannot guess a stable location, return empty and handle later return "" @@ -808,14 +788,16 @@ func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) { // checkFile is a convenience function to check if a file // * exists -// * is mode 0400 +// * is mode 0400 (unix only) func checkFile(filename string) error { info, err := os.Stat(filename) if err != nil { return fmt.Errorf("failed stat on %s: %v", filename, err) } // Check the unix permission bits - if info.Mode().Perm()&0377 != 0 { + // However, on windows, we cannot use the unix perm-bits, see + // https://github.com/ethereum/go-ethereum/issues/20123 + if runtime.GOOS != "windows" && info.Mode().Perm()&0377 != 0 { return fmt.Errorf("file (%v) has insecure file permissions (%v)", filename, info.Mode().String()) } return nil @@ -1125,7 +1107,7 @@ func GenDoc(ctx *cli.Context) { rlpdata := common.FromHex("0xf85d640101948a8eafb1cf62bfbeb1741769dae1a9dd47996192018026a0716bd90515acb1e68e5ac5867aa11a1e65399c3349d479f5fb698554ebc6f293a04e8a4ebfff434e971e0ef12c5bf3a881b06fd04fc3f8b8a7291fb67a26a1d4ed") var tx types.Transaction - rlp.DecodeBytes(rlpdata, &tx) + tx.UnmarshalBinary(rlpdata) add("OnApproved - SignTransactionResult", desc, ðapi.SignTransactionResult{Raw: rlpdata, Tx: &tx}) } diff --git a/cmd/clef/sign_flow.png b/cmd/clef/sign_flow.png index 93ef81a32e..e7010ab43f 100644 Binary files a/cmd/clef/sign_flow.png and b/cmd/clef/sign_flow.png differ diff --git a/cmd/devp2p/README.md b/cmd/devp2p/README.md index e1372d0158..e934ee25c9 100644 --- a/cmd/devp2p/README.md +++ b/cmd/devp2p/README.md @@ -94,11 +94,23 @@ To run the eth protocol test suite against your implementation, the node needs t geth --datadir --nodiscover --nat=none --networkid 19763 --verbosity 5 ``` -Then, run the following command, replacing `` with the enode of the geth node: +Then, run the following command, replacing `` with the enode of the geth node: ``` - devp2p rlpx eth-test cmd/devp2p/internal/ethtest/testdata/fullchain.rlp cmd/devp2p/internal/ethtest/testdata/genesis.json + devp2p rlpx eth-test cmd/devp2p/internal/ethtest/testdata/chain.rlp cmd/devp2p/internal/ethtest/testdata/genesis.json ``` - + +Repeat the above process (re-initialising the node) in order to run the Eth Protocol test suite again. + +#### Eth66 Test Suite + +The Eth66 test suite is also a conformance test suite for the eth 66 protocol version specifically. +To run the eth66 protocol test suite, initialize a geth node as described above and run the following command, +replacing `` with the enode of the geth node: + + ``` + devp2p rlpx eth66-test cmd/devp2p/internal/ethtest/testdata/chain.rlp cmd/devp2p/internal/ethtest/testdata/genesis.json +``` + [eth]: https://github.com/ethereum/devp2p/blob/master/caps/eth.md [dns-tutorial]: https://geth.ethereum.org/docs/developers/dns-discovery-setup [discv4]: https://github.com/ethereum/devp2p/tree/master/discv4.md diff --git a/cmd/devp2p/dns_cloudflare.go b/cmd/devp2p/dns_cloudflare.go index a4d10dcfdd..596254df91 100644 --- a/cmd/devp2p/dns_cloudflare.go +++ b/cmd/devp2p/dns_cloudflare.go @@ -17,6 +17,7 @@ package main import ( + "context" "fmt" "strings" @@ -79,7 +80,7 @@ func (c *cloudflareClient) checkZone(name string) error { c.zoneID = id } log.Info(fmt.Sprintf("Checking Permissions on zone %s", c.zoneID)) - zone, err := c.ZoneDetails(c.zoneID) + zone, err := c.ZoneDetails(context.Background(), c.zoneID) if err != nil { return err } @@ -112,7 +113,7 @@ func (c *cloudflareClient) uploadRecords(name string, records map[string]string) records = lrecords log.Info(fmt.Sprintf("Retrieving existing TXT records on %s", name)) - entries, err := c.DNSRecords(c.zoneID, cloudflare.DNSRecord{Type: "TXT"}) + entries, err := c.DNSRecords(context.Background(), c.zoneID, cloudflare.DNSRecord{Type: "TXT"}) if err != nil { return err } @@ -134,14 +135,15 @@ func (c *cloudflareClient) uploadRecords(name string, records map[string]string) if path != name { ttl = treeNodeTTL // Max TTL permitted by Cloudflare } - _, err = c.CreateDNSRecord(c.zoneID, cloudflare.DNSRecord{Type: "TXT", Name: path, Content: val, TTL: ttl}) + record := cloudflare.DNSRecord{Type: "TXT", Name: path, Content: val, TTL: ttl} + _, err = c.CreateDNSRecord(context.Background(), c.zoneID, record) } else if old.Content != val { // Entry already exists, only change its content. log.Info(fmt.Sprintf("Updating %s from %q to %q", path, old.Content, val)) old.Content = val - err = c.UpdateDNSRecord(c.zoneID, old.ID, old) + err = c.UpdateDNSRecord(context.Background(), c.zoneID, old.ID, old) } else { - log.Info(fmt.Sprintf("Skipping %s = %q", path, val)) + log.Debug(fmt.Sprintf("Skipping %s = %q", path, val)) } if err != nil { return fmt.Errorf("failed to publish %s: %v", path, err) @@ -155,7 +157,7 @@ func (c *cloudflareClient) uploadRecords(name string, records map[string]string) } // Stale entry, nuke it. log.Info(fmt.Sprintf("Deleting %s = %q", path, entry.Content)) - if err := c.DeleteDNSRecord(c.zoneID, entry.ID); err != nil { + if err := c.DeleteDNSRecord(context.Background(), c.zoneID, entry.ID); err != nil { return fmt.Errorf("failed to delete %s: %v", path, err) } } diff --git a/cmd/devp2p/dns_route53.go b/cmd/devp2p/dns_route53.go index c5f99529b8..2b6a30e60f 100644 --- a/cmd/devp2p/dns_route53.go +++ b/cmd/devp2p/dns_route53.go @@ -17,16 +17,19 @@ package main import ( + "context" "errors" "fmt" "sort" "strconv" "strings" + "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/route53" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/service/route53" + "github.com/aws/aws-sdk-go-v2/service/route53/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/dnsdisc" "gopkg.in/urfave/cli.v1" @@ -38,6 +41,7 @@ const ( // https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests-changeresourcerecordsets route53ChangeSizeLimit = 32000 route53ChangeCountLimit = 1000 + maxRetryLimit = 60 ) var ( @@ -55,10 +59,15 @@ var ( Name: "zone-id", Usage: "Route53 Zone ID", } + route53RegionFlag = cli.StringFlag{ + Name: "aws-region", + Usage: "AWS Region", + Value: "eu-central-1", + } ) type route53Client struct { - api *route53.Route53 + api *route53.Client zoneID string } @@ -72,15 +81,16 @@ func newRoute53Client(ctx *cli.Context) *route53Client { akey := ctx.String(route53AccessKeyFlag.Name) asec := ctx.String(route53AccessSecretFlag.Name) if akey == "" || asec == "" { - exit(fmt.Errorf("need Route53 Access Key ID and secret proceed")) + exit(fmt.Errorf("need Route53 Access Key ID and secret to proceed")) } - config := &aws.Config{Credentials: credentials.NewStaticCredentials(akey, asec, "")} - session, err := session.NewSession(config) + creds := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(akey, asec, "")) + cfg, err := config.LoadDefaultConfig(context.Background(), config.WithCredentialsProvider(creds)) if err != nil { - exit(fmt.Errorf("can't create AWS session: %v", err)) + exit(fmt.Errorf("can't initialize AWS configuration: %v", err)) } + cfg.Region = ctx.String(route53RegionFlag.Name) return &route53Client{ - api: route53.New(session), + api: route53.NewFromConfig(cfg), zoneID: ctx.String(route53ZoneIDFlag.Name), } } @@ -105,25 +115,43 @@ func (c *route53Client) deploy(name string, t *dnsdisc.Tree) error { return nil } - // Submit change batches. + // Submit all change batches. batches := splitChanges(changes, route53ChangeSizeLimit, route53ChangeCountLimit) + changesToCheck := make([]*route53.ChangeResourceRecordSetsOutput, len(batches)) for i, changes := range batches { log.Info(fmt.Sprintf("Submitting %d changes to Route53", len(changes))) - batch := new(route53.ChangeBatch) - batch.SetChanges(changes) - batch.SetComment(fmt.Sprintf("enrtree update %d/%d of %s at seq %d", i+1, len(batches), name, t.Seq())) + batch := &types.ChangeBatch{ + Changes: changes, + Comment: aws.String(fmt.Sprintf("enrtree update %d/%d of %s at seq %d", i+1, len(batches), name, t.Seq())), + } req := &route53.ChangeResourceRecordSetsInput{HostedZoneId: &c.zoneID, ChangeBatch: batch} - resp, err := c.api.ChangeResourceRecordSets(req) + changesToCheck[i], err = c.api.ChangeResourceRecordSets(context.TODO(), req) if err != nil { return err } + } - log.Info(fmt.Sprintf("Waiting for change request %s", *resp.ChangeInfo.Id)) - wreq := &route53.GetChangeInput{Id: resp.ChangeInfo.Id} - if err := c.api.WaitUntilResourceRecordSetsChanged(wreq); err != nil { - return err + // Wait for all change batches to propagate. + for _, change := range changesToCheck { + log.Info(fmt.Sprintf("Waiting for change request %s", *change.ChangeInfo.Id)) + wreq := &route53.GetChangeInput{Id: change.ChangeInfo.Id} + var count int + for { + wresp, err := c.api.GetChange(context.TODO(), wreq) + if err != nil { + return err + } + + count++ + + if wresp.ChangeInfo.Status == types.ChangeStatusInsync || count >= maxRetryLimit { + break + } + + time.Sleep(30 * time.Second) } } + return nil } @@ -140,7 +168,7 @@ func (c *route53Client) findZoneID(name string) (string, error) { log.Info(fmt.Sprintf("Finding Route53 Zone ID for %s", name)) var req route53.ListHostedZonesByNameInput for { - resp, err := c.api.ListHostedZonesByName(&req) + resp, err := c.api.ListHostedZonesByName(context.TODO(), &req) if err != nil { return "", err } @@ -149,7 +177,7 @@ func (c *route53Client) findZoneID(name string) (string, error) { return *zone.Id, nil } } - if !*resp.IsTruncated { + if !resp.IsTruncated { break } req.DNSName = resp.NextDNSName @@ -159,7 +187,7 @@ func (c *route53Client) findZoneID(name string) (string, error) { } // computeChanges creates DNS changes for the given record. -func (c *route53Client) computeChanges(name string, records map[string]string, existing map[string]recordSet) []*route53.Change { +func (c *route53Client) computeChanges(name string, records map[string]string, existing map[string]recordSet) []types.Change { // Convert all names to lowercase. lrecords := make(map[string]string, len(records)) for name, r := range records { @@ -167,25 +195,30 @@ func (c *route53Client) computeChanges(name string, records map[string]string, e } records = lrecords - var changes []*route53.Change - for path, val := range records { + var changes []types.Change + for path, newValue := range records { + prevRecords, exists := existing[path] + prevValue := strings.Join(prevRecords.values, "") + + // prevValue contains quoted strings, encode newValue to compare. + newValue = splitTXT(newValue) + + // Assign TTL. ttl := int64(rootTTL) if path != name { ttl = int64(treeNodeTTL) } - prevRecords, exists := existing[path] - prevValue := strings.Join(prevRecords.values, "") if !exists { // Entry is unknown, push a new one - log.Info(fmt.Sprintf("Creating %s = %q", path, val)) - changes = append(changes, newTXTChange("CREATE", path, ttl, splitTXT(val))) - } else if prevValue != val || prevRecords.ttl != ttl { + log.Info(fmt.Sprintf("Creating %s = %s", path, newValue)) + changes = append(changes, newTXTChange("CREATE", path, ttl, newValue)) + } else if prevValue != newValue || prevRecords.ttl != ttl { // Entry already exists, only change its content. - log.Info(fmt.Sprintf("Updating %s from %q to %q", path, prevValue, val)) - changes = append(changes, newTXTChange("UPSERT", path, ttl, splitTXT(val))) + log.Info(fmt.Sprintf("Updating %s from %s to %s", path, prevValue, newValue)) + changes = append(changes, newTXTChange("UPSERT", path, ttl, newValue)) } else { - log.Info(fmt.Sprintf("Skipping %s = %q", path, val)) + log.Debug(fmt.Sprintf("Skipping %s = %s", path, newValue)) } } @@ -204,21 +237,21 @@ func (c *route53Client) computeChanges(name string, records map[string]string, e } // sortChanges ensures DNS changes are in leaf-added -> root-changed -> leaf-deleted order. -func sortChanges(changes []*route53.Change) { +func sortChanges(changes []types.Change) { score := map[string]int{"CREATE": 1, "UPSERT": 2, "DELETE": 3} sort.Slice(changes, func(i, j int) bool { - if *changes[i].Action == *changes[j].Action { + if changes[i].Action == changes[j].Action { return *changes[i].ResourceRecordSet.Name < *changes[j].ResourceRecordSet.Name } - return score[*changes[i].Action] < score[*changes[j].Action] + return score[string(changes[i].Action)] < score[string(changes[j].Action)] }) } // splitChanges splits up DNS changes such that each change batch // is smaller than the given RDATA limit. -func splitChanges(changes []*route53.Change, sizeLimit, countLimit int) [][]*route53.Change { +func splitChanges(changes []types.Change, sizeLimit, countLimit int) [][]types.Change { var ( - batches [][]*route53.Change + batches [][]types.Change batchSize int batchCount int ) @@ -241,7 +274,7 @@ func splitChanges(changes []*route53.Change, sizeLimit, countLimit int) [][]*rou } // changeSize returns the RDATA size of a DNS change. -func changeSize(ch *route53.Change) int { +func changeSize(ch types.Change) int { size := 0 for _, rr := range ch.ResourceRecordSet.ResourceRecords { if rr.Value != nil { @@ -251,8 +284,8 @@ func changeSize(ch *route53.Change) int { return size } -func changeCount(ch *route53.Change) int { - if *ch.Action == "UPSERT" { +func changeCount(ch types.Change) int { + if ch.Action == types.ChangeActionUpsert { return 2 } return 1 @@ -260,13 +293,17 @@ func changeCount(ch *route53.Change) int { // collectRecords collects all TXT records below the given name. func (c *route53Client) collectRecords(name string) (map[string]recordSet, error) { - log.Info(fmt.Sprintf("Retrieving existing TXT records on %s (%s)", name, c.zoneID)) var req route53.ListResourceRecordSetsInput - req.SetHostedZoneId(c.zoneID) + req.HostedZoneId = &c.zoneID existing := make(map[string]recordSet) - err := c.api.ListResourceRecordSetsPages(&req, func(resp *route53.ListResourceRecordSetsOutput, last bool) bool { + for page := 0; ; page++ { + log.Info("Loading existing TXT records", "name", name, "zone", c.zoneID, "page", page) + resp, err := c.api.ListResourceRecordSets(context.TODO(), &req) + if err != nil { + return existing, err + } for _, set := range resp.ResourceRecordSets { - if !isSubdomain(*set.Name, name) || *set.Type != "TXT" { + if !isSubdomain(*set.Name, name) || set.Type != types.RRTypeTxt { continue } s := recordSet{ttl: *set.TTL} @@ -276,28 +313,44 @@ func (c *route53Client) collectRecords(name string) (map[string]recordSet, error name := strings.TrimSuffix(*set.Name, ".") existing[name] = s } - return true - }) - return existing, err + + if !resp.IsTruncated { + break + } + // Set the cursor to the next batch. From the AWS docs: + // + // To display the next page of results, get the values of NextRecordName, + // NextRecordType, and NextRecordIdentifier (if any) from the response. Then submit + // another ListResourceRecordSets request, and specify those values for + // StartRecordName, StartRecordType, and StartRecordIdentifier. + req.StartRecordIdentifier = resp.NextRecordIdentifier + req.StartRecordName = resp.NextRecordName + req.StartRecordType = resp.NextRecordType + } + + return existing, nil } // newTXTChange creates a change to a TXT record. -func newTXTChange(action, name string, ttl int64, values ...string) *route53.Change { - var c route53.Change - var r route53.ResourceRecordSet - var rrs []*route53.ResourceRecord +func newTXTChange(action, name string, ttl int64, values ...string) types.Change { + r := types.ResourceRecordSet{ + Type: types.RRTypeTxt, + Name: &name, + TTL: &ttl, + } + var rrs []types.ResourceRecord for _, val := range values { - rr := new(route53.ResourceRecord) - rr.SetValue(val) + var rr types.ResourceRecord + rr.Value = aws.String(val) rrs = append(rrs, rr) } - r.SetType("TXT") - r.SetName(name) - r.SetTTL(ttl) - r.SetResourceRecords(rrs) - c.SetAction(action) - c.SetResourceRecordSet(&r) - return &c + + r.ResourceRecords = rrs + + return types.Change{ + Action: types.ChangeAction(action), + ResourceRecordSet: &r, + } } // isSubdomain returns true if name is a subdomain of domain. diff --git a/cmd/devp2p/dns_route53_test.go b/cmd/devp2p/dns_route53_test.go index a2ef3791f6..e6eb516e6b 100644 --- a/cmd/devp2p/dns_route53_test.go +++ b/cmd/devp2p/dns_route53_test.go @@ -20,7 +20,7 @@ import ( "reflect" "testing" - "github.com/aws/aws-sdk-go/service/route53" + "github.com/aws/aws-sdk-go-v2/service/route53/types" ) // This test checks that computeChanges/splitChanges create DNS changes in @@ -43,93 +43,93 @@ func TestRoute53ChangeSort(t *testing.T) { "MHTDO6TMUBRIA2XWG5LUDACK24.n": "enr:-HW4QLAYqmrwllBEnzWWs7I5Ev2IAs7x_dZlbYdRdMUx5EyKHDXp7AV5CkuPGUPdvbv1_Ms1CPfhcGCvSElSosZmyoqAgmlkgnY0iXNlY3AyNTZrMaECriawHKWdDRk2xeZkrOXBQ0dfMFLHY4eENZwdufn1S1o", } - wantChanges := []*route53.Change{ + wantChanges := []types.Change{ { - Action: sp("CREATE"), - ResourceRecordSet: &route53.ResourceRecordSet{ + Action: "CREATE", + ResourceRecordSet: &types.ResourceRecordSet{ Name: sp("2xs2367yhaxjfglzhvawlqd4zy.n"), - ResourceRecords: []*route53.ResourceRecord{{ + ResourceRecords: []types.ResourceRecord{{ Value: sp(`"enr:-HW4QOFzoVLaFJnNhbgMoDXPnOvcdVuj7pDpqRvh6BRDO68aVi5ZcjB3vzQRZH2IcLBGHzo8uUN3snqmgTiE56CH3AMBgmlkgnY0iXNlY3AyNTZrMaECC2_24YYkYHEgdzxlSNKQEnHhuNAbNlMlWJxrJxbAFvA"`), }}, TTL: ip(treeNodeTTL), - Type: sp("TXT"), + Type: "TXT", }, }, { - Action: sp("CREATE"), - ResourceRecordSet: &route53.ResourceRecordSet{ + Action: "CREATE", + ResourceRecordSet: &types.ResourceRecordSet{ Name: sp("c7hrfpf3blgf3yr4dy5kx3smbe.n"), - ResourceRecords: []*route53.ResourceRecord{{ + ResourceRecords: []types.ResourceRecord{{ Value: sp(`"enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org"`), }}, TTL: ip(treeNodeTTL), - Type: sp("TXT"), + Type: "TXT", }, }, { - Action: sp("CREATE"), - ResourceRecordSet: &route53.ResourceRecordSet{ + Action: "CREATE", + ResourceRecordSet: &types.ResourceRecordSet{ Name: sp("h4fht4b454p6uxfd7jcyq5pwdy.n"), - ResourceRecords: []*route53.ResourceRecord{{ + ResourceRecords: []types.ResourceRecord{{ Value: sp(`"enr:-HW4QAggRauloj2SDLtIHN1XBkvhFZ1vtf1raYQp9TBW2RD5EEawDzbtSmlXUfnaHcvwOizhVYLtr7e6vw7NAf6mTuoCgmlkgnY0iXNlY3AyNTZrMaECjrXI8TLNXU0f8cthpAMxEshUyQlK-AM0PW2wfrnacNI"`), }}, TTL: ip(treeNodeTTL), - Type: sp("TXT"), + Type: "TXT", }, }, { - Action: sp("CREATE"), - ResourceRecordSet: &route53.ResourceRecordSet{ + Action: "CREATE", + ResourceRecordSet: &types.ResourceRecordSet{ Name: sp("jwxydbpxywg6fx3gmdibfa6cj4.n"), - ResourceRecords: []*route53.ResourceRecord{{ + ResourceRecords: []types.ResourceRecord{{ Value: sp(`"enrtree-branch:2XS2367YHAXJFGLZHVAWLQD4ZY,H4FHT4B454P6UXFD7JCYQ5PWDY,MHTDO6TMUBRIA2XWG5LUDACK24"`), }}, TTL: ip(treeNodeTTL), - Type: sp("TXT"), + Type: "TXT", }, }, { - Action: sp("CREATE"), - ResourceRecordSet: &route53.ResourceRecordSet{ + Action: "CREATE", + ResourceRecordSet: &types.ResourceRecordSet{ Name: sp("mhtdo6tmubria2xwg5ludack24.n"), - ResourceRecords: []*route53.ResourceRecord{{ + ResourceRecords: []types.ResourceRecord{{ Value: sp(`"enr:-HW4QLAYqmrwllBEnzWWs7I5Ev2IAs7x_dZlbYdRdMUx5EyKHDXp7AV5CkuPGUPdvbv1_Ms1CPfhcGCvSElSosZmyoqAgmlkgnY0iXNlY3AyNTZrMaECriawHKWdDRk2xeZkrOXBQ0dfMFLHY4eENZwdufn1S1o"`), }}, TTL: ip(treeNodeTTL), - Type: sp("TXT"), + Type: "TXT", }, }, { - Action: sp("UPSERT"), - ResourceRecordSet: &route53.ResourceRecordSet{ + Action: "UPSERT", + ResourceRecordSet: &types.ResourceRecordSet{ Name: sp("n"), - ResourceRecords: []*route53.ResourceRecord{{ + ResourceRecords: []types.ResourceRecord{{ Value: sp(`"enrtree-root:v1 e=JWXYDBPXYWG6FX3GMDIBFA6CJ4 l=C7HRFPF3BLGF3YR4DY5KX3SMBE seq=1 sig=o908WmNp7LibOfPsr4btQwatZJ5URBr2ZAuxvK4UWHlsB9sUOTJQaGAlLPVAhM__XJesCHxLISo94z5Z2a463gA"`), }}, TTL: ip(rootTTL), - Type: sp("TXT"), + Type: "TXT", }, }, { - Action: sp("DELETE"), - ResourceRecordSet: &route53.ResourceRecordSet{ + Action: "DELETE", + ResourceRecordSet: &types.ResourceRecordSet{ Name: sp("2kfjogvxdqtxxugbh7gs7naaai.n"), - ResourceRecords: []*route53.ResourceRecord{ + ResourceRecords: []types.ResourceRecord{ {Value: sp(`"enr:-HW4QO1ml1DdXLeZLsUxewnthhUy8eROqkDyoMTyavfks9JlYQIlMFEUoM78PovJDPQrAkrb3LRJ-""vtrymDguKCOIAWAgmlkgnY0iXNlY3AyNTZrMaEDffaGfJzgGhUif1JqFruZlYmA31HzathLSWxfbq_QoQ4"`)}, }, TTL: ip(3333), - Type: sp("TXT"), + Type: "TXT", }, }, { - Action: sp("DELETE"), - ResourceRecordSet: &route53.ResourceRecordSet{ + Action: "DELETE", + ResourceRecordSet: &types.ResourceRecordSet{ Name: sp("fdxn3sn67na5dka4j2gok7bvqi.n"), - ResourceRecords: []*route53.ResourceRecord{{ + ResourceRecords: []types.ResourceRecord{{ Value: sp(`"enrtree-branch:"`), }}, TTL: ip(treeNodeTTL), - Type: sp("TXT"), + Type: "TXT", }, }, } @@ -141,7 +141,7 @@ func TestRoute53ChangeSort(t *testing.T) { } // Check splitting according to size. - wantSplit := [][]*route53.Change{ + wantSplit := [][]types.Change{ wantChanges[:4], wantChanges[4:6], wantChanges[6:], @@ -152,7 +152,7 @@ func TestRoute53ChangeSort(t *testing.T) { } // Check splitting according to count. - wantSplit = [][]*route53.Change{ + wantSplit = [][]types.Change{ wantChanges[:5], wantChanges[5:], } @@ -162,5 +162,29 @@ func TestRoute53ChangeSort(t *testing.T) { } } +// This test checks that computeChanges compares the quoted value of the records correctly. +func TestRoute53NoChange(t *testing.T) { + // Existing record set. + testTree0 := map[string]recordSet{ + "n": {ttl: rootTTL, values: []string{ + `"enrtree-root:v1 e=JWXYDBPXYWG6FX3GMDIBFA6CJ4 l=C7HRFPF3BLGF3YR4DY5KX3SMBE seq=1 sig=o908WmNp7LibOfPsr4btQwatZJ5URBr2ZAuxvK4UWHlsB9sUOTJQaGAlLPVAhM__XJesCHxLISo94z5Z2a463gA"`, + }}, + "2xs2367yhaxjfglzhvawlqd4zy.n": {ttl: treeNodeTTL, values: []string{ + `"enr:-HW4QOFzoVLaFJnNhbgMoDXPnOvcdVuj7pDpqRvh6BRDO68aVi5ZcjB3vzQRZH2IcLBGHzo8uUN3snqmgTiE56CH3AMBgmlkgnY0iXNlY3AyNTZrMaECC2_24YYkYHEgdzxlSNKQEnHhuNAbNlMlWJxrJxbAFvA"`, + }}, + } + // New set. + testTree1 := map[string]string{ + "n": "enrtree-root:v1 e=JWXYDBPXYWG6FX3GMDIBFA6CJ4 l=C7HRFPF3BLGF3YR4DY5KX3SMBE seq=1 sig=o908WmNp7LibOfPsr4btQwatZJ5URBr2ZAuxvK4UWHlsB9sUOTJQaGAlLPVAhM__XJesCHxLISo94z5Z2a463gA", + "2XS2367YHAXJFGLZHVAWLQD4ZY.n": "enr:-HW4QOFzoVLaFJnNhbgMoDXPnOvcdVuj7pDpqRvh6BRDO68aVi5ZcjB3vzQRZH2IcLBGHzo8uUN3snqmgTiE56CH3AMBgmlkgnY0iXNlY3AyNTZrMaECC2_24YYkYHEgdzxlSNKQEnHhuNAbNlMlWJxrJxbAFvA", + } + + var client route53Client + changes := client.computeChanges("n", testTree1, testTree0) + if len(changes) > 0 { + t.Fatalf("wrong changes (got %d, want 0)", len(changes)) + } +} + func sp(s string) *string { return &s } func ip(i int64) *int64 { return &i } diff --git a/cmd/devp2p/dnscmd.go b/cmd/devp2p/dnscmd.go index 13110f21c6..50ab7bf983 100644 --- a/cmd/devp2p/dnscmd.go +++ b/cmd/devp2p/dnscmd.go @@ -30,7 +30,7 @@ import ( "github.com/ethereum/go-ethereum/console/prompt" "github.com/ethereum/go-ethereum/p2p/dnsdisc" "github.com/ethereum/go-ethereum/p2p/enode" - cli "gopkg.in/urfave/cli.v1" + "gopkg.in/urfave/cli.v1" ) var ( @@ -77,7 +77,12 @@ var ( Usage: "Deploy DNS TXT records to Amazon Route53", ArgsUsage: "", Action: dnsToRoute53, - Flags: []cli.Flag{route53AccessKeyFlag, route53AccessSecretFlag, route53ZoneIDFlag}, + Flags: []cli.Flag{ + route53AccessKeyFlag, + route53AccessSecretFlag, + route53ZoneIDFlag, + route53RegionFlag, + }, } ) diff --git a/cmd/devp2p/internal/ethtest/chain_test.go b/cmd/devp2p/internal/ethtest/chain_test.go index 604b908687..ec98833ab5 100644 --- a/cmd/devp2p/internal/ethtest/chain_test.go +++ b/cmd/devp2p/internal/ethtest/chain_test.go @@ -21,6 +21,7 @@ import ( "strconv" "testing" + "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/p2p" "github.com/stretchr/testify/assert" ) @@ -34,7 +35,31 @@ func TestEthProtocolNegotiation(t *testing.T) { expected uint32 }{ { - conn: &Conn{}, + conn: &Conn{ + ourHighestProtoVersion: 65, + }, + caps: []p2p.Cap{ + {Name: "eth", Version: 63}, + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + }, + expected: uint32(65), + }, + { + conn: &Conn{ + ourHighestProtoVersion: 65, + }, + caps: []p2p.Cap{ + {Name: "eth", Version: 63}, + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + }, + expected: uint32(65), + }, + { + conn: &Conn{ + ourHighestProtoVersion: 65, + }, caps: []p2p.Cap{ {Name: "eth", Version: 63}, {Name: "eth", Version: 64}, @@ -43,7 +68,20 @@ func TestEthProtocolNegotiation(t *testing.T) { expected: uint32(65), }, { - conn: &Conn{}, + conn: &Conn{ + ourHighestProtoVersion: 64, + }, + caps: []p2p.Cap{ + {Name: "eth", Version: 63}, + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + }, + expected: 64, + }, + { + conn: &Conn{ + ourHighestProtoVersion: 65, + }, caps: []p2p.Cap{ {Name: "eth", Version: 0}, {Name: "eth", Version: 89}, @@ -52,7 +90,20 @@ func TestEthProtocolNegotiation(t *testing.T) { expected: uint32(65), }, { - conn: &Conn{}, + conn: &Conn{ + ourHighestProtoVersion: 64, + }, + caps: []p2p.Cap{ + {Name: "eth", Version: 63}, + {Name: "eth", Version: 64}, + {Name: "wrongProto", Version: 65}, + }, + expected: uint32(64), + }, + { + conn: &Conn{ + ourHighestProtoVersion: 65, + }, caps: []p2p.Cap{ {Name: "eth", Version: 63}, {Name: "eth", Version: 64}, @@ -65,7 +116,7 @@ func TestEthProtocolNegotiation(t *testing.T) { for i, tt := range tests { t.Run(strconv.Itoa(i), func(t *testing.T) { tt.conn.negotiateEthProtocol(tt.caps) - assert.Equal(t, tt.expected, uint32(tt.conn.ethProtocolVersion)) + assert.Equal(t, tt.expected, uint32(tt.conn.negotiatedProtoVersion)) }) } } @@ -73,7 +124,7 @@ func TestEthProtocolNegotiation(t *testing.T) { // TestChain_GetHeaders tests whether the test suite can correctly // respond to a GetBlockHeaders request from a node. func TestChain_GetHeaders(t *testing.T) { - chainFile, err := filepath.Abs("./testdata/fullchain.rlp.gz") + chainFile, err := filepath.Abs("./testdata/chain.rlp") if err != nil { t.Fatal(err) } @@ -93,7 +144,7 @@ func TestChain_GetHeaders(t *testing.T) { }{ { req: GetBlockHeaders{ - Origin: hashOrNumber{ + Origin: eth.HashOrNumber{ Number: uint64(2), }, Amount: uint64(5), @@ -110,7 +161,7 @@ func TestChain_GetHeaders(t *testing.T) { }, { req: GetBlockHeaders{ - Origin: hashOrNumber{ + Origin: eth.HashOrNumber{ Number: uint64(chain.Len() - 1), }, Amount: uint64(3), @@ -125,7 +176,7 @@ func TestChain_GetHeaders(t *testing.T) { }, { req: GetBlockHeaders{ - Origin: hashOrNumber{ + Origin: eth.HashOrNumber{ Hash: chain.Head().Hash(), }, Amount: uint64(1), diff --git a/cmd/devp2p/internal/ethtest/eth66_suite.go b/cmd/devp2p/internal/ethtest/eth66_suite.go new file mode 100644 index 0000000000..276328f279 --- /dev/null +++ b/cmd/devp2p/internal/ethtest/eth66_suite.go @@ -0,0 +1,396 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethtest + +import ( + "time" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/internal/utesting" + "github.com/ethereum/go-ethereum/p2p" +) + +// Is_66 checks if the node supports the eth66 protocol version, +// and if not, exists the test suite +func (s *Suite) Is_66(t *utesting.T) { + conn := s.dial66(t) + conn.handshake(t) + if conn.negotiatedProtoVersion < 66 { + t.Fail() + } +} + +// TestStatus_66 attempts to connect to the given node and exchange +// a status message with it on the eth66 protocol, and then check to +// make sure the chain head is correct. +func (s *Suite) TestStatus_66(t *utesting.T) { + conn := s.dial66(t) + // get protoHandshake + conn.handshake(t) + // get status + switch msg := conn.statusExchange66(t, s.chain).(type) { + case *Status: + status := *msg + if status.ProtocolVersion != uint32(66) { + t.Fatalf("mismatch in version: wanted 66, got %d", status.ProtocolVersion) + } + t.Logf("got status message: %s", pretty.Sdump(msg)) + default: + t.Fatalf("unexpected: %s", pretty.Sdump(msg)) + } +} + +// TestGetBlockHeaders_66 tests whether the given node can respond to +// an eth66 `GetBlockHeaders` request and that the response is accurate. +func (s *Suite) TestGetBlockHeaders_66(t *utesting.T) { + conn := s.setupConnection66(t) + // get block headers + req := ð.GetBlockHeadersPacket66{ + RequestId: 3, + GetBlockHeadersPacket: ð.GetBlockHeadersPacket{ + Origin: eth.HashOrNumber{ + Hash: s.chain.blocks[1].Hash(), + }, + Amount: 2, + Skip: 1, + Reverse: false, + }, + } + // write message + headers := s.getBlockHeaders66(t, conn, req, req.RequestId) + // check for correct headers + headersMatch(t, s.chain, headers) +} + +// TestSimultaneousRequests_66 sends two simultaneous `GetBlockHeader` requests +// with different request IDs and checks to make sure the node responds with the correct +// headers per request. +func (s *Suite) TestSimultaneousRequests_66(t *utesting.T) { + // create two connections + conn1, conn2 := s.setupConnection66(t), s.setupConnection66(t) + // create two requests + req1 := ð.GetBlockHeadersPacket66{ + RequestId: 111, + GetBlockHeadersPacket: ð.GetBlockHeadersPacket{ + Origin: eth.HashOrNumber{ + Hash: s.chain.blocks[1].Hash(), + }, + Amount: 2, + Skip: 1, + Reverse: false, + }, + } + req2 := ð.GetBlockHeadersPacket66{ + RequestId: 222, + GetBlockHeadersPacket: ð.GetBlockHeadersPacket{ + Origin: eth.HashOrNumber{ + Hash: s.chain.blocks[1].Hash(), + }, + Amount: 4, + Skip: 1, + Reverse: false, + }, + } + // wait for headers for first request + headerChan := make(chan BlockHeaders, 1) + go func(headers chan BlockHeaders) { + headers <- s.getBlockHeaders66(t, conn1, req1, req1.RequestId) + }(headerChan) + // check headers of second request + headersMatch(t, s.chain, s.getBlockHeaders66(t, conn2, req2, req2.RequestId)) + // check headers of first request + headersMatch(t, s.chain, <-headerChan) +} + +// TestBroadcast_66 tests whether a block announcement is correctly +// propagated to the given node's peer(s) on the eth66 protocol. +func (s *Suite) TestBroadcast_66(t *utesting.T) { + sendConn, receiveConn := s.setupConnection66(t), s.setupConnection66(t) + nextBlock := len(s.chain.blocks) + blockAnnouncement := &NewBlock{ + Block: s.fullChain.blocks[nextBlock], + TD: s.fullChain.TD(nextBlock + 1), + } + s.testAnnounce66(t, sendConn, receiveConn, blockAnnouncement) + // update test suite chain + s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock]) + // wait for client to update its chain + if err := receiveConn.waitForBlock66(s.chain.Head()); err != nil { + t.Fatal(err) + } +} + +// TestGetBlockBodies_66 tests whether the given node can respond to +// a `GetBlockBodies` request and that the response is accurate over +// the eth66 protocol. +func (s *Suite) TestGetBlockBodies_66(t *utesting.T) { + conn := s.setupConnection66(t) + // create block bodies request + id := uint64(55) + req := ð.GetBlockBodiesPacket66{ + RequestId: id, + GetBlockBodiesPacket: eth.GetBlockBodiesPacket{ + s.chain.blocks[54].Hash(), + s.chain.blocks[75].Hash(), + }, + } + if err := conn.write66(req, GetBlockBodies{}.Code()); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + + reqID, msg := conn.readAndServe66(s.chain, timeout) + switch msg := msg.(type) { + case BlockBodies: + if reqID != req.RequestId { + t.Fatalf("request ID mismatch: wanted %d, got %d", req.RequestId, reqID) + } + t.Logf("received %d block bodies", len(msg)) + default: + t.Fatalf("unexpected: %s", pretty.Sdump(msg)) + } +} + +// TestLargeAnnounce_66 tests the announcement mechanism with a large block. +func (s *Suite) TestLargeAnnounce_66(t *utesting.T) { + nextBlock := len(s.chain.blocks) + blocks := []*NewBlock{ + { + Block: largeBlock(), + TD: s.fullChain.TD(nextBlock + 1), + }, + { + Block: s.fullChain.blocks[nextBlock], + TD: largeNumber(2), + }, + { + Block: largeBlock(), + TD: largeNumber(2), + }, + { + Block: s.fullChain.blocks[nextBlock], + TD: s.fullChain.TD(nextBlock + 1), + }, + } + + for i, blockAnnouncement := range blocks[0:3] { + t.Logf("Testing malicious announcement: %v\n", i) + sendConn := s.setupConnection66(t) + if err := sendConn.Write(blockAnnouncement); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + // Invalid announcement, check that peer disconnected + switch msg := sendConn.ReadAndServe(s.chain, timeout).(type) { + case *Disconnect: + case *Error: + break + default: + t.Fatalf("unexpected: %s wanted disconnect", pretty.Sdump(msg)) + } + } + // Test the last block as a valid block + sendConn := s.setupConnection66(t) + receiveConn := s.setupConnection66(t) + s.testAnnounce66(t, sendConn, receiveConn, blocks[3]) + // update test suite chain + s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock]) + // wait for client to update its chain + if err := receiveConn.waitForBlock66(s.fullChain.blocks[nextBlock]); err != nil { + t.Fatal(err) + } +} + +func (s *Suite) TestOldAnnounce_66(t *utesting.T) { + s.oldAnnounce(t, s.setupConnection66(t), s.setupConnection66(t)) +} + +// TestMaliciousHandshake_66 tries to send malicious data during the handshake. +func (s *Suite) TestMaliciousHandshake_66(t *utesting.T) { + conn := s.dial66(t) + // write hello to client + pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:] + handshakes := []*Hello{ + { + Version: 5, + Caps: []p2p.Cap{ + {Name: largeString(2), Version: 66}, + }, + ID: pub0, + }, + { + Version: 5, + Caps: []p2p.Cap{ + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + {Name: "eth", Version: 66}, + }, + ID: append(pub0, byte(0)), + }, + { + Version: 5, + Caps: []p2p.Cap{ + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + {Name: "eth", Version: 66}, + }, + ID: append(pub0, pub0...), + }, + { + Version: 5, + Caps: []p2p.Cap{ + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + {Name: "eth", Version: 66}, + }, + ID: largeBuffer(2), + }, + { + Version: 5, + Caps: []p2p.Cap{ + {Name: largeString(2), Version: 66}, + }, + ID: largeBuffer(2), + }, + } + for i, handshake := range handshakes { + t.Logf("Testing malicious handshake %v\n", i) + // Init the handshake + if err := conn.Write(handshake); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + // check that the peer disconnected + timeout := 20 * time.Second + // Discard one hello + for i := 0; i < 2; i++ { + switch msg := conn.ReadAndServe(s.chain, timeout).(type) { + case *Disconnect: + case *Error: + case *Hello: + // Hello's are sent concurrently, so ignore them + continue + default: + t.Fatalf("unexpected: %s", pretty.Sdump(msg)) + } + } + // Dial for the next round + conn = s.dial66(t) + } +} + +// TestMaliciousStatus_66 sends a status package with a large total difficulty. +func (s *Suite) TestMaliciousStatus_66(t *utesting.T) { + conn := s.dial66(t) + // get protoHandshake + conn.handshake(t) + status := &Status{ + ProtocolVersion: uint32(66), + NetworkID: s.chain.chainConfig.GetChainID().Uint64(), + TD: largeNumber(2), + Head: s.chain.blocks[s.chain.Len()-1].Hash(), + Genesis: s.chain.blocks[0].Hash(), + ForkID: s.chain.ForkID(), + } + // get status + switch msg := conn.statusExchange(t, s.chain, status).(type) { + case *Status: + t.Logf("%+v\n", msg) + default: + t.Fatalf("expected status, got: %#v ", msg) + } + // wait for disconnect + switch msg := conn.ReadAndServe(s.chain, timeout).(type) { + case *Disconnect: + case *Error: + return + default: + t.Fatalf("expected disconnect, got: %s", pretty.Sdump(msg)) + } +} + +func (s *Suite) TestTransaction_66(t *utesting.T) { + tests := []*types.Transaction{ + getNextTxFromChain(t, s), + unknownTx(t, s), + } + for i, tx := range tests { + t.Logf("Testing tx propagation: %v\n", i) + sendSuccessfulTx66(t, s, tx) + } +} + +func (s *Suite) TestMaliciousTx_66(t *utesting.T) { + tests := []*types.Transaction{ + getOldTxFromChain(t, s), + invalidNonceTx(t, s), + hugeAmount(t, s), + hugeGasPrice(t, s), + hugeData(t, s), + } + for i, tx := range tests { + t.Logf("Testing malicious tx propagation: %v\n", i) + sendFailingTx66(t, s, tx) + } +} + +// TestZeroRequestID_66 checks that a request ID of zero is still handled +// by the node. +func (s *Suite) TestZeroRequestID_66(t *utesting.T) { + conn := s.setupConnection66(t) + req := ð.GetBlockHeadersPacket66{ + RequestId: 0, + GetBlockHeadersPacket: ð.GetBlockHeadersPacket{ + Origin: eth.HashOrNumber{ + Number: 0, + }, + Amount: 2, + }, + } + headersMatch(t, s.chain, s.getBlockHeaders66(t, conn, req, req.RequestId)) +} + +// TestSameRequestID_66 sends two requests with the same request ID +// concurrently to a single node. +func (s *Suite) TestSameRequestID_66(t *utesting.T) { + conn := s.setupConnection66(t) + // create two separate requests with same ID + reqID := uint64(1234) + req1 := ð.GetBlockHeadersPacket66{ + RequestId: reqID, + GetBlockHeadersPacket: ð.GetBlockHeadersPacket{ + Origin: eth.HashOrNumber{ + Number: 0, + }, + Amount: 2, + }, + } + req2 := ð.GetBlockHeadersPacket66{ + RequestId: reqID, + GetBlockHeadersPacket: ð.GetBlockHeadersPacket{ + Origin: eth.HashOrNumber{ + Number: 33, + }, + Amount: 2, + }, + } + // send requests concurrently + go func() { + headersMatch(t, s.chain, s.getBlockHeaders66(t, conn, req2, reqID)) + }() + // check response from first request + headersMatch(t, s.chain, s.getBlockHeaders66(t, conn, req1, reqID)) +} diff --git a/cmd/devp2p/internal/ethtest/eth66_suiteHelpers.go b/cmd/devp2p/internal/ethtest/eth66_suiteHelpers.go new file mode 100644 index 0000000000..d8faf31f74 --- /dev/null +++ b/cmd/devp2p/internal/ethtest/eth66_suiteHelpers.go @@ -0,0 +1,274 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethtest + +import ( + "fmt" + "time" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/internal/utesting" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" + "github.com/stretchr/testify/assert" +) + +func (c *Conn) statusExchange66(t *utesting.T, chain *Chain) Message { + status := &Status{ + ProtocolVersion: uint32(66), + NetworkID: chain.chainConfig.GetChainID().Uint64(), + TD: chain.TD(chain.Len()), + Head: chain.blocks[chain.Len()-1].Hash(), + Genesis: chain.blocks[0].Hash(), + ForkID: chain.ForkID(), + } + return c.statusExchange(t, chain, status) +} + +func (s *Suite) dial66(t *utesting.T) *Conn { + conn, err := s.dial() + if err != nil { + t.Fatalf("could not dial: %v", err) + } + conn.caps = append(conn.caps, p2p.Cap{Name: "eth", Version: 66}) + conn.ourHighestProtoVersion = 66 + return conn +} + +func (c *Conn) write66(req eth.Packet, code int) error { + payload, err := rlp.EncodeToBytes(req) + if err != nil { + return err + } + _, err = c.Conn.Write(uint64(code), payload) + return err +} + +func (c *Conn) read66() (uint64, Message) { + code, rawData, _, err := c.Conn.Read() + if err != nil { + return 0, errorf("could not read from connection: %v", err) + } + + var msg Message + + switch int(code) { + case (Hello{}).Code(): + msg = new(Hello) + + case (Ping{}).Code(): + msg = new(Ping) + case (Pong{}).Code(): + msg = new(Pong) + case (Disconnect{}).Code(): + msg = new(Disconnect) + case (Status{}).Code(): + msg = new(Status) + case (GetBlockHeaders{}).Code(): + ethMsg := new(eth.GetBlockHeadersPacket66) + if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { + return 0, errorf("could not rlp decode message: %v", err) + } + return ethMsg.RequestId, GetBlockHeaders(*ethMsg.GetBlockHeadersPacket) + case (BlockHeaders{}).Code(): + ethMsg := new(eth.BlockHeadersPacket66) + if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { + return 0, errorf("could not rlp decode message: %v", err) + } + return ethMsg.RequestId, BlockHeaders(ethMsg.BlockHeadersPacket) + case (GetBlockBodies{}).Code(): + ethMsg := new(eth.GetBlockBodiesPacket66) + if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { + return 0, errorf("could not rlp decode message: %v", err) + } + return ethMsg.RequestId, GetBlockBodies(ethMsg.GetBlockBodiesPacket) + case (BlockBodies{}).Code(): + ethMsg := new(eth.BlockBodiesPacket66) + if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { + return 0, errorf("could not rlp decode message: %v", err) + } + return ethMsg.RequestId, BlockBodies(ethMsg.BlockBodiesPacket) + case (NewBlock{}).Code(): + msg = new(NewBlock) + case (NewBlockHashes{}).Code(): + msg = new(NewBlockHashes) + case (Transactions{}).Code(): + msg = new(Transactions) + case (NewPooledTransactionHashes{}).Code(): + msg = new(NewPooledTransactionHashes) + default: + msg = errorf("invalid message code: %d", code) + } + + if msg != nil { + if err := rlp.DecodeBytes(rawData, msg); err != nil { + return 0, errorf("could not rlp decode message: %v", err) + } + return 0, msg + } + return 0, errorf("invalid message: %s", string(rawData)) +} + +// ReadAndServe serves GetBlockHeaders requests while waiting +// on another message from the node. +func (c *Conn) readAndServe66(chain *Chain, timeout time.Duration) (uint64, Message) { + start := time.Now() + for time.Since(start) < timeout { + timeout := time.Now().Add(10 * time.Second) + c.SetReadDeadline(timeout) + + reqID, msg := c.read66() + + switch msg := msg.(type) { + case *Ping: + c.Write(&Pong{}) + case *GetBlockHeaders: + headers, err := chain.GetHeaders(*msg) + if err != nil { + return 0, errorf("could not get headers for inbound header request: %v", err) + } + resp := ð.BlockHeadersPacket66{ + RequestId: reqID, + BlockHeadersPacket: eth.BlockHeadersPacket(headers), + } + if err := c.write66(resp, BlockHeaders{}.Code()); err != nil { + return 0, errorf("could not write to connection: %v", err) + } + default: + return reqID, msg + } + } + return 0, errorf("no message received within %v", timeout) +} + +func (s *Suite) setupConnection66(t *utesting.T) *Conn { + // create conn + sendConn := s.dial66(t) + sendConn.handshake(t) + sendConn.statusExchange66(t, s.chain) + return sendConn +} + +func (s *Suite) testAnnounce66(t *utesting.T, sendConn, receiveConn *Conn, blockAnnouncement *NewBlock) { + // Announce the block. + if err := sendConn.Write(blockAnnouncement); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + s.waitAnnounce66(t, receiveConn, blockAnnouncement) +} + +func (s *Suite) waitAnnounce66(t *utesting.T, conn *Conn, blockAnnouncement *NewBlock) { + timeout := 20 * time.Second + _, msg := conn.readAndServe66(s.chain, timeout) + switch msg := msg.(type) { + case *NewBlock: + t.Logf("received NewBlock message: %s", pretty.Sdump(msg.Block)) + assert.Equal(t, + blockAnnouncement.Block.Header(), msg.Block.Header(), + "wrong block header in announcement", + ) + assert.Equal(t, + blockAnnouncement.TD, msg.TD, + "wrong TD in announcement", + ) + case *NewBlockHashes: + blockHashes := *msg + t.Logf("received NewBlockHashes message: %s", pretty.Sdump(blockHashes)) + assert.Equal(t, blockAnnouncement.Block.Hash(), blockHashes[0].Hash, + "wrong block hash in announcement", + ) + default: + t.Fatalf("unexpected: %s", pretty.Sdump(msg)) + } +} + +// waitForBlock66 waits for confirmation from the client that it has +// imported the given block. +func (c *Conn) waitForBlock66(block *types.Block) error { + defer c.SetReadDeadline(time.Time{}) + + timeout := time.Now().Add(20 * time.Second) + c.SetReadDeadline(timeout) + for { + req := eth.GetBlockHeadersPacket66{ + RequestId: 54, + GetBlockHeadersPacket: ð.GetBlockHeadersPacket{ + Origin: eth.HashOrNumber{ + Hash: block.Hash(), + }, + Amount: 1, + }, + } + if err := c.write66(req, GetBlockHeaders{}.Code()); err != nil { + return err + } + + reqID, msg := c.read66() + // check message + switch msg := msg.(type) { + case BlockHeaders: + // check request ID + if reqID != req.RequestId { + return fmt.Errorf("request ID mismatch: wanted %d, got %d", req.RequestId, reqID) + } + if len(msg) > 0 { + return nil + } + time.Sleep(100 * time.Millisecond) + default: + return fmt.Errorf("invalid message: %s", pretty.Sdump(msg)) + } + } +} + +func sendSuccessfulTx66(t *utesting.T, s *Suite, tx *types.Transaction) { + sendConn := s.setupConnection66(t) + sendSuccessfulTxWithConn(t, s, tx, sendConn) +} + +func sendFailingTx66(t *utesting.T, s *Suite, tx *types.Transaction) { + sendConn, recvConn := s.setupConnection66(t), s.setupConnection66(t) + sendFailingTxWithConns(t, s, tx, sendConn, recvConn) +} + +func (s *Suite) getBlockHeaders66(t *utesting.T, conn *Conn, req eth.Packet, expectedID uint64) BlockHeaders { + if err := conn.write66(req, GetBlockHeaders{}.Code()); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + // check block headers response + reqID, msg := conn.readAndServe66(s.chain, timeout) + + switch msg := msg.(type) { + case BlockHeaders: + if reqID != expectedID { + t.Fatalf("request ID mismatch: wanted %d, got %d", expectedID, reqID) + } + return msg + default: + t.Fatalf("unexpected: %s", pretty.Sdump(msg)) + return nil + } +} + +func headersMatch(t *utesting.T, chain *Chain, headers BlockHeaders) { + for _, header := range headers { + num := header.Number.Uint64() + t.Logf("received header (%d): %s", num, pretty.Sdump(header.Hash())) + assert.Equal(t, chain.blocks[int(num)].Header(), header) + } +} diff --git a/cmd/devp2p/internal/ethtest/large.go b/cmd/devp2p/internal/ethtest/large.go new file mode 100644 index 0000000000..deca00be53 --- /dev/null +++ b/cmd/devp2p/internal/ethtest/large.go @@ -0,0 +1,80 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethtest + +import ( + "crypto/rand" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" +) + +// largeNumber returns a very large big.Int. +func largeNumber(megabytes int) *big.Int { + buf := make([]byte, megabytes*1024*1024) + rand.Read(buf) + bigint := new(big.Int) + bigint.SetBytes(buf) + return bigint +} + +// largeBuffer returns a very large buffer. +func largeBuffer(megabytes int) []byte { + buf := make([]byte, megabytes*1024*1024) + rand.Read(buf) + return buf +} + +// largeString returns a very large string. +func largeString(megabytes int) string { + buf := make([]byte, megabytes*1024*1024) + rand.Read(buf) + return hexutil.Encode(buf) +} + +func largeBlock() *types.Block { + return types.NewBlockWithHeader(largeHeader()) +} + +// Returns a random hash +func randHash() common.Hash { + var h common.Hash + rand.Read(h[:]) + return h +} + +func largeHeader() *types.Header { + return &types.Header{ + MixDigest: randHash(), + ReceiptHash: randHash(), + TxHash: randHash(), + Nonce: types.BlockNonce{}, + Extra: []byte{}, + Bloom: types.Bloom{}, + GasUsed: 0, + Coinbase: common.Address{}, + GasLimit: 0, + UncleHash: randHash(), + Time: 1337, + ParentHash: randHash(), + Root: randHash(), + Number: largeNumber(2), + Difficulty: largeNumber(2), + } +} diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index d5928bede4..f43545ad5a 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -19,11 +19,15 @@ package ethtest import ( "fmt" "net" + "strings" "time" "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/internal/utesting" + "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/rlpx" "github.com/stretchr/testify/assert" @@ -36,6 +40,8 @@ var pretty = spew.ConfigState{ SortKeys: true, } +var timeout = 20 * time.Second + // Suite represents a structure used to test the eth // protocol of a node(s). type Suite struct { @@ -48,24 +54,81 @@ type Suite struct { // NewSuite creates and returns a new eth-test suite that can // be used to test the given node against the given blockchain // data. -func NewSuite(dest *enode.Node, chainfile string, genesisfile string) *Suite { +func NewSuite(dest *enode.Node, chainfile string, genesisfile string) (*Suite, error) { chain, err := loadChain(chainfile, genesisfile) if err != nil { - panic(err) + return nil, err } return &Suite{ Dest: dest, chain: chain.Shorten(1000), fullChain: chain, - } + }, nil } -func (s *Suite) AllTests() []utesting.Test { +func (s *Suite) AllEthTests() []utesting.Test { return []utesting.Test{ + // status {Name: "Status", Fn: s.TestStatus}, + {Name: "Status_66", Fn: s.TestStatus_66}, + // get block headers {Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders}, + {Name: "GetBlockHeaders_66", Fn: s.TestGetBlockHeaders_66}, + {Name: "TestSimultaneousRequests_66", Fn: s.TestSimultaneousRequests_66}, + {Name: "TestSameRequestID_66", Fn: s.TestSameRequestID_66}, + {Name: "TestZeroRequestID_66", Fn: s.TestZeroRequestID_66}, + // get block bodies + {Name: "GetBlockBodies", Fn: s.TestGetBlockBodies}, + {Name: "GetBlockBodies_66", Fn: s.TestGetBlockBodies_66}, + // broadcast {Name: "Broadcast", Fn: s.TestBroadcast}, + {Name: "Broadcast_66", Fn: s.TestBroadcast_66}, + {Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce}, + {Name: "TestLargeAnnounce_66", Fn: s.TestLargeAnnounce_66}, + {Name: "TestOldAnnounce", Fn: s.TestOldAnnounce}, + {Name: "TestOldAnnounce_66", Fn: s.TestOldAnnounce_66}, + // malicious handshakes + status + {Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake}, + {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus}, + {Name: "TestMaliciousHandshake_66", Fn: s.TestMaliciousHandshake_66}, + {Name: "TestMaliciousStatus_66", Fn: s.TestMaliciousStatus}, + // test transactions + {Name: "TestTransactions", Fn: s.TestTransaction}, + {Name: "TestTransactions_66", Fn: s.TestTransaction_66}, + {Name: "TestMaliciousTransactions", Fn: s.TestMaliciousTx}, + {Name: "TestMaliciousTransactions_66", Fn: s.TestMaliciousTx_66}, + } +} + +func (s *Suite) EthTests() []utesting.Test { + return []utesting.Test{ + {Name: "Status", Fn: s.TestStatus}, + {Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders}, {Name: "GetBlockBodies", Fn: s.TestGetBlockBodies}, + {Name: "Broadcast", Fn: s.TestBroadcast}, + {Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce}, + {Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake}, + {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus}, + {Name: "TestMaliciousStatus_66", Fn: s.TestMaliciousStatus}, + {Name: "TestTransactions", Fn: s.TestTransaction}, + {Name: "TestMaliciousTransactions", Fn: s.TestMaliciousTx}, + } +} + +func (s *Suite) Eth66Tests() []utesting.Test { + return []utesting.Test{ + // only proceed with eth66 test suite if node supports eth 66 protocol + {Name: "Status_66", Fn: s.TestStatus_66}, + {Name: "GetBlockHeaders_66", Fn: s.TestGetBlockHeaders_66}, + {Name: "TestSimultaneousRequests_66", Fn: s.TestSimultaneousRequests_66}, + {Name: "TestSameRequestID_66", Fn: s.TestSameRequestID_66}, + {Name: "TestZeroRequestID_66", Fn: s.TestZeroRequestID_66}, + {Name: "GetBlockBodies_66", Fn: s.TestGetBlockBodies_66}, + {Name: "Broadcast_66", Fn: s.TestBroadcast_66}, + {Name: "TestLargeAnnounce_66", Fn: s.TestLargeAnnounce_66}, + {Name: "TestMaliciousHandshake_66", Fn: s.TestMaliciousHandshake_66}, + {Name: "TestTransactions_66", Fn: s.TestTransaction_66}, + {Name: "TestMaliciousTransactions_66", Fn: s.TestMaliciousTx_66}, } } @@ -80,7 +143,7 @@ func (s *Suite) TestStatus(t *utesting.T) { // get protoHandshake conn.handshake(t) // get status - switch msg := conn.statusExchange(t, s.chain).(type) { + switch msg := conn.statusExchange(t, s.chain, nil).(type) { case *Status: t.Logf("got status message: %s", pretty.Sdump(msg)) default: @@ -88,6 +151,39 @@ func (s *Suite) TestStatus(t *utesting.T) { } } +// TestMaliciousStatus sends a status package with a large total difficulty. +func (s *Suite) TestMaliciousStatus(t *utesting.T) { + conn, err := s.dial() + if err != nil { + t.Fatalf("could not dial: %v", err) + } + // get protoHandshake + conn.handshake(t) + status := &Status{ + ProtocolVersion: uint32(conn.negotiatedProtoVersion), + NetworkID: s.chain.chainConfig.GetChainID().Uint64(), + TD: largeNumber(2), + Head: s.chain.blocks[s.chain.Len()-1].Hash(), + Genesis: s.chain.blocks[0].Hash(), + ForkID: s.chain.ForkID(), + } + // get status + switch msg := conn.statusExchange(t, s.chain, status).(type) { + case *Status: + t.Logf("%+v\n", msg) + default: + t.Fatalf("expected status, got: %#v ", msg) + } + // wait for disconnect + switch msg := conn.ReadAndServe(s.chain, timeout).(type) { + case *Disconnect: + case *Error: + return + default: + t.Fatalf("expected disconnect, got: %s", pretty.Sdump(msg)) + } +} + // TestGetBlockHeaders tests whether the given node can respond to // a `GetBlockHeaders` request and that the response is accurate. func (s *Suite) TestGetBlockHeaders(t *utesting.T) { @@ -97,11 +193,11 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) { } conn.handshake(t) - conn.statusExchange(t, s.chain) + conn.statusExchange(t, s.chain, nil) // get block headers req := &GetBlockHeaders{ - Origin: hashOrNumber{ + Origin: eth.HashOrNumber{ Hash: s.chain.blocks[1].Hash(), }, Amount: 2, @@ -113,13 +209,12 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) { t.Fatalf("could not write to connection: %v", err) } - timeout := 20 * time.Second switch msg := conn.ReadAndServe(s.chain, timeout).(type) { case *BlockHeaders: - headers := msg - for _, header := range *headers { + headers := *msg + for _, header := range headers { num := header.Number.Uint64() - t.Logf("received header (%d): %s", num, pretty.Sdump(header)) + t.Logf("received header (%d): %s", num, pretty.Sdump(header.Hash())) assert.Equal(t, s.chain.blocks[int(num)].Header(), header) } default: @@ -136,14 +231,16 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { } conn.handshake(t) - conn.statusExchange(t, s.chain) + conn.statusExchange(t, s.chain, nil) // create block bodies request - req := &GetBlockBodies{s.chain.blocks[54].Hash(), s.chain.blocks[75].Hash()} + req := &GetBlockBodies{ + s.chain.blocks[54].Hash(), + s.chain.blocks[75].Hash(), + } if err := conn.Write(req); err != nil { t.Fatalf("could not write to connection: %v", err) } - timeout := 20 * time.Second switch msg := conn.ReadAndServe(s.chain, timeout).(type) { case *BlockBodies: t.Logf("received %d block bodies", len(*msg)) @@ -155,34 +252,187 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { // TestBroadcast tests whether a block announcement is correctly // propagated to the given node's peer(s). func (s *Suite) TestBroadcast(t *utesting.T) { - // create conn to send block announcement - sendConn, err := s.dial() - if err != nil { - t.Fatalf("could not dial: %v", err) + sendConn, receiveConn := s.setupConnection(t), s.setupConnection(t) + nextBlock := len(s.chain.blocks) + blockAnnouncement := &NewBlock{ + Block: s.fullChain.blocks[nextBlock], + TD: s.fullChain.TD(nextBlock + 1), + } + s.testAnnounce(t, sendConn, receiveConn, blockAnnouncement) + // update test suite chain + s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock]) + // wait for client to update its chain + if err := receiveConn.waitForBlock(s.chain.Head()); err != nil { + t.Fatal(err) } - // create conn to receive block announcement - receiveConn, err := s.dial() +} + +// TestMaliciousHandshake tries to send malicious data during the handshake. +func (s *Suite) TestMaliciousHandshake(t *utesting.T) { + conn, err := s.dial() if err != nil { t.Fatalf("could not dial: %v", err) } + // write hello to client + pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:] + handshakes := []*Hello{ + { + Version: 5, + Caps: []p2p.Cap{ + {Name: largeString(2), Version: 64}, + }, + ID: pub0, + }, + { + Version: 5, + Caps: []p2p.Cap{ + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + }, + ID: append(pub0, byte(0)), + }, + { + Version: 5, + Caps: []p2p.Cap{ + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + }, + ID: append(pub0, pub0...), + }, + { + Version: 5, + Caps: []p2p.Cap{ + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + }, + ID: largeBuffer(2), + }, + { + Version: 5, + Caps: []p2p.Cap{ + {Name: largeString(2), Version: 64}, + }, + ID: largeBuffer(2), + }, + } + for i, handshake := range handshakes { + t.Logf("Testing malicious handshake %v\n", i) + // Init the handshake + if err := conn.Write(handshake); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + // check that the peer disconnected + timeout := 20 * time.Second + // Discard one hello + for i := 0; i < 2; i++ { + switch msg := conn.ReadAndServe(s.chain, timeout).(type) { + case *Disconnect: + case *Error: + case *Hello: + // Hello's are send concurrently, so ignore them + continue + default: + t.Fatalf("unexpected: %s", pretty.Sdump(msg)) + } + } + // Dial for the next round + conn, err = s.dial() + if err != nil { + t.Fatalf("could not dial: %v", err) + } + } +} - sendConn.handshake(t) - receiveConn.handshake(t) +// TestLargeAnnounce tests the announcement mechanism with a large block. +func (s *Suite) TestLargeAnnounce(t *utesting.T) { + nextBlock := len(s.chain.blocks) + blocks := []*NewBlock{ + { + Block: largeBlock(), + TD: s.fullChain.TD(nextBlock + 1), + }, + { + Block: s.fullChain.blocks[nextBlock], + TD: largeNumber(2), + }, + { + Block: largeBlock(), + TD: largeNumber(2), + }, + { + Block: s.fullChain.blocks[nextBlock], + TD: s.fullChain.TD(nextBlock + 1), + }, + } + + for i, blockAnnouncement := range blocks[0:3] { + t.Logf("Testing malicious announcement: %v\n", i) + sendConn := s.setupConnection(t) + if err := sendConn.Write(blockAnnouncement); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + // Invalid announcement, check that peer disconnected + switch msg := sendConn.ReadAndServe(s.chain, timeout).(type) { + case *Disconnect: + case *Error: + break + default: + t.Fatalf("unexpected: %s wanted disconnect", pretty.Sdump(msg)) + } + } + // Test the last block as a valid block + sendConn := s.setupConnection(t) + receiveConn := s.setupConnection(t) + s.testAnnounce(t, sendConn, receiveConn, blocks[3]) + // update test suite chain + s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock]) + // wait for client to update its chain + if err := receiveConn.waitForBlock(s.fullChain.blocks[nextBlock]); err != nil { + t.Fatal(err) + } +} - sendConn.statusExchange(t, s.chain) - receiveConn.statusExchange(t, s.chain) +func (s *Suite) TestOldAnnounce(t *utesting.T) { + s.oldAnnounce(t, s.setupConnection(t), s.setupConnection(t)) +} - // sendConn sends the block announcement - blockAnnouncement := &NewBlock{ - Block: s.fullChain.blocks[1000], - TD: s.fullChain.TD(1001), +func (s *Suite) oldAnnounce(t *utesting.T, sendConn, receiveConn *Conn) { + oldBlockAnnounce := &NewBlock{ + Block: s.chain.blocks[len(s.chain.blocks)/2], + TD: s.chain.blocks[len(s.chain.blocks)/2].Difficulty(), + } + + if err := sendConn.Write(oldBlockAnnounce); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + + switch msg := receiveConn.ReadAndServe(s.chain, timeout*2).(type) { + case *NewBlock: + t.Fatalf("unexpected: block propagated: %s", pretty.Sdump(msg)) + case *NewBlockHashes: + t.Fatalf("unexpected: block announced: %s", pretty.Sdump(msg)) + case *Error: + errMsg := *msg + // check to make sure error is timeout (propagation didn't come through == test successful) + if !strings.Contains(errMsg.String(), "timeout") { + t.Fatalf("unexpected error: %v", pretty.Sdump(msg)) + } + default: + t.Fatalf("unexpected: %s", pretty.Sdump(msg)) } +} + +func (s *Suite) testAnnounce(t *utesting.T, sendConn, receiveConn *Conn, blockAnnouncement *NewBlock) { + // Announce the block. if err := sendConn.Write(blockAnnouncement); err != nil { t.Fatalf("could not write to connection: %v", err) } + s.waitAnnounce(t, receiveConn, blockAnnouncement) +} +func (s *Suite) waitAnnounce(t *utesting.T, conn *Conn, blockAnnouncement *NewBlock) { timeout := 20 * time.Second - switch msg := receiveConn.ReadAndServe(s.chain, timeout).(type) { + switch msg := conn.ReadAndServe(s.chain, timeout).(type) { case *NewBlock: t.Logf("received NewBlock message: %s", pretty.Sdump(msg.Block)) assert.Equal(t, @@ -194,40 +444,73 @@ func (s *Suite) TestBroadcast(t *utesting.T) { "wrong TD in announcement", ) case *NewBlockHashes: - hashes := *msg - t.Logf("received NewBlockHashes message: %s", pretty.Sdump(hashes)) - assert.Equal(t, - blockAnnouncement.Block.Hash(), hashes[0].Hash, + message := *msg + t.Logf("received NewBlockHashes message: %s", pretty.Sdump(message)) + assert.Equal(t, blockAnnouncement.Block.Hash(), message[0].Hash, "wrong block hash in announcement", ) default: t.Fatalf("unexpected: %s", pretty.Sdump(msg)) } - // update test suite chain - s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[1000]) - // wait for client to update its chain - if err := receiveConn.waitForBlock(s.chain.Head()); err != nil { - t.Fatal(err) +} + +func (s *Suite) setupConnection(t *utesting.T) *Conn { + // create conn + sendConn, err := s.dial() + if err != nil { + t.Fatalf("could not dial: %v", err) } + sendConn.handshake(t) + sendConn.statusExchange(t, s.chain, nil) + return sendConn } // dial attempts to dial the given node and perform a handshake, // returning the created Conn if successful. func (s *Suite) dial() (*Conn, error) { var conn Conn - + // dial fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", s.Dest.IP(), s.Dest.TCP())) if err != nil { return nil, err } conn.Conn = rlpx.NewConn(fd, s.Dest.Pubkey()) - // do encHandshake conn.ourKey, _ = crypto.GenerateKey() _, err = conn.Handshake(conn.ourKey) if err != nil { return nil, err } - + // set default p2p capabilities + conn.caps = []p2p.Cap{ + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + } + conn.ourHighestProtoVersion = 65 return &conn, nil } + +func (s *Suite) TestTransaction(t *utesting.T) { + tests := []*types.Transaction{ + getNextTxFromChain(t, s), + unknownTx(t, s), + } + for i, tx := range tests { + t.Logf("Testing tx propagation: %v\n", i) + sendSuccessfulTx(t, s, tx) + } +} + +func (s *Suite) TestMaliciousTx(t *utesting.T) { + tests := []*types.Transaction{ + getOldTxFromChain(t, s), + invalidNonceTx(t, s), + hugeAmount(t, s), + hugeGasPrice(t, s), + hugeData(t, s), + } + for i, tx := range tests { + t.Logf("Testing malicious tx propagation: %v\n", i) + sendFailingTx(t, s, tx) + } +} diff --git a/cmd/devp2p/internal/ethtest/testdata/chain.rlp b/cmd/devp2p/internal/ethtest/testdata/chain.rlp new file mode 100644 index 0000000000..5ebc2f3bb7 Binary files /dev/null and b/cmd/devp2p/internal/ethtest/testdata/chain.rlp differ diff --git a/cmd/devp2p/internal/ethtest/testdata/fullchain.rlp.gz b/cmd/devp2p/internal/ethtest/testdata/fullchain.rlp.gz deleted file mode 100644 index 50f52eafa2..0000000000 Binary files a/cmd/devp2p/internal/ethtest/testdata/fullchain.rlp.gz and /dev/null differ diff --git a/cmd/devp2p/internal/ethtest/testdata/genesis.json b/cmd/devp2p/internal/ethtest/testdata/genesis.json index ed78488b67..d8b5d22502 100644 --- a/cmd/devp2p/internal/ethtest/testdata/genesis.json +++ b/cmd/devp2p/internal/ethtest/testdata/genesis.json @@ -17,7 +17,7 @@ "coinbase": "0x0000000000000000000000000000000000000000", "alloc": { "71562b71999873db5b286df957af199ec94617f7": { - "balance": "0xffffffff" + "balance": "0xffffffffffffffffffffffffff" } }, "number": "0x0", diff --git a/cmd/devp2p/internal/ethtest/testdata/halfchain.rlp b/cmd/devp2p/internal/ethtest/testdata/halfchain.rlp new file mode 100644 index 0000000000..1a820734e1 Binary files /dev/null and b/cmd/devp2p/internal/ethtest/testdata/halfchain.rlp differ diff --git a/cmd/devp2p/internal/ethtest/testdata/halfchain.rlp.gz b/cmd/devp2p/internal/ethtest/testdata/halfchain.rlp.gz deleted file mode 100644 index 82d5271361..0000000000 Binary files a/cmd/devp2p/internal/ethtest/testdata/halfchain.rlp.gz and /dev/null differ diff --git a/cmd/devp2p/internal/ethtest/transaction.go b/cmd/devp2p/internal/ethtest/transaction.go new file mode 100644 index 0000000000..21aa221e8b --- /dev/null +++ b/cmd/devp2p/internal/ethtest/transaction.go @@ -0,0 +1,189 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethtest + +import ( + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/internal/utesting" +) + +//var faucetAddr = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") +var faucetKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + +func sendSuccessfulTx(t *utesting.T, s *Suite, tx *types.Transaction) { + sendConn := s.setupConnection(t) + sendSuccessfulTxWithConn(t, s, tx, sendConn) +} + +func sendSuccessfulTxWithConn(t *utesting.T, s *Suite, tx *types.Transaction, sendConn *Conn) { + t.Logf("sending tx: %v %v %v\n", tx.Hash().String(), tx.GasPrice(), tx.Gas()) + // Send the transaction + if err := sendConn.Write(&Transactions{tx}); err != nil { + t.Fatal(err) + } + time.Sleep(100 * time.Millisecond) + recvConn := s.setupConnection(t) + // Wait for the transaction announcement + switch msg := recvConn.ReadAndServe(s.chain, timeout).(type) { + case *Transactions: + recTxs := *msg + for _, gotTx := range recTxs { + if gotTx.Hash() == tx.Hash() { + // Ok + return + } + } + t.Fatalf("missing transaction: got %v missing %v", recTxs, tx.Hash()) + case *NewPooledTransactionHashes: + txHashes := *msg + for _, gotHash := range txHashes { + if gotHash == tx.Hash() { + return + } + } + t.Fatalf("missing transaction announcement: got %v missing %v", txHashes, tx.Hash()) + default: + t.Fatalf("unexpected message in sendSuccessfulTx: %s", pretty.Sdump(msg)) + } +} + +func sendFailingTx(t *utesting.T, s *Suite, tx *types.Transaction) { + sendConn, recvConn := s.setupConnection(t), s.setupConnection(t) + sendFailingTxWithConns(t, s, tx, sendConn, recvConn) +} + +func sendFailingTxWithConns(t *utesting.T, s *Suite, tx *types.Transaction, sendConn, recvConn *Conn) { + // Wait for a transaction announcement + switch msg := recvConn.ReadAndServe(s.chain, timeout).(type) { + case *NewPooledTransactionHashes: + break + default: + t.Logf("unexpected message, logging: %v", pretty.Sdump(msg)) + } + // Send the transaction + if err := sendConn.Write(&Transactions{tx}); err != nil { + t.Fatal(err) + } + // Wait for another transaction announcement + switch msg := recvConn.ReadAndServe(s.chain, timeout).(type) { + case *Transactions: + t.Fatalf("Received unexpected transaction announcement: %v", msg) + case *NewPooledTransactionHashes: + t.Fatalf("Received unexpected pooledTx announcement: %v", msg) + case *Error: + // Transaction should not be announced -> wait for timeout + return + default: + t.Fatalf("unexpected message in sendFailingTx: %s", pretty.Sdump(msg)) + } +} + +func unknownTx(t *utesting.T, s *Suite) *types.Transaction { + tx := getNextTxFromChain(t, s) + var to common.Address + if tx.To() != nil { + to = *tx.To() + } + txNew := types.NewTransaction(tx.Nonce()+1, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.Data()) + return signWithFaucet(t, txNew) +} + +func getNextTxFromChain(t *utesting.T, s *Suite) *types.Transaction { + // Get a new transaction + var tx *types.Transaction + for _, blocks := range s.fullChain.blocks[s.chain.Len():] { + txs := blocks.Transactions() + if txs.Len() != 0 { + tx = txs[0] + break + } + } + if tx == nil { + t.Fatal("could not find transaction") + } + return tx +} + +func getOldTxFromChain(t *utesting.T, s *Suite) *types.Transaction { + var tx *types.Transaction + for _, blocks := range s.fullChain.blocks[:s.chain.Len()-1] { + txs := blocks.Transactions() + if txs.Len() != 0 { + tx = txs[0] + break + } + } + if tx == nil { + t.Fatal("could not find transaction") + } + return tx +} + +func invalidNonceTx(t *utesting.T, s *Suite) *types.Transaction { + tx := getNextTxFromChain(t, s) + var to common.Address + if tx.To() != nil { + to = *tx.To() + } + txNew := types.NewTransaction(tx.Nonce()-2, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.Data()) + return signWithFaucet(t, txNew) +} + +func hugeAmount(t *utesting.T, s *Suite) *types.Transaction { + tx := getNextTxFromChain(t, s) + amount := largeNumber(2) + var to common.Address + if tx.To() != nil { + to = *tx.To() + } + txNew := types.NewTransaction(tx.Nonce(), to, amount, tx.Gas(), tx.GasPrice(), tx.Data()) + return signWithFaucet(t, txNew) +} + +func hugeGasPrice(t *utesting.T, s *Suite) *types.Transaction { + tx := getNextTxFromChain(t, s) + gasPrice := largeNumber(2) + var to common.Address + if tx.To() != nil { + to = *tx.To() + } + txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), gasPrice, tx.Data()) + return signWithFaucet(t, txNew) +} + +func hugeData(t *utesting.T, s *Suite) *types.Transaction { + tx := getNextTxFromChain(t, s) + var to common.Address + if tx.To() != nil { + to = *tx.To() + } + txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), tx.GasPrice(), largeBuffer(2)) + return signWithFaucet(t, txNew) +} + +func signWithFaucet(t *utesting.T, tx *types.Transaction) *types.Transaction { + signer := types.HomesteadSigner{} + signedTx, err := types.SignTx(tx, signer, faucetKey) + if err != nil { + t.Fatalf("could not sign tx: %v\n", err) + } + return signedTx +} diff --git a/cmd/devp2p/internal/ethtest/types.go b/cmd/devp2p/internal/ethtest/types.go index 4a0a45a1f4..66d6a08546 100644 --- a/cmd/devp2p/internal/ethtest/types.go +++ b/cmd/devp2p/internal/ethtest/types.go @@ -19,15 +19,12 @@ package ethtest import ( "crypto/ecdsa" "fmt" - "io" - "math/big" "reflect" "time" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/internal/utesting" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/rlpx" @@ -81,98 +78,55 @@ type Pong struct{} func (p Pong) Code() int { return 0x03 } // Status is the network packet for the status message for eth/64 and later. -type Status struct { - ProtocolVersion uint32 - NetworkID uint64 - TD *big.Int - Head common.Hash - Genesis common.Hash - ForkID forkid.ID -} +type Status eth.StatusPacket func (s Status) Code() int { return 16 } // NewBlockHashes is the network packet for the block announcements. -type NewBlockHashes []struct { - Hash common.Hash // Hash of one particular block being announced - Number uint64 // Number of one particular block being announced -} +type NewBlockHashes eth.NewBlockHashesPacket func (nbh NewBlockHashes) Code() int { return 17 } -// NewBlock is the network packet for the block propagation message. -type NewBlock struct { - Block *types.Block - TD *big.Int -} +type Transactions eth.TransactionsPacket -func (nb NewBlock) Code() int { return 23 } +func (t Transactions) Code() int { return 18 } // GetBlockHeaders represents a block header query. -type GetBlockHeaders struct { - Origin hashOrNumber // Block from which to retrieve headers - Amount uint64 // Maximum number of headers to retrieve - Skip uint64 // Blocks to skip between consecutive headers - Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis) -} +type GetBlockHeaders eth.GetBlockHeadersPacket func (g GetBlockHeaders) Code() int { return 19 } -type BlockHeaders []*types.Header +type BlockHeaders eth.BlockHeadersPacket func (bh BlockHeaders) Code() int { return 20 } -// HashOrNumber is a combined field for specifying an origin block. -type hashOrNumber struct { - Hash common.Hash // Block hash from which to retrieve headers (excludes Number) - Number uint64 // Block hash from which to retrieve headers (excludes Hash) -} - -// EncodeRLP is a specialized encoder for hashOrNumber to encode only one of the -// two contained union fields. -func (hn *hashOrNumber) EncodeRLP(w io.Writer) error { - if hn.Hash == (common.Hash{}) { - return rlp.Encode(w, hn.Number) - } - if hn.Number != 0 { - return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number) - } - return rlp.Encode(w, hn.Hash) -} - -// DecodeRLP is a specialized decoder for hashOrNumber to decode the contents -// into either a block hash or a block number. -func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error { - _, size, _ := s.Kind() - origin, err := s.Raw() - if err == nil { - switch { - case size == 32: - err = rlp.DecodeBytes(origin, &hn.Hash) - case size <= 8: - err = rlp.DecodeBytes(origin, &hn.Number) - default: - err = fmt.Errorf("invalid input size %d for origin", size) - } - } - return err -} - // GetBlockBodies represents a GetBlockBodies request -type GetBlockBodies []common.Hash +type GetBlockBodies eth.GetBlockBodiesPacket func (gbb GetBlockBodies) Code() int { return 21 } // BlockBodies is the network packet for block content distribution. -type BlockBodies []*types.Body +type BlockBodies eth.BlockBodiesPacket func (bb BlockBodies) Code() int { return 22 } +// NewBlock is the network packet for the block propagation message. +type NewBlock eth.NewBlockPacket + +func (nb NewBlock) Code() int { return 23 } + +// NewPooledTransactionHashes is the network packet for the tx hash propagation message. +type NewPooledTransactionHashes eth.NewPooledTransactionHashesPacket + +func (nb NewPooledTransactionHashes) Code() int { return 24 } + // Conn represents an individual connection with a peer type Conn struct { *rlpx.Conn - ourKey *ecdsa.PrivateKey - ethProtocolVersion uint + ourKey *ecdsa.PrivateKey + negotiatedProtoVersion uint + ourHighestProtoVersion uint + caps []p2p.Cap } func (c *Conn) Read() Message { @@ -205,10 +159,14 @@ func (c *Conn) Read() Message { msg = new(NewBlock) case (NewBlockHashes{}).Code(): msg = new(NewBlockHashes) + case (Transactions{}).Code(): + msg = new(Transactions) + case (NewPooledTransactionHashes{}).Code(): + msg = new(NewPooledTransactionHashes) default: return errorf("invalid message code: %d", code) } - + // if message is devp2p, decode here if err := rlp.DecodeBytes(rawData, msg); err != nil { return errorf("could not rlp decode message: %v", err) } @@ -243,7 +201,12 @@ func (c *Conn) ReadAndServe(chain *Chain, timeout time.Duration) Message { } func (c *Conn) Write(msg Message) error { - payload, err := rlp.EncodeToBytes(msg) + // check if message is eth protocol message + var ( + payload []byte + err error + ) + payload, err = rlp.EncodeToBytes(msg) if err != nil { return err } @@ -260,11 +223,8 @@ func (c *Conn) handshake(t *utesting.T) Message { pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:] ourHandshake := &Hello{ Version: 5, - Caps: []p2p.Cap{ - {Name: "eth", Version: 64}, - {Name: "eth", Version: 65}, - }, - ID: pub0, + Caps: c.caps, + ID: pub0, } if err := c.Write(ourHandshake); err != nil { t.Fatalf("could not write to connection: %v", err) @@ -277,7 +237,7 @@ func (c *Conn) handshake(t *utesting.T) Message { c.SetSnappy(true) } c.negotiateEthProtocol(msg.Caps) - if c.ethProtocolVersion == 0 { + if c.negotiatedProtoVersion == 0 { t.Fatalf("unexpected eth protocol version") } return msg @@ -295,16 +255,16 @@ func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) { if capability.Name != "eth" { continue } - if capability.Version > highestEthVersion && capability.Version <= 65 { + if capability.Version > highestEthVersion && capability.Version <= c.ourHighestProtoVersion { highestEthVersion = capability.Version } } - c.ethProtocolVersion = highestEthVersion + c.negotiatedProtoVersion = highestEthVersion } // statusExchange performs a `Status` message exchange with the given // node. -func (c *Conn) statusExchange(t *utesting.T, chain *Chain) Message { +func (c *Conn) statusExchange(t *utesting.T, chain *Chain, status *Status) Message { defer c.SetDeadline(time.Time{}) c.SetDeadline(time.Now().Add(20 * time.Second)) @@ -314,14 +274,15 @@ loop: for { switch msg := c.Read().(type) { case *Status: - if msg.Head != chain.blocks[chain.Len()-1].Hash() { - t.Fatalf("wrong head block in status: %s", msg.Head.String()) + if have, want := msg.Head, chain.blocks[chain.Len()-1].Hash(); have != want { + t.Fatalf("wrong head block in status, want: %#x (block %d) have %#x", + want, chain.blocks[chain.Len()-1].NumberU64(), have) } - if msg.TD.Cmp(chain.TD(chain.Len())) != 0 { - t.Fatalf("wrong TD in status: %v", msg.TD) + if have, want := msg.TD.Cmp(chain.TD(chain.Len())), 0; have != want { + t.Fatalf("wrong TD in status: have %v want %v", have, want) } - if !reflect.DeepEqual(msg.ForkID, chain.ForkID()) { - t.Fatalf("wrong fork ID in status: %v", msg.ForkID) + if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) { + t.Fatalf("wrong fork ID in status: have %v, want %v", have, want) } message = msg break loop @@ -335,18 +296,21 @@ loop: } } // make sure eth protocol version is set for negotiation - if c.ethProtocolVersion == 0 { + if c.negotiatedProtoVersion == 0 { t.Fatalf("eth protocol version must be set in Conn") } - // write status message to client - status := Status{ - ProtocolVersion: uint32(c.ethProtocolVersion), - NetworkID: chain.chainConfig.GetChainID().Uint64(), - TD: chain.TD(chain.Len()), - Head: chain.blocks[chain.Len()-1].Hash(), - Genesis: chain.blocks[0].Hash(), - ForkID: chain.ForkID(), + if status == nil { + // write status message to client + status = &Status{ + ProtocolVersion: uint32(c.negotiatedProtoVersion), + NetworkID: chain.chainConfig.GetChainID().Uint64(), + TD: chain.TD(chain.Len()), + Head: chain.blocks[chain.Len()-1].Hash(), + Genesis: chain.blocks[0].Hash(), + ForkID: chain.ForkID(), + } } + if err := c.Write(status); err != nil { t.Fatalf("could not write to connection: %v", err) } @@ -362,7 +326,7 @@ func (c *Conn) waitForBlock(block *types.Block) error { timeout := time.Now().Add(20 * time.Second) c.SetReadDeadline(timeout) for { - req := &GetBlockHeaders{Origin: hashOrNumber{Hash: block.Hash()}, Amount: 1} + req := &GetBlockHeaders{Origin: eth.HashOrNumber{Hash: block.Hash()}, Amount: 1} if err := c.Write(req); err != nil { return err } diff --git a/cmd/devp2p/nodesetcmd.go b/cmd/devp2p/nodesetcmd.go index d56a1f3cff..16e77bb556 100644 --- a/cmd/devp2p/nodesetcmd.go +++ b/cmd/devp2p/nodesetcmd.go @@ -95,6 +95,7 @@ var filterFlags = map[string]nodeFilterC{ "-min-age": {1, minAgeFilter}, "-eth-network": {1, ethFilter}, "-les-server": {0, lesFilter}, + "-snap": {0, snapFilter}, } func parseFilters(args []string) ([]nodeFilter, error) { @@ -104,15 +105,15 @@ func parseFilters(args []string) ([]nodeFilter, error) { if !ok { return nil, fmt.Errorf("invalid filter %q", args[0]) } - if len(args) < fc.narg { - return nil, fmt.Errorf("filter %q wants %d arguments, have %d", args[0], fc.narg, len(args)) + if len(args)-1 < fc.narg { + return nil, fmt.Errorf("filter %q wants %d arguments, have %d", args[0], fc.narg, len(args)-1) } - filter, err := fc.fn(args[1:]) + filter, err := fc.fn(args[1 : 1+fc.narg]) if err != nil { return nil, fmt.Errorf("%s: %v", args[0], err) } filters = append(filters, filter) - args = args[fc.narg+1:] + args = args[1+fc.narg:] } return filters, nil } @@ -178,7 +179,7 @@ func ethFilter(args []string) (nodeFilter, error) { f := func(n nodeJSON) bool { var eth struct { ForkID forkid.ID - _ []rlp.RawValue `rlp:"tail"` + Tail []rlp.RawValue `rlp:"tail"` } if n.N.Load(enr.WithEntry("eth", ð)) != nil { return false @@ -191,9 +192,19 @@ func ethFilter(args []string) (nodeFilter, error) { func lesFilter(args []string) (nodeFilter, error) { f := func(n nodeJSON) bool { var les struct { - _ []rlp.RawValue `rlp:"tail"` + Tail []rlp.RawValue `rlp:"tail"` } return n.N.Load(enr.WithEntry("les", &les)) == nil } return f, nil } + +func snapFilter(args []string) (nodeFilter, error) { + f := func(n nodeJSON) bool { + var snap struct { + Tail []rlp.RawValue `rlp:"tail"` + } + return n.N.Load(enr.WithEntry("snap", &snap)) == nil + } + return f, nil +} diff --git a/cmd/devp2p/rlpxcmd.go b/cmd/devp2p/rlpxcmd.go index d90eb4687c..24a16f0b3c 100644 --- a/cmd/devp2p/rlpxcmd.go +++ b/cmd/devp2p/rlpxcmd.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/cmd/devp2p/internal/ethtest" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/internal/utesting" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/rlpx" "github.com/ethereum/go-ethereum/rlp" @@ -94,6 +95,14 @@ func rlpxEthTest(ctx *cli.Context) error { if ctx.NArg() < 3 { exit("missing path to chain.rlp as command-line argument") } - suite := ethtest.NewSuite(getNodeArg(ctx), ctx.Args()[1], ctx.Args()[2]) - return runTests(ctx, suite.AllTests()) + suite, err := ethtest.NewSuite(getNodeArg(ctx), ctx.Args()[1], ctx.Args()[2]) + if err != nil { + exit(err) + } + // check if given node supports eth66, and if so, run eth66 protocol tests as well + is66Failed, _ := utesting.Run(utesting.Test{Name: "Is_66", Fn: suite.Is_66}) + if is66Failed { + return runTests(ctx, suite.EthTests()) + } + return runTests(ctx, suite.AllEthTests()) } diff --git a/cmd/echaindb/cmd/readChainconfig.go b/cmd/echaindb/cmd/readChainconfig.go deleted file mode 100644 index b098952cd8..0000000000 --- a/cmd/echaindb/cmd/readChainconfig.go +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright © 2020 NAME HERE - -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. -*/ -package cmd - -import ( - "fmt" - "log" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/spf13/cobra" - "github.com/tidwall/pretty" -) - -// readChainconfigCmd represents the readChainconfig command -var readChainconfigCmd = &cobra.Command{ - Use: "read-chainconfig", - Short: "Print stored chain config", - Long: `Chain configs are stored in the database and can be printed (dumped). -This command does that. - -Use: - - read-chainconfig <0xgenesisHash> - -Example: - - read-chainconfig 0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3 - -`, - Run: func(cmd *cobra.Command, args []string) { - if len(args) == 0 { - log.Fatal("need canonical hash key for config") - } - - log.Println("Opening database...") - db, err := rawdb.NewLevelDBDatabase(chainDBPath, 256, 16, "") - if err != nil { - log.Fatal(err) - } - - data, err := db.Get(rawdb.ConfigKey(common.HexToHash(args[0]))) - if err != nil { - log.Fatal(err) - } - - data = pretty.Pretty(data) - fmt.Println(string(data)) - }, -} - -func init() { - rootCmd.AddCommand(readChainconfigCmd) - - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // readChainconfigCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // readChainconfigCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") -} diff --git a/cmd/echaindb/cmd/root.go b/cmd/echaindb/cmd/root.go deleted file mode 100644 index d4c8829938..0000000000 --- a/cmd/echaindb/cmd/root.go +++ /dev/null @@ -1,104 +0,0 @@ -/* -Copyright © 2020 NAME HERE - -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. -*/ -package cmd - -import ( - "errors" - "fmt" - "os" - - "github.com/spf13/cobra" - - homedir "github.com/mitchellh/go-homedir" - "github.com/spf13/viper" -) - -var cfgFile string - -var ( - chainDBPath string = "./chaindata" -) - -// rootCmd represents the base command when called without any subcommands -var rootCmd = &cobra.Command{ - Use: "echaindb", - Short: "A utility for inspecting and managing the chain database.", - Long: ``, - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - if chainDBPath == "" { - return errors.New("empty chaindb path") - } - f, err := os.Stat(chainDBPath) - if err != nil { - return err - } - if !f.IsDir() { - return errors.New("chaindb path is not a directory") - } - - return nil - }, -} - -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - if err := rootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(1) - } -} - -func init() { - cobra.OnInitialize(initConfig) - - // Here you will define your flags and configuration settings. - // Cobra supports persistent flags, which, if defined here, - // will be global for your application. - - rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.echaindb.yaml)") - rootCmd.PersistentFlags().StringVar(&chainDBPath, "chaindb", "./chaindata", "path to chaindata directory") - - // Cobra also supports local flags, which will only run - // when this action is called directly. - rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") -} - -// initConfig reads in config file and ENV variables if set. -func initConfig() { - if cfgFile != "" { - // Use config file from the flag. - viper.SetConfigFile(cfgFile) - } else { - // Find home directory. - home, err := homedir.Dir() - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - // Search config in home directory with name ".echaindb" (without extension). - viper.AddConfigPath(home) - viper.SetConfigName(".echaindb") - } - - viper.AutomaticEnv() // read in environment variables that match - - // If a config file is found, read it in. - if err := viper.ReadInConfig(); err == nil { - fmt.Println("Using config file:", viper.ConfigFileUsed()) - } -} diff --git a/cmd/echaindb/cmd/writeChainconfig.go b/cmd/echaindb/cmd/writeChainconfig.go deleted file mode 100644 index d4163a4297..0000000000 --- a/cmd/echaindb/cmd/writeChainconfig.go +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright © 2020 NAME HERE - -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. -*/ -package cmd - -import ( - "io/ioutil" - "log" - "os" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/params/confp/generic" - "github.com/spf13/cobra" -) - -// writeChainconfigCmd represents the writeChainconfig command -var writeChainconfigCmd = &cobra.Command{ - Use: "write-chainconfig", - Short: "Write a chain config to the database", - Long: `Chain configuration is stored in the chain database. -This command writes that value. - -Value to write is taken from standard input (stdin). - -Use: - - echaindb --chaindb write-chainconfig <0xgenesisHash> - -Example: - - cat myconfig.json | echaindb --chaindb ./path/to/chaindata write-chainconfig 0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3 - - echaindb --chaindb ./path/to/chaindata write-chainconfig 0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3 < myconfig.json - -`, - Run: func(cmd *cobra.Command, args []string) { - if len(args) == 0 { - log.Fatal("need canonical hash key for config") - } - - bs, err := ioutil.ReadAll(os.Stdin) - if err != nil { - log.Fatal(err) - } - - conf, err := generic.UnmarshalChainConfigurator(bs) - if err != nil { - log.Fatal(err) - } - - log.Println("Opening database...") - db, err := rawdb.NewLevelDBDatabase(chainDBPath, 256, 16, "") - if err != nil { - log.Fatal(err) - } - - rawdb.WriteChainConfig(db, common.HexToHash(args[0]), conf) - }, -} - -func init() { - rootCmd.AddCommand(writeChainconfigCmd) - - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // writeChainconfigCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // writeChainconfigCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") -} diff --git a/cmd/echaindb/main.go b/cmd/echaindb/main.go deleted file mode 100644 index 90e5a93421..0000000000 --- a/cmd/echaindb/main.go +++ /dev/null @@ -1,22 +0,0 @@ -/* -Copyright © 2020 NAME HERE - -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. -*/ -package main - -import "github.com/ethereum/go-ethereum/cmd/echaindb/cmd" - -func main() { - cmd.Execute() -} diff --git a/cmd/echainspec/main.go b/cmd/echainspec/main.go index f742b62fbc..c81bc530ac 100644 --- a/cmd/echainspec/main.go +++ b/cmd/echainspec/main.go @@ -56,7 +56,7 @@ var defaultChainspecValues = map[string]ctypes.Configurator{ "ropsten": params.DefaultRopstenGenesisBlock(), "rinkeby": params.DefaultRinkebyGenesisBlock(), "goerli": params.DefaultGoerliGenesisBlock(), - "yolov2": params.DefaultYoloV2GenesisBlock(), + "yolov3": params.DefaultYoloV3GenesisBlock(), } var defaultChainspecNames = func() []string { diff --git a/cmd/ethkey/generate.go b/cmd/ethkey/generate.go index c2aa1c6fb4..629d23da5b 100644 --- a/cmd/ethkey/generate.go +++ b/cmd/ethkey/generate.go @@ -26,7 +26,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/crypto" - "github.com/pborman/uuid" + "github.com/google/uuid" "gopkg.in/urfave/cli.v1" ) @@ -86,9 +86,12 @@ If you want to encrypt an existing private key, it can be specified by setting } // Create the keyfile object with a random UUID. - id := uuid.NewRandom() + UUID, err := uuid.NewRandom() + if err != nil { + utils.Fatalf("Failed to generate random uuid: %v", err) + } key := &keystore.Key{ - Id: id, + Id: UUID, Address: crypto.PubkeyToAddress(privateKey.PublicKey), PrivateKey: privateKey, } diff --git a/cmd/evm/README.md b/cmd/evm/README.md index 8f0848bde8..7742ccbbb7 100644 --- a/cmd/evm/README.md +++ b/cmd/evm/README.md @@ -30,7 +30,7 @@ Command line params that has to be supported are --trace.nomemory Disable full memory dump in traces --trace.nostack Disable stack output in traces --trace.noreturndata Disable return data output in traces - --output.basedir value Specifies where output files are placed. Will be created if it does not exist. (default: ".") + --output.basedir value Specifies where output files are placed. Will be created if it does not exist. --output.alloc alloc Determines where to put the alloc of the post-state. `stdout` - into the stdout output `stderr` - into the stderr output @@ -237,10 +237,10 @@ Example where blockhashes are provided: cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2 ``` ``` -{"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"returnData":null,"depth":1,"refund":0,"opName":"PUSH1","error":""} -{"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnStack":[],"returnData":null,"depth":1,"refund":0,"opName":"BLOCKHASH","error":""} -{"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnStack":[],"returnData":null,"depth":1,"refund":0,"opName":"STOP","error":""} -{"output":"","gasUsed":"0x17","time":112885} +{"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} +{"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"BLOCKHASH","error":""} +{"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"STOP","error":""} +{"output":"","gasUsed":"0x17","time":142709} ``` In this example, the caller has not provided the required blockhash: @@ -256,9 +256,9 @@ Error code: 4 Another thing that can be done, is to chain invocations: ``` ./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.alloc=stdout | ./evm t8n --input.alloc=stdin --input.env=./testdata/1/env.json --input.txs=./testdata/1/txs.json -INFO [08-03|15:25:15.168] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low" -INFO [08-03|15:25:15.169] rejected tx index=0 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low" -INFO [08-03|15:25:15.169] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low" +INFO [01-21|22:41:22.963] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1" +INFO [01-21|22:41:22.966] rejected tx index=0 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1" +INFO [01-21|22:41:22.967] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1" ``` What happened here, is that we first applied two identical transactions, so the second one was rejected. @@ -267,4 +267,3 @@ the same two transactions: this time, both failed due to too low nonce. In order to meaningfully chain invocations, one would need to provide meaningful new `env`, otherwise the actual blocknumber (exposed to the EVM) would not increase. - diff --git a/cmd/evm/compiler.go b/cmd/evm/compiler.go index c019a2fe70..40ad9313c5 100644 --- a/cmd/evm/compiler.go +++ b/cmd/evm/compiler.go @@ -23,7 +23,7 @@ import ( "github.com/ethereum/go-ethereum/cmd/evm/internal/compiler" - cli "gopkg.in/urfave/cli.v1" + "gopkg.in/urfave/cli.v1" ) var compileCommand = cli.Command{ diff --git a/cmd/evm/disasm.go b/cmd/evm/disasm.go index 5bc743aa88..68a09cbf50 100644 --- a/cmd/evm/disasm.go +++ b/cmd/evm/disasm.go @@ -23,7 +23,7 @@ import ( "strings" "github.com/ethereum/go-ethereum/core/asm" - cli "gopkg.in/urfave/cli.v1" + "gopkg.in/urfave/cli.v1" ) var disasmCommand = cli.Command{ diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 80c54f5b15..3424e4a5b6 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -111,7 +111,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig ctypes.ChainConfigura txIndex = 0 ) gaspool.AddGas(pre.Env.GasLimit) - vmContext := vm.Context{ + vmContext := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, Coinbase: pre.Env.Coinbase, @@ -120,7 +120,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig ctypes.ChainConfigura Difficulty: pre.Env.Difficulty, GasLimit: pre.Env.GasLimit, GetHash: getHash, - // GasPrice and Origin needs to be set per transaction } // If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's // done in StateProcessor.Process(block, ...), right before transactions are applied. @@ -145,22 +144,12 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig ctypes.ChainConfigura vmConfig.Tracer = tracer vmConfig.Debug = (tracer != nil) statedb.Prepare(tx.Hash(), blockHash, txIndex) - vmContext.GasPrice = msg.GasPrice() - vmContext.Origin = msg.From() + txContext := core.NewEVMTxContext(msg) + snapshot := statedb.Snapshot() + evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig) - evm := vm.NewEVM(vmContext, statedb, chainConfig, vmConfig) - if chainConfig.IsEnabled(chainConfig.GetEIP2929Transition, vmContext.BlockNumber) { - statedb.AddAddressToAccessList(msg.From()) - if dst := msg.To(); dst != nil { - statedb.AddAddressToAccessList(*dst) - // If it's a create-tx, the destination will be added inside evm.create - } - for addr := range vm.PrecompiledContractsForConfig(chainConfig, vmContext.BlockNumber) { - statedb.AddAddressToAccessList(addr) - } - } + // NOTE(ia): EIP2929 switch removed here for statedb.AddAddressToAccessList - snapshot := statedb.Snapshot() // (ret []byte, usedGas uint64, failed bool, err error) msgResult, err := core.ApplyMessage(evm, msg, gaspool) if err != nil { @@ -174,7 +163,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig ctypes.ChainConfigura return nil, nil, NewError(ErrorMissingBlockhash, hashError) } gasUsed += msgResult.UsedGas - // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx + + // Receipt: { var root []byte eip161d := chainConfig.IsEnabled(chainConfig.GetEIP161dTransition, vmContext.BlockNumber) @@ -184,22 +174,32 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig ctypes.ChainConfigura root = statedb.IntermediateRoot(eip161d).Bytes() } - receipt := types.NewReceipt(root, msgResult.Failed(), gasUsed) + // Create a new receipt for the transaction, storing the intermediate root and + // gas used by the tx. + receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: gasUsed} + if msgResult.Failed() { + receipt.Status = types.ReceiptStatusFailed + } else { + receipt.Status = types.ReceiptStatusSuccessful + } receipt.TxHash = tx.Hash() receipt.GasUsed = msgResult.UsedGas - // if the transaction created a contract, store the creation address in the receipt. + + // If the transaction created a contract, store the creation address in the receipt. if msg.To() == nil { - receipt.ContractAddress = crypto.CreateAddress(evm.Context.Origin, tx.Nonce()) + receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) } - // Set the receipt logs and create a bloom for filtering + + // Set the receipt logs and create the bloom filter. receipt.Logs = statedb.GetLogs(tx.Hash()) receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) - // These three are non-consensus fields + // These three are non-consensus fields: //receipt.BlockHash - //receipt.BlockNumber = + //receipt.BlockNumber receipt.TransactionIndex = uint(txIndex) receipts = append(receipts, receipt) } + txIndex++ } statedb.IntermediateRoot(chainConfig.IsEnabled(chainConfig.GetEIP161dTransition, vmContext.BlockNumber)) @@ -235,8 +235,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig ctypes.ChainConfigura } execRs := &ExecutionResult{ StateRoot: root, - TxRoot: types.DeriveSha(includedTxs, new(trie.Trie)), - ReceiptRoot: types.DeriveSha(receipts, new(trie.Trie)), + TxRoot: types.DeriveSha(includedTxs, trie.NewStackTrie(nil)), + ReceiptRoot: types.DeriveSha(receipts, trie.NewStackTrie(nil)), Bloom: types.CreateBloom(receipts), LogsHash: rlpHash(statedb.Logs()), Receipts: receipts, diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go index 424156ba82..a599462cc6 100644 --- a/cmd/evm/internal/t8ntool/flags.go +++ b/cmd/evm/internal/t8ntool/flags.go @@ -47,6 +47,11 @@ var ( Usage: "Specifies where output files are placed. Will be created if it does not exist.", Value: "", } + OutputBodyFlag = cli.StringFlag{ + Name: "output.body", + Usage: "If set, the RLP of the transactions (block body) will be written to this file.", + Value: "", + } OutputAllocFlag = cli.StringFlag{ Name: "output.alloc", Usage: "Determines where to put the `alloc` of the post-state.\n" + diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 419e117ec3..2e87699c8d 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -17,6 +17,7 @@ package t8ntool import ( + "crypto/ecdsa" "encoding/json" "fmt" "io/ioutil" @@ -25,12 +26,15 @@ import ( "path" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "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/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params/types/ctypes" "github.com/ethereum/go-ethereum/params/types/genesisT" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/tests" "gopkg.in/urfave/cli.v1" ) @@ -66,7 +70,7 @@ func (n *NumberedError) Code() int { type input struct { Alloc genesisT.GenesisAlloc `json:"alloc,omitempty"` Env *stEnv `json:"env,omitempty"` - Txs types.Transactions `json:"txs,omitempty"` + Txs []*txWithKey `json:"txs,omitempty"` } func Main(ctx *cli.Context) error { @@ -135,7 +139,7 @@ func Main(ctx *cli.Context) error { txStr = ctx.String(InputTxsFlag.Name) inputData = &input{} ) - + // Figure out the prestate alloc if allocStr == stdinSelector || envStr == stdinSelector || txStr == stdinSelector { decoder := json.NewDecoder(os.Stdin) decoder.Decode(inputData) @@ -151,7 +155,9 @@ func Main(ctx *cli.Context) error { return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling alloc-file: %v", err)) } } + prestate.Pre = inputData.Alloc + // Set the block environment if envStr != stdinSelector { inFile, err := os.Open(envStr) if err != nil { @@ -165,26 +171,8 @@ func Main(ctx *cli.Context) error { } inputData.Env = &env } - - if txStr != stdinSelector { - inFile, err := os.Open(txStr) - if err != nil { - return NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err)) - } - defer inFile.Close() - decoder := json.NewDecoder(inFile) - var txs types.Transactions - if err := decoder.Decode(&txs); err != nil { - return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling txs-file: %v", err)) - } - inputData.Txs = txs - } - - prestate.Pre = inputData.Alloc prestate.Env = *inputData.Env - txs = inputData.Txs - // Iterate over all the tests, run them and aggregate the results vmConfig := vm.Config{ Tracer: tracer, Debug: (tracer != nil), @@ -202,17 +190,105 @@ func Main(ctx *cli.Context) error { return err } + var txsWithKeys []*txWithKey + if txStr != stdinSelector { + inFile, err := os.Open(txStr) + if err != nil { + return NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err)) + } + defer inFile.Close() + decoder := json.NewDecoder(inFile) + if err := decoder.Decode(&txsWithKeys); err != nil { + return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling txs-file: %v", err)) + } + } else { + txsWithKeys = inputData.Txs + } + // We may have to sign the transactions. + signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number))) + + if txs, err = signUnsignedTransactions(txsWithKeys, signer); err != nil { + return NewError(ErrorJson, fmt.Errorf("Failed signing transactions: %v", err)) + } + + // Iterate over all the tests, run them and aggregate the results + // Run the test and aggregate the result state, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer) if err != nil { return err } + body, _ := rlp.EncodeToBytes(txs) // Dump the excution result - //postAlloc := state.DumpGenesisFormat(false, false, false) collector := make(Alloc) state.DumpToCollector(collector, false, false, false, nil, -1) - return dispatchOutput(ctx, baseDir, result, collector) + return dispatchOutput(ctx, baseDir, result, collector, body) + +} +// txWithKey is a helper-struct, to allow us to use the types.Transaction along with +// a `secretKey`-field, for input +type txWithKey struct { + key *ecdsa.PrivateKey + tx *types.Transaction +} + +func (t *txWithKey) UnmarshalJSON(input []byte) error { + // Read the secretKey, if present + type sKey struct { + Key *common.Hash `json:"secretKey"` + } + var key sKey + if err := json.Unmarshal(input, &key); err != nil { + return err + } + if key.Key != nil { + k := key.Key.Hex()[2:] + if ecdsaKey, err := crypto.HexToECDSA(k); err != nil { + return err + } else { + t.key = ecdsaKey + } + } + // Now, read the transaction itself + var tx types.Transaction + if err := json.Unmarshal(input, &tx); err != nil { + return err + } + t.tx = &tx + return nil +} + +// signUnsignedTransactions converts the input txs to canonical transactions. +// +// The transactions can have two forms, either +// 1. unsigned or +// 2. signed +// For (1), r, s, v, need so be zero, and the `secretKey` needs to be set. +// If so, we sign it here and now, with the given `secretKey` +// If the condition above is not met, then it's considered a signed transaction. +// +// To manage this, we read the transactions twice, first trying to read the secretKeys, +// and secondly to read them with the standard tx json format +func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Transactions, error) { + var signedTxs []*types.Transaction + for i, txWithKey := range txs { + tx := txWithKey.tx + key := txWithKey.key + v, r, s := tx.RawSignatureValues() + if key != nil && v.BitLen()+r.BitLen()+s.BitLen() == 0 { + // This transaction needs to be signed + signed, err := types.SignTx(tx, signer, key) + if err != nil { + return nil, NewError(ErrorJson, fmt.Errorf("Tx %d: failed to sign tx: %v", i, err)) + } + signedTxs = append(signedTxs, signed) + } else { + // Already signed + signedTxs = append(signedTxs, tx) + } + } + return signedTxs, nil } type Alloc map[common.Address]genesisT.GenesisAccount @@ -243,15 +319,17 @@ func saveFile(baseDir, filename string, data interface{}) error { if err != nil { return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err)) } - if err = ioutil.WriteFile(path.Join(baseDir, filename), b, 0644); err != nil { + location := path.Join(baseDir, filename) + if err = ioutil.WriteFile(location, b, 0644); err != nil { return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err)) } + log.Info("Wrote file", "file", location) return nil } // dispatchOutput writes the output data to either stderr or stdout, or to the specified // files -func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc) error { +func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes) error { stdOutObject := make(map[string]interface{}) stdErrObject := make(map[string]interface{}) dispatch := func(baseDir, fName, name string, obj interface{}) error { @@ -260,6 +338,8 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a stdOutObject[name] = obj case "stderr": stdErrObject[name] = obj + case "": + // don't save default: // save to file if err := saveFile(baseDir, fName, obj); err != nil { return err @@ -273,6 +353,9 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil { return err } + if err := dispatch(baseDir, ctx.String(OutputBodyFlag.Name), "body", body); err != nil { + return err + } if len(stdOutObject) > 0 { b, err := json.MarshalIndent(stdOutObject, "", " ") if err != nil { diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 35c672142d..8a3e4e0ea2 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -149,6 +149,7 @@ var stateTransitionCommand = cli.Command{ t8ntool.OutputBasedir, t8ntool.OutputAllocFlag, t8ntool.OutputResultFlag, + t8ntool.OutputBodyFlag, t8ntool.InputAllocFlag, t8ntool.InputEnvFlag, t8ntool.InputTxsFlag, diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 8bdb2429cb..47cf1cdc54 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -40,7 +40,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/types/ctypes" "github.com/ethereum/go-ethereum/params/types/genesisT" - cli "gopkg.in/urfave/cli.v1" + "gopkg.in/urfave/cli.v1" ) var runCommand = cli.Command{ diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index d42017e83f..88225bda87 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -28,7 +28,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/tests" - cli "gopkg.in/urfave/cli.v1" + "gopkg.in/urfave/cli.v1" ) var stateTestCommand = cli.Command{ diff --git a/cmd/evm/testdata/8/alloc.json b/cmd/evm/testdata/8/alloc.json new file mode 100644 index 0000000000..1d1b5f86c6 --- /dev/null +++ b/cmd/evm/testdata/8/alloc.json @@ -0,0 +1,11 @@ +{ + "0x000000000000000000000000000000000000aaaa": { + "balance": "0x03", + "code": "0x5854505854", + "nonce": "0x1" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x100000", + "nonce": "0x00" + } +} \ No newline at end of file diff --git a/cmd/evm/testdata/8/env.json b/cmd/evm/testdata/8/env.json new file mode 100644 index 0000000000..8b91934724 --- /dev/null +++ b/cmd/evm/testdata/8/env.json @@ -0,0 +1,7 @@ +{ + "currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty": "0x20000", + "currentGasLimit": "0x1000000000", + "currentNumber": "0x1000000", + "currentTimestamp": "0x04" +} \ No newline at end of file diff --git a/cmd/evm/testdata/8/readme.md b/cmd/evm/testdata/8/readme.md new file mode 100644 index 0000000000..778fc6151a --- /dev/null +++ b/cmd/evm/testdata/8/readme.md @@ -0,0 +1,63 @@ +## EIP-2930 testing + +This test contains testcases for EIP-2930, which uses transactions with access lists. + +### Prestate + +The alloc portion contains one contract (`0x000000000000000000000000000000000000aaaa`), containing the +following code: `0x5854505854`: `PC ;SLOAD; POP; PC; SLOAD`. + +Essentialy, this contract does `SLOAD(0)` and `SLOAD(3)`. + +The alloc also contains some funds on `0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b`. + +## Transactions + +There are three transactions, each invokes the contract above. + +1. ACL-transaction, which contains some non-used slots +2. Regular transaction +3. ACL-transaction, which contains the slots `1` and `3` in `0x000000000000000000000000000000000000aaaa` + +## Execution + +Running it yields: +``` +dir=./testdata/8 && ./evm t8n --state.fork=Berlin --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --trace && cat trace-* | grep SLOAD +{"pc":1,"op":84,"gas":"0x484be","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +{"pc":4,"op":84,"gas":"0x47c86","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x3"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +{"pc":1,"op":84,"gas":"0x49cf6","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +{"pc":4,"op":84,"gas":"0x494be","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x3"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +{"pc":1,"op":84,"gas":"0x484be","gasCost":"0x64","memory":"0x","memSize":0,"stack":["0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +{"pc":4,"op":84,"gas":"0x48456","gasCost":"0x64","memory":"0x","memSize":0,"stack":["0x3"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} + +``` + +Simlarly, we can provide the input transactions via `stdin` instead of as file: + +``` +dir=./testdata/8 \ + && cat $dir/txs.json | jq "{txs: .}" \ + | ./evm t8n --state.fork=Berlin \ + --input.alloc=$dir/alloc.json \ + --input.txs=stdin \ + --input.env=$dir/env.json \ + --trace \ + && cat trace-* | grep SLOAD + +{"pc":1,"op":84,"gas":"0x484be","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +{"pc":4,"op":84,"gas":"0x47c86","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x3"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +{"pc":1,"op":84,"gas":"0x49cf6","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +{"pc":4,"op":84,"gas":"0x494be","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x3"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +{"pc":1,"op":84,"gas":"0x484be","gasCost":"0x64","memory":"0x","memSize":0,"stack":["0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +{"pc":4,"op":84,"gas":"0x48456","gasCost":"0x64","memory":"0x","memSize":0,"stack":["0x3"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +``` + +If we try to execute it on older rules: +``` +dir=./testdata/8 && ./evm t8n --state.fork=Istanbul --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json +INFO [01-21|23:21:51.265] rejected tx index=0 hash="d2818d…6ab3da" error="tx type not supported" +INFO [01-21|23:21:51.265] rejected tx index=1 hash="26ea00…81c01b" from=0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B error="nonce too high: address 0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B, tx: 1 state: 0" +INFO [01-21|23:21:51.265] rejected tx index=2 hash="698d01…369cee" error="tx type not supported" +``` +Number `1` and `3` are not applicable, and therefore number `2` has wrong nonce. \ No newline at end of file diff --git a/cmd/evm/testdata/8/txs.json b/cmd/evm/testdata/8/txs.json new file mode 100644 index 0000000000..35142ba234 --- /dev/null +++ b/cmd/evm/testdata/8/txs.json @@ -0,0 +1,58 @@ +[ + { + "gas": "0x4ef00", + "gasPrice": "0x1", + "chainId": "0x1", + "input": "0x", + "nonce": "0x0", + "to": "0x000000000000000000000000000000000000aaaa", + "value": "0x1", + "type" : "0x1", + "accessList": [ + {"address": "0x0000000000000000000000000000000000000000", + "storageKeys": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ] + } + ], + "v": "0x0", + "r": "0x0", + "s": "0x0", + "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + }, + { + "gas": "0x4ef00", + "gasPrice": "0x1", + "input": "0x", + "nonce": "0x1", + "to": "0x000000000000000000000000000000000000aaaa", + "value": "0x2", + "v": "0x0", + "r": "0x0", + "s": "0x0", + "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + }, + { + "gas": "0x4ef00", + "gasPrice": "0x1", + "chainId": "0x1", + "input": "0x", + "nonce": "0x2", + "to": "0x000000000000000000000000000000000000aaaa", + "value": "0x1", + "type" : "0x1", + "accessList": [ + {"address": "0x000000000000000000000000000000000000aaaa", + "storageKeys": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000003" + ] + } + ], + "v": "0x0", + "r": "0x0", + "s": "0x0", + "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + } +] diff --git a/cmd/faucet/README.md b/cmd/faucet/README.md new file mode 100644 index 0000000000..364689a782 --- /dev/null +++ b/cmd/faucet/README.md @@ -0,0 +1,50 @@ +# Faucet + +The `faucet` is a simplistic web application with the goal of distributing small amounts of Ether in private and test networks. + +Users need to post their Ethereum addresses to fund in a Twitter status update or public Facebook post and share the link to the faucet. The faucet will in turn deduplicate user requests and send the Ether. After a funding round, the faucet prevents the same user requesting again for a pre-configured amount of time, proportional to the amount of Ether requested. + +## Operation + +The `faucet` is a single binary app (everything included) with all configurations set via command line flags and a few files. + +First thing's first, the `faucet` needs to connect to an Ethereum network, for which it needs the necessary genesis and network infos. Each of the following flags must be set: + +- `--genesis` is a path to a file containin the network `genesis.json` +- `--network` is the devp2p network id used during connection +- `--bootnodes` is a list of `enode://` ids to join the network through + +The `faucet` will use the `les` protocol to join the configured Ethereum network and will store its data in `$HOME/.faucet` (currently not configurable). + +## Funding + +To be able to distribute funds, the `faucet` needs access to an already funded Ethereum account. This can be configured via: + +- `--account.json` is a path to the Ethereum account's JSON key file +- `--account.pass` is a path to a text file with the decryption passphrase + +The faucet is able to distribute various amounts of Ether in exchange for various timeouts. These can be configured via: + +- `--faucet.amount` is the number of Ethers to send by default +- `--faucet.minutes` is the time to wait before allowing a rerequest +- `--faucet.tiers` is the funding tiers to support (x3 time, x2.5 funds) + +## Sybil protection + +To prevent the same user from exhausting funds in a loop, the `faucet` ties requests to social networks and captcha resolvers. + +Captcha protection uses Google's invisible ReCaptcha, thus the `faucet` needs to run on a live domain. The domain needs to be registered in Google's systems to retrieve the captcha API token and secrets. After doing so, captcha protection may be enabled via: + +- `--captcha.token` is the API token for ReCaptcha +- `--captcha.secret` is the API secret for ReCaptcha + +Sybil protection via Twitter requires an API key as of 15th December, 2020. To obtain it, a Twitter user must be upgraded to developer status and a new Twitter App deployed with it. The app's `Bearer` token is required by the faucet to retrieve tweet data: + +- `--twitter.token` is the Bearer token for `v2` API access +- `--twitter.token.v1` is the Bearer token for `v1` API access + +Sybil protection via Facebook uses the website to directly download post data thus does not currently require an API configuration. + +## Miscellaneous + +Beside the above - mostly essential - CLI flags, there are a number that can be used to fine tune the `faucet`'s operation. Please see `faucet --help` for a full list. \ No newline at end of file diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index db51f60afb..01023ad5c3 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with go-ethereum. If not, see . -// faucet is a Ether faucet backed by a light client. +// faucet is an Ether faucet backed by a light client. package main //go:generate go-bindata -nometadata -o website.go faucet.html @@ -50,13 +50,13 @@ import ( "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/eth/ethconfig" "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/discv5" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/params" @@ -293,7 +293,7 @@ func main() { // Load and parse the genesis block requested by the user var genesis *genesisT.Genesis - var enodes []*discv5.Node + var enodes []*enode.Node var blob []byte // client will be used if the faucet is attaching. If not it won't be touched. @@ -347,8 +347,9 @@ func main() { log.Info("Using chain/net config", "network id", *netFlag, "bootnodes", *bootFlag, "chain config", fmt.Sprintf("%v", genesis.Config)) // Convert the bootnodes to internal enode representations - for _, boot := range utils.SplitAndTrim(*bootFlag) { - if url, err := discv5.ParseNode(boot); err == nil { + var enodes []*enode.Node + for _, boot := range strings.Split(*bootFlag, ",") { + if url, err := enode.Parse(enode.ValidSchemes, boot); err == nil { enodes = append(enodes, url) } else { log.Error("Failed to parse bootnode URL", "url", boot, "err", err) @@ -464,7 +465,7 @@ type faucet struct { 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 + conns []*wsConn // Currently live websocket connections timeouts map[string]time.Time // History of users and their funding timeouts reqs []*request // Currently pending funding requests update chan struct{} // Channel to signal request updates @@ -472,6 +473,13 @@ type faucet struct { lock sync.RWMutex // Lock protecting the faucet's internals } +// wsConn wraps a websocket connection with a write mutex as the underlying +// websocket library does not synchronize access to the stream. +type wsConn struct { + conn *websocket.Conn + wlock sync.Mutex +} + const ( multiFaucetNodeName = "MultiFaucet" coreFaucetNodeName = "CoreFaucet" @@ -508,7 +516,7 @@ func migrateFaucetDirectory(faucetDataDir string) error { } // startStack starts the node stack, ensures peering, and assigns the respective ethclient to the faucet. -func (f *faucet) startStack(genesis *genesisT.Genesis, port int, enodes []*discv5.Node, network uint64) error { +func (f *faucet) startStack(genesis *genesisT.Genesis, port int, enodes []*enode.Node, network uint64) error { genesisHash := core.GenesisToBlock(genesis, nil).Hash() @@ -539,7 +547,7 @@ func (f *faucet) startStack(genesis *genesisT.Genesis, port int, enodes []*discv } // Assemble the Ethereum light client protocol - cfg := eth.DefaultConfig + cfg := ethconfig.Defaults cfg.NetworkId = network cfg.Genesis = genesis @@ -548,10 +556,10 @@ func (f *faucet) startStack(genesis *genesisT.Genesis, port int, enodes []*discv cfg.SyncMode = downloader.LightSync case "fast": cfg.SyncMode = downloader.FastSync - cfg.ProtocolVersions = eth.DefaultProtocolVersions + cfg.ProtocolVersions = ethconfig.Defaults.ProtocolVersions case "full": cfg.SyncMode = downloader.FullSync - cfg.ProtocolVersions = eth.DefaultProtocolVersions + cfg.ProtocolVersions = ethconfig.Defaults.ProtocolVersions default: panic("impossible to reach, this should be handled in the auditFlagUse function") } @@ -572,7 +580,7 @@ func (f *faucet) startStack(genesis *genesisT.Genesis, port int, enodes []*discv default: utils.SetDNSDiscoveryDefaults(&cfg, core.GenesisToBlock(genesis, nil).Hash()) } - log.Info("Config discovery", "urls", cfg.DiscoveryURLs) + log.Info("Config discovery", "urls", cfg.EthDiscoveryURLs) // Establish the backend and enable stats reporting if configured to do so. switch *syncmodeFlag { @@ -673,13 +681,14 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { defer conn.Close() f.lock.Lock() - f.conns = append(f.conns, conn) + wsconn := &wsConn{conn: conn} + f.conns = append(f.conns, wsconn) f.lock.Unlock() defer func() { f.lock.Lock() for i, c := range f.conns { - if c == conn { + if c.conn == conn { f.conns = append(f.conns[:i], f.conns[i+1:]...) break } @@ -707,7 +716,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { if head == nil || balance == nil { // Report the faucet offline until initial stats are ready //lint:ignore ST1005 This error is to be displayed in the browser - if err = sendError(conn, errors.New("Faucet offline")); err != nil { + if err = sendError(wsconn, errors.New("Faucet offline")); err != nil { log.Warn("Failed to send faucet error to client", "err", err) return } @@ -723,7 +732,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { log.Warn("Failed to get peer count", "error", err) return } - if err = send(conn, map[string]interface{}{ + if err = send(wsconn, map[string]interface{}{ "funds": new(big.Int).Div(balance, ether), "funded": nonce, "peers": peerCount, @@ -732,7 +741,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { log.Warn("Failed to send initial stats to client", "err", err) return } - if err = send(conn, head, 3*time.Second); err != nil { + if err = send(wsconn, head, 3*time.Second); err != nil { log.Warn("Failed to send initial header to client", "err", err) return } @@ -748,7 +757,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { return } if !*noauthFlag && !strings.HasPrefix(msg.URL, "https://twitter.com/") && !strings.HasPrefix(msg.URL, "https://www.facebook.com/") { - if err = sendError(conn, errors.New("URL doesn't link to supported services")); err != nil { + if err = sendError(wsconn, errors.New("URL doesn't link to supported services")); err != nil { log.Warn("Failed to send URL error to client", "err", err) return } @@ -756,7 +765,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { } if msg.Tier >= uint(*tiersFlag) { //lint:ignore ST1005 This error is to be displayed in the browser - if err = sendError(conn, errors.New("Invalid funding tier requested")); err != nil { + if err = sendError(wsconn, errors.New("Invalid funding tier requested")); err != nil { log.Warn("Failed to send tier error to client", "err", err) return } @@ -772,7 +781,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { res, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", form) if err != nil { - if err = sendError(conn, err); err != nil { + if err = sendError(wsconn, err); err != nil { log.Warn("Failed to send captcha post error to client", "err", err) return } @@ -785,7 +794,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { err = json.NewDecoder(res.Body).Decode(&result) res.Body.Close() if err != nil { - if err = sendError(conn, err); err != nil { + if err = sendError(wsconn, err); err != nil { log.Warn("Failed to send captcha decode error to client", "err", err) return } @@ -794,7 +803,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { if !result.Success { log.Warn("Captcha verification failed", "err", string(result.Errors)) //lint:ignore ST1005 it's funny and the robot won't mind - if err = sendError(conn, errors.New("Beep-bop, you're a robot!")); err != nil { + if err = sendError(wsconn, errors.New("Beep-bop, you're a robot!")); err != nil { log.Warn("Failed to send captcha failure to client", "err", err) return } @@ -822,7 +831,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { err = errors.New("Something funky happened, please open an issue at https://github.com/ethereum/go-ethereum/issues") } if err != nil { - if err = sendError(conn, err); err != nil { + if err = sendError(wsconn, err); err != nil { log.Warn("Failed to send prefix error to client", "err", err) return } @@ -854,7 +863,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { signed, err := f.keystore.SignTx(f.account, tx, chainId) if err != nil { f.lock.Unlock() - if err = sendError(conn, err); err != nil { + if err = sendError(wsconn, err); err != nil { log.Warn("Failed to send transaction creation error to client", "err", err) return } @@ -863,7 +872,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { // Submit the transaction and mark as funded if successful if err := f.client.SendTransaction(context.Background(), signed); err != nil { f.lock.Unlock() - if err = sendError(conn, err); err != nil { + if err = sendError(wsconn, err); err != nil { log.Warn("Failed to send transaction transmission error to client", "err", err) return } @@ -885,13 +894,13 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { // Send an error if too frequent funding, othewise a success if !fund { - if err = sendError(conn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(time.Until(timeout)))); err != nil { // nolint: gosimple + if err = sendError(wsconn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(time.Until(timeout)))); err != nil { // nolint: gosimple log.Warn("Failed to send funding error to client", "err", err) return } continue } - if err = sendSuccess(conn, fmt.Sprintf("Funding request accepted for %s into %s", username, address.Hex())); err != nil { + if err = sendSuccess(wsconn, fmt.Sprintf("Funding request accepted for %s into %s", username, address.Hex())); err != nil { log.Warn("Failed to send funding success to client", "err", err) return } @@ -988,12 +997,12 @@ func (f *faucet) loop() { "requests": f.reqs, }, time.Second); err != nil { log.Warn("Failed to send stats to client", "err", err) - conn.Close() + conn.conn.Close() continue } if err := send(conn, head, time.Second); err != nil { log.Warn("Failed to send header to client", "err", err) - conn.Close() + conn.conn.Close() } } f.lock.RUnlock() @@ -1015,7 +1024,7 @@ func (f *faucet) loop() { for _, conn := range f.conns { if err := send(conn, map[string]interface{}{"requests": f.reqs}, time.Second); err != nil { log.Warn("Failed to send requests to client", "err", err) - conn.Close() + conn.conn.Close() } } f.lock.RUnlock() @@ -1029,23 +1038,25 @@ func (f *faucet) loop() { // sends transmits a data packet to the remote end of the websocket, but also // setting a write deadline to prevent waiting forever on the node. -func send(conn *websocket.Conn, value interface{}, timeout time.Duration) error { +func send(conn *wsConn, value interface{}, timeout time.Duration) error { if timeout == 0 { timeout = 60 * time.Second } - conn.SetWriteDeadline(time.Now().Add(timeout)) - return conn.WriteJSON(value) + conn.wlock.Lock() + defer conn.wlock.Unlock() + conn.conn.SetWriteDeadline(time.Now().Add(timeout)) + return conn.conn.WriteJSON(value) } // sendError transmits an error to the remote end of the websocket, also setting // the write deadline to 1 second to prevent waiting forever. -func sendError(conn *websocket.Conn, err error) error { +func sendError(conn *wsConn, err error) error { return send(conn, map[string]string{"error": err.Error()}, time.Second) } // sendSuccess transmits a success message to the remote end of the websocket, also // setting the write deadline to 1 second to prevent waiting forever. -func sendSuccess(conn *websocket.Conn, msg string) error { +func sendSuccess(conn *wsConn, msg string) error { return send(conn, map[string]string{"success": msg}, time.Second) } @@ -1209,7 +1220,13 @@ func authFacebook(url string) (string, string, common.Address, error) { // Facebook's Graph API isn't really friendly with direct links. Still, we don't // want to do ask read permissions from users, so just load the public posts and // scrape it for the Ethereum address and profile URL. - res, err := http.Get(url) + // + // Facebook recently changed their desktop webpage to use AJAX for loading post + // content, so switch over to the mobile site for now. Will probably end up having + // to use the API eventually. + crawl := strings.Replace(url, "www.facebook.com", "m.facebook.com", 1) + + res, err := http.Get(crawl) if err != nil { return "", "", common.Address{}, err } diff --git a/cmd/faucet/faucet.html b/cmd/faucet/faucet.html index ba14333186..dad5ad84f2 100644 --- a/cmd/faucet/faucet.html +++ b/cmd/faucet/faucet.html @@ -177,7 +177,7 @@

How does this work?

} // Iterate over our entire local collection and re-render the funding table var content = ""; - for (var i=0; i= 0; i--) { var done = requests[i].time == ""; var elapsed = moment().unix()-moment(requests[i].time).unix(); diff --git a/cmd/faucet/faucet_test.go b/cmd/faucet/faucet_test.go index c991d899fd..aa27d52bda 100644 --- a/cmd/faucet/faucet_test.go +++ b/cmd/faucet/faucet_test.go @@ -6,6 +6,8 @@ import ( "path/filepath" "testing" "time" + + "github.com/ethereum/go-ethereum/common" ) func TestMigrateFaucetDirectory(t *testing.T) { @@ -44,3 +46,23 @@ func TestMigrateFaucetDirectory(t *testing.T) { return nil }) } + +func TestFacebook(t *testing.T) { + for _, tt := range []struct { + url string + want common.Address + }{ + { + "https://www.facebook.com/fooz.gazonk/posts/2837228539847129", + common.HexToAddress("0xDeadDeaDDeaDbEefbEeFbEEfBeeFBeefBeeFbEEF"), + }, + } { + _, _, gotAddress, err := authFacebook(tt.url) + if err != nil { + t.Fatal(err) + } + if gotAddress != tt.want { + t.Fatalf("address wrong, have %v want %v", gotAddress, tt.want) + } + } +} diff --git a/cmd/faucet/website.go b/cmd/faucet/website.go index a091d24919..aed067893a 100644 --- a/cmd/faucet/website.go +++ b/cmd/faucet/website.go @@ -1,6 +1,6 @@ // Code generated by go-bindata. DO NOT EDIT. // sources: -// faucet.html (11.27kB) +// faucet.html (11.276kB) package main @@ -69,7 +69,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _faucetHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x5a\x6d\x93\xdb\x36\x92\xfe\x3c\xfe\x15\x1d\x9e\xbd\x92\xce\x43\x52\x33\x63\x7b\x7d\x12\xa9\x94\xd7\x9b\xdd\xf3\xd5\x5d\x92\x4a\x9c\xba\xdb\xca\xa6\xae\x40\xb2\x25\xc2\x03\x02\x0c\x00\x4a\xa3\x4c\xe9\xbf\x5f\x35\x40\x52\xd4\xcb\x4c\xec\xb5\xaf\x6a\xfd\x61\x4c\x02\x8d\x46\xa3\xfb\x69\xf4\x0b\x95\x7c\xf5\xe7\xef\xde\xbe\xff\xdb\xf7\xdf\x40\x69\x2b\xb1\x78\x92\xd0\x7f\x20\x98\x5c\xa5\x01\xca\x60\xf1\xe4\x22\x29\x91\x15\x8b\x27\x17\x17\x49\x85\x96\x41\x5e\x32\x6d\xd0\xa6\x41\x63\x97\xe1\xeb\x60\x3f\x51\x5a\x5b\x87\xf8\x6b\xc3\xd7\x69\xf0\x3f\xe1\x4f\x6f\xc2\xb7\xaa\xaa\x99\xe5\x99\xc0\x00\x72\x25\x2d\x4a\x9b\x06\xef\xbe\x49\xb1\x58\xe1\x60\x9d\x64\x15\xa6\xc1\x9a\xe3\xa6\x56\xda\x0e\x48\x37\xbc\xb0\x65\x5a\xe0\x9a\xe7\x18\xba\x97\x4b\xe0\x92\x5b\xce\x44\x68\x72\x26\x30\xbd\x0a\x16\x4f\x88\x8f\xe5\x56\xe0\xe2\xfe\x3e\xfa\x16\xed\x46\xe9\xdb\xdd\x6e\x06\x6f\x1a\x5b\xa2\xb4\x3c\x67\x16\x0b\xf8\x0b\x6b\x72\xb4\x49\xec\x29\xdd\x22\xc1\xe5\x2d\x94\x1a\x97\x69\x40\xa2\x9b\x59\x1c\xe7\x85\xfc\x60\xa2\x5c\xa8\xa6\x58\x0a\xa6\x31\xca\x55\x15\xb3\x0f\xec\x2e\x16\x3c\x33\xb1\xdd\x70\x6b\x51\x87\x99\x52\xd6\x58\xcd\xea\xf8\x26\xba\x89\xfe\x18\xe7\xc6\xc4\xfd\x58\x54\x71\x19\xe5\xc6\x04\xa0\x51\xa4\x81\xb1\x5b\x81\xa6\x44\xb4\x01\xc4\x8b\x7f\x6c\xdf\xa5\x92\x36\x64\x1b\x34\xaa\xc2\xf8\x45\xf4\xc7\x68\xea\xb6\x1c\x0e\x3f\xbe\x2b\x6d\x6b\x72\xcd\x6b\x0b\x46\xe7\x1f\xbd\xef\x87\x5f\x1b\xd4\xdb\xf8\x26\xba\x8a\xae\xda\x17\xb7\xcf\x07\x13\x2c\x92\xd8\x33\x5c\x7c\x16\xef\x50\x2a\xbb\x8d\xaf\xa3\x17\xd1\x55\x5c\xb3\xfc\x96\xad\xb0\xe8\x76\xa2\xa9\xa8\x1b\xfc\x62\xfb\x3e\x64\xc3\x0f\xc7\x26\xfc\x12\x9b\x55\xaa\x42\x69\xa3\x0f\x26\xbe\x8e\xae\x5e\x47\xd3\x6e\xe0\x94\xbf\xdb\x80\x8c\x46\x5b\x5d\x44\x6b\xd4\x84\x5c\x11\xe6\x28\x2d\x6a\xb8\xa7\xd1\x8b\x8a\xcb\xb0\x44\xbe\x2a\xed\x0c\xae\xa6\xd3\x67\xf3\x73\xa3\xeb\xd2\x0f\x17\xdc\xd4\x82\x6d\x67\xb0\x14\x78\xe7\x87\x98\xe0\x2b\x19\x72\x8b\x95\x99\x81\xe7\xec\x26\x76\x6e\xcf\x5a\xab\x95\x46\x63\xda\xcd\x6a\x65\xb8\xe5\x4a\xce\x08\x51\xcc\xf2\x35\x9e\xa3\x35\x35\x93\x27\x0b\x58\x66\x94\x68\x2c\x1e\x09\x92\x09\x95\xdf\xfa\x31\xe7\xcd\xc3\x43\xe4\x4a\x28\x3d\x83\x4d\xc9\xdb\x65\xe0\x36\x82\x5a\x63\xcb\x1e\x6a\x56\x14\x5c\xae\x66\xf0\xaa\x6e\xcf\x03\x15\xd3\x2b\x2e\x67\x30\xdd\x2f\x49\xe2\x4e\x8d\x49\xec\x2f\xae\x27\x17\x49\xa6\x8a\xad\xb3\x61\xc1\xd7\x90\x0b\x66\x4c\x1a\x1c\xa9\xd8\x5d\x48\x07\x04\x74\x0f\x31\x2e\xbb\xa9\x83\x39\xad\x36\x01\xb8\x8d\xd2\xc0\x0b\x11\x66\xca\x5a\x55\xcd\xe0\x8a\xc4\x6b\x97\x1c\xf1\x13\xa1\x58\x85\x57\xd7\xdd\xe4\x45\x52\x5e\x75\x4c\x2c\xde\xd9\xd0\xd9\xa7\xb7\x4c\xb0\x48\x78\xb7\x76\xc9\x60\xc9\xc2\x8c\xd9\x32\x00\xa6\x39\x0b\x4b\x5e\x14\x28\xd3\xc0\xea\x06\x09\x47\x7c\x01\xc3\xeb\xef\x81\xdb\xaf\xbc\xea\xe4\x8a\x0b\xbe\x6e\x8f\x35\x78\x3c\x3a\xe1\xc3\x87\x78\x0d\xed\x83\x5a\x2e\x0d\xda\x70\x70\xa6\x01\x31\x97\x75\x63\xc3\x95\x56\x4d\xdd\xcf\x5f\x24\x6e\x14\x78\x91\x06\x8d\x16\x41\x7b\xfd\xbb\x47\xbb\xad\x5b\x55\x04\xfd\xc1\x95\xae\x42\xb2\x84\x56\x22\x80\x5a\xb0\x1c\x4b\x25\x0a\xd4\x69\xf0\xa3\xca\x39\x13\x20\xfd\x99\xe1\xa7\x1f\xfe\x13\x5a\x93\x71\xb9\x82\xad\x6a\x34\x7c\x63\x4b\xd4\xd8\x54\xc0\x8a\x82\xe0\x1a\x45\xd1\x40\x10\x87\xdd\x53\x51\xc3\xcc\xca\x3d\xd5\x45\x92\x35\xd6\xaa\x9e\x30\xb3\x12\x32\x2b\xc3\x02\x97\xac\x11\x16\x0a\xad\xea\x42\x6d\x64\x68\xd5\x6a\x45\x91\xce\x1f\xc2\x2f\x0a\xa0\x60\x96\xb5\x53\x69\xd0\xd1\x76\x36\x64\xa6\x56\x75\x53\xb7\x56\xf4\x83\x78\x57\x33\x59\x60\x41\x36\x17\x06\x83\xc5\x5f\xf9\x1a\xa1\x42\x7f\x96\x8b\x63\x48\xe4\x4c\xa3\x0d\x87\x4c\x4f\x80\x91\xc4\x5e\x18\x7f\x24\x68\xff\x25\x8d\xe8\x38\xf5\x47\xa8\x50\x36\x70\xf0\x16\x6a\xba\x57\x82\xc5\xfd\xbd\x66\x72\x85\xf0\x94\x17\x77\x97\xf0\x94\x55\xaa\x91\x16\x66\x29\x44\x6f\xdc\xa3\xd9\xed\x0e\xb8\x03\x24\x82\x2f\x12\xf6\x18\xbc\x41\xc9\x5c\xf0\xfc\x36\x0d\x2c\x47\x9d\xde\xdf\x13\xf3\xdd\x6e\x0e\xf7\xf7\x7c\x09\x4f\xa3\x1f\x30\x67\xb5\xcd\x4b\xb6\xdb\xad\x74\xf7\x1c\xe1\x1d\xe6\x8d\xc5\xf1\xe4\xfe\x1e\x85\xc1\xdd\xce\x34\x59\xc5\xed\xb8\x5b\x4e\xe3\xb2\xd8\xed\x48\xe6\x56\xce\xdd\x0e\x62\x62\x2a\x0b\xbc\x83\xa7\xd1\xf7\xa8\xb9\x2a\x0c\x78\xfa\x24\x66\x8b\x24\x16\x7c\xd1\xae\x3b\x54\x52\xdc\x88\x3d\x5e\x62\x02\x4c\x8f\x73\xe7\x36\x4e\xd4\xa1\xa4\x67\xbc\x60\x15\xf6\xd2\xb7\x78\x30\xdc\xe2\x2d\x6e\xd3\xe0\xfe\x7e\xb8\xb6\x9d\xcd\x99\x10\x19\x23\xbd\xf8\xa3\xf5\x8b\x7e\x43\xc2\xe9\x9a\x1b\x97\x52\x2d\x3a\x09\xf6\x62\x7f\xa4\x5b\x1f\x5d\x5c\x56\xd5\x33\xb8\xb9\x1e\xdc\x5a\xe7\x3c\xfe\xd5\x91\xc7\xdf\x9c\x25\xae\x99\x44\x01\xee\x6f\x68\x2a\x26\xba\xe7\xd6\x5b\x06\xce\x77\xbc\x28\xa4\x3b\xba\x17\xad\xbf\xeb\xa7\x73\x50\x6b\xd4\x4b\xa1\x36\x33\x60\x8d\x55\x73\xa8\xd8\x5d\x1f\xef\x6e\xa6\xd3\xa1\xdc\x94\x0a\xb2\x4c\xa0\xbb\x5d\x34\xfe\xda\xa0\xb1\xa6\xbf\x4b\xfc\x94\xfb\x4b\x57\x4a\x81\xd2\x60\x71\xa4\x0d\xda\x91\x54\xeb\xa8\x06\xa6\xef\x95\x79\x56\xf6\xa5\x52\x7d\x08\x19\x8a\xd1\xb2\x1e\x44\xbb\x60\x91\x58\xbd\xa7\xbb\x48\x6c\xf1\x49\x21\x40\x53\x8a\xf7\x50\x04\xf0\x37\x1a\x9d\xbd\x46\xd4\x3e\xbf\x20\xc8\x82\x7b\x4d\x62\x5b\x7c\xc6\xce\x04\xc2\x8c\x19\xfc\x98\xed\x5d\xa4\xdf\x6f\xef\x5e\x3f\x77\xff\x12\x99\xb6\x19\x32\xfb\x31\x02\x2c\x1b\x59\x0c\xce\xef\xee\xce\xcf\x15\xa0\x91\x7c\x8d\xda\x70\xbb\xfd\x58\x09\xb0\xd8\x8b\xe0\xdf\x0f\x45\x48\x62\xab\x1f\xc7\xda\xf0\xe5\x0b\x39\xf7\xef\xa5\x24\x37\x8b\x7f\x57\x1b\x28\x14\x1a\xb0\x25\x37\x40\xc1\xf5\xeb\x24\x2e\x6f\x7a\x92\x7a\xf1\x9e\x26\x9c\x52\x61\xe9\x52\x0b\xe0\x06\x74\x23\x5d\xe4\x55\x12\x6c\x89\x87\xe9\x48\x1b\xa4\x23\x78\xaf\x28\xa5\x5b\xa3\xb4\x50\x31\xc1\x73\xae\x1a\x03\x2c\xb7\x4a\x1b\x58\x6a\x55\x01\xde\x95\xac\x31\x96\x18\xd1\xf5\xc1\xd6\x8c\x0b\xe7\x4b\xce\xa4\xa0\x34\xb0\x3c\x6f\xaa\x86\x52\x52\xb9\x02\x94\xaa\x59\x95\xad\x2c\x56\x81\x0f\x4c\x42\xc9\x55\x2f\x8f\xa9\x59\x05\xcc\x5a\x96\xdf\x9a\x4b\xe8\x6e\x05\x60\x1a\xc1\x72\x2c\x68\x55\xae\xaa\x4a\x49\xb8\xd1\x05\xd4\x4c\xdb\x2d\x98\xc3\xdc\x82\xe5\xb9\x8b\x72\x11\xbc\x91\x5b\x25\x11\x4a\xb6\x76\x12\xc2\x7b\x5f\x4e\x90\x5c\x7f\x61\x39\x66\x4a\xf5\xd4\x50\xb1\x6d\xb7\x5d\x2b\xfd\x86\xdb\x92\x7b\xf5\xd4\xa8\x2b\x5a\x5a\x80\xe0\x15\xb7\x26\x4a\xe2\x7a\x7f\xa3\xee\x63\xb3\x08\x4b\xa5\xf9\x6f\x94\xd8\x88\xe1\xf5\x69\x8f\x2e\x97\xee\x6e\x74\x56\x17\xb8\xb4\x33\x78\xe1\xef\xc6\x63\x1c\xb7\x15\xd0\x39\x10\x77\x3c\x5d\x65\x49\x01\x67\x06\x37\x3e\x9d\xf5\x89\x44\x61\x07\x12\x14\x47\x50\xf3\x9b\xbe\x7e\x5d\xdf\xf5\x72\xf4\x39\xf1\xb4\x67\x42\x08\x38\x54\xca\x9a\xf7\x6a\xbc\x84\x8a\xdd\x22\x30\x48\xd8\x51\x85\xdc\x0a\xed\xea\x2b\xee\xfa\x03\xb1\xdd\x20\xda\xaf\xc9\x75\xd3\x1f\x3c\x43\x2e\x57\xcf\xae\xa7\x1e\x91\xf4\x40\xec\x9f\x5d\x4f\xb9\xb4\xea\xd9\xf5\x74\x7a\x37\xfd\xc8\x7f\xcf\xae\xa7\x4a\x3e\xbb\x9e\xda\x12\x9f\x5d\x4f\x9f\x5d\xdf\x0c\xb1\xec\x47\xba\xcc\x92\xa8\xd0\xd0\x6e\x1d\xc4\x03\xb0\x4c\xaf\xd0\xa6\xc1\xff\xb2\x4c\x35\x76\x96\x09\x26\x6f\x83\x85\x13\x97\xb2\x0d\x87\x82\xf3\xf9\x29\xd4\xcc\x10\x24\x48\x62\x87\x92\xb6\x17\x62\x60\x6c\x1a\xad\x55\x23\x29\x2a\x02\x9d\xd9\x79\xa8\x1c\x11\xca\x48\x31\x93\x28\xc9\x74\xbc\x78\xab\xea\x6d\xe8\x98\xb8\xe5\x27\x6a\x34\x4d\x5d\x2b\x6d\xa3\xa1\x3a\x19\xd5\x41\x02\x4d\xfc\x7a\xfa\xf2\xf5\xab\x47\xc5\x37\x94\x65\xbb\x33\xf4\x12\xb2\x4c\xad\x11\x7c\x4e\x9f\xa9\x3b\x60\xb2\x80\x25\xd7\x08\x6c\xc3\xb6\x5f\x25\x71\xe1\x2a\xb0\xcf\x47\xed\xb2\xf5\xae\x7f\x2a\xd8\x76\x2e\x7f\x09\x75\x93\x09\x6e\x4a\x60\x20\x71\x03\x89\xb1\x5a\xc9\xd5\xc2\x8d\xe6\x54\x92\xba\x57\xa8\x95\xb1\x8f\x99\x1f\xab\x0c\x8b\xe2\x0c\x00\xbe\x94\xfd\x37\x9b\x4d\xd4\x69\xd2\x19\xbf\x44\x51\xc7\x74\xfd\x35\x92\xdb\x6d\xec\xdd\x48\xc9\xf8\x6b\x5e\xa4\xd7\xaf\xaf\x5f\xbd\xba\x7e\xf1\x6f\xaf\x5f\xbe\xbc\x7e\xfd\xe2\xe5\x43\xc8\xa0\x43\x7d\x26\x30\x7c\x1a\xfd\xad\xa2\xaa\xb5\xcf\xa1\x3d\x5e\xba\xdc\x8d\x22\x74\x41\x35\x88\x0e\xfe\x61\x0c\x35\x92\x12\x91\x90\x89\xb3\x39\xc4\x27\xa0\xc8\xc1\xe8\x11\xc9\x3e\x13\x5a\x1d\x7c\x08\x29\xaa\xb1\x74\xc2\xae\x98\xe7\x4a\xf6\x70\xba\x04\xc3\xab\x5a\x6c\x21\xdf\x5b\xfd\x3c\xae\x1e\x34\xca\xef\xc2\xea\xd0\x6c\x1e\x64\x2e\xfa\x57\xaa\x40\x8a\xfa\xa6\x31\x39\xd6\xae\xcb\x4b\x91\xf4\x4f\xdb\xdf\x98\xb4\x5c\x62\x17\x71\x23\xf8\x4e\x8a\x2d\x34\x06\x61\xa9\x34\x14\x98\x35\xab\x95\x4b\x13\x34\xd4\x9a\xaf\x99\xc5\x2e\xcc\x9a\x16\x15\x3d\x28\x06\x95\x0d\xa5\x3c\x62\x90\x81\xfc\x4d\x35\x90\x33\x09\x56\xb3\xfc\xd6\x7b\x4a\xa3\x35\x79\x4a\x8d\xfe\x34\x7d\xa0\xcf\x50\xa8\x8d\x23\xf1\xe7\x5e\x72\x14\x2e\xea\x1b\x44\x28\xd5\x06\xaa\x26\x77\x0e\x49\x51\xdd\x1d\x62\xc3\xb8\x85\x46\x5a\x2e\xbc\x3e\x6d\xa3\x25\xe5\x08\x78\x10\xa5\x4f\x6a\xbf\x04\xab\xc5\xfb\x12\xcf\xa4\x44\x7d\xd5\x06\x1a\xdf\x7a\x72\xa8\xb5\xb2\x98\x93\x41\x81\xad\x18\x97\x86\x2c\xe2\xf2\x00\xac\x3e\xa2\xaa\xeb\x9f\xda\x87\x7d\x87\xd2\x4d\xc7\x31\xfc\x55\xa8\x8c\x09\x58\x13\xd2\x33\x41\xe9\x9c\x82\x52\xd1\xd1\x07\xda\x32\x96\xd9\xc6\x80\x5a\xba\x51\x2f\x39\xad\x5f\x33\x4d\x16\xc4\xaa\xb6\x90\xb6\xfd\x35\x1a\x33\xa8\xd7\x6d\xd7\x90\x5e\xa9\x72\x3f\x98\xef\xb5\x9e\xc2\xcf\xbf\xcc\x9f\xb4\xa2\xfc\x19\x97\x0e\x12\x84\x6f\x7f\x64\x5b\x32\x0b\xb9\x46\x66\xd1\x40\x2e\x94\x69\xb4\x97\xb0\xd0\xaa\x06\x92\xb2\xe3\xd4\x71\xa6\x89\xda\xed\xd6\x31\x19\x97\xcc\x94\x93\xb6\x3d\xa8\xd1\x59\xa9\x9f\xeb\xc6\x2f\x08\x75\x63\x62\xc0\xd3\xe9\x1c\x78\xd2\xf1\x8d\x04\xca\x95\x2d\xe7\xc0\x9f\x3f\xef\x89\x2f\xf8\x12\xc6\x1d\xc5\xcf\xfc\x97\xc8\xde\x45\xb4\x0b\xa4\x29\x0c\x77\x73\x1b\xb6\x7c\x4c\x2d\x78\x8e\x63\x7e\x09\x57\x93\x79\x37\x9b\x69\x64\xb7\xdd\x5b\x6b\x47\xff\x9f\xfb\xbb\x9b\x1f\x6a\xc6\x29\xff\x40\x37\xbe\xf6\x37\xc0\x60\xc5\x8d\x85\x46\x0b\x68\x7d\xd8\x9b\xa0\x37\x88\xa3\x1b\x6a\xe5\x04\x97\xed\x43\x8b\xa9\xee\x08\x9e\x4d\x64\x50\x16\xe3\xff\xf8\xf1\xbb\x6f\x23\x63\x35\x97\x2b\xbe\xdc\x8e\xef\x1b\x2d\x66\xf0\x74\x1c\xfc\x4b\xa3\x45\x30\xf9\x79\xfa\x4b\xb4\x66\xa2\xc1\x4b\x67\xef\x99\xfb\x7b\xb2\xcb\x25\xb4\x8f\x33\x38\xdc\x70\x37\x99\xcc\xcf\xf7\x49\x06\x6d\x1d\x8d\x06\xed\x98\x08\x7b\xe0\x1f\xeb\x88\x41\x85\xb6\x54\xce\x75\x35\xe6\x4a\x4a\xcc\x2d\x34\xb5\x92\xad\x4a\x40\x28\x63\xf6\x40\xec\x28\xd2\x53\x50\xb4\xf4\xa9\x0b\xd6\xff\x8d\xd9\x8f\x2a\xbf\x45\x3b\x1e\x8f\x37\x5c\x16\x6a\x13\x09\xe5\xaf\xda\x88\x9c\x54\xe5\x4a\x40\x9a\xa6\xd0\x46\xd1\x60\x02\x5f\x43\xb0\x31\x14\x4f\x03\x98\xd1\x23\x3d\x4d\xe0\x39\x1c\x2f\x2f\x29\xde\x3f\x87\x20\x66\x35\x0f\x26\xde\x1d\x3a\xc5\x2b\x59\xa1\x31\x6c\x85\x43\x01\x5d\x65\xd4\x83\x8c\xce\x51\x99\x15\xa4\xe0\x0c\x54\x33\x6d\xd0\x93\x44\x54\x8d\x77\x68\x23\xcc\x3a\xb2\x34\x05\xd9\x08\xb1\x07\xa9\x77\x8a\x79\x07\xbf\x03\xf2\xc8\xc7\x9a\xaf\xd2\x14\xa8\x34\x25\x15\x17\xfb\x95\x64\x7c\x5f\x44\x4f\x22\x8a\x0b\xfb\x15\x93\xf9\x10\xcd\x07\xdc\xb0\xf8\x3d\x76\x58\x1c\xf3\xc3\xe2\x01\x86\xae\x67\xf1\x18\x3f\xdf\xe3\x18\xb0\x73\x03\x0f\x70\x93\x4d\x95\xa1\x7e\x8c\x9d\xef\x59\xb4\xec\x9c\xaa\xdf\x49\x3b\x58\x7b\x09\x57\xaf\x26\x0f\x70\x47\xad\xd5\x83\xcc\xa5\xb2\xdb\xf1\xbd\x60\x5b\xca\x99\x60\x64\x55\xfd\xd6\xb5\x18\x46\x97\x2e\xe2\xce\xa0\xe7\x70\xe9\x9a\xc7\x33\x18\xb9\x37\x9a\xe7\x15\xba\x55\x2f\xa7\xd3\xe9\x25\x74\x5f\x5d\xfe\xc4\xc8\x09\x75\x83\xbb\x07\xe4\x31\x4d\x9e\x53\xdc\xff\x1c\x89\x5a\x1e\xbd\x4c\xed\xfb\x67\x48\xd5\xc7\x86\x03\xb1\xe0\x0f\x7f\x80\x93\xd9\x43\x18\xc7\x31\xfc\x17\xa3\x32\x5c\x08\xd7\x3d\x70\x4d\x83\x9e\xbe\xe2\xc6\xb8\x62\xdc\x40\xa1\x24\xb6\x6b\x3e\xed\xda\x3f\x91\xb1\x25\x83\x05\x4c\x8f\x05\xa4\xeb\x70\x10\x16\xce\x44\x8b\x01\xdf\xc3\x40\x70\xb1\x1b\xee\x77\xb0\x92\x57\x08\x5f\xa5\x10\x04\xc3\xc5\x27\x14\x44\xd0\x33\xbb\x30\x68\xdf\x7b\x5b\x8c\xdb\xe8\x78\x2e\x76\x4d\x2e\xe1\x66\x3a\x9d\x4e\x4e\x84\xd8\xed\xd5\xfb\xa6\xa6\xb4\x09\x98\xdc\xba\x2b\xb1\xd7\xad\x4b\x1c\x29\x05\xa2\x2b\x4d\x40\xae\x84\xf0\x39\x4b\xbb\x94\x14\xdc\x36\x4f\x52\x08\xaf\xe6\x67\xa2\xe8\x40\x93\x83\xa3\x1d\x9b\xe7\x8c\xee\x8f\x4d\x74\xa8\xb3\x23\xe2\xf0\xea\xc0\x28\x07\xf6\x3a\x6f\x98\x8b\x5e\x6e\xbe\xd7\xe8\x91\xb9\xf6\xf6\x3a\xd6\xd9\x40\x7e\xcf\xe7\xf9\xd5\x47\x1e\xa3\x9f\xae\x1b\x53\x8e\x8f\x04\x9d\xcc\x4f\x6d\xf3\xce\xa2\xa6\x2c\x59\x51\xc8\x22\x5b\x50\x29\xa0\xf1\xc4\x24\x2e\x55\xd7\x18\x6a\x94\x05\xea\x2e\xa5\xf0\x99\x3d\x25\x80\x07\x26\xf3\x55\xe5\x10\x4e\x9f\xe8\x30\x2e\x25\x53\x12\x01\x00\x8e\x9c\xc0\x01\xf5\x00\xa9\x44\x8c\x82\xd5\x06\x0b\x48\xc1\x7f\x04\x1f\x4f\xa2\x46\xf2\xbb\xf1\x24\x6c\xdf\x8f\x79\x74\xf3\xf3\xbe\x4c\xec\xc4\x7e\x9e\x42\x90\x58\x0d\xbc\x48\x47\x01\x3c\x3f\xe7\x82\x14\x75\x47\x8b\xbd\x04\xc3\xa5\x00\x89\x2d\x16\xae\x0f\xea\xeb\xb5\xbf\x07\x19\xcb\x6f\x57\xae\x10\x9a\x51\xaa\x35\x3e\x61\xcb\xd6\xcc\x32\xed\xb8\x4e\xe6\xb0\x27\x6f\x0b\xc5\x9c\x8c\x33\x07\x5f\x91\xba\x76\x2b\xf4\x9f\x28\xdc\x5b\xa6\x74\x81\x3a\xd4\xac\xe0\x8d\x99\xc1\x8b\xfa\x6e\xfe\xf7\xee\x13\x8e\x6b\x0a\x3f\x2a\x6a\xad\x71\x71\x22\x51\xdb\x65\x7c\x0e\x41\x12\x13\xc1\xef\xb1\xe9\x0f\x3b\xfc\xf8\x0e\x67\x5a\xdf\xd0\x7f\x1a\x6f\xc7\x2b\x5e\x14\x02\x49\xe0\x3d\x7b\x72\x46\xb2\xff\xd0\xa5\x0e\xb7\x84\xb6\xe7\xbd\x5f\xb3\x03\x14\x06\x1f\x59\xd0\xb7\xcf\x47\x04\x80\x90\x8e\xcc\x9d\xce\xdb\x62\xdb\x0d\xeb\x91\xd3\x45\xfb\x53\x8a\xa2\xd1\x2e\xd7\x1a\x87\x2d\xc0\x2e\x61\x64\x28\xf7\x2b\xcc\x68\x12\x95\x4d\xc5\x24\xff\x0d\xc7\x14\x97\x26\x5e\x57\xae\x1f\x1f\x9c\x5e\xc9\x27\xc2\xec\x1b\xe5\xa3\x2e\xc6\x8d\x5a\x25\x8e\x3a\xeb\xbe\xd8\xd7\xf6\x33\x98\xce\x47\x9f\xa8\xa1\xf3\xbb\x84\x19\xd3\x30\x7c\x09\xbb\xe0\x0b\x5a\xd1\xee\xdd\x5c\xc6\xf4\xc8\x77\x32\x5c\x7e\x2e\xd5\x26\x1d\xdd\x4c\x7b\x21\xbd\xa1\x9d\x9d\x47\x2d\xd6\x4e\x8c\x41\x52\x76\xae\xb9\x80\x9b\xe9\x97\x90\xd6\x77\x43\x8e\x4e\x60\x35\xaf\xb1\x00\x96\x5b\xbe\xc6\xff\x87\x83\x7c\x01\x25\x7f\xb2\x88\x84\xc3\x4e\x79\x0e\xa6\x07\xf2\xd2\x6c\xaf\xdb\x7f\x25\x7f\x83\xd8\x69\xf8\x39\x04\x67\x0f\xf2\x20\x12\x8f\x08\x8f\x5c\xfb\x61\xbf\x77\x1f\x98\x82\xe3\x98\x42\xd9\x6e\xff\x71\x74\x12\x95\xb6\x12\xe3\x20\xb1\xee\x47\x32\x24\x73\xcf\xc1\x31\xf0\xc3\x87\x29\xdd\xee\xb0\x90\xa1\xfa\x1d\x8f\xea\x2c\x18\x24\x27\x7d\x2d\xd6\x65\x22\xb0\xdb\xff\x96\x28\x8e\xe1\x47\xcb\xb4\x05\x06\x3f\xbd\x83\xa6\x2e\x98\xf5\x9f\x72\x28\x3e\xfa\x4f\x25\xdd\x8f\x8d\x32\xa6\x0d\x2c\x95\xde\x30\x5d\xb4\xfd\x19\x5b\xe2\xd6\x7d\xca\xe9\x52\x3f\x83\xf6\x1d\xdd\x62\x6b\x26\xc6\x27\x75\xdf\xd3\xf1\x28\x1a\x9a\x7c\x34\x89\x90\xe5\xe5\x29\xa1\x8b\x58\xfd\xbe\x29\x7c\xeb\x4a\x80\xf1\xd3\xb1\x2d\xb9\x99\x44\xcc\x5a\x3d\x1e\x1d\x80\x61\x34\x21\xbb\x5e\x0d\x4a\xb2\x7e\x79\x72\xe0\x56\x8f\xf1\xd8\x27\xd3\x7d\x22\xd0\x91\xe7\xc6\x8c\x3d\xae\x46\x97\x03\xde\x87\xb0\x1a\x3d\x1b\xf5\x86\xda\xbb\xf7\xfe\x1c\xe9\x59\x49\x0e\x58\x8f\xc8\xcb\x46\x27\xdb\xb3\xa2\x78\x4b\xfe\x33\x0e\xce\x78\xfa\x31\x3a\x26\xbd\xb2\xfd\x7d\xfd\xa8\x96\xfd\xcf\x32\x1e\x50\x31\x2f\x46\x93\xc8\x34\x99\xef\x4d\x8c\x5f\xf6\x05\x58\x47\xe6\xc0\x7b\x1c\x0a\x4e\x12\x0a\xda\xe2\x30\xa9\x08\x8f\x92\x90\x47\xa2\x46\xbb\xa5\x3f\xd5\xee\x92\x14\x3e\x9d\xf4\xad\xad\x6f\x0c\x25\x57\xbe\xf5\xbf\xc1\xcc\xb8\x4e\x02\xb4\x78\x77\xdd\x1c\xdf\xb5\x79\xf3\xfd\xbb\x41\xe7\xa6\xf7\x88\xb1\xe3\xde\xff\x0e\xf0\x5c\x9f\xe4\xec\x0f\x0f\x37\x9b\x4d\xb4\x52\x6a\x25\xfc\x4f\x0e\xfb\x46\x4a\xcc\x6a\x1e\x7d\x30\x01\x30\xb3\x95\x39\x14\xb8\x44\xbd\x18\xb0\x6f\xbb\x2b\x49\xec\x7f\x12\x97\xc4\xfe\x57\xbf\xff\x17\x00\x00\xff\xff\x31\x9f\x54\x5e\x06\x2c\x00\x00") +var _faucetHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x5a\x7b\x93\xdb\x36\x92\xff\x7b\xfc\x29\x3a\x3c\x7b\x25\x9d\x87\xa4\x66\xc6\xf6\xfa\x24\x52\x29\xaf\x37\xbb\xe7\xab\xbb\x24\x95\x38\x75\xb7\x95\x4d\x5d\x81\x64\x4b\x84\x07\x04\x18\x00\x94\x46\x99\xd2\x77\xbf\x6a\x80\xa4\xa8\xc7\x4c\xec\xb5\xaf\x6a\xfd\xc7\x98\xc4\xa3\xd1\x8f\x5f\xa3\x1f\x54\xf2\xd5\x9f\xbf\x7b\xfb\xfe\x6f\xdf\x7f\x03\xa5\xad\xc4\xe2\x49\x42\xff\x81\x60\x72\x95\x06\x28\x83\xc5\x93\x8b\xa4\x44\x56\x2c\x9e\x5c\x5c\x24\x15\x5a\x06\x79\xc9\xb4\x41\x9b\x06\x8d\x5d\x86\xaf\x83\xfd\x44\x69\x6d\x1d\xe2\xaf\x0d\x5f\xa7\xc1\xff\x84\x3f\xbd\x09\xdf\xaa\xaa\x66\x96\x67\x02\x03\xc8\x95\xb4\x28\x6d\x1a\xbc\xfb\x26\xc5\x62\x85\x83\x7d\x92\x55\x98\x06\x6b\x8e\x9b\x5a\x69\x3b\x58\xba\xe1\x85\x2d\xd3\x02\xd7\x3c\xc7\xd0\xbd\x5c\x02\x97\xdc\x72\x26\x42\x93\x33\x81\xe9\x55\xb0\x78\x42\x74\x2c\xb7\x02\x17\xf7\xf7\xd1\xb7\x68\x37\x4a\xdf\xee\x76\x33\x78\xd3\xd8\x12\xa5\xe5\x39\xb3\x58\xc0\x5f\x58\x93\xa3\x4d\x62\xbf\xd2\x6d\x12\x5c\xde\x42\xa9\x71\x99\x06\xc4\xba\x99\xc5\x71\x5e\xc8\x0f\x26\xca\x85\x6a\x8a\xa5\x60\x1a\xa3\x5c\x55\x31\xfb\xc0\xee\x62\xc1\x33\x13\xdb\x0d\xb7\x16\x75\x98\x29\x65\x8d\xd5\xac\x8e\x6f\xa2\x9b\xe8\x8f\x71\x6e\x4c\xdc\x8f\x45\x15\x97\x51\x6e\x4c\x00\x1a\x45\x1a\x18\xbb\x15\x68\x4a\x44\x1b\x40\xbc\xf8\xc7\xce\x5d\x2a\x69\x43\xb6\x41\xa3\x2a\x8c\x5f\x44\x7f\x8c\xa6\xee\xc8\xe1\xf0\xe3\xa7\xd2\xb1\x26\xd7\xbc\xb6\x60\x74\xfe\xd1\xe7\x7e\xf8\xb5\x41\xbd\x8d\x6f\xa2\xab\xe8\xaa\x7d\x71\xe7\x7c\x30\xc1\x22\x89\x3d\xc1\xc5\x67\xd1\x0e\xa5\xb2\xdb\xf8\x3a\x7a\x11\x5d\xc5\x35\xcb\x6f\xd9\x0a\x8b\xee\x24\x9a\x8a\xba\xc1\x2f\x76\xee\x43\x36\xfc\x70\x6c\xc2\x2f\x71\x58\xa5\x2a\x94\x36\xfa\x60\xe2\xeb\xe8\xea\x75\x34\xed\x06\x4e\xe9\xbb\x03\xc8\x68\x74\xd4\x45\xb4\x46\x4d\xc8\x15\x61\x8e\xd2\xa2\x86\x7b\x1a\xbd\xa8\xb8\x0c\x4b\xe4\xab\xd2\xce\xe0\x6a\x3a\x7d\x36\x3f\x37\xba\x2e\xfd\x70\xc1\x4d\x2d\xd8\x76\x06\x4b\x81\x77\x7e\x88\x09\xbe\x92\x21\xb7\x58\x99\x19\x78\xca\x6e\x62\xe7\xce\xac\xb5\x5a\x69\x34\xa6\x3d\xac\x56\x86\x5b\xae\xe4\x8c\x10\xc5\x2c\x5f\xe3\xb9\xb5\xa6\x66\xf2\x64\x03\xcb\x8c\x12\x8d\xc5\x23\x46\x32\xa1\xf2\x5b\x3f\xe6\xbc\x79\x28\x44\xae\x84\xd2\x33\xd8\x94\xbc\xdd\x06\xee\x20\xa8\x35\xb6\xe4\xa1\x66\x45\xc1\xe5\x6a\x06\xaf\xea\x56\x1e\xa8\x98\x5e\x71\x39\x83\xe9\x7e\x4b\x12\x77\x6a\x4c\x62\x7f\x71\x3d\xb9\x48\x32\x55\x6c\x9d\x0d\x0b\xbe\x86\x5c\x30\x63\xd2\xe0\x48\xc5\xee\x42\x3a\x58\x40\xf7\x10\xe3\xb2\x9b\x3a\x98\xd3\x6a\x13\x80\x3b\x28\x0d\x3c\x13\x61\xa6\xac\x55\xd5\x0c\xae\x88\xbd\x76\xcb\x11\x3d\x11\x8a\x55\x78\x75\xdd\x4d\x5e\x24\xe5\x55\x47\xc4\xe2\x9d\x0d\x9d\x7d\x7a\xcb\x04\x8b\x84\x77\x7b\x97\x0c\x96\x2c\xcc\x98\x2d\x03\x60\x9a\xb3\xb0\xe4\x45\x81\x32\x0d\xac\x6e\x90\x70\xc4\x17\x30\xbc\xfe\x1e\xb8\xfd\xca\xab\x8e\xaf\xb8\xe0\xeb\x56\xac\xc1\xe3\x91\x84\x0f\x0b\xf1\x1a\xda\x07\xb5\x5c\x1a\xb4\xe1\x40\xa6\xc1\x62\x2e\xeb\xc6\x86\x2b\xad\x9a\xba\x9f\xbf\x48\xdc\x28\xf0\x22\x0d\x1a\x2d\x82\xf6\xfa\x77\x8f\x76\x5b\xb7\xaa\x08\x7a\xc1\x95\xae\x42\xb2\x84\x56\x22\x80\x5a\xb0\x1c\x4b\x25\x0a\xd4\x69\xf0\xa3\xca\x39\x13\x20\xbd\xcc\xf0\xd3\x0f\xff\x09\xad\xc9\xb8\x5c\xc1\x56\x35\x1a\xbe\xb1\x25\x6a\x6c\x2a\x60\x45\x41\x70\x8d\xa2\x28\x88\xf7\x9c\x38\xf0\x9e\xf2\x1a\x66\x56\xee\xf9\xbd\x48\xb2\xc6\x5a\xd5\x2f\xcc\xac\x84\xcc\xca\xb0\xc0\x25\x6b\x84\x85\x42\xab\xba\x50\x1b\x19\x5a\xb5\x5a\x51\xa8\xf3\x52\xf8\x4d\x01\x14\xcc\xb2\x76\x2a\x0d\xba\xb5\x9d\x11\x99\xa9\x55\xdd\xd4\xad\x19\xfd\x20\xde\xd5\x4c\x16\x58\x90\xd1\x85\xc1\x60\xf1\x57\xbe\x46\xa8\xd0\x0b\x73\x71\x8c\x89\x9c\x69\xb4\xe1\x90\xe8\x09\x32\x92\xd8\x33\xe3\x45\x82\xf6\x5f\xd2\x88\x8e\x52\x2f\x42\x85\xb2\x81\x83\xb7\x50\xd3\xc5\x12\x2c\xee\xef\x35\x93\x2b\x84\xa7\xbc\xb8\xbb\x84\xa7\xac\x52\x8d\xb4\x30\x4b\x21\x7a\xe3\x1e\xcd\x6e\x77\x40\x1d\x20\x11\x7c\x91\xb0\xc7\xf0\x0d\x4a\xe6\x82\xe7\xb7\x69\x60\x39\xea\xf4\xfe\x9e\x88\xef\x76\x73\xb8\xbf\xe7\x4b\x78\x1a\xfd\x80\x39\xab\x6d\x5e\xb2\xdd\x6e\xa5\xbb\xe7\x08\xef\x30\x6f\x2c\x8e\x27\xf7\xf7\x28\x0c\xee\x76\xa6\xc9\x2a\x6e\xc7\xdd\x76\x1a\x97\xc5\x6e\x47\x3c\xb7\x7c\xee\x76\x10\x13\x51\x59\xe0\x1d\x3c\x8d\xbe\x47\xcd\x55\x61\xc0\xaf\x4f\x62\xb6\x48\x62\xc1\x17\xed\xbe\x43\x25\xc5\x8d\xd8\xe3\x25\x26\xc0\xf4\x40\x77\x7e\xe3\x58\x1d\x72\x7a\xc6\x0d\x56\x61\xcf\x7d\x8b\x07\xc3\x2d\xde\xe2\x36\x0d\xee\xef\x87\x7b\xdb\xd9\x9c\x09\x91\x31\xd2\x8b\x17\xad\xdf\xf4\x1b\x12\x4e\xd7\xdc\xb8\x9c\x6a\xd1\x71\xb0\x67\xfb\x23\xfd\xfa\xe8\xe6\xb2\xaa\x9e\xc1\xcd\xf5\xe0\xda\x3a\xe7\xf2\xaf\x8e\x5c\xfe\xe6\xec\xe2\x9a\x49\x14\xe0\xfe\x86\xa6\x62\xa2\x7b\x6e\xbd\x65\x70\x0d\x1c\x6f\x0a\xe9\x92\xee\x59\xeb\x2f\xfb\xe9\x1c\xd4\x1a\xf5\x52\xa8\xcd\x0c\x58\x63\xd5\x1c\x2a\x76\xd7\x07\xbc\x9b\xe9\x74\xc8\x37\xe5\x82\x2c\x13\xe8\xae\x17\x8d\xbf\x36\x68\xac\xe9\x2f\x13\x3f\xe5\xfe\xd2\x9d\x52\xa0\x34\x58\x1c\x69\x83\x4e\x24\xd5\xba\x55\x03\xd3\xf7\xca\x3c\xcb\xfb\x52\xa9\x3e\x86\x0c\xd9\x68\x49\x0f\xc2\x5d\xb0\x48\xac\xde\xaf\xbb\x48\x6c\xf1\x49\x31\x40\x53\x8e\xf7\x50\x08\xf0\x37\x1a\xc9\x5e\x23\x6a\x9f\x60\x10\x64\xc1\xbd\x26\xb1\x2d\x3e\xe3\x64\x02\x61\xc6\x0c\x7e\xcc\xf1\x2e\xd4\xef\x8f\x77\xaf\x9f\x7b\x7e\x89\x4c\xdb\x0c\x99\xfd\x18\x06\x96\x8d\x2c\x06\xf2\xbb\xbb\xf3\x73\x19\x68\x24\x5f\xa3\x36\xdc\x6e\x3f\x96\x03\x2c\xf6\x2c\xf8\xf7\x43\x16\x92\xd8\xea\xc7\xb1\x36\x7c\xf9\x42\xce\xfd\x7b\x39\xc9\xcd\xe2\xdf\xd5\x06\x0a\x85\x06\x6c\xc9\x0d\x50\x74\xfd\x3a\x89\xcb\x9b\x7e\x49\xbd\x78\x4f\x13\x4e\xa9\xb0\x74\xb9\x05\x70\x03\xba\x91\x2e\xf4\x2a\x09\xb6\xc4\xc3\x7c\xa4\x8d\xd2\x11\xbc\x57\x94\xd3\xad\x51\x5a\xa8\x98\xe0\x39\x57\x8d\x01\x96\x5b\xa5\x0d\x2c\xb5\xaa\x00\xef\x4a\xd6\x18\x4b\x84\xe8\xfa\x60\x6b\xc6\x85\xf3\x25\x67\x52\x50\x1a\x58\x9e\x37\x55\x43\x39\xa9\x5c\x01\x4a\xd5\xac\xca\x96\x17\xab\xc0\x07\x26\xa1\xe4\xaa\xe7\xc7\xd4\xac\x02\x66\x2d\xcb\x6f\xcd\x25\x74\xb7\x02\x30\x8d\x60\x39\x16\xb4\x2b\x57\x55\xa5\x24\xdc\xe8\x02\x6a\xa6\xed\x16\xcc\x61\x72\xc1\xf2\xdc\x45\xb9\x08\xde\xc8\xad\x92\x08\x25\x5b\x3b\x0e\xe1\xbd\xaf\x27\x88\xaf\xbf\xb0\x1c\x33\xa5\xfa\xd5\x50\xb1\x6d\x77\x5c\xcb\xfd\x86\xdb\x92\x7b\xf5\xd4\xa8\x2b\xda\x5a\x80\xe0\x15\xb7\x26\x4a\xe2\x7a\x7f\xa3\xee\x63\xb3\x08\x4b\xa5\xf9\x6f\x94\xd9\x88\xe1\xf5\x69\x8f\x2e\x97\xee\x6e\x74\x56\x17\xb8\xb4\x33\x78\xe1\xef\xc6\x63\x1c\xb7\x25\xd0\x39\x10\x77\x34\x5d\x69\x49\x01\x67\x06\x37\x3e\x9f\xf5\x89\x44\x61\x07\x1c\x14\x47\x50\xf3\x87\xbe\x7e\x5d\xdf\xf5\x7c\xf4\x49\xf1\xb4\x27\x42\x08\x38\x54\xca\x9a\xf7\x6a\xbc\x84\x8a\xdd\x22\x30\x48\xd8\x51\x89\xdc\x32\xed\x0a\x2c\xee\x1a\x04\xb1\xdd\x20\xda\xaf\xc9\x75\xd3\x1f\x3c\x41\x2e\x57\xcf\xae\xa7\x1e\x91\xf4\x40\xe4\x9f\x5d\x4f\xb9\xb4\xea\xd9\xf5\x74\x7a\x37\xfd\xc8\x7f\xcf\xae\xa7\x4a\x3e\xbb\x9e\xda\x12\x9f\x5d\x4f\x9f\x5d\xdf\x0c\xb1\xec\x47\xba\xd4\x92\x56\xa1\xa1\xd3\x3a\x88\x07\x60\x99\x5e\xa1\x4d\x83\xff\x65\x99\x6a\xec\x2c\x13\x4c\xde\x06\x0b\xc7\x2e\x65\x1b\x0e\x05\xe7\x13\x54\xa8\x99\x21\x48\x10\xc7\x0e\x25\x6d\x33\xc4\xc0\xd8\x34\x5a\xab\x46\x52\x54\x04\x92\xd9\x79\xa8\x1c\x11\xca\x48\x31\x93\x28\xc9\x74\xbc\x78\xab\xea\x6d\xe8\x88\xb8\xed\x27\x6a\x34\x4d\x5d\x2b\x6d\xa3\xa1\x3a\x19\x15\x42\x02\x4d\xfc\x7a\xfa\xf2\xf5\xab\x47\xd9\x37\x94\x66\x3b\x19\x7a\x0e\x59\xa6\xd6\x08\x3e\xa9\xcf\xd4\x1d\x30\x59\xc0\x92\x6b\x04\xb6\x61\xdb\xaf\x92\xb8\x70\x25\xd8\xe7\xa3\x76\xd9\x7a\xd7\x3f\x15\x6c\x3b\x97\xbf\x84\xba\xc9\x04\x37\x25\x30\x90\xb8\x81\xc4\x58\xad\xe4\x6a\xe1\x46\x73\xaa\x49\xdd\x2b\xd4\xca\xd8\xc7\xcc\x8f\x55\x86\x45\x71\x06\x00\x5f\xca\xfe\x9b\xcd\x26\xea\x34\xe9\x8c\x5f\xa2\xa8\x63\xba\xfe\x1a\xc9\xed\x36\xf6\x6e\xa4\x64\xfc\x35\x2f\xd2\xeb\xd7\xd7\xaf\x5e\x5d\xbf\xf8\xb7\xd7\x2f\x5f\x5e\xbf\x7e\xf1\xf2\x21\x64\x90\x50\x9f\x09\x0c\x9f\x46\x7f\xab\xa8\x6c\xed\x73\x68\x8f\x97\x2e\x77\xa3\x08\x5d\x50\x0d\xa2\x83\x7f\x18\x43\x8d\xa4\x44\x24\x64\xe2\x6c\x0e\xf1\x09\x28\x72\x30\x7a\x84\xb3\xcf\x84\x56\x07\x1f\x42\x8a\x6a\x2c\x49\xd8\x55\xf3\x5c\xc9\x1e\x4e\x97\x60\x78\x55\x8b\x2d\xe4\x7b\xab\x9f\xc7\xd5\x83\x46\xf9\x5d\x58\x1d\x9a\xcd\x83\xcc\x45\xff\x4a\x15\x48\x51\xdf\x34\x26\xc7\xda\xb5\x79\x29\x92\xfe\x69\xfb\x1b\x93\x96\x4b\xec\x22\x6e\x04\xdf\x49\xb1\x85\xc6\x20\x2c\x95\x86\x02\xb3\x66\xb5\x72\x69\x82\x86\x5a\xf3\x35\xb3\xd8\x85\x59\xd3\xa2\xa2\x07\xc5\xa0\xb2\xa1\x94\x47\x0c\x32\x90\xbf\xa9\x06\x72\x26\xc1\x6a\x96\xdf\x7a\x4f\x69\xb4\x26\x4f\xa9\xd1\x4b\xd3\x07\xfa\x0c\x85\xda\xb8\x25\x5e\xee\x25\x47\xe1\xa2\xbe\x41\x84\x52\x6d\xa0\x6a\x72\xe7\x90\x14\xd5\x9d\x10\x1b\xc6\x2d\x34\xd2\x72\xe1\xf5\x69\x1b\x2d\x29\x47\xc0\x83\x28\x7d\x52\xfb\x25\x58\x2d\xde\x97\x78\x26\x25\xea\xab\x36\xd0\xf8\xd6\x2f\x87\x5a\x2b\x8b\x39\x19\x14\xd8\x8a\x71\x69\xc8\x22\x2e\x0f\xc0\xea\x23\xaa\xba\xfe\xa9\x7d\xd8\xb7\x28\xdd\x74\x1c\xc3\x5f\x85\xca\x98\x80\x35\x21\x3d\x13\x94\xce\x29\x28\x15\x89\x3e\xd0\x96\xb1\xcc\x36\x06\xd4\xd2\x8d\x7a\xce\x69\xff\x9a\x69\xb2\x20\x56\xb5\x85\xb4\x6d\xb0\xd1\x98\x41\xbd\x6e\xdb\x86\xf4\x4a\x95\xfb\xc1\x7c\xaf\xf5\x14\x7e\xfe\x65\xfe\xa4\x65\xe5\xcf\xb8\x74\x90\x20\x7c\x7b\x91\x6d\xc9\x2c\xe4\x1a\x99\x45\x03\xb9\x50\xa6\xd1\x9e\xc3\x42\xab\x1a\x88\xcb\x8e\x52\x47\x99\x26\x6a\x77\x5a\x47\x64\x5c\x32\x53\x4e\xda\xfe\xa0\x46\x67\xa5\x7e\xae\x1b\xbf\x20\xd4\x8d\x89\x00\x4f\xa7\x73\xe0\x49\x47\x37\x12\x28\x57\xb6\x9c\x03\x7f\xfe\xbc\x5f\x7c\xc1\x97\x30\xee\x56\xfc\xcc\x7f\x89\xec\x5d\x44\xa7\x40\x9a\xc2\xf0\x34\x77\x60\x4b\xc7\xd4\x82\xe7\x38\xe6\x97\x70\x35\x99\x77\xb3\x99\x46\x76\xdb\xbd\xb5\x76\xf4\xff\xb9\xbf\xbb\xf9\xa1\x66\x9c\xf2\x0f\x74\xe3\x6b\x7f\x03\x0c\x56\xdc\x58\x68\xb4\x80\xd6\x87\xbd\x09\x7a\x83\xb8\x75\x43\xad\x9c\xe0\xb2\x7d\x68\x31\xd5\x89\xe0\xc9\x44\x06\x65\x31\xfe\x8f\x1f\xbf\xfb\x36\x32\x56\x73\xb9\xe2\xcb\xed\xf8\xbe\xd1\x62\x06\x4f\xc7\xc1\xbf\x34\x5a\x04\x93\x9f\xa7\xbf\x44\x6b\x26\x1a\xbc\x74\xf6\x9e\xb9\xbf\x27\xa7\x5c\x42\xfb\x38\x83\xc3\x03\x77\x93\xc9\xfc\x7c\x9f\x64\xd0\xd6\xd1\x68\xd0\x8e\x69\x61\x0f\xfc\x63\x1d\x31\xa8\xd0\x96\xca\xb9\xae\xc6\x5c\x49\x89\xb9\x85\xa6\x56\xb2\x55\x09\x08\x65\xcc\x1e\x88\xdd\x8a\xf4\x14\x14\xed\xfa\xd4\x05\xeb\xff\xc6\xec\x47\x95\xdf\xa2\x1d\x8f\xc7\x1b\x2e\x0b\xb5\x89\x84\xf2\x57\x6d\x44\x4e\xaa\x72\x25\x20\x4d\x53\x68\xa3\x68\x30\x81\xaf\x21\xd8\x18\x8a\xa7\x01\xcc\xe8\x91\x9e\x26\xf0\x1c\x8e\xb7\x97\x14\xef\x9f\x43\x10\xb3\x9a\x07\x13\xef\x0e\x9d\xe2\x95\xac\xd0\x18\xb6\xc2\x21\x83\xae\x32\xea\x41\x46\x72\x54\x66\x05\x29\x38\x03\xd5\x4c\x1b\xf4\x4b\x22\xaa\xc6\x3b\xb4\x11\x66\xdd\xb2\x34\x05\xd9\x08\xb1\x07\xa9\x77\x8a\x79\x07\xbf\x83\xe5\x91\x8f\x35\x5f\xa5\x29\x50\x69\x4a\x2a\x2e\xf6\x3b\xc9\xf8\xbe\x88\x9e\x44\x14\x17\xf6\x3b\x26\xf3\x21\x9a\x0f\xa8\x61\xf1\x7b\xe4\xb0\x38\xa6\x87\xc5\x03\x04\x5d\xcf\xe2\x31\x7a\xbe\xc7\x31\x20\xe7\x06\x1e\xa0\x26\x9b\x2a\x43\xfd\x18\x39\xdf\xb3\x68\xc9\x39\x55\xbf\x93\x76\xb0\xf7\x12\xae\x5e\x4d\x1e\xa0\x8e\x5a\xab\x07\x89\x4b\x65\xb7\xe3\x7b\xc1\xb6\x94\x33\xc1\xc8\xaa\xfa\xad\x6b\x31\x8c\x2e\x5d\xc4\x9d\x41\x4f\xe1\xd2\x35\x8f\x67\x30\x72\x6f\x34\xcf\x2b\x74\xbb\x5e\x4e\xa7\xd3\x4b\xe8\x3e\xbb\xfc\x89\x91\x13\xea\x06\x77\x0f\xf0\x63\x9a\x3c\xa7\xb8\xff\x39\x1c\xb5\x34\x7a\x9e\xda\xf7\xcf\xe0\xaa\x8f\x0d\x07\x6c\xc1\x1f\xfe\x00\x27\xb3\x87\x30\x8e\x63\xf8\x2f\x46\x65\xb8\x10\xae\x7b\xe0\x9a\x06\xfd\xfa\x8a\x1b\xe3\x8a\x71\x03\x85\x92\xd8\xee\xf9\xb4\x6b\xff\x84\xc7\x76\x19\x2c\x60\x7a\xcc\x20\x5d\x87\x83\xb0\x70\x26\x5a\x0c\xe8\x1e\x06\x82\x8b\xdd\xf0\xbc\x83\x9d\xbc\x42\xf8\x2a\x85\x20\x18\x6e\x3e\x59\x41\x0b\x7a\x62\x17\x06\xed\x7b\x6f\x8b\x71\x1b\x1d\xcf\xc5\xae\xc9\x25\xdc\x4c\xa7\xd3\xc9\x09\x13\xbb\xbd\x7a\xdf\xd4\x94\x36\x01\x93\x5b\x77\x25\xf6\xba\x75\x89\x23\xa5\x40\x74\xa5\x09\xc8\x95\x10\x3e\x67\x69\xb7\x92\x82\xdb\xe6\x49\x0a\xe1\xd5\xfc\x4c\x14\x1d\x68\x72\x20\xda\xb1\x79\xce\xe8\xfe\xd8\x44\x87\x3a\x3b\x5a\x1c\x5e\x1d\x18\xe5\xc0\x5e\xe7\x0d\x73\xd1\xf3\xcd\xf7\x1a\x3d\x32\xd7\xde\x5e\xc7\x3a\x1b\xf0\xef\xe9\x3c\xbf\xfa\x48\x31\xfa\xe9\xba\x31\xe5\xf8\x88\xd1\xc9\xfc\xd4\x36\xef\x2c\x6a\xca\x92\x15\x85\x2c\xb2\x05\x95\x02\x1a\x4f\x4c\xe2\x52\x75\x8d\xa1\x46\x59\xa0\xee\x52\x0a\x9f\xd9\x53\x02\x78\x60\x32\x5f\x55\x0e\xe1\x34\x90\xe8\x44\xb7\x73\xe0\xb0\xa0\x34\x0f\x78\x18\x0e\x64\x71\x79\x99\x92\x08\x00\x70\xe4\x09\x0e\xad\x07\x70\xa5\xc5\x28\x58\x6d\xb0\x80\x14\xfc\xa7\xf0\xf1\x24\x6a\x24\xbf\x1b\x4f\xc2\xf6\xfd\x98\x46\x37\x3f\xef\x6b\xc5\x8e\xf7\xe7\x29\x04\x89\xd5\xc0\x8b\x74\x14\xc0\xf3\x73\x7e\x48\xa1\x77\xb4\xd8\x73\x30\xdc\x0a\x90\xd8\x62\xe1\x9a\xa1\xbe\x68\xfb\x7b\x90\xb1\xfc\x76\xe5\xaa\xa1\x19\xe5\x5b\xe3\x13\xb2\x6c\xcd\x2c\xd3\x8e\xea\x64\x0e\xfb\xe5\x6d\xb5\x98\x93\x85\xe6\xe0\xcb\x52\xd7\x73\x85\xfe\x3b\x85\x7b\xcb\x94\x2e\x50\x87\x9a\x15\xbc\x31\x33\x78\x51\xdf\xcd\xff\xde\x7d\xc7\x71\x9d\xe1\x47\x59\xad\x35\x2e\x4e\x38\x6a\x5b\x8d\xcf\x21\x48\x62\x5a\xf0\x7b\x64\x7a\x61\x87\x9f\xe0\xe1\x4c\xff\x1b\xfa\x0f\xe4\xed\x78\xc5\x8b\x42\x20\x31\xbc\x27\x4f\x1e\x49\xf6\x1f\xfa\xd5\xe1\x91\xd0\x36\xbe\xf7\x7b\x76\x80\xc2\xe0\x23\x1b\xfa\x1e\xfa\x88\x00\x10\x92\xc8\xdc\xe9\xbc\xad\xb8\xdd\xb0\x1e\x39\x5d\xb4\x3f\xa8\x28\x1a\xed\x12\xae\x71\xd8\x02\xec\x12\x46\x86\x12\xc0\xc2\x8c\x26\x51\xd9\x54\x4c\xf2\xdf\x70\x4c\xc1\x69\xe2\x75\xe5\x9a\xf2\xc1\xe9\xbd\x7c\xc2\xcc\xbe\x5b\x3e\xea\x02\xdd\xa8\x55\xe2\xa8\xb3\xee\x8b\x7d\x81\x3f\x83\xe9\x7c\xf4\x89\x1a\x3a\x7f\x4a\x98\x31\x0d\xc3\x97\xb0\x8b\xc0\xa0\x15\x9d\xde\xcd\x65\x4c\x8f\x7c\x3b\xc3\x25\xe9\x52\x6d\xd2\xd1\xcd\xb4\x67\xd2\x1b\xda\xd9\x79\xd4\x62\xed\xc4\x18\xc4\x65\xe7\x9a\x0b\xb8\x99\x7e\x09\x6e\x7d\x4b\xe4\x48\x02\xab\x79\x8d\x05\xb0\xdc\xf2\x35\xfe\x3f\x08\xf2\x05\x94\xfc\xc9\x2c\x12\x0e\x3b\xe5\x39\x98\x1e\xf0\x4b\xb3\xbd\x6e\xff\x95\xfc\x0d\x62\xa7\xe1\xe7\x10\x9c\x15\xe4\x41\x24\x1e\x2d\x3c\x72\xed\x87\xfd\xde\x7d\x65\x0a\x8e\x03\x0b\xa5\xbc\xfd\x17\xd2\x49\x54\xda\x4a\x8c\x83\xc4\xba\x9f\xca\x10\xcf\x3d\x05\x47\xc0\x0f\x1f\xe6\x75\xbb\xc3\x6a\x86\x8a\x78\x3c\x2a\xb6\x60\x90\xa1\xf4\x05\x59\x97\x8e\xc0\x6e\xff\x8b\xa2\x38\x86\x1f\x2d\xd3\x16\x18\xfc\xf4\x0e\x9a\xba\x60\xd6\x7f\xcf\xa1\x20\xe9\xbf\x97\x74\x3f\x39\xca\x98\x36\xb0\x54\x7a\xc3\x74\xd1\x36\x69\x6c\x89\x5b\xf7\x3d\xa7\xcb\xff\x0c\xda\x77\x74\x8b\xad\x99\x18\x9f\x14\x7f\x4f\xc7\xa3\x68\x68\xf2\xd1\x24\x42\x96\x97\xa7\x0b\x5d\xc4\xea\xcf\x4d\xe1\x5b\x57\x07\x8c\x9f\x8e\x6d\xc9\xcd\x24\x62\xd6\xea\xf1\xe8\x00\x0c\xa3\x09\xd9\xf5\x6a\x50\x97\xf5\xdb\x93\x03\xb7\x7a\x8c\xc6\x3e\xa3\xee\xb3\x81\x6e\x79\x6e\xcc\xd8\xe3\x6a\x74\x39\xa0\x7d\x08\xab\xd1\xb3\x51\x6f\xa8\xbd\x7b\xef\xe5\x48\xcf\x72\x72\x40\x7a\x44\x5e\x36\x3a\x39\x9e\x15\xc5\x5b\xf2\x9f\x71\x70\xc6\xd3\x8f\xd1\x31\xe9\x95\xed\xef\xeb\x47\xb5\xec\x7f\x9b\xf1\x80\x8a\x79\x31\x9a\x44\xa6\xc9\x7c\x83\x62\xfc\xb2\xaf\xc2\xba\x65\x0e\xbc\xc7\xa1\xe0\x24\xa1\xa0\x23\x0e\x93\x8a\xf0\x28\x09\x79\x24\x6a\xb4\x47\x7a\xa9\x76\x97\xa4\xf0\xe9\xa4\xef\x6f\x7d\x63\x28\xc3\xf2\xfd\xff\x0d\x66\xc6\xb5\x13\xa0\xc5\xbb\x6b\xe9\xf8\xd6\xcd\x9b\xef\xdf\x0d\xda\x37\xbd\x47\x8c\x1d\xf5\xfe\xd7\x80\xe7\x9a\x25\x67\x7f\x7e\xb8\xd9\x6c\xa2\x95\x52\x2b\xe1\x7f\x78\xd8\x77\x53\x62\x56\xf3\xe8\x83\x09\x80\x99\xad\xcc\xa1\xc0\x25\xea\xc5\x80\x7c\xdb\x62\x49\x62\xff\xc3\xb8\x24\xf6\xbf\xfd\xfd\xbf\x00\x00\x00\xff\xff\xb2\x1e\x6f\x68\x0c\x2c\x00\x00") func faucetHtmlBytes() ([]byte, error) { return bindataRead( @@ -85,7 +85,7 @@ func faucetHtml() (*asset, error) { } info := bindataFileInfo{name: "faucet.html", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xdb, 0xa2, 0x98, 0x44, 0x4b, 0x50, 0xf8, 0xa1, 0xac, 0x4a, 0x76, 0x2e, 0xcc, 0x3d, 0xcb, 0x81, 0x9e, 0x2a, 0xaa, 0x87, 0xf5, 0x9d, 0x53, 0x4, 0x8a, 0xdd, 0x5a, 0xfe, 0xd3, 0xc3, 0xf, 0x11}} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc5, 0x8d, 0xb, 0x7a, 0xfd, 0x70, 0x68, 0x68, 0xd2, 0xd8, 0xf3, 0xf6, 0xac, 0x72, 0xed, 0xc2, 0x76, 0x18, 0x2d, 0x1, 0xe5, 0x3b, 0x55, 0xb, 0xce, 0xfc, 0xb6, 0xd5, 0x59, 0xc3, 0x94, 0x5b}} return a, nil } diff --git a/cmd/geth/accountcmd_test.go b/cmd/geth/accountcmd_test.go index 819864de01..9455eeda36 100644 --- a/cmd/geth/accountcmd_test.go +++ b/cmd/geth/accountcmd_test.go @@ -102,6 +102,7 @@ func TestAccountImport(t *testing.T) { }, } for _, test := range tests { + test := test t.Run(test.name, func(t *testing.T) { t.Parallel() importAccountWithExpect(t, test.key, test.output) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 1d7688b5a4..da5ab27045 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -21,7 +21,6 @@ import ( "fmt" "io/ioutil" "os" - "path/filepath" "runtime" "strconv" "sync/atomic" @@ -29,18 +28,14 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/console/prompt" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/types/genesisT" - "github.com/ethereum/go-ethereum/trie" "gopkg.in/urfave/cli.v1" ) @@ -67,7 +62,14 @@ It expects the genesis file as argument.`, Usage: "Dumps genesis block JSON configuration to stdout", ArgsUsage: "", Flags: []cli.Flag{ - utils.DataDirFlag, + utils.MainnetFlag, + utils.RopstenFlag, + utils.RinkebyFlag, + utils.GoerliFlag, + utils.YoloV3Flag, + utils.ClassicFlag, + utils.MordorFlag, + utils.KottiFlag, }, Category: "BLOCKCHAIN COMMANDS", Description: ` @@ -153,45 +155,6 @@ be gzipped.`, Category: "BLOCKCHAIN COMMANDS", Description: ` The export-preimages command export hash preimages to an RLP encoded stream`, - } - copydbCommand = cli.Command{ - Action: utils.MigrateFlags(copyDb), - Name: "copydb", - Usage: "Create a local chain from a target chaindata folder", - ArgsUsage: "", - Flags: []cli.Flag{ - utils.DataDirFlag, - utils.AncientFlag, - utils.AncientRPCFlag, - utils.CacheFlag, - utils.SyncModeFlag, - utils.FakePoWFlag, - utils.MainnetFlag, - utils.ClassicFlag, - utils.MordorFlag, - utils.KottiFlag, - utils.RinkebyFlag, - utils.GoerliFlag, - utils.TxLookupLimitFlag, - utils.YoloV2Flag, - utils.LegacyTestnetFlag, - }, - Category: "BLOCKCHAIN COMMANDS", - Description: ` -The first argument must be the chaindata directory containing the blockchain to download from. -The second argument can be the ancient chain directory path (default = inside chaindata).`, - } - removedbCommand = cli.Command{ - Action: utils.MigrateFlags(removeDB), - Name: "removedb", - Usage: "Remove blockchain and state databases", - ArgsUsage: " ", - Flags: []cli.Flag{ - utils.DataDirFlag, - }, - Category: "BLOCKCHAIN COMMANDS", - Description: ` -Remove blockchain and state databases`, } dumpCommand = cli.Command{ Action: utils.MigrateFlags(dump), @@ -212,29 +175,6 @@ Remove blockchain and state databases`, The arguments are interpreted as block numbers or hashes. Use "ethereum dump 0" to dump the genesis block.`, } - inspectCommand = cli.Command{ - Action: utils.MigrateFlags(inspect), - Name: "inspect", - Usage: "Inspect the storage size for each type of data in the database", - ArgsUsage: " ", - Flags: []cli.Flag{ - utils.DataDirFlag, - utils.AncientFlag, - utils.CacheFlag, - utils.MainnetFlag, - utils.ClassicFlag, - utils.MordorFlag, - utils.KottiFlag, - utils.LegacyTestnetFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, - utils.YoloV2Flag, - utils.LegacyTestnetFlag, - utils.SyncModeFlag, - }, - Category: "BLOCKCHAIN COMMANDS", - } ) // initGenesis will initialise the given JSON format genesis file and writes it as @@ -261,7 +201,7 @@ func initGenesis(ctx *cli.Context) error { defer stack.Close() for _, name := range []string{"chaindata", "lightchaindata"} { - chaindb, err := stack.OpenDatabase(name, 0, 0, "") + chaindb, err := stack.OpenDatabase(name, 0, 0, "", false) if err != nil { utils.Fatalf("Failed to open database: %v", err) } @@ -276,6 +216,7 @@ func initGenesis(ctx *cli.Context) error { } func dumpGenesis(ctx *cli.Context) error { + // TODO(rjl493456442) support loading from the custom datadir genesis := utils.MakeGenesis(ctx) if genesis == nil { genesis = params.DefaultGenesisBlock() @@ -298,7 +239,7 @@ func importChain(ctx *cli.Context) error { stack, _ := makeConfigNode(ctx) defer stack.Close() - chain, db := utils.MakeChain(ctx, stack, false) + chain, db := utils.MakeChain(ctx, stack) defer db.Close() // Start periodically gathering memory profiles @@ -338,17 +279,7 @@ func importChain(ctx *cli.Context) error { fmt.Printf("Import done in %v.\n\n", time.Since(start)) // Output pre-compaction stats mostly to see the import trashing - stats, err := db.Stat("leveldb.stats") - if err != nil { - utils.Fatalf("Failed to read database stats: %v", err) - } - fmt.Println(stats) - - ioStats, err := db.Stat("leveldb.iostats") - if err != nil { - utils.Fatalf("Failed to read database iostats: %v", err) - } - fmt.Println(ioStats) + showLeveldbStats(db) // Print the memory statistics used by the importing mem := new(runtime.MemStats) @@ -366,22 +297,12 @@ func importChain(ctx *cli.Context) error { // Compact the entire database to more accurately measure disk io and print the stats start = time.Now() fmt.Println("Compacting entire database...") - if err = db.Compact(nil, nil); err != nil { + if err := db.Compact(nil, nil); err != nil { utils.Fatalf("Compaction failed: %v", err) } fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) - stats, err = db.Stat("leveldb.stats") - if err != nil { - utils.Fatalf("Failed to read database stats: %v", err) - } - fmt.Println(stats) - - ioStats, err = db.Stat("leveldb.iostats") - if err != nil { - utils.Fatalf("Failed to read database iostats: %v", err) - } - fmt.Println(ioStats) + showLeveldbStats(db) return importErr } @@ -393,7 +314,7 @@ func exportChain(ctx *cli.Context) error { stack, _ := makeConfigNode(ctx) defer stack.Close() - chain, _ := utils.MakeChain(ctx, stack, true) + chain, _ := utils.MakeChain(ctx, stack) start := time.Now() var err error @@ -410,6 +331,9 @@ func exportChain(ctx *cli.Context) error { if first < 0 || last < 0 { utils.Fatalf("Export error: block number must be greater than 0\n") } + if head := chain.CurrentFastBlock(); uint64(last) > head.NumberU64() { + utils.Fatalf("Export error: block number %d larger than head block %d\n", uint64(last), head.NumberU64()) + } err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last)) } @@ -429,7 +353,7 @@ func importPreimages(ctx *cli.Context) error { stack, _ := makeConfigNode(ctx) defer stack.Close() - db := utils.MakeChainDatabase(ctx, stack) + db := utils.MakeChainDatabase(ctx, stack, false) start := time.Now() if err := utils.ImportPreimages(db, ctx.Args().First()); err != nil { @@ -448,7 +372,7 @@ func exportPreimages(ctx *cli.Context) error { stack, _ := makeConfigNode(ctx) defer stack.Close() - db := utils.MakeChainDatabase(ctx, stack) + db := utils.MakeChainDatabase(ctx, stack, true) start := time.Now() if err := utils.ExportPreimages(db, ctx.Args().First()); err != nil { @@ -458,144 +382,31 @@ func exportPreimages(ctx *cli.Context) error { return nil } -func copyDb(ctx *cli.Context) error { - // Ensure we have a source chain directory to copy - if len(ctx.Args()) < 1 { - utils.Fatalf("Source chaindata directory path argument missing") - } - root := ctx.Args().First() - freezer := filepath.Join(root, "ancient") - // Ancient freeer path can be set as a second argument - if len(ctx.Args()) == 2 { - freezer = ctx.Args().Get(1) - } - // Initialize a new chain for the running node to sync into - stack, _ := makeConfigNode(ctx) - defer stack.Close() - - chain, chainDb := utils.MakeChain(ctx, stack, false) - syncMode := *utils.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode) - - var syncBloom *trie.SyncBloom - if syncMode == downloader.FastSync { - syncBloom = trie.NewSyncBloom(uint64(ctx.GlobalInt(utils.CacheFlag.Name)/2), chainDb) - } - dl := downloader.New(0, chainDb, syncBloom, new(event.TypeMux), chain, nil, nil) - - // Create a source peer to satisfy downloader requests from - db, err := rawdb.NewLevelDBDatabaseWithFreezer(root, ctx.GlobalInt(utils.CacheFlag.Name)/2, 256, freezer, "") - if err != nil { - return err - } - hc, err := core.NewHeaderChain(db, chain.Config(), chain.Engine(), func() bool { return false }) - if err != nil { - return err - } - peer := downloader.NewFakePeer("local", db, hc, dl) - if err = dl.RegisterPeer("local", 63, peer); err != nil { - return err - } - // Synchronise with the simulated peer - start := time.Now() - - currentHeader := hc.CurrentHeader() - if err = dl.Synchronise("local", currentHeader.Hash(), hc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64()), syncMode); err != nil { - return err - } - for dl.Synchronising() { - time.Sleep(10 * time.Millisecond) - } - fmt.Printf("Database copy done in %v\n", time.Since(start)) - - // Compact the entire database to remove any sync overhead - start = time.Now() - fmt.Println("Compacting entire database...") - if err = db.Compact(nil, nil); err != nil { - utils.Fatalf("Compaction failed: %v", err) - } - fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) - return nil -} - -func removeDB(ctx *cli.Context) error { - stack, config := makeConfigNode(ctx) - - // Remove the full node state database - path := stack.ResolvePath("chaindata") - if common.FileExist(path) { - confirmAndRemoveDB(path, "full node state database") - } else { - log.Info("Full node state database missing", "path", path) - } - // Remove the full node ancient database - path = config.Eth.DatabaseFreezer - switch { - case path == "": - path = filepath.Join(stack.ResolvePath("chaindata"), "ancient") - case !filepath.IsAbs(path): - path = config.Node.ResolvePath(path) - } - if common.FileExist(path) { - confirmAndRemoveDB(path, "full node ancient database") - } else { - log.Info("Full node ancient database missing", "path", path) - } - // Remove the light node database - path = stack.ResolvePath("lightchaindata") - if common.FileExist(path) { - confirmAndRemoveDB(path, "light node database") - } else { - log.Info("Light node database missing", "path", path) - } - return nil -} - -// confirmAndRemoveDB prompts the user for a last confirmation and removes the -// folder if accepted. -func confirmAndRemoveDB(database string, kind string) { - confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database)) - switch { - case err != nil: - utils.Fatalf("%v", err) - case !confirm: - log.Info("Database deletion skipped", "path", database) - default: - start := time.Now() - filepath.Walk(database, func(path string, info os.FileInfo, err error) error { - // If we're at the top level folder, recurse into - if path == database { - return nil - } - // Delete all the files, but not subfolders - if !info.IsDir() { - os.Remove(path) - return nil - } - return filepath.SkipDir - }) - log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start))) - } -} - func dump(ctx *cli.Context) error { stack, _ := makeConfigNode(ctx) defer stack.Close() - chain, chainDb := utils.MakeChain(ctx, stack, true) - defer chainDb.Close() + db := utils.MakeChainDatabase(ctx, stack, true) for _, arg := range ctx.Args() { - var block *types.Block + var header *types.Header if hashish(arg) { - block = chain.GetBlockByHash(common.HexToHash(arg)) + hash := common.HexToHash(arg) + number := rawdb.ReadHeaderNumber(db, hash) + if number != nil { + header = rawdb.ReadHeader(db, hash, *number) + } } else { - num, _ := strconv.Atoi(arg) - block = chain.GetBlockByNumber(uint64(num)) + number, _ := strconv.Atoi(arg) + hash := rawdb.ReadCanonicalHash(db, uint64(number)) + if hash != (common.Hash{}) { + header = rawdb.ReadHeader(db, hash, uint64(number)) + } } - if block == nil { + if header == nil { fmt.Println("{}") utils.Fatalf("block not found") } else { - state, err := state.New(block.Root(), state.NewDatabase(chainDb), nil) + state, err := state.New(header.Root, state.NewDatabase(db), nil) if err != nil { utils.Fatalf("could not create new state: %v", err) } @@ -616,16 +427,6 @@ func dump(ctx *cli.Context) error { return nil } -func inspect(ctx *cli.Context) error { - node, _ := makeConfigNode(ctx) - defer node.Close() - - _, chainDb := utils.MakeChain(ctx, node, true) - defer chainDb.Close() - - return rawdb.InspectDatabase(chainDb) -} - // hashish returns true for strings that look like hashes. func hashish(x string) bool { _, err := strconv.Atoi(x) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 80aea54443..bf6d357073 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -20,18 +20,18 @@ import ( "bufio" "errors" "fmt" + "math" "math/big" "os" "reflect" "unicode" - "github.com/ethereum/go-ethereum/common/math" - cli "gopkg.in/urfave/cli.v1" + "gopkg.in/urfave/cli.v1" "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/ethapi" - "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/naoina/toml" @@ -43,7 +43,7 @@ var ( Name: "dumpconfig", Usage: "Show configuration values", ArgsUsage: "", - Flags: append(append(nodeFlags, rpcFlags...), whisperFlags...), + Flags: append(nodeFlags, rpcFlags...), Category: "MISCELLANEOUS COMMANDS", Description: `The dumpconfig command shows configuration values.`, } @@ -75,21 +75,11 @@ type ethstatsConfig struct { URL string `toml:",omitempty"` } -// whisper has been deprecated, but clients out there might still have [Shh] -// in their config, which will crash. Cut them some slack by keeping the -// config, and displaying a message that those config switches are ineffectual. -// To be removed circa Q1 2021 -- @gballet. -type whisperDeprecatedConfig struct { - MaxMessageSize uint32 `toml:",omitempty"` - MinimumAcceptedPOW float64 `toml:",omitempty"` - RestrictConnectionBetweenLightClients bool `toml:",omitempty"` -} - type gethConfig struct { - Eth eth.Config - Shh whisperDeprecatedConfig + Eth ethconfig.Config Node node.Config Ethstats ethstatsConfig + Metrics metrics.Config } func loadConfig(file string, cfg *gethConfig) error { @@ -121,8 +111,9 @@ func defaultNodeConfig() node.Config { func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) { // Load defaults. cfg := gethConfig{ - Eth: eth.DefaultConfig, - Node: defaultNodeConfig(), + Eth: ethconfig.Defaults, + Node: defaultNodeConfig(), + Metrics: metrics.DefaultConfig, } // Load config file. @@ -130,10 +121,6 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) { if err := loadConfig(file, &cfg); err != nil { utils.Fatalf("%v", err) } - - if cfg.Shh != (whisperDeprecatedConfig{}) { - log.Warn("Deprecated whisper config detected. Whisper has been moved to github.com/ethereum/whisper") - } } // Apply flags. @@ -146,25 +133,19 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) { if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) { cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name) } - utils.SetShhConfig(ctx, stack) + applyMetricConfig(ctx, &cfg) return stack, cfg } -// enableWhisper returns true in case one of the whisper flags is set. -func checkWhisper(ctx *cli.Context) { - for _, flag := range whisperFlags { - if ctx.GlobalIsSet(flag.GetName()) { - log.Warn("deprecated whisper flag detected. Whisper has been moved to github.com/ethereum/whisper") - } - } -} - // makeFullNode loads geth configuration and creates the Ethereum backend. func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { stack, cfg := makeConfigNode(ctx) // Handle cross-chain configuration override cases. + if ctx.GlobalIsSet(utils.OverrideMagnetoFlag.Name) { + cfg.Eth.OverrideMagneto = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideMagnetoFlag.Name)) + } if ctx.GlobalIsSet(utils.ECBP1100Flag.Name) { if n := ctx.GlobalUint64(utils.ECBP1100Flag.Name); n != math.MaxUint64 { cfg.Eth.ECBP1100 = new(big.Int).SetUint64(n) @@ -178,7 +159,6 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { backend := utils.RegisterEthService(stack, &cfg.Eth) - checkWhisper(ctx) // Configure GraphQL if requested if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) { utils.RegisterGraphQLService(stack, backend, cfg.Node) @@ -218,3 +198,36 @@ func dumpConfig(ctx *cli.Context) error { return nil } + +func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) { + if ctx.GlobalIsSet(utils.MetricsEnabledFlag.Name) { + cfg.Metrics.Enabled = ctx.GlobalBool(utils.MetricsEnabledFlag.Name) + } + if ctx.GlobalIsSet(utils.MetricsEnabledExpensiveFlag.Name) { + cfg.Metrics.EnabledExpensive = ctx.GlobalBool(utils.MetricsEnabledExpensiveFlag.Name) + } + if ctx.GlobalIsSet(utils.MetricsHTTPFlag.Name) { + cfg.Metrics.HTTP = ctx.GlobalString(utils.MetricsHTTPFlag.Name) + } + if ctx.GlobalIsSet(utils.MetricsPortFlag.Name) { + cfg.Metrics.Port = ctx.GlobalInt(utils.MetricsPortFlag.Name) + } + if ctx.GlobalIsSet(utils.MetricsEnableInfluxDBFlag.Name) { + cfg.Metrics.EnableInfluxDB = ctx.GlobalBool(utils.MetricsEnableInfluxDBFlag.Name) + } + if ctx.GlobalIsSet(utils.MetricsInfluxDBEndpointFlag.Name) { + cfg.Metrics.InfluxDBEndpoint = ctx.GlobalString(utils.MetricsInfluxDBEndpointFlag.Name) + } + if ctx.GlobalIsSet(utils.MetricsInfluxDBDatabaseFlag.Name) { + cfg.Metrics.InfluxDBDatabase = ctx.GlobalString(utils.MetricsInfluxDBDatabaseFlag.Name) + } + if ctx.GlobalIsSet(utils.MetricsInfluxDBUsernameFlag.Name) { + cfg.Metrics.InfluxDBUsername = ctx.GlobalString(utils.MetricsInfluxDBUsernameFlag.Name) + } + if ctx.GlobalIsSet(utils.MetricsInfluxDBPasswordFlag.Name) { + cfg.Metrics.InfluxDBPassword = ctx.GlobalString(utils.MetricsInfluxDBPasswordFlag.Name) + } + if ctx.GlobalIsSet(utils.MetricsInfluxDBTagsFlag.Name) { + cfg.Metrics.InfluxDBTags = ctx.GlobalString(utils.MetricsInfluxDBTagsFlag.Name) + } +} diff --git a/cmd/geth/consolecmd.go b/cmd/geth/consolecmd.go index fa3ef0a297..c8c5a661ba 100644 --- a/cmd/geth/consolecmd.go +++ b/cmd/geth/consolecmd.go @@ -37,12 +37,12 @@ var ( Action: utils.MigrateFlags(localConsole), Name: "console", Usage: "Start an interactive JavaScript environment", - Flags: append(append(append(nodeFlags, rpcFlags...), consoleFlags...), whisperFlags...), + Flags: append(append(nodeFlags, rpcFlags...), consoleFlags...), Category: "CONSOLE COMMANDS", Description: ` The Geth console is an interactive shell for the JavaScript runtime environment which exposes a node admin interface as well as the Ðapp JavaScript API. -See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console.`, +See https://geth.ethereum.org/docs/interface/javascript-console.`, } attachCommand = cli.Command{ @@ -55,7 +55,7 @@ See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console.`, Description: ` The Geth console is an interactive shell for the JavaScript runtime environment which exposes a node admin interface as well as the Ðapp JavaScript API. -See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console. +See https://geth.ethereum.org/docs/interface/javascript-console. This command allows to open a console on a running geth node.`, } @@ -68,7 +68,7 @@ This command allows to open a console on a running geth node.`, Category: "CONSOLE COMMANDS", Description: ` The JavaScript VM exposes a node admin interface as well as the Ðapp -JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console`, +JavaScript API. See https://geth.ethereum.org/docs/interface/javascript-console`, } ) @@ -122,7 +122,7 @@ func remoteConsole(ctx *cli.Context) error { path = ctx.GlobalString(utils.DataDirFlag.Name) } if path != "" { - if ctx.GlobalBool(utils.LegacyTestnetFlag.Name) || ctx.GlobalBool(utils.RopstenFlag.Name) { + if ctx.GlobalBool(utils.RopstenFlag.Name) { // Maintain compatibility with older Geth configurations storing the // Ropsten database in `testnet` instead of `ropsten`. legacyPath := filepath.Join(path, "testnet") @@ -141,8 +141,8 @@ func remoteConsole(ctx *cli.Context) error { path = filepath.Join(path, "mordor") } else if ctx.GlobalBool(utils.GoerliFlag.Name) { path = filepath.Join(path, "goerli") - } else if ctx.GlobalBool(utils.YoloV2Flag.Name) { - path = filepath.Join(path, "yolo-v2") + } else if ctx.GlobalBool(utils.YoloV3Flag.Name) { + path = filepath.Join(path, "yolo-v3") } } endpoint = fmt.Sprintf("%s/geth.ipc", path) diff --git a/cmd/geth/consolecmd_cg_test.go b/cmd/geth/consolecmd_cg_test.go index 6851cef75a..ded641626b 100644 --- a/cmd/geth/consolecmd_cg_test.go +++ b/cmd/geth/consolecmd_cg_test.go @@ -33,13 +33,12 @@ func TestConsoleCmdNetworkIdentities(t *testing.T) { // All other possible -- values. {[]string{"--mainnet"}, 1, 1, params.MainnetGenesisHash.Hex()}, - {[]string{"--testnet"}, 3, 3, params.RopstenGenesisHash.Hex()}, {[]string{"--ropsten"}, 3, 3, params.RopstenGenesisHash.Hex()}, {[]string{"--rinkeby"}, 4, 4, params.RinkebyGenesisHash.Hex()}, {[]string{"--goerli"}, 5, 5, params.GoerliGenesisHash.Hex()}, {[]string{"--kotti"}, 6, 6, params.KottiGenesisHash.Hex()}, {[]string{"--mordor"}, 7, 63, params.MordorGenesisHash.Hex()}, - {[]string{"--yolov2"}, 133519467574834, 133519467574834, params.YoloV2GenesisHash.Hex()}, + {[]string{"--yolov3"}, int(params.YoloV3ChainConfig.ChainID.Uint64()), int(params.YoloV3ChainConfig.ChainID.Uint64()), params.YoloV3GenesisHash.Hex()}, } for i, p := range chainIdentityCases { @@ -161,6 +160,7 @@ func TestGethStartupLogs(t *testing.T) { for i, c := range cases { t.Run(fmt.Sprintf("TestGethStartupLogs/%d: %v", i, c.flags), func(t *testing.T) { geth := runGeth(t, append(c.flags, "--exec", "admin.nodeInfo.name", "console")...) + geth.KillTimeout = 10 * time.Second geth.ExpectRegexp("(?ism).*CoreGeth.*") geth.ExpectExit() if status := geth.ExitStatus(); status != 0 { diff --git a/cmd/geth/consolecmd_test.go b/cmd/geth/consolecmd_test.go index 80e9d75c98..18701fc16f 100644 --- a/cmd/geth/consolecmd_test.go +++ b/cmd/geth/consolecmd_test.go @@ -53,7 +53,7 @@ func TestConsoleWelcome(t *testing.T) { coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" // Start a geth console, make sure it's cleaned up and terminate the console - geth := runMinimalGeth(t, "--etherbase", coinbase, "console") + geth := runMinimalGeth(t, "--miner.etherbase", coinbase, "console") // Gather all the infos the welcome message needs to contain geth.SetTemplateFunc("clientname", func() string { @@ -109,7 +109,7 @@ func TestAttachWelcome(t *testing.T) { p := trulyRandInt(1024, 65533) // Yeah, sometimes this will fail, sorry :P httpPort = strconv.Itoa(p) wsPort = strconv.Itoa(p + 1) - geth := runMinimalGeth(t, "--etherbase", "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182", + geth := runMinimalGeth(t, "--miner.etherbase", "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182", "--ipcpath", ipc, "--http", "--http.port", httpPort, "--ws", "--ws.port", wsPort) diff --git a/cmd/geth/dao_test.go b/cmd/geth/dao_test.go index 21fa64d1df..67a5f89772 100644 --- a/cmd/geth/dao_test.go +++ b/cmd/geth/dao_test.go @@ -107,7 +107,7 @@ func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBloc } // Retrieve the DAO config flag from the database path := filepath.Join(datadir, "geth", "chaindata") - db, err := rawdb.NewLevelDBDatabase(path, 0, 0, "") + db, err := rawdb.NewLevelDBDatabase(path, 0, 0, "", false) if err != nil { t.Fatalf("test %d: failed to open test database: %v", test, err) } diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go new file mode 100644 index 0000000000..dcdbb3e270 --- /dev/null +++ b/cmd/geth/dbcmd.go @@ -0,0 +1,472 @@ +// Copyright 2020 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 . + +package main + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "time" + + "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/prompt" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/trie" + "gopkg.in/urfave/cli.v1" +) + +var ( + removedbCommand = cli.Command{ + Action: utils.MigrateFlags(removeDB), + Name: "removedb", + Usage: "Remove blockchain and state databases", + ArgsUsage: "", + Flags: []cli.Flag{ + utils.DataDirFlag, + }, + Category: "DATABASE COMMANDS", + Description: ` +Remove blockchain and state databases`, + } + dbCommand = cli.Command{ + Name: "db", + Usage: "Low level database operations", + ArgsUsage: "", + Category: "DATABASE COMMANDS", + Subcommands: []cli.Command{ + dbInspectCmd, + dbStatCmd, + dbCompactCmd, + dbGetCmd, + dbDeleteCmd, + dbPutCmd, + dbGetSlotsCmd, + }, + } + dbInspectCmd = cli.Command{ + Action: utils.MigrateFlags(inspect), + Name: "inspect", + ArgsUsage: " ", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.SyncModeFlag, + utils.MainnetFlag, + utils.RopstenFlag, + utils.RinkebyFlag, + utils.GoerliFlag, + utils.YoloV3Flag, + utils.ClassicFlag, + utils.MordorFlag, + utils.KottiFlag, + }, + Usage: "Inspect the storage size for each type of data in the database", + Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`, + } + dbStatCmd = cli.Command{ + Action: utils.MigrateFlags(dbStats), + Name: "stats", + Usage: "Print leveldb statistics", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.SyncModeFlag, + utils.MainnetFlag, + utils.RopstenFlag, + utils.RinkebyFlag, + utils.GoerliFlag, + utils.YoloV3Flag, + utils.ClassicFlag, + utils.MordorFlag, + utils.KottiFlag, + }, + } + dbCompactCmd = cli.Command{ + Action: utils.MigrateFlags(dbCompact), + Name: "compact", + Usage: "Compact leveldb database. WARNING: May take a very long time", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.SyncModeFlag, + utils.MainnetFlag, + utils.RopstenFlag, + utils.RinkebyFlag, + utils.GoerliFlag, + utils.YoloV3Flag, + utils.ClassicFlag, + utils.MordorFlag, + utils.KottiFlag, + utils.CacheFlag, + utils.CacheDatabaseFlag, + }, + Description: `This command performs a database compaction. +WARNING: This operation may take a very long time to finish, and may cause database +corruption if it is aborted during execution'!`, + } + dbGetCmd = cli.Command{ + Action: utils.MigrateFlags(dbGet), + Name: "get", + Usage: "Show the value of a database key", + ArgsUsage: "", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.SyncModeFlag, + utils.MainnetFlag, + utils.RopstenFlag, + utils.RinkebyFlag, + utils.GoerliFlag, + utils.YoloV3Flag, + utils.ClassicFlag, + utils.MordorFlag, + utils.KottiFlag, + }, + Description: "This command looks up the specified database key from the database.", + } + dbDeleteCmd = cli.Command{ + Action: utils.MigrateFlags(dbDelete), + Name: "delete", + Usage: "Delete a database key (WARNING: may corrupt your database)", + ArgsUsage: "", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.SyncModeFlag, + utils.MainnetFlag, + utils.RopstenFlag, + utils.RinkebyFlag, + utils.GoerliFlag, + utils.YoloV3Flag, + utils.ClassicFlag, + utils.MordorFlag, + utils.KottiFlag, + }, + Description: `This command deletes the specified database key from the database. +WARNING: This is a low-level operation which may cause database corruption!`, + } + dbPutCmd = cli.Command{ + Action: utils.MigrateFlags(dbPut), + Name: "put", + Usage: "Set the value of a database key (WARNING: may corrupt your database)", + ArgsUsage: " ", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.SyncModeFlag, + utils.MainnetFlag, + utils.RopstenFlag, + utils.RinkebyFlag, + utils.GoerliFlag, + utils.YoloV3Flag, + utils.ClassicFlag, + utils.MordorFlag, + utils.KottiFlag, + }, + Description: `This command sets a given database key to the given value. +WARNING: This is a low-level operation which may cause database corruption!`, + } + dbGetSlotsCmd = cli.Command{ + Action: utils.MigrateFlags(dbDumpTrie), + Name: "dumptrie", + Usage: "Show the storage key/values of a given storage trie", + ArgsUsage: " ", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.SyncModeFlag, + utils.MainnetFlag, + utils.RopstenFlag, + utils.RinkebyFlag, + utils.GoerliFlag, + utils.YoloV3Flag, + utils.ClassicFlag, + utils.MordorFlag, + utils.KottiFlag, + }, + Description: "This command looks up the specified database key from the database.", + } +) + +func removeDB(ctx *cli.Context) error { + stack, config := makeConfigNode(ctx) + + // Remove the full node state database + path := stack.ResolvePath("chaindata") + if common.FileExist(path) { + confirmAndRemoveDB(path, "full node state database") + } else { + log.Info("Full node state database missing", "path", path) + } + // Remove the full node ancient database + path = config.Eth.DatabaseFreezer + switch { + case path == "": + path = filepath.Join(stack.ResolvePath("chaindata"), "ancient") + case !filepath.IsAbs(path): + path = config.Node.ResolvePath(path) + } + if common.FileExist(path) { + confirmAndRemoveDB(path, "full node ancient database") + } else { + log.Info("Full node ancient database missing", "path", path) + } + // Remove the light node database + path = stack.ResolvePath("lightchaindata") + if common.FileExist(path) { + confirmAndRemoveDB(path, "light node database") + } else { + log.Info("Light node database missing", "path", path) + } + return nil +} + +// confirmAndRemoveDB prompts the user for a last confirmation and removes the +// folder if accepted. +func confirmAndRemoveDB(database string, kind string) { + confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database)) + switch { + case err != nil: + utils.Fatalf("%v", err) + case !confirm: + log.Info("Database deletion skipped", "path", database) + default: + start := time.Now() + filepath.Walk(database, func(path string, info os.FileInfo, err error) error { + // If we're at the top level folder, recurse into + if path == database { + return nil + } + // Delete all the files, but not subfolders + if !info.IsDir() { + os.Remove(path) + return nil + } + return filepath.SkipDir + }) + log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start))) + } +} + +func inspect(ctx *cli.Context) error { + var ( + prefix []byte + start []byte + ) + if ctx.NArg() > 2 { + return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage) + } + if ctx.NArg() >= 1 { + if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil { + return fmt.Errorf("failed to hex-decode 'prefix': %v", err) + } else { + prefix = d + } + } + if ctx.NArg() >= 2 { + if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil { + return fmt.Errorf("failed to hex-decode 'start': %v", err) + } else { + start = d + } + } + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + db := utils.MakeChainDatabase(ctx, stack, true) + defer db.Close() + + return rawdb.InspectDatabase(db, prefix, start) +} + +func showLeveldbStats(db ethdb.Stater) { + if stats, err := db.Stat("leveldb.stats"); err != nil { + log.Warn("Failed to read database stats", "error", err) + } else { + fmt.Println(stats) + } + if ioStats, err := db.Stat("leveldb.iostats"); err != nil { + log.Warn("Failed to read database iostats", "error", err) + } else { + fmt.Println(ioStats) + } +} + +func dbStats(ctx *cli.Context) error { + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + db := utils.MakeChainDatabase(ctx, stack, true) + defer db.Close() + + showLeveldbStats(db) + return nil +} + +func dbCompact(ctx *cli.Context) error { + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + db := utils.MakeChainDatabase(ctx, stack, false) + defer db.Close() + + log.Info("Stats before compaction") + showLeveldbStats(db) + + log.Info("Triggering compaction") + if err := db.Compact(nil, nil); err != nil { + log.Info("Compact err", "error", err) + return err + } + log.Info("Stats after compaction") + showLeveldbStats(db) + return nil +} + +// dbGet shows the value of a given database key +func dbGet(ctx *cli.Context) error { + if ctx.NArg() != 1 { + return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) + } + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + db := utils.MakeChainDatabase(ctx, stack, true) + defer db.Close() + + key, err := hexutil.Decode(ctx.Args().Get(0)) + if err != nil { + log.Info("Could not decode the key", "error", err) + return err + } + data, err := db.Get(key) + if err != nil { + log.Info("Get operation failed", "error", err) + return err + } + fmt.Printf("key %#x: %#x\n", key, data) + return nil +} + +// dbDelete deletes a key from the database +func dbDelete(ctx *cli.Context) error { + if ctx.NArg() != 1 { + return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) + } + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + db := utils.MakeChainDatabase(ctx, stack, false) + defer db.Close() + + key, err := hexutil.Decode(ctx.Args().Get(0)) + if err != nil { + log.Info("Could not decode the key", "error", err) + return err + } + data, err := db.Get(key) + if err == nil { + fmt.Printf("Previous value: %#x\n", data) + } + if err = db.Delete(key); err != nil { + log.Info("Delete operation returned an error", "error", err) + return err + } + return nil +} + +// dbPut overwrite a value in the database +func dbPut(ctx *cli.Context) error { + if ctx.NArg() != 2 { + return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) + } + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + db := utils.MakeChainDatabase(ctx, stack, false) + defer db.Close() + + var ( + key []byte + value []byte + data []byte + err error + ) + key, err = hexutil.Decode(ctx.Args().Get(0)) + if err != nil { + log.Info("Could not decode the key", "error", err) + return err + } + value, err = hexutil.Decode(ctx.Args().Get(1)) + if err != nil { + log.Info("Could not decode the value", "error", err) + return err + } + data, err = db.Get(key) + if err == nil { + fmt.Printf("Previous value: %#x\n", data) + } + return db.Put(key, value) +} + +// dbDumpTrie shows the key-value slots of a given storage trie +func dbDumpTrie(ctx *cli.Context) error { + if ctx.NArg() < 1 { + return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) + } + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + db := utils.MakeChainDatabase(ctx, stack, true) + defer db.Close() + var ( + root []byte + start []byte + max = int64(-1) + err error + ) + if root, err = hexutil.Decode(ctx.Args().Get(0)); err != nil { + log.Info("Could not decode the root", "error", err) + return err + } + stRoot := common.BytesToHash(root) + if ctx.NArg() >= 2 { + if start, err = hexutil.Decode(ctx.Args().Get(1)); err != nil { + log.Info("Could not decode the seek position", "error", err) + return err + } + } + if ctx.NArg() >= 3 { + if max, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil { + log.Info("Could not decode the max count", "error", err) + return err + } + } + theTrie, err := trie.New(stRoot, trie.NewDatabase(db)) + if err != nil { + return err + } + var count int64 + it := trie.NewIterator(theTrie.NodeIterator(start)) + for it.Next() { + if max > 0 && count == max { + fmt.Printf("Exiting after %d values\n", count) + break + } + fmt.Printf(" %d. key %#x: %#x\n", count, it.Key, it.Value) + count++ + } + return it.Err +} diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 219d91a56a..3037ae5fe3 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -19,9 +19,7 @@ package main import ( "fmt" - "math" "os" - godebug "runtime/debug" "sort" "strconv" "strings" @@ -41,8 +39,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" - gopsutil "github.com/shirou/gopsutil/mem" - cli "gopkg.in/urfave/cli.v1" + "gopkg.in/urfave/cli.v1" ) const ( @@ -62,15 +59,16 @@ var ( utils.UnlockedAccountFlag, utils.PasswordFileFlag, utils.BootnodesFlag, - utils.LegacyBootnodesV4Flag, - utils.LegacyBootnodesV5Flag, utils.DataDirFlag, utils.AncientFlag, + utils.MinFreeDiskSpaceFlag, utils.AncientRPCFlag, utils.KeyStoreDirFlag, utils.ExternalSignerFlag, utils.NoUSBFlag, + utils.USBFlag, utils.SmartCardDaemonPathFlag, + utils.OverrideMagnetoFlag, utils.EthashCacheDirFlag, utils.EthashCachesInMemoryFlag, utils.EthashCachesOnDiskFlag, @@ -96,17 +94,17 @@ var ( utils.SnapshotFlag, utils.TxLookupLimitFlag, utils.LightServeFlag, - utils.LegacyLightServFlag, utils.LightIngressFlag, utils.LightEgressFlag, utils.LightMaxPeersFlag, - utils.LegacyLightPeersFlag, utils.LightNoPruneFlag, utils.LightKDFFlag, utils.UltraLightServersFlag, utils.UltraLightFractionFlag, utils.UltraLightOnlyAnnounceFlag, + utils.LightNoSyncServeFlag, utils.WhitelistFlag, + utils.BloomFilterSizeFlag, utils.CacheFlag, utils.CacheDatabaseFlag, utils.CacheTrieFlag, @@ -115,22 +113,18 @@ var ( utils.CacheGCFlag, utils.CacheSnapshotFlag, utils.CacheNoPrefetchFlag, + utils.CachePreimagesFlag, utils.ListenPortFlag, utils.MaxPeersFlag, utils.MaxPendingPeersFlag, utils.MiningEnabledFlag, utils.MinerThreadsFlag, - utils.LegacyMinerThreadsFlag, utils.MinerNotifyFlag, utils.MinerGasTargetFlag, - utils.LegacyMinerGasTargetFlag, utils.MinerGasLimitFlag, utils.MinerGasPriceFlag, - utils.LegacyMinerGasPriceFlag, utils.MinerEtherbaseFlag, - utils.LegacyMinerEtherbaseFlag, utils.MinerExtraDataFlag, - utils.LegacyMinerExtraDataFlag, utils.MinerRecommitIntervalFlag, utils.MinerNoVerfiyFlag, utils.NATFlag, @@ -145,14 +139,13 @@ var ( utils.DeveloperFlag, utils.DeveloperPeriodFlag, utils.DeveloperPoWFlag, - utils.ClassicFlag, - utils.MordorFlag, - utils.LegacyTestnetFlag, utils.RopstenFlag, utils.RinkebyFlag, - utils.KottiFlag, utils.GoerliFlag, - utils.YoloV2Flag, + utils.YoloV3Flag, + utils.ClassicFlag, + utils.MordorFlag, + utils.KottiFlag, utils.VMEnableDebugFlag, utils.NetworkIdFlag, utils.EthStatsURLFlag, @@ -160,12 +153,11 @@ var ( utils.FakePoWPoissonFlag, utils.NoCompactionFlag, utils.GpoBlocksFlag, - utils.LegacyGpoBlocksFlag, utils.GpoPercentileFlag, - utils.LegacyGpoPercentileFlag, utils.GpoMaxGasPriceFlag, utils.EWASMInterpreterFlag, utils.EVMInterpreterFlag, + utils.MinerNotifyFullFlag, utils.ECBP1100Flag, utils.ECBP1100NoDisableFlag, configFileFlag, @@ -182,32 +174,24 @@ var ( utils.LegacyRPCPortFlag, utils.LegacyRPCCORSDomainFlag, utils.LegacyRPCVirtualHostsFlag, + utils.LegacyRPCApiFlag, utils.GraphQLEnabledFlag, utils.GraphQLCORSDomainFlag, utils.GraphQLVirtualHostsFlag, utils.HTTPApiFlag, - utils.LegacyRPCApiFlag, + utils.HTTPPathPrefixFlag, utils.WSEnabledFlag, utils.WSListenAddrFlag, - utils.LegacyWSListenAddrFlag, utils.WSPortFlag, - utils.LegacyWSPortFlag, utils.WSApiFlag, - utils.LegacyWSApiFlag, utils.WSAllowedOriginsFlag, - utils.LegacyWSAllowedOriginsFlag, + utils.WSPathPrefixFlag, utils.IPCDisabledFlag, utils.IPCPathFlag, utils.InsecureUnlockAllowedFlag, utils.RPCGlobalGasCapFlag, utils.RPCGlobalTxFeeCapFlag, - } - - whisperFlags = []cli.Flag{ - utils.WhisperEnabledFlag, - utils.WhisperMaxMessageSizeFlag, - utils.WhisperMinPOWFlag, - utils.WhisperRestrictConnectionBetweenLightClientsFlag, + utils.AllowUnprotectedTxs, } metricsFlags = []cli.Flag{ @@ -228,7 +212,7 @@ func init() { // Initialize the CLI app and start Geth app.Action = geth app.HideVersion = true // we have a command to print the version - app.Copyright = "Copyright 2013-2020 The core-geth and go-ethereum Authors" + app.Copyright = "Copyright 2013-2021 The core-geth and go-ethereum Authors" app.Commands = []cli.Command{ // See chaincmd.go: initCommand, @@ -236,11 +220,9 @@ func init() { exportCommand, importPreimagesCommand, exportPreimagesCommand, - copydbCommand, removedbCommand, dumpCommand, dumpGenesisCommand, - inspectCommand, // See accountcmd.go: accountCommand, walletCommand, @@ -252,13 +234,16 @@ func init() { makecacheCommand, makedagCommand, versionCommand, + versionCheckCommand, licenseCommand, // See config.go dumpConfigCommand, - // See retesteth.go - retestethCommand, + // see dbcmd.go + dbCommand, // See cmd/utils/flags_legacy.go utils.ShowDeprecated, + // See snapshot.go + snapshotCommand, } sort.Sort(cli.CommandsByName(app.Commands)) @@ -266,8 +251,6 @@ func init() { app.Flags = append(app.Flags, rpcFlags...) app.Flags = append(app.Flags, consoleFlags...) app.Flags = append(app.Flags, debug.Flags...) - app.Flags = append(app.Flags, debug.DeprecatedFlags...) - app.Flags = append(app.Flags, whisperFlags...) app.Flags = append(app.Flags, metricsFlags...) app.Before = func(ctx *cli.Context) error { @@ -291,11 +274,6 @@ func checkMainnet(ctx *cli.Context) bool { isMainnet := false switch { - case ctx.GlobalIsSet(utils.LegacyTestnetFlag.Name): - log.Info("Starting Geth on Ropsten testnet...") - log.Warn("The --testnet flag is ambiguous! Please specify one of --goerli, --rinkeby, or --ropsten.") - log.Warn("The generic --testnet flag is deprecated and will be removed in the future!") - case ctx.GlobalIsSet(utils.RopstenFlag.Name): log.Info("Starting Geth on Ropsten testnet...") @@ -305,6 +283,9 @@ func checkMainnet(ctx *cli.Context) bool { case ctx.GlobalIsSet(utils.GoerliFlag.Name): log.Info("Starting Geth on Görli testnet...") + case ctx.GlobalIsSet(utils.YoloV3Flag.Name): + log.Info("Starting Geth on YOLOv3 testnet...") + case ctx.GlobalIsSet(utils.DeveloperFlag.Name): log.Info("Starting Geth in ephemeral proof-of-authority network dev mode...") @@ -320,8 +301,8 @@ func checkMainnet(ctx *cli.Context) bool { case ctx.GlobalIsSet(utils.KottiFlag.Name): log.Info("Starting Geth on Kotti testnet...") - case ctx.GlobalIsSet(utils.YoloV2Flag.Name): - log.Info("Starting Geth on YoloV2 testnet...") + case ctx.GlobalIsSet(utils.YoloV3Flag.Name): + log.Info("Starting Geth on YoloV3 testnet...") case !ctx.GlobalIsSet(utils.NetworkIdFlag.Name): log.Info("Starting Geth on Ethereum mainnet...") @@ -352,25 +333,6 @@ func prepare(ctx *cli.Context) { log.Info("Dropping default light client cache", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 128) ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(128)) } - // Cap the cache allowance and tune the garbage collector - mem, err := gopsutil.VirtualMemory() - if err == nil { - if 32<<(^uintptr(0)>>63) == 32 && mem.Total > 2*1024*1024*1024 { - log.Warn("Lowering memory allowance on 32bit arch", "available", mem.Total/1024/1024, "addressable", 2*1024) - mem.Total = 2 * 1024 * 1024 * 1024 - } - allowance := int(mem.Total / 1024 / 1024 / 3) - if cache := ctx.GlobalInt(utils.CacheFlag.Name); cache > allowance { - log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance) - ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(allowance)) - } - } - // Ensure Go's GC ignores the database cache for trigger percentage - cache := ctx.GlobalInt(utils.CacheFlag.Name) - gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024))) - - log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc)) - godebug.SetGCPercent(int(gogc)) // Start metrics export if enabled utils.SetupMetrics(ctx) @@ -403,7 +365,7 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) { debug.Memsize.Add("node", stack) // Start up the node itself - utils.StartNode(stack) + utils.StartNode(ctx, stack) // Unlock any account specifically requested unlockAccounts(ctx, stack) @@ -487,22 +449,11 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) { if !ok { utils.Fatalf("Ethereum service not running: %v", err) } - // Set the gas price to the limits from the CLI and start mining gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name) - if ctx.GlobalIsSet(utils.LegacyMinerGasPriceFlag.Name) && !ctx.GlobalIsSet(utils.MinerGasPriceFlag.Name) { - gasprice = utils.GlobalBig(ctx, utils.LegacyMinerGasPriceFlag.Name) - } ethBackend.TxPool().SetGasPrice(gasprice) // start mining threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name) - if ctx.GlobalIsSet(utils.LegacyMinerThreadsFlag.Name) && !ctx.GlobalIsSet(utils.MinerThreadsFlag.Name) { - threads = ctx.GlobalInt(utils.LegacyMinerThreadsFlag.Name) - log.Warn("The flag --minerthreads is deprecated and will be removed in the future, please use --miner.threads") - } - if isDeveloperMode && !ctx.GlobalIsSet(utils.MinerThreadsFlag.Name) && !ctx.GlobalIsSet(utils.LegacyMinerThreadsFlag.Name) { - threads = 1 - } if err := ethBackend.StartMining(threads); err != nil { utils.Fatalf("Failed to start mining: %v", err) } diff --git a/cmd/geth/misccmd.go b/cmd/geth/misccmd.go index 97e4fe806a..2524daed94 100644 --- a/cmd/geth/misccmd.go +++ b/cmd/geth/misccmd.go @@ -25,12 +25,23 @@ import ( "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" "gopkg.in/urfave/cli.v1" ) var ( + VersionCheckUrlFlag = cli.StringFlag{ + Name: "check.url", + Usage: "URL to use when checking vulnerabilities", + Value: "https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities.json", + } + VersionCheckVersionFlag = cli.StringFlag{ + Name: "check.version", + Usage: "Version to check", + Value: fmt.Sprintf("Geth/v%v/%v-%v/%v", + params.VersionWithCommit(gitCommit, gitDate), + runtime.GOOS, runtime.GOARCH, runtime.Version()), + } makecacheCommand = cli.Command{ Action: utils.MigrateFlags(makecache), Name: "makecache", @@ -71,6 +82,21 @@ Regular users do not need to execute it. Category: "MISCELLANEOUS COMMANDS", Description: ` The output of this command is supposed to be machine-readable. +`, + } + versionCheckCommand = cli.Command{ + Action: utils.MigrateFlags(versionCheck), + Flags: []cli.Flag{ + VersionCheckUrlFlag, + VersionCheckVersionFlag, + }, + Name: "version-check", + Usage: "Checks (online) whether the current version suffers from any known security vulnerabilities", + ArgsUsage: "", + Category: "MISCELLANEOUS COMMANDS", + Description: ` +The version-check command fetches vulnerability-information from https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities.json, +and displays information about any security vulnerabilities that affect the currently executing version. `, } licenseCommand = cli.Command{ @@ -132,7 +158,6 @@ func version(ctx *cli.Context) error { fmt.Println("Git Commit Date:", gitDate) } fmt.Println("Architecture:", runtime.GOARCH) - fmt.Println("Protocol Versions:", eth.DefaultProtocolVersions) fmt.Println("Go Version:", runtime.Version()) fmt.Println("Operating System:", runtime.GOOS) fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH")) diff --git a/cmd/geth/retesteth.go b/cmd/geth/retesteth.go deleted file mode 100644 index a0fcf95c12..0000000000 --- a/cmd/geth/retesteth.go +++ /dev/null @@ -1,920 +0,0 @@ -// Copyright 2019 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 . - -package main - -import ( - "context" - "fmt" - "math/big" - "os" - "os/signal" - "time" - - "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/common/math" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/consensus/misc" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/params/confp/generic" - "github.com/ethereum/go-ethereum/params/types/ctypes" - "github.com/ethereum/go-ethereum/params/types/genesisT" - "github.com/ethereum/go-ethereum/params/types/goethereum" - "github.com/ethereum/go-ethereum/params/vars" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/trie" - - cli "gopkg.in/urfave/cli.v1" -) - -var ( - rpcPortFlag = cli.IntFlag{ - Name: "rpcport", - Usage: "HTTP-RPC server listening port", - Value: node.DefaultHTTPPort, - } - retestethCommand = cli.Command{ - Action: utils.MigrateFlags(retesteth), - Name: "retesteth", - Usage: "Launches geth in retesteth mode", - ArgsUsage: "", - Flags: []cli.Flag{rpcPortFlag}, - Category: "MISCELLANEOUS COMMANDS", - Description: `Launches geth in retesteth mode (no database, no network, only retesteth RPC interface)`, - } -) - -type RetestethTestAPI interface { - SetChainParams(ctx context.Context, chainParams ChainParams) (bool, error) - MineBlocks(ctx context.Context, number uint64) (bool, error) - ModifyTimestamp(ctx context.Context, interval uint64) (bool, error) - ImportRawBlock(ctx context.Context, rawBlock hexutil.Bytes) (common.Hash, error) - RewindToBlock(ctx context.Context, number uint64) (bool, error) - GetLogHash(ctx context.Context, txHash common.Hash) (common.Hash, error) -} - -type RetestethEthAPI interface { - SendRawTransaction(ctx context.Context, rawTx hexutil.Bytes) (common.Hash, error) - BlockNumber(ctx context.Context) (uint64, error) - GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, error) - GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) - GetBalance(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (*math.HexOrDecimal256, error) - GetCode(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (hexutil.Bytes, error) - GetTransactionCount(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (uint64, error) -} - -type RetestethDebugAPI interface { - AccountRange(ctx context.Context, - blockHashOrNumber *math.HexOrDecimal256, txIndex uint64, - addressHash *math.HexOrDecimal256, maxResults uint64, - ) (AccountRangeResult, error) - StorageRangeAt(ctx context.Context, - blockHashOrNumber *math.HexOrDecimal256, txIndex uint64, - address common.Address, - begin *math.HexOrDecimal256, maxResults uint64, - ) (StorageRangeResult, error) -} - -type RetestWeb3API interface { - ClientVersion(ctx context.Context) (string, error) -} - -type RetestethAPI struct { - ethDb ethdb.Database - db state.Database - chainConfig ctypes.ChainConfigurator - author common.Address - extraData []byte - genesisHash common.Hash - engine *NoRewardEngine - blockchain *core.BlockChain - txMap map[common.Address]map[uint64]*types.Transaction // Sender -> Nonce -> Transaction - txSenders map[common.Address]struct{} // Set of transaction senders - blockInterval uint64 -} - -type ChainParams struct { - SealEngine string `json:"sealEngine"` - Params CParamsParams `json:"params"` - Genesis CParamsGenesis `json:"genesis"` - Accounts map[common.Address]CParamsAccount `json:"accounts"` -} - -type CParamsParams struct { - AccountStartNonce math.HexOrDecimal64 `json:"accountStartNonce"` - HomesteadForkBlock *math.HexOrDecimal64 `json:"homesteadForkBlock"` - EIP150ForkBlock *math.HexOrDecimal64 `json:"EIP150ForkBlock"` - EIP158ForkBlock *math.HexOrDecimal64 `json:"EIP158ForkBlock"` - DaoHardforkBlock *math.HexOrDecimal64 `json:"daoHardforkBlock"` - ByzantiumForkBlock *math.HexOrDecimal64 `json:"byzantiumForkBlock"` - ConstantinopleForkBlock *math.HexOrDecimal64 `json:"constantinopleForkBlock"` - ConstantinopleFixForkBlock *math.HexOrDecimal64 `json:"constantinopleFixForkBlock"` - IstanbulBlock *math.HexOrDecimal64 `json:"istanbulForkBlock"` - ChainID *math.HexOrDecimal256 `json:"chainID"` - MaximumExtraDataSize math.HexOrDecimal64 `json:"maximumExtraDataSize"` - TieBreakingGas bool `json:"tieBreakingGas"` - MinGasLimit math.HexOrDecimal64 `json:"minGasLimit"` - MaxGasLimit math.HexOrDecimal64 `json:"maxGasLimit"` - GasLimitBoundDivisor math.HexOrDecimal64 `json:"gasLimitBoundDivisor"` - MinimumDifficulty math.HexOrDecimal256 `json:"minimumDifficulty"` - DifficultyBoundDivisor math.HexOrDecimal256 `json:"difficultyBoundDivisor"` - DurationLimit math.HexOrDecimal256 `json:"durationLimit"` - BlockReward math.HexOrDecimal256 `json:"blockReward"` - NetworkID math.HexOrDecimal256 `json:"networkID"` -} - -type CParamsGenesis struct { - Nonce math.HexOrDecimal64 `json:"nonce"` - Difficulty *math.HexOrDecimal256 `json:"difficulty"` - MixHash *math.HexOrDecimal256 `json:"mixHash"` - Author common.Address `json:"author"` - Timestamp math.HexOrDecimal64 `json:"timestamp"` - ParentHash common.Hash `json:"parentHash"` - ExtraData hexutil.Bytes `json:"extraData"` - GasLimit math.HexOrDecimal64 `json:"gasLimit"` -} - -type CParamsAccount struct { - Balance *math.HexOrDecimal256 `json:"balance"` - Precompiled *CPAccountPrecompiled `json:"precompiled"` - Code hexutil.Bytes `json:"code"` - Storage map[string]string `json:"storage"` - Nonce *math.HexOrDecimal64 `json:"nonce"` -} - -type CPAccountPrecompiled struct { - Name string `json:"name"` - StartingBlock math.HexOrDecimal64 `json:"startingBlock"` - Linear *CPAPrecompiledLinear `json:"linear"` -} - -type CPAPrecompiledLinear struct { - Base uint64 `json:"base"` - Word uint64 `json:"word"` -} - -type AccountRangeResult struct { - AddressMap map[common.Hash]common.Address `json:"addressMap"` - NextKey common.Hash `json:"nextKey"` -} - -type StorageRangeResult struct { - Complete bool `json:"complete"` - Storage map[common.Hash]SRItem `json:"storage"` -} - -type SRItem struct { - Key string `json:"key"` - Value string `json:"value"` -} - -type NoRewardEngine struct { - inner consensus.Engine - rewardsOn bool -} - -func (e *NoRewardEngine) Author(header *types.Header) (common.Address, error) { - return e.inner.Author(header) -} - -func (e *NoRewardEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error { - return e.inner.VerifyHeader(chain, header, seal) -} - -func (e *NoRewardEngine) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { - return e.inner.VerifyHeaders(chain, headers, seals) -} - -func (e *NoRewardEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { - return e.inner.VerifyUncles(chain, block) -} - -func (e *NoRewardEngine) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error { - return e.inner.VerifySeal(chain, header) -} - -func (e *NoRewardEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { - return e.inner.Prepare(chain, header) -} - -func (e *NoRewardEngine) accumulateRewards(config ctypes.ChainConfigurator, state *state.StateDB, header *types.Header, uncles []*types.Header) { - // Simply touch miner and uncle coinbase accounts - reward := big.NewInt(0) - for _, uncle := range uncles { - state.AddBalance(uncle.Coinbase, reward) - } - state.AddBalance(header.Coinbase, reward) -} - -func (e *NoRewardEngine) Finalize(chain consensus.ChainHeaderReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction, - uncles []*types.Header) { - if e.rewardsOn { - e.inner.Finalize(chain, header, statedb, txs, uncles) - } else { - e.accumulateRewards(chain.Config(), statedb, header, uncles) - header.Root = statedb.IntermediateRoot(chain.Config().IsEnabled(chain.Config().GetEIP161dTransition, header.Number)) - } -} - -func (e *NoRewardEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction, - uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { - if e.rewardsOn { - return e.inner.FinalizeAndAssemble(chain, header, statedb, txs, uncles, receipts) - } else { - e.accumulateRewards(chain.Config(), statedb, header, uncles) - header.Root = statedb.IntermediateRoot(chain.Config().IsEnabled(chain.Config().GetEIP161dTransition, header.Number)) - - // Header seems complete, assemble into a block and return - return types.NewBlock(header, txs, uncles, receipts, new(trie.Trie)), nil - } -} - -func (e *NoRewardEngine) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { - return e.inner.Seal(chain, block, results, stop) -} - -func (e *NoRewardEngine) SealHash(header *types.Header) common.Hash { - return e.inner.SealHash(header) -} - -func (e *NoRewardEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { - return e.inner.CalcDifficulty(chain, time, parent) -} - -func (e *NoRewardEngine) APIs(chain consensus.ChainHeaderReader) []rpc.API { - return e.inner.APIs(chain) -} - -func (e *NoRewardEngine) Close() error { - return e.inner.Close() -} - -func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainParams) (bool, error) { - // Clean up - if api.blockchain != nil { - api.blockchain.Stop() - } - if api.engine != nil { - api.engine.Close() - } - if api.ethDb != nil { - api.ethDb.Close() - } - ethDb := rawdb.NewMemoryDatabase() - accounts := make(genesisT.GenesisAlloc) - for address, account := range chainParams.Accounts { - balance := big.NewInt(0) - if account.Balance != nil { - balance.Set((*big.Int)(account.Balance)) - } - var nonce uint64 - if account.Nonce != nil { - nonce = uint64(*account.Nonce) - } - if account.Precompiled == nil || account.Balance != nil { - storage := make(map[common.Hash]common.Hash) - for k, v := range account.Storage { - storage[common.HexToHash(k)] = common.HexToHash(v) - } - accounts[address] = genesisT.GenesisAccount{ - Balance: balance, - Code: account.Code, - Nonce: nonce, - Storage: storage, - } - } - } - chainId := big.NewInt(1) - if chainParams.Params.ChainID != nil { - chainId.Set((*big.Int)(chainParams.Params.ChainID)) - } - var ( - homesteadBlock *big.Int - daoForkBlock *big.Int - eip150Block *big.Int - eip155Block *big.Int - eip158Block *big.Int - byzantiumBlock *big.Int - constantinopleBlock *big.Int - petersburgBlock *big.Int - istanbulBlock *big.Int - ) - if chainParams.Params.HomesteadForkBlock != nil { - homesteadBlock = big.NewInt(int64(*chainParams.Params.HomesteadForkBlock)) - } - if chainParams.Params.DaoHardforkBlock != nil { - daoForkBlock = big.NewInt(int64(*chainParams.Params.DaoHardforkBlock)) - } - if chainParams.Params.EIP150ForkBlock != nil { - eip150Block = big.NewInt(int64(*chainParams.Params.EIP150ForkBlock)) - } - if chainParams.Params.EIP158ForkBlock != nil { - eip158Block = big.NewInt(int64(*chainParams.Params.EIP158ForkBlock)) - eip155Block = eip158Block - } - if chainParams.Params.ByzantiumForkBlock != nil { - byzantiumBlock = big.NewInt(int64(*chainParams.Params.ByzantiumForkBlock)) - } - if chainParams.Params.ConstantinopleForkBlock != nil { - constantinopleBlock = big.NewInt(int64(*chainParams.Params.ConstantinopleForkBlock)) - } - if chainParams.Params.ConstantinopleFixForkBlock != nil { - petersburgBlock = big.NewInt(int64(*chainParams.Params.ConstantinopleFixForkBlock)) - } - if constantinopleBlock != nil && petersburgBlock == nil { - petersburgBlock = big.NewInt(100000000000) - } - if chainParams.Params.IstanbulBlock != nil { - istanbulBlock = big.NewInt(int64(*chainParams.Params.IstanbulBlock)) - } - - genesis := &genesisT.Genesis{ - Config: &goethereum.ChainConfig{ - ChainID: chainId, - HomesteadBlock: homesteadBlock, - DAOForkBlock: daoForkBlock, - DAOForkSupport: true, - EIP150Block: eip150Block, - EIP155Block: eip155Block, - EIP158Block: eip158Block, - ByzantiumBlock: byzantiumBlock, - ConstantinopleBlock: constantinopleBlock, - PetersburgBlock: petersburgBlock, - IstanbulBlock: istanbulBlock, - }, - Nonce: uint64(chainParams.Genesis.Nonce), - Timestamp: uint64(chainParams.Genesis.Timestamp), - ExtraData: chainParams.Genesis.ExtraData, - GasLimit: uint64(chainParams.Genesis.GasLimit), - Difficulty: big.NewInt(0).Set((*big.Int)(chainParams.Genesis.Difficulty)), - Mixhash: common.BigToHash((*big.Int)(chainParams.Genesis.MixHash)), - Coinbase: chainParams.Genesis.Author, - ParentHash: chainParams.Genesis.ParentHash, - Alloc: accounts, - } - chainConfig, genesisHash, err := core.SetupGenesisBlock(ethDb, genesis) - if err != nil { - return false, err - } - fmt.Printf("Chain config: %v\n", chainConfig) - - var inner consensus.Engine - switch chainParams.SealEngine { - case "NoProof", "NoReward": - inner = ethash.NewFaker() - case "Ethash": - inner = ethash.New(ethash.Config{ - CacheDir: "ethash", - CachesInMem: 2, - CachesOnDisk: 3, - CachesLockMmap: false, - DatasetsInMem: 1, - DatasetsOnDisk: 2, - DatasetsLockMmap: false, - ECIP1099Block: api.chainConfig.GetEthashECIP1099Transition(), - }, nil, false) - default: - return false, fmt.Errorf("unrecognised seal engine: %s", chainParams.SealEngine) - } - engine := &NoRewardEngine{inner: inner, rewardsOn: chainParams.SealEngine != "NoReward"} - - blockchain, err := core.NewBlockChain(ethDb, nil, chainConfig, engine, vm.Config{}, nil, nil) - if err != nil { - return false, err - } - - api.chainConfig = chainConfig - api.genesisHash = genesisHash - api.author = chainParams.Genesis.Author - api.extraData = chainParams.Genesis.ExtraData - api.ethDb = ethDb - api.engine = engine - api.blockchain = blockchain - api.db = state.NewDatabase(api.ethDb) - api.txMap = make(map[common.Address]map[uint64]*types.Transaction) - api.txSenders = make(map[common.Address]struct{}) - api.blockInterval = 0 - return true, nil -} - -func (api *RetestethAPI) SendRawTransaction(ctx context.Context, rawTx hexutil.Bytes) (common.Hash, error) { - tx := new(types.Transaction) - if err := rlp.DecodeBytes(rawTx, tx); err != nil { - // Return nil is not by mistake - some tests include sending transaction where gasLimit overflows uint64 - return common.Hash{}, nil - } - signer := types.MakeSigner(api.chainConfig, big.NewInt(int64(api.currentNumber()))) - sender, err := types.Sender(signer, tx) - if err != nil { - return common.Hash{}, err - } - if nonceMap, ok := api.txMap[sender]; ok { - nonceMap[tx.Nonce()] = tx - } else { - nonceMap = make(map[uint64]*types.Transaction) - nonceMap[tx.Nonce()] = tx - api.txMap[sender] = nonceMap - } - api.txSenders[sender] = struct{}{} - return tx.Hash(), nil -} - -func (api *RetestethAPI) MineBlocks(ctx context.Context, number uint64) (bool, error) { - for i := 0; i < int(number); i++ { - if err := api.mineBlock(); err != nil { - return false, err - } - } - fmt.Printf("Mined %d blocks\n", number) - return true, nil -} - -func (api *RetestethAPI) currentNumber() uint64 { - if current := api.blockchain.CurrentBlock(); current != nil { - return current.NumberU64() - } - return 0 -} - -func (api *RetestethAPI) mineBlock() error { - number := api.currentNumber() - parentHash := rawdb.ReadCanonicalHash(api.ethDb, number) - parent := rawdb.ReadBlock(api.ethDb, parentHash, number) - var timestamp uint64 - if api.blockInterval == 0 { - timestamp = uint64(time.Now().Unix()) - } else { - timestamp = parent.Time() + api.blockInterval - } - gasLimit := core.CalcGasLimit(parent, 9223372036854775807, 9223372036854775807) - header := &types.Header{ - ParentHash: parent.Hash(), - Number: big.NewInt(int64(number + 1)), - GasLimit: gasLimit, - Extra: api.extraData, - Time: timestamp, - } - header.Coinbase = api.author - if api.engine != nil { - api.engine.Prepare(api.blockchain, header) - } - // If we are care about TheDAO hard-fork check whether to override the extra-data or not - if generic.AsGenericCC(api.chainConfig).DAOSupport() { - daoBlock := new(big.Int).SetUint64(*api.chainConfig.GetEthashEIP779Transition()) - // Check whether the block is among the fork extra-override range - limit := new(big.Int).Add(daoBlock, vars.DAOForkExtraRange) - if header.Number.Cmp(daoBlock) >= 0 && header.Number.Cmp(limit) < 0 { - header.Extra = common.CopyBytes(vars.DAOForkBlockExtra) - } - } - statedb, err := api.blockchain.StateAt(parent.Root()) - if err != nil { - return err - } - if api.chainConfig.IsEnabled(api.chainConfig.GetEthashEIP779Transition, header.Number) { - misc.ApplyDAOHardFork(statedb) - } - gasPool := new(core.GasPool).AddGas(header.GasLimit) - txCount := 0 - var txs []*types.Transaction - var receipts []*types.Receipt - var blockFull = gasPool.Gas() < vars.TxGas - for address := range api.txSenders { - if blockFull { - break - } - m := api.txMap[address] - for nonce := statedb.GetNonce(address); ; nonce++ { - if tx, ok := m[nonce]; ok { - // Try to apply transactions to the state - statedb.Prepare(tx.Hash(), common.Hash{}, txCount) - snap := statedb.Snapshot() - - receipt, err := core.ApplyTransaction( - api.chainConfig, - api.blockchain, - &api.author, - gasPool, - statedb, - header, tx, &header.GasUsed, *api.blockchain.GetVMConfig(), - ) - if err != nil { - statedb.RevertToSnapshot(snap) - break - } - txs = append(txs, tx) - receipts = append(receipts, receipt) - delete(m, nonce) - if len(m) == 0 { - // Last tx for the sender - delete(api.txMap, address) - delete(api.txSenders, address) - } - txCount++ - if gasPool.Gas() < vars.TxGas { - blockFull = true - break - } - } else { - break // Gap in the nonces - } - } - } - block, err := api.engine.FinalizeAndAssemble(api.blockchain, header, statedb, txs, []*types.Header{}, receipts) - if err != nil { - return err - } - return api.importBlock(block) -} - -func (api *RetestethAPI) importBlock(block *types.Block) error { - if _, err := api.blockchain.InsertChain([]*types.Block{block}); err != nil { - return err - } - fmt.Printf("Imported block %d, head is %d\n", block.NumberU64(), api.currentNumber()) - return nil -} - -func (api *RetestethAPI) ModifyTimestamp(ctx context.Context, interval uint64) (bool, error) { - api.blockInterval = interval - return true, nil -} - -func (api *RetestethAPI) ImportRawBlock(ctx context.Context, rawBlock hexutil.Bytes) (common.Hash, error) { - block := new(types.Block) - if err := rlp.DecodeBytes(rawBlock, block); err != nil { - return common.Hash{}, err - } - fmt.Printf("Importing block %d with parent hash: %x, genesisHash: %x\n", block.NumberU64(), block.ParentHash(), api.genesisHash) - if err := api.importBlock(block); err != nil { - return common.Hash{}, err - } - return block.Hash(), nil -} - -func (api *RetestethAPI) RewindToBlock(ctx context.Context, newHead uint64) (bool, error) { - if err := api.blockchain.SetHead(newHead); err != nil { - return false, err - } - // When we rewind, the transaction pool should be cleaned out. - api.txMap = make(map[common.Address]map[uint64]*types.Transaction) - api.txSenders = make(map[common.Address]struct{}) - return true, nil -} - -var emptyListHash common.Hash = common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") - -func (api *RetestethAPI) GetLogHash(ctx context.Context, txHash common.Hash) (common.Hash, error) { - receipt, _, _, _ := rawdb.ReadReceipt(api.ethDb, txHash, api.chainConfig) - if receipt == nil { - return emptyListHash, nil - } else { - if logListRlp, err := rlp.EncodeToBytes(receipt.Logs); err != nil { - return common.Hash{}, err - } else { - return common.BytesToHash(crypto.Keccak256(logListRlp)), nil - } - } -} - -func (api *RetestethAPI) BlockNumber(ctx context.Context) (uint64, error) { - return api.currentNumber(), nil -} - -func (api *RetestethAPI) GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, error) { - block := api.blockchain.GetBlockByNumber(uint64(blockNr)) - if block != nil { - response, err := RPCMarshalBlock(block, true, fullTx) - if err != nil { - return nil, err - } - response["author"] = response["miner"] - response["totalDifficulty"] = (*hexutil.Big)(api.blockchain.GetTd(block.Hash(), uint64(blockNr))) - return response, err - } - return nil, fmt.Errorf("block %d not found", blockNr) -} - -func (api *RetestethAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) { - block := api.blockchain.GetBlockByHash(blockHash) - if block != nil { - response, err := RPCMarshalBlock(block, true, fullTx) - if err != nil { - return nil, err - } - response["author"] = response["miner"] - response["totalDifficulty"] = (*hexutil.Big)(api.blockchain.GetTd(block.Hash(), block.Number().Uint64())) - return response, err - } - return nil, fmt.Errorf("block 0x%x not found", blockHash) -} - -func (api *RetestethAPI) AccountRange(ctx context.Context, - blockHashOrNumber *math.HexOrDecimal256, txIndex uint64, - addressHash *math.HexOrDecimal256, maxResults uint64, -) (AccountRangeResult, error) { - var ( - header *types.Header - block *types.Block - ) - if (*big.Int)(blockHashOrNumber).Cmp(big.NewInt(math.MaxInt64)) > 0 { - blockHash := common.BigToHash((*big.Int)(blockHashOrNumber)) - header = api.blockchain.GetHeaderByHash(blockHash) - block = api.blockchain.GetBlockByHash(blockHash) - //fmt.Printf("Account range: %x, txIndex %d, start: %x, maxResults: %d\n", blockHash, txIndex, common.BigToHash((*big.Int)(addressHash)), maxResults) - } else { - blockNumber := (*big.Int)(blockHashOrNumber).Uint64() - header = api.blockchain.GetHeaderByNumber(blockNumber) - block = api.blockchain.GetBlockByNumber(blockNumber) - //fmt.Printf("Account range: %d, txIndex %d, start: %x, maxResults: %d\n", blockNumber, txIndex, common.BigToHash((*big.Int)(addressHash)), maxResults) - } - parentHeader := api.blockchain.GetHeaderByHash(header.ParentHash) - var root common.Hash - var statedb *state.StateDB - var err error - if parentHeader == nil || int(txIndex) >= len(block.Transactions()) { - root = header.Root - statedb, err = api.blockchain.StateAt(root) - if err != nil { - return AccountRangeResult{}, err - } - } else { - root = parentHeader.Root - statedb, err = api.blockchain.StateAt(root) - if err != nil { - return AccountRangeResult{}, err - } - // Recompute transactions up to the target index. - signer := types.MakeSigner(api.blockchain.Config(), block.Number()) - for idx, tx := range block.Transactions() { - // Assemble the transaction call message and return if the requested offset - msg, _ := tx.AsMessage(signer) - context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil) - // Not yet the searched for transaction, execute on top of the current state - vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{}) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { - return AccountRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) - } - // Ensure any modifications are committed to the state - // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - root = statedb.IntermediateRoot(vmenv.ChainConfig().IsEnabled(vmenv.ChainConfig().GetEIP161dTransition, block.Number())) - if idx == int(txIndex) { - // This is to make sure root can be opened by OpenTrie - root, err = statedb.Commit(api.chainConfig.IsEnabled(api.chainConfig.GetEIP161dTransition, block.Number())) - if err != nil { - return AccountRangeResult{}, err - } - break - } - } - } - accountTrie, err := statedb.Database().OpenTrie(root) - if err != nil { - return AccountRangeResult{}, err - } - it := trie.NewIterator(accountTrie.NodeIterator(common.BigToHash((*big.Int)(addressHash)).Bytes())) - result := AccountRangeResult{AddressMap: make(map[common.Hash]common.Address)} - for i := 0; i < int(maxResults) && it.Next(); i++ { - if preimage := accountTrie.GetKey(it.Key); preimage != nil { - result.AddressMap[common.BytesToHash(it.Key)] = common.BytesToAddress(preimage) - } - } - //fmt.Printf("Number of entries returned: %d\n", len(result.AddressMap)) - // Add the 'next key' so clients can continue downloading. - if it.Next() { - next := common.BytesToHash(it.Key) - result.NextKey = next - } - return result, nil -} - -func (api *RetestethAPI) GetBalance(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (*math.HexOrDecimal256, error) { - //fmt.Printf("GetBalance %x, block %d\n", address, blockNr) - header := api.blockchain.GetHeaderByNumber(uint64(blockNr)) - statedb, err := api.blockchain.StateAt(header.Root) - if err != nil { - return nil, err - } - return (*math.HexOrDecimal256)(statedb.GetBalance(address)), nil -} - -func (api *RetestethAPI) GetCode(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (hexutil.Bytes, error) { - header := api.blockchain.GetHeaderByNumber(uint64(blockNr)) - statedb, err := api.blockchain.StateAt(header.Root) - if err != nil { - return nil, err - } - return statedb.GetCode(address), nil -} - -func (api *RetestethAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (uint64, error) { - header := api.blockchain.GetHeaderByNumber(uint64(blockNr)) - statedb, err := api.blockchain.StateAt(header.Root) - if err != nil { - return 0, err - } - return statedb.GetNonce(address), nil -} - -func (api *RetestethAPI) StorageRangeAt(ctx context.Context, - blockHashOrNumber *math.HexOrDecimal256, txIndex uint64, - address common.Address, - begin *math.HexOrDecimal256, maxResults uint64, -) (StorageRangeResult, error) { - var ( - header *types.Header - block *types.Block - ) - if (*big.Int)(blockHashOrNumber).Cmp(big.NewInt(math.MaxInt64)) > 0 { - blockHash := common.BigToHash((*big.Int)(blockHashOrNumber)) - header = api.blockchain.GetHeaderByHash(blockHash) - block = api.blockchain.GetBlockByHash(blockHash) - //fmt.Printf("Storage range: %x, txIndex %d, addr: %x, start: %x, maxResults: %d\n", - // blockHash, txIndex, address, common.BigToHash((*big.Int)(begin)), maxResults) - } else { - blockNumber := (*big.Int)(blockHashOrNumber).Uint64() - header = api.blockchain.GetHeaderByNumber(blockNumber) - block = api.blockchain.GetBlockByNumber(blockNumber) - //fmt.Printf("Storage range: %d, txIndex %d, addr: %x, start: %x, maxResults: %d\n", - // blockNumber, txIndex, address, common.BigToHash((*big.Int)(begin)), maxResults) - } - parentHeader := api.blockchain.GetHeaderByHash(header.ParentHash) - var root common.Hash - var statedb *state.StateDB - var err error - if parentHeader == nil || int(txIndex) >= len(block.Transactions()) { - root = header.Root - statedb, err = api.blockchain.StateAt(root) - if err != nil { - return StorageRangeResult{}, err - } - } else { - root = parentHeader.Root - statedb, err = api.blockchain.StateAt(root) - if err != nil { - return StorageRangeResult{}, err - } - // Recompute transactions up to the target index. - signer := types.MakeSigner(api.blockchain.Config(), block.Number()) - for idx, tx := range block.Transactions() { - // Assemble the transaction call message and return if the requested offset - msg, _ := tx.AsMessage(signer) - context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil) - // Not yet the searched for transaction, execute on top of the current state - vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{}) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { - return StorageRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) - } - // Ensure any modifications are committed to the state - // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - _ = statedb.IntermediateRoot(vmenv.ChainConfig().IsEnabled(vmenv.ChainConfig().GetEIP161dTransition, block.Number())) - if idx == int(txIndex) { - // This is to make sure root can be opened by OpenTrie - _, err = statedb.Commit(vmenv.ChainConfig().IsEnabled(vmenv.ChainConfig().GetEIP161dTransition, block.Number())) - if err != nil { - return StorageRangeResult{}, err - } - } - } - } - storageTrie := statedb.StorageTrie(address) - it := trie.NewIterator(storageTrie.NodeIterator(common.BigToHash((*big.Int)(begin)).Bytes())) - result := StorageRangeResult{Storage: make(map[common.Hash]SRItem)} - for i := 0; /*i < int(maxResults) && */ it.Next(); i++ { - if preimage := storageTrie.GetKey(it.Key); preimage != nil { - key := (*math.HexOrDecimal256)(big.NewInt(0).SetBytes(preimage)) - v, _, err := rlp.SplitString(it.Value) - if err != nil { - return StorageRangeResult{}, err - } - value := (*math.HexOrDecimal256)(big.NewInt(0).SetBytes(v)) - ks, _ := key.MarshalText() - vs, _ := value.MarshalText() - if len(ks)%2 != 0 { - ks = append(append(append([]byte{}, ks[:2]...), byte('0')), ks[2:]...) - } - if len(vs)%2 != 0 { - vs = append(append(append([]byte{}, vs[:2]...), byte('0')), vs[2:]...) - } - result.Storage[common.BytesToHash(it.Key)] = SRItem{ - Key: string(ks), - Value: string(vs), - } - //fmt.Printf("Key: %s, Value: %s\n", ks, vs) - } else { - //fmt.Printf("Did not find preimage for %x\n", it.Key) - } - } - if it.Next() { - result.Complete = false - } else { - result.Complete = true - } - return result, nil -} - -func (api *RetestethAPI) ClientVersion(ctx context.Context) (string, error) { - return "Geth-" + params.VersionWithCommit(gitCommit, gitDate), nil -} - -func retesteth(ctx *cli.Context) error { - log.Info("Welcome to retesteth!") - // register signer API with server - var ( - extapiURL string - ) - apiImpl := &RetestethAPI{} - var testApi RetestethTestAPI = apiImpl - var ethApi RetestethEthAPI = apiImpl - var debugApi RetestethDebugAPI = apiImpl - var web3Api RetestWeb3API = apiImpl - rpcAPI := []rpc.API{ - { - Namespace: "test", - Public: true, - Service: testApi, - Version: "1.0", - }, - { - Namespace: "eth", - Public: true, - Service: ethApi, - Version: "1.0", - }, - { - Namespace: "debug", - Public: true, - Service: debugApi, - Version: "1.0", - }, - { - Namespace: "web3", - Public: true, - Service: web3Api, - Version: "1.0", - }, - } - vhosts := utils.SplitAndTrim(ctx.GlobalString(utils.HTTPVirtualHostsFlag.Name)) - cors := utils.SplitAndTrim(ctx.GlobalString(utils.HTTPCORSDomainFlag.Name)) - - // register apis and create handler stack - srv := rpc.NewServer() - err := node.RegisterApisFromWhitelist(rpcAPI, []string{"test", "eth", "debug", "web3"}, srv, false) - if err != nil { - utils.Fatalf("Could not register RPC apis: %w", err) - } - handler := node.NewHTTPHandlerStack(srv, cors, vhosts) - - // start http server - var RetestethHTTPTimeouts = rpc.HTTPTimeouts{ - ReadTimeout: 120 * time.Second, - WriteTimeout: 120 * time.Second, - IdleTimeout: 120 * time.Second, - } - httpEndpoint := fmt.Sprintf("%s:%d", ctx.GlobalString(utils.HTTPListenAddrFlag.Name), ctx.Int(rpcPortFlag.Name)) - httpServer, _, err := node.StartHTTPEndpoint(httpEndpoint, RetestethHTTPTimeouts, handler) - if err != nil { - utils.Fatalf("Could not start RPC api: %v", err) - } - extapiURL = fmt.Sprintf("http://%s", httpEndpoint) - log.Info("HTTP endpoint opened", "url", extapiURL) - - defer func() { - // Don't bother imposing a timeout here. - httpServer.Shutdown(context.Background()) - log.Info("HTTP endpoint closed", "url", httpEndpoint) - }() - - abortChan := make(chan os.Signal, 11) - signal.Notify(abortChan, os.Interrupt) - - sig := <-abortChan - log.Info("Exiting...", "signal", sig) - return nil -} diff --git a/cmd/geth/retesteth_copypaste.go b/cmd/geth/retesteth_copypaste.go deleted file mode 100644 index e2795af7f9..0000000000 --- a/cmd/geth/retesteth_copypaste.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2019 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 . - -package main - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" -) - -// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction -type RPCTransaction struct { - BlockHash common.Hash `json:"blockHash"` - BlockNumber *hexutil.Big `json:"blockNumber"` - From common.Address `json:"from"` - Gas hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - Hash common.Hash `json:"hash"` - Input hexutil.Bytes `json:"input"` - Nonce hexutil.Uint64 `json:"nonce"` - To *common.Address `json:"to"` - TransactionIndex hexutil.Uint `json:"transactionIndex"` - Value *hexutil.Big `json:"value"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` -} - -// newRPCTransaction returns a transaction that will serialize to the RPC -// representation, with the given location metadata set (if available). -func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransaction { - var signer types.Signer = types.FrontierSigner{} - if tx.Protected() { - signer = types.NewEIP155Signer(tx.ChainId()) - } - from, _ := types.Sender(signer, tx) - v, r, s := tx.RawSignatureValues() - - result := &RPCTransaction{ - From: from, - Gas: hexutil.Uint64(tx.Gas()), - GasPrice: (*hexutil.Big)(tx.GasPrice()), - Hash: tx.Hash(), - Input: hexutil.Bytes(tx.Data()), - Nonce: hexutil.Uint64(tx.Nonce()), - To: tx.To(), - Value: (*hexutil.Big)(tx.Value()), - V: (*hexutil.Big)(v), - R: (*hexutil.Big)(r), - S: (*hexutil.Big)(s), - } - if blockHash != (common.Hash{}) { - result.BlockHash = blockHash - result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) - result.TransactionIndex = hexutil.Uint(index) - } - return result -} - -// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation. -func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction { - txs := b.Transactions() - if index >= uint64(len(txs)) { - return nil - } - return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index) -} - -// newRPCTransactionFromBlockHash returns a transaction that will serialize to the RPC representation. -func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransaction { - for idx, tx := range b.Transactions() { - if tx.Hash() == hash { - return newRPCTransactionFromBlockIndex(b, uint64(idx)) - } - } - return nil -} - -// RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are -// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain -// transaction hashes. -func RPCMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { - head := b.Header() // copies the header once - fields := map[string]interface{}{ - "number": (*hexutil.Big)(head.Number), - "hash": b.Hash(), - "parentHash": head.ParentHash, - "nonce": head.Nonce, - "mixHash": head.MixDigest, - "sha3Uncles": head.UncleHash, - "logsBloom": head.Bloom, - "stateRoot": head.Root, - "miner": head.Coinbase, - "difficulty": (*hexutil.Big)(head.Difficulty), - "extraData": hexutil.Bytes(head.Extra), - "size": hexutil.Uint64(b.Size()), - "gasLimit": hexutil.Uint64(head.GasLimit), - "gasUsed": hexutil.Uint64(head.GasUsed), - "timestamp": hexutil.Uint64(head.Time), - "transactionsRoot": head.TxHash, - "receiptsRoot": head.ReceiptHash, - } - - if inclTx { - formatTx := func(tx *types.Transaction) (interface{}, error) { - return tx.Hash(), nil - } - if fullTx { - formatTx = func(tx *types.Transaction) (interface{}, error) { - return newRPCTransactionFromBlockHash(b, tx.Hash()), nil - } - } - txs := b.Transactions() - transactions := make([]interface{}, len(txs)) - var err error - for i, tx := range txs { - if transactions[i], err = formatTx(tx); err != nil { - return nil, err - } - } - fields["transactions"] = transactions - } - - uncles := b.Uncles() - uncleHashes := make([]common.Hash, len(uncles)) - for i, uncle := range uncles { - uncleHashes[i] = uncle.Hash() - } - fields["uncles"] = uncleHashes - - return fields, nil -} diff --git a/cmd/geth/run_test.go b/cmd/geth/run_test.go index f7b735b84c..527c38a657 100644 --- a/cmd/geth/run_test.go +++ b/cmd/geth/run_test.go @@ -70,12 +70,12 @@ func runGeth(t *testing.T, args ...string) *testgeth { tt := &testgeth{} tt.TestCmd = cmdtest.NewTestCmd(t, tt) for i, arg := range args { - switch { - case arg == "-datadir" || arg == "--datadir": + switch arg { + case "--datadir": if i < len(args)-1 { tt.Datadir = args[i+1] } - case arg == "-etherbase" || arg == "--etherbase": + case "--miner.etherbase": if i < len(args)-1 { tt.Etherbase = args[i+1] } @@ -84,7 +84,7 @@ func runGeth(t *testing.T, args ...string) *testgeth { if tt.Datadir == "" { tt.Datadir = tmpdir(t) tt.Cleanup = func() { os.RemoveAll(tt.Datadir) } - args = append([]string{"-datadir", tt.Datadir}, args...) + args = append([]string{"--datadir", tt.Datadir}, args...) // Remove the temporary datadir if something fails below. defer func() { if t.Failed() { diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go new file mode 100644 index 0000000000..e8f6a35438 --- /dev/null +++ b/cmd/geth/snapshot.go @@ -0,0 +1,432 @@ +// Copyright 2020 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 . + +package main + +import ( + "bytes" + "errors" + "time" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/state/pruner" + "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" + cli "gopkg.in/urfave/cli.v1" +) + +var ( + // emptyRoot is the known root hash of an empty trie. + emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + + // emptyCode is the known hash of the empty EVM bytecode. + emptyCode = crypto.Keccak256(nil) +) + +var ( + snapshotCommand = cli.Command{ + Name: "snapshot", + Usage: "A set of commands based on the snapshot", + Category: "MISCELLANEOUS COMMANDS", + Description: "", + Subcommands: []cli.Command{ + { + Name: "prune-state", + Usage: "Prune stale ethereum state data based on the snapshot", + ArgsUsage: "", + Action: utils.MigrateFlags(pruneState), + Category: "MISCELLANEOUS COMMANDS", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.AncientFlag, + utils.RopstenFlag, + utils.RinkebyFlag, + utils.GoerliFlag, + utils.CacheTrieJournalFlag, + utils.BloomFilterSizeFlag, + }, + Description: ` +geth snapshot prune-state +will prune historical state data with the help of the state snapshot. +All trie nodes and contract codes that do not belong to the specified +version state will be deleted from the database. After pruning, only +two version states are available: genesis and the specific one. + +The default pruning target is the HEAD-127 state. + +WARNING: It's necessary to delete the trie clean cache after the pruning. +If you specify another directory for the trie clean cache via "--cache.trie.journal" +during the use of Geth, please also specify it here for correct deletion. Otherwise +the trie clean cache with default directory will be deleted. +`, + }, + { + Name: "verify-state", + Usage: "Recalculate state hash based on the snapshot for verification", + ArgsUsage: "", + Action: utils.MigrateFlags(verifyState), + Category: "MISCELLANEOUS COMMANDS", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.AncientFlag, + utils.RopstenFlag, + utils.RinkebyFlag, + utils.GoerliFlag, + }, + Description: ` +geth snapshot verify-state +will traverse the whole accounts and storages set based on the specified +snapshot and recalculate the root hash of state for verification. +In other words, this command does the snapshot to trie conversion. +`, + }, + { + Name: "traverse-state", + Usage: "Traverse the state with given root hash for verification", + ArgsUsage: "", + Action: utils.MigrateFlags(traverseState), + Category: "MISCELLANEOUS COMMANDS", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.AncientFlag, + utils.RopstenFlag, + utils.RinkebyFlag, + utils.GoerliFlag, + }, + Description: ` +geth snapshot traverse-state +will traverse the whole state from the given state root and will abort if any +referenced trie node or contract code is missing. This command can be used for +state integrity verification. The default checking target is the HEAD state. + +It's also usable without snapshot enabled. +`, + }, + { + Name: "traverse-rawstate", + Usage: "Traverse the state with given root hash for verification", + ArgsUsage: "", + Action: utils.MigrateFlags(traverseRawState), + Category: "MISCELLANEOUS COMMANDS", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.AncientFlag, + utils.RopstenFlag, + utils.RinkebyFlag, + utils.GoerliFlag, + }, + Description: ` +geth snapshot traverse-rawstate +will traverse the whole state from the given root and will abort if any referenced +trie node or contract code is missing. This command can be used for state integrity +verification. The default checking target is the HEAD state. It's basically identical +to traverse-state, but the check granularity is smaller. + +It's also usable without snapshot enabled. +`, + }, + }, + } +) + +func pruneState(ctx *cli.Context) error { + stack, config := makeConfigNode(ctx) + defer stack.Close() + + chaindb := utils.MakeChainDatabase(ctx, stack, false) + pruner, err := pruner.NewPruner(chaindb, stack.ResolvePath(""), stack.ResolvePath(config.Eth.TrieCleanCacheJournal), ctx.GlobalUint64(utils.BloomFilterSizeFlag.Name)) + if err != nil { + log.Error("Failed to open snapshot tree", "error", err) + return err + } + if ctx.NArg() > 1 { + log.Error("Too many arguments given") + return errors.New("too many arguments") + } + var targetRoot common.Hash + if ctx.NArg() == 1 { + targetRoot, err = parseRoot(ctx.Args()[0]) + if err != nil { + log.Error("Failed to resolve state root", "error", err) + return err + } + } + if err = pruner.Prune(targetRoot); err != nil { + log.Error("Failed to prune state", "error", err) + return err + } + return nil +} + +func verifyState(ctx *cli.Context) error { + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + chaindb := utils.MakeChainDatabase(ctx, stack, true) + headBlock := rawdb.ReadHeadBlock(chaindb) + if headBlock == nil { + log.Error("Failed to load head block") + return errors.New("no head block") + } + snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, headBlock.Root(), false, false, false) + if err != nil { + log.Error("Failed to open snapshot tree", "error", err) + return err + } + if ctx.NArg() > 1 { + log.Error("Too many arguments given") + return errors.New("too many arguments") + } + var root = headBlock.Root() + if ctx.NArg() == 1 { + root, err = parseRoot(ctx.Args()[0]) + if err != nil { + log.Error("Failed to resolve state root", "error", err) + return err + } + } + if err := snaptree.Verify(root); err != nil { + log.Error("Failed to verfiy state", "error", err) + return err + } + log.Info("Verified the state") + return nil +} + +// traverseState is a helper function used for pruning verification. +// Basically it just iterates the trie, ensure all nodes and associated +// contract codes are present. +func traverseState(ctx *cli.Context) error { + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + chaindb := utils.MakeChainDatabase(ctx, stack, true) + headBlock := rawdb.ReadHeadBlock(chaindb) + if headBlock == nil { + log.Error("Failed to load head block") + return errors.New("no head block") + } + if ctx.NArg() > 1 { + log.Error("Too many arguments given") + return errors.New("too many arguments") + } + var ( + root common.Hash + err error + ) + if ctx.NArg() == 1 { + root, err = parseRoot(ctx.Args()[0]) + if err != nil { + log.Error("Failed to resolve state root", "error", err) + return err + } + log.Info("Start traversing the state", "root", root) + } else { + root = headBlock.Root() + log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64()) + } + triedb := trie.NewDatabase(chaindb) + t, err := trie.NewSecure(root, triedb) + if err != nil { + log.Error("Failed to open trie", "root", root, "error", err) + return err + } + var ( + accounts int + slots int + codes int + lastReport time.Time + start = time.Now() + ) + accIter := trie.NewIterator(t.NodeIterator(nil)) + for accIter.Next() { + accounts += 1 + var acc state.Account + if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil { + log.Error("Invalid account encountered during traversal", "error", err) + return err + } + if acc.Root != emptyRoot { + storageTrie, err := trie.NewSecure(acc.Root, triedb) + if err != nil { + log.Error("Failed to open storage trie", "root", acc.Root, "error", err) + return err + } + storageIter := trie.NewIterator(storageTrie.NodeIterator(nil)) + for storageIter.Next() { + slots += 1 + } + if storageIter.Err != nil { + log.Error("Failed to traverse storage trie", "root", acc.Root, "error", storageIter.Err) + return storageIter.Err + } + } + if !bytes.Equal(acc.CodeHash, emptyCode) { + code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash)) + if len(code) == 0 { + log.Error("Code is missing", "hash", common.BytesToHash(acc.CodeHash)) + return errors.New("missing code") + } + codes += 1 + } + if time.Since(lastReport) > time.Second*8 { + log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) + lastReport = time.Now() + } + } + if accIter.Err != nil { + log.Error("Failed to traverse state trie", "root", root, "error", accIter.Err) + return accIter.Err + } + log.Info("State is complete", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) + return nil +} + +// traverseRawState is a helper function used for pruning verification. +// Basically it just iterates the trie, ensure all nodes and associated +// contract codes are present. It's basically identical to traverseState +// but it will check each trie node. +func traverseRawState(ctx *cli.Context) error { + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + chaindb := utils.MakeChainDatabase(ctx, stack, true) + headBlock := rawdb.ReadHeadBlock(chaindb) + if headBlock == nil { + log.Error("Failed to load head block") + return errors.New("no head block") + } + if ctx.NArg() > 1 { + log.Error("Too many arguments given") + return errors.New("too many arguments") + } + var ( + root common.Hash + err error + ) + if ctx.NArg() == 1 { + root, err = parseRoot(ctx.Args()[0]) + if err != nil { + log.Error("Failed to resolve state root", "error", err) + return err + } + log.Info("Start traversing the state", "root", root) + } else { + root = headBlock.Root() + log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64()) + } + triedb := trie.NewDatabase(chaindb) + t, err := trie.NewSecure(root, triedb) + if err != nil { + log.Error("Failed to open trie", "root", root, "error", err) + return err + } + var ( + nodes int + accounts int + slots int + codes int + lastReport time.Time + start = time.Now() + ) + accIter := t.NodeIterator(nil) + for accIter.Next(true) { + nodes += 1 + node := accIter.Hash() + + if node != (common.Hash{}) { + // Check the present for non-empty hash node(embedded node doesn't + // have their own hash). + blob := rawdb.ReadTrieNode(chaindb, node) + if len(blob) == 0 { + log.Error("Missing trie node(account)", "hash", node) + return errors.New("missing account") + } + } + // If it's a leaf node, yes we are touching an account, + // dig into the storage trie further. + if accIter.Leaf() { + accounts += 1 + var acc state.Account + if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil { + log.Error("Invalid account encountered during traversal", "error", err) + return errors.New("invalid account") + } + if acc.Root != emptyRoot { + storageTrie, err := trie.NewSecure(acc.Root, triedb) + if err != nil { + log.Error("Failed to open storage trie", "root", acc.Root, "error", err) + return errors.New("missing storage trie") + } + storageIter := storageTrie.NodeIterator(nil) + for storageIter.Next(true) { + nodes += 1 + node := storageIter.Hash() + + // Check the present for non-empty hash node(embedded node doesn't + // have their own hash). + if node != (common.Hash{}) { + blob := rawdb.ReadTrieNode(chaindb, node) + if len(blob) == 0 { + log.Error("Missing trie node(storage)", "hash", node) + return errors.New("missing storage") + } + } + // Bump the counter if it's leaf node. + if storageIter.Leaf() { + slots += 1 + } + } + if storageIter.Error() != nil { + log.Error("Failed to traverse storage trie", "root", acc.Root, "error", storageIter.Error()) + return storageIter.Error() + } + } + if !bytes.Equal(acc.CodeHash, emptyCode) { + code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash)) + if len(code) == 0 { + log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey())) + return errors.New("missing code") + } + codes += 1 + } + if time.Since(lastReport) > time.Second*8 { + log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) + lastReport = time.Now() + } + } + } + if accIter.Error() != nil { + log.Error("Failed to traverse state trie", "root", root, "error", accIter.Error()) + return accIter.Error() + } + log.Info("State is complete", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) + return nil +} + +func parseRoot(input string) (common.Hash, error) { + var h common.Hash + if err := h.UnmarshalText([]byte(input)); err != nil { + return h, err + } + return h, nil +} diff --git a/cmd/geth/testdata/vcheck/data.json b/cmd/geth/testdata/vcheck/data.json new file mode 100644 index 0000000000..e7ee2bf7e4 --- /dev/null +++ b/cmd/geth/testdata/vcheck/data.json @@ -0,0 +1,61 @@ +[ + { + "name": "CorruptedDAG", + "uid": "GETH-2020-01", + "summary": "Mining nodes will generate erroneous PoW on epochs > `385`.", + "description": "A mining flaw could cause miners to erroneously calculate PoW, due to an index overflow, if DAG size is exceeding the maximum 32 bit unsigned value.\n\nThis occurred on the ETC chain on 2020-11-06. This is likely to trigger for ETH mainnet around block `11550000`/epoch `385`, slated to occur early January 2021.\n\nThis issue is relevant only for miners, non-mining nodes are unaffected, since non-mining nodes use a smaller verification cache instead of a full DAG.", + "links": [ + "https://github.com/ethereum/go-ethereum/pull/21793", + "https://blog.ethereum.org/2020/11/12/geth_security_release/", + "https://github.com/ethereum/go-ethereum/commit/567d41d9363706b4b13ce0903804e8acf214af49" + ], + "introduced": "v1.6.0", + "fixed": "v1.9.24", + "published": "2020-11-12", + "severity": "Medium", + "check": "Geth\\/v1\\.(6|7|8)\\..*|Geth\\/v1\\.9\\.2(1|2|3)-.*" + }, + { + "name": "GoCrash", + "uid": "GETH-2020-02", + "summary": "A denial-of-service issue can be used to crash Geth nodes during block processing, due to an underlying bug in Go (CVE-2020-28362) versions < `1.15.5`, or `<1.14.12`", + "description": "The DoS issue can be used to crash all Geth nodes during block processing, the effects of which would be that a major part of the Ethereum network went offline.\n\nOutside of Go-Ethereum, the issue is most likely relevant for all forks of Geth (such as TurboGeth or ETC’s core-geth) which is built with versions of Go which contains the vulnerability.", + "links": [ + "https://blog.ethereum.org/2020/11/12/geth_security_release/", + "https://groups.google.com/g/golang-announce/c/NpBGTTmKzpM", + "https://github.com/golang/go/issues/42552" + ], + "fixed": "v1.9.24", + "published": "2020-11-12", + "severity": "Critical", + "check": "Geth.*\\/go1\\.(11(.*)|12(.*)|13(.*)|14|14\\.(\\d|10|11|)|15|15\\.[0-4])$" + }, + { + "name": "ShallowCopy", + "uid": "GETH-2020-03", + "summary": "A consensus flaw in Geth, related to `datacopy` precompile", + "description": "Geth erroneously performed a 'shallow' copy when the precompiled `datacopy` (at `0x00...04`) was invoked. An attacker could deploy a contract that uses the shallow copy to corrupt the contents of the `RETURNDATA`, thus causing a consensus failure.", + "links": [ + "https://blog.ethereum.org/2020/11/12/geth_security_release/" + ], + "introduced": "v1.9.7", + "fixed": "v1.9.17", + "published": "2020-11-12", + "severity": "Critical", + "check": "Geth\\/v1\\.9\\.(7|8|9|10|11|12|13|14|15|16).*$" + }, + { + "name": "GethCrash", + "uid": "GETH-2020-04", + "summary": "A denial-of-service issue can be used to crash Geth nodes during block processing", + "description": "Full details to be disclosed at a later date", + "links": [ + "https://blog.ethereum.org/2020/11/12/geth_security_release/" + ], + "introduced": "v1.9.16", + "fixed": "v1.9.18", + "published": "2020-11-12", + "severity": "Critical", + "check": "Geth\\/v1\\.9.(16|17).*$" + } +] diff --git a/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.1 b/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.1 new file mode 100644 index 0000000000..f9066d4fe0 --- /dev/null +++ b/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.1 @@ -0,0 +1,4 @@ +untrusted comment: signature from minisign secret key +RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0= +trusted comment: timestamp:1605618622 file:vulnerabilities.json +osAPs4QPdDkmiWQxqeMIzYv/b+ZGxJ+19Sbrk1Cpq4t2gHBT+lqFtwL3OCzKWWyjGRTmHfsVGBYpzEdPRQ0/BQ== diff --git a/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.2 b/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.2 new file mode 100644 index 0000000000..a89a83d21a --- /dev/null +++ b/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.2 @@ -0,0 +1,4 @@ +untrusted comment: Here's a comment +RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0= +trusted comment: Here's a trusted comment +3CnkIuz9MEDa7uNyGZAbKZhuirwfiqm7E1uQHrd2SiO4Y8+Akw9vs052AyKw0s5nhbYHCZE2IMQdHNjKwxEGAQ== diff --git a/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.3 b/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.3 new file mode 100644 index 0000000000..6fd33b19a3 --- /dev/null +++ b/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.3 @@ -0,0 +1,4 @@ +untrusted comment: One more (untrusted) comment +RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0= +trusted comment: Here's a trusted comment +3CnkIuz9MEDa7uNyGZAbKZhuirwfiqm7E1uQHrd2SiO4Y8+Akw9vs052AyKw0s5nhbYHCZE2IMQdHNjKwxEGAQ== diff --git a/cmd/geth/testdata/vcheck/minisign.pub b/cmd/geth/testdata/vcheck/minisign.pub new file mode 100644 index 0000000000..183dce5f6b --- /dev/null +++ b/cmd/geth/testdata/vcheck/minisign.pub @@ -0,0 +1,2 @@ +untrusted comment: minisign public key 284E00B52C269624 +RWQkliYstQBOKOdtClfgC3IypIPX6TAmoEi7beZ4gyR3wsaezvqOMWsp diff --git a/cmd/geth/testdata/vcheck/minisign.sec b/cmd/geth/testdata/vcheck/minisign.sec new file mode 100644 index 0000000000..5c50715b20 --- /dev/null +++ b/cmd/geth/testdata/vcheck/minisign.sec @@ -0,0 +1,2 @@ +untrusted comment: minisign encrypted secret key +RWRTY0Iyz8kmPMKrqk6DCtlO9a33akKiaOQG1aLolqDxs52qvPoAAAACAAAAAAAAAEAAAAAArEiggdvyn6+WzTprirLtgiYQoU+ihz/HyGgjhuF+Pz2ddMduyCO+xjCHeq+vgVVW039fbsI8hW6LRGJZLBKV5/jdxCXAVVQE7qTQ6xpEdO0z8Z731/pV1hlspQXG2PNd16NMtwd9dWw= diff --git a/cmd/geth/testdata/vcheck/signify-sigs/data.json.sig b/cmd/geth/testdata/vcheck/signify-sigs/data.json.sig new file mode 100644 index 0000000000..3d5fcacf9a --- /dev/null +++ b/cmd/geth/testdata/vcheck/signify-sigs/data.json.sig @@ -0,0 +1,2 @@ +untrusted comment: verify with ./signifykey.pub +RWSKLNhZb0KdAbhRUhW2LQZXdnwttu2SYhM9EuC4mMgOJB85h7/YIPupf8/ldTs4N8e9Y/fhgdY40q5LQpt5IFC62fq0v8U1/w8= diff --git a/cmd/geth/testdata/vcheck/signifykey.pub b/cmd/geth/testdata/vcheck/signifykey.pub new file mode 100644 index 0000000000..328f973ab4 --- /dev/null +++ b/cmd/geth/testdata/vcheck/signifykey.pub @@ -0,0 +1,2 @@ +untrusted comment: signify public key +RWSKLNhZb0KdATtRT7mZC/bybI3t3+Hv/O2i3ye04Dq9fnT9slpZ1a2/ diff --git a/cmd/geth/testdata/vcheck/signifykey.sec b/cmd/geth/testdata/vcheck/signifykey.sec new file mode 100644 index 0000000000..3279a2e58b --- /dev/null +++ b/cmd/geth/testdata/vcheck/signifykey.sec @@ -0,0 +1,2 @@ +untrusted comment: signify secret key +RWRCSwAAACpLQDLawSQCtI7eAVIvaiHzjTsTyJsfV5aKLNhZb0KdAWeICXJGa93/bHAcsY6jUh9I8RdEcDWEoGxmaXZC+IdVBPxDpkix9fBRGEUdKWHi3dOfqME0YRzErWI5AVg3cRw= diff --git a/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.1 b/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.1 new file mode 100644 index 0000000000..f9066d4fe0 --- /dev/null +++ b/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.1 @@ -0,0 +1,4 @@ +untrusted comment: signature from minisign secret key +RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0= +trusted comment: timestamp:1605618622 file:vulnerabilities.json +osAPs4QPdDkmiWQxqeMIzYv/b+ZGxJ+19Sbrk1Cpq4t2gHBT+lqFtwL3OCzKWWyjGRTmHfsVGBYpzEdPRQ0/BQ== diff --git a/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.2 b/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.2 new file mode 100644 index 0000000000..a89a83d21a --- /dev/null +++ b/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.2 @@ -0,0 +1,4 @@ +untrusted comment: Here's a comment +RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0= +trusted comment: Here's a trusted comment +3CnkIuz9MEDa7uNyGZAbKZhuirwfiqm7E1uQHrd2SiO4Y8+Akw9vs052AyKw0s5nhbYHCZE2IMQdHNjKwxEGAQ== diff --git a/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.3 b/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.3 new file mode 100644 index 0000000000..6fd33b19a3 --- /dev/null +++ b/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.3 @@ -0,0 +1,4 @@ +untrusted comment: One more (untrusted) comment +RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0= +trusted comment: Here's a trusted comment +3CnkIuz9MEDa7uNyGZAbKZhuirwfiqm7E1uQHrd2SiO4Y8+Akw9vs052AyKw0s5nhbYHCZE2IMQdHNjKwxEGAQ== diff --git a/cmd/geth/testdata/vcheck/vulnerabilities.json b/cmd/geth/testdata/vcheck/vulnerabilities.json new file mode 100644 index 0000000000..36509f95a9 --- /dev/null +++ b/cmd/geth/testdata/vcheck/vulnerabilities.json @@ -0,0 +1,70 @@ +[ + { + "name": "CorruptedDAG", + "uid": "GETH-2020-01", + "summary": "Mining nodes will generate erroneous PoW on epochs > `385`.", + "description": "A mining flaw could cause miners to erroneously calculate PoW, due to an index overflow, if DAG size is exceeding the maximum 32 bit unsigned value.\n\nThis occurred on the ETC chain on 2020-11-06. This is likely to trigger for ETH mainnet around block `11550000`/epoch `385`, slated to occur early January 2021.\n\nThis issue is relevant only for miners, non-mining nodes are unaffected, since non-mining nodes use a smaller verification cache instead of a full DAG.", + "links": [ + "https://github.com/ethereum/go-ethereum/pull/21793", + "https://blog.ethereum.org/2020/11/12/geth_security_release/", + "https://github.com/ethereum/go-ethereum/commit/567d41d9363706b4b13ce0903804e8acf214af49", + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-v592-xf75-856p" + ], + "introduced": "v1.6.0", + "fixed": "v1.9.24", + "published": "2020-11-12", + "severity": "Medium", + "CVE": "CVE-2020-26240", + "check": "Geth\\/v1\\.(6|7|8)\\..*|Geth\\/v1\\.9\\.\\d-.*|Geth\\/v1\\.9\\.1.*|Geth\\/v1\\.9\\.2(0|1|2|3)-.*" + }, + { + "name": "Denial of service due to Go CVE-2020-28362", + "uid": "GETH-2020-02", + "summary": "A denial-of-service issue can be used to crash Geth nodes during block processing, due to an underlying bug in Go (CVE-2020-28362) versions < `1.15.5`, or `<1.14.12`", + "description": "The DoS issue can be used to crash all Geth nodes during block processing, the effects of which would be that a major part of the Ethereum network went offline.\n\nOutside of Go-Ethereum, the issue is most likely relevant for all forks of Geth (such as TurboGeth or ETC’s core-geth) which is built with versions of Go which contains the vulnerability.", + "links": [ + "https://blog.ethereum.org/2020/11/12/geth_security_release/", + "https://groups.google.com/g/golang-announce/c/NpBGTTmKzpM", + "https://github.com/golang/go/issues/42552", + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-m6gx-rhvj-fh52" + ], + "introduced": "v0.0.0", + "fixed": "v1.9.24", + "published": "2020-11-12", + "severity": "Critical", + "CVE": "CVE-2020-28362", + "check": "Geth.*\\/go1\\.(11(.*)|12(.*)|13(.*)|14|14\\.(\\d|10|11|)|15|15\\.[0-4])$" + }, + { + "name": "ShallowCopy", + "uid": "GETH-2020-03", + "summary": "A consensus flaw in Geth, related to `datacopy` precompile", + "description": "Geth erroneously performed a 'shallow' copy when the precompiled `datacopy` (at `0x00...04`) was invoked. An attacker could deploy a contract that uses the shallow copy to corrupt the contents of the `RETURNDATA`, thus causing a consensus failure.", + "links": [ + "https://blog.ethereum.org/2020/11/12/geth_security_release/", + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-69v6-xc2j-r2jf" + ], + "introduced": "v1.9.7", + "fixed": "v1.9.17", + "published": "2020-11-12", + "severity": "Critical", + "CVE": "CVE-2020-26241", + "check": "Geth\\/v1\\.9\\.(7|8|9|10|11|12|13|14|15|16).*$" + }, + { + "name": "GethCrash", + "uid": "GETH-2020-04", + "summary": "A denial-of-service issue can be used to crash Geth nodes during block processing", + "description": "Full details to be disclosed at a later date", + "links": [ + "https://blog.ethereum.org/2020/11/12/geth_security_release/", + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-jm5c-rv3w-w83m" + ], + "introduced": "v1.9.16", + "fixed": "v1.9.18", + "published": "2020-11-12", + "severity": "Critical", + "CVE": "CVE-2020-26242", + "check": "Geth\\/v1\\.9.(16|17).*$" + } +] diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 5d168cab14..55bfd259de 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -25,7 +25,7 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/flags" - cli "gopkg.in/urfave/cli.v1" + "gopkg.in/urfave/cli.v1" ) // AppHelpFlagGroups is the application flags, grouped by functionality. @@ -37,8 +37,9 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.DataDirFlag, utils.AncientFlag, utils.AncientRPCFlag, + utils.MinFreeDiskSpaceFlag, utils.KeyStoreDirFlag, - utils.NoUSBFlag, + utils.USBFlag, utils.SmartCardDaemonPathFlag, utils.NetworkIdFlag, utils.MainnetFlag, @@ -47,8 +48,8 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.KottiFlag, utils.GoerliFlag, utils.RinkebyFlag, + utils.YoloV3Flag, utils.RopstenFlag, - utils.YoloV2Flag, utils.SyncModeFlag, utils.ExitWhenSyncedFlag, utils.GCModeFlag, @@ -72,6 +73,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.UltraLightFractionFlag, utils.UltraLightOnlyAnnounceFlag, utils.LightNoPruneFlag, + utils.LightNoSyncServeFlag, }, }, { @@ -122,6 +124,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.CacheGCFlag, utils.CacheSnapshotFlag, utils.CacheNoPrefetchFlag, + utils.CachePreimagesFlag, }, }, { @@ -142,18 +145,21 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.HTTPListenAddrFlag, utils.HTTPPortFlag, utils.HTTPApiFlag, + utils.HTTPPathPrefixFlag, utils.HTTPCORSDomainFlag, utils.HTTPVirtualHostsFlag, utils.WSEnabledFlag, utils.WSListenAddrFlag, utils.WSPortFlag, utils.WSApiFlag, + utils.WSPathPrefixFlag, utils.WSAllowedOriginsFlag, utils.GraphQLEnabledFlag, utils.GraphQLCORSDomainFlag, utils.GraphQLVirtualHostsFlag, utils.RPCGlobalGasCapFlag, utils.RPCGlobalTxFeeCapFlag, + utils.AllowUnprotectedTxs, utils.JSpathFlag, utils.ExecFlag, utils.PreloadJSFlag, @@ -163,8 +169,6 @@ var AppHelpFlagGroups = []flags.FlagGroup{ Name: "NETWORKING", Flags: []cli.Flag{ utils.BootnodesFlag, - utils.LegacyBootnodesV4Flag, - utils.LegacyBootnodesV5Flag, utils.DNSDiscoveryFlag, utils.EthProtocolsFlag, utils.ListenPortFlag, @@ -184,6 +188,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.MiningEnabledFlag, utils.MinerThreadsFlag, utils.MinerNotifyFlag, + utils.MinerNotifyFullFlag, utils.MinerGasPriceFlag, utils.MinerGasTargetFlag, utils.MinerGasLimitFlag, @@ -221,33 +226,23 @@ var AppHelpFlagGroups = []flags.FlagGroup{ Name: "METRICS AND STATS", Flags: metricsFlags, }, - { - Name: "WHISPER (deprecated)", - Flags: whisperFlags, - }, { Name: "ALIASED (deprecated)", - Flags: append([]cli.Flag{ + Flags: []cli.Flag{ + utils.NoUSBFlag, utils.LegacyRPCEnabledFlag, utils.LegacyRPCListenAddrFlag, utils.LegacyRPCPortFlag, utils.LegacyRPCCORSDomainFlag, utils.LegacyRPCVirtualHostsFlag, utils.LegacyRPCApiFlag, - utils.LegacyWSListenAddrFlag, - utils.LegacyWSPortFlag, - utils.LegacyWSAllowedOriginsFlag, - utils.LegacyWSApiFlag, - utils.LegacyGpoBlocksFlag, - utils.LegacyGpoPercentileFlag, - utils.LegacyGraphQLListenAddrFlag, - utils.LegacyGraphQLPortFlag, - }, debug.DeprecatedFlags...), + }, }, { Name: "MISC", Flags: []cli.Flag{ utils.SnapshotFlag, + utils.BloomFilterSizeFlag, cli.HelpFlag, }, }, diff --git a/cmd/geth/version_check.go b/cmd/geth/version_check.go new file mode 100644 index 0000000000..2101a69e98 --- /dev/null +++ b/cmd/geth/version_check.go @@ -0,0 +1,169 @@ +// Copyright 2020 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 . + +package main + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "regexp" + "strings" + + "github.com/ethereum/go-ethereum/log" + "github.com/jedisct1/go-minisign" + "gopkg.in/urfave/cli.v1" +) + +var gethPubKeys []string = []string{ + //@holiman, minisign public key FB1D084D39BAEC24 + "RWQk7Lo5TQgd+wxBNZM+Zoy+7UhhMHaWKzqoes9tvSbFLJYZhNTbrIjx", + //minisign public key 138B1CA303E51687 + "RWSHFuUDoxyLEzjszuWZI1xStS66QTyXFFZG18uDfO26CuCsbckX1e9J", + //minisign public key FD9813B2D2098484 + "RWSEhAnSshOY/b+GmaiDkObbCWefsAoavjoLcPjBo1xn71yuOH5I+Lts", +} + +type vulnJson struct { + Name string + Uid string + Summary string + Description string + Links []string + Introduced string + Fixed string + Published string + Severity string + Check string + CVE string +} + +func versionCheck(ctx *cli.Context) error { + url := ctx.String(VersionCheckUrlFlag.Name) + version := ctx.String(VersionCheckVersionFlag.Name) + log.Info("Checking vulnerabilities", "version", version, "url", url) + return checkCurrent(url, version) +} + +func checkCurrent(url, current string) error { + var ( + data []byte + sig []byte + err error + ) + if data, err = fetch(url); err != nil { + return fmt.Errorf("could not retrieve data: %w", err) + } + if sig, err = fetch(fmt.Sprintf("%v.minisig", url)); err != nil { + return fmt.Errorf("could not retrieve signature: %w", err) + } + if err = verifySignature(gethPubKeys, data, sig); err != nil { + return err + } + var vulns []vulnJson + if err = json.Unmarshal(data, &vulns); err != nil { + return err + } + allOk := true + for _, vuln := range vulns { + r, err := regexp.Compile(vuln.Check) + if err != nil { + return err + } + if r.MatchString(current) { + allOk = false + fmt.Printf("## Vulnerable to %v (%v)\n\n", vuln.Uid, vuln.Name) + fmt.Printf("Severity: %v\n", vuln.Severity) + fmt.Printf("Summary : %v\n", vuln.Summary) + fmt.Printf("Fixed in: %v\n", vuln.Fixed) + if len(vuln.CVE) > 0 { + fmt.Printf("CVE: %v\n", vuln.CVE) + } + if len(vuln.Links) > 0 { + fmt.Printf("References:\n") + for _, ref := range vuln.Links { + fmt.Printf("\t- %v\n", ref) + } + } + fmt.Println() + } + } + if allOk { + fmt.Println("No vulnerabilities found") + } + return nil +} + +// fetch makes an HTTP request to the given url and returns the response body +func fetch(url string) ([]byte, error) { + if filep := strings.TrimPrefix(url, "file://"); filep != url { + return ioutil.ReadFile(filep) + } + res, err := http.Get(url) + if err != nil { + return nil, err + } + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + return body, nil +} + +// verifySignature checks that the sigData is a valid signature of the given +// data, for pubkey GethPubkey +func verifySignature(pubkeys []string, data, sigdata []byte) error { + sig, err := minisign.DecodeSignature(string(sigdata)) + if err != nil { + return err + } + // find the used key + var key *minisign.PublicKey + for _, pubkey := range pubkeys { + pub, err := minisign.NewPublicKey(pubkey) + if err != nil { + // our pubkeys should be parseable + return err + } + if pub.KeyId != sig.KeyId { + continue + } + key = &pub + break + } + if key == nil { + log.Info("Signing key not trusted", "keyid", keyID(sig.KeyId), "error", err) + return errors.New("signature could not be verified") + } + if ok, err := key.Verify(data, sig); !ok || err != nil { + log.Info("Verification failed error", "keyid", keyID(key.KeyId), "error", err) + return errors.New("signature could not be verified") + } + return nil +} + +// keyID turns a binary minisign key ID into a hex string. +// Note: key IDs are printed in reverse byte order. +func keyID(id [8]byte) string { + var rev [8]byte + for i := range id { + rev[len(rev)-1-i] = id[i] + } + return fmt.Sprintf("%X", rev) +} diff --git a/cmd/geth/version_check_test.go b/cmd/geth/version_check_test.go new file mode 100644 index 0000000000..0f056d1967 --- /dev/null +++ b/cmd/geth/version_check_test.go @@ -0,0 +1,130 @@ +// Copyright 2020 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 . + +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "path/filepath" + "regexp" + "strconv" + "strings" + "testing" +) + +func TestVerification(t *testing.T) { + // Signatures generated with `minisign` + t.Run("minisig", func(t *testing.T) { + // For this test, the pubkey is in testdata/minisign.pub + // (the privkey is `minisign.sec`, if we want to expand this test. Password 'test' ) + pub := "RWQkliYstQBOKOdtClfgC3IypIPX6TAmoEi7beZ4gyR3wsaezvqOMWsp" + testVerification(t, pub, "./testdata/vcheck/minisig-sigs/") + }) + // Signatures generated with `signify-openbsd` + t.Run("signify-openbsd", func(t *testing.T) { + t.Skip("This currently fails, minisign expects 4 lines of data, signify provides only 2") + // For this test, the pubkey is in testdata/signifykey.pub + // (the privkey is `signifykey.sec`, if we want to expand this test. Password 'test' ) + pub := "RWSKLNhZb0KdATtRT7mZC/bybI3t3+Hv/O2i3ye04Dq9fnT9slpZ1a2/" + testVerification(t, pub, "./testdata/vcheck/signify-sigs/") + }) +} + +func testVerification(t *testing.T, pubkey, sigdir string) { + // Data to verify + data, err := ioutil.ReadFile("./testdata/vcheck/data.json") + if err != nil { + t.Fatal(err) + } + // Signatures, with and without comments, both trusted and untrusted + files, err := ioutil.ReadDir(sigdir) + if err != nil { + t.Fatal(err) + } + for _, f := range files { + sig, err := ioutil.ReadFile(filepath.Join(sigdir, f.Name())) + if err != nil { + t.Fatal(err) + } + err = verifySignature([]string{pubkey}, data, sig) + if err != nil { + t.Fatal(err) + } + } +} + +func versionUint(v string) int { + mustInt := func(s string) int { + a, err := strconv.Atoi(s) + if err != nil { + panic(v) + } + return a + } + components := strings.Split(strings.TrimPrefix(v, "v"), ".") + a := mustInt(components[0]) + b := mustInt(components[1]) + c := mustInt(components[2]) + return a*100*100 + b*100 + c +} + +// TestMatching can be used to check that the regexps are correct +func TestMatching(t *testing.T) { + data, _ := ioutil.ReadFile("./testdata/vcheck/vulnerabilities.json") + var vulns []vulnJson + if err := json.Unmarshal(data, &vulns); err != nil { + t.Fatal(err) + } + check := func(version string) { + vFull := fmt.Sprintf("Geth/%v-unstable-15339cf1-20201204/linux-amd64/go1.15.4", version) + for _, vuln := range vulns { + r, err := regexp.Compile(vuln.Check) + vulnIntro := versionUint(vuln.Introduced) + vulnFixed := versionUint(vuln.Fixed) + current := versionUint(version) + if err != nil { + t.Fatal(err) + } + if vuln.Name == "Denial of service due to Go CVE-2020-28362" { + // this one is not tied to geth-versions + continue + } + if vulnIntro <= current && vulnFixed > current { + // Should be vulnerable + if !r.MatchString(vFull) { + t.Errorf("Should be vulnerable, version %v, intro: %v, fixed: %v %v %v", + version, vuln.Introduced, vuln.Fixed, vuln.Name, vuln.Check) + } + } else { + if r.MatchString(vFull) { + t.Errorf("Should not be flagged vulnerable, version %v, intro: %v, fixed: %v %v %d %d %d", + version, vuln.Introduced, vuln.Fixed, vuln.Name, vulnIntro, current, vulnFixed) + } + } + + } + } + for major := 1; major < 2; major++ { + for minor := 0; minor < 30; minor++ { + for patch := 0; patch < 30; patch++ { + vShort := fmt.Sprintf("v%d.%d.%d", major, minor, patch) + check(vShort) + } + } + } +} diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index 869cf90ea5..d4051e59ef 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -26,17 +26,20 @@ import ( "runtime" "strings" "syscall" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rlp" + "gopkg.in/urfave/cli.v1" ) const ( @@ -63,7 +66,7 @@ func Fatalf(format string, args ...interface{}) { os.Exit(1) } -func StartNode(stack *node.Node) { +func StartNode(ctx *cli.Context, stack *node.Node) { if err := stack.Start(); err != nil { Fatalf("Error starting protocol stack: %v", err) } @@ -71,6 +74,17 @@ func StartNode(stack *node.Node) { sigc := make(chan os.Signal, 1) signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM) defer signal.Stop(sigc) + + minFreeDiskSpace := ethconfig.Defaults.TrieDirtyCache + if ctx.GlobalIsSet(MinFreeDiskSpaceFlag.Name) { + minFreeDiskSpace = ctx.GlobalInt(MinFreeDiskSpaceFlag.Name) + } else if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) { + minFreeDiskSpace = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100 + } + if minFreeDiskSpace > 0 { + go monitorFreeDiskSpace(sigc, stack.InstanceDir(), uint64(minFreeDiskSpace)*1024*1024) + } + <-sigc log.Info("Got interrupt, shutting down...") go stack.Close() @@ -85,6 +99,24 @@ func StartNode(stack *node.Node) { }() } +func monitorFreeDiskSpace(sigc chan os.Signal, path string, freeDiskSpaceCritical uint64) { + for { + freeSpace, err := getFreeDiskSpace(path) + if err != nil { + log.Warn("Failed to get free disk space", "path", path, "err", err) + break + } + if freeSpace < freeDiskSpaceCritical { + log.Error("Low disk space. Gracefully shutting down Geth to prevent database corruption.", "available", common.StorageSize(freeSpace)) + sigc <- syscall.SIGTERM + break + } else if freeSpace < 2*freeDiskSpaceCritical { + log.Warn("Disk space is running low. Geth will shutdown if disk space runs below critical level.", "available", common.StorageSize(freeSpace), "critical_level", common.StorageSize(freeDiskSpaceCritical)) + } + time.Sleep(60 * time.Second) + } +} + func ImportChain(chain *core.BlockChain, fn string) error { // Watch for Ctrl-C while the import is running. // If a signal is received, the import will stop at the next batch. diff --git a/cmd/utils/diskusage.go b/cmd/utils/diskusage.go new file mode 100644 index 0000000000..c3d51765f8 --- /dev/null +++ b/cmd/utils/diskusage.go @@ -0,0 +1,42 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build !windows,!openbsd + +package utils + +import ( + "fmt" + + "golang.org/x/sys/unix" +) + +func getFreeDiskSpace(path string) (uint64, error) { + var stat unix.Statfs_t + if err := unix.Statfs(path, &stat); err != nil { + return 0, fmt.Errorf("failed to call Statfs: %v", err) + } + + // Available blocks * size per block = available space in bytes + var bavail = stat.Bavail + if stat.Bavail < 0 { + // FreeBSD can have a negative number of blocks available + // because of the grace limit. + bavail = 0 + } + //nolint:unconvert + return uint64(bavail) * uint64(stat.Bsize), nil +} diff --git a/cmd/utils/diskusage_openbsd.go b/cmd/utils/diskusage_openbsd.go new file mode 100644 index 0000000000..54f759d291 --- /dev/null +++ b/cmd/utils/diskusage_openbsd.go @@ -0,0 +1,43 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build openbsd + +package utils + +import ( + "fmt" + + "golang.org/x/sys/unix" +) + +func getFreeDiskSpace(path string) (uint64, error) { + var stat unix.Statfs_t + if err := unix.Statfs(path, &stat); err != nil { + return 0, fmt.Errorf("failed to call Statfs: %v", err) + } + + // Available blocks * size per block = available space in bytes + var bavail = stat.F_bavail + // Not sure if the following check is necessary for OpenBSD + if stat.F_bavail < 0 { + // FreeBSD can have a negative number of blocks available + // because of the grace limit. + bavail = 0 + } + //nolint:unconvert + return uint64(bavail) * uint64(stat.F_bsize), nil +} diff --git a/cmd/utils/diskusage_windows.go b/cmd/utils/diskusage_windows.go new file mode 100644 index 0000000000..9bf7740b99 --- /dev/null +++ b/cmd/utils/diskusage_windows.go @@ -0,0 +1,38 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package utils + +import ( + "fmt" + + "golang.org/x/sys/windows" +) + +func getFreeDiskSpace(path string) (uint64, error) { + + cwd, err := windows.UTF16PtrFromString(path) + if err != nil { + return 0, fmt.Errorf("failed to call UTF16PtrFromString: %v", err) + } + + var freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes uint64 + if err := windows.GetDiskFreeSpaceEx(cwd, &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes); err != nil { + return 0, fmt.Errorf("failed to call GetDiskFreeSpaceEx: %v", err) + } + + return freeBytesAvailableToCaller, nil +} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index f49010f9a5..9f731b8ecb 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -22,10 +22,12 @@ import ( "fmt" "io" "io/ioutil" + "math" "math/big" "os" "path/filepath" "runtime" + godebug "runtime/debug" "strconv" "strings" "text/tabwriter" @@ -36,7 +38,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/fdlimit" - "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" @@ -46,7 +48,9 @@ import ( "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/ethconfig" "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethstats" "github.com/ethereum/go-ethereum/graphql" @@ -60,7 +64,6 @@ import ( "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/netutil" @@ -69,7 +72,8 @@ import ( "github.com/ethereum/go-ethereum/params/types/genesisT" "github.com/ethereum/go-ethereum/params/vars" pcsclite "github.com/gballet/go-libpcsclite" - cli "gopkg.in/urfave/cli.v1" + gopsutil "github.com/shirou/gopsutil/mem" + "gopkg.in/urfave/cli.v1" ) func init() { @@ -118,6 +122,10 @@ var ( Name: "datadir.ancient", Usage: "Data directory for ancient chain segments (default = inside chaindata)", } + MinFreeDiskSpaceFlag = DirectoryFlag{ + Name: "datadir.minfreedisk", + Usage: "Minimum free disk space in MB, once reached triggers auto shut down (default = --cache.gc converted to MB, 0 = disabled)", + } AncientRPCFlag = cli.StringFlag{ Name: "ancient.rpc", Usage: "Connect to a remote freezer via RPC. Value must an HTTP(S), WS(S), unix socket, or 'stdio' URL. Incompatible with --datadir.ancient", @@ -129,7 +137,11 @@ var ( } NoUSBFlag = cli.BoolFlag{ Name: "nousb", - Usage: "Disables monitoring for and managing USB hardware wallets", + Usage: "Disables monitoring for and managing USB hardware wallets (deprecated)", + } + USBFlag = cli.BoolFlag{ + Name: "usb", + Usage: "Enable monitoring and management of USB hardware wallets", } SmartCardDaemonPathFlag = cli.StringFlag{ Name: "pcscdpath", @@ -138,12 +150,12 @@ var ( } NetworkIdFlag = cli.Uint64Flag{ Name: "networkid", - Usage: "Explicitly set network id (integer)(For testnets: use --ropsten, --rinkeby, --goerli, --kotti, --mordor, --yolov2 instead)", - Value: eth.DefaultConfig.NetworkId, + Usage: "Explicitly set network id (integer)(For testnets: use --ropsten, --rinkeby, --goerli instead)", + Value: ethconfig.Defaults.NetworkId, } EthProtocolsFlag = cli.StringFlag{ Name: "eth.protocols", - Usage: "Sets the Ethereum Protocol versions (65|64|63) (default = 65,64,63 first is primary)", + Usage: "Sets the Ethereum Protocol versions (66|65|64) (default = 66,65,64 first is primary)", Value: "", } ClassicFlag = cli.BoolFlag{ @@ -154,9 +166,9 @@ var ( Name: "mainnet", Usage: "Ethereum mainnet: pre-configured Ethereum mainnet", } - YoloV2Flag = cli.BoolFlag{ - Name: "yolov2", - Usage: "YOLOv2 network: pre-configured proof-of-authority shortlived test network.", + YoloV3Flag = cli.BoolFlag{ + Name: "yolov3", + Usage: "YOLOv3 network: pre-configured proof-of-authority shortlived test network.", } RinkebyFlag = cli.BoolFlag{ Name: "rinkeby", @@ -219,10 +231,10 @@ var ( Name: "nocode", Usage: "Exclude contract code (save db lookups)", } - defaultSyncMode = eth.DefaultConfig.SyncMode + defaultSyncMode = ethconfig.Defaults.SyncMode SyncModeFlag = TextMarshalerFlag{ Name: "syncmode", - Usage: `Blockchain sync mode ("fast", "full", or "light")`, + Usage: `Blockchain sync mode ("fast", "full", "snap" or "light")`, Value: &defaultSyncMode, } GCModeFlag = cli.StringFlag{ @@ -230,14 +242,14 @@ var ( Usage: `Blockchain garbage collection mode ("full", "archive")`, Value: "full", } - SnapshotFlag = cli.BoolFlag{ + SnapshotFlag = cli.BoolTFlag{ Name: "snapshot", - Usage: `Enables snapshot-database mode -- experimental work in progress feature`, + Usage: `Enables snapshot-database mode (default = enable)`, } - TxLookupLimitFlag = cli.Int64Flag{ + TxLookupLimitFlag = cli.Uint64Flag{ Name: "txlookuplimit", - Usage: "Number of recent blocks to maintain transactions index by-hash for (default = index all blocks)", - Value: 0, + Usage: "Number of recent blocks to maintain transactions index for (default = about one year, 0 = entire chain)", + Value: ethconfig.Defaults.TxLookupLimit, } LightKDFFlag = cli.BoolFlag{ Name: "lightkdf", @@ -247,36 +259,45 @@ var ( Name: "whitelist", Usage: "Comma separated block number-to-hash mappings to enforce (=)", } + BloomFilterSizeFlag = cli.Uint64Flag{ + Name: "bloomfilter.size", + Usage: "Megabytes of memory allocated to bloom-filter for pruning", + Value: 2048, + } + OverrideMagnetoFlag = cli.Uint64Flag{ + Name: "override.magneto", + Usage: "Manually specify magneto fork-block, overriding the bundled setting", + } // Light server and client settings LightServeFlag = cli.IntFlag{ Name: "light.serve", Usage: "Maximum percentage of time allowed for serving LES requests (multi-threaded processing allows values over 100)", - Value: eth.DefaultConfig.LightServ, + Value: ethconfig.Defaults.LightServ, } LightIngressFlag = cli.IntFlag{ Name: "light.ingress", Usage: "Incoming bandwidth limit for serving light clients (kilobytes/sec, 0 = unlimited)", - Value: eth.DefaultConfig.LightIngress, + Value: ethconfig.Defaults.LightIngress, } LightEgressFlag = cli.IntFlag{ Name: "light.egress", Usage: "Outgoing bandwidth limit for serving light clients (kilobytes/sec, 0 = unlimited)", - Value: eth.DefaultConfig.LightEgress, + Value: ethconfig.Defaults.LightEgress, } LightMaxPeersFlag = cli.IntFlag{ Name: "light.maxpeers", Usage: "Maximum number of light clients to serve, or light servers to attach to", - Value: eth.DefaultConfig.LightPeers, + Value: ethconfig.Defaults.LightPeers, } UltraLightServersFlag = cli.StringFlag{ Name: "ulc.servers", Usage: "List of trusted ultra-light servers", - Value: strings.Join(eth.DefaultConfig.UltraLightServers, ","), + Value: strings.Join(ethconfig.Defaults.UltraLightServers, ","), } UltraLightFractionFlag = cli.IntFlag{ Name: "ulc.fraction", Usage: "Minimum % of trusted ultra-light servers required to announce a new head", - Value: eth.DefaultConfig.UltraLightFraction, + Value: ethconfig.Defaults.UltraLightFraction, } UltraLightOnlyAnnounceFlag = cli.BoolFlag{ Name: "ulc.onlyannounce", @@ -286,6 +307,10 @@ var ( Name: "light.nopruning", Usage: "Disable ancient light chain data pruning", } + LightNoSyncServeFlag = cli.BoolFlag{ + Name: "light.nosyncserve", + Usage: "Enables serving light clients before syncing", + } // Ethash settings EthashCacheDirFlag = DirectoryFlag{ Name: "ethash.cachedir", @@ -294,12 +319,12 @@ var ( EthashCachesInMemoryFlag = cli.IntFlag{ Name: "ethash.cachesinmem", Usage: "Number of recent ethash caches to keep in memory (16MB each)", - Value: eth.DefaultConfig.Ethash.CachesInMem, + Value: ethconfig.Defaults.Ethash.CachesInMem, } EthashCachesOnDiskFlag = cli.IntFlag{ Name: "ethash.cachesondisk", Usage: "Number of recent ethash caches to keep on disk (16MB each)", - Value: eth.DefaultConfig.Ethash.CachesOnDisk, + Value: ethconfig.Defaults.Ethash.CachesOnDisk, } EthashCachesLockMmapFlag = cli.BoolFlag{ Name: "ethash.cacheslockmmap", @@ -307,18 +332,18 @@ var ( } EthashDatasetDirFlag = DirectoryFlag{ Name: "ethash.dagdir", - Usage: "Directory to store the [ethash|etchash] mining DAGs based on supported network", - Value: DirectoryString(eth.DefaultConfig.Ethash.DatasetDir), + Usage: "Directory to store the ethash mining DAGs", + Value: DirectoryString(ethconfig.Defaults.Ethash.DatasetDir), } EthashDatasetsInMemoryFlag = cli.IntFlag{ Name: "ethash.dagsinmem", Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)", - Value: eth.DefaultConfig.Ethash.DatasetsInMem, + Value: ethconfig.Defaults.Ethash.DatasetsInMem, } EthashDatasetsOnDiskFlag = cli.IntFlag{ Name: "ethash.dagsondisk", Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)", - Value: eth.DefaultConfig.Ethash.DatasetsOnDisk, + Value: ethconfig.Defaults.Ethash.DatasetsOnDisk, } EthashDatasetsLockMmapFlag = cli.BoolFlag{ Name: "ethash.dagslockmmap", @@ -351,37 +376,37 @@ var ( TxPoolPriceLimitFlag = cli.Uint64Flag{ Name: "txpool.pricelimit", Usage: "Minimum gas price limit to enforce for acceptance into the pool", - Value: eth.DefaultConfig.TxPool.PriceLimit, + Value: ethconfig.Defaults.TxPool.PriceLimit, } TxPoolPriceBumpFlag = cli.Uint64Flag{ Name: "txpool.pricebump", Usage: "Price bump percentage to replace an already existing transaction", - Value: eth.DefaultConfig.TxPool.PriceBump, + Value: ethconfig.Defaults.TxPool.PriceBump, } TxPoolAccountSlotsFlag = cli.Uint64Flag{ Name: "txpool.accountslots", Usage: "Minimum number of executable transaction slots guaranteed per account", - Value: eth.DefaultConfig.TxPool.AccountSlots, + Value: ethconfig.Defaults.TxPool.AccountSlots, } TxPoolGlobalSlotsFlag = cli.Uint64Flag{ Name: "txpool.globalslots", Usage: "Maximum number of executable transaction slots for all accounts", - Value: eth.DefaultConfig.TxPool.GlobalSlots, + Value: ethconfig.Defaults.TxPool.GlobalSlots, } TxPoolAccountQueueFlag = cli.Uint64Flag{ Name: "txpool.accountqueue", Usage: "Maximum number of non-executable transaction slots permitted per account", - Value: eth.DefaultConfig.TxPool.AccountQueue, + Value: ethconfig.Defaults.TxPool.AccountQueue, } TxPoolGlobalQueueFlag = cli.Uint64Flag{ Name: "txpool.globalqueue", Usage: "Maximum number of non-executable transaction slots for all accounts", - Value: eth.DefaultConfig.TxPool.GlobalQueue, + Value: ethconfig.Defaults.TxPool.GlobalQueue, } TxPoolLifetimeFlag = cli.DurationFlag{ Name: "txpool.lifetime", Usage: "Maximum amount of time non-executable transaction are queued", - Value: eth.DefaultConfig.TxPool.Lifetime, + Value: ethconfig.Defaults.TxPool.Lifetime, } // Performance tuning settings CacheFlag = cli.IntFlag{ @@ -402,12 +427,12 @@ var ( CacheTrieJournalFlag = cli.StringFlag{ Name: "cache.trie.journal", Usage: "Disk journal directory for trie cache to survive node restarts", - Value: eth.DefaultConfig.TrieCleanCacheJournal, + Value: ethconfig.Defaults.TrieCleanCacheJournal, } CacheTrieRejournalFlag = cli.DurationFlag{ Name: "cache.trie.rejournal", Usage: "Time interval to regenerate the trie cache journal", - Value: eth.DefaultConfig.TrieCleanCacheRejournal, + Value: ethconfig.Defaults.TrieCleanCacheRejournal, } CacheGCFlag = cli.IntFlag{ Name: "cache.gc", @@ -423,6 +448,10 @@ var ( Name: "cache.noprefetch", Usage: "Disable heuristic state prefetch during block import (less CPU and disk IO, more time waiting for data)", } + CachePreimagesFlag = cli.BoolFlag{ + Name: "cache.preimages", + Usage: "Enable recording the SHA3/keccak preimages of trie keys", + } // Miner settings MiningEnabledFlag = cli.BoolFlag{ Name: "mine", @@ -437,20 +466,24 @@ var ( Name: "miner.notify", Usage: "Comma separated HTTP URL list to notify of new work packages", } + MinerNotifyFullFlag = cli.BoolFlag{ + Name: "miner.notify.full", + Usage: "Notify with pending block headers instead of work packages", + } MinerGasTargetFlag = cli.Uint64Flag{ Name: "miner.gastarget", Usage: "Target gas floor for mined blocks", - Value: eth.DefaultConfig.Miner.GasFloor, + Value: ethconfig.Defaults.Miner.GasFloor, } MinerGasLimitFlag = cli.Uint64Flag{ Name: "miner.gaslimit", Usage: "Target gas ceiling for mined blocks", - Value: eth.DefaultConfig.Miner.GasCeil, + Value: ethconfig.Defaults.Miner.GasCeil, } MinerGasPriceFlag = BigFlag{ Name: "miner.gasprice", Usage: "Minimum gas price for mining a transaction", - Value: eth.DefaultConfig.Miner.GasPrice, + Value: ethconfig.Defaults.Miner.GasPrice, } MinerEtherbaseFlag = cli.StringFlag{ Name: "miner.etherbase", @@ -464,7 +497,7 @@ var ( MinerRecommitIntervalFlag = cli.DurationFlag{ Name: "miner.recommit", Usage: "Time interval to recreate the block being mined", - Value: eth.DefaultConfig.Miner.Recommit, + Value: ethconfig.Defaults.Miner.Recommit, } MinerNoVerfiyFlag = cli.BoolFlag{ Name: "miner.noverify", @@ -497,12 +530,12 @@ var ( RPCGlobalGasCapFlag = cli.Uint64Flag{ Name: "rpc.gascap", Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)", - Value: eth.DefaultConfig.RPCGasCap, + Value: ethconfig.Defaults.RPCGasCap, } RPCGlobalTxFeeCapFlag = cli.Float64Flag{ Name: "rpc.txfeecap", Usage: "Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)", - Value: eth.DefaultConfig.RPCTxFeeCap, + Value: ethconfig.Defaults.RPCTxFeeCap, } // Logging and debug settings EthStatsURLFlag = cli.StringFlag{ @@ -559,6 +592,11 @@ var ( Usage: "API's offered over the HTTP-RPC interface", Value: "", } + HTTPPathPrefixFlag = cli.StringFlag{ + Name: "http.rpcprefix", + Usage: "HTTP path path prefix on which JSON-RPC is served. Use '/' to serve on all paths.", + Value: "", + } GraphQLEnabledFlag = cli.BoolFlag{ Name: "graphql", Usage: "Enable GraphQL on the HTTP-RPC server. Note that GraphQL can only be started if an HTTP server is started as well.", @@ -597,6 +635,11 @@ var ( Usage: "Origins from which to accept websockets requests", Value: "", } + WSPathPrefixFlag = cli.StringFlag{ + Name: "ws.rpcprefix", + Usage: "HTTP path prefix on which JSON-RPC is served. Use '/' to serve on all paths.", + Value: "", + } ExecFlag = cli.StringFlag{ Name: "exec", Usage: "Execute JavaScript statement", @@ -605,6 +648,10 @@ var ( Name: "preload", Usage: "Comma separated list of JavaScript files to preload into the console", } + AllowUnprotectedTxs = cli.BoolFlag{ + Name: "rpc.allow-unprotected-txs", + Usage: "Allow for unprotected (non EIP155 signed) transactions to be submitted via RPC", + } // Network Settings MaxPeersFlag = cli.IntFlag{ @@ -668,35 +715,17 @@ var ( GpoBlocksFlag = cli.IntFlag{ Name: "gpo.blocks", Usage: "Number of recent blocks to check for gas prices", - Value: eth.DefaultConfig.GPO.Blocks, + Value: ethconfig.Defaults.GPO.Blocks, } GpoPercentileFlag = cli.IntFlag{ Name: "gpo.percentile", Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices", - Value: eth.DefaultConfig.GPO.Percentile, + Value: ethconfig.Defaults.GPO.Percentile, } GpoMaxGasPriceFlag = cli.Int64Flag{ Name: "gpo.maxprice", Usage: "Maximum gas price will be recommended by gpo", - Value: eth.DefaultConfig.GPO.MaxPrice.Int64(), - } - WhisperEnabledFlag = cli.BoolFlag{ - Name: "shh", - Usage: "Enable Whisper", - } - WhisperMaxMessageSizeFlag = cli.IntFlag{ - Name: "shh.maxmessagesize", - Usage: "Max message size accepted", - Value: 1024 * 1024, - } - WhisperMinPOWFlag = cli.Float64Flag{ - Name: "shh.pow", - Usage: "Minimum POW accepted", - Value: 0.2, - } - WhisperRestrictConnectionBetweenLightClientsFlag = cli.BoolFlag{ - Name: "shh.restrict-light", - Usage: "Restrict connection between two whisper light clients", + Value: ethconfig.Defaults.GPO.MaxPrice.Int64(), } // Metrics flags @@ -716,12 +745,12 @@ var ( MetricsHTTPFlag = cli.StringFlag{ Name: "metrics.addr", Usage: "Enable stand-alone metrics HTTP server listening interface", - Value: "127.0.0.1", + Value: metrics.DefaultConfig.HTTP, } MetricsPortFlag = cli.IntFlag{ Name: "metrics.port", Usage: "Metrics HTTP server listening port", - Value: 6060, + Value: metrics.DefaultConfig.Port, } MetricsEnableInfluxDBFlag = cli.BoolFlag{ Name: "metrics.influxdb", @@ -730,22 +759,22 @@ var ( MetricsInfluxDBEndpointFlag = cli.StringFlag{ Name: "metrics.influxdb.endpoint", Usage: "InfluxDB API endpoint to report metrics to", - Value: "http://localhost:8086", + Value: metrics.DefaultConfig.InfluxDBEndpoint, } MetricsInfluxDBDatabaseFlag = cli.StringFlag{ Name: "metrics.influxdb.database", Usage: "InfluxDB database name to push reported metrics to", - Value: "geth", + Value: metrics.DefaultConfig.InfluxDBDatabase, } MetricsInfluxDBUsernameFlag = cli.StringFlag{ Name: "metrics.influxdb.username", Usage: "Username to authorize access to the database", - Value: "test", + Value: metrics.DefaultConfig.InfluxDBUsername, } MetricsInfluxDBPasswordFlag = cli.StringFlag{ Name: "metrics.influxdb.password", Usage: "Password to authorize access to the database", - Value: "test", + Value: metrics.DefaultConfig.InfluxDBPassword, } // Tags are part of every measurement sent to InfluxDB. Queries on tags are faster in InfluxDB. // For example `host` tag could be used so that we can group all nodes and average a measurement @@ -754,7 +783,7 @@ var ( MetricsInfluxDBTagsFlag = cli.StringFlag{ Name: "metrics.influxdb.tags", Usage: "Comma-separated InfluxDB tags (key/values) attached to all measurements", - Value: "host=localhost", + Value: metrics.DefaultConfig.InfluxDBTags, } EWASMInterpreterFlag = cli.StringFlag{ Name: "vm.ewasm", @@ -783,17 +812,6 @@ var ( func MakeDataDir(ctx *cli.Context) string { if path := ctx.GlobalString(DataDirFlag.Name); path != "" { - - // Maintain compatibility with older Geth configurations storing the - // Ropsten database in `testnet` instead of `ropsten`. - if ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name) { - legacyPath := filepath.Join(path, "testnet") - if _, err := os.Stat(legacyPath); !os.IsNotExist(err) { - return legacyPath - } else { - return filepath.Join(path, "ropsten") - } - } return dataDirPathForCtxChainConfig(ctx, path) } Fatalf("Cannot determine default data directory, please set manually (--%s)", DataDirFlag.Name) @@ -838,17 +856,13 @@ func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) { func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { urls := params.MainnetBootnodes switch { - case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(LegacyBootnodesV4Flag.Name): - if ctx.GlobalIsSet(LegacyBootnodesV4Flag.Name) { - urls = SplitAndTrim(ctx.GlobalString(LegacyBootnodesV4Flag.Name)) - } else { - urls = SplitAndTrim(ctx.GlobalString(BootnodesFlag.Name)) - } + case ctx.GlobalIsSet(BootnodesFlag.Name): + urls = SplitAndTrim(ctx.GlobalString(BootnodesFlag.Name)) case ctx.GlobalBool(ClassicFlag.Name): urls = params.ClassicBootnodes case ctx.GlobalBool(MordorFlag.Name): urls = params.MordorBootnodes - case ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name): + case ctx.GlobalBool(RopstenFlag.Name): urls = params.RopstenBootnodes case ctx.GlobalBool(RinkebyFlag.Name): urls = params.RinkebyBootnodes @@ -856,8 +870,8 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { urls = params.KottiBootnodes case ctx.GlobalBool(GoerliFlag.Name): urls = params.GoerliBootnodes - case ctx.GlobalBool(YoloV2Flag.Name): - urls = params.YoloV2Bootnodes + case ctx.GlobalBool(YoloV3Flag.Name): + urls = params.YoloV3Bootnodes case cfg.BootstrapNodes != nil: return // already set, don't apply defaults. } @@ -878,14 +892,10 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { // setBootstrapNodesV5 creates a list of bootstrap nodes from the command line // flags, reverting to pre-configured ones if none have been specified. func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) { - urls := params.MainnetBootnodes + urls := params.V5Bootnodes switch { - case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(LegacyBootnodesV5Flag.Name): - if ctx.GlobalIsSet(LegacyBootnodesV5Flag.Name) { - urls = SplitAndTrim(ctx.GlobalString(LegacyBootnodesV5Flag.Name)) - } else { - urls = SplitAndTrim(ctx.GlobalString(BootnodesFlag.Name)) - } + case ctx.GlobalIsSet(BootnodesFlag.Name): + urls = SplitAndTrim(ctx.GlobalString(BootnodesFlag.Name)) case ctx.GlobalIsSet(ClassicFlag.Name): urls = params.ClassicBootnodes case ctx.GlobalIsSet(MordorFlag.Name): @@ -898,16 +908,16 @@ func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) { urls = params.KottiBootnodes case ctx.GlobalBool(GoerliFlag.Name): urls = params.GoerliBootnodes - case ctx.GlobalBool(YoloV2Flag.Name): - urls = params.YoloV2Bootnodes + case ctx.GlobalBool(YoloV3Flag.Name): + urls = params.YoloV3Bootnodes case cfg.BootstrapNodesV5 != nil: return // already set, don't apply defaults. } - cfg.BootstrapNodesV5 = make([]*discv5.Node, 0, len(urls)) + cfg.BootstrapNodesV5 = make([]*enode.Node, 0, len(urls)) for _, url := range urls { if url != "" { - node, err := discv5.ParseNode(url) + node, err := enode.Parse(enode.ValidSchemes, url) if err != nil { log.Error("Bootstrap URL invalid", "enode", url, "err", err) continue @@ -952,11 +962,11 @@ func SplitAndTrim(input string) (ret []string) { // command line flags, returning empty if the HTTP endpoint is disabled. func setHTTP(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalBool(LegacyRPCEnabledFlag.Name) && cfg.HTTPHost == "" { - log.Warn("The flag --rpc is deprecated and will be removed in the future, please use --http") + log.Warn("The flag --rpc is deprecated and will be removed June 2021, please use --http") cfg.HTTPHost = "127.0.0.1" if ctx.GlobalIsSet(LegacyRPCListenAddrFlag.Name) { cfg.HTTPHost = ctx.GlobalString(LegacyRPCListenAddrFlag.Name) - log.Warn("The flag --rpcaddr is deprecated and will be removed in the future, please use --http.addr") + log.Warn("The flag --rpcaddr is deprecated and will be removed June 2021, please use --http.addr") } } if ctx.GlobalBool(HTTPEnabledFlag.Name) && cfg.HTTPHost == "" { @@ -968,7 +978,7 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(LegacyRPCPortFlag.Name) { cfg.HTTPPort = ctx.GlobalInt(LegacyRPCPortFlag.Name) - log.Warn("The flag --rpcport is deprecated and will be removed in the future, please use --http.port") + log.Warn("The flag --rpcport is deprecated and will be removed June 2021, please use --http.port") } if ctx.GlobalIsSet(HTTPPortFlag.Name) { cfg.HTTPPort = ctx.GlobalInt(HTTPPortFlag.Name) @@ -976,7 +986,7 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(LegacyRPCCORSDomainFlag.Name) { cfg.HTTPCors = SplitAndTrim(ctx.GlobalString(LegacyRPCCORSDomainFlag.Name)) - log.Warn("The flag --rpccorsdomain is deprecated and will be removed in the future, please use --http.corsdomain") + log.Warn("The flag --rpccorsdomain is deprecated and will be removed June 2021, please use --http.corsdomain") } if ctx.GlobalIsSet(HTTPCORSDomainFlag.Name) { cfg.HTTPCors = SplitAndTrim(ctx.GlobalString(HTTPCORSDomainFlag.Name)) @@ -984,7 +994,7 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(LegacyRPCApiFlag.Name) { cfg.HTTPModules = SplitAndTrim(ctx.GlobalString(LegacyRPCApiFlag.Name)) - log.Warn("The flag --rpcapi is deprecated and will be removed in the future, please use --http.api") + log.Warn("The flag --rpcapi is deprecated and will be removed June 2021, please use --http.api") } if ctx.GlobalIsSet(HTTPApiFlag.Name) { cfg.HTTPModules = SplitAndTrim(ctx.GlobalString(HTTPApiFlag.Name)) @@ -992,11 +1002,18 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(LegacyRPCVirtualHostsFlag.Name) { cfg.HTTPVirtualHosts = SplitAndTrim(ctx.GlobalString(LegacyRPCVirtualHostsFlag.Name)) - log.Warn("The flag --rpcvhosts is deprecated and will be removed in the future, please use --http.vhosts") + log.Warn("The flag --rpcvhosts is deprecated and will be removed June 2021, please use --http.vhosts") } if ctx.GlobalIsSet(HTTPVirtualHostsFlag.Name) { cfg.HTTPVirtualHosts = SplitAndTrim(ctx.GlobalString(HTTPVirtualHostsFlag.Name)) } + + if ctx.GlobalIsSet(HTTPPathPrefixFlag.Name) { + cfg.HTTPPathPrefix = ctx.GlobalString(HTTPPathPrefixFlag.Name) + } + if ctx.GlobalIsSet(AllowUnprotectedTxs.Name) { + cfg.AllowUnprotectedTxs = ctx.GlobalBool(AllowUnprotectedTxs.Name) + } } // setGraphQL creates the GraphQL listener interface string from the set @@ -1015,37 +1032,25 @@ func setGraphQL(ctx *cli.Context, cfg *node.Config) { func setWS(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalBool(WSEnabledFlag.Name) && cfg.WSHost == "" { cfg.WSHost = "127.0.0.1" - if ctx.GlobalIsSet(LegacyWSListenAddrFlag.Name) { - cfg.WSHost = ctx.GlobalString(LegacyWSListenAddrFlag.Name) - log.Warn("The flag --wsaddr is deprecated and will be removed in the future, please use --ws.addr") - } if ctx.GlobalIsSet(WSListenAddrFlag.Name) { cfg.WSHost = ctx.GlobalString(WSListenAddrFlag.Name) } } - if ctx.GlobalIsSet(LegacyWSPortFlag.Name) { - cfg.WSPort = ctx.GlobalInt(LegacyWSPortFlag.Name) - log.Warn("The flag --wsport is deprecated and will be removed in the future, please use --ws.port") - } if ctx.GlobalIsSet(WSPortFlag.Name) { cfg.WSPort = ctx.GlobalInt(WSPortFlag.Name) } - if ctx.GlobalIsSet(LegacyWSAllowedOriginsFlag.Name) { - cfg.WSOrigins = SplitAndTrim(ctx.GlobalString(LegacyWSAllowedOriginsFlag.Name)) - log.Warn("The flag --wsorigins is deprecated and will be removed in the future, please use --ws.origins") - } if ctx.GlobalIsSet(WSAllowedOriginsFlag.Name) { cfg.WSOrigins = SplitAndTrim(ctx.GlobalString(WSAllowedOriginsFlag.Name)) } - if ctx.GlobalIsSet(LegacyWSApiFlag.Name) { - cfg.WSModules = SplitAndTrim(ctx.GlobalString(LegacyWSApiFlag.Name)) - log.Warn("The flag --wsapi is deprecated and will be removed in the future, please use --ws.api") - } if ctx.GlobalIsSet(WSApiFlag.Name) { cfg.WSModules = SplitAndTrim(ctx.GlobalString(WSApiFlag.Name)) } + + if ctx.GlobalIsSet(WSPathPrefixFlag.Name) { + cfg.WSPathPrefix = ctx.GlobalString(WSPathPrefixFlag.Name) + } } // setIPC creates an IPC path configuration from the set command line flags, @@ -1061,11 +1066,7 @@ func setIPC(ctx *cli.Context, cfg *node.Config) { } // setLes configures the les server and ultra light client settings from the command line flags. -func setLes(ctx *cli.Context, cfg *eth.Config) { - if ctx.GlobalIsSet(LegacyLightServFlag.Name) { - cfg.LightServ = ctx.GlobalInt(LegacyLightServFlag.Name) - log.Warn("The flag --lightserv is deprecated and will be removed in the future, please use --light.serve") - } +func setLes(ctx *cli.Context, cfg *ethconfig.Config) { if ctx.GlobalIsSet(LightServeFlag.Name) { cfg.LightServ = ctx.GlobalInt(LightServeFlag.Name) } @@ -1075,10 +1076,6 @@ func setLes(ctx *cli.Context, cfg *eth.Config) { if ctx.GlobalIsSet(LightEgressFlag.Name) { cfg.LightEgress = ctx.GlobalInt(LightEgressFlag.Name) } - if ctx.GlobalIsSet(LegacyLightPeersFlag.Name) { - cfg.LightPeers = ctx.GlobalInt(LegacyLightPeersFlag.Name) - log.Warn("The flag --lightpeers is deprecated and will be removed in the future, please use --light.maxpeers") - } if ctx.GlobalIsSet(LightMaxPeersFlag.Name) { cfg.LightPeers = ctx.GlobalInt(LightMaxPeersFlag.Name) } @@ -1089,8 +1086,8 @@ func setLes(ctx *cli.Context, cfg *eth.Config) { cfg.UltraLightFraction = ctx.GlobalInt(UltraLightFractionFlag.Name) } if cfg.UltraLightFraction <= 0 && cfg.UltraLightFraction > 100 { - log.Error("Ultra light fraction is invalid", "had", cfg.UltraLightFraction, "updated", eth.DefaultConfig.UltraLightFraction) - cfg.UltraLightFraction = eth.DefaultConfig.UltraLightFraction + log.Error("Ultra light fraction is invalid", "had", cfg.UltraLightFraction, "updated", ethconfig.Defaults.UltraLightFraction) + cfg.UltraLightFraction = ethconfig.Defaults.UltraLightFraction } if ctx.GlobalIsSet(UltraLightOnlyAnnounceFlag.Name) { cfg.UltraLightOnlyAnnounce = ctx.GlobalBool(UltraLightOnlyAnnounceFlag.Name) @@ -1098,11 +1095,14 @@ func setLes(ctx *cli.Context, cfg *eth.Config) { if ctx.GlobalIsSet(LightNoPruneFlag.Name) { cfg.LightNoPrune = ctx.GlobalBool(LightNoPruneFlag.Name) } + if ctx.GlobalIsSet(LightNoSyncServeFlag.Name) { + cfg.LightNoSyncServe = ctx.GlobalBool(LightNoSyncServeFlag.Name) + } } -// makeDatabaseHandles raises out the number of allowed file handles per process +// 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 { limit, err := fdlimit.Maximum() if err != nil { Fatalf("Failed to retrieve file descriptor allowance: %v", err) @@ -1141,14 +1141,9 @@ func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error // setEtherbase retrieves the etherbase either from the directly specified // command line flags or from the keystore if CLI indexed. -func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) { - // Extract the current etherbase, new flag overriding legacy one +func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *ethconfig.Config) { + // Extract the current etherbase var etherbase string - if ctx.GlobalIsSet(LegacyMinerEtherbaseFlag.Name) { - etherbase = ctx.GlobalString(LegacyMinerEtherbaseFlag.Name) - log.Warn("The flag --etherbase is deprecated and will be removed in the future, please use --miner.etherbase") - - } if ctx.GlobalIsSet(MinerEtherbaseFlag.Name) { etherbase = ctx.GlobalString(MinerEtherbaseFlag.Name) } @@ -1192,27 +1187,24 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { setBootstrapNodesV5(ctx, cfg) lightClient := ctx.GlobalString(SyncModeFlag.Name) == "light" - lightServer := (ctx.GlobalInt(LegacyLightServFlag.Name) != 0 || ctx.GlobalInt(LightServeFlag.Name) != 0) + lightServer := (ctx.GlobalInt(LightServeFlag.Name) != 0) - lightPeers := ctx.GlobalInt(LegacyLightPeersFlag.Name) - if ctx.GlobalIsSet(LightMaxPeersFlag.Name) { - lightPeers = ctx.GlobalInt(LightMaxPeersFlag.Name) - } - if lightClient && !ctx.GlobalIsSet(LegacyLightPeersFlag.Name) && !ctx.GlobalIsSet(LightMaxPeersFlag.Name) { + lightPeers := ctx.GlobalInt(LightMaxPeersFlag.Name) + if lightClient && !ctx.GlobalIsSet(LightMaxPeersFlag.Name) { // dynamic default - for clients we use 1/10th of the default for servers lightPeers /= 10 } if ctx.GlobalIsSet(MaxPeersFlag.Name) { cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name) - if lightServer && !ctx.GlobalIsSet(LegacyLightPeersFlag.Name) && !ctx.GlobalIsSet(LightMaxPeersFlag.Name) { + if lightServer && !ctx.GlobalIsSet(LightMaxPeersFlag.Name) { cfg.MaxPeers += lightPeers } } else { if lightServer { cfg.MaxPeers += lightPeers } - if lightClient && (ctx.GlobalIsSet(LegacyLightPeersFlag.Name) || ctx.GlobalIsSet(LightMaxPeersFlag.Name)) && cfg.MaxPeers < lightPeers { + if lightClient && ctx.GlobalIsSet(LightMaxPeersFlag.Name) && cfg.MaxPeers < lightPeers { cfg.MaxPeers = lightPeers } } @@ -1280,8 +1272,11 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(LightKDFFlag.Name) { cfg.UseLightweightKDF = ctx.GlobalBool(LightKDFFlag.Name) } - if ctx.GlobalIsSet(NoUSBFlag.Name) { - cfg.NoUSB = ctx.GlobalBool(NoUSBFlag.Name) + if ctx.GlobalIsSet(NoUSBFlag.Name) || cfg.NoUSB { + log.Warn("Option nousb is deprecated and USB is deactivated by default. Use --usb to enable") + } + if ctx.GlobalIsSet(USBFlag.Name) { + cfg.USB = ctx.GlobalBool(USBFlag.Name) } if ctx.GlobalIsSet(InsecureUnlockAllowedFlag.Name) { cfg.InsecureUnlockAllowed = ctx.GlobalBool(InsecureUnlockAllowedFlag.Name) @@ -1322,7 +1317,7 @@ func dataDirPathForCtxChainConfig(ctx *cli.Context, baseDataDirPath string) stri return filepath.Join(baseDataDirPath, "kotti") case ctx.GlobalBool(GoerliFlag.Name): return filepath.Join(baseDataDirPath, "goerli") - case ctx.GlobalBool(YoloV2Flag.Name): + case ctx.GlobalBool(YoloV3Flag.Name): return filepath.Join(baseDataDirPath, "yolo-v2") } return baseDataDirPath @@ -1336,7 +1331,7 @@ func setDataDir(ctx *cli.Context, cfg *node.Config) { case ctx.GlobalBool(DeveloperFlag.Name) || ctx.GlobalBool(DeveloperPoWFlag.Name): cfg.DataDir = "" // unless explicitly requested, use memory databases - case (ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name)) && cfg.DataDir == vars.DefaultDataDir(): + case ctx.GlobalBool(RopstenFlag.Name) && cfg.DataDir == vars.DefaultDataDir(): // Maintain compatibility with older Geth configurations storing the // Ropsten database in `testnet` instead of `ropsten`. @@ -1361,20 +1356,12 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) { // If we are running the light client, apply another group // settings for gas oracle. if light { - cfg.Blocks = eth.DefaultLightGPOConfig.Blocks - cfg.Percentile = eth.DefaultLightGPOConfig.Percentile - } - if ctx.GlobalIsSet(LegacyGpoBlocksFlag.Name) { - cfg.Blocks = ctx.GlobalInt(LegacyGpoBlocksFlag.Name) - log.Warn("The flag --gpoblocks is deprecated and will be removed in the future, please use --gpo.blocks") + cfg.Blocks = ethconfig.LightClientGPO.Blocks + cfg.Percentile = ethconfig.LightClientGPO.Percentile } if ctx.GlobalIsSet(GpoBlocksFlag.Name) { cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name) } - if ctx.GlobalIsSet(LegacyGpoPercentileFlag.Name) { - cfg.Percentile = ctx.GlobalInt(LegacyGpoPercentileFlag.Name) - log.Warn("The flag --gpopercentile is deprecated and will be removed in the future, please use --gpo.percentile") - } if ctx.GlobalIsSet(GpoPercentileFlag.Name) { cfg.Percentile = ctx.GlobalInt(GpoPercentileFlag.Name) } @@ -1426,12 +1413,12 @@ func setTxPool(ctx *cli.Context, cfg *core.TxPoolConfig) { } } -func setEthashDatasetDir(ctx *cli.Context, cfg *eth.Config) { +func setEthashDatasetDir(ctx *cli.Context, cfg *ethconfig.Config) { switch { case ctx.GlobalIsSet(EthashDatasetDirFlag.Name): cfg.Ethash.DatasetDir = ctx.GlobalString(EthashDatasetDirFlag.Name) - case (ctx.GlobalBool(ClassicFlag.Name) || ctx.GlobalBool(MordorFlag.Name)) && cfg.Ethash.DatasetDir == eth.DefaultConfig.Ethash.DatasetDir: + case (ctx.GlobalBool(ClassicFlag.Name) || ctx.GlobalBool(MordorFlag.Name)) && cfg.Ethash.DatasetDir == ethconfig.Defaults.Ethash.DatasetDir: // ECIP-1099 is set, use etchash dir for DAGs instead home := HomeDir() if runtime.GOOS == "darwin" { @@ -1454,7 +1441,7 @@ func setEthashCacheDir(ctx *cli.Context, cfg *eth.Config) { case ctx.GlobalIsSet(EthashCacheDirFlag.Name): cfg.Ethash.CacheDir = ctx.GlobalString(EthashCacheDirFlag.Name) - case (ctx.GlobalBool(ClassicFlag.Name) || ctx.GlobalBool(MordorFlag.Name)) && cfg.Ethash.CacheDir == eth.DefaultConfig.Ethash.CacheDir: + case (ctx.GlobalBool(ClassicFlag.Name) || ctx.GlobalBool(MordorFlag.Name)) && cfg.Ethash.CacheDir == ethconfig.Defaults.Ethash.CacheDir: // ECIP-1099 is set, use etchash dir for caches instead cfg.Ethash.CacheDir = "etchash" } @@ -1492,27 +1479,16 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) { if ctx.GlobalIsSet(MinerNotifyFlag.Name) { cfg.Notify = strings.Split(ctx.GlobalString(MinerNotifyFlag.Name), ",") } - if ctx.GlobalIsSet(LegacyMinerExtraDataFlag.Name) { - cfg.ExtraData = []byte(ctx.GlobalString(LegacyMinerExtraDataFlag.Name)) - log.Warn("The flag --extradata is deprecated and will be removed in the future, please use --miner.extradata") - } + cfg.NotifyFull = ctx.GlobalBool(MinerNotifyFullFlag.Name) if ctx.GlobalIsSet(MinerExtraDataFlag.Name) { cfg.ExtraData = []byte(ctx.GlobalString(MinerExtraDataFlag.Name)) } - if ctx.GlobalIsSet(LegacyMinerGasTargetFlag.Name) { - cfg.GasFloor = ctx.GlobalUint64(LegacyMinerGasTargetFlag.Name) - log.Warn("The flag --targetgaslimit is deprecated and will be removed in the future, please use --miner.gastarget") - } if ctx.GlobalIsSet(MinerGasTargetFlag.Name) { cfg.GasFloor = ctx.GlobalUint64(MinerGasTargetFlag.Name) } if ctx.GlobalIsSet(MinerGasLimitFlag.Name) { cfg.GasCeil = ctx.GlobalUint64(MinerGasLimitFlag.Name) } - if ctx.GlobalIsSet(LegacyMinerGasPriceFlag.Name) { - cfg.GasPrice = GlobalBig(ctx, LegacyMinerGasPriceFlag.Name) - log.Warn("The flag --gasprice is deprecated and will be removed in the future, please use --miner.gasprice") - } if ctx.GlobalIsSet(MinerGasPriceFlag.Name) { cfg.GasPrice = GlobalBig(ctx, MinerGasPriceFlag.Name) } @@ -1524,7 +1500,7 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) { } } -func setWhitelist(ctx *cli.Context, cfg *eth.Config) { +func setWhitelist(ctx *cli.Context, cfg *ethconfig.Config) { whitelist := ctx.GlobalString(WhitelistFlag.Name) if whitelist == "" { return @@ -1588,27 +1564,24 @@ func CheckExclusive(ctx *cli.Context, args ...interface{}) { } } -// SetShhConfig applies shh-related command line flags to the config. -func SetShhConfig(ctx *cli.Context, stack *node.Node) { - if ctx.GlobalIsSet(WhisperEnabledFlag.Name) || - ctx.GlobalIsSet(WhisperMaxMessageSizeFlag.Name) || - ctx.GlobalIsSet(WhisperMinPOWFlag.Name) || - ctx.GlobalIsSet(WhisperRestrictConnectionBetweenLightClientsFlag.Name) { - log.Warn("Whisper support has been deprecated and the code has been moved to github.com/ethereum/whisper") - } -} - // SetEthConfig applies eth-related command line flags to the config. -func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { +func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // Avoid conflicting network flags - CheckExclusive(ctx, DeveloperFlag, DeveloperPoWFlag, LegacyTestnetFlag, MainnetFlag, RopstenFlag, RinkebyFlag, GoerliFlag, YoloV2Flag, ClassicFlag, KottiFlag, MordorFlag) - CheckExclusive(ctx, LegacyLightServFlag, LightServeFlag, SyncModeFlag, "light") + CheckExclusive(ctx, DeveloperFlag, DeveloperPoWFlag, MainnetFlag, RopstenFlag, RinkebyFlag, GoerliFlag, YoloV3Flag, ClassicFlag, KottiFlag, MordorFlag) + CheckExclusive(ctx, LightServeFlag, SyncModeFlag, "light") CheckExclusive(ctx, DeveloperFlag, DeveloperPoWFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer CheckExclusive(ctx, GCModeFlag, "archive", TxLookupLimitFlag) + if ctx.GlobalString(GCModeFlag.Name) == "archive" && ctx.GlobalUint64(TxLookupLimitFlag.Name) != 0 { + ctx.GlobalSet(TxLookupLimitFlag.Name, "0") + log.Warn("Disable transaction unindexing for archive node") + } + if ctx.GlobalIsSet(LightServeFlag.Name) && ctx.GlobalUint64(TxLookupLimitFlag.Name) != 0 { + log.Warn("LES server cannot serve old transaction status and cannot connect below les/4 protocol version if transaction lookup index is limited") + } // todo(rjl493456442) make it available for les server // Ancient tx indices pruning is not available for les server now // since light client relies on the server for transaction status query. - CheckExclusive(ctx, LegacyLightServFlag, LightServeFlag, TxLookupLimitFlag) + CheckExclusive(ctx, LightServeFlag, TxLookupLimitFlag) CheckExclusive(ctx, AncientFlag, AncientRPCFlag) CheckExclusive(ctx, DeveloperPoWFlag, DeveloperPeriodFlag, FakePoWFlag) @@ -1626,13 +1599,33 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { setWhitelist(ctx, cfg) setLes(ctx, cfg) + // Cap the cache allowance and tune the garbage collector + mem, err := gopsutil.VirtualMemory() + if err == nil { + if 32<<(^uintptr(0)>>63) == 32 && mem.Total > 2*1024*1024*1024 { + log.Warn("Lowering memory allowance on 32bit arch", "available", mem.Total/1024/1024, "addressable", 2*1024) + mem.Total = 2 * 1024 * 1024 * 1024 + } + allowance := int(mem.Total / 1024 / 1024 / 3) + if cache := ctx.GlobalInt(CacheFlag.Name); cache > allowance { + log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance) + ctx.GlobalSet(CacheFlag.Name, strconv.Itoa(allowance)) + } + } + // Ensure Go's GC ignores the database cache for trigger percentage + cache := ctx.GlobalInt(CacheFlag.Name) + gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024))) + + log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc)) + godebug.SetGCPercent(int(gogc)) + if ctx.GlobalIsSet(SyncModeFlag.Name) { cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode) } if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheDatabaseFlag.Name) { cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100 } - cfg.DatabaseHandles = makeDatabaseHandles() + cfg.DatabaseHandles = MakeDatabaseHandles() if ctx.GlobalIsSet(AncientFlag.Name) { cfg.DatabaseFreezer = ctx.GlobalString(AncientFlag.Name) } @@ -1649,6 +1642,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { if ctx.GlobalIsSet(CacheNoPrefetchFlag.Name) { cfg.NoPrefetch = ctx.GlobalBool(CacheNoPrefetchFlag.Name) } + // Read the value from the flag no matter if it's set or not. + cfg.Preimages = ctx.GlobalBool(CachePreimagesFlag.Name) + if cfg.NoPruning && !cfg.Preimages { + cfg.Preimages = true + log.Info("Enabling recording of key preimages since archive mode is used") + } if ctx.GlobalIsSet(TxLookupLimitFlag.Name) { cfg.TxLookupLimit = ctx.GlobalUint64(TxLookupLimitFlag.Name) } @@ -1667,9 +1666,14 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheSnapshotFlag.Name) { cfg.SnapshotCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheSnapshotFlag.Name) / 100 } - if !ctx.GlobalIsSet(SnapshotFlag.Name) { - cfg.TrieCleanCache += cfg.SnapshotCache - cfg.SnapshotCache = 0 // Disabled + if !ctx.GlobalBool(SnapshotFlag.Name) { + // If snap-sync is requested, this flag is also required + if cfg.SyncMode == downloader.SnapSync { + log.Info("Snap sync requested, enabling --snapshot") + } else { + cfg.TrieCleanCache += cfg.SnapshotCache + cfg.SnapshotCache = 0 // Disabled + } } if ctx.GlobalIsSet(DocRootFlag.Name) { cfg.DocRoot = ctx.GlobalString(DocRootFlag.Name) @@ -1699,15 +1703,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { if ctx.GlobalIsSet(RPCGlobalTxFeeCapFlag.Name) { cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCapFlag.Name) } - if ctx.GlobalIsSet(DNSDiscoveryFlag.Name) { + if ctx.GlobalIsSet(NoDiscoverFlag.Name) { + cfg.EthDiscoveryURLs, cfg.SnapDiscoveryURLs = []string{}, []string{} + } else if ctx.GlobalIsSet(DNSDiscoveryFlag.Name) { urls := ctx.GlobalString(DNSDiscoveryFlag.Name) if urls == "" { - cfg.DiscoveryURLs = []string{} + cfg.EthDiscoveryURLs = []string{} } else { - cfg.DiscoveryURLs = SplitAndTrim(urls) + cfg.EthDiscoveryURLs = SplitAndTrim(urls) } } - // Override any default configs for hard coded networks. // Override genesis configuration if a -- flag. @@ -1768,7 +1773,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { cfg.NetworkId = 1 } SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash) - case ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name): + case ctx.GlobalBool(RopstenFlag.Name): SetDNSDiscoveryDefaults(cfg, params.RopstenGenesisHash) case ctx.GlobalBool(RinkebyFlag.Name): SetDNSDiscoveryDefaults(cfg, params.RinkebyGenesisHash) @@ -1780,6 +1785,8 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { SetDNSDiscoveryDefaults2(cfg, params.KottiDNSNetwork1) case ctx.GlobalBool(MordorFlag.Name): SetDNSDiscoveryDefaults2(cfg, params.MordorDNSNetwork1) + case ctx.GlobalBool(YoloV3Flag.Name): + SetDNSDiscoveryDefaults(cfg, params.YoloV3GenesisHash) default: // No -- flag was given. } @@ -1822,66 +1829,71 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { if ctx.GlobalIsSet(DataDirFlag.Name) { // Check if we have an already initialized chain and fall back to // that if so. Otherwise we need to generate a new genesis spec. - chaindb := MakeChainDatabase(ctx, stack) + chaindb := MakeChainDatabase(ctx, stack, true) if rawdb.ReadCanonicalHash(chaindb, 0) != (common.Hash{}) { cfg.Genesis = nil // fallback to db content } chaindb.Close() } - if !ctx.GlobalIsSet(MinerGasPriceFlag.Name) && !ctx.GlobalIsSet(LegacyMinerGasPriceFlag.Name) { + if !ctx.GlobalIsSet(MinerGasPriceFlag.Name) { cfg.Miner.GasPrice = big.NewInt(1) } } } // SetDNSDiscoveryDefaults2 configures DNS discovery with the given URL if no URLs are set. -func SetDNSDiscoveryDefaults2(cfg *eth.Config, url string) { - if cfg.DiscoveryURLs != nil { +func SetDNSDiscoveryDefaults2(cfg *ethconfig.Config, url string) { + if cfg.EthDiscoveryURLs != nil { return } if cfg.SyncMode == downloader.LightSync { url = strings.Replace(url, "@all.", "@les.", 1) } - cfg.DiscoveryURLs = []string{url} + cfg.EthDiscoveryURLs = []string{url} } // SetDNSDiscoveryDefaults configures DNS discovery with the given URL if // no URLs are set. -func SetDNSDiscoveryDefaults(cfg *eth.Config, genesis common.Hash) { - if cfg.DiscoveryURLs != nil { +func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) { + if cfg.EthDiscoveryURLs != nil { return // already set through flags/config } - protocol := "all" if cfg.SyncMode == downloader.LightSync { protocol = "les" } if url := params.KnownDNSNetwork(genesis, protocol); url != "" { - cfg.DiscoveryURLs = []string{url} + cfg.EthDiscoveryURLs = []string{url} + } + if cfg.SyncMode == downloader.SnapSync { + if url := params.KnownDNSNetwork(genesis, "snap"); url != "" { + cfg.SnapDiscoveryURLs = []string{url} + } } } // RegisterEthService adds an Ethereum client to the stack. -func RegisterEthService(stack *node.Node, cfg *eth.Config) ethapi.Backend { +func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) ethapi.Backend { if cfg.SyncMode == downloader.LightSync { backend, err := les.New(stack, cfg) if err != nil { Fatalf("Failed to register the Ethereum service: %v", err) } + stack.RegisterAPIs(tracers.APIs(backend.ApiBackend)) return backend.ApiBackend - } else { - backend, err := eth.New(stack, cfg) + } + backend, err := eth.New(stack, cfg) + if err != nil { + Fatalf("Failed to register the Ethereum service: %v", err) + } + if cfg.LightServ > 0 { + _, err := les.NewLesServer(stack, backend, cfg) if err != nil { - Fatalf("Failed to register the Ethereum service: %v", err) - } - if cfg.LightServ > 0 { - _, err := les.NewLesServer(stack, backend, cfg) - if err != nil { - Fatalf("Failed to create the LES server: %v", err) - } + Fatalf("Failed to create the LES server: %v", err) } - return backend.APIBackend } + stack.RegisterAPIs(tracers.APIs(backend.APIBackend)) + return backend.APIBackend } // RegisterEthStatsService configures the Ethereum Stats daemon and adds it to @@ -1945,10 +1957,10 @@ func SplitTagsFlag(tagsFlag string) map[string]string { } // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. -func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { +func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb.Database { var ( cache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100 - handles = makeDatabaseHandles() + handles = MakeDatabaseHandles() err error chainDb ethdb.Database @@ -1959,9 +1971,9 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { name = "lightchaindata" } if ctx.GlobalIsSet(AncientRPCFlag.Name) { - chainDb, err = stack.OpenDatabaseWithFreezerRemote(name, cache, handles, ctx.GlobalString(AncientRPCFlag.Name)) + chainDb, err = stack.OpenDatabaseWithFreezerRemote(name, cache, handles, ctx.GlobalString(AncientRPCFlag.Name), readonly) } else { - chainDb, err = stack.OpenDatabaseWithFreezer(name, cache, handles, ctx.GlobalString(AncientFlag.Name), "") + chainDb, err = stack.OpenDatabaseWithFreezer(name, cache, handles, ctx.GlobalString(AncientFlag.Name), "", readonly) } if err != nil { Fatalf("Could not open database: %v", err) @@ -1982,7 +1994,7 @@ func genesisForCtxChainConfig(ctx *cli.Context) *genesisT.Genesis { genesis = params.DefaultClassicGenesisBlock() case ctx.GlobalBool(MordorFlag.Name): genesis = params.DefaultMordorGenesisBlock() - case ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name): + case ctx.GlobalBool(RopstenFlag.Name): genesis = params.DefaultRopstenGenesisBlock() case ctx.GlobalBool(RinkebyFlag.Name): genesis = params.DefaultRinkebyGenesisBlock() @@ -1990,8 +2002,8 @@ func genesisForCtxChainConfig(ctx *cli.Context) *genesisT.Genesis { genesis = params.DefaultKottiGenesisBlock() case ctx.GlobalBool(GoerliFlag.Name): genesis = params.DefaultGoerliGenesisBlock() - case ctx.GlobalBool(YoloV2Flag.Name): - genesis = params.DefaultYoloV2GenesisBlock() + case ctx.GlobalBool(YoloV3Flag.Name): + genesis = params.DefaultYoloV3GenesisBlock() } return genesis } @@ -2004,9 +2016,9 @@ func MakeGenesis(ctx *cli.Context) *genesisT.Genesis { } // MakeChain creates a chain manager from set command line flags. -func MakeChain(ctx *cli.Context, stack *node.Node, readOnly bool) (chain *core.BlockChain, chainDb ethdb.Database) { +func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) { var err error - chainDb = MakeChainDatabase(ctx, stack) + chainDb = MakeChainDatabase(ctx, stack, false) // TODO(rjl493456442) support read-only database config, _, err := core.SetupGenesisBlock(chainDb, MakeGenesis(ctx)) if err != nil { Fatalf("%v", err) @@ -2023,14 +2035,14 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readOnly bool) (chain *core.B engine = ethash.NewPoissonFaker() } else if !ctx.GlobalBool(FakePoWFlag.Name) { engine = ethash.New(ethash.Config{ - CacheDir: stack.ResolvePath(eth.DefaultConfig.Ethash.CacheDir), - CachesInMem: eth.DefaultConfig.Ethash.CachesInMem, - CachesOnDisk: eth.DefaultConfig.Ethash.CachesOnDisk, - CachesLockMmap: eth.DefaultConfig.Ethash.CachesLockMmap, - DatasetDir: stack.ResolvePath(eth.DefaultConfig.Ethash.DatasetDir), - DatasetsInMem: eth.DefaultConfig.Ethash.DatasetsInMem, - DatasetsOnDisk: eth.DefaultConfig.Ethash.DatasetsOnDisk, - DatasetsLockMmap: eth.DefaultConfig.Ethash.DatasetsLockMmap, + CacheDir: stack.ResolvePath(ethconfig.Defaults.Ethash.CacheDir), + CachesInMem: ethconfig.Defaults.Ethash.CachesInMem, + CachesOnDisk: ethconfig.Defaults.Ethash.CachesOnDisk, + CachesLockMmap: ethconfig.Defaults.Ethash.CachesLockMmap, + DatasetDir: stack.ResolvePath(ethconfig.Defaults.Ethash.DatasetDir), + DatasetsInMem: ethconfig.Defaults.Ethash.DatasetsInMem, + DatasetsOnDisk: ethconfig.Defaults.Ethash.DatasetsOnDisk, + DatasetsLockMmap: ethconfig.Defaults.Ethash.DatasetsLockMmap, ECIP1099Block: config.GetEthashECIP1099Transition(), }, nil, false) } @@ -2039,14 +2051,19 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readOnly bool) (chain *core.B Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name) } cache := &core.CacheConfig{ - TrieCleanLimit: eth.DefaultConfig.TrieCleanCache, + TrieCleanLimit: ethconfig.Defaults.TrieCleanCache, TrieCleanNoPrefetch: ctx.GlobalBool(CacheNoPrefetchFlag.Name), - TrieDirtyLimit: eth.DefaultConfig.TrieDirtyCache, + TrieDirtyLimit: ethconfig.Defaults.TrieDirtyCache, TrieDirtyDisabled: ctx.GlobalString(GCModeFlag.Name) == "archive", - TrieTimeLimit: eth.DefaultConfig.TrieTimeout, - SnapshotLimit: eth.DefaultConfig.SnapshotCache, + TrieTimeLimit: ethconfig.Defaults.TrieTimeout, + SnapshotLimit: ethconfig.Defaults.SnapshotCache, + Preimages: ctx.GlobalBool(CachePreimagesFlag.Name), + } + if cache.TrieDirtyDisabled && !cache.Preimages { + cache.Preimages = true + log.Info("Enabling recording of key preimages since archive mode is used") } - if !ctx.GlobalIsSet(SnapshotFlag.Name) { + if !ctx.GlobalBool(SnapshotFlag.Name) { cache.SnapshotLimit = 0 // Disabled } if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheTrieFlag.Name) { @@ -2056,12 +2073,10 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readOnly bool) (chain *core.B cache.TrieDirtyLimit = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100 } vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)} - var limit *uint64 - if ctx.GlobalIsSet(TxLookupLimitFlag.Name) && !readOnly { - l := ctx.GlobalUint64(TxLookupLimitFlag.Name) - limit = &l - } - chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, limit) + + // TODO(rjl493456442) disable snapshot generation/wiping if the chain is read only. + // Disable transaction indexing/unindexing by default. + chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, nil) if err != nil { Fatalf("Can't create BlockChain: %v", err) } diff --git a/cmd/utils/flags_legacy.go b/cmd/utils/flags_legacy.go index 6dc99a968a..fb5fde6576 100644 --- a/cmd/utils/flags_legacy.go +++ b/cmd/utils/flags_legacy.go @@ -20,7 +20,6 @@ import ( "fmt" "strings" - "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/node" "gopkg.in/urfave/cli.v1" ) @@ -34,143 +33,39 @@ var ShowDeprecated = cli.Command{ Description: "Show flags that have been deprecated and will soon be removed", } -var DeprecatedFlags = []cli.Flag{ - LegacyTestnetFlag, - LegacyLightServFlag, - LegacyLightPeersFlag, - LegacyMinerThreadsFlag, - LegacyMinerGasTargetFlag, - LegacyMinerGasPriceFlag, - LegacyMinerEtherbaseFlag, - LegacyMinerExtraDataFlag, -} +var DeprecatedFlags = []cli.Flag{} var ( - // (Deprecated April 2018) - LegacyMinerThreadsFlag = cli.IntFlag{ - Name: "minerthreads", - Usage: "Number of CPU threads to use for mining (deprecated, use --miner.threads)", - Value: 0, - } - LegacyMinerGasTargetFlag = cli.Uint64Flag{ - Name: "targetgaslimit", - Usage: "Target gas floor for mined blocks (deprecated, use --miner.gastarget)", - Value: eth.DefaultConfig.Miner.GasFloor, - } - LegacyMinerGasPriceFlag = BigFlag{ - Name: "gasprice", - Usage: "Minimum gas price for mining a transaction (deprecated, use --miner.gasprice)", - Value: eth.DefaultConfig.Miner.GasPrice, - } - LegacyMinerEtherbaseFlag = cli.StringFlag{ - Name: "etherbase", - Usage: "Public address for block mining rewards (default = first account, deprecated, use --miner.etherbase)", - Value: "0", - } - LegacyMinerExtraDataFlag = cli.StringFlag{ - Name: "extradata", - Usage: "Block extra data set by the miner (default = client version, deprecated, use --miner.extradata)", - } - - // (Deprecated June 2019) - LegacyLightServFlag = cli.IntFlag{ - Name: "lightserv", - Usage: "Maximum percentage of time allowed for serving LES requests (deprecated, use --light.serve)", - Value: eth.DefaultConfig.LightServ, - } - LegacyLightPeersFlag = cli.IntFlag{ - Name: "lightpeers", - Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated, use --light.maxpeers)", - Value: eth.DefaultConfig.LightPeers, - } - - // (Deprecated April 2020) - LegacyTestnetFlag = cli.BoolFlag{ // TODO(q9f): Remove after Ropsten is discontinued. - Name: "testnet", - Usage: "Pre-configured test network (Deprecated: Please choose one of --kotti, --mordor, --goerli, --rinkeby, or --ropsten.)", - } - // (Deprecated May 2020, shown in aliased flags section) LegacyRPCEnabledFlag = cli.BoolFlag{ Name: "rpc", - Usage: "Enable the HTTP-RPC server (deprecated, use --http)", + Usage: "Enable the HTTP-RPC server (deprecated and will be removed June 2021, use --http)", } LegacyRPCListenAddrFlag = cli.StringFlag{ Name: "rpcaddr", - Usage: "HTTP-RPC server listening interface (deprecated, use --http.addr)", + Usage: "HTTP-RPC server listening interface (deprecated and will be removed June 2021, use --http.addr)", Value: node.DefaultHTTPHost, } LegacyRPCPortFlag = cli.IntFlag{ Name: "rpcport", - Usage: "HTTP-RPC server listening port (deprecated, use --http.port)", + Usage: "HTTP-RPC server listening port (deprecated and will be removed June 2021, use --http.port)", Value: node.DefaultHTTPPort, } LegacyRPCCORSDomainFlag = cli.StringFlag{ Name: "rpccorsdomain", - Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced) (deprecated, use --http.corsdomain)", + Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced) (deprecated and will be removed June 2021, use --http.corsdomain)", Value: "", } LegacyRPCVirtualHostsFlag = cli.StringFlag{ Name: "rpcvhosts", - Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (deprecated, use --http.vhosts)", + Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (deprecated and will be removed June 2021, use --http.vhosts)", Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","), } LegacyRPCApiFlag = cli.StringFlag{ Name: "rpcapi", - Usage: "API's offered over the HTTP-RPC interface (deprecated, use --http.api)", - Value: "", - } - LegacyWSListenAddrFlag = cli.StringFlag{ - Name: "wsaddr", - Usage: "WS-RPC server listening interface (deprecated, use --ws.addr)", - Value: node.DefaultWSHost, - } - LegacyWSPortFlag = cli.IntFlag{ - Name: "wsport", - Usage: "WS-RPC server listening port (deprecated, use --ws.port)", - Value: node.DefaultWSPort, - } - LegacyWSApiFlag = cli.StringFlag{ - Name: "wsapi", - Usage: "API's offered over the WS-RPC interface (deprecated, use --ws.api)", + Usage: "API's offered over the HTTP-RPC interface (deprecated and will be removed June 2021, use --http.api)", Value: "", } - LegacyWSAllowedOriginsFlag = cli.StringFlag{ - Name: "wsorigins", - Usage: "Origins from which to accept websockets requests (deprecated, use --ws.origins)", - Value: "", - } - LegacyGpoBlocksFlag = cli.IntFlag{ - Name: "gpoblocks", - Usage: "Number of recent blocks to check for gas prices (deprecated, use --gpo.blocks)", - Value: eth.DefaultConfig.GPO.Blocks, - } - LegacyGpoPercentileFlag = cli.IntFlag{ - Name: "gpopercentile", - Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices (deprecated, use --gpo.percentile)", - Value: eth.DefaultConfig.GPO.Percentile, - } - LegacyBootnodesV4Flag = cli.StringFlag{ - Name: "bootnodesv4", - Usage: "Comma separated enode URLs for P2P v4 discovery bootstrap (light server, full nodes) (deprecated, use --bootnodes)", - Value: "", - } - LegacyBootnodesV5Flag = cli.StringFlag{ - Name: "bootnodesv5", - Usage: "Comma separated enode URLs for P2P v5 discovery bootstrap (light server, light nodes) (deprecated, use --bootnodes)", - Value: "", - } - - // (Deprecated July 2020, shown in aliased flags section) - LegacyGraphQLListenAddrFlag = cli.StringFlag{ - Name: "graphql.addr", - Usage: "GraphQL server listening interface (deprecated, graphql can only be enabled on the HTTP-RPC server endpoint, use --graphql)", - } - LegacyGraphQLPortFlag = cli.IntFlag{ - Name: "graphql.port", - Usage: "GraphQL server listening port (deprecated, graphql can only be enabled on the HTTP-RPC server endpoint, use --graphql)", - Value: node.DefaultHTTPPort, - } ) // showDeprecated displays deprecated flags that will be soon removed from the codebase. @@ -179,8 +74,7 @@ func showDeprecated(*cli.Context) { fmt.Println("The following flags are deprecated and will be removed in the future!") fmt.Println("--------------------------------------------------------------------") fmt.Println() - - for _, flag := range DeprecatedFlags { - fmt.Println(flag.String()) - } + // TODO remove when there are newly deprecated flags + fmt.Println("no deprecated flags to show at this time") + fmt.Println() } diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index b689f258a3..01de3d4c65 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -44,6 +44,20 @@ type solcOutput struct { Version string } +// solidity v.0.8 changes the way ABI, Devdoc and Userdoc are serialized +type solcOutputV8 struct { + Contracts map[string]struct { + BinRuntime string `json:"bin-runtime"` + SrcMapRuntime string `json:"srcmap-runtime"` + Bin, SrcMap, Metadata string + Abi interface{} + Devdoc interface{} + Userdoc interface{} + Hashes map[string]string + } + Version string +} + func (s *Solidity) makeArgs() []string { p := []string{ "--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc", @@ -125,7 +139,6 @@ func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, erro if err := cmd.Run(); err != nil { return nil, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) } - return ParseCombinedJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " ")) } @@ -141,7 +154,8 @@ func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, erro func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { var output solcOutput if err := json.Unmarshal(combinedJSON, &output); err != nil { - return nil, err + // Try to parse the output with the new solidity v.0.8.0 rules + return parseCombinedJSONV8(combinedJSON, source, languageVersion, compilerVersion, compilerOptions) } // Compilation succeeded, assemble and return the contracts. contracts := make(map[string]*Contract) @@ -176,3 +190,35 @@ func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion strin } return contracts, nil } + +// parseCombinedJSONV8 parses the direct output of solc --combined-output +// and parses it using the rules from solidity v.0.8.0 and later. +func parseCombinedJSONV8(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { + var output solcOutputV8 + if err := json.Unmarshal(combinedJSON, &output); err != nil { + return nil, err + } + // Compilation succeeded, assemble and return the contracts. + contracts := make(map[string]*Contract) + for name, info := range output.Contracts { + contracts[name] = &Contract{ + Code: "0x" + info.Bin, + RuntimeCode: "0x" + info.BinRuntime, + Hashes: info.Hashes, + Info: ContractInfo{ + Source: source, + Language: "Solidity", + LanguageVersion: languageVersion, + CompilerVersion: compilerVersion, + CompilerOptions: compilerOptions, + SrcMap: info.SrcMap, + SrcMapRuntime: info.SrcMapRuntime, + AbiDefinition: info.Abi, + UserDoc: info.Userdoc, + DeveloperDoc: info.Devdoc, + Metadata: info.Metadata, + }, + } + } + return contracts, nil +} diff --git a/common/hexutil/json_test.go b/common/hexutil/json_test.go index 8a6b8643a1..ed7d6fad1a 100644 --- a/common/hexutil/json_test.go +++ b/common/hexutil/json_test.go @@ -88,7 +88,7 @@ func TestUnmarshalBytes(t *testing.T) { if !checkError(t, test.input, err, test.wantErr) { continue } - if !bytes.Equal(test.want.([]byte), []byte(v)) { + if !bytes.Equal(test.want.([]byte), v) { t.Errorf("input %s: value mismatch: got %x, want %x", test.input, &v, test.want) continue } diff --git a/common/mclock/mclock.go b/common/mclock/mclock.go index 3aca257cb3..c05738cf2b 100644 --- a/common/mclock/mclock.go +++ b/common/mclock/mclock.go @@ -20,15 +20,19 @@ package mclock import ( "time" - "github.com/aristanetworks/goarista/monotime" + _ "unsafe" // for go:linkname ) +//go:noescape +//go:linkname nanotime runtime.nanotime +func nanotime() int64 + // AbsTime represents absolute monotonic time. -type AbsTime time.Duration +type AbsTime int64 // Now returns the current absolute monotonic time. func Now() AbsTime { - return AbsTime(monotime.Now()) + return AbsTime(nanotime()) } // Add returns t + d as absolute time. @@ -74,7 +78,7 @@ type System struct{} // Now returns the current monotonic time. func (c System) Now() AbsTime { - return AbsTime(monotime.Now()) + return Now() } // Sleep blocks for the given duration. diff --git a/common/mclock/mclock.s b/common/mclock/mclock.s new file mode 100644 index 0000000000..99a7a878f0 --- /dev/null +++ b/common/mclock/mclock.s @@ -0,0 +1 @@ +// This file exists in order to be able to use go:linkname. diff --git a/common/prque/lazyqueue.go b/common/prque/lazyqueue.go index 52403df464..37c2f3bd42 100644 --- a/common/prque/lazyqueue.go +++ b/common/prque/lazyqueue.go @@ -48,14 +48,14 @@ type LazyQueue struct { } type ( - PriorityCallback func(data interface{}, now mclock.AbsTime) int64 // actual priority callback + PriorityCallback func(data interface{}) int64 // actual priority callback MaxPriorityCallback func(data interface{}, until mclock.AbsTime) int64 // estimated maximum priority callback ) // NewLazyQueue creates a new lazy queue func NewLazyQueue(setIndex SetIndexCallback, priority PriorityCallback, maxPriority MaxPriorityCallback, clock mclock.Clock, refreshPeriod time.Duration) *LazyQueue { q := &LazyQueue{ - popQueue: newSstack(nil), + popQueue: newSstack(nil, false), setIndex: setIndex, priority: priority, maxPriority: maxPriority, @@ -71,8 +71,8 @@ func NewLazyQueue(setIndex SetIndexCallback, priority PriorityCallback, maxPrior // Reset clears the contents of the queue func (q *LazyQueue) Reset() { - q.queue[0] = newSstack(q.setIndex0) - q.queue[1] = newSstack(q.setIndex1) + q.queue[0] = newSstack(q.setIndex0, false) + q.queue[1] = newSstack(q.setIndex1, false) } // Refresh performs queue re-evaluation if necessary @@ -139,11 +139,10 @@ func (q *LazyQueue) peekIndex() int { // Pop multiple times. Popped items are passed to the callback. MultiPop returns // when the callback returns false or there are no more items to pop. func (q *LazyQueue) MultiPop(callback func(data interface{}, priority int64) bool) { - now := q.clock.Now() nextIndex := q.peekIndex() for nextIndex != -1 { data := heap.Pop(q.queue[nextIndex]).(*item).value - heap.Push(q.popQueue, &item{data, q.priority(data, now)}) + heap.Push(q.popQueue, &item{data, q.priority(data)}) nextIndex = q.peekIndex() for q.popQueue.Len() != 0 && (nextIndex == -1 || q.queue[nextIndex].blocks[0][0].priority < q.popQueue.blocks[0][0].priority) { i := heap.Pop(q.popQueue).(*item) diff --git a/common/prque/lazyqueue_test.go b/common/prque/lazyqueue_test.go index be9491e24e..9a831d628b 100644 --- a/common/prque/lazyqueue_test.go +++ b/common/prque/lazyqueue_test.go @@ -40,7 +40,7 @@ type lazyItem struct { index int } -func testPriority(a interface{}, now mclock.AbsTime) int64 { +func testPriority(a interface{}) int64 { return a.(*lazyItem).p } diff --git a/common/prque/prque.go b/common/prque/prque.go index 3cc5a1adaf..54c78b5fc2 100755 --- a/common/prque/prque.go +++ b/common/prque/prque.go @@ -28,7 +28,12 @@ type Prque struct { // New creates a new priority queue. func New(setIndex SetIndexCallback) *Prque { - return &Prque{newSstack(setIndex)} + return &Prque{newSstack(setIndex, false)} +} + +// NewWrapAround creates a new priority queue with wrap-around priority handling. +func NewWrapAround(setIndex SetIndexCallback) *Prque { + return &Prque{newSstack(setIndex, true)} } // Pushes a value with a given priority into the queue, expanding if necessary. diff --git a/common/prque/prque_test.go b/common/prque/prque_test.go new file mode 100644 index 0000000000..1cffcebad4 --- /dev/null +++ b/common/prque/prque_test.go @@ -0,0 +1,130 @@ +// CookieJar - A contestant's algorithm toolbox +// Copyright (c) 2013 Peter Szilagyi. All rights reserved. +// +// CookieJar is dual licensed: use of this source code is governed by a BSD +// license that can be found in the LICENSE file. Alternatively, the CookieJar +// toolbox may be used in accordance with the terms and conditions contained +// in a signed written agreement between you and the author(s). + +package prque + +import ( + "math/rand" + "testing" +) + +func TestPrque(t *testing.T) { + // Generate a batch of random data and a specific priority order + size := 16 * blockSize + prio := rand.Perm(size) + data := make([]int, size) + for i := 0; i < size; i++ { + data[i] = rand.Int() + } + queue := New(nil) + for rep := 0; rep < 2; rep++ { + // Fill a priority queue with the above data + for i := 0; i < size; i++ { + queue.Push(data[i], int64(prio[i])) + if queue.Size() != i+1 { + t.Errorf("queue size mismatch: have %v, want %v.", queue.Size(), i+1) + } + } + // Create a map the values to the priorities for easier verification + dict := make(map[int64]int) + for i := 0; i < size; i++ { + dict[int64(prio[i])] = data[i] + } + // Pop out the elements in priority order and verify them + prevPrio := int64(size + 1) + for !queue.Empty() { + val, prio := queue.Pop() + if prio > prevPrio { + t.Errorf("invalid priority order: %v after %v.", prio, prevPrio) + } + prevPrio = prio + if val != dict[prio] { + t.Errorf("push/pop mismatch: have %v, want %v.", val, dict[prio]) + } + delete(dict, prio) + } + } +} + +func TestReset(t *testing.T) { + // Generate a batch of random data and a specific priority order + size := 16 * blockSize + prio := rand.Perm(size) + data := make([]int, size) + for i := 0; i < size; i++ { + data[i] = rand.Int() + } + queue := New(nil) + for rep := 0; rep < 2; rep++ { + // Fill a priority queue with the above data + for i := 0; i < size; i++ { + queue.Push(data[i], int64(prio[i])) + if queue.Size() != i+1 { + t.Errorf("queue size mismatch: have %v, want %v.", queue.Size(), i+1) + } + } + // Create a map the values to the priorities for easier verification + dict := make(map[int64]int) + for i := 0; i < size; i++ { + dict[int64(prio[i])] = data[i] + } + // Pop out half the elements in priority order and verify them + prevPrio := int64(size + 1) + for i := 0; i < size/2; i++ { + val, prio := queue.Pop() + if prio > prevPrio { + t.Errorf("invalid priority order: %v after %v.", prio, prevPrio) + } + prevPrio = prio + if val != dict[prio] { + t.Errorf("push/pop mismatch: have %v, want %v.", val, dict[prio]) + } + delete(dict, prio) + } + // Reset and ensure it's empty + queue.Reset() + if !queue.Empty() { + t.Errorf("priority queue not empty after reset: %v", queue) + } + } +} + +func BenchmarkPush(b *testing.B) { + // Create some initial data + data := make([]int, b.N) + prio := make([]int64, b.N) + for i := 0; i < len(data); i++ { + data[i] = rand.Int() + prio[i] = rand.Int63() + } + // Execute the benchmark + b.ResetTimer() + queue := New(nil) + for i := 0; i < len(data); i++ { + queue.Push(data[i], prio[i]) + } +} + +func BenchmarkPop(b *testing.B) { + // Create some initial data + data := make([]int, b.N) + prio := make([]int64, b.N) + for i := 0; i < len(data); i++ { + data[i] = rand.Int() + prio[i] = rand.Int63() + } + queue := New(nil) + for i := 0; i < len(data); i++ { + queue.Push(data[i], prio[i]) + } + // Execute the benchmark + b.ResetTimer() + for !queue.Empty() { + queue.Pop() + } +} diff --git a/common/prque/sstack.go b/common/prque/sstack.go index 8518af54ff..b06a95413d 100755 --- a/common/prque/sstack.go +++ b/common/prque/sstack.go @@ -31,22 +31,24 @@ type SetIndexCallback func(data interface{}, index int) // the stack (heap) functionality and the Len, Less and Swap methods for the // sortability requirements of the heaps. type sstack struct { - setIndex SetIndexCallback - size int - capacity int - offset int + setIndex SetIndexCallback + size int + capacity int + offset int + wrapAround bool blocks [][]*item active []*item } // Creates a new, empty stack. -func newSstack(setIndex SetIndexCallback) *sstack { +func newSstack(setIndex SetIndexCallback, wrapAround bool) *sstack { result := new(sstack) result.setIndex = setIndex result.active = make([]*item, blockSize) result.blocks = [][]*item{result.active} result.capacity = blockSize + result.wrapAround = wrapAround return result } @@ -94,7 +96,11 @@ func (s *sstack) Len() int { // Compares the priority of two elements of the stack (higher is first). // Required by sort.Interface. func (s *sstack) Less(i, j int) bool { - return (s.blocks[i/blockSize][i%blockSize].priority - s.blocks[j/blockSize][j%blockSize].priority) > 0 + a, b := s.blocks[i/blockSize][i%blockSize].priority, s.blocks[j/blockSize][j%blockSize].priority + if s.wrapAround { + return a-b > 0 + } + return a > b } // Swaps two elements in the stack. Required by sort.Interface. @@ -110,5 +116,5 @@ func (s *sstack) Swap(i, j int) { // Resets the stack, effectively clearing its contents. func (s *sstack) Reset() { - *s = *newSstack(s.setIndex) + *s = *newSstack(s.setIndex, false) } diff --git a/common/prque/sstack_test.go b/common/prque/sstack_test.go new file mode 100644 index 0000000000..bc6298979c --- /dev/null +++ b/common/prque/sstack_test.go @@ -0,0 +1,100 @@ +// CookieJar - A contestant's algorithm toolbox +// Copyright (c) 2013 Peter Szilagyi. All rights reserved. +// +// CookieJar is dual licensed: use of this source code is governed by a BSD +// license that can be found in the LICENSE file. Alternatively, the CookieJar +// toolbox may be used in accordance with the terms and conditions contained +// in a signed written agreement between you and the author(s). + +package prque + +import ( + "math/rand" + "sort" + "testing" +) + +func TestSstack(t *testing.T) { + // Create some initial data + size := 16 * blockSize + data := make([]*item, size) + for i := 0; i < size; i++ { + data[i] = &item{rand.Int(), rand.Int63()} + } + stack := newSstack(nil, false) + for rep := 0; rep < 2; rep++ { + // Push all the data into the stack, pop out every second + secs := []*item{} + for i := 0; i < size; i++ { + stack.Push(data[i]) + if i%2 == 0 { + secs = append(secs, stack.Pop().(*item)) + } + } + rest := []*item{} + for stack.Len() > 0 { + rest = append(rest, stack.Pop().(*item)) + } + // Make sure the contents of the resulting slices are ok + for i := 0; i < size; i++ { + if i%2 == 0 && data[i] != secs[i/2] { + t.Errorf("push/pop mismatch: have %v, want %v.", secs[i/2], data[i]) + } + if i%2 == 1 && data[i] != rest[len(rest)-i/2-1] { + t.Errorf("push/pop mismatch: have %v, want %v.", rest[len(rest)-i/2-1], data[i]) + } + } + } +} + +func TestSstackSort(t *testing.T) { + // Create some initial data + size := 16 * blockSize + data := make([]*item, size) + for i := 0; i < size; i++ { + data[i] = &item{rand.Int(), int64(i)} + } + // Push all the data into the stack + stack := newSstack(nil, false) + for _, val := range data { + stack.Push(val) + } + // Sort and pop the stack contents (should reverse the order) + sort.Sort(stack) + for _, val := range data { + out := stack.Pop() + if out != val { + t.Errorf("push/pop mismatch after sort: have %v, want %v.", out, val) + } + } +} + +func TestSstackReset(t *testing.T) { + // Create some initial data + size := 16 * blockSize + data := make([]*item, size) + for i := 0; i < size; i++ { + data[i] = &item{rand.Int(), rand.Int63()} + } + stack := newSstack(nil, false) + for rep := 0; rep < 2; rep++ { + // Push all the data into the stack, pop out every second + secs := []*item{} + for i := 0; i < size; i++ { + stack.Push(data[i]) + if i%2 == 0 { + secs = append(secs, stack.Pop().(*item)) + } + } + // Reset and verify both pulled and stack contents + stack.Reset() + if stack.Len() != 0 { + t.Errorf("stack not empty after reset: %v", stack) + } + for i := 0; i < size; i++ { + if i%2 == 0 && data[i] != secs[i/2] { + t.Errorf("push/pop mismatch: have %v, want %v.", secs[i/2], data[i]) + } + } + } +} diff --git a/common/types.go b/common/types.go index cdcc6c20ad..d920e8b1f1 100644 --- a/common/types.go +++ b/common/types.go @@ -17,6 +17,7 @@ package common import ( + "bytes" "database/sql/driver" "encoding/hex" "encoding/json" @@ -84,10 +85,34 @@ func (h Hash) String() string { return h.Hex() } -// Format implements fmt.Formatter, forcing the byte slice to be formatted as is, -// without going through the stringer interface used for logging. +// Format implements fmt.Formatter. +// Hash supports the %v, %s, %v, %x, %X and %d format verbs. func (h Hash) Format(s fmt.State, c rune) { - fmt.Fprintf(s, "%"+string(c), h[:]) + hexb := make([]byte, 2+len(h)*2) + copy(hexb, "0x") + hex.Encode(hexb[2:], h[:]) + + switch c { + case 'x', 'X': + if !s.Flag('#') { + hexb = hexb[2:] + } + if c == 'X' { + hexb = bytes.ToUpper(hexb) + } + fallthrough + case 'v', 's': + s.Write(hexb) + case 'q': + q := []byte{'"'} + s.Write(q) + s.Write(hexb) + s.Write(q) + case 'd': + fmt.Fprint(s, ([len(h)]byte)(h)) + default: + fmt.Fprintf(s, "%%!%c(hash=%x)", c, h) + } } // UnmarshalText parses a hash in hex syntax. @@ -208,39 +233,72 @@ func (a Address) Hash() Hash { return BytesToHash(a[:]) } // Hex returns an EIP55-compliant hex string representation of the address. func (a Address) Hex() string { - unchecksummed := hex.EncodeToString(a[:]) + return string(a.checksumHex()) +} + +// String implements fmt.Stringer. +func (a Address) String() string { + return a.Hex() +} + +func (a *Address) checksumHex() []byte { + buf := a.hex() + + // compute checksum sha := sha3.NewLegacyKeccak256() - sha.Write([]byte(unchecksummed)) + sha.Write(buf[2:]) hash := sha.Sum(nil) - - result := []byte(unchecksummed) - for i := 0; i < len(result); i++ { - hashByte := hash[i/2] + for i := 2; i < len(buf); i++ { + hashByte := hash[(i-2)/2] if i%2 == 0 { hashByte = hashByte >> 4 } else { hashByte &= 0xf } - if result[i] > '9' && hashByte > 7 { - result[i] -= 32 + if buf[i] > '9' && hashByte > 7 { + buf[i] -= 32 } } - return "0x" + string(result) + return buf[:] } -// String implements fmt.Stringer. -func (a Address) String() string { - return a.Hex() +func (a Address) hex() []byte { + var buf [len(a)*2 + 2]byte + copy(buf[:2], "0x") + hex.Encode(buf[2:], a[:]) + return buf[:] } -// Format implements fmt.Formatter, forcing the byte slice to be formatted as is, -// without going through the stringer interface used for logging. +// Format implements fmt.Formatter. +// Address supports the %v, %s, %v, %x, %X and %d format verbs. func (a Address) Format(s fmt.State, c rune) { - fmt.Fprintf(s, "%"+string(c), a[:]) + switch c { + case 'v', 's': + s.Write(a.checksumHex()) + case 'q': + q := []byte{'"'} + s.Write(q) + s.Write(a.checksumHex()) + s.Write(q) + case 'x', 'X': + // %x disables the checksum. + hex := a.hex() + if !s.Flag('#') { + hex = hex[2:] + } + if c == 'X' { + hex = bytes.ToUpper(hex) + } + s.Write(hex) + case 'd': + fmt.Fprint(s, ([len(a)]byte)(a)) + default: + fmt.Fprintf(s, "%%!%c(address=%x)", c, a) + } } // SetBytes sets the address to the value of b. -// If b is larger than len(a) it will panic. +// If b is larger than len(a), b will be cropped from the left. func (a *Address) SetBytes(b []byte) { if len(b) > len(a) { b = b[len(b)-AddressLength:] diff --git a/common/types_test.go b/common/types_test.go index fffd673c6e..318e985f87 100644 --- a/common/types_test.go +++ b/common/types_test.go @@ -17,8 +17,10 @@ package common import ( + "bytes" "database/sql/driver" "encoding/json" + "fmt" "math/big" "reflect" "strings" @@ -371,3 +373,167 @@ func TestAddress_Value(t *testing.T) { }) } } + +func TestAddress_Format(t *testing.T) { + b := []byte{ + 0xb2, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc, 0xf6, 0x3e, + 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x15, + } + var addr Address + addr.SetBytes(b) + + tests := []struct { + name string + out string + want string + }{ + { + name: "println", + out: fmt.Sprintln(addr), + want: "0xB26f2b342AAb24BCF63ea218c6A9274D30Ab9A15\n", + }, + { + name: "print", + out: fmt.Sprint(addr), + want: "0xB26f2b342AAb24BCF63ea218c6A9274D30Ab9A15", + }, + { + name: "printf-s", + out: func() string { + buf := new(bytes.Buffer) + fmt.Fprintf(buf, "%s", addr) + return buf.String() + }(), + want: "0xB26f2b342AAb24BCF63ea218c6A9274D30Ab9A15", + }, + { + name: "printf-q", + out: fmt.Sprintf("%q", addr), + want: `"0xB26f2b342AAb24BCF63ea218c6A9274D30Ab9A15"`, + }, + { + name: "printf-x", + out: fmt.Sprintf("%x", addr), + want: "b26f2b342aab24bcf63ea218c6a9274d30ab9a15", + }, + { + name: "printf-X", + out: fmt.Sprintf("%X", addr), + want: "B26F2B342AAB24BCF63EA218C6A9274D30AB9A15", + }, + { + name: "printf-#x", + out: fmt.Sprintf("%#x", addr), + want: "0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15", + }, + { + name: "printf-v", + out: fmt.Sprintf("%v", addr), + want: "0xB26f2b342AAb24BCF63ea218c6A9274D30Ab9A15", + }, + // The original default formatter for byte slice + { + name: "printf-d", + out: fmt.Sprintf("%d", addr), + want: "[178 111 43 52 42 171 36 188 246 62 162 24 198 169 39 77 48 171 154 21]", + }, + // Invalid format char. + { + name: "printf-t", + out: fmt.Sprintf("%t", addr), + want: "%!t(address=b26f2b342aab24bcf63ea218c6a9274d30ab9a15)", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.out != tt.want { + t.Errorf("%s does not render as expected:\n got %s\nwant %s", tt.name, tt.out, tt.want) + } + }) + } +} + +func TestHash_Format(t *testing.T) { + var hash Hash + hash.SetBytes([]byte{ + 0xb2, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc, 0xf6, 0x3e, + 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x15, + 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x15, + 0x10, 0x00, + }) + + tests := []struct { + name string + out string + want string + }{ + { + name: "println", + out: fmt.Sprintln(hash), + want: "0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000\n", + }, + { + name: "print", + out: fmt.Sprint(hash), + want: "0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000", + }, + { + name: "printf-s", + out: func() string { + buf := new(bytes.Buffer) + fmt.Fprintf(buf, "%s", hash) + return buf.String() + }(), + want: "0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000", + }, + { + name: "printf-q", + out: fmt.Sprintf("%q", hash), + want: `"0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000"`, + }, + { + name: "printf-x", + out: fmt.Sprintf("%x", hash), + want: "b26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000", + }, + { + name: "printf-X", + out: fmt.Sprintf("%X", hash), + want: "B26F2B342AAB24BCF63EA218C6A9274D30AB9A15A218C6A9274D30AB9A151000", + }, + { + name: "printf-#x", + out: fmt.Sprintf("%#x", hash), + want: "0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000", + }, + { + name: "printf-#X", + out: fmt.Sprintf("%#X", hash), + want: "0XB26F2B342AAB24BCF63EA218C6A9274D30AB9A15A218C6A9274D30AB9A151000", + }, + { + name: "printf-v", + out: fmt.Sprintf("%v", hash), + want: "0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000", + }, + // The original default formatter for byte slice + { + name: "printf-d", + out: fmt.Sprintf("%d", hash), + want: "[178 111 43 52 42 171 36 188 246 62 162 24 198 169 39 77 48 171 154 21 162 24 198 169 39 77 48 171 154 21 16 0]", + }, + // Invalid format char. + { + name: "printf-t", + out: fmt.Sprintf("%t", hash), + want: "%!t(hash=b26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000)", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.out != tt.want { + t.Errorf("%s does not render as expected:\n got %s\nwant %s", tt.name, tt.out, tt.want) + } + }) + } +} diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 287eba8a76..2cab9a1505 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -435,12 +435,6 @@ func (c *Clique) VerifyUncles(chain consensus.ChainReader, block *types.Block) e 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.ChainHeaderReader, 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 @@ -562,12 +556,11 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, // nor block rewards given, and returns the final block. func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, 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().IsEnabled(chain.Config().GetEIP161dTransition, header.Number)) - header.UncleHash = types.CalcUncleHash(nil) + // Finalize block + c.Finalize(chain, header, state, txs, uncles) // Assemble and return the final block for sealing - return types.NewBlock(header, txs, nil, receipts, new(trie.Trie)), nil + return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil } // Authorize injects a private key into the consensus engine to mint new blocks diff --git a/consensus/consensus.go b/consensus/consensus.go index a76af04e0f..da0a3bd516 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -77,10 +77,6 @@ type Engine interface { // 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 ChainHeaderReader, 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 ChainHeaderReader, header *types.Header) error diff --git a/consensus/ethash/algorithm.go b/consensus/ethash/algorithm.go index 46c80c5121..af9f4bc1f2 100644 --- a/consensus/ethash/algorithm.go +++ b/consensus/ethash/algorithm.go @@ -176,10 +176,12 @@ func generateCache(dest []uint32, epoch uint64, epochLength uint64, seed []byte) logFn("Generated ethash verification cache", "epochLength", epochLength, "elapsed", common.PrettyDuration(elapsed)) }() // Convert our destination slice to a byte buffer - header := *(*reflect.SliceHeader)(unsafe.Pointer(&dest)) - header.Len *= 4 - header.Cap *= 4 - cache := *(*[]byte)(unsafe.Pointer(&header)) + var cache []byte + cacheHdr := (*reflect.SliceHeader)(unsafe.Pointer(&cache)) + dstHdr := (*reflect.SliceHeader)(unsafe.Pointer(&dest)) + cacheHdr.Data = dstHdr.Data + cacheHdr.Len = dstHdr.Len * 4 + cacheHdr.Cap = dstHdr.Cap * 4 // Calculate the number of theoretical rows (we'll store in one buffer nonetheless) size := uint64(len(cache)) @@ -197,7 +199,7 @@ func generateCache(dest []uint32, epoch uint64, epochLength uint64, seed []byte) case <-done: return case <-time.After(3 * time.Second): - logger.Info("Generating ethash verification cache", "epochLength", epochLength, "percentage", atomic.LoadUint32(&progress)*100/uint32(rows)/4, "elapsed", common.PrettyDuration(time.Since(start))) + logger.Info("Generating ethash verification cache", "epochLength", epochLength, "percentage", atomic.LoadUint32(&progress)*100/uint32(rows)/(cacheRounds+1), "elapsed", common.PrettyDuration(time.Since(start))) } } }() @@ -308,10 +310,12 @@ func generateDataset(dest []uint32, epoch uint64, epochLength uint64, cache []ui swapped := !isLittleEndian() // Convert our destination slice to a byte buffer - header := *(*reflect.SliceHeader)(unsafe.Pointer(&dest)) - header.Len *= 4 - header.Cap *= 4 - dataset := *(*[]byte)(unsafe.Pointer(&header)) + var dataset []byte + datasetHdr := (*reflect.SliceHeader)(unsafe.Pointer(&dataset)) + destHdr := (*reflect.SliceHeader)(unsafe.Pointer(&dest)) + datasetHdr.Data = destHdr.Data + datasetHdr.Len = destHdr.Len * 4 + datasetHdr.Cap = destHdr.Cap * 4 // Generate the dataset on many goroutines since it takes a while threads := runtime.NumCPU() diff --git a/consensus/ethash/algorithm_test.go b/consensus/ethash/algorithm_test.go index c920c22fef..84b55282d3 100644 --- a/consensus/ethash/algorithm_test.go +++ b/consensus/ethash/algorithm_test.go @@ -752,12 +752,16 @@ func TestConcurrentDiskCacheGeneration(t *testing.T) { for i := 0; i < 3; i++ { pend.Add(1) - go func(idx int) { defer pend.Done() - ethash := New(Config{cachedir, 0, 1, false, "", 0, 0, false, ModeNormal, nil, nil}, nil, false) + + config := Config{ + CacheDir: cachedir, + CachesOnDisk: 1, + } + ethash := New(config, nil, false) defer ethash.Close() - if err := ethash.VerifySeal(nil, block.Header()); err != nil { + if err := ethash.verifySeal(nil, block.Header(), false); err != nil { t.Errorf("proc %d: block verification failed: %v", idx, err) } }(i) diff --git a/consensus/ethash/api.go b/consensus/ethash/api.go index 68b3a84b09..f4d3802e0b 100644 --- a/consensus/ethash/api.go +++ b/consensus/ethash/api.go @@ -89,7 +89,7 @@ func (api *API) SubmitWork(nonce types.BlockNonce, hash, digest common.Hash) boo // // It accepts the miner hash rate and an identifier which must be unique // between nodes. -func (api *API) SubmitHashRate(rate hexutil.Uint64, id common.Hash) bool { +func (api *API) SubmitHashrate(rate hexutil.Uint64, id common.Hash) bool { if api.ethash.remote == nil { return false } diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 9a3e42c3dd..884dd4c96e 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -83,7 +83,7 @@ func (ethash *Ethash) VerifyHeader(chain consensus.ChainHeaderReader, header *ty return consensus.ErrUnknownAncestor } // Sanity checks passed, do a proper verification - return ethash.verifyHeader(chain, header, parent, false, seal) + return ethash.verifyHeader(chain, header, parent, false, seal, time.Now().Unix()) } // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers @@ -107,15 +107,16 @@ func (ethash *Ethash) VerifyHeaders(chain consensus.ChainHeaderReader, 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{}) + inputs = make(chan int) + done = make(chan int, workers) + errors = make([]error, len(headers)) + abort = make(chan struct{}) + unixNow = time.Now().Unix() ) for i := 0; i < workers; i++ { go func() { for index := range inputs { - errors[index] = ethash.verifyHeaderWorker(chain, headers, seals, index) + errors[index] = ethash.verifyHeaderWorker(chain, headers, seals, index, unixNow) done <- index } }() @@ -151,7 +152,7 @@ func (ethash *Ethash) VerifyHeaders(chain consensus.ChainHeaderReader, headers [ return abort, errorsOut } -func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool, index int) error { +func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool, index int, unixNow int64) error { var parent *types.Header if index == 0 { parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1) @@ -161,10 +162,7 @@ func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainHeaderReader, head 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]) + return ethash.verifyHeader(chain, headers[index], parent, false, seals[index], unixNow) } // VerifyUncles verifies that the given block's uncles conform to the consensus @@ -215,7 +213,7 @@ func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Blo if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == block.ParentHash() { return errDanglingUncle } - if err := ethash.verifyHeader(chain, uncle, ancestors[uncle.ParentHash], true, true); err != nil { + if err := ethash.verifyHeader(chain, uncle, ancestors[uncle.ParentHash], true, true, time.Now().Unix()); err != nil { return err } } @@ -225,14 +223,14 @@ func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Blo // 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.ChainHeaderReader, header, parent *types.Header, uncle bool, seal bool) error { +func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header, uncle bool, seal bool, unixNow int64) error { // Ensure that the header's extra-data section is of a reasonable size if uint64(len(header.Extra)) > vars.MaximumExtraDataSize { return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), vars.MaximumExtraDataSize) } // Verify the header's timestamp if !uncle { - if header.Time > uint64(time.Now().Add(allowedFutureBlockTime).Unix()) { + if header.Time > uint64(unixNow+int64(allowedFutureBlockTime.Seconds())) { return consensus.ErrFutureBlock } } @@ -271,7 +269,7 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, pa } // Verify the engine specific seal securing the block if seal { - if err := ethash.VerifySeal(chain, header); err != nil { + if err := ethash.verifySeal(chain, header, false); err != nil { return err } } @@ -452,11 +450,10 @@ var ( bigMinus99 = big.NewInt(-99) ) -// VerifySeal implements consensus.Engine, checking whether the given block satisfies -// the PoW difficulty requirements. -func (ethash *Ethash) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error { - return ethash.verifySeal(chain, header, false) -} +// Exported for fuzzing +var FrontierDifficultyCalulator = CalcDifficultyFrontierU256 +var HomesteadDifficultyCalulator = CalcDifficultyHomesteadU256 +var DynamicDifficultyCalculator = MakeDifficultyCalculatorU256 // verifySeal checks whether a block satisfies the PoW difficulty requirements, // either using the usual ethash cache for it, or alternatively using a full DAG @@ -547,12 +544,11 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types. // FinalizeAndAssemble implements consensus.Engine, accumulating the block and // uncle rewards, setting the final state and assembling the block. func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, 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(chain.Config(), state, header, uncles) - header.Root = state.IntermediateRoot(chain.Config().IsEnabled(chain.Config().GetEIP161dTransition, header.Number)) + // Finalize block + ethash.Finalize(chain, header, state, txs, uncles) // Header seems complete, assemble into a block and return - return types.NewBlock(header, txs, uncles, receipts, new(trie.Trie)), nil + return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil } // SealHash returns the hash of a block prior to it being sealed. diff --git a/consensus/ethash/consensus_test.go b/consensus/ethash/consensus_test.go index 546331ff3d..5683d1f697 100644 --- a/consensus/ethash/consensus_test.go +++ b/consensus/ethash/consensus_test.go @@ -17,15 +17,19 @@ package ethash import ( + "encoding/binary" "encoding/json" "math/big" + "math/rand" "os" "path/filepath" "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/params/types/goethereum" + "github.com/ethereum/go-ethereum/params/vars" ) type diffTest struct { @@ -84,3 +88,102 @@ func TestCalcDifficulty(t *testing.T) { } } } + +func randSlice(min, max uint32) []byte { + var b = make([]byte, 4) + rand.Read(b) + a := binary.LittleEndian.Uint32(b) + size := min + a%(max-min) + out := make([]byte, size) + rand.Read(out) + return out +} + +func TestDifficultyCalculators(t *testing.T) { + rand.Seed(2) + for i := 0; i < 5000; i++ { + // 1 to 300 seconds diff + var timeDelta = uint64(1 + rand.Uint32()%3000) + diffBig := big.NewInt(0).SetBytes(randSlice(2, 10)) + if diffBig.Cmp(vars.MinimumDifficulty) < 0 { + diffBig.Set(vars.MinimumDifficulty) + } + //rand.Read(difficulty) + header := &types.Header{ + Difficulty: diffBig, + Number: new(big.Int).SetUint64(rand.Uint64() % 50_000_000), + Time: rand.Uint64() - timeDelta, + } + if rand.Uint32()&1 == 0 { + header.UncleHash = types.EmptyUncleHash + } + bombDelay := new(big.Int).SetUint64(rand.Uint64() % 50_000_000) + for i, pair := range []struct { + bigFn func(time uint64, parent *types.Header) *big.Int + u256Fn func(time uint64, parent *types.Header) *big.Int + }{ + {FrontierDifficultyCalulator, CalcDifficultyFrontierU256}, + {HomesteadDifficultyCalulator, CalcDifficultyHomesteadU256}, + {DynamicDifficultyCalculator(bombDelay), MakeDifficultyCalculatorU256(bombDelay)}, + } { + time := header.Time + timeDelta + want := pair.bigFn(time, header) + have := pair.u256Fn(time, header) + if want.BitLen() > 256 { + continue + } + if want.Cmp(have) != 0 { + t.Fatalf("pair %d: want %x have %x\nparent.Number: %x\np.Time: %x\nc.Time: %x\nBombdelay: %v\n", i, want, have, + header.Number, header.Time, time, bombDelay) + } + } + } +} + +func BenchmarkDifficultyCalculator(b *testing.B) { + x1 := MakeDifficultyCalculatorU256(big.NewInt(1000000)) + x2 := MakeDifficultyCalculatorU256(big.NewInt(1000000)) + h := &types.Header{ + ParentHash: common.Hash{}, + UncleHash: types.EmptyUncleHash, + Difficulty: big.NewInt(0xffffff), + Number: big.NewInt(500000), + Time: 1000000, + } + b.Run("big-frontier", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + CalcDifficultyFrontierU256(1000014, h) + } + }) + b.Run("u256-frontier", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + CalcDifficultyFrontierU256(1000014, h) + } + }) + b.Run("big-homestead", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + CalcDifficultyHomesteadU256(1000014, h) + } + }) + b.Run("u256-homestead", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + CalcDifficultyHomesteadU256(1000014, h) + } + }) + b.Run("big-generic", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + x1(1000014, h) + } + }) + b.Run("u256-generic", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + x2(1000014, h) + } + }) +} diff --git a/consensus/ethash/difficulty.go b/consensus/ethash/difficulty.go new file mode 100644 index 0000000000..59c4ac7419 --- /dev/null +++ b/consensus/ethash/difficulty.go @@ -0,0 +1,193 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethash + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/holiman/uint256" +) + +const ( + // frontierDurationLimit is for Frontier: + // The decision boundary on the blocktime duration used to determine + // whether difficulty should go up or down. + frontierDurationLimit = 13 + // minimumDifficulty The minimum that the difficulty may ever be. + minimumDifficulty = 131072 + // expDiffPeriod is the exponential difficulty period + expDiffPeriodUint = 100000 + // difficultyBoundDivisorBitShift is the bound divisor of the difficulty (2048), + // This constant is the right-shifts to use for the division. + difficultyBoundDivisor = 11 +) + +// CalcDifficultyFrontierU256 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 CalcDifficultyFrontierU256(time uint64, parent *types.Header) *big.Int { + /* + Algorithm + block_diff = pdiff + pdiff / 2048 * (1 if time - ptime < 13 else -1) + int(2^((num // 100000) - 2)) + + Where: + - pdiff = parent.difficulty + - ptime = parent.time + - time = block.timestamp + - num = block.number + */ + + pDiff := uint256.NewInt() + pDiff.SetFromBig(parent.Difficulty) // pDiff: pdiff + adjust := pDiff.Clone() + adjust.Rsh(adjust, difficultyBoundDivisor) // adjust: pDiff / 2048 + + if time-parent.Time < frontierDurationLimit { + pDiff.Add(pDiff, adjust) + } else { + pDiff.Sub(pDiff, adjust) + } + if pDiff.LtUint64(minimumDifficulty) { + pDiff.SetUint64(minimumDifficulty) + } + // 'pdiff' now contains: + // pdiff + pdiff / 2048 * (1 if time - ptime < 13 else -1) + + if periodCount := (parent.Number.Uint64() + 1) / expDiffPeriodUint; periodCount > 1 { + // diff = diff + 2^(periodCount - 2) + expDiff := adjust.SetOne() + expDiff.Lsh(expDiff, uint(periodCount-2)) // expdiff: 2 ^ (periodCount -2) + pDiff.Add(pDiff, expDiff) + } + return pDiff.ToBig() +} + +// CalcDifficultyHomesteadU256 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 CalcDifficultyHomesteadU256(time uint64, parent *types.Header) *big.Int { + /* + https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md + Algorithm: + block_diff = pdiff + pdiff / 2048 * max(1 - (time - ptime) / 10, -99) + 2 ^ int((num / 100000) - 2)) + + Our modification, to use unsigned ints: + block_diff = pdiff - pdiff / 2048 * max((time - ptime) / 10 - 1, 99) + 2 ^ int((num / 100000) - 2)) + + Where: + - pdiff = parent.difficulty + - ptime = parent.time + - time = block.timestamp + - num = block.number + */ + + pDiff := uint256.NewInt() + pDiff.SetFromBig(parent.Difficulty) // pDiff: pdiff + adjust := pDiff.Clone() + adjust.Rsh(adjust, difficultyBoundDivisor) // adjust: pDiff / 2048 + + x := (time - parent.Time) / 10 // (time - ptime) / 10) + var neg = true + if x == 0 { + x = 1 + neg = false + } else if x >= 100 { + x = 99 + } else { + x = x - 1 + } + z := new(uint256.Int).SetUint64(x) + adjust.Mul(adjust, z) // adjust: (pdiff / 2048) * max((time - ptime) / 10 - 1, 99) + if neg { + pDiff.Sub(pDiff, adjust) // pdiff - pdiff / 2048 * max((time - ptime) / 10 - 1, 99) + } else { + pDiff.Add(pDiff, adjust) // pdiff + pdiff / 2048 * max((time - ptime) / 10 - 1, 99) + } + if pDiff.LtUint64(minimumDifficulty) { + pDiff.SetUint64(minimumDifficulty) + } + // for the exponential factor, a.k.a "the bomb" + // diff = diff + 2^(periodCount - 2) + if periodCount := (1 + parent.Number.Uint64()) / expDiffPeriodUint; periodCount > 1 { + expFactor := adjust.Lsh(adjust.SetOne(), uint(periodCount-2)) + pDiff.Add(pDiff, expFactor) + } + return pDiff.ToBig() +} + +// MakeDifficultyCalculatorU256 creates a difficultyCalculator with the given bomb-delay. +// the difficulty is calculated with Byzantium rules, which differs from Homestead in +// how uncles affect the calculation +func MakeDifficultyCalculatorU256(bombDelay *big.Int) func(time uint64, parent *types.Header) *big.Int { + // Note, the calculations below looks at the parent number, which is 1 below + // the block number. Thus we remove one from the delay given + bombDelayFromParent := bombDelay.Uint64() - 1 + return func(time uint64, parent *types.Header) *big.Int { + /* + https://github.com/ethereum/EIPs/issues/100 + pDiff = parent.difficulty + BLOCK_DIFF_FACTOR = 9 + a = pDiff + (pDiff // BLOCK_DIFF_FACTOR) * adj_factor + b = min(parent.difficulty, MIN_DIFF) + child_diff = max(a,b ) + */ + x := (time - parent.Time) / 9 // (block_timestamp - parent_timestamp) // 9 + c := uint64(1) // if parent.unclehash == emptyUncleHashHash + if parent.UncleHash != types.EmptyUncleHash { + c = 2 + } + xNeg := x >= c + if xNeg { + // x is now _negative_ adjustment factor + x = x - c // - ( (t-p)/p -( 2 or 1) ) + } else { + x = c - x // (2 or 1) - (t-p)/9 + } + if x > 99 { + x = 99 // max(x, 99) + } + // parent_diff + (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99)) + y := new(uint256.Int) + y.SetFromBig(parent.Difficulty) // y: p_diff + pDiff := y.Clone() // pdiff: p_diff + z := new(uint256.Int).SetUint64(x) //z : +-adj_factor (either pos or negative) + y.Rsh(y, difficultyBoundDivisor) // y: p__diff / 2048 + z.Mul(y, z) // z: (p_diff / 2048 ) * (+- adj_factor) + + if xNeg { + y.Sub(pDiff, z) // y: parent_diff + parent_diff/2048 * adjustment_factor + } else { + y.Add(pDiff, z) // y: parent_diff + parent_diff/2048 * adjustment_factor + } + // minimum difficulty can ever be (before exponential factor) + if y.LtUint64(minimumDifficulty) { + y.SetUint64(minimumDifficulty) + } + // calculate a fake block number for the ice-age delay + // Specification: https://eips.ethereum.org/EIPS/eip-1234 + var pNum = parent.Number.Uint64() + if pNum >= bombDelayFromParent { + if fakeBlockNumber := pNum - bombDelayFromParent; fakeBlockNumber >= 2*expDiffPeriodUint { + z.SetOne() + z.Lsh(z, uint(fakeBlockNumber/expDiffPeriodUint-2)) + y.Add(z, y) + } + } + return y.ToBig() + } +} diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go index f5e6778237..3f74b266ab 100644 --- a/consensus/ethash/ethash.go +++ b/consensus/ethash/ethash.go @@ -51,7 +51,7 @@ var ( two256 = 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 = New(Config{"", 3, 0, false, "", 1, 0, false, ModeNormal, nil, nil}, nil, false) + sharedEthash *Ethash // algorithmRevision is the data structure version used for file naming. algorithmRevision = 23 @@ -60,6 +60,15 @@ var ( dumpMagic = []uint32{0xbaddcafe, 0xfee1dead} ) +func init() { + sharedConfig := Config{ + PowMode: ModeNormal, + CachesInMem: 3, + DatasetsInMem: 1, + } + sharedEthash = New(sharedConfig, nil, false) +} + // isLittleEndian returns whether the local system is running in little or big // endian byte order. func isLittleEndian() bool { @@ -533,6 +542,10 @@ type Config struct { DatasetsLockMmap bool PowMode Mode + // When set, notifications sent by the remote sealer will + // be block header JSON objects instead of work package arrays. + NotifyFull bool + Log log.Logger `toml:"-"` // ECIP-1099 ECIP1099Block *uint64 `toml:"-"` @@ -586,6 +599,9 @@ func New(config Config, notify []string, noverify bool) *Ethash { update: make(chan struct{}), hashrate: metrics.NewMeterForced(), } + if config.PowMode == ModeShared { + ethash.shared = sharedEthash + } ethash.remote = startRemoteSealer(ethash, notify, noverify) return ethash } @@ -593,15 +609,7 @@ func New(config Config, notify []string, noverify bool) *Ethash { // NewTester creates a small sized ethash PoW scheme useful only for testing // purposes. func NewTester(notify []string, noverify bool) *Ethash { - ethash := &Ethash{ - config: Config{PowMode: ModeTest, Log: log.Root()}, - caches: newlru("cache", 1, newCache), - datasets: newlru("dataset", 1, newDataset), - update: make(chan struct{}), - hashrate: metrics.NewMeterForced(), - } - ethash.remote = startRemoteSealer(ethash, notify, noverify) - return ethash + return New(Config{PowMode: ModeTest}, notify, noverify) } // NewFaker creates a ethash consensus engine with a fake PoW scheme that accepts @@ -673,7 +681,6 @@ func NewShared() *Ethash { // Close closes the exit channel to notify all backend threads exiting. func (ethash *Ethash) Close() error { - var err error ethash.closeOnce.Do(func() { // Short circuit if the exit channel is not allocated. if ethash.remote == nil { @@ -682,7 +689,7 @@ func (ethash *Ethash) Close() error { close(ethash.remote.requestExit) <-ethash.remote.exitCh }) - return err + return nil } // cache tries to retrieve a verification cache for the specified block number diff --git a/consensus/ethash/ethash_test.go b/consensus/ethash/ethash_test.go index d978adcc42..cbdb131fba 100644 --- a/consensus/ethash/ethash_test.go +++ b/consensus/ethash/ethash_test.go @@ -97,10 +97,10 @@ func TestTestMode(t *testing.T) { case block := <-results: header.Nonce = types.EncodeNonce(block.Nonce()) header.MixDigest = block.MixDigest() - if err := ethash.VerifySeal(nil, header); err != nil { + if err := ethash.verifySeal(nil, header, false); err != nil { t.Fatalf("unexpected verification error: %v", err) } - case <-time.NewTimer(2 * time.Second).C: + case <-time.NewTimer(4 * time.Second).C: t.Error("sealing result timeout") } } @@ -113,7 +113,14 @@ func TestCacheFileEvict(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(tmpdir) - e := New(Config{CachesInMem: 3, CachesOnDisk: 10, CacheDir: tmpdir, PowMode: ModeTest}, nil, false) + + config := Config{ + CachesInMem: 3, + CachesOnDisk: 10, + CacheDir: tmpdir, + PowMode: ModeTest, + } + e := New(config, nil, false) defer e.Close() workers := 8 @@ -136,7 +143,7 @@ func verifyTest(wg *sync.WaitGroup, e *Ethash, workerIndex, epochs int) { block = 0 } header := &types.Header{Number: big.NewInt(block), Difficulty: big.NewInt(100)} - e.VerifySeal(nil, header) + e.verifySeal(nil, header, false) } } @@ -178,7 +185,7 @@ func TestRemoteSealer(t *testing.T) { } } -func TestHashRate(t *testing.T) { +func TestHashrate(t *testing.T) { var ( hashrate = []hexutil.Uint64{100, 200, 300} expect uint64 @@ -193,7 +200,7 @@ func TestHashRate(t *testing.T) { api := &API{ethash} for i := 0; i < len(hashrate); i += 1 { - if res := api.SubmitHashRate(hashrate[i], ids[i]); !res { + if res := api.SubmitHashrate(hashrate[i], ids[i]); !res { t.Error("remote miner submit hashrate failed") } expect += uint64(hashrate[i]) @@ -213,7 +220,7 @@ func TestClosedRemoteSealer(t *testing.T) { t.Error("expect to return an error to indicate ethash is stopped") } - if res := api.SubmitHashRate(hexutil.Uint64(100), common.HexToHash("a")); res { + if res := api.SubmitHashrate(hexutil.Uint64(100), common.HexToHash("a")); res { t.Error("expect to return false when submit hashrate to a stopped ethash") } } diff --git a/consensus/ethash/sealer.go b/consensus/ethash/sealer.go index 480afe4cf3..f28ed7ffc4 100644 --- a/consensus/ethash/sealer.go +++ b/consensus/ethash/sealer.go @@ -406,7 +406,16 @@ func (s *remoteSealer) makeWork(block *types.Block) { // new work to be processed. func (s *remoteSealer) notifyWork() { work := s.currentWork - blob, _ := json.Marshal(work) + + // Encode the JSON payload of the notification. When NotifyFull is set, + // this is the complete block header, otherwise it is a JSON array. + var blob []byte + if s.ethash.config.NotifyFull { + blob, _ = json.Marshal(s.currentBlock.Header()) + } else { + blob, _ = json.Marshal(work) + } + s.reqWG.Add(len(s.notifyURLs)) for _, url := range s.notifyURLs { go s.sendNotification(s.notifyCtx, url, blob, work) diff --git a/consensus/ethash/sealer_test.go b/consensus/ethash/sealer_test.go index 7f32832d90..4f42b789bd 100644 --- a/consensus/ethash/sealer_test.go +++ b/consensus/ethash/sealer_test.go @@ -23,6 +23,7 @@ import ( "net/http" "net/http/httptest" "sort" + "strconv" "testing" "time" @@ -94,6 +95,50 @@ func TestRemoteNotify(t *testing.T) { } } +// Tests whether remote HTTP servers are correctly notified of new work. (Full pending block body / --miner.notify.full) +func TestRemoteNotifyFull(t *testing.T) { + // Start a simple web server to capture notifications. + sink := make(chan map[string]interface{}) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + blob, err := ioutil.ReadAll(req.Body) + if err != nil { + t.Errorf("failed to read miner notification: %v", err) + } + var work map[string]interface{} + if err := json.Unmarshal(blob, &work); err != nil { + t.Errorf("failed to unmarshal miner notification: %v", err) + } + sink <- work + })) + defer server.Close() + + // Create the custom ethash engine. + config := Config{ + PowMode: ModeTest, + NotifyFull: true, + Log: testlog.Logger(t, log.LvlWarn), + } + ethash := New(config, []string{server.URL}, false) + defer ethash.Close() + + // Stream a work task and ensure the notification bubbles out. + header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)} + block := types.NewBlockWithHeader(header) + + ethash.Seal(nil, block, nil, nil) + select { + case work := <-sink: + if want := "0x" + strconv.FormatUint(header.Number.Uint64(), 16); work["number"] != want { + t.Errorf("pending block number mismatch: have %v, want %v", work["number"], want) + } + if want := "0x" + header.Difficulty.Text(16); work["difficulty"] != want { + t.Errorf("pending block difficulty mismatch: have %s, want %s", work["difficulty"], want) + } + case <-time.After(3 * time.Second): + t.Fatalf("notification timed out") + } +} + // Tests that pushing work packages fast to the miner doesn't cause any data race // issues in the notifications. func TestRemoteMultiNotify(t *testing.T) { @@ -139,6 +184,55 @@ func TestRemoteMultiNotify(t *testing.T) { } } +// Tests that pushing work packages fast to the miner doesn't cause any data race +// issues in the notifications. Full pending block body / --miner.notify.full) +func TestRemoteMultiNotifyFull(t *testing.T) { + // Start a simple web server to capture notifications. + sink := make(chan map[string]interface{}, 64) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + blob, err := ioutil.ReadAll(req.Body) + if err != nil { + t.Errorf("failed to read miner notification: %v", err) + } + var work map[string]interface{} + if err := json.Unmarshal(blob, &work); err != nil { + t.Errorf("failed to unmarshal miner notification: %v", err) + } + sink <- work + })) + defer server.Close() + + // Create the custom ethash engine. + config := Config{ + PowMode: ModeTest, + NotifyFull: true, + Log: testlog.Logger(t, log.LvlWarn), + } + ethash := New(config, []string{server.URL}, false) + defer ethash.Close() + + // Provide a results reader. + // Otherwise the unread results will be logged asynchronously + // and this can happen after the test is finished, causing a panic. + results := make(chan *types.Block, cap(sink)) + + // Stream a lot of work task and ensure all the notifications bubble out. + for i := 0; i < cap(sink); i++ { + header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)} + block := types.NewBlockWithHeader(header) + ethash.Seal(nil, block, results, nil) + } + + for i := 0; i < cap(sink); i++ { + select { + case <-sink: + <-results + case <-time.After(10 * time.Second): + t.Fatalf("notification %d timed out", i) + } + } +} + // Tests whether stale solutions are correctly processed. func TestStaleSubmission(t *testing.T) { ethash := NewTester(nil, true) diff --git a/console/bridge.go b/console/bridge.go index 1a23269194..21ef0e8e7b 100644 --- a/console/bridge.go +++ b/console/bridge.go @@ -144,15 +144,14 @@ func (b *bridge) OpenWallet(call jsre.Call) (goja.Value, error) { if val, err = openWallet(goja.Null(), wallet, passwd); err != nil { if !strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()) { return nil, err - } else { - // PIN input requested, fetch from the user and call open again - input, err := b.prompter.PromptPassword("Please enter current PIN: ") - if err != nil { - return nil, err - } - if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(input)); err != nil { - return nil, err - } + } + // PIN input requested, fetch from the user and call open again + input, err := b.prompter.PromptPassword("Please enter current PIN: ") + if err != nil { + return nil, err + } + if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(input)); err != nil { + return nil, err } } diff --git a/console/console_test.go b/console/console_test.go index c0515929c2..e5d9336b04 100644 --- a/console/console_test.go +++ b/console/console_test.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/console/prompt" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/jsre" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" @@ -85,7 +86,7 @@ type tester struct { // newTester creates a test environment based on which the console can operate. // Please ensure you call Close() on the returned tester to avoid leaks. -func newTester(t *testing.T, confOverride func(*eth.Config)) *tester { +func newTester(t *testing.T, confOverride func(*ethconfig.Config)) *tester { // Create a temporary storage for the node keys and initialize it workspace, err := ioutil.TempDir("", "console-tester-") if err != nil { @@ -97,8 +98,8 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester { if err != nil { t.Fatalf("failed to create node: %v", err) } - ethConf := ð.Config{ - Genesis: params.DeveloperGenesisBlock(15, common.Address{}, false), + ethConf := ðconfig.Config{ + Genesis: params.DeveloperGenesisBlock(15, common.Address{}, true), Miner: miner.Config{ Etherbase: common.HexToAddress(testAddress), }, @@ -125,7 +126,7 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester { printer := new(bytes.Buffer) console, err := New(Config{ - DataDir: stack.DataDir(), + DataDir: stack.InstanceDir(), DocRoot: "testdata", Client: client, Prompter: prompter, diff --git a/contracts/checkpointoracle/oracle.go b/contracts/checkpointoracle/oracle.go index c9581cfdb9..f847c6d86f 100644 --- a/contracts/checkpointoracle/oracle.go +++ b/contracts/checkpointoracle/oracle.go @@ -65,7 +65,7 @@ func (oracle *CheckpointOracle) LookupCheckpointEvents(blockLogs [][]*types.Log, if err != nil { continue } - if event.Index == section && common.Hash(event.CheckpointHash) == hash { + if event.Index == section && event.CheckpointHash == hash { votes = append(votes, event) } } diff --git a/contracts/checkpointoracle/oracle_test.go b/contracts/checkpointoracle/oracle_test.go index 1019df1d18..bea57a1797 100644 --- a/contracts/checkpointoracle/oracle_test.go +++ b/contracts/checkpointoracle/oracle_test.go @@ -178,7 +178,7 @@ func TestCheckpointRegister(t *testing.T) { contractBackend := backends.NewSimulatedBackend(genesisT.GenesisAlloc{accounts[0].addr: {Balance: big.NewInt(1000000000)}, accounts[1].addr: {Balance: big.NewInt(1000000000)}, accounts[2].addr: {Balance: big.NewInt(1000000000)}}, 10000000) defer contractBackend.Close() - transactOpts := bind.NewKeyedTransactor(accounts[0].key) + transactOpts, _ := bind.NewKeyedTransactorWithChainID(accounts[0].key, big.NewInt(1337)) // 3 trusted signers, threshold 2 contractAddr, _, c, err := contract.DeployCheckpointOracle(transactOpts, contractBackend, []common.Address{accounts[0].addr, accounts[1].addr, accounts[2].addr}, sectionSize, processConfirms, big.NewInt(2)) diff --git a/core/bench_test.go b/core/bench_test.go index 61cf6c0de7..5624fb76e9 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -87,7 +87,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) { return func(i int, gen *BlockGen) { toaddr := common.Address{} data := make([]byte, nbytes) - gas, _ := IntrinsicGas(data, false, false, false) + gas, _ := IntrinsicGas(data, nil, false, false, false) tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey) gen.AddTx(tx) } @@ -159,7 +159,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { b.Fatalf("cannot create temporary directory: %v", err) } defer os.RemoveAll(dir) - db, err = rawdb.NewLevelDBDatabase(dir, 128, 128, "") + db, err = rawdb.NewLevelDBDatabase(dir, 128, 128, "", false) if err != nil { b.Fatalf("cannot create temporary database: %v", err) } @@ -257,7 +257,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) { if err != nil { b.Fatalf("cannot create temporary directory: %v", err) } - db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "") + db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false) if err != nil { b.Fatalf("error opening database at %v: %v", dir, err) } @@ -274,7 +274,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) { } defer os.RemoveAll(dir) - db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "") + db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false) if err != nil { b.Fatalf("error opening database at %v: %v", dir, err) } @@ -285,7 +285,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) { b.ResetTimer() for i := 0; i < b.N; i++ { - db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "") + db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false) if err != nil { b.Fatalf("error opening database at %v: %v", dir, err) } diff --git a/core/blockchain.go b/core/blockchain.go index e1c11f6c3b..08dd4fccbd 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -89,7 +89,6 @@ const ( txLookupCacheLimit = 1024 maxFutureBlocks = 256 maxTimeFutureBlocks = 30 - badBlockLimit = 10 TriesInMemory = 128 // BlockChainVersion ensures that an incompatible database forces a resync from scratch. @@ -129,6 +128,7 @@ type CacheConfig struct { TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node) TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory + Preimages bool // Whether to store preimage of trie key to the disk SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it } @@ -202,12 +202,11 @@ type BlockChain struct { procInterrupt int32 // interrupt signaler for block processing engine consensus.Engine - validator Validator // Block and state validator interface - prefetcher Prefetcher // Block state prefetcher interface - processor Processor // Block transaction processor interface + validator Validator // Block and state validator interface + prefetcher Prefetcher + processor Processor // Block transaction processor interface vmConfig vm.Config - badBlocks *lru.Cache // Bad block cache shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block. terminateInsert func(common.Hash, uint64) bool // Testing hook used to terminate ancient receipt chain insertion. writeLegacyJournal bool // Testing flag used to flush the snapshot journal in legacy format. @@ -229,14 +228,17 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig ctyp blockCache, _ := lru.New(blockCacheLimit) txLookupCache, _ := lru.New(txLookupCacheLimit) futureBlocks, _ := lru.New(maxFutureBlocks) - badBlocks, _ := lru.New(badBlockLimit) bc := &BlockChain{ - chainConfig: chainConfig, - cacheConfig: cacheConfig, - db: db, - triegc: prque.New(nil), - stateCache: state.NewDatabaseWithCache(db, cacheConfig.TrieCleanLimit, cacheConfig.TrieCleanJournal), + chainConfig: chainConfig, + cacheConfig: cacheConfig, + db: db, + triegc: prque.New(nil), + stateCache: state.NewDatabaseWithConfig(db, &trie.Config{ + Cache: cacheConfig.TrieCleanLimit, + Journal: cacheConfig.TrieCleanJournal, + Preimages: cacheConfig.Preimages, + }), quit: make(chan struct{}), shouldPreserve: shouldPreserve, bodyCache: bodyCache, @@ -247,7 +249,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig ctyp futureBlocks: futureBlocks, engine: engine, vmConfig: vmConfig, - badBlocks: badBlocks, } bc.validator = NewBlockValidator(chainConfig, bc, engine) bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine) @@ -378,7 +379,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig ctyp log.Warn("Enabling snapshot recovery", "chainhead", head.NumberU64(), "diskbase", *layer) recover = true } - bc.snaps = snapshot.New(bc.db, bc.stateCache.TrieDB(), bc.cacheConfig.SnapshotLimit, head.Root(), !bc.cacheConfig.SnapshotWait, recover) + bc.snaps, _ = snapshot.New(bc.db, bc.stateCache.TrieDB(), bc.cacheConfig.SnapshotLimit, head.Root(), !bc.cacheConfig.SnapshotWait, true, recover) } // Take ownership of this particular state go bc.update() @@ -533,8 +534,13 @@ func (bc *BlockChain) SetHeadBeyondRoot(head uint64, root common.Hash) (uint64, if _, err := state.New(newHeadBlock.Root(), bc.stateCache, bc.snaps); err != nil { log.Trace("Block state missing, rewinding further", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash()) if pivot == nil || newHeadBlock.NumberU64() > *pivot { - newHeadBlock = bc.GetBlock(newHeadBlock.ParentHash(), newHeadBlock.NumberU64()-1) - continue + parent := bc.GetBlock(newHeadBlock.ParentHash(), newHeadBlock.NumberU64()-1) + if parent != nil { + newHeadBlock = parent + continue + } + log.Error("Missing block in the middle, aiming genesis", "number", newHeadBlock.NumberU64()-1, "hash", newHeadBlock.ParentHash()) + newHeadBlock = bc.genesisBlock } else { log.Trace("Rewind passed pivot, aiming genesis", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash(), "pivot", *pivot) newHeadBlock = bc.genesisBlock @@ -664,12 +670,8 @@ func (bc *BlockChain) CurrentBlock() *types.Block { return bc.currentBlock.Load().(*types.Block) } -// Snapshot returns the blockchain snapshot tree. This method is mainly used for -// testing, to make it possible to verify the snapshot after execution. -// -// Warning: There are no guarantees about the safety of using the returned 'snap' if the -// blockchain is simultaneously importing blocks, so take care. -func (bc *BlockChain) Snapshot() *snapshot.Tree { +// Snapshots returns the blockchain snapshot tree. +func (bc *BlockChain) Snapshots() *snapshot.Tree { return bc.snaps } @@ -1922,6 +1924,17 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er return it.index, err } // No validation errors for the first block (or chain prefix skipped) + var activeState *state.StateDB + defer func() { + // The chain importer is starting and stopping trie prefetchers. If a bad + // block or other error is hit however, an early return may not properly + // terminate the background threads. This defer ensures that we clean up + // and dangling prefetcher, without defering each and holding on live refs. + if activeState != nil { + activeState.StopPrefetcher() + } + }() + for ; block != nil && err == nil || err == ErrKnownBlock; block, err = it.next() { // If the chain is terminating, stop processing blocks if bc.insertStopped() { @@ -1982,12 +1995,17 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er if err != nil { return it.index, err } + // Enable prefetching to pull in trie node paths while processing transactions + statedb.StartPrefetcher("chain") + activeState = statedb + // If we have a followup block, run that against the current state to pre-cache // transactions and probabilistically some of the account/storage trie nodes. var followupInterrupt uint32 if !bc.cacheConfig.TrieCleanNoPrefetch { if followup, err := it.peek(); followup != nil && err == nil { throwaway, _ := state.New(parent.Root, bc.stateCache, bc.snaps) + go func(start time.Time, followup *types.Block, throwaway *state.StateDB, interrupt *uint32) { bc.prefetcher.Prefetch(followup, throwaway, bc.vmConfig, &followupInterrupt) @@ -2013,8 +2031,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete, we can mark them snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete, we can mark them snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete, we can mark them - - triehash := statedb.AccountHashes + statedb.StorageHashes // Save to not double count in validation + triehash := statedb.AccountHashes + statedb.StorageHashes // Save to not double count in validation trieproc := statedb.SnapshotAccountReads + statedb.AccountReads + statedb.AccountUpdates trieproc += statedb.SnapshotStorageReads + statedb.StorageReads + statedb.StorageUpdates @@ -2042,7 +2059,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er if err != nil { return it.index, err } - // Update the metrics touched during block commit accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them @@ -2580,26 +2596,9 @@ func (bc *BlockChain) maintainTxIndex(ancients uint64) { } } -// BadBlocks returns a list of the last 'bad blocks' that the client has seen on the network -func (bc *BlockChain) BadBlocks() []*types.Block { - blocks := make([]*types.Block, 0, bc.badBlocks.Len()) - for _, hash := range bc.badBlocks.Keys() { - if blk, exist := bc.badBlocks.Peek(hash); exist { - block := blk.(*types.Block) - blocks = append(blocks, block) - } - } - return blocks -} - -// addBadBlock adds a bad block to the bad-block LRU cache -func (bc *BlockChain) addBadBlock(block *types.Block) { - bc.badBlocks.Add(block.Hash(), block) -} - // reportBlock logs a bad block error. func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) { - bc.addBadBlock(block) + rawdb.WriteBadBlock(bc.db, block) var receiptString string for i, receipt := range receipts { @@ -2640,12 +2639,8 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i bc.wg.Add(1) defer bc.wg.Done() - - whFunc := func(header *types.Header) error { - _, err := bc.hc.WriteHeader(header) - return err - } - return bc.hc.InsertHeaderChain(chain, whFunc, start) + _, err := bc.hc.InsertHeaderChain(chain, start) + return 0, err } // CurrentHeader retrieves the current head header of the canonical chain. The diff --git a/core/blockchain_remotefreezer_test.go b/core/blockchain_remotefreezer_test.go index a255e81a0d..58d1ac0d1d 100644 --- a/core/blockchain_remotefreezer_test.go +++ b/core/blockchain_remotefreezer_test.go @@ -80,7 +80,7 @@ func testRPCRemoteFreezer(t *testing.T) (rpcFreezerEndpoint string, server *rpc. t.Log("Using external freezer:", rpcFreezerEndpoint) } - ancientDb, err := rawdb.NewDatabaseWithFreezerRemote(rawdb.NewMemoryDatabase(), rpcFreezerEndpoint) + ancientDb, err := rawdb.NewDatabaseWithFreezerRemote(rawdb.NewMemoryDatabase(), rpcFreezerEndpoint, false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -424,7 +424,7 @@ func TestTransactionIndices_RemoteFreezer(t *testing.T) { // Init block chain with external ancients, check all needed indices has been indexed. limit := []uint64{0, 32, 64, 128} for _, l := range limit { - ancientDb, err := rawdb.NewDatabaseWithFreezerRemote(rawdb.NewMemoryDatabase(), freezerRPCEndpoint) + ancientDb, err := rawdb.NewDatabaseWithFreezerRemote(rawdb.NewMemoryDatabase(), freezerRPCEndpoint, false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -444,7 +444,7 @@ func TestTransactionIndices_RemoteFreezer(t *testing.T) { } // Reconstruct a block chain which only reserves HEAD-64 tx indices - ancientDb, err = rawdb.NewDatabaseWithFreezerRemote(rawdb.NewMemoryDatabase(), freezerRPCEndpoint) + ancientDb, err = rawdb.NewDatabaseWithFreezerRemote(rawdb.NewMemoryDatabase(), freezerRPCEndpoint, false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } diff --git a/core/blockchain_repair_test.go b/core/blockchain_repair_test.go index f35fb62636..cd13767b99 100644 --- a/core/blockchain_repair_test.go +++ b/core/blockchain_repair_test.go @@ -1763,7 +1763,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) { } os.RemoveAll(datadir) - db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "") + db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false) if err != nil { t.Fatalf("Failed to create persistent database: %v", err) } @@ -1818,7 +1818,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) { } // Force run a freeze cycle type freezer interface { - Freeze(threshold uint64) + Freeze(threshold uint64) error Ancients() (uint64, error) } db.(freezer).Freeze(tt.freezeThreshold) @@ -1831,7 +1831,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) { db.Close() // Start a new blockchain back up and see where the repait leads us - db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "") + db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false) if err != nil { t.Fatalf("Failed to reopen persistent database: %v", err) } diff --git a/core/blockchain_sethead_test.go b/core/blockchain_sethead_test.go index 8d2af33822..ee1f951f61 100644 --- a/core/blockchain_sethead_test.go +++ b/core/blockchain_sethead_test.go @@ -1962,7 +1962,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) { } os.RemoveAll(datadir) - db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "") + db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false) if err != nil { t.Fatalf("Failed to create persistent database: %v", err) } @@ -2024,7 +2024,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) { } // Force run a freeze cycle type freezer interface { - Freeze(threshold uint64) + Freeze(threshold uint64) error Ancients() (uint64, error) } db.(freezer).Freeze(tt.freezeThreshold) diff --git a/core/blockchain_snapshot_test.go b/core/blockchain_snapshot_test.go index 5b7d7823e0..2eda07d4aa 100644 --- a/core/blockchain_snapshot_test.go +++ b/core/blockchain_snapshot_test.go @@ -28,28 +28,19 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/types/genesisT" ) -// snapshotTest is a test case for snapshot recovery. It can be used for -// simulating these scenarios: -// (i) Geth restarts normally with valid legacy snapshot -// (ii) Geth restarts normally with valid new-format snapshot -// (iii) Geth restarts after the crash, with broken legacy snapshot -// (iv) Geth restarts after the crash, with broken new-format snapshot -// (v) Geth restarts normally, but it's requested to be rewound to a lower point via SetHead -// (vi) Geth restarts normally with a stale snapshot -type snapshotTest struct { - legacy bool // Flag whether the loaded snapshot is in legacy format - crash bool // Flag whether the Geth restarts from the previous crash - restartCrash int // Number of blocks to insert after the normal stop, then the crash happens - gapped int // Number of blocks to insert without enabling snapshot - setHead uint64 // Block number to set head back to - +// snapshotTestBasic wraps the common testing fields in the snapshot tests. +type snapshotTestBasic struct { + legacy bool // Wether write the snapshot journal in legacy format chainBlocks int // Number of blocks to generate for the canonical chain snapshotBlock uint64 // Block number of the relevant snapshot disk layer commitBlock uint64 // Block number for which to commit the state to disk @@ -59,56 +50,418 @@ type snapshotTest struct { expHeadFastBlock uint64 // Block number of the expected head fast sync block expHeadBlock uint64 // Block number of the expected head full block expSnapshotBottom uint64 // The block height corresponding to the snapshot disk layer + + // share fields, set in runtime + datadir string + db ethdb.Database + gendb ethdb.Database + engine consensus.Engine +} + +func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Block) { + // Create a temporary persistent database + datadir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("Failed to create temporary datadir: %v", err) + } + os.RemoveAll(datadir) + + db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false) + if err != nil { + t.Fatalf("Failed to create persistent database: %v", err) + } + // Initialize a fresh chain + var ( + genesis = MustCommitGenesis(db, new(genesisT.Genesis)) + engine = ethash.NewFullFaker() + gendb = rawdb.NewMemoryDatabase() + + // Snapshot is enabled, the first snapshot is created from the Genesis. + // The snapshot memory allowance is 256MB, it means no snapshot flush + // will happen during the block insertion. + cacheConfig = defaultCacheConfig + ) + chain, err := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("Failed to create chain: %v", err) + } + blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, gendb, basic.chainBlocks, func(i int, b *BlockGen) {}) + + // Insert the blocks with configured settings. + var breakpoints []uint64 + if basic.commitBlock > basic.snapshotBlock { + breakpoints = append(breakpoints, basic.snapshotBlock, basic.commitBlock) + } else { + breakpoints = append(breakpoints, basic.commitBlock, basic.snapshotBlock) + } + var startPoint uint64 + for _, point := range breakpoints { + if _, err := chain.InsertChain(blocks[startPoint:point]); err != nil { + t.Fatalf("Failed to import canonical chain start: %v", err) + } + startPoint = point + + if basic.commitBlock > 0 && basic.commitBlock == point { + chain.stateCache.TrieDB().Commit(blocks[point-1].Root(), true, nil) + } + if basic.snapshotBlock > 0 && basic.snapshotBlock == point { + if basic.legacy { + // Here we commit the snapshot disk root to simulate + // committing the legacy snapshot. + rawdb.WriteSnapshotRoot(db, blocks[point-1].Root()) + } else { + // Flushing the entire snap tree into the disk, the + // relavant (a) snapshot root and (b) snapshot generator + // will be persisted atomically. + chain.snaps.Cap(blocks[point-1].Root(), 0) + diskRoot, blockRoot := chain.snaps.DiskRoot(), blocks[point-1].Root() + if !bytes.Equal(diskRoot.Bytes(), blockRoot.Bytes()) { + t.Fatalf("Failed to flush disk layer change, want %x, got %x", blockRoot, diskRoot) + } + } + } + } + if _, err := chain.InsertChain(blocks[startPoint:]); err != nil { + t.Fatalf("Failed to import canonical chain tail: %v", err) + } + + // Set runtime fields + basic.datadir = datadir + basic.db = db + basic.gendb = gendb + basic.engine = engine + + // Ugly hack, notify the chain to flush the journal in legacy format + // if it's requested. + if basic.legacy { + chain.writeLegacyJournal = true + } + return chain, blocks +} + +func (basic *snapshotTestBasic) verify(t *testing.T, chain *BlockChain, blocks []*types.Block) { + // Iterate over all the remaining blocks and ensure there are no gaps + verifyNoGaps(t, chain, true, blocks) + verifyCutoff(t, chain, true, blocks, basic.expCanonicalBlocks) + + if head := chain.CurrentHeader(); head.Number.Uint64() != basic.expHeadHeader { + t.Errorf("Head header mismatch: have %d, want %d", head.Number, basic.expHeadHeader) + } + if head := chain.CurrentFastBlock(); head.NumberU64() != basic.expHeadFastBlock { + t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), basic.expHeadFastBlock) + } + if head := chain.CurrentBlock(); head.NumberU64() != basic.expHeadBlock { + t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), basic.expHeadBlock) + } + + // Check the disk layer, ensure they are matched + block := chain.GetBlockByNumber(basic.expSnapshotBottom) + if block == nil { + t.Errorf("The correspnding block[%d] of snapshot disk layer is missing", basic.expSnapshotBottom) + } else if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) { + t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot()) + } + + // Check the snapshot, ensure it's integrated + if err := chain.snaps.Verify(block.Root()); err != nil { + t.Errorf("The disk layer is not integrated %v", err) + } } -func (tt *snapshotTest) dump() string { +func (basic *snapshotTestBasic) dump() string { buffer := new(strings.Builder) fmt.Fprint(buffer, "Chain:\n G") - for i := 0; i < tt.chainBlocks; i++ { + for i := 0; i < basic.chainBlocks; i++ { fmt.Fprintf(buffer, "->C%d", i+1) } fmt.Fprint(buffer, " (HEAD)\n\n") fmt.Fprintf(buffer, "Commit: G") - if tt.commitBlock > 0 { - fmt.Fprintf(buffer, ", C%d", tt.commitBlock) + if basic.commitBlock > 0 { + fmt.Fprintf(buffer, ", C%d", basic.commitBlock) } fmt.Fprint(buffer, "\n") fmt.Fprintf(buffer, "Snapshot: G") - if tt.snapshotBlock > 0 { - fmt.Fprintf(buffer, ", C%d", tt.snapshotBlock) + if basic.snapshotBlock > 0 { + fmt.Fprintf(buffer, ", C%d", basic.snapshotBlock) } fmt.Fprint(buffer, "\n") - if tt.crash { - fmt.Fprintf(buffer, "\nCRASH\n\n") - } else { - fmt.Fprintf(buffer, "\nSetHead(%d)\n\n", tt.setHead) - } + //if crash { + // fmt.Fprintf(buffer, "\nCRASH\n\n") + //} else { + // fmt.Fprintf(buffer, "\nSetHead(%d)\n\n", basic.setHead) + //} fmt.Fprintf(buffer, "------------------------------\n\n") fmt.Fprint(buffer, "Expected in leveldb:\n G") - for i := 0; i < tt.expCanonicalBlocks; i++ { + for i := 0; i < basic.expCanonicalBlocks; i++ { fmt.Fprintf(buffer, "->C%d", i+1) } fmt.Fprintf(buffer, "\n\n") - fmt.Fprintf(buffer, "Expected head header : C%d\n", tt.expHeadHeader) - fmt.Fprintf(buffer, "Expected head fast block: C%d\n", tt.expHeadFastBlock) - if tt.expHeadBlock == 0 { + fmt.Fprintf(buffer, "Expected head header : C%d\n", basic.expHeadHeader) + fmt.Fprintf(buffer, "Expected head fast block: C%d\n", basic.expHeadFastBlock) + if basic.expHeadBlock == 0 { fmt.Fprintf(buffer, "Expected head block : G\n") } else { - fmt.Fprintf(buffer, "Expected head block : C%d\n", tt.expHeadBlock) + fmt.Fprintf(buffer, "Expected head block : C%d\n", basic.expHeadBlock) } - if tt.expSnapshotBottom == 0 { + if basic.expSnapshotBottom == 0 { fmt.Fprintf(buffer, "Expected snapshot disk : G\n") } else { - fmt.Fprintf(buffer, "Expected snapshot disk : C%d\n", tt.expSnapshotBottom) + fmt.Fprintf(buffer, "Expected snapshot disk : C%d\n", basic.expSnapshotBottom) } return buffer.String() } +func (basic *snapshotTestBasic) teardown() { + basic.db.Close() + basic.gendb.Close() + os.RemoveAll(basic.datadir) +} + +// snapshotTest is a test case type for normal snapshot recovery. +// It can be used for testing that restart Geth normally. +type snapshotTest struct { + snapshotTestBasic +} + +func (snaptest *snapshotTest) test(t *testing.T) { + // It's hard to follow the test case, visualize the input + // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + // fmt.Println(tt.dump()) + chain, blocks := snaptest.prepare(t) + + // Restart the chain normally + chain.Stop() + newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("Failed to recreate chain: %v", err) + } + defer newchain.Stop() + + snaptest.verify(t, newchain, blocks) +} + +// crashSnapshotTest is a test case type for innormal snapshot recovery. +// It can be used for testing that restart Geth after the crash. +type crashSnapshotTest struct { + snapshotTestBasic +} + +func (snaptest *crashSnapshotTest) test(t *testing.T) { + // It's hard to follow the test case, visualize the input + // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + // fmt.Println(tt.dump()) + chain, blocks := snaptest.prepare(t) + + // Pull the plug on the database, simulating a hard crash + db := chain.db + db.Close() + + // Start a new blockchain back up and see where the repair leads us + newdb, err := rawdb.NewLevelDBDatabaseWithFreezer(snaptest.datadir, 0, 0, snaptest.datadir, "", false) + if err != nil { + t.Fatalf("Failed to reopen persistent database: %v", err) + } + defer newdb.Close() + + // The interesting thing is: instead of starting the blockchain after + // the crash, we do restart twice here: one after the crash and one + // after the normal stop. It's used to ensure the broken snapshot + // can be detected all the time. + newchain, err := NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("Failed to recreate chain: %v", err) + } + newchain.Stop() + + newchain, err = NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("Failed to recreate chain: %v", err) + } + defer newchain.Stop() + + snaptest.verify(t, newchain, blocks) +} + +// gappedSnapshotTest is a test type used to test this scenario: +// - have a complete snapshot +// - restart without enabling the snapshot +// - insert a few blocks +// - restart with enabling the snapshot again +type gappedSnapshotTest struct { + snapshotTestBasic + gapped int // Number of blocks to insert without enabling snapshot +} + +func (snaptest *gappedSnapshotTest) test(t *testing.T) { + // It's hard to follow the test case, visualize the input + // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + // fmt.Println(tt.dump()) + chain, blocks := snaptest.prepare(t) + + // Insert blocks without enabling snapshot if gapping is required. + chain.Stop() + gappedBlocks, _ := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], snaptest.engine, snaptest.gendb, snaptest.gapped, func(i int, b *BlockGen) {}) + + // Insert a few more blocks without enabling snapshot + var cacheConfig = &CacheConfig{ + TrieCleanLimit: 256, + TrieDirtyLimit: 256, + TrieTimeLimit: 5 * time.Minute, + SnapshotLimit: 0, + } + newchain, err := NewBlockChain(snaptest.db, cacheConfig, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("Failed to recreate chain: %v", err) + } + newchain.InsertChain(gappedBlocks) + newchain.Stop() + + // Restart the chain with enabling the snapshot + newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("Failed to recreate chain: %v", err) + } + defer newchain.Stop() + + snaptest.verify(t, newchain, blocks) +} + +// setHeadSnapshotTest is the test type used to test this scenario: +// - have a complete snapshot +// - set the head to a lower point +// - restart +type setHeadSnapshotTest struct { + snapshotTestBasic + setHead uint64 // Block number to set head back to +} + +func (snaptest *setHeadSnapshotTest) test(t *testing.T) { + // It's hard to follow the test case, visualize the input + // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + // fmt.Println(tt.dump()) + chain, blocks := snaptest.prepare(t) + + // Rewind the chain if setHead operation is required. + chain.SetHead(snaptest.setHead) + chain.Stop() + + newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("Failed to recreate chain: %v", err) + } + defer newchain.Stop() + + snaptest.verify(t, newchain, blocks) +} + +// restartCrashSnapshotTest is the test type used to test this scenario: +// - have a complete snapshot +// - restart chain +// - insert more blocks with enabling the snapshot +// - commit the snapshot +// - crash +// - restart again +type restartCrashSnapshotTest struct { + snapshotTestBasic + newBlocks int +} + +func (snaptest *restartCrashSnapshotTest) test(t *testing.T) { + // It's hard to follow the test case, visualize the input + // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + // fmt.Println(tt.dump()) + chain, blocks := snaptest.prepare(t) + + // Firstly, stop the chain properly, with all snapshot journal + // and state committed. + chain.Stop() + + newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("Failed to recreate chain: %v", err) + } + newBlocks, _ := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], snaptest.engine, snaptest.gendb, snaptest.newBlocks, func(i int, b *BlockGen) {}) + newchain.InsertChain(newBlocks) + + // Commit the entire snapshot into the disk if requested. Note only + // (a) snapshot root and (b) snapshot generator will be committed, + // the diff journal is not. + newchain.Snapshots().Cap(newBlocks[len(newBlocks)-1].Root(), 0) + + // Simulate the blockchain crash + // Don't call chain.Stop here, so that no snapshot + // journal and latest state will be committed + + // Restart the chain after the crash + newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("Failed to recreate chain: %v", err) + } + defer newchain.Stop() + + snaptest.verify(t, newchain, blocks) +} + +// wipeCrashSnapshotTest is the test type used to test this scenario: +// - have a complete snapshot +// - restart, insert more blocks without enabling the snapshot +// - restart again with enabling the snapshot +// - crash +type wipeCrashSnapshotTest struct { + snapshotTestBasic + newBlocks int +} + +func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) { + // It's hard to follow the test case, visualize the input + // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + // fmt.Println(tt.dump()) + chain, blocks := snaptest.prepare(t) + + // Firstly, stop the chain properly, with all snapshot journal + // and state committed. + chain.Stop() + + config := &CacheConfig{ + TrieCleanLimit: 256, + TrieDirtyLimit: 256, + TrieTimeLimit: 5 * time.Minute, + SnapshotLimit: 0, + } + newchain, err := NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("Failed to recreate chain: %v", err) + } + newBlocks, _ := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], snaptest.engine, snaptest.gendb, snaptest.newBlocks, func(i int, b *BlockGen) {}) + newchain.InsertChain(newBlocks) + newchain.Stop() + + // Restart the chain, the wiper should starts working + config = &CacheConfig{ + TrieCleanLimit: 256, + TrieDirtyLimit: 256, + TrieTimeLimit: 5 * time.Minute, + SnapshotLimit: 256, + SnapshotWait: false, // Don't wait rebuild + } + newchain, err = NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("Failed to recreate chain: %v", err) + } + // Simulate the blockchain crash. + + newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("Failed to recreate chain: %v", err) + } + snaptest.verify(t, newchain, blocks) +} + // Tests a Geth restart with valid snapshot. Before the shutdown, all snapshot // journal will be persisted correctly. In this case no snapshot recovery is // required. @@ -130,20 +483,21 @@ func TestRestartWithNewSnapshot(t *testing.T) { // Expected head fast block: C8 // Expected head block : C8 // Expected snapshot disk : G - testSnapshot(t, &snapshotTest{ - legacy: false, - crash: false, - gapped: 0, - setHead: 0, - chainBlocks: 8, - snapshotBlock: 0, - commitBlock: 0, - expCanonicalBlocks: 8, - expHeadHeader: 8, - expHeadFastBlock: 8, - expHeadBlock: 8, - expSnapshotBottom: 0, // Initial disk layer built from genesis - }) + test := &snapshotTest{ + snapshotTestBasic{ + legacy: false, + chainBlocks: 8, + snapshotBlock: 0, + commitBlock: 0, + expCanonicalBlocks: 8, + expHeadHeader: 8, + expHeadFastBlock: 8, + expHeadBlock: 8, + expSnapshotBottom: 0, // Initial disk layer built from genesis + }, + } + test.test(t) + test.teardown() } // Tests a Geth restart with valid but "legacy" snapshot. Before the shutdown, @@ -167,20 +521,22 @@ func TestRestartWithLegacySnapshot(t *testing.T) { // Expected head fast block: C8 // Expected head block : C8 // Expected snapshot disk : G - testSnapshot(t, &snapshotTest{ - legacy: true, - crash: false, - gapped: 0, - setHead: 0, - chainBlocks: 8, - snapshotBlock: 0, - commitBlock: 0, - expCanonicalBlocks: 8, - expHeadHeader: 8, - expHeadFastBlock: 8, - expHeadBlock: 8, - expSnapshotBottom: 0, // Initial disk layer built from genesis - }) + t.Skip("Legacy format testing is not supported") + test := &snapshotTest{ + snapshotTestBasic{ + legacy: true, + chainBlocks: 8, + snapshotBlock: 0, + commitBlock: 0, + expCanonicalBlocks: 8, + expHeadHeader: 8, + expHeadFastBlock: 8, + expHeadBlock: 8, + expSnapshotBottom: 0, // Initial disk layer built from genesis + }, + } + test.test(t) + test.teardown() } // Tests a Geth was crashed and restarts with a broken snapshot. In this case the @@ -206,20 +562,21 @@ func TestNoCommitCrashWithNewSnapshot(t *testing.T) { // Expected head fast block: C8 // Expected head block : G // Expected snapshot disk : C4 - testSnapshot(t, &snapshotTest{ - legacy: false, - crash: true, - gapped: 0, - setHead: 0, - chainBlocks: 8, - snapshotBlock: 4, - commitBlock: 0, - expCanonicalBlocks: 8, - expHeadHeader: 8, - expHeadFastBlock: 8, - expHeadBlock: 0, - expSnapshotBottom: 4, // Last committed disk layer, wait recovery - }) + test := &crashSnapshotTest{ + snapshotTestBasic{ + legacy: false, + chainBlocks: 8, + snapshotBlock: 4, + commitBlock: 0, + expCanonicalBlocks: 8, + expHeadHeader: 8, + expHeadFastBlock: 8, + expHeadBlock: 0, + expSnapshotBottom: 4, // Last committed disk layer, wait recovery + }, + } + test.test(t) + test.teardown() } // Tests a Geth was crashed and restarts with a broken snapshot. In this case the @@ -245,20 +602,21 @@ func TestLowCommitCrashWithNewSnapshot(t *testing.T) { // Expected head fast block: C8 // Expected head block : C2 // Expected snapshot disk : C4 - testSnapshot(t, &snapshotTest{ - legacy: false, - crash: true, - gapped: 0, - setHead: 0, - chainBlocks: 8, - snapshotBlock: 4, - commitBlock: 2, - expCanonicalBlocks: 8, - expHeadHeader: 8, - expHeadFastBlock: 8, - expHeadBlock: 2, - expSnapshotBottom: 4, // Last committed disk layer, wait recovery - }) + test := &crashSnapshotTest{ + snapshotTestBasic{ + legacy: false, + chainBlocks: 8, + snapshotBlock: 4, + commitBlock: 2, + expCanonicalBlocks: 8, + expHeadHeader: 8, + expHeadFastBlock: 8, + expHeadBlock: 2, + expSnapshotBottom: 4, // Last committed disk layer, wait recovery + }, + } + test.test(t) + test.teardown() } // Tests a Geth was crashed and restarts with a broken snapshot. In this case @@ -284,20 +642,21 @@ func TestHighCommitCrashWithNewSnapshot(t *testing.T) { // Expected head fast block: C8 // Expected head block : G // Expected snapshot disk : C4 - testSnapshot(t, &snapshotTest{ - legacy: false, - crash: true, - gapped: 0, - setHead: 0, - chainBlocks: 8, - snapshotBlock: 4, - commitBlock: 6, - expCanonicalBlocks: 8, - expHeadHeader: 8, - expHeadFastBlock: 8, - expHeadBlock: 0, - expSnapshotBottom: 4, // Last committed disk layer, wait recovery - }) + test := &crashSnapshotTest{ + snapshotTestBasic{ + legacy: false, + chainBlocks: 8, + snapshotBlock: 4, + commitBlock: 6, + expCanonicalBlocks: 8, + expHeadHeader: 8, + expHeadFastBlock: 8, + expHeadBlock: 0, + expSnapshotBottom: 4, // Last committed disk layer, wait recovery + }, + } + test.test(t) + test.teardown() } // Tests a Geth was crashed and restarts with a broken and "legacy format" @@ -322,20 +681,22 @@ func TestNoCommitCrashWithLegacySnapshot(t *testing.T) { // Expected head fast block: C8 // Expected head block : G // Expected snapshot disk : G - testSnapshot(t, &snapshotTest{ - legacy: true, - crash: true, - gapped: 0, - setHead: 0, - chainBlocks: 8, - snapshotBlock: 4, - commitBlock: 0, - expCanonicalBlocks: 8, - expHeadHeader: 8, - expHeadFastBlock: 8, - expHeadBlock: 0, - expSnapshotBottom: 0, // Rebuilt snapshot from the latest HEAD(genesis) - }) + t.Skip("Legacy format testing is not supported") + test := &crashSnapshotTest{ + snapshotTestBasic{ + legacy: true, + chainBlocks: 8, + snapshotBlock: 4, + commitBlock: 0, + expCanonicalBlocks: 8, + expHeadHeader: 8, + expHeadFastBlock: 8, + expHeadBlock: 0, + expSnapshotBottom: 0, // Rebuilt snapshot from the latest HEAD(genesis) + }, + } + test.test(t) + test.teardown() } // Tests a Geth was crashed and restarts with a broken and "legacy format" @@ -360,20 +721,22 @@ func TestLowCommitCrashWithLegacySnapshot(t *testing.T) { // Expected head fast block: C8 // Expected head block : C2 // Expected snapshot disk : C2 - testSnapshot(t, &snapshotTest{ - legacy: true, - crash: true, - gapped: 0, - setHead: 0, - chainBlocks: 8, - snapshotBlock: 4, - commitBlock: 2, - expCanonicalBlocks: 8, - expHeadHeader: 8, - expHeadFastBlock: 8, - expHeadBlock: 2, - expSnapshotBottom: 2, // Rebuilt snapshot from the latest HEAD - }) + t.Skip("Legacy format testing is not supported") + test := &crashSnapshotTest{ + snapshotTestBasic{ + legacy: true, + chainBlocks: 8, + snapshotBlock: 4, + commitBlock: 2, + expCanonicalBlocks: 8, + expHeadHeader: 8, + expHeadFastBlock: 8, + expHeadBlock: 2, + expSnapshotBottom: 2, // Rebuilt snapshot from the latest HEAD + }, + } + test.test(t) + test.teardown() } // Tests a Geth was crashed and restarts with a broken and "legacy format" @@ -403,20 +766,22 @@ func TestHighCommitCrashWithLegacySnapshot(t *testing.T) { // Expected head fast block: C8 // Expected head block : G // Expected snapshot disk : G - testSnapshot(t, &snapshotTest{ - legacy: true, - crash: true, - gapped: 0, - setHead: 0, - chainBlocks: 8, - snapshotBlock: 4, - commitBlock: 6, - expCanonicalBlocks: 8, - expHeadHeader: 8, - expHeadFastBlock: 8, - expHeadBlock: 0, - expSnapshotBottom: 0, // Rebuilt snapshot from the latest HEAD(genesis) - }) + t.Skip("Legacy format testing is not supported") + test := &crashSnapshotTest{ + snapshotTestBasic{ + legacy: true, + chainBlocks: 8, + snapshotBlock: 4, + commitBlock: 6, + expCanonicalBlocks: 8, + expHeadHeader: 8, + expHeadFastBlock: 8, + expHeadBlock: 0, + expSnapshotBottom: 0, // Rebuilt snapshot from the latest HEAD(genesis) + }, + } + test.test(t) + test.teardown() } // Tests a Geth was running with snapshot enabled. Then restarts without @@ -440,20 +805,22 @@ func TestGappedNewSnapshot(t *testing.T) { // Expected head fast block: C10 // Expected head block : C10 // Expected snapshot disk : C10 - testSnapshot(t, &snapshotTest{ - legacy: false, - crash: false, - gapped: 2, - setHead: 0, - chainBlocks: 8, - snapshotBlock: 0, - commitBlock: 0, - expCanonicalBlocks: 10, - expHeadHeader: 10, - expHeadFastBlock: 10, - expHeadBlock: 10, - expSnapshotBottom: 10, // Rebuilt snapshot from the latest HEAD - }) + test := &gappedSnapshotTest{ + snapshotTestBasic: snapshotTestBasic{ + legacy: false, + chainBlocks: 8, + snapshotBlock: 0, + commitBlock: 0, + expCanonicalBlocks: 10, + expHeadHeader: 10, + expHeadFastBlock: 10, + expHeadBlock: 10, + expSnapshotBottom: 10, // Rebuilt snapshot from the latest HEAD + }, + gapped: 2, + } + test.test(t) + test.teardown() } // Tests a Geth was running with leagcy snapshot enabled. Then restarts @@ -477,20 +844,23 @@ func TestGappedLegacySnapshot(t *testing.T) { // Expected head fast block: C10 // Expected head block : C10 // Expected snapshot disk : C10 - testSnapshot(t, &snapshotTest{ - legacy: true, - crash: false, - gapped: 2, - setHead: 0, - chainBlocks: 8, - snapshotBlock: 0, - commitBlock: 0, - expCanonicalBlocks: 10, - expHeadHeader: 10, - expHeadFastBlock: 10, - expHeadBlock: 10, - expSnapshotBottom: 10, // Rebuilt snapshot from the latest HEAD - }) + t.Skip("Legacy format testing is not supported") + test := &gappedSnapshotTest{ + snapshotTestBasic: snapshotTestBasic{ + legacy: true, + chainBlocks: 8, + snapshotBlock: 0, + commitBlock: 0, + expCanonicalBlocks: 10, + expHeadHeader: 10, + expHeadFastBlock: 10, + expHeadBlock: 10, + expSnapshotBottom: 10, // Rebuilt snapshot from the latest HEAD + }, + gapped: 2, + } + test.test(t) + test.teardown() } // Tests the Geth was running with snapshot enabled and resetHead is applied. @@ -514,20 +884,22 @@ func TestSetHeadWithNewSnapshot(t *testing.T) { // Expected head fast block: C4 // Expected head block : C4 // Expected snapshot disk : G - testSnapshot(t, &snapshotTest{ - legacy: false, - crash: false, - gapped: 0, - setHead: 4, - chainBlocks: 8, - snapshotBlock: 0, - commitBlock: 0, - expCanonicalBlocks: 4, - expHeadHeader: 4, - expHeadFastBlock: 4, - expHeadBlock: 4, - expSnapshotBottom: 0, // The initial disk layer is built from the genesis - }) + test := &setHeadSnapshotTest{ + snapshotTestBasic: snapshotTestBasic{ + legacy: false, + chainBlocks: 8, + snapshotBlock: 0, + commitBlock: 0, + expCanonicalBlocks: 4, + expHeadHeader: 4, + expHeadFastBlock: 4, + expHeadBlock: 4, + expSnapshotBottom: 0, // The initial disk layer is built from the genesis + }, + setHead: 4, + } + test.test(t) + test.teardown() } // Tests the Geth was running with snapshot(legacy-format) enabled and resetHead @@ -551,20 +923,23 @@ func TestSetHeadWithLegacySnapshot(t *testing.T) { // Expected head fast block: C4 // Expected head block : C4 // Expected snapshot disk : G - testSnapshot(t, &snapshotTest{ - legacy: true, - crash: false, - gapped: 0, - setHead: 4, - chainBlocks: 8, - snapshotBlock: 0, - commitBlock: 0, - expCanonicalBlocks: 4, - expHeadHeader: 4, - expHeadFastBlock: 4, - expHeadBlock: 4, - expSnapshotBottom: 0, // The initial disk layer is built from the genesis - }) + t.Skip("Legacy format testing is not supported") + test := &setHeadSnapshotTest{ + snapshotTestBasic: snapshotTestBasic{ + legacy: true, + chainBlocks: 8, + snapshotBlock: 0, + commitBlock: 0, + expCanonicalBlocks: 4, + expHeadHeader: 4, + expHeadFastBlock: 4, + expHeadBlock: 4, + expSnapshotBottom: 0, // The initial disk layer is built from the genesis + }, + setHead: 4, + } + test.test(t) + test.teardown() } // Tests the Geth was running with snapshot(legacy-format) enabled and upgrades @@ -590,209 +965,60 @@ func TestRecoverSnapshotFromCrashWithLegacyDiffJournal(t *testing.T) { // Expected head fast block: C10 // Expected head block : C8 // Expected snapshot disk : C10 - testSnapshot(t, &snapshotTest{ - legacy: true, - crash: false, - restartCrash: 2, - gapped: 0, - setHead: 0, - chainBlocks: 8, - snapshotBlock: 0, - commitBlock: 0, - expCanonicalBlocks: 10, - expHeadHeader: 10, - expHeadFastBlock: 10, - expHeadBlock: 8, // The persisted state in the first running - expSnapshotBottom: 10, // The persisted disk layer in the second running - }) -} - -func testSnapshot(t *testing.T, tt *snapshotTest) { - // It's hard to follow the test case, visualize the input - // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) - // fmt.Println(tt.dump()) - - // Create a temporary persistent database - datadir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatalf("Failed to create temporary datadir: %v", err) + t.Skip("Legacy format testing is not supported") + test := &restartCrashSnapshotTest{ + snapshotTestBasic: snapshotTestBasic{ + legacy: true, + chainBlocks: 8, + snapshotBlock: 0, + commitBlock: 0, + expCanonicalBlocks: 10, + expHeadHeader: 10, + expHeadFastBlock: 10, + expHeadBlock: 8, // The persisted state in the first running + expSnapshotBottom: 10, // The persisted disk layer in the second running + }, + newBlocks: 2, } - os.RemoveAll(datadir) - - db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "") - if err != nil { - t.Fatalf("Failed to create persistent database: %v", err) - } - defer db.Close() // Might double close, should be fine - - // Initialize a fresh chain - var ( - genesis = MustCommitGenesis(db, new(genesisT.Genesis)) - engine = ethash.NewFullFaker() - gendb = rawdb.NewMemoryDatabase() - - // Snapshot is enabled, the first snapshot is created from the Genesis. - // The snapshot memory allowance is 256MB, it means no snapshot flush - // will happen during the block insertion. - cacheConfig = defaultCacheConfig - ) - chain, err := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("Failed to create chain: %v", err) - } - blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, gendb, tt.chainBlocks, func(i int, b *BlockGen) {}) - - // Insert the blocks with configured settings. - var breakpoints []uint64 - if tt.commitBlock > tt.snapshotBlock { - breakpoints = append(breakpoints, tt.snapshotBlock, tt.commitBlock) - } else { - breakpoints = append(breakpoints, tt.commitBlock, tt.snapshotBlock) - } - var startPoint uint64 - for _, point := range breakpoints { - if _, err := chain.InsertChain(blocks[startPoint:point]); err != nil { - t.Fatalf("Failed to import canonical chain start: %v", err) - } - startPoint = point - - if tt.commitBlock > 0 && tt.commitBlock == point { - chain.stateCache.TrieDB().Commit(blocks[point-1].Root(), true, nil) - } - if tt.snapshotBlock > 0 && tt.snapshotBlock == point { - if tt.legacy { - // Here we commit the snapshot disk root to simulate - // committing the legacy snapshot. - rawdb.WriteSnapshotRoot(db, blocks[point-1].Root()) - } else { - chain.snaps.Cap(blocks[point-1].Root(), 0) - diskRoot, blockRoot := chain.snaps.DiskRoot(), blocks[point-1].Root() - if !bytes.Equal(diskRoot.Bytes(), blockRoot.Bytes()) { - t.Fatalf("Failed to flush disk layer change, want %x, got %x", blockRoot, diskRoot) - } - } - } - } - if _, err := chain.InsertChain(blocks[startPoint:]); err != nil { - t.Fatalf("Failed to import canonical chain tail: %v", err) - } - // Set the flag for writing legacy journal if necessary - if tt.legacy { - chain.writeLegacyJournal = true - } - // Pull the plug on the database, simulating a hard crash - if tt.crash { - db.Close() - - // Start a new blockchain back up and see where the repair leads us - db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "") - if err != nil { - t.Fatalf("Failed to reopen persistent database: %v", err) - } - defer db.Close() - - // The interesting thing is: instead of start the blockchain after - // the crash, we do restart twice here: one after the crash and one - // after the normal stop. It's used to ensure the broken snapshot - // can be detected all the time. - chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("Failed to recreate chain: %v", err) - } - chain.Stop() - - chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("Failed to recreate chain: %v", err) - } - defer chain.Stop() - } else if tt.gapped > 0 { - // Insert blocks without enabling snapshot if gapping is required. - chain.Stop() - gappedBlocks, _ := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, gendb, tt.gapped, func(i int, b *BlockGen) {}) - - // Insert a few more blocks without enabling snapshot - var cacheConfig = &CacheConfig{ - TrieCleanLimit: 256, - TrieDirtyLimit: 256, - TrieTimeLimit: 5 * time.Minute, - SnapshotLimit: 0, - } - chain, err = NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("Failed to recreate chain: %v", err) - } - chain.InsertChain(gappedBlocks) - chain.Stop() - - chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("Failed to recreate chain: %v", err) - } - defer chain.Stop() - } else if tt.setHead != 0 { - // Rewind the chain if setHead operation is required. - chain.SetHead(tt.setHead) - chain.Stop() - - chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("Failed to recreate chain: %v", err) - } - defer chain.Stop() - } else if tt.restartCrash != 0 { - // Firstly, stop the chain properly, with all snapshot journal - // and state committed. - chain.Stop() - - // Restart chain, forcibly flush the disk layer journal with new format - newBlocks, _ := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, gendb, tt.restartCrash, func(i int, b *BlockGen) {}) - chain, err = NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("Failed to recreate chain: %v", err) - } - chain.InsertChain(newBlocks) - chain.Snapshot().Cap(newBlocks[len(newBlocks)-1].Root(), 0) - - // Simulate the blockchain crash - // Don't call chain.Stop here, so that no snapshot - // journal and latest state will be committed - - // Restart the chain after the crash - chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("Failed to recreate chain: %v", err) - } - defer chain.Stop() - } else { - chain.Stop() - - // Restart the chain normally - chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("Failed to recreate chain: %v", err) - } - defer chain.Stop() - } - - // Iterate over all the remaining blocks and ensure there are no gaps - verifyNoGaps(t, chain, true, blocks) - verifyCutoff(t, chain, true, blocks, tt.expCanonicalBlocks) + test.test(t) + test.teardown() +} - if head := chain.CurrentHeader(); head.Number.Uint64() != tt.expHeadHeader { - t.Errorf("Head header mismatch: have %d, want %d", head.Number, tt.expHeadHeader) - } - if head := chain.CurrentFastBlock(); head.NumberU64() != tt.expHeadFastBlock { - t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadFastBlock) - } - if head := chain.CurrentBlock(); head.NumberU64() != tt.expHeadBlock { - t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadBlock) - } - // Check the disk layer, ensure they are matched - block := chain.GetBlockByNumber(tt.expSnapshotBottom) - if block == nil { - t.Errorf("The correspnding block[%d] of snapshot disk layer is missing", tt.expSnapshotBottom) - } else if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) { - t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot()) +// Tests the Geth was running with a complete snapshot and then imports a few +// more new blocks on top without enabling the snapshot. After the restart, +// crash happens. Check everything is ok after the restart. +func TestRecoverSnapshotFromWipingCrash(t *testing.T) { + // Chain: + // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) + // + // Commit: G + // Snapshot: G + // + // SetHead(0) + // + // ------------------------------ + // + // Expected in leveldb: + // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10 + // + // Expected head header : C10 + // Expected head fast block: C10 + // Expected head block : C8 + // Expected snapshot disk : C10 + test := &wipeCrashSnapshotTest{ + snapshotTestBasic: snapshotTestBasic{ + legacy: false, + chainBlocks: 8, + snapshotBlock: 4, + commitBlock: 0, + expCanonicalBlocks: 10, + expHeadHeader: 10, + expHeadFastBlock: 10, + expHeadBlock: 10, + expSnapshotBottom: 10, + }, + newBlocks: 2, } + test.test(t) + test.teardown() } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 26ee56cab7..5f7c3a89f0 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -17,6 +17,7 @@ package core import ( + "errors" "fmt" "io/ioutil" "math/big" @@ -471,7 +472,7 @@ func testBadHashes(t *testing.T, full bool) { _, err = blockchain.InsertHeaderChain(headers, 1) } - if err != ErrBlacklistedHash { + if !errors.Is(err, ErrBlacklistedHash) { t.Errorf("error mismatch: have: %v, want: %v", err, ErrBlacklistedHash) } } @@ -602,7 +603,7 @@ func TestFastVsFullChains(t *testing.T) { Alloc: genesisT.GenesisAlloc{address: {Balance: funds}}, } genesis = MustCommitGenesis(gendb, gspec) - signer = types.NewEIP155Signer(gspec.Config.GetChainID()) + signer = types.LatestSigner(gspec.Config) ) blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, 1024, func(i int, block *BlockGen) { block.SetCoinbase(common.Address{0x00}) @@ -653,7 +654,7 @@ func TestFastVsFullChains(t *testing.T) { t.Fatalf("failed to create temp freezer dir: %v", err) } defer os.Remove(frdir) - ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "") + ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -685,12 +686,12 @@ func TestFastVsFullChains(t *testing.T) { } if fblock, arblock, anblock := fast.GetBlockByHash(hash), archive.GetBlockByHash(hash), ancient.GetBlockByHash(hash); fblock.Hash() != arblock.Hash() || anblock.Hash() != arblock.Hash() { t.Errorf("block #%d [%x]: block mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock, anblock, arblock) - } else if types.DeriveSha(fblock.Transactions(), new(trie.Trie)) != types.DeriveSha(arblock.Transactions(), new(trie.Trie)) || types.DeriveSha(anblock.Transactions(), new(trie.Trie)) != types.DeriveSha(arblock.Transactions(), new(trie.Trie)) { + } else if types.DeriveSha(fblock.Transactions(), trie.NewStackTrie(nil)) != types.DeriveSha(arblock.Transactions(), trie.NewStackTrie(nil)) || types.DeriveSha(anblock.Transactions(), trie.NewStackTrie(nil)) != types.DeriveSha(arblock.Transactions(), trie.NewStackTrie(nil)) { t.Errorf("block #%d [%x]: transactions mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Transactions(), anblock.Transactions(), arblock.Transactions()) } else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) || types.CalcUncleHash(anblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) { t.Errorf("block #%d [%x]: uncles mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Uncles(), anblock, arblock.Uncles()) } - if freceipts, anreceipts, areceipts := rawdb.ReadReceipts(fastDb, hash, *rawdb.ReadHeaderNumber(fastDb, hash), fast.Config()), rawdb.ReadReceipts(ancientDb, hash, *rawdb.ReadHeaderNumber(ancientDb, hash), fast.Config()), rawdb.ReadReceipts(archiveDb, hash, *rawdb.ReadHeaderNumber(archiveDb, hash), fast.Config()); types.DeriveSha(freceipts, new(trie.Trie)) != types.DeriveSha(areceipts, new(trie.Trie)) { + if freceipts, anreceipts, areceipts := rawdb.ReadReceipts(fastDb, hash, *rawdb.ReadHeaderNumber(fastDb, hash), fast.Config()), rawdb.ReadReceipts(ancientDb, hash, *rawdb.ReadHeaderNumber(ancientDb, hash), fast.Config()), rawdb.ReadReceipts(archiveDb, hash, *rawdb.ReadHeaderNumber(archiveDb, hash), fast.Config()); types.DeriveSha(freceipts, trie.NewStackTrie(nil)) != types.DeriveSha(areceipts, trie.NewStackTrie(nil)) { t.Errorf("block #%d [%x]: receipts mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, freceipts, anreceipts, areceipts) } } @@ -727,7 +728,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { t.Fatalf("failed to create temp freezer dir: %v", err) } defer os.Remove(dir) - db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), dir, "") + db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), dir, "", false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -841,7 +842,7 @@ func TestChainTxReorgs(t *testing.T) { }, } genesis = MustCommitGenesis(db, gspec) - signer = types.NewEIP155Signer(gspec.Config.GetChainID()) + signer = types.LatestSigner(gspec.Config) ) // Create two transactions shared between the chains: @@ -946,7 +947,7 @@ func TestLogReorgs(t *testing.T) { code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") gspec = &genesisT.Genesis{Config: params.TestChainConfig, Alloc: genesisT.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} genesis = MustCommitGenesis(db, gspec) - signer = types.NewEIP155Signer(gspec.Config.GetChainID()) + signer = types.LatestSigner(gspec.Config) ) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) @@ -1000,7 +1001,7 @@ func TestLogRebirth(t *testing.T) { db = rawdb.NewMemoryDatabase() gspec = &genesisT.Genesis{Config: params.TestChainConfig, Alloc: genesisT.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} genesis = MustCommitGenesis(db, gspec) - signer = types.NewEIP155Signer(gspec.Config.GetChainID()) + signer = types.LatestSigner(gspec.Config) engine = ethash.NewFaker() blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil) ) @@ -1064,7 +1065,7 @@ func TestSideLogRebirth(t *testing.T) { db = rawdb.NewMemoryDatabase() gspec = &genesisT.Genesis{Config: params.TestChainConfig, Alloc: genesisT.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} genesis = MustCommitGenesis(db, gspec) - signer = types.NewEIP155Signer(gspec.Config.GetChainID()) + signer = types.LatestSigner(gspec.Config) blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) ) @@ -1137,7 +1138,7 @@ func TestReorgSideEvent(t *testing.T) { Alloc: genesisT.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}, } genesis = MustCommitGenesis(db, gspec) - signer = types.NewEIP155Signer(gspec.Config.GetChainID()) + signer = types.LatestSigner(gspec.Config) ) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) @@ -1267,9 +1268,10 @@ func TestEIP155Transition(t *testing.T) { deleteAddr = common.Address{1} gspec = &genesisT.Genesis{ Config: &goethereum.ChainConfig{ - ChainID: big.NewInt(1), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int), + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(2), }, Alloc: genesisT.GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}}, } @@ -1279,7 +1281,7 @@ func TestEIP155Transition(t *testing.T) { blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) defer blockchain.Stop() - blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) { + blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(blockIndex int, block *BlockGen) { var ( tx *types.Transaction err error @@ -1287,7 +1289,7 @@ func TestEIP155Transition(t *testing.T) { return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key) } ) - switch i { + switch blockIndex { case 0: tx, err = basicTx(types.HomesteadSigner{}) if err != nil { @@ -1301,19 +1303,21 @@ func TestEIP155Transition(t *testing.T) { } block.AddTx(tx) - tx, err = basicTx(types.NewEIP155Signer(gspec.Config.GetChainID())) + tx, err = basicTx(types.LatestSigner(gspec.Config)) if err != nil { t.Fatal(err) } block.AddTx(tx) case 3: + // 0 tx, err = basicTx(types.HomesteadSigner{}) if err != nil { t.Fatal(err) } block.AddTx(tx) - tx, err = basicTx(types.NewEIP155Signer(gspec.Config.GetChainID())) + // 1 + tx, err = basicTx(types.LatestSigner(gspec.Config)) if err != nil { t.Fatal(err) } @@ -1334,7 +1338,8 @@ func TestEIP155Transition(t *testing.T) { t.Error("Expected block[3].txs[0] to not be replay protected") } if !block.Transactions()[1].Protected() { - t.Error("Expected block[3].txs[1] to be replay protected") + tx := block.Transactions()[1] + t.Errorf("Expected block[3].txs[1] to be replay protected; tx.chainid=%d", tx.ChainId()) } if _, err := blockchain.InsertChain(blocks[4:]); err != nil { t.Fatal(err) @@ -1351,7 +1356,7 @@ func TestEIP155Transition(t *testing.T) { } ) if i == 0 { - tx, err = basicTx(types.NewEIP155Signer(big.NewInt(2))) + tx, err = basicTx(types.LatestSigner(config)) if err != nil { t.Fatal(err) } @@ -1359,8 +1364,8 @@ func TestEIP155Transition(t *testing.T) { } }) _, err := blockchain.InsertChain(blocks) - if err != types.ErrInvalidChainId { - t.Error("expected error:", types.ErrInvalidChainId) + if !errors.Is(err, types.ErrInvalidChainId) { + t.Errorf("expected error: %q, got: %q", types.ErrInvalidChainId, err) } } @@ -1391,7 +1396,7 @@ func TestEIP161AccountRemoval(t *testing.T) { var ( tx *types.Transaction err error - signer = types.NewEIP155Signer(gspec.Config.GetChainID()) + signer = types.LatestSigner(gspec.Config) ) switch i { case 0: @@ -1598,7 +1603,7 @@ func TestBlockchainRecovery(t *testing.T) { } defer os.Remove(frdir) - ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "") + ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -1655,7 +1660,7 @@ func TestIncompleteAncientReceiptChainInsertion(t *testing.T) { t.Fatalf("failed to create temp freezer dir: %v", err) } defer os.Remove(frdir) - ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "") + ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -1854,7 +1859,7 @@ func testInsertKnownChainData(t *testing.T, typ string) { t.Fatalf("failed to create temp freezer dir: %v", err) } defer os.Remove(dir) - chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), dir, "") + chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), dir, "", false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -2084,7 +2089,7 @@ func TestTransactionIndices(t *testing.T) { funds = big.NewInt(1000000000) gspec = &genesisT.Genesis{Config: params.TestChainConfig, Alloc: genesisT.GenesisAlloc{address: {Balance: funds}}} genesis = MustCommitGenesis(gendb, gspec) - signer = types.NewEIP155Signer(gspec.Config.GetChainID()) + signer = types.LatestSigner(gspec.Config) ) height := uint64(128) blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), func(i int, block *BlockGen) { @@ -2134,7 +2139,7 @@ func TestTransactionIndices(t *testing.T) { t.Fatalf("failed to create temp freezer dir: %v", err) } defer os.Remove(frdir) - ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "") + ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -2162,7 +2167,7 @@ func TestTransactionIndices(t *testing.T) { // Init block chain with external ancients, check all needed indices has been indexed. limit := []uint64{0, 32, 64, 128} for _, l := range limit { - ancientDb, err = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "") + ancientDb, err = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -2182,7 +2187,7 @@ func TestTransactionIndices(t *testing.T) { } // Reconstruct a block chain which only reserves HEAD-64 tx indices - ancientDb, err = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "") + ancientDb, err = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -2211,7 +2216,7 @@ func TestSkipStaleTxIndicesInFastSync(t *testing.T) { funds = big.NewInt(1000000000) gspec = &genesisT.Genesis{Config: params.TestChainConfig, Alloc: genesisT.GenesisAlloc{address: {Balance: funds}}} genesis = MustCommitGenesis(gendb, gspec) - signer = types.NewEIP155Signer(gspec.Config.GetChainID()) + signer = types.LatestSigner(gspec.Config) ) height := uint64(128) blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), func(i int, block *BlockGen) { @@ -2261,7 +2266,7 @@ func TestSkipStaleTxIndicesInFastSync(t *testing.T) { t.Fatalf("failed to create temp freezer dir: %v", err) } defer os.Remove(frdir) - ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "") + ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -3036,3 +3041,81 @@ func TestInitThenFailCreateContract(t *testing.T) { } } } + +// TestEIP2718Transition tests that an EIP-2718 transaction will be accepted +// after the fork block has passed. This is verified by sending an EIP-2930 +// access list transaction, which specifies a single slot access, and then +// checking that the gas usage of a hot SLOAD and a cold SLOAD are calculated +// correctly. +func TestEIP2718Transition(t *testing.T) { + var ( + aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") + + // Generate a canonical chain to act as the main dataset + engine = ethash.NewFaker() + db = rawdb.NewMemoryDatabase() + + // A sender who makes transactions, has some funds + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000) + gspec = &genesisT.Genesis{ + Config: params.YoloV3ChainConfig, + Alloc: genesisT.GenesisAlloc{ + address: {Balance: funds}, + // The address 0xAAAA sloads 0x00 and 0x01 + aa: { + Code: []byte{ + byte(vm.PC), + byte(vm.PC), + byte(vm.SLOAD), + byte(vm.SLOAD), + }, + Nonce: 0, + Balance: big.NewInt(0), + }, + }, + } + genesis = MustCommitGenesis(db, gspec) + ) + + blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *BlockGen) { + b.SetCoinbase(common.Address{1}) + + // One transaction to 0xAAAA + signer := types.LatestSigner(gspec.Config) + tx, _ := types.SignNewTx(key, signer, &types.AccessListTx{ + ChainID: gspec.Config.GetChainID(), + Nonce: 0, + To: &aa, + Gas: 30000, + GasPrice: big.NewInt(1), + AccessList: types.AccessList{{ + Address: aa, + StorageKeys: []common.Hash{{0}}, + }}, + }) + b.AddTx(tx) + }) + + // Import the canonical chain + diskdb := rawdb.NewMemoryDatabase() + MustCommitGenesis(diskdb, gspec) + + chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + if n, err := chain.InsertChain(blocks); err != nil { + t.Fatalf("block %d: failed to insert into chain: %v", n, err) + } + + block := chain.GetBlockByNumber(1) + + // Expected gas is intrinsic + 2 * pc + hot load + cold load, since only one load is in the access list + expected := vars.TxGas + vars.TxAccessListAddressGas + vars.TxAccessListStorageKeyGas + vm.GasQuickStep*2 + vm.WarmStorageReadCostEIP2929 + vm.ColdSloadCostEIP2929 + if block.GasUsed() != expected { + t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed()) + + } +} diff --git a/core/bloom_indexer.go b/core/bloom_indexer.go new file mode 100644 index 0000000000..856746a1c0 --- /dev/null +++ b/core/bloom_indexer.go @@ -0,0 +1,92 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "context" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/bitutil" + "github.com/ethereum/go-ethereum/core/bloombits" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" +) + +const ( + // bloomThrottling is the time to wait between processing two consecutive index + // sections. It's useful during chain upgrades to prevent disk overload. + bloomThrottling = 100 * time.Millisecond +) + +// BloomIndexer implements a core.ChainIndexer, building up a rotated bloom bits index +// for the Ethereum header bloom filters, permitting blazing fast filtering. +type BloomIndexer struct { + size uint64 // section size to generate bloombits for + db ethdb.Database // database instance to write index data and metadata into + gen *bloombits.Generator // generator to rotate the bloom bits crating the bloom index + section uint64 // Section is the section number being processed currently + head common.Hash // Head is the hash of the last header processed +} + +// NewBloomIndexer returns a chain indexer that generates bloom bits data for the +// canonical chain for fast logs filtering. +func NewBloomIndexer(db ethdb.Database, size, confirms uint64) *ChainIndexer { + backend := &BloomIndexer{ + db: db, + size: size, + } + table := rawdb.NewTable(db, string(rawdb.BloomBitsIndexPrefix)) + + return NewChainIndexer(db, table, backend, size, confirms, bloomThrottling, "bloombits") +} + +// Reset implements core.ChainIndexerBackend, starting a new bloombits index +// section. +func (b *BloomIndexer) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error { + gen, err := bloombits.NewGenerator(uint(b.size)) + b.gen, b.section, b.head = gen, section, common.Hash{} + return err +} + +// Process implements core.ChainIndexerBackend, adding a new header's bloom into +// the index. +func (b *BloomIndexer) Process(ctx context.Context, header *types.Header) error { + b.gen.AddBloom(uint(header.Number.Uint64()-b.section*b.size), header.Bloom) + b.head = header.Hash() + return nil +} + +// Commit implements core.ChainIndexerBackend, finalizing the bloom section and +// writing it out into the database. +func (b *BloomIndexer) Commit() error { + batch := b.db.NewBatch() + for i := 0; i < types.BloomBitLength; i++ { + bits, err := b.gen.Bitset(uint(i)) + if err != nil { + return err + } + rawdb.WriteBloomBits(batch, uint(i), b.section, b.head, bitutil.CompressBytes(bits)) + } + return batch.Write() +} + +// Prune returns an empty error since we don't support pruning here. +func (b *BloomIndexer) Prune(threshold uint64) error { + return nil +} diff --git a/core/bloombits/matcher_test.go b/core/bloombits/matcher_test.go index 91143e525e..923579221f 100644 --- a/core/bloombits/matcher_test.go +++ b/core/bloombits/matcher_test.go @@ -30,6 +30,7 @@ const testSectionSize = 4096 // Tests that wildcard filter rules (nil) can be specified and are handled well. func TestMatcherWildcards(t *testing.T) { + t.Parallel() matcher := NewMatcher(testSectionSize, [][][]byte{ {common.Address{}.Bytes(), common.Address{0x01}.Bytes()}, // Default address is not a wildcard {common.Hash{}.Bytes(), common.Hash{0x01}.Bytes()}, // Default hash is not a wildcard @@ -56,6 +57,7 @@ func TestMatcherWildcards(t *testing.T) { // Tests the matcher pipeline on a single continuous workflow without interrupts. func TestMatcherContinuous(t *testing.T) { + t.Parallel() testMatcherDiffBatches(t, [][]bloomIndexes{{{10, 20, 30}}}, 0, 100000, false, 75) testMatcherDiffBatches(t, [][]bloomIndexes{{{32, 3125, 100}}, {{40, 50, 10}}}, 0, 100000, false, 81) testMatcherDiffBatches(t, [][]bloomIndexes{{{4, 8, 11}, {7, 8, 17}}, {{9, 9, 12}, {15, 20, 13}}, {{18, 15, 15}, {12, 10, 4}}}, 0, 10000, false, 36) @@ -64,6 +66,7 @@ func TestMatcherContinuous(t *testing.T) { // Tests the matcher pipeline on a constantly interrupted and resumed work pattern // with the aim of ensuring data items are requested only once. func TestMatcherIntermittent(t *testing.T) { + t.Parallel() testMatcherDiffBatches(t, [][]bloomIndexes{{{10, 20, 30}}}, 0, 100000, true, 75) testMatcherDiffBatches(t, [][]bloomIndexes{{{32, 3125, 100}}, {{40, 50, 10}}}, 0, 100000, true, 81) testMatcherDiffBatches(t, [][]bloomIndexes{{{4, 8, 11}, {7, 8, 17}}, {{9, 9, 12}, {15, 20, 13}}, {{18, 15, 15}, {12, 10, 4}}}, 0, 10000, true, 36) @@ -71,6 +74,7 @@ func TestMatcherIntermittent(t *testing.T) { // Tests the matcher pipeline on random input to hopefully catch anomalies. func TestMatcherRandom(t *testing.T) { + t.Parallel() for i := 0; i < 10; i++ { testMatcherBothModes(t, makeRandomIndexes([]int{1}, 50), 0, 10000, 0) testMatcherBothModes(t, makeRandomIndexes([]int{3}, 50), 0, 10000, 0) @@ -84,6 +88,7 @@ func TestMatcherRandom(t *testing.T) { // shifter from a multiple of 8. This is needed to cover an optimisation with // bitset matching https://github.com/ethereum/go-ethereum/issues/15309. func TestMatcherShifted(t *testing.T) { + t.Parallel() // Block 0 always matches in the tests, skip ahead of first 8 blocks with the // start to get a potential zero byte in the matcher bitset. @@ -97,6 +102,7 @@ func TestMatcherShifted(t *testing.T) { // Tests that matching on everything doesn't crash (special case internally). func TestWildcardMatcher(t *testing.T) { + t.Parallel() testMatcherBothModes(t, nil, 0, 10000, 0) } diff --git a/core/bloombits/scheduler_test.go b/core/bloombits/scheduler_test.go index 70772e4ab9..707e8ea11d 100644 --- a/core/bloombits/scheduler_test.go +++ b/core/bloombits/scheduler_test.go @@ -35,6 +35,7 @@ func TestSchedulerMultiClientSingleFetcher(t *testing.T) { testScheduler(t, 10, func TestSchedulerMultiClientMultiFetcher(t *testing.T) { testScheduler(t, 10, 10, 5000) } func testScheduler(t *testing.T, clients int, fetchers int, requests int) { + t.Parallel() f := newScheduler(0) // Create a batch of handler goroutines that respond to bloom bit requests and @@ -88,10 +89,10 @@ func testScheduler(t *testing.T, clients int, fetchers int, requests int) { } close(in) }() - + b := new(big.Int) for j := 0; j < requests; j++ { bits := <-out - if want := new(big.Int).SetUint64(uint64(j)).Bytes(); !bytes.Equal(bits, want) { + if want := b.SetUint64(uint64(j)).Bytes(); !bytes.Equal(bits, want) { t.Errorf("vector %d: delivered content mismatch: have %x, want %x", j, bits, want) } } diff --git a/core/chain_indexer_test.go b/core/chain_indexer_test.go index b76203dc8f..f099609015 100644 --- a/core/chain_indexer_test.go +++ b/core/chain_indexer_test.go @@ -18,6 +18,7 @@ package core import ( "context" + "errors" "fmt" "math/big" "math/rand" @@ -224,7 +225,10 @@ func (b *testChainIndexBackend) Process(ctx context.Context, header *types.Heade //t.processCh <- header.Number.Uint64() select { case <-time.After(10 * time.Second): - b.t.Fatal("Unexpected call to Process") + b.t.Error("Unexpected call to Process") + // Can't use Fatal since this is not the test's goroutine. + // Returning error stops the chainIndexer's updateLoop + return errors.New("Unexpected call to Process") case b.processCh <- header.Number.Uint64(): } return nil diff --git a/core/chain_makers.go b/core/chain_makers.go index 73ec19cec7..0297462111 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -114,6 +114,11 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { b.receipts = append(b.receipts, receipt) } +// GetBalance returns the balance of the given address at the generated block. +func (b *BlockGen) GetBalance(addr common.Address) *big.Int { + return b.statedb.GetBalance(addr) +} + // AddUncheckedTx forcefully adds a transaction to the block without any // validation. // @@ -286,8 +291,7 @@ func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethd } type fakeChainReader struct { - config ctypes.ChainConfigurator - genesis *types.Block + config ctypes.ChainConfigurator } // Config returns the chain configuration. diff --git a/core/error.go b/core/error.go index 5a28be7e1c..197dd81567 100644 --- a/core/error.go +++ b/core/error.go @@ -16,7 +16,11 @@ package core -import "errors" +import ( + "errors" + + "github.com/ethereum/go-ethereum/core/types" +) var ( // ErrKnownBlock is returned when a block to import is already known locally. @@ -63,4 +67,8 @@ var ( // ErrIntrinsicGas is returned if the transaction is specified to use less gas // than required to start the invocation. ErrIntrinsicGas = errors.New("intrinsic gas too low") + + // ErrTxTypeNotSupported is returned if a transaction is not supported in the + // current network configuration. + ErrTxTypeNotSupported = types.ErrTxTypeNotSupported ) diff --git a/core/evm.go b/core/evm.go index 8abe5a0477..8f69d51499 100644 --- a/core/evm.go +++ b/core/evm.go @@ -35,8 +35,8 @@ type ChainContext interface { GetHeader(common.Hash, uint64) *types.Header } -// NewEVMContext creates a new context for use in the EVM. -func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author *common.Address) vm.Context { +// NewEVMBlockContext creates a new context for use in the EVM. +func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address) vm.BlockContext { // If we don't have an explicit author (i.e. not mining), extract from the header var beneficiary common.Address if author == nil { @@ -44,17 +44,23 @@ func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author } else { beneficiary = *author } - return vm.Context{ + return vm.BlockContext{ CanTransfer: CanTransfer, Transfer: Transfer, GetHash: GetHashFn(header, chain), - Origin: msg.From(), Coinbase: beneficiary, BlockNumber: new(big.Int).Set(header.Number), Time: new(big.Int).SetUint64(header.Time), Difficulty: new(big.Int).Set(header.Difficulty), GasLimit: header.GasLimit, - GasPrice: new(big.Int).Set(msg.GasPrice()), + } +} + +// NewEVMTxContext creates a new transaction context for a single transaction. +func NewEVMTxContext(msg Message) vm.TxContext { + return vm.TxContext{ + Origin: msg.From(), + GasPrice: new(big.Int).Set(msg.GasPrice()), } } diff --git a/core/forkid/forkid.go b/core/forkid/forkid.go index a7d528fef2..df9a2efe3a 100644 --- a/core/forkid/forkid.go +++ b/core/forkid/forkid.go @@ -82,6 +82,15 @@ func NewID(config ctypes.ChainConfigurator, genesis common.Hash, head uint64) ID return ID{Hash: checksumToBytes(hash), Next: next} } +// NewIDWithChain calculates the Ethereum fork ID from an existing chain instance. +func NewIDWithChain(chain Blockchain) ID { + return NewID( + chain.Config(), + chain.Genesis().Hash(), + chain.CurrentHeader().Number.Uint64(), + ) +} + // NewFilter creates a filter that returns if a fork ID should be rejected or not // based on the local chain's status. func NewFilter(chain Blockchain) Filter { diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index 4db78db617..4fd2bcbbc4 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -53,24 +53,26 @@ func TestCreation(t *testing.T) { params.MainnetChainConfig, params.MainnetGenesisHash, []testcase{ - {0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Unsynced - {1149999, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Last Frontier block - {1150000, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // First Homestead block - {1919999, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // Last Homestead block - {1920000, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // First DAO block - {2462999, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // Last DAO block - {2463000, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // First Tangerine block - {2674999, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // Last Tangerine block - {2675000, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // First Spurious block - {4369999, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // Last Spurious block - {4370000, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // First Byzantium block - {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // Last Byzantium block - {7280000, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // First and last Constantinople, first Petersburg block - {9068999, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // Last Petersburg block - {9069000, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // First Istanbul and first Muir Glacier block - {9199999, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // Last Istanbul and first Muir Glacier block - {9200000, ID{Hash: checksumToBytes(0xe029e991), Next: 0}}, // First Muir Glacier block - {10000000, ID{Hash: checksumToBytes(0xe029e991), Next: 0}}, // Future Muir Glacier block + {0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Unsynced + {1149999, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Last Frontier block + {1150000, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // First Homestead block + {1919999, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // Last Homestead block + {1920000, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // First DAO block + {2462999, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // Last DAO block + {2463000, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // First Tangerine block + {2674999, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // Last Tangerine block + {2675000, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // First Spurious block + {4369999, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // Last Spurious block + {4370000, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // First Byzantium block + {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // Last Byzantium block + {7280000, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // First and last Constantinople, first Petersburg block + {9068999, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // Last Petersburg block + {9069000, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // First Istanbul and first Muir Glacier block + {9199999, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // Last Istanbul and first Muir Glacier block + {9200000, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // First Muir Glacier block + {12243999, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // Last Muir Glacier block + {12244000, ID{Hash: checksumToBytes(0x0eb440f6), Next: 0}}, // First Berlin block + {20000000, ID{Hash: checksumToBytes(0x0eb440f6), Next: 0}}, // Future Berlin block }, }, // Ropsten test cases @@ -91,8 +93,10 @@ func TestCreation(t *testing.T) { {6485845, ID{Hash: checksumToBytes(0xd6e2149b), Next: 6485846}}, // Last Petersburg block {6485846, ID{Hash: checksumToBytes(0x4bc66396), Next: 7117117}}, // First Istanbul block {7117116, ID{Hash: checksumToBytes(0x4bc66396), Next: 7117117}}, // Last Istanbul block - {7117117, ID{Hash: checksumToBytes(0x6727ef90), Next: 0}}, // First Muir Glacier block - {7500000, ID{Hash: checksumToBytes(0x6727ef90), Next: 0}}, // Future + {7117117, ID{Hash: checksumToBytes(0x6727ef90), Next: 9812189}}, // First Muir Glacier block + {9812188, ID{Hash: checksumToBytes(0x6727ef90), Next: 9812189}}, // Last Muir Glacier block + {9812189, ID{Hash: checksumToBytes(0xa157d377), Next: 0}}, // First Berlin block + {10000000, ID{Hash: checksumToBytes(0xa157d377), Next: 0}}, // Future Berlin block }, }, // Rinkeby test cases @@ -112,8 +116,10 @@ func TestCreation(t *testing.T) { {4321233, ID{Hash: checksumToBytes(0xe49cab14), Next: 4321234}}, // Last Constantinople block {4321234, ID{Hash: checksumToBytes(0xafec6b27), Next: 5435345}}, // First Petersburg block {5435344, ID{Hash: checksumToBytes(0xafec6b27), Next: 5435345}}, // Last Petersburg block - {5435345, ID{Hash: checksumToBytes(0xcbdb8838), Next: 0}}, // First Istanbul block - {6000000, ID{Hash: checksumToBytes(0xcbdb8838), Next: 0}}, // Future Istanbul block + {5435345, ID{Hash: checksumToBytes(0xcbdb8838), Next: 8290928}}, // First Istanbul block + {8290927, ID{Hash: checksumToBytes(0xcbdb8838), Next: 8290928}}, // Last Istanbul block + {8290928, ID{Hash: checksumToBytes(0x6910c8bd), Next: 0}}, // First Berlin block + {10000000, ID{Hash: checksumToBytes(0x6910c8bd), Next: 0}}, // Future Berlin block }, }, // Goerli test cases @@ -124,8 +130,10 @@ func TestCreation(t *testing.T) { []testcase{ {0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople and first Petersburg block {1561650, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Last Petersburg block - {1561651, ID{Hash: checksumToBytes(0xc25efa5c), Next: 0}}, // First Istanbul block - {2000000, ID{Hash: checksumToBytes(0xc25efa5c), Next: 0}}, // Future Istanbul block + {1561651, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // First Istanbul block + {4460643, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // Last Istanbul block + {4460644, ID{Hash: checksumToBytes(0x757a1c47), Next: 0}}, // First Berlin block + {5000000, ID{Hash: checksumToBytes(0x757a1c47), Next: 0}}, // Future Berlin block }, }, { @@ -276,11 +284,11 @@ func TestValidation(t *testing.T) { // Local is mainnet Petersburg, remote is Rinkeby Petersburg. {7987396, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale}, - // Local is mainnet Muir Glacier, far in the future. Remote announces Gopherium (non existing fork) + // Local is mainnet Berlin, far in the future. Remote announces Gopherium (non existing fork) // at some future block 88888888, for itself, but past block for local. Local is incompatible. // // This case detects non-upgraded nodes with majority hash power (typical Ropsten mess). - {88888888, ID{Hash: checksumToBytes(0xe029e991), Next: 88888888}, ErrLocalIncompatibleOrStale}, + {88888888, ID{Hash: checksumToBytes(0x0eb440f6), Next: 88888888}, ErrLocalIncompatibleOrStale}, // Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing // fork) at block 7279999, before Petersburg. Local is incompatible. @@ -331,7 +339,7 @@ func TestGatherForks(t *testing.T) { { "mainnet", params.MainnetChainConfig, - []uint64{1150000, 1920000, 2463000, 2675000, 4370000, 7280000, 9069000, 9200000}, + []uint64{1150000, 1920000, 2463000, 2675000, 4370000, 7280000, 9069000, 9200000, 12_244_000}, }, { "mordor", diff --git a/core/genesis.go b/core/genesis.go index 1f6a7319b4..54e4394e38 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -34,11 +34,12 @@ import ( "github.com/ethereum/go-ethereum/trie" ) +// SetupGenesisBlock wraps SetupGenesisBlockWithOverride, always using a nil value for the override. func SetupGenesisBlock(db ethdb.Database, genesis *genesisT.Genesis) (ctypes.ChainConfigurator, common.Hash, error) { return SetupGenesisBlockWithOverride(db, genesis, nil) } -// SetupGenesisBlock writes or updates the genesis block in db. +// SetupGenesisBlockWithOverride writes or updates the genesis block in db. // The block that will be used is: // // genesis == nil genesis != nil @@ -51,7 +52,7 @@ func SetupGenesisBlock(db ethdb.Database, genesis *genesisT.Genesis) (ctypes.Cha // error is a *params.ConfigCompatError and the new, unwritten config is returned. // // The returned chain configuration is never nil. -func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *genesisT.Genesis, ecbp1100 *big.Int) (ctypes.ChainConfigurator, common.Hash, error) { +func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *genesisT.Genesis, overrideMagneto *big.Int) (ctypes.ChainConfigurator, common.Hash, error) { if genesis != nil && confp.IsEmpty(genesis.Config) { return params.AllEthashProtocolChanges, common.Hash{}, genesisT.ErrGenesisNoConfig } @@ -66,9 +67,18 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *genesisT.Genesis, log.Info("Writing custom genesis block") } - if ecbp1100 != nil { - n := ecbp1100.Uint64() - if err := genesis.SetECBP1100Transition(&n); err != nil { + if overrideMagneto != nil { + n := overrideMagneto.Uint64() + if err := genesis.SetEIP2565Transition(&n); err != nil { + return genesis, stored, err + } + if err := genesis.SetEIP2929Transition(&n); err != nil { + return genesis, stored, err + } + if err := genesis.SetEIP2718Transition(&n); err != nil { + return genesis, stored, err + } + if err := genesis.SetEIP2930Transition(&n); err != nil { return genesis, stored, err } } @@ -80,11 +90,10 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *genesisT.Genesis, log.Info("Wrote custom genesis block OK", "config", genesis.Config) return genesis.Config, block.Hash(), nil } - // We have the genesis block in database(perhaps in ancient database) // but the corresponding state is missing. header := rawdb.ReadHeader(db, stored, 0) - if _, err := state.New(header.Root, state.NewDatabaseWithCache(db, 0, ""), nil); err != nil { + if _, err := state.New(header.Root, state.NewDatabaseWithConfig(db, nil), nil); err != nil { if genesis == nil { genesis = params.DefaultGenesisBlock() } @@ -99,7 +108,6 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *genesisT.Genesis, } return genesis.Config, block.Hash(), nil } - // Check whether the genesis block is already written. if genesis != nil { hash := GenesisToBlock(genesis, nil).Hash() @@ -107,13 +115,21 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *genesisT.Genesis, return genesis.Config, hash, &genesisT.GenesisMismatchError{Stored: stored, New: hash} } } - // Get the existing chain configuration. newcfg := configOrDefault(genesis, stored) - if ecbp1100 != nil { - n := ecbp1100.Uint64() - if err := newcfg.SetECBP1100Transition(&n); err != nil { + if overrideMagneto != nil { + n := overrideMagneto.Uint64() + if err := newcfg.SetEIP2565Transition(&n); err != nil { + return newcfg, stored, err + } + if err := newcfg.SetEIP2929Transition(&n); err != nil { + return newcfg, stored, err + } + if err := newcfg.SetEIP2718Transition(&n); err != nil { + return newcfg, stored, err + } + if err := newcfg.SetEIP2930Transition(&n); err != nil { return newcfg, stored, err } } @@ -139,7 +155,6 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *genesisT.Genesis, log.Info("Found non-defaulty stored config, using it.") return storedcfg, stored, nil } - // Check config compatibility and write the config. Compatibility errors // are returned to the caller unless we're already at block zero. height := rawdb.ReadHeaderNumber(db, rawdb.ReadHeadHeaderHash(db)) @@ -170,8 +185,8 @@ func configOrDefault(g *genesisT.Genesis, ghash common.Hash) ctypes.ChainConfigu return params.MordorChainConfig case ghash == params.RopstenGenesisHash: return params.RopstenChainConfig - case ghash == params.YoloV2GenesisHash: - return params.YoloV2ChainConfig + case ghash == params.YoloV3GenesisHash: + return params.YoloV3ChainConfig default: return params.AllEthashProtocolChanges } @@ -215,7 +230,7 @@ func GenesisToBlock(g *genesisT.Genesis, db ethdb.Database) *types.Block { statedb.Commit(false) statedb.Database().TrieDB().Commit(root, true, nil) - return types.NewBlock(head, nil, nil, nil, new(trie.Trie)) + return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)) } // CommitGenesis writes the block and state of a genesis specification to the database. diff --git a/core/headerchain.go b/core/headerchain.go index 77a0506c2c..678c54450b 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -129,121 +129,203 @@ func (hc *HeaderChain) GetBlockNumber(hash common.Hash) *uint64 { return number } -// WriteHeader writes a header into the local chain, given that its parent is -// already known. If the total difficulty of the newly inserted header becomes -// greater than the current known TD, the canonical chain is re-routed. +type headerWriteResult struct { + status WriteStatus + ignored int + imported int + lastHash common.Hash + lastHeader *types.Header +} + +// WriteHeaders writes a chain of headers into the local chain, given that the parents +// are already known. If the total difficulty of the newly inserted chain becomes +// greater than the current known TD, the canonical chain is reorged. // // Note: This method is not concurrent-safe with inserting blocks simultaneously // into the chain, as side effects caused by reorganisations cannot be emulated // without the real blocks. Hence, writing headers directly should only be done // in two scenarios: pure-header mode of operation (light clients), or properly // separated header/block phases (non-archive clients). -func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, err error) { - // Cache some values to prevent constant recalculation +func (hc *HeaderChain) writeHeaders(headers []*types.Header) (result *headerWriteResult, err error) { + if len(headers) == 0 { + return &headerWriteResult{}, nil + } + ptd := hc.GetTd(headers[0].ParentHash, headers[0].Number.Uint64()-1) + if ptd == nil { + return &headerWriteResult{}, consensus.ErrUnknownAncestor + } var ( - hash = header.Hash() - number = header.Number.Uint64() + lastNumber = headers[0].Number.Uint64() - 1 // Last successfully imported number + lastHash = headers[0].ParentHash // Last imported header hash + newTD = new(big.Int).Set(ptd) // Total difficulty of inserted chain + + lastHeader *types.Header + inserted []numberHash // Ephemeral lookup of number/hash for the chain + firstInserted = -1 // Index of the first non-ignored header ) - // Calculate the total difficulty of the header - ptd := hc.GetTd(header.ParentHash, number-1) - if ptd == nil { - return NonStatTy, consensus.ErrUnknownAncestor - } - head := hc.CurrentHeader().Number.Uint64() - localTd := hc.GetTd(hc.currentHeaderHash, head) - externTd := new(big.Int).Add(header.Difficulty, ptd) - - // Irrelevant of the canonical status, write the td and header to the database - // - // Note all the components of header(td, hash->number index and header) should - // be written atomically. - headerBatch := hc.chainDb.NewBatch() - rawdb.WriteTd(headerBatch, hash, number, externTd) - rawdb.WriteHeader(headerBatch, header) - if err := headerBatch.Write(); err != nil { - log.Crit("Failed to write header into disk", "err", err) + + batch := hc.chainDb.NewBatch() + for i, header := range headers { + var hash common.Hash + // The headers have already been validated at this point, so we already + // know that it's a contiguous chain, where + // headers[i].Hash() == headers[i+1].ParentHash + if i < len(headers)-1 { + hash = headers[i+1].ParentHash + } else { + hash = header.Hash() + } + number := header.Number.Uint64() + newTD.Add(newTD, header.Difficulty) + + // If the header is already known, skip it, otherwise store + if !hc.HasHeader(hash, number) { + // Irrelevant of the canonical status, write the TD and header to the database. + rawdb.WriteTd(batch, hash, number, newTD) + hc.tdCache.Add(hash, new(big.Int).Set(newTD)) + + rawdb.WriteHeader(batch, header) + inserted = append(inserted, numberHash{number, hash}) + hc.headerCache.Add(hash, header) + hc.numberCache.Add(hash, number) + if firstInserted < 0 { + firstInserted = i + } + } + lastHeader, lastHash, lastNumber = header, hash, number } + + // Skip the slow disk write of all headers if interrupted. + if hc.procInterrupt() { + log.Debug("Premature abort during headers import") + return &headerWriteResult{}, errors.New("aborted") + } + // Commit to disk! + if err := batch.Write(); err != nil { + log.Crit("Failed to write headers", "error", err) + } + batch.Reset() + + var ( + head = hc.CurrentHeader().Number.Uint64() + localTD = hc.GetTd(hc.currentHeaderHash, head) + status = SideStatTy + ) // If the total difficulty is higher than our known, add it to the canonical chain // Second clause in the if statement reduces the vulnerability to selfish mining. // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf - reorg := externTd.Cmp(localTd) > 0 - if !reorg && externTd.Cmp(localTd) == 0 { - if header.Number.Uint64() < head { + reorg := newTD.Cmp(localTD) > 0 + if !reorg && newTD.Cmp(localTD) == 0 { + if lastNumber < head { reorg = true - } else if header.Number.Uint64() == head { + } else if lastNumber == head { reorg = mrand.Float64() < 0.5 } } + // If the parent of the (first) block is already the canon header, + // we don't have to go backwards to delete canon blocks, but + // simply pile them onto the existing chain + chainAlreadyCanon := headers[0].ParentHash == hc.currentHeaderHash if reorg { // If the header can be added into canonical chain, adjust the // header chain markers(canonical indexes and head header flag). // // Note all markers should be written atomically. - - // Delete any canonical number assignments above the new head - markerBatch := hc.chainDb.NewBatch() - for i := number + 1; ; i++ { - hash := rawdb.ReadCanonicalHash(hc.chainDb, i) - if hash == (common.Hash{}) { - break + markerBatch := batch // we can reuse the batch to keep allocs down + if !chainAlreadyCanon { + // Delete any canonical number assignments above the new head + for i := lastNumber + 1; ; i++ { + hash := rawdb.ReadCanonicalHash(hc.chainDb, i) + if hash == (common.Hash{}) { + break + } + rawdb.DeleteCanonicalHash(markerBatch, i) + } + // Overwrite any stale canonical number assignments, going + // backwards from the first header in this import + var ( + headHash = headers[0].ParentHash // inserted[0].parent? + headNumber = headers[0].Number.Uint64() - 1 // inserted[0].num-1 ? + headHeader = hc.GetHeader(headHash, headNumber) + ) + for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash { + rawdb.WriteCanonicalHash(markerBatch, headHash, headNumber) + headHash = headHeader.ParentHash + headNumber = headHeader.Number.Uint64() - 1 + headHeader = hc.GetHeader(headHash, headNumber) + } + // If some of the older headers were already known, but obtained canon-status + // during this import batch, then we need to write that now + // Further down, we continue writing the staus for the ones that + // were not already known + for i := 0; i < firstInserted; i++ { + hash := headers[i].Hash() + num := headers[i].Number.Uint64() + rawdb.WriteCanonicalHash(markerBatch, hash, num) + rawdb.WriteHeadHeaderHash(markerBatch, hash) } - rawdb.DeleteCanonicalHash(markerBatch, i) } - - // Overwrite any stale canonical number assignments - var ( - headHash = header.ParentHash - headNumber = header.Number.Uint64() - 1 - headHeader = hc.GetHeader(headHash, headNumber) - ) - for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash { - rawdb.WriteCanonicalHash(markerBatch, headHash, headNumber) - - headHash = headHeader.ParentHash - headNumber = headHeader.Number.Uint64() - 1 - headHeader = hc.GetHeader(headHash, headNumber) + // Extend the canonical chain with the new headers + for _, hn := range inserted { + rawdb.WriteCanonicalHash(markerBatch, hn.hash, hn.number) + rawdb.WriteHeadHeaderHash(markerBatch, hn.hash) } - // Extend the canonical chain with the new header - rawdb.WriteCanonicalHash(markerBatch, hash, number) - rawdb.WriteHeadHeaderHash(markerBatch, hash) if err := markerBatch.Write(); err != nil { log.Crit("Failed to write header markers into disk", "err", err) } + markerBatch.Reset() // Last step update all in-memory head header markers - hc.currentHeaderHash = hash - hc.currentHeader.Store(types.CopyHeader(header)) - headHeaderGauge.Update(header.Number.Int64()) + hc.currentHeaderHash = lastHash + hc.currentHeader.Store(types.CopyHeader(lastHeader)) + headHeaderGauge.Update(lastHeader.Number.Int64()) + // Chain status is canonical since this insert was a reorg. + // Note that all inserts which have higher TD than existing are 'reorg'. status = CanonStatTy - } else { - status = SideStatTy } - hc.tdCache.Add(hash, externTd) - hc.headerCache.Add(hash, header) - hc.numberCache.Add(hash, number) - return -} -// WhCallback is a callback function for inserting individual headers. -// A callback is used for two reasons: first, in a LightChain, status should be -// processed and light chain events sent, while in a BlockChain this is not -// necessary since chain events are sent after inserting blocks. Second, the -// header writes should be protected by the parent chain mutex individually. -type WhCallback func(*types.Header) error + if len(inserted) == 0 { + status = NonStatTy + } + return &headerWriteResult{ + status: status, + ignored: len(headers) - len(inserted), + imported: len(inserted), + lastHash: lastHash, + lastHeader: lastHeader, + }, nil +} func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) (int, error) { // Do a sanity check that the provided chain is actually ordered and linked + // NOTE(meowsbits): These checks for nil heads are core-geth specific. + if len(chain) == 0 { + return 0, nil + } + if chain[0] == nil { + return 0, fmt.Errorf("header was nil") + } for i := 1; i < len(chain); i++ { if chain[i] == nil { return 0, fmt.Errorf("header was nil") } - if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 || chain[i].ParentHash != chain[i-1].Hash() { + if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 { + hash := chain[i].Hash() + parentHash := chain[i-1].Hash() // Chain broke ancestry, log a message (programming error) and skip insertion - log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", chain[i].Hash(), - "parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", chain[i-1].Hash()) + log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", hash, + "parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", parentHash) return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].Number, - chain[i-1].Hash().Bytes()[:4], i, chain[i].Number, chain[i].Hash().Bytes()[:4], chain[i].ParentHash[:4]) + parentHash.Bytes()[:4], i, chain[i].Number, hash.Bytes()[:4], chain[i].ParentHash[:4]) + } + // If the header is a banned one, straight out abort + if BadHashes[chain[i].ParentHash] { + return i - 1, ErrBlacklistedHash + } + // If it's the last header in the cunk, we need to check it too + if i == len(chain)-1 && BadHashes[chain[i].Hash()] { + return i, ErrBlacklistedHash } } @@ -251,7 +333,7 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) seals := make([]bool, len(chain)) if checkFreq != 0 { // In case of checkFreq == 0 all seals are left false. - for i := 0; i < len(seals)/checkFreq; i++ { + for i := 0; i <= len(seals)/checkFreq; i++ { index := i*checkFreq + hc.rand.Intn(checkFreq) if index >= len(seals) { index = len(seals) - 1 @@ -266,16 +348,12 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) defer close(abort) // Iterate over the headers and ensure they all check out - for i, header := range chain { + for i := 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") } - // 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 @@ -285,58 +363,41 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) return 0, nil } -// InsertHeaderChain attempts to insert the given header chain in to the local -// chain, possibly creating a reorg. If an error is returned, it will return the -// index number of the failing header as well an error describing what went wrong. +// InsertHeaderChain inserts the given headers. // -// The verify parameter can be used to fine tune whether nonce verification -// 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, 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 import") - return i, errors.New("aborted") - } - if header == nil { - return i, fmt.Errorf("nil header") - } - // If the header's already known, skip it, otherwise store - hash := header.Hash() - if hc.HasHeader(hash, header.Number.Uint64()) { - externTd := hc.GetTd(hash, header.Number.Uint64()) - localTd := hc.GetTd(hc.currentHeaderHash, hc.CurrentHeader().Number.Uint64()) - if externTd == nil || externTd.Cmp(localTd) <= 0 { - stats.ignored++ - continue - } - } - if err := writeHeader(header); err != nil { - return i, err - } - stats.processed++ +// The validity of the headers is NOT CHECKED by this method, i.e. they need to be +// validated by ValidateHeaderChain before calling InsertHeaderChain. +// +// This insert is all-or-nothing. If this returns an error, no headers were written, +// otherwise they were all processed successfully. +// +// The returned 'write status' says if the inserted headers are part of the canonical chain +// or a side chain. +func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, start time.Time) (WriteStatus, error) { + if hc.procInterrupt() { + return 0, errors.New("aborted") } - // Report some public statistics so the user has a clue what's going on - last := chain[len(chain)-1] + res, err := hc.writeHeaders(chain) + // Report some public statistics so the user has a clue what's going on context := []interface{}{ - "count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)), - "number", last.Number, "hash", last.Hash(), + "count", res.imported, + "elapsed", common.PrettyDuration(time.Since(start)), + } + if err != nil { + context = append(context, "err", err) } - if timestamp := time.Unix(int64(last.Time), 0); time.Since(timestamp) > time.Minute { - context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...) + if last := res.lastHeader; last != nil { + context = append(context, "number", last.Number, "hash", res.lastHash) + if timestamp := time.Unix(int64(last.Time), 0); time.Since(timestamp) > time.Minute { + context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...) + } } - if stats.ignored > 0 { - context = append(context, []interface{}{"ignored", stats.ignored}...) + if res.ignored > 0 { + context = append(context, []interface{}{"ignored", res.ignored}...) } log.Info("Imported new block headers", context...) - - return 0, nil + return res.status, err } // GetBlockHashesFromHash retrieves a number of block hashes starting at a given @@ -375,9 +436,8 @@ func (hc *HeaderChain) GetAncestor(hash common.Hash, number, ancestor uint64, ma // in this case it is cheaper to just read the header if header := hc.GetHeader(hash, number); header != nil { return header.ParentHash, number - 1 - } else { - return common.Hash{}, 0 } + return common.Hash{}, 0 } for ancestor != 0 { if rawdb.ReadCanonicalHash(hc.chainDb, number) == hash { diff --git a/core/headerchain_test.go b/core/headerchain_test.go new file mode 100644 index 0000000000..516ebe0a53 --- /dev/null +++ b/core/headerchain_test.go @@ -0,0 +1,116 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "errors" + "fmt" + "testing" + "time" + + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/params/types/genesisT" +) + +func verifyUnbrokenCanonchain(hc *HeaderChain) error { + h := hc.CurrentHeader() + for { + canonHash := rawdb.ReadCanonicalHash(hc.chainDb, h.Number.Uint64()) + if exp := h.Hash(); canonHash != exp { + return fmt.Errorf("Canon hash chain broken, block %d got %x, expected %x", + h.Number, canonHash[:8], exp[:8]) + } + // Verify that we have the TD + if td := rawdb.ReadTd(hc.chainDb, canonHash, h.Number.Uint64()); td == nil { + return fmt.Errorf("Canon TD missing at block %d", h.Number) + } + if h.Number.Uint64() == 0 { + break + } + h = hc.GetHeader(h.ParentHash, h.Number.Uint64()-1) + } + return nil +} + +func testInsert(t *testing.T, hc *HeaderChain, chain []*types.Header, wantStatus WriteStatus, wantErr error) { + t.Helper() + + status, err := hc.InsertHeaderChain(chain, time.Now()) + if status != wantStatus { + t.Errorf("wrong write status from InsertHeaderChain: got %v, want %v", status, wantStatus) + } + // Always verify that the header chain is unbroken + if err := verifyUnbrokenCanonchain(hc); err != nil { + t.Fatal(err) + } + if !errors.Is(err, wantErr) { + t.Fatalf("unexpected error from InsertHeaderChain: %v", err) + } +} + +// This test checks status reporting of InsertHeaderChain. +func TestHeaderInsertion(t *testing.T) { + var ( + db = rawdb.NewMemoryDatabase() + genesis = MustCommitGenesis(db, new(genesisT.Genesis)) + ) + + hc, err := NewHeaderChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), func() bool { return false }) + if err != nil { + t.Fatal(err) + } + // chain A: G->A1->A2...A128 + chainA := makeHeaderChain(genesis.Header(), 128, ethash.NewFaker(), db, 10) + // chain B: G->A1->B2...B128 + chainB := makeHeaderChain(chainA[0], 128, ethash.NewFaker(), db, 10) + log.Root().SetHandler(log.StdoutHandler) + + // Inserting 64 headers on an empty chain, expecting + // 1 callbacks, 1 canon-status, 0 sidestatus, + testInsert(t, hc, chainA[:64], CanonStatTy, nil) + + // Inserting 64 identical headers, expecting + // 0 callbacks, 0 canon-status, 0 sidestatus, + testInsert(t, hc, chainA[:64], NonStatTy, nil) + + // Inserting the same some old, some new headers + // 1 callbacks, 1 canon, 0 side + testInsert(t, hc, chainA[32:96], CanonStatTy, nil) + + // Inserting side blocks, but not overtaking the canon chain + testInsert(t, hc, chainB[0:32], SideStatTy, nil) + + // Inserting more side blocks, but we don't have the parent + testInsert(t, hc, chainB[34:36], NonStatTy, consensus.ErrUnknownAncestor) + + // Inserting more sideblocks, overtaking the canon chain + testInsert(t, hc, chainB[32:97], CanonStatTy, nil) + + // Inserting more A-headers, taking back the canonicality + testInsert(t, hc, chainA[90:100], CanonStatTy, nil) + + // And B becomes canon again + testInsert(t, hc, chainB[97:107], CanonStatTy, nil) + + // And B becomes even longer + testInsert(t, hc, chainB[107:128], CanonStatTy, nil) +} diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 9ff607ad91..7a8ee4ab4f 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/binary" "math/big" + "sort" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -702,6 +703,102 @@ func DeleteBlockWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number DeleteTd(db, hash, number) } +const badBlockToKeep = 10 + +type badBlock struct { + Header *types.Header + Body *types.Body +} + +// badBlockList implements the sort interface to allow sorting a list of +// bad blocks by their number in the reverse order. +type badBlockList []*badBlock + +func (s badBlockList) Len() int { return len(s) } +func (s badBlockList) Less(i, j int) bool { + return s[i].Header.Number.Uint64() < s[j].Header.Number.Uint64() +} +func (s badBlockList) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// ReadBadBlock retrieves the bad block with the corresponding block hash. +func ReadBadBlock(db ethdb.Reader, hash common.Hash) *types.Block { + blob, err := db.Get(badBlockKey) + if err != nil { + return nil + } + var badBlocks badBlockList + if err := rlp.DecodeBytes(blob, &badBlocks); err != nil { + return nil + } + for _, bad := range badBlocks { + if bad.Header.Hash() == hash { + return types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles) + } + } + return nil +} + +// ReadAllBadBlocks retrieves all the bad blocks in the database. +// All returned blocks are sorted in reverse order by number. +func ReadAllBadBlocks(db ethdb.Reader) []*types.Block { + blob, err := db.Get(badBlockKey) + if err != nil { + return nil + } + var badBlocks badBlockList + if err := rlp.DecodeBytes(blob, &badBlocks); err != nil { + return nil + } + var blocks []*types.Block + for _, bad := range badBlocks { + blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles)) + } + return blocks +} + +// WriteBadBlock serializes the bad block into the database. If the cumulated +// bad blocks exceeds the limitation, the oldest will be dropped. +func WriteBadBlock(db ethdb.KeyValueStore, block *types.Block) { + blob, err := db.Get(badBlockKey) + if err != nil { + log.Warn("Failed to load old bad blocks", "error", err) + } + var badBlocks badBlockList + if len(blob) > 0 { + if err := rlp.DecodeBytes(blob, &badBlocks); err != nil { + log.Crit("Failed to decode old bad blocks", "error", err) + } + } + for _, b := range badBlocks { + if b.Header.Number.Uint64() == block.NumberU64() && b.Header.Hash() == block.Hash() { + log.Info("Skip duplicated bad block", "number", block.NumberU64(), "hash", block.Hash()) + return + } + } + badBlocks = append(badBlocks, &badBlock{ + Header: block.Header(), + Body: block.Body(), + }) + sort.Sort(sort.Reverse(badBlocks)) + if len(badBlocks) > badBlockToKeep { + badBlocks = badBlocks[:badBlockToKeep] + } + data, err := rlp.EncodeToBytes(badBlocks) + if err != nil { + log.Crit("Failed to encode bad blocks", "err", err) + } + if err := db.Put(badBlockKey, data); err != nil { + log.Crit("Failed to write bad blocks", "err", err) + } +} + +// DeleteBadBlocks deletes all the bad blocks from the database +func DeleteBadBlocks(db ethdb.KeyValueWriter) { + if err := db.Delete(badBlockKey); err != nil { + log.Crit("Failed to delete bad blocks", "err", err) + } +} + // FindCommonAncestor returns the last common ancestor of two block headers func FindCommonAncestor(db ethdb.Reader, a, b *types.Header) *types.Header { for bn := b.Number.Uint64(); a.Number.Uint64() > bn; { @@ -728,3 +825,29 @@ func FindCommonAncestor(db ethdb.Reader, a, b *types.Header) *types.Header { } return a } + +// ReadHeadHeader returns the current canonical head header. +func ReadHeadHeader(db ethdb.Reader) *types.Header { + headHeaderHash := ReadHeadHeaderHash(db) + if headHeaderHash == (common.Hash{}) { + return nil + } + headHeaderNumber := ReadHeaderNumber(db, headHeaderHash) + if headHeaderNumber == nil { + return nil + } + return ReadHeader(db, headHeaderHash, *headHeaderNumber) +} + +// ReadHeadHeader returns the current canonical head block. +func ReadHeadBlock(db ethdb.Reader) *types.Block { + headBlockHash := ReadHeadBlockHash(db) + if headBlockHash == (common.Hash{}) { + return nil + } + headBlockNumber := ReadHeaderNumber(db, headBlockHash) + if headBlockNumber == nil { + return nil + } + return ReadBlock(db, headBlockHash, *headBlockNumber) +} diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 074c24d8fe..ea9dc436cf 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -22,6 +22,7 @@ import ( "fmt" "io/ioutil" "math/big" + "math/rand" "os" "reflect" "testing" @@ -188,6 +189,75 @@ func TestPartialBlockStorage(t *testing.T) { } } +// Tests block storage and retrieval operations. +func TestBadBlockStorage(t *testing.T) { + db := NewMemoryDatabase() + + // Create a test block to move around the database and make sure it's really new + block := types.NewBlockWithHeader(&types.Header{ + Number: big.NewInt(1), + Extra: []byte("bad block"), + UncleHash: types.EmptyUncleHash, + TxHash: types.EmptyRootHash, + ReceiptHash: types.EmptyRootHash, + }) + if entry := ReadBadBlock(db, block.Hash()); entry != nil { + t.Fatalf("Non existent block returned: %v", entry) + } + // Write and verify the block in the database + WriteBadBlock(db, block) + if entry := ReadBadBlock(db, block.Hash()); entry == nil { + t.Fatalf("Stored block not found") + } else if entry.Hash() != block.Hash() { + t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block) + } + // Write one more bad block + blockTwo := types.NewBlockWithHeader(&types.Header{ + Number: big.NewInt(2), + Extra: []byte("bad block two"), + UncleHash: types.EmptyUncleHash, + TxHash: types.EmptyRootHash, + ReceiptHash: types.EmptyRootHash, + }) + WriteBadBlock(db, blockTwo) + + // Write the block one again, should be filtered out. + WriteBadBlock(db, block) + badBlocks := ReadAllBadBlocks(db) + if len(badBlocks) != 2 { + t.Fatalf("Failed to load all bad blocks") + } + + // Write a bunch of bad blocks, all the blocks are should sorted + // in reverse order. The extra blocks should be truncated. + for _, n := range rand.Perm(100) { + block := types.NewBlockWithHeader(&types.Header{ + Number: big.NewInt(int64(n)), + Extra: []byte("bad block"), + UncleHash: types.EmptyUncleHash, + TxHash: types.EmptyRootHash, + ReceiptHash: types.EmptyRootHash, + }) + WriteBadBlock(db, block) + } + badBlocks = ReadAllBadBlocks(db) + if len(badBlocks) != badBlockToKeep { + t.Fatalf("The number of persised bad blocks in incorrect %d", len(badBlocks)) + } + for i := 0; i < len(badBlocks)-1; i++ { + if badBlocks[i].NumberU64() < badBlocks[i+1].NumberU64() { + t.Fatalf("The bad blocks are not sorted #[%d](%d) < #[%d](%d)", i, i+1, badBlocks[i].NumberU64(), badBlocks[i+1].NumberU64()) + } + } + + // Delete all bad blocks + DeleteBadBlocks(db) + badBlocks = ReadAllBadBlocks(db) + if len(badBlocks) != 0 { + t.Fatalf("Failed to delete bad blocks") + } +} + // Tests block total difficulty storage and retrieval operations. func TestTdStorage(t *testing.T) { db := NewMemoryDatabase() @@ -370,7 +440,7 @@ func TestAncientStorage(t *testing.T) { } defer os.Remove(frdir) - db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "") + db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false) if err != nil { t.Fatalf("failed to create database with ancient backend") } diff --git a/core/rawdb/accessors_metadata.go b/core/rawdb/accessors_metadata.go index 6e540df010..444c6b2c5b 100644 --- a/core/rawdb/accessors_metadata.go +++ b/core/rawdb/accessors_metadata.go @@ -18,6 +18,7 @@ package rawdb import ( "encoding/json" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" @@ -31,7 +32,7 @@ import ( func ReadDatabaseVersion(db ethdb.KeyValueReader) *uint64 { var version uint64 - enc, _ := db.Get(databaseVerisionKey) + enc, _ := db.Get(databaseVersionKey) if len(enc) == 0 { return nil } @@ -48,7 +49,7 @@ func WriteDatabaseVersion(db ethdb.KeyValueWriter, version uint64) { if err != nil { log.Crit("Failed to encode database version", "err", err) } - if err = db.Put(databaseVerisionKey, enc); err != nil { + if err = db.Put(databaseVersionKey, enc); err != nil { log.Crit("Failed to store the database version", "err", err) } } @@ -80,3 +81,61 @@ func WriteChainConfig(db ethdb.KeyValueWriter, hash common.Hash, cfg ctypes.Chai log.Crit("Failed to store chain config", "err", err) } } + +// crashList is a list of unclean-shutdown-markers, for rlp-encoding to the +// database +type crashList struct { + Discarded uint64 // how many ucs have we deleted + Recent []uint64 // unix timestamps of 10 latest unclean shutdowns +} + +const crashesToKeep = 10 + +// PushUncleanShutdownMarker appends a new unclean shutdown marker and returns +// the previous data +// - a list of timestamps +// - a count of how many old unclean-shutdowns have been discarded +func PushUncleanShutdownMarker(db ethdb.KeyValueStore) ([]uint64, uint64, error) { + var uncleanShutdowns crashList + // Read old data + if data, err := db.Get(uncleanShutdownKey); err != nil { + log.Warn("Error reading unclean shutdown markers", "error", err) + } else if err := rlp.DecodeBytes(data, &uncleanShutdowns); err != nil { + return nil, 0, err + } + var discarded = uncleanShutdowns.Discarded + var previous = make([]uint64, len(uncleanShutdowns.Recent)) + copy(previous, uncleanShutdowns.Recent) + // Add a new (but cap it) + uncleanShutdowns.Recent = append(uncleanShutdowns.Recent, uint64(time.Now().Unix())) + if count := len(uncleanShutdowns.Recent); count > crashesToKeep+1 { + numDel := count - (crashesToKeep + 1) + uncleanShutdowns.Recent = uncleanShutdowns.Recent[numDel:] + uncleanShutdowns.Discarded += uint64(numDel) + } + // And save it again + data, _ := rlp.EncodeToBytes(uncleanShutdowns) + if err := db.Put(uncleanShutdownKey, data); err != nil { + log.Warn("Failed to write unclean-shutdown marker", "err", err) + return nil, 0, err + } + return previous, discarded, nil +} + +// PopUncleanShutdownMarker removes the last unclean shutdown marker +func PopUncleanShutdownMarker(db ethdb.KeyValueStore) { + var uncleanShutdowns crashList + // Read old data + if data, err := db.Get(uncleanShutdownKey); err != nil { + log.Warn("Error reading unclean shutdown markers", "error", err) + } else if err := rlp.DecodeBytes(data, &uncleanShutdowns); err != nil { + log.Error("Error decoding unclean shutdown markers", "error", err) // Should mos def _not_ happen + } + if l := len(uncleanShutdowns.Recent); l > 0 { + uncleanShutdowns.Recent = uncleanShutdowns.Recent[:l-1] + } + data, _ := rlp.EncodeToBytes(uncleanShutdowns) + if err := db.Put(uncleanShutdownKey, data); err != nil { + log.Warn("Failed to clear unclean-shutdown marker", "err", err) + } +} diff --git a/core/rawdb/accessors_snapshot.go b/core/rawdb/accessors_snapshot.go index 5bd48ad5fa..c3616ba3aa 100644 --- a/core/rawdb/accessors_snapshot.go +++ b/core/rawdb/accessors_snapshot.go @@ -175,3 +175,24 @@ func DeleteSnapshotRecoveryNumber(db ethdb.KeyValueWriter) { log.Crit("Failed to remove snapshot recovery number", "err", err) } } + +// ReadSnapshotSyncStatus retrieves the serialized sync status saved at shutdown. +func ReadSnapshotSyncStatus(db ethdb.KeyValueReader) []byte { + data, _ := db.Get(snapshotSyncStatusKey) + return data +} + +// WriteSnapshotSyncStatus stores the serialized sync status to save at shutdown. +func WriteSnapshotSyncStatus(db ethdb.KeyValueWriter, status []byte) { + if err := db.Put(snapshotSyncStatusKey, status); err != nil { + log.Crit("Failed to store snapshot sync status", "err", err) + } +} + +// DeleteSnapshotSyncStatus deletes the serialized sync status saved at the last +// shutdown +func DeleteSnapshotSyncStatus(db ethdb.KeyValueWriter) { + if err := db.Delete(snapshotSyncStatusKey); err != nil { + log.Crit("Failed to remove snapshot sync status", "err", err) + } +} diff --git a/core/rawdb/chain_iterator.go b/core/rawdb/chain_iterator.go index 393b72c26c..ad222005be 100644 --- a/core/rawdb/chain_iterator.go +++ b/core/rawdb/chain_iterator.go @@ -23,10 +23,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/prque" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/crypto/sha3" ) // InitDatabaseFromFreezer reinitializes an empty database from a previous batch @@ -135,32 +135,15 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool close(hashesCh) } }() - - var hasher = sha3.NewLegacyKeccak256() for data := range rlpCh { - it, err := rlp.NewListIterator(data.rlp) - if err != nil { - log.Warn("tx iteration error", "error", err) - return - } - it.Next() - txs := it.Value() - txIt, err := rlp.NewListIterator(txs) - if err != nil { - log.Warn("tx iteration error", "error", err) + var body types.Body + if err := rlp.DecodeBytes(data.rlp, &body); err != nil { + log.Warn("Failed to decode block body", "block", data.number, "error", err) return } var hashes []common.Hash - for txIt.Next() { - if err := txIt.Err(); err != nil { - log.Warn("tx iteration error", "error", err) - return - } - var txHash common.Hash - hasher.Reset() - hasher.Write(txIt.Value()) - hasher.Sum(txHash[:0]) - hashes = append(hashes, txHash) + for _, tx := range body.Transactions { + hashes = append(hashes, tx.Hash()) } result := &blockTxHashes{ hashes: hashes, @@ -243,13 +226,13 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan } } } - // If there exists uncommitted data, flush them. - if batch.ValueSize() > 0 { - WriteTxIndexTail(batch, lastNum) // Also write the tail there - if err := batch.Write(); err != nil { - log.Crit("Failed writing batch to db", "error", err) - return - } + // Flush the new indexing tail and the last committed data. It can also happen + // that the last batch is empty because nothing to index, but the tail has to + // be flushed anyway. + WriteTxIndexTail(batch, lastNum) + if err := batch.Write(); err != nil { + log.Crit("Failed writing batch to db", "error", err) + return } select { case <-interrupt: @@ -334,13 +317,13 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch } } } - // Commit the last batch if there exists uncommitted data - if batch.ValueSize() > 0 { - WriteTxIndexTail(batch, nextNum) - if err := batch.Write(); err != nil { - log.Crit("Failed writing batch to db", "error", err) - return - } + // Flush the new indexing tail and the last committed data. It can also happen + // that the last batch is empty because nothing to unindex, but the tail has to + // be flushed anyway. + WriteTxIndexTail(batch, nextNum) + if err := batch.Write(); err != nil { + log.Crit("Failed writing batch to db", "error", err) + return } select { case <-interrupt: diff --git a/core/rawdb/chain_iterator_test.go b/core/rawdb/chain_iterator_test.go index 90b2639d38..45cc6323e0 100644 --- a/core/rawdb/chain_iterator_test.go +++ b/core/rawdb/chain_iterator_test.go @@ -33,14 +33,34 @@ func TestChainIterator(t *testing.T) { var block *types.Block var txs []*types.Transaction - for i := uint64(0); i <= 10; i++ { - if i == 0 { - block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, nil, nil, nil, newHasher()) // Empty genesis block + to := common.BytesToAddress([]byte{0x11}) + block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newHasher()) // Empty genesis block + WriteBlock(chainDb, block) + WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) + for i := uint64(1); i <= 10; i++ { + var tx *types.Transaction + if i%2 == 0 { + tx = types.NewTx(&types.LegacyTx{ + Nonce: i, + GasPrice: big.NewInt(11111), + Gas: 1111, + To: &to, + Value: big.NewInt(111), + Data: []byte{0x11, 0x11, 0x11}, + }) } else { - tx := types.NewTransaction(i, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) - txs = append(txs, tx) - block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher()) + tx = types.NewTx(&types.AccessListTx{ + ChainID: big.NewInt(1337), + Nonce: i, + GasPrice: big.NewInt(11111), + Gas: 1111, + To: &to, + Value: big.NewInt(111), + Data: []byte{0x11, 0x11, 0x11}, + }) } + txs = append(txs, tx) + block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher()) WriteBlock(chainDb, block) WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) } @@ -66,7 +86,7 @@ func TestChainIterator(t *testing.T) { numbers = append(numbers, int(h.number)) if len(h.hashes) > 0 { if got, exp := h.hashes[0], txs[h.number-1].Hash(); got != exp { - t.Fatalf("hash wrong, got %x exp %x", got, exp) + t.Fatalf("block %d: hash wrong, got %x exp %x", h.number, got, exp) } } } @@ -88,14 +108,37 @@ func TestIndexTransactions(t *testing.T) { var block *types.Block var txs []*types.Transaction - for i := uint64(0); i <= 10; i++ { - if i == 0 { - block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, nil, nil, nil, newHasher()) // Empty genesis block + to := common.BytesToAddress([]byte{0x11}) + + // Write empty genesis block + block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newHasher()) + WriteBlock(chainDb, block) + WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) + + for i := uint64(1); i <= 10; i++ { + var tx *types.Transaction + if i%2 == 0 { + tx = types.NewTx(&types.LegacyTx{ + Nonce: i, + GasPrice: big.NewInt(11111), + Gas: 1111, + To: &to, + Value: big.NewInt(111), + Data: []byte{0x11, 0x11, 0x11}, + }) } else { - tx := types.NewTransaction(i, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) - txs = append(txs, tx) - block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher()) + tx = types.NewTx(&types.AccessListTx{ + ChainID: big.NewInt(1337), + Nonce: i, + GasPrice: big.NewInt(11111), + Gas: 1111, + To: &to, + Value: big.NewInt(111), + Data: []byte{0x11, 0x11, 0x11}, + }) } + txs = append(txs, tx) + block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher()) WriteBlock(chainDb, block) WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) } @@ -108,10 +151,10 @@ func TestIndexTransactions(t *testing.T) { } number := ReadTxLookupEntry(chainDb, txs[i-1].Hash()) if exist && number == nil { - t.Fatalf("Transaction indice missing") + t.Fatalf("Transaction index %d missing", i) } if !exist && number != nil { - t.Fatalf("Transaction indice is not deleted") + t.Fatalf("Transaction index %d is not deleted", i) } } number := ReadTxIndexTail(chainDb) diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 44ff1fdbee..fb3e80308c 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -58,7 +58,10 @@ func (frdb *freezerdb) Close() error { // Freeze is a helper method used for external testing to trigger and block until // a freeze cycle completes, without having to sleep for a minute to trigger the // automatic background run. -func (frdb *freezerdb) Freeze(threshold uint64) { +func (frdb *freezerdb) Freeze(threshold uint64) error { + if frdb.AncientStore.(*freezer).readonly { + return errReadOnly + } // Set the freezer threshold to a temporary value defer func(old uint64) { atomic.StoreUint64(&frdb.AncientStore.(*freezer).threshold, old) @@ -69,6 +72,7 @@ func (frdb *freezerdb) Freeze(threshold uint64) { trigger := make(chan struct{}, 1) frdb.AncientStore.(*freezer).trigger <- trigger <-trigger + return nil } // nofreezedb is a database wrapper that disables freezer data retrievals. @@ -122,11 +126,11 @@ func NewDatabase(db ethdb.KeyValueStore) ethdb.Database { // NewDatabaseWithFreezerRemote creates a high level database on top of a given key- // value data store with a freezer moving immutable chain segments into cold // storage. -func NewDatabaseWithFreezerRemote(db ethdb.KeyValueStore, freezerURL string) (ethdb.Database, error) { +func NewDatabaseWithFreezerRemote(db ethdb.KeyValueStore, freezerURL string, readonly bool) (ethdb.Database, error) { // Create the idle freezer instance log.Info("New remote freezer", "freezer", freezerURL) - frdb, err := newFreezerRemoteClient(freezerURL) + frdb, err := newFreezerRemoteClient(freezerURL, readonly) if err != nil { log.Error("NewDatabaseWithFreezerRemote error", "error", err) return nil, err @@ -203,8 +207,9 @@ Please set --ancient.rpc to the correct path, and/or review the remote freezer's } } // Freezer is consistent with the key-value database, permit combining the two - go freezeRemote(db, frdb, frdb.threshold, frdb.quit, frdb.trigger) - + if !frdb.readonly { + go freezeRemote(db, frdb, frdb.threshold, frdb.quit, frdb.trigger) + } return &freezerdb{ KeyValueStore: db, AncientStore: frdb, @@ -214,9 +219,9 @@ Please set --ancient.rpc to the correct path, and/or review the remote freezer's // NewDatabaseWithFreezer creates a high level database on top of a given key- // value data store with a freezer moving immutable chain segments into cold // storage. -func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezerStr string, namespace string) (ethdb.Database, error) { +func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezerStr string, namespace string, readonly bool) (ethdb.Database, error) { // Create the idle freezer instance - frdb, err := newFreezer(freezerStr, namespace) + frdb, err := newFreezer(freezerStr, namespace, readonly) if err != nil { return nil, err } @@ -272,8 +277,9 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezerStr string, namespace } // Freezer is consistent with the key-value database, permit combining the two - go frdb.freeze(db) - + if !frdb.readonly { + go frdb.freeze(db) + } return &freezerdb{ KeyValueStore: db, AncientStore: frdb, @@ -295,8 +301,8 @@ func NewMemoryDatabaseWithCap(size int) ethdb.Database { // NewLevelDBDatabase creates a persistent key-value database without a freezer // moving immutable chain segments into cold storage. -func NewLevelDBDatabase(file string, cache int, handles int, namespace string) (ethdb.Database, error) { - db, err := leveldb.New(file, cache, handles, namespace) +func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) { + db, err := leveldb.New(file, cache, handles, namespace, readonly) if err != nil { return nil, err } @@ -305,12 +311,12 @@ func NewLevelDBDatabase(file string, cache int, handles int, namespace string) ( // NewLevelDBDatabaseWithFreezer creates a persistent key-value database with a // freezer moving immutable chain segments into cold storage. -func NewLevelDBDatabaseWithFreezerRemote(file string, cache int, handles int, freezerURL string) (ethdb.Database, error) { - kvdb, err := leveldb.New(file, cache, handles, "eth/db/chaindata") +func NewLevelDBDatabaseWithFreezerRemote(file string, cache int, handles int, freezerURL string, readonly bool) (ethdb.Database, error) { + kvdb, err := leveldb.New(file, cache, handles, "eth/db/chaindata", readonly) if err != nil { return nil, err } - frdb, err := NewDatabaseWithFreezerRemote(kvdb, freezerURL) + frdb, err := NewDatabaseWithFreezerRemote(kvdb, freezerURL, readonly) if err != nil { kvdb.Close() return nil, err @@ -320,12 +326,12 @@ func NewLevelDBDatabaseWithFreezerRemote(file string, cache int, handles int, fr // NewLevelDBDatabaseWithFreezer creates a persistent key-value database with a // freezer moving immutable chain segments into cold storage. -func NewLevelDBDatabaseWithFreezer(file string, cache int, handles int, freezer string, namespace string) (ethdb.Database, error) { - kvdb, err := leveldb.New(file, cache, handles, namespace) +func NewLevelDBDatabaseWithFreezer(file string, cache int, handles int, freezer string, namespace string, readonly bool) (ethdb.Database, error) { + kvdb, err := leveldb.New(file, cache, handles, namespace, readonly) if err != nil { return nil, err } - frdb, err := NewDatabaseWithFreezer(kvdb, freezer, namespace) + frdb, err := NewDatabaseWithFreezer(kvdb, freezer, namespace, readonly) if err != nil { kvdb.Close() return nil, err @@ -365,8 +371,8 @@ func (s *stat) Count() string { // InspectDatabase traverses the entire database and checks the size // of all different categories of data. -func InspectDatabase(db ethdb.Database) error { - it := db.NewIterator(nil, nil) +func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { + it := db.NewIterator(keyPrefix, keyStart) defer it.Release() var ( @@ -402,8 +408,9 @@ func InspectDatabase(db ethdb.Database) error { bloomTrieNodes stat // Meta- and unaccounted data - metadata stat - unaccounted stat + metadata stat + unaccounted stat + shutdownInfo stat // Totals total common.StorageSize @@ -430,7 +437,7 @@ func InspectDatabase(db ethdb.Database) error { hashNumPairings.Add(size) case len(key) == common.HashLength: tries.Add(size) - case bytes.HasPrefix(key, codePrefix) && len(key) == len(codePrefix)+common.HashLength: + case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength: codes.Add(size) case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength): txLookups.Add(size) @@ -442,15 +449,28 @@ func InspectDatabase(db ethdb.Database) error { preimages.Add(size) case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength): bloomBits.Add(size) + case bytes.HasPrefix(key, BloomBitsIndexPrefix): + bloomBits.Add(size) case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength: cliqueSnaps.Add(size) - case bytes.HasPrefix(key, []byte("cht-")) && len(key) == 4+common.HashLength: + case bytes.HasPrefix(key, []byte("cht-")) || + bytes.HasPrefix(key, []byte("chtIndexV2-")) || + bytes.HasPrefix(key, []byte("chtRootV2-")): // Canonical hash trie chtTrieNodes.Add(size) - case bytes.HasPrefix(key, []byte("blt-")) && len(key) == 4+common.HashLength: + case bytes.HasPrefix(key, []byte("blt-")) || + bytes.HasPrefix(key, []byte("bltIndex-")) || + bytes.HasPrefix(key, []byte("bltRoot-")): // Bloomtrie sub bloomTrieNodes.Add(size) + case bytes.Equal(key, uncleanShutdownKey): + shutdownInfo.Add(size) default: var accounted bool - for _, meta := range [][]byte{databaseVerisionKey, headHeaderKey, headBlockKey, headFastBlockKey, fastTrieProgressKey} { + for _, meta := range [][]byte{ + databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, lastPivotKey, + fastTrieProgressKey, snapshotRootKey, snapshotJournalKey, snapshotGeneratorKey, + snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey, uncleanShutdownKey, + badBlockKey, + } { if bytes.Equal(key, meta) { metadata.Add(size) accounted = true @@ -497,6 +517,7 @@ func InspectDatabase(db ethdb.Database) error { {"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()}, {"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()}, {"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()}, + {"Key-Value store", "Shutdown metadata", shutdownInfo.Size(), shutdownInfo.Count()}, {"Ancient store", "Headers", ancientHeadersSize.String(), ancients.String()}, {"Ancient store", "Bodies", ancientBodiesSize.String(), ancients.String()}, {"Ancient store", "Receipt lists", ancientReceiptsSize.String(), ancients.String()}, diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go index 1da5694264..7988b665cc 100644 --- a/core/rawdb/freezer.go +++ b/core/rawdb/freezer.go @@ -35,6 +35,10 @@ import ( ) var ( + // errReadOnly is returned if the freezer is opened in read only mode. All the + // mutations are disallowed. + errReadOnly = errors.New("read only") + // errUnknownTable is returned if the user attempts to read from a table that is // not tracked by the freezer. errUnknownTable = errors.New("unknown table") @@ -73,6 +77,7 @@ type freezer struct { frozen uint64 // Number of blocks already frozen threshold uint64 // Number of recent blocks not to freeze (params.FullImmutabilityThreshold apart from tests) + readonly bool tables map[string]*freezerTable // Data tables for storing everything instanceLock fileutil.Releaser // File-system lock to prevent double opens @@ -84,7 +89,7 @@ type freezer struct { // newFreezer creates a chain freezer that moves ancient chain data into // append-only flat file containers. -func newFreezer(datadir string, namespace string) (*freezer, error) { +func newFreezer(datadir string, namespace string, readonly bool) (*freezer, error) { // Create the initial freezer object var ( readMeter = metrics.NewRegisteredMeter(namespace+"ancient/read", nil) @@ -106,6 +111,7 @@ func newFreezer(datadir string, namespace string) (*freezer, error) { } // Open all the supported data tables freezer := &freezer{ + readonly: readonly, threshold: vars.FullImmutabilityThreshold, tables: make(map[string]*freezerTable), instanceLock: lock, @@ -130,7 +136,7 @@ func newFreezer(datadir string, namespace string) (*freezer, error) { lock.Release() return nil, err } - log.Info("Opened ancient database", "database", datadir) + log.Info("Opened ancient database", "database", datadir, "readonly", readonly) return freezer, nil } @@ -138,7 +144,7 @@ func newFreezer(datadir string, namespace string) (*freezer, error) { func (f *freezer) Close() error { var errs []error f.closeOnce.Do(func() { - f.quit <- struct{}{} + close(f.quit) for _, table := range f.tables { if err := table.Close(); err != nil { errs = append(errs, err) @@ -191,6 +197,9 @@ func (f *freezer) AncientSize(kind string) (uint64, error) { // injection will be rejected. But if two injections with same number happen at // the same time, we can get into the trouble. func (f *freezer) AppendAncient(number uint64, hash, header, body, receipts, td []byte) (err error) { + if f.readonly { + return errReadOnly + } // Ensure the binary blobs we are appending is continuous with freezer. if atomic.LoadUint64(&f.frozen) != number { return errOutOrderInsertion @@ -233,6 +242,9 @@ func (f *freezer) AppendAncient(number uint64, hash, header, body, receipts, td // TruncateAncients discards any recent data above the provided threshold number. func (f *freezer) TruncateAncients(items uint64) error { + if f.readonly { + return errReadOnly + } if atomic.LoadUint64(&f.frozen) <= items { return nil } diff --git a/core/rawdb/freezer_remote_client.go b/core/rawdb/freezer_remote_client.go index d0ad602b99..64c1cf8125 100644 --- a/core/rawdb/freezer_remote_client.go +++ b/core/rawdb/freezer_remote_client.go @@ -20,6 +20,7 @@ type FreezerRemoteClient struct { threshold uint64 // Number of recent blocks not to freeze (params.FullImmutabilityThreshold apart from tests) trigger chan chan struct{} // Manual blocking freeze trigger, test determinism closeOnce sync.Once + readonly bool } const ( @@ -34,7 +35,7 @@ const ( ) // newFreezerRemoteClient constructs a rpc client to connect to a remote freezer -func newFreezerRemoteClient(endpoint string) (*FreezerRemoteClient, error) { +func newFreezerRemoteClient(endpoint string, readonly bool) (*FreezerRemoteClient, error) { client, err := rpc.Dial(endpoint) if err != nil { return nil, err @@ -44,6 +45,7 @@ func newFreezerRemoteClient(endpoint string) (*FreezerRemoteClient, error) { threshold: vars.FullImmutabilityThreshold, quit: make(chan struct{}), trigger: make(chan chan struct{}), + readonly: readonly, }, nil } diff --git a/core/rawdb/freezer_table.go b/core/rawdb/freezer_table.go index b9d8a274a8..cd273222b1 100644 --- a/core/rawdb/freezer_table.go +++ b/core/rawdb/freezer_table.go @@ -103,6 +103,11 @@ type freezerTable struct { lock sync.RWMutex // Mutex protecting the data file descriptors } +// NewFreezerTable opens the given path as a freezer table. +func NewFreezerTable(path, name string, disableSnappy bool) (*freezerTable, error) { + return newTable(path, name, metrics.NilMeter{}, metrics.NilMeter{}, metrics.NilGauge{}, disableSnappy) +} + // newTable opens a freezer table with default settings - 2G files func newTable(path string, name string, readMeter metrics.Meter, writeMeter metrics.Meter, sizeGauge metrics.Gauge, disableSnappy bool) (*freezerTable, error) { return newCustomTable(path, name, readMeter, writeMeter, sizeGauge, 2*1000*1000*1000, disableSnappy) diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 8710a62990..0983b4245a 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -27,8 +27,8 @@ import ( // The fields below define the low level database schema prefixing. var ( - // databaseVerisionKey tracks the current database version. - databaseVerisionKey = []byte("DatabaseVersion") + // databaseVersionKey tracks the current database version. + databaseVersionKey = []byte("DatabaseVersion") // headHeaderKey tracks the latest known header's hash. headHeaderKey = []byte("LastHeader") @@ -57,12 +57,21 @@ var ( // snapshotRecoveryKey tracks the snapshot recovery marker across restarts. snapshotRecoveryKey = []byte("SnapshotRecovery") + // snapshotSyncStatusKey tracks the snapshot sync status across restarts. + snapshotSyncStatusKey = []byte("SnapshotSyncStatus") + // txIndexTailKey tracks the oldest block whose transactions have been indexed. txIndexTailKey = []byte("TransactionIndexTail") // fastTxLookupLimitKey tracks the transaction lookup limit during fast sync. fastTxLookupLimitKey = []byte("FastTransactionLookupLimit") + // badBlockKey tracks the list of bad blocks seen by local + badBlockKey = []byte("InvalidBlock") + + // uncleanShutdownKey tracks the list of local crashes + uncleanShutdownKey = []byte("unclean-shutdown") // config prefix for the db + // Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes). headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td @@ -76,7 +85,7 @@ var ( bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits SnapshotAccountPrefix = []byte("a") // SnapshotAccountPrefix + account hash -> account trie value SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value - codePrefix = []byte("c") // codePrefix + code hash -> account code + CodePrefix = []byte("c") // CodePrefix + code hash -> account code preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage ConfigPrefix = []byte("ethereum-config-") // config prefix for the db @@ -231,16 +240,16 @@ func preimageKey(hash common.Hash) []byte { return append(preimagePrefix, hash.Bytes()...) } -// codeKey = codePrefix + hash +// codeKey = CodePrefix + hash func codeKey(hash common.Hash) []byte { - return append(codePrefix, hash.Bytes()...) + return append(CodePrefix, hash.Bytes()...) } // IsCodeKey reports whether the given byte slice is the key of contract code, // if so return the raw code hash as well. func IsCodeKey(key []byte) (bool, []byte) { - if bytes.HasPrefix(key, codePrefix) && len(key) == common.HashLength+len(codePrefix) { - return true, key[len(codePrefix):] + if bytes.HasPrefix(key, CodePrefix) && len(key) == common.HashLength+len(CodePrefix) { + return true, key[len(CodePrefix):] } return false, nil } diff --git a/core/state/database.go b/core/state/database.go index a9342f5179..1a06e33409 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -104,18 +104,18 @@ type Trie interface { // NewDatabase creates a backing store for state. The returned database is safe for // concurrent use, but does not retain any recent trie nodes in memory. To keep some -// historical state in memory, use the NewDatabaseWithCache constructor. +// historical state in memory, use the NewDatabaseWithConfig constructor. func NewDatabase(db ethdb.Database) Database { - return NewDatabaseWithCache(db, 0, "") + return NewDatabaseWithConfig(db, nil) } -// NewDatabaseWithCache creates a backing store for state. The returned database +// NewDatabaseWithConfig creates a backing store for state. The returned database // is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a // large memory cache. -func NewDatabaseWithCache(db ethdb.Database, cache int, journal string) Database { +func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database { csc, _ := lru.New(codeSizeCacheSize) return &cachingDB{ - db: trie.NewDatabaseWithCache(db, cache, journal), + db: trie.NewDatabaseWithConfig(db, config), codeSizeCache: csc, codeCache: fastcache.New(codeCacheSize), } @@ -129,12 +129,20 @@ type cachingDB struct { // OpenTrie opens the main account trie at a specific root hash. func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { - return trie.NewSecure(root, db.db) + tr, err := trie.NewSecure(root, db.db) + if err != nil { + return nil, err + } + return tr, nil } // OpenStorageTrie opens the storage trie of an account. func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) { - return trie.NewSecure(root, db.db) + tr, err := trie.NewSecure(root, db.db) + if err != nil { + return nil, err + } + return tr, nil } // CopyTrie returns an independent copy of the given trie. diff --git a/core/state/dump.go b/core/state/dump.go index 9bb946d14b..b25da714fd 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -138,7 +138,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, excludeCode, excludeStorage, account.SecureKey = it.Key } addr := common.BytesToAddress(addrBytes) - obj := newObject(nil, addr, data) + obj := newObject(s, addr, data) if !excludeCode { account.Code = common.Bytes2Hex(obj.Code(s.db)) } diff --git a/core/state/pruner/bloom.go b/core/state/pruner/bloom.go new file mode 100644 index 0000000000..4aeeb176e8 --- /dev/null +++ b/core/state/pruner/bloom.go @@ -0,0 +1,132 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package pruner + +import ( + "encoding/binary" + "errors" + "os" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/log" + bloomfilter "github.com/holiman/bloomfilter/v2" +) + +// stateBloomHasher is a wrapper around a byte blob to satisfy the interface API +// requirements of the bloom library used. It's used to convert a trie hash or +// contract code hash into a 64 bit mini hash. +type stateBloomHasher []byte + +func (f stateBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") } +func (f stateBloomHasher) Sum(b []byte) []byte { panic("not implemented") } +func (f stateBloomHasher) Reset() { panic("not implemented") } +func (f stateBloomHasher) BlockSize() int { panic("not implemented") } +func (f stateBloomHasher) Size() int { return 8 } +func (f stateBloomHasher) Sum64() uint64 { return binary.BigEndian.Uint64(f) } + +// stateBloom is a bloom filter used during the state convesion(snapshot->state). +// The keys of all generated entries will be recorded here so that in the pruning +// stage the entries belong to the specific version can be avoided for deletion. +// +// The false-positive is allowed here. The "false-positive" entries means they +// actually don't belong to the specific version but they are not deleted in the +// pruning. The downside of the false-positive allowance is we may leave some "dangling" +// nodes in the disk. But in practice the it's very unlike the dangling node is +// state root. So in theory this pruned state shouldn't be visited anymore. Another +// potential issue is for fast sync. If we do another fast sync upon the pruned +// database, it's problematic which will stop the expansion during the syncing. +// TODO address it @rjl493456442 @holiman @karalabe. +// +// After the entire state is generated, the bloom filter should be persisted into +// the disk. It indicates the whole generation procedure is finished. +type stateBloom struct { + bloom *bloomfilter.Filter +} + +// newStateBloomWithSize creates a brand new state bloom for state generation. +// The bloom filter will be created by the passing bloom filter size. According +// to the https://hur.st/bloomfilter/?n=600000000&p=&m=2048MB&k=4, the parameters +// are picked so that the false-positive rate for mainnet is low enough. +func newStateBloomWithSize(size uint64) (*stateBloom, error) { + bloom, err := bloomfilter.New(size*1024*1024*8, 4) + if err != nil { + return nil, err + } + log.Info("Initialized state bloom", "size", common.StorageSize(float64(bloom.M()/8))) + return &stateBloom{bloom: bloom}, nil +} + +// NewStateBloomFromDisk loads the state bloom from the given file. +// In this case the assumption is held the bloom filter is complete. +func NewStateBloomFromDisk(filename string) (*stateBloom, error) { + bloom, _, err := bloomfilter.ReadFile(filename) + if err != nil { + return nil, err + } + return &stateBloom{bloom: bloom}, nil +} + +// Commit flushes the bloom filter content into the disk and marks the bloom +// as complete. +func (bloom *stateBloom) Commit(filename, tempname string) error { + // Write the bloom out into a temporary file + _, err := bloom.bloom.WriteFile(tempname) + if err != nil { + return err + } + // Ensure the file is synced to disk + f, err := os.Open(tempname) + if err != nil { + return err + } + if err := f.Sync(); err != nil { + f.Close() + return err + } + f.Close() + + // Move the teporary file into it's final location + return os.Rename(tempname, filename) +} + +// Put implements the KeyValueWriter interface. But here only the key is needed. +func (bloom *stateBloom) Put(key []byte, value []byte) error { + // If the key length is not 32bytes, ensure it's contract code + // entry with new scheme. + if len(key) != common.HashLength { + isCode, codeKey := rawdb.IsCodeKey(key) + if !isCode { + return errors.New("invalid entry") + } + bloom.bloom.Add(stateBloomHasher(codeKey)) + return nil + } + bloom.bloom.Add(stateBloomHasher(key)) + return nil +} + +// Delete removes the key from the key-value data store. +func (bloom *stateBloom) Delete(key []byte) error { panic("not supported") } + +// Contain is the wrapper of the underlying contains function which +// reports whether the key is contained. +// - If it says yes, the key may be contained +// - If it says no, the key is definitely not contained. +func (bloom *stateBloom) Contain(key []byte) (bool, error) { + return bloom.bloom.Contains(stateBloomHasher(key)), nil +} diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go new file mode 100644 index 0000000000..4d6e415511 --- /dev/null +++ b/core/state/pruner/pruner.go @@ -0,0 +1,507 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package pruner + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "math" + "os" + "path/filepath" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +const ( + // stateBloomFilePrefix is the filename prefix of state bloom filter. + stateBloomFilePrefix = "statebloom" + + // stateBloomFilePrefix is the filename suffix of state bloom filter. + stateBloomFileSuffix = "bf.gz" + + // stateBloomFileTempSuffix is the filename suffix of state bloom filter + // while it is being written out to detect write aborts. + stateBloomFileTempSuffix = ".tmp" + + // rangeCompactionThreshold is the minimal deleted entry number for + // triggering range compaction. It's a quite arbitrary number but just + // to avoid triggering range compaction because of small deletion. + rangeCompactionThreshold = 100000 +) + +var ( + // emptyRoot is the known root hash of an empty trie. + emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + + // emptyCode is the known hash of the empty EVM bytecode. + emptyCode = crypto.Keccak256(nil) +) + +// Pruner is an offline tool to prune the stale state with the +// help of the snapshot. The workflow of pruner is very simple: +// +// - iterate the snapshot, reconstruct the relevant state +// - iterate the database, delete all other state entries which +// don't belong to the target state and the genesis state +// +// It can take several hours(around 2 hours for mainnet) to finish +// the whole pruning work. It's recommended to run this offline tool +// periodically in order to release the disk usage and improve the +// disk read performance to some extent. +type Pruner struct { + db ethdb.Database + stateBloom *stateBloom + datadir string + trieCachePath string + headHeader *types.Header + snaptree *snapshot.Tree +} + +// NewPruner creates the pruner instance. +func NewPruner(db ethdb.Database, datadir, trieCachePath string, bloomSize uint64) (*Pruner, error) { + headBlock := rawdb.ReadHeadBlock(db) + if headBlock == nil { + return nil, errors.New("Failed to load head block") + } + snaptree, err := snapshot.New(db, trie.NewDatabase(db), 256, headBlock.Root(), false, false, false) + if err != nil { + return nil, err // The relevant snapshot(s) might not exist + } + // Sanitize the bloom filter size if it's too small. + if bloomSize < 256 { + log.Warn("Sanitizing bloomfilter size", "provided(MB)", bloomSize, "updated(MB)", 256) + bloomSize = 256 + } + stateBloom, err := newStateBloomWithSize(bloomSize) + if err != nil { + return nil, err + } + return &Pruner{ + db: db, + stateBloom: stateBloom, + datadir: datadir, + trieCachePath: trieCachePath, + headHeader: headBlock.Header(), + snaptree: snaptree, + }, nil +} + +func prune(snaptree *snapshot.Tree, root common.Hash, maindb ethdb.Database, stateBloom *stateBloom, bloomPath string, middleStateRoots map[common.Hash]struct{}, start time.Time) error { + // Delete all stale trie nodes in the disk. With the help of state bloom + // the trie nodes(and codes) belong to the active state will be filtered + // out. A very small part of stale tries will also be filtered because of + // the false-positive rate of bloom filter. But the assumption is held here + // that the false-positive is low enough(~0.05%). The probablity of the + // dangling node is the state root is super low. So the dangling nodes in + // theory will never ever be visited again. + var ( + count int + size common.StorageSize + pstart = time.Now() + logged = time.Now() + batch = maindb.NewBatch() + iter = maindb.NewIterator(nil, nil) + ) + for iter.Next() { + key := iter.Key() + + // All state entries don't belong to specific state and genesis are deleted here + // - trie node + // - legacy contract code + // - new-scheme contract code + isCode, codeKey := rawdb.IsCodeKey(key) + if len(key) == common.HashLength || isCode { + checkKey := key + if isCode { + checkKey = codeKey + } + if _, exist := middleStateRoots[common.BytesToHash(checkKey)]; exist { + log.Debug("Forcibly delete the middle state roots", "hash", common.BytesToHash(checkKey)) + } else { + if ok, err := stateBloom.Contain(checkKey); err != nil { + return err + } else if ok { + continue + } + } + count += 1 + size += common.StorageSize(len(key) + len(iter.Value())) + batch.Delete(key) + + var eta time.Duration // Realistically will never remain uninited + if done := binary.BigEndian.Uint64(key[:8]); done > 0 { + var ( + left = math.MaxUint64 - binary.BigEndian.Uint64(key[:8]) + speed = done/uint64(time.Since(pstart)/time.Millisecond+1) + 1 // +1s to avoid division by zero + ) + eta = time.Duration(left/speed) * time.Millisecond + } + if time.Since(logged) > 8*time.Second { + log.Info("Pruning state data", "nodes", count, "size", size, + "elapsed", common.PrettyDuration(time.Since(pstart)), "eta", common.PrettyDuration(eta)) + logged = time.Now() + } + // Recreate the iterator after every batch commit in order + // to allow the underlying compactor to delete the entries. + if batch.ValueSize() >= ethdb.IdealBatchSize { + batch.Write() + batch.Reset() + + iter.Release() + iter = maindb.NewIterator(nil, key) + } + } + } + if batch.ValueSize() > 0 { + batch.Write() + batch.Reset() + } + iter.Release() + log.Info("Pruned state data", "nodes", count, "size", size, "elapsed", common.PrettyDuration(time.Since(pstart))) + + // Pruning is done, now drop the "useless" layers from the snapshot. + // Firstly, flushing the target layer into the disk. After that all + // diff layers below the target will all be merged into the disk. + if err := snaptree.Cap(root, 0); err != nil { + return err + } + // Secondly, flushing the snapshot journal into the disk. All diff + // layers upon are dropped silently. Eventually the entire snapshot + // tree is converted into a single disk layer with the pruning target + // as the root. + if _, err := snaptree.Journal(root); err != nil { + return err + } + // Delete the state bloom, it marks the entire pruning procedure is + // finished. If any crashes or manual exit happens before this, + // `RecoverPruning` will pick it up in the next restarts to redo all + // the things. + os.RemoveAll(bloomPath) + + // Start compactions, will remove the deleted data from the disk immediately. + // Note for small pruning, the compaction is skipped. + if count >= rangeCompactionThreshold { + cstart := time.Now() + for b := 0x00; b <= 0xf0; b += 0x10 { + var ( + start = []byte{byte(b)} + end = []byte{byte(b + 0x10)} + ) + if b == 0xf0 { + end = nil + } + log.Info("Compacting database", "range", fmt.Sprintf("%#x-%#x", start, end), "elapsed", common.PrettyDuration(time.Since(cstart))) + if err := maindb.Compact(start, end); err != nil { + log.Error("Database compaction failed", "error", err) + return err + } + } + log.Info("Database compaction finished", "elapsed", common.PrettyDuration(time.Since(cstart))) + } + log.Info("State pruning successful", "pruned", size, "elapsed", common.PrettyDuration(time.Since(start))) + return nil +} + +// Prune deletes all historical state nodes except the nodes belong to the +// specified state version. If user doesn't specify the state version, use +// the bottom-most snapshot diff layer as the target. +func (p *Pruner) Prune(root common.Hash) error { + // If the state bloom filter is already committed previously, + // reuse it for pruning instead of generating a new one. It's + // mandatory because a part of state may already be deleted, + // the recovery procedure is necessary. + _, stateBloomRoot, err := findBloomFilter(p.datadir) + if err != nil { + return err + } + if stateBloomRoot != (common.Hash{}) { + return RecoverPruning(p.datadir, p.db, p.trieCachePath) + } + // If the target state root is not specified, use the HEAD-127 as the + // target. The reason for picking it is: + // - in most of the normal cases, the related state is available + // - the probability of this layer being reorg is very low + var layers []snapshot.Snapshot + if root == (common.Hash{}) { + // Retrieve all snapshot layers from the current HEAD. + // In theory there are 128 difflayers + 1 disk layer present, + // so 128 diff layers are expected to be returned. + layers = p.snaptree.Snapshots(p.headHeader.Root, 128, true) + if len(layers) != 128 { + // Reject if the accumulated diff layers are less than 128. It + // means in most of normal cases, there is no associated state + // with bottom-most diff layer. + return fmt.Errorf("snapshot not old enough yet: need %d more blocks", 128-len(layers)) + } + // Use the bottom-most diff layer as the target + root = layers[len(layers)-1].Root() + } + // Ensure the root is really present. The weak assumption + // is the presence of root can indicate the presence of the + // entire trie. + if blob := rawdb.ReadTrieNode(p.db, root); len(blob) == 0 { + // The special case is for clique based networks(rinkeby, goerli + // and some other private networks), it's possible that two + // consecutive blocks will have same root. In this case snapshot + // difflayer won't be created. So HEAD-127 may not paired with + // head-127 layer. Instead the paired layer is higher than the + // bottom-most diff layer. Try to find the bottom-most snapshot + // layer with state available. + // + // Note HEAD and HEAD-1 is ignored. Usually there is the associated + // state available, but we don't want to use the topmost state + // as the pruning target. + var found bool + for i := len(layers) - 2; i >= 2; i-- { + if blob := rawdb.ReadTrieNode(p.db, layers[i].Root()); len(blob) != 0 { + root = layers[i].Root() + found = true + log.Info("Selecting middle-layer as the pruning target", "root", root, "depth", i) + break + } + } + if !found { + if len(layers) > 0 { + return errors.New("no snapshot paired state") + } + return fmt.Errorf("associated state[%x] is not present", root) + } + } else { + if len(layers) > 0 { + log.Info("Selecting bottom-most difflayer as the pruning target", "root", root, "height", p.headHeader.Number.Uint64()-127) + } else { + log.Info("Selecting user-specified state as the pruning target", "root", root) + } + } + // Before start the pruning, delete the clean trie cache first. + // It's necessary otherwise in the next restart we will hit the + // deleted state root in the "clean cache" so that the incomplete + // state is picked for usage. + deleteCleanTrieCache(p.trieCachePath) + + // All the state roots of the middle layer should be forcibly pruned, + // otherwise the dangling state will be left. + middleRoots := make(map[common.Hash]struct{}) + for _, layer := range layers { + if layer.Root() == root { + break + } + middleRoots[layer.Root()] = struct{}{} + } + // Traverse the target state, re-construct the whole state trie and + // commit to the given bloom filter. + start := time.Now() + if err := snapshot.GenerateTrie(p.snaptree, root, p.db, p.stateBloom); err != nil { + return err + } + // Traverse the genesis, put all genesis state entries into the + // bloom filter too. + if err := extractGenesis(p.db, p.stateBloom); err != nil { + return err + } + filterName := bloomFilterName(p.datadir, root) + + log.Info("Writing state bloom to disk", "name", filterName) + if err := p.stateBloom.Commit(filterName, filterName+stateBloomFileTempSuffix); err != nil { + return err + } + log.Info("State bloom filter committed", "name", filterName) + return prune(p.snaptree, root, p.db, p.stateBloom, filterName, middleRoots, start) +} + +// RecoverPruning will resume the pruning procedure during the system restart. +// This function is used in this case: user tries to prune state data, but the +// system was interrupted midway because of crash or manual-kill. In this case +// if the bloom filter for filtering active state is already constructed, the +// pruning can be resumed. What's more if the bloom filter is constructed, the +// pruning **has to be resumed**. Otherwise a lot of dangling nodes may be left +// in the disk. +func RecoverPruning(datadir string, db ethdb.Database, trieCachePath string) error { + stateBloomPath, stateBloomRoot, err := findBloomFilter(datadir) + if err != nil { + return err + } + if stateBloomPath == "" { + return nil // nothing to recover + } + headBlock := rawdb.ReadHeadBlock(db) + if headBlock == nil { + return errors.New("Failed to load head block") + } + // Initialize the snapshot tree in recovery mode to handle this special case: + // - Users run the `prune-state` command multiple times + // - Neither these `prune-state` running is finished(e.g. interrupted manually) + // - The state bloom filter is already generated, a part of state is deleted, + // so that resuming the pruning here is mandatory + // - The state HEAD is rewound already because of multiple incomplete `prune-state` + // In this case, even the state HEAD is not exactly matched with snapshot, it + // still feasible to recover the pruning correctly. + snaptree, err := snapshot.New(db, trie.NewDatabase(db), 256, headBlock.Root(), false, false, true) + if err != nil { + return err // The relevant snapshot(s) might not exist + } + stateBloom, err := NewStateBloomFromDisk(stateBloomPath) + if err != nil { + return err + } + log.Info("Loaded state bloom filter", "path", stateBloomPath) + + // Before start the pruning, delete the clean trie cache first. + // It's necessary otherwise in the next restart we will hit the + // deleted state root in the "clean cache" so that the incomplete + // state is picked for usage. + deleteCleanTrieCache(trieCachePath) + + // All the state roots of the middle layers should be forcibly pruned, + // otherwise the dangling state will be left. + var ( + found bool + layers = snaptree.Snapshots(headBlock.Root(), 128, true) + middleRoots = make(map[common.Hash]struct{}) + ) + for _, layer := range layers { + if layer.Root() == stateBloomRoot { + found = true + break + } + middleRoots[layer.Root()] = struct{}{} + } + if !found { + log.Error("Pruning target state is not existent") + return errors.New("non-existent target state") + } + return prune(snaptree, stateBloomRoot, db, stateBloom, stateBloomPath, middleRoots, time.Now()) +} + +// extractGenesis loads the genesis state and commits all the state entries +// into the given bloomfilter. +func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error { + genesisHash := rawdb.ReadCanonicalHash(db, 0) + if genesisHash == (common.Hash{}) { + return errors.New("missing genesis hash") + } + genesis := rawdb.ReadBlock(db, genesisHash, 0) + if genesis == nil { + return errors.New("missing genesis block") + } + t, err := trie.NewSecure(genesis.Root(), trie.NewDatabase(db)) + if err != nil { + return err + } + accIter := t.NodeIterator(nil) + for accIter.Next(true) { + hash := accIter.Hash() + + // Embedded nodes don't have hash. + if hash != (common.Hash{}) { + stateBloom.Put(hash.Bytes(), nil) + } + // If it's a leaf node, yes we are touching an account, + // dig into the storage trie further. + if accIter.Leaf() { + var acc state.Account + if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil { + return err + } + if acc.Root != emptyRoot { + storageTrie, err := trie.NewSecure(acc.Root, trie.NewDatabase(db)) + if err != nil { + return err + } + storageIter := storageTrie.NodeIterator(nil) + for storageIter.Next(true) { + hash := storageIter.Hash() + if hash != (common.Hash{}) { + stateBloom.Put(hash.Bytes(), nil) + } + } + if storageIter.Error() != nil { + return storageIter.Error() + } + } + if !bytes.Equal(acc.CodeHash, emptyCode) { + stateBloom.Put(acc.CodeHash, nil) + } + } + } + return accIter.Error() +} + +func bloomFilterName(datadir string, hash common.Hash) string { + return filepath.Join(datadir, fmt.Sprintf("%s.%s.%s", stateBloomFilePrefix, hash.Hex(), stateBloomFileSuffix)) +} + +func isBloomFilter(filename string) (bool, common.Hash) { + filename = filepath.Base(filename) + if strings.HasPrefix(filename, stateBloomFilePrefix) && strings.HasSuffix(filename, stateBloomFileSuffix) { + return true, common.HexToHash(filename[len(stateBloomFilePrefix)+1 : len(filename)-len(stateBloomFileSuffix)-1]) + } + return false, common.Hash{} +} + +func findBloomFilter(datadir string) (string, common.Hash, error) { + var ( + stateBloomPath string + stateBloomRoot common.Hash + ) + if err := filepath.Walk(datadir, func(path string, info os.FileInfo, err error) error { + if info != nil && !info.IsDir() { + ok, root := isBloomFilter(path) + if ok { + stateBloomPath = path + stateBloomRoot = root + } + } + return nil + }); err != nil { + return "", common.Hash{}, err + } + return stateBloomPath, stateBloomRoot, nil +} + +const warningLog = ` + +WARNING! + +The clean trie cache is not found. Please delete it by yourself after the +pruning. Remember don't start the Geth without deleting the clean trie cache +otherwise the entire database may be damaged! + +Check the command description "geth snapshot prune-state --help" for more details. +` + +func deleteCleanTrieCache(path string) { + if _, err := os.Stat(path); os.IsNotExist(err) { + log.Warn(warningLog) + return + } + os.RemoveAll(path) + log.Info("Deleted trie clean cache", "path", path) +} diff --git a/core/state/snapshot/conversion.go b/core/state/snapshot/conversion.go index dee9ff0bf2..bb87ecddf1 100644 --- a/core/state/snapshot/conversion.go +++ b/core/state/snapshot/conversion.go @@ -18,12 +18,17 @@ package snapshot import ( "bytes" + "encoding/binary" + "errors" "fmt" + "math" + "runtime" "sync" "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethdb/memorydb" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -38,46 +43,56 @@ type trieKV struct { type ( // trieGeneratorFn is the interface of trie generation which can // be implemented by different trie algorithm. - trieGeneratorFn func(in chan (trieKV), out chan (common.Hash)) + trieGeneratorFn func(db ethdb.KeyValueWriter, in chan (trieKV), out chan (common.Hash)) // leafCallbackFn is the callback invoked at the leaves of the trie, // returns the subtrie root with the specified subtrie identifier. - leafCallbackFn func(hash common.Hash, stat *generateStats) common.Hash + leafCallbackFn func(db ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) ) // GenerateAccountTrieRoot takes an account iterator and reproduces the root hash. func GenerateAccountTrieRoot(it AccountIterator) (common.Hash, error) { - return generateTrieRoot(it, common.Hash{}, stdGenerate, nil, &generateStats{start: time.Now()}, true) + return generateTrieRoot(nil, it, common.Hash{}, stackTrieGenerate, nil, newGenerateStats(), true) } // GenerateStorageTrieRoot takes a storage iterator and reproduces the root hash. func GenerateStorageTrieRoot(account common.Hash, it StorageIterator) (common.Hash, error) { - return generateTrieRoot(it, account, stdGenerate, nil, &generateStats{start: time.Now()}, true) + return generateTrieRoot(nil, it, account, stackTrieGenerate, nil, newGenerateStats(), true) } -// VerifyState takes the whole snapshot tree as the input, traverses all the accounts -// as well as the corresponding storages and compares the re-computed hash with the -// original one(state root and the storage root). -func VerifyState(snaptree *Tree, root common.Hash) error { +// GenerateTrie takes the whole snapshot tree as the input, traverses all the +// accounts as well as the corresponding storages and regenerate the whole state +// (account trie + all storage tries). +func GenerateTrie(snaptree *Tree, root common.Hash, src ethdb.Database, dst ethdb.KeyValueWriter) error { + // Traverse all state by snapshot, re-generate the whole state trie acctIt, err := snaptree.AccountIterator(root, common.Hash{}) if err != nil { - return err + return err // The required snapshot might not exist. } defer acctIt.Release() - got, err := generateTrieRoot(acctIt, common.Hash{}, stdGenerate, func(account common.Hash, stat *generateStats) common.Hash { - storageIt, err := snaptree.StorageIterator(root, account, common.Hash{}) + got, err := generateTrieRoot(dst, acctIt, common.Hash{}, stackTrieGenerate, func(dst ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) { + // Migrate the code first, commit the contract code into the tmp db. + if codeHash != emptyCode { + code := rawdb.ReadCode(src, codeHash) + if len(code) == 0 { + return common.Hash{}, errors.New("failed to read contract code") + } + rawdb.WriteCode(dst, codeHash, code) + } + // Then migrate all storage trie nodes into the tmp db. + storageIt, err := snaptree.StorageIterator(root, accountHash, common.Hash{}) if err != nil { - return common.Hash{} + return common.Hash{}, err } defer storageIt.Release() - hash, err := generateTrieRoot(storageIt, account, stdGenerate, nil, stat, false) + hash, err := generateTrieRoot(dst, storageIt, accountHash, stackTrieGenerate, nil, stat, false) if err != nil { - return common.Hash{} + return common.Hash{}, err } - return hash - }, &generateStats{start: time.Now()}, true) + return hash, nil + }, newGenerateStats(), true) if err != nil { return err @@ -91,23 +106,64 @@ func VerifyState(snaptree *Tree, root common.Hash) error { // generateStats is a collection of statistics gathered by the trie generator // for logging purposes. type generateStats struct { - accounts uint64 - slots uint64 - curAccount common.Hash - curSlot common.Hash - start time.Time - lock sync.RWMutex + head common.Hash + start time.Time + + accounts uint64 // Number of accounts done (including those being crawled) + slots uint64 // Number of storage slots done (including those being crawled) + + slotsStart map[common.Hash]time.Time // Start time for account slot crawling + slotsHead map[common.Hash]common.Hash // Slot head for accounts being crawled + + lock sync.RWMutex } -// progress records the progress trie generator made recently. -func (stat *generateStats) progress(accounts, slots uint64, curAccount common.Hash, curSlot common.Hash) { +// newGenerateStats creates a new generator stats. +func newGenerateStats() *generateStats { + return &generateStats{ + slotsStart: make(map[common.Hash]time.Time), + slotsHead: make(map[common.Hash]common.Hash), + start: time.Now(), + } +} + +// progressAccounts updates the generator stats for the account range. +func (stat *generateStats) progressAccounts(account common.Hash, done uint64) { stat.lock.Lock() defer stat.lock.Unlock() - stat.accounts += accounts - stat.slots += slots - stat.curAccount = curAccount - stat.curSlot = curSlot + stat.accounts += done + stat.head = account +} + +// finishAccounts updates the gemerator stats for the finished account range. +func (stat *generateStats) finishAccounts(done uint64) { + stat.lock.Lock() + defer stat.lock.Unlock() + + stat.accounts += done +} + +// progressContract updates the generator stats for a specific in-progress contract. +func (stat *generateStats) progressContract(account common.Hash, slot common.Hash, done uint64) { + stat.lock.Lock() + defer stat.lock.Unlock() + + stat.slots += done + stat.slotsHead[account] = slot + if _, ok := stat.slotsStart[account]; !ok { + stat.slotsStart[account] = time.Now() + } +} + +// finishContract updates the generator stats for a specific just-finished contract. +func (stat *generateStats) finishContract(account common.Hash, done uint64) { + stat.lock.Lock() + defer stat.lock.Unlock() + + stat.slots += done + delete(stat.slotsHead, account) + delete(stat.slotsStart, account) } // report prints the cumulative progress statistic smartly. @@ -115,22 +171,39 @@ func (stat *generateStats) report() { stat.lock.RLock() defer stat.lock.RUnlock() - var ctx []interface{} - if stat.curSlot != (common.Hash{}) { - ctx = append(ctx, []interface{}{ - "in", stat.curAccount, - "at", stat.curSlot, - }...) - } else { - ctx = append(ctx, []interface{}{"at", stat.curAccount}...) + ctx := []interface{}{ + "accounts", stat.accounts, + "slots", stat.slots, + "elapsed", common.PrettyDuration(time.Since(stat.start)), } - // Add the usual measurements - ctx = append(ctx, []interface{}{"accounts", stat.accounts}...) - if stat.slots != 0 { - ctx = append(ctx, []interface{}{"slots", stat.slots}...) + if stat.accounts > 0 { + // If there's progress on the account trie, estimate the time to finish crawling it + if done := binary.BigEndian.Uint64(stat.head[:8]) / stat.accounts; done > 0 { + var ( + left = (math.MaxUint64 - binary.BigEndian.Uint64(stat.head[:8])) / stat.accounts + speed = done/uint64(time.Since(stat.start)/time.Millisecond+1) + 1 // +1s to avoid division by zero + eta = time.Duration(left/speed) * time.Millisecond + ) + // If there are large contract crawls in progress, estimate their finish time + for acc, head := range stat.slotsHead { + start := stat.slotsStart[acc] + if done := binary.BigEndian.Uint64(head[:8]); done > 0 { + var ( + left = math.MaxUint64 - binary.BigEndian.Uint64(head[:8]) + speed = done/uint64(time.Since(start)/time.Millisecond+1) + 1 // +1s to avoid division by zero + ) + // Override the ETA if larger than the largest until now + if slotETA := time.Duration(left/speed) * time.Millisecond; eta < slotETA { + eta = slotETA + } + } + } + ctx = append(ctx, []interface{}{ + "eta", common.PrettyDuration(eta), + }...) + } } - ctx = append(ctx, []interface{}{"elapsed", common.PrettyDuration(time.Since(stat.start))}...) - log.Info("Generating trie hash from snapshot", ctx...) + log.Info("Iterating state snapshot", ctx...) } // reportDone prints the last log when the whole generation is finished. @@ -144,13 +217,32 @@ func (stat *generateStats) reportDone() { ctx = append(ctx, []interface{}{"slots", stat.slots}...) } ctx = append(ctx, []interface{}{"elapsed", common.PrettyDuration(time.Since(stat.start))}...) - log.Info("Generated trie hash from snapshot", ctx...) + log.Info("Iterated snapshot", ctx...) +} + +// runReport periodically prints the progress information. +func runReport(stats *generateStats, stop chan bool) { + timer := time.NewTimer(0) + defer timer.Stop() + + for { + select { + case <-timer.C: + stats.report() + timer.Reset(time.Second * 8) + case success := <-stop: + if success { + stats.reportDone() + } + return + } + } } // generateTrieRoot generates the trie hash based on the snapshot iterator. // It can be used for generating account trie, storage trie or even the // whole state which connects the accounts and the corresponding storages. -func generateTrieRoot(it Iterator, account common.Hash, generatorFn trieGeneratorFn, leafCallback leafCallbackFn, stats *generateStats, report bool) (common.Hash, error) { +func generateTrieRoot(db ethdb.KeyValueWriter, it Iterator, account common.Hash, generatorFn trieGeneratorFn, leafCallback leafCallbackFn, stats *generateStats, report bool) (common.Hash, error) { var ( in = make(chan trieKV) // chan to pass leaves out = make(chan common.Hash, 1) // chan to collect result @@ -161,46 +253,43 @@ func generateTrieRoot(it Iterator, account common.Hash, generatorFn trieGenerato wg.Add(1) go func() { defer wg.Done() - generatorFn(in, out) + generatorFn(db, in, out) }() - // Spin up a go-routine for progress logging if report && stats != nil { wg.Add(1) go func() { defer wg.Done() - - timer := time.NewTimer(0) - defer timer.Stop() - - for { - select { - case <-timer.C: - stats.report() - timer.Reset(time.Second * 8) - case success := <-stoplog: - if success { - stats.reportDone() - } - return - } - } + runReport(stats, stoplog) }() } + // Create a semaphore to assign tasks and collect results through. We'll pre- + // fill it with nils, thus using the same channel for both limiting concurrent + // processing and gathering results. + threads := runtime.NumCPU() + results := make(chan error, threads) + for i := 0; i < threads; i++ { + results <- nil // fill the semaphore + } // stop is a helper function to shutdown the background threads // and return the re-generated trie hash. - stop := func(success bool) common.Hash { + stop := func(fail error) (common.Hash, error) { close(in) result := <-out - stoplog <- success + for i := 0; i < threads; i++ { + if err := <-results; err != nil && fail == nil { + fail = err + } + } + stoplog <- fail == nil + wg.Wait() - return result + return result, fail } var ( logged = time.Now() processed = uint64(0) leaf trieKV - last common.Hash ) // Start to feed leaves for it.Next() { @@ -212,26 +301,35 @@ func generateTrieRoot(it Iterator, account common.Hash, generatorFn trieGenerato if leafCallback == nil { fullData, err = FullAccountRLP(it.(AccountIterator).Account()) if err != nil { - stop(false) - return common.Hash{}, err + return stop(err) } } else { + // Wait until the semaphore allows us to continue, aborting if + // a sub-task failed + if err := <-results; err != nil { + results <- nil // stop will drain the results, add a noop back for this error we just consumed + return stop(err) + } + // Fetch the next account and process it concurrently account, err := FullAccount(it.(AccountIterator).Account()) if err != nil { - stop(false) - return common.Hash{}, err - } - // Apply the leaf callback. Normally the callback is used to traverse - // the storage trie and re-generate the subtrie root. - subroot := leafCallback(it.Hash(), stats) - if !bytes.Equal(account.Root, subroot.Bytes()) { - stop(false) - return common.Hash{}, fmt.Errorf("invalid subroot(%x), want %x, got %x", it.Hash(), account.Root, subroot) + return stop(err) } + go func(hash common.Hash) { + subroot, err := leafCallback(db, hash, common.BytesToHash(account.CodeHash), stats) + if err != nil { + results <- err + return + } + if !bytes.Equal(account.Root, subroot.Bytes()) { + results <- fmt.Errorf("invalid subroot(%x), want %x, got %x", it.Hash(), account.Root, subroot) + return + } + results <- nil + }(it.Hash()) fullData, err = rlp.EncodeToBytes(account) if err != nil { - stop(false) - return common.Hash{}, err + return stop(err) } } leaf = trieKV{it.Hash(), fullData} @@ -240,36 +338,38 @@ func generateTrieRoot(it Iterator, account common.Hash, generatorFn trieGenerato } in <- leaf - // Accumulate the generaation statistic if it's required. + // Accumulate the generation statistic if it's required. processed++ if time.Since(logged) > 3*time.Second && stats != nil { if account == (common.Hash{}) { - stats.progress(processed, 0, it.Hash(), common.Hash{}) + stats.progressAccounts(it.Hash(), processed) } else { - stats.progress(0, processed, account, it.Hash()) + stats.progressContract(account, it.Hash(), processed) } logged, processed = time.Now(), 0 } - last = it.Hash() } // Commit the last part statistic. if processed > 0 && stats != nil { if account == (common.Hash{}) { - stats.progress(processed, 0, last, common.Hash{}) + stats.finishAccounts(processed) } else { - stats.progress(0, processed, account, last) + stats.finishContract(account, processed) } } - result := stop(true) - return result, nil + return stop(nil) } -// stdGenerate is a very basic hexary trie builder which uses the same Trie -// as the rest of geth, with no enhancements or optimizations -func stdGenerate(in chan (trieKV), out chan (common.Hash)) { - t, _ := trie.New(common.Hash{}, trie.NewDatabase(memorydb.New())) +func stackTrieGenerate(db ethdb.KeyValueWriter, in chan trieKV, out chan common.Hash) { + t := trie.NewStackTrie(db) for leaf := range in { t.TryUpdate(leaf.key[:], leaf.value) } - out <- t.Hash() + var root common.Hash + if db == nil { + root = t.Hash() + } else { + root, _ = t.Commit() + } + out <- root } diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index 0aef6cf570..ee88938b77 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -28,7 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rlp" - "github.com/steakknife/bloomfilter" + bloomfilter "github.com/holiman/bloomfilter/v2" ) var ( @@ -44,7 +44,7 @@ var ( // aggregatorItemLimit is an approximate number of items that will end up // in the agregator layer before it's flushed out to disk. A plain account // weighs around 14B (+hash), a storage slot 32B (+hash), a deleted slot - // 0B (+hash). Slots are mostly set/unset in lockstep, so thet average at + // 0B (+hash). Slots are mostly set/unset in lockstep, so that average at // 16B (+hash). All in all, the average entry seems to be 15+32=47B. Use a // smaller number to be on the safe side. aggregatorItemLimit = aggregatorMemoryLimit / 42 @@ -114,9 +114,9 @@ type diffLayer struct { // deleted, all data in other set belongs to the "new" A. destructSet map[common.Hash]struct{} // Keyed markers for deleted (and potentially) recreated accounts accountList []common.Hash // List of account for iteration. If it exists, it's sorted, otherwise it's nil - accountData map[common.Hash][]byte // Keyed accounts for direct retrival (nil means deleted) + accountData map[common.Hash][]byte // Keyed accounts for direct retrieval (nil means deleted) storageList map[common.Hash][]common.Hash // List of storage slots for iterated retrievals, one per account. Any existing lists are sorted if non-nil - storageData map[common.Hash]map[common.Hash][]byte // Keyed storage slots for direct retrival. one per account (nil means deleted) + storageData map[common.Hash]map[common.Hash][]byte // Keyed storage slots for direct retrieval. one per account (nil means deleted) diffed *bloomfilter.Filter // Bloom filter tracking all the diffed items up to the disk layer @@ -191,19 +191,15 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s if blob == nil { panic(fmt.Sprintf("account %#x nil", accountHash)) } + // Determine memory size and track the dirty writes + dl.memory += uint64(common.HashLength + len(blob)) + snapshotDirtyAccountWriteMeter.Mark(int64(len(blob))) } for accountHash, slots := range storage { if slots == nil { panic(fmt.Sprintf("storage %#x nil", accountHash)) } - } - // Determine memory size and track the dirty writes - for _, data := range accounts { - dl.memory += uint64(common.HashLength + len(data)) - snapshotDirtyAccountWriteMeter.Mark(int64(len(data))) - } - // Determine memory size and track the dirty writes - for _, slots := range storage { + // Determine memory size and track the dirty writes for _, data := range slots { dl.memory += uint64(common.HashLength + len(data)) snapshotDirtyStorageWriteMeter.Mark(int64(len(data))) @@ -300,13 +296,17 @@ func (dl *diffLayer) AccountRLP(hash common.Hash) ([]byte, error) { if !hit { hit = dl.diffed.Contains(destructBloomHasher(hash)) } + var origin *diskLayer + if !hit { + origin = dl.origin // extract origin while holding the lock + } dl.lock.RUnlock() // If the bloom filter misses, don't even bother with traversing the memory // diff layers, reach straight into the bottom persistent disk layer - if !hit { + if origin != nil { snapshotBloomAccountMissMeter.Mark(1) - return dl.origin.AccountRLP(hash) + return origin.AccountRLP(hash) } // The bloom filter hit, start poking in the internal maps return dl.accountRLP(hash, 0) @@ -362,13 +362,17 @@ func (dl *diffLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro if !hit { hit = dl.diffed.Contains(destructBloomHasher(accountHash)) } + var origin *diskLayer + if !hit { + origin = dl.origin // extract origin while holding the lock + } dl.lock.RUnlock() // If the bloom filter misses, don't even bother with traversing the memory // diff layers, reach straight into the bottom persistent disk layer - if !hit { + if origin != nil { snapshotBloomStorageMissMeter.Mark(1) - return dl.origin.Storage(accountHash, storageHash) + return origin.Storage(accountHash, storageHash) } // The bloom filter hit, start poking in the internal maps return dl.storage(accountHash, storageHash, 0) @@ -482,7 +486,7 @@ func (dl *diffLayer) flatten() snapshot { } } -// AccountList returns a sorted list of all accounts in this difflayer, including +// AccountList returns a sorted list of all accounts in this diffLayer, including // the deleted ones. // // Note, the returned slice is not a copy, so do not modify it. @@ -513,7 +517,7 @@ func (dl *diffLayer) AccountList() []common.Hash { return dl.accountList } -// StorageList returns a sorted list of all storage slot hashes in this difflayer +// StorageList returns a sorted list of all storage slot hashes in this diffLayer // for the given account. If the whole storage is destructed in this layer, then // an additional flag *destructed = true* will be returned, otherwise the flag is // false. Besides, the returned list will include the hash of deleted storage slot. diff --git a/core/state/snapshot/difflayer_test.go b/core/state/snapshot/difflayer_test.go index 31636ee133..919af5fa86 100644 --- a/core/state/snapshot/difflayer_test.go +++ b/core/state/snapshot/difflayer_test.go @@ -314,7 +314,7 @@ func BenchmarkSearchSlot(b *testing.B) { // With accountList and sorting // BenchmarkFlatten-6 50 29890856 ns/op // -// Without sorting and tracking accountlist +// Without sorting and tracking accountList // BenchmarkFlatten-6 300 5511511 ns/op func BenchmarkFlatten(b *testing.B) { fill := func(parent snapshot) *diffLayer { diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index e8f2bc853f..7cbf6e293d 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -31,7 +31,7 @@ import ( // diskLayer is a low level persistent snapshot built on top of a key-value store. type diskLayer struct { diskdb ethdb.KeyValueStore // Key-value store containing the base snapshot - triedb *trie.Database // Trie node cache for reconstuction purposes + triedb *trie.Database // Trie node cache for reconstruction purposes cache *fastcache.Cache // Cache to avoid hitting the disk for direct access root common.Hash // Root hash of the base snapshot diff --git a/core/state/snapshot/disklayer_test.go b/core/state/snapshot/disklayer_test.go index 40ff5ade4c..ccde2fc094 100644 --- a/core/state/snapshot/disklayer_test.go +++ b/core/state/snapshot/disklayer_test.go @@ -482,7 +482,7 @@ func TestDiskGeneratorPersistence(t *testing.T) { if !bytes.Equal(generator.Marker, genMarker) { t.Fatalf("Generator marker is not matched") } - // Test senario 2, the disk layer is fully generated + // Test scenario 2, the disk layer is fully generated // Modify or delete some accounts, flatten everything onto disk if err := snaps.Update(diffTwoRoot, diffRoot, nil, map[common.Hash][]byte{ accThree: accThree.Bytes(), @@ -524,7 +524,7 @@ func TestDiskSeek(t *testing.T) { t.Fatal(err) } else { defer os.RemoveAll(dir) - diskdb, err := leveldb.New(dir, 256, 0, "") + diskdb, err := leveldb.New(dir, 256, 0, "", false) if err != nil { t.Fatal(err) } diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go index 92c7640c40..2b41dd5513 100644 --- a/core/state/snapshot/generate.go +++ b/core/state/snapshot/generate.go @@ -101,18 +101,26 @@ func generateSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache i wiper = wipeSnapshot(diskdb, true) } // Create a new disk layer with an initialized state marker at zero - rawdb.WriteSnapshotRoot(diskdb, root) - + var ( + stats = &generatorStats{wiping: wiper, start: time.Now()} + batch = diskdb.NewBatch() + genMarker = []byte{} // Initialized but empty! + ) + rawdb.WriteSnapshotRoot(batch, root) + journalProgress(batch, genMarker, stats) + if err := batch.Write(); err != nil { + log.Crit("Failed to write initialized state marker", "error", err) + } base := &diskLayer{ diskdb: diskdb, triedb: triedb, root: root, cache: fastcache.New(cache * 1024 * 1024), - genMarker: []byte{}, // Initialized but empty! + genMarker: genMarker, genPending: make(chan struct{}), genAbort: make(chan chan *generatorStats), } - go base.generate(&generatorStats{wiping: wiper, start: time.Now()}) + go base.generate(stats) log.Debug("Start snapshot generation", "root", root) return base } @@ -137,10 +145,12 @@ func journalProgress(db ethdb.KeyValueWriter, marker []byte, stats *generatorSta panic(err) // Cannot happen, here to catch dev errors } var logstr string - switch len(marker) { - case 0: + switch { + case marker == nil: logstr = "done" - case common.HashLength: + case bytes.Equal(marker, []byte{}): + logstr = "empty" + case len(marker) == common.HashLength: logstr = fmt.Sprintf("%#x", marker) default: logstr = fmt.Sprintf("%#x:%#x", marker[:common.HashLength], marker[common.HashLength:]) @@ -151,7 +161,7 @@ func journalProgress(db ethdb.KeyValueWriter, marker []byte, stats *generatorSta // generate is a background thread that iterates over the state and storage tries, // constructing the state snapshot. All the arguments are purely for statistics -// gethering and logging, since the method surfs the blocks as they arrive, often +// gathering and logging, since the method surfs the blocks as they arrive, often // being restarted. func (dl *diskLayer) generate(stats *generatorStats) { // If a database wipe is in operation, wait until it's done @@ -241,7 +251,7 @@ func (dl *diskLayer) generate(stats *generatorStats) { if acc.Root != emptyRoot { storeTrie, err := trie.NewSecure(acc.Root, dl.triedb) if err != nil { - log.Error("Generator failed to access storage trie", "accroot", dl.root, "acchash", common.BytesToHash(accIt.Key), "stroot", acc.Root, "err", err) + log.Error("Generator failed to access storage trie", "root", dl.root, "account", accountHash, "stroot", acc.Root, "err", err) abort := <-dl.genAbort abort <- stats return @@ -281,6 +291,10 @@ func (dl *diskLayer) generate(stats *generatorStats) { abort <- stats return } + if time.Since(logged) > 8*time.Second { + stats.Log("Generating state snapshot", dl.root, append(accountHash[:], storeIt.Key...)) + logged = time.Now() + } } } if err := storeIt.Err; err != nil { @@ -303,13 +317,12 @@ func (dl *diskLayer) generate(stats *generatorStats) { abort <- stats return } - // Snapshot fully generated, set the marker to nil - if batch.ValueSize() > 0 { - // Ensure the generator entry is in sync with the data - journalProgress(batch, nil, stats) + // Snapshot fully generated, set the marker to nil. + // Note even there is nothing to commit, persist the + // generator anyway to mark the snapshot is complete. + journalProgress(batch, nil, stats) + batch.Write() - batch.Write() - } log.Info("Generated state snapshot", "accounts", stats.accounts, "slots", stats.slots, "storage", stats.storage, "elapsed", common.PrettyDuration(time.Since(stats.start))) diff --git a/core/state/snapshot/iterator.go b/core/state/snapshot/iterator.go index 5f943fea9f..1d9340bbad 100644 --- a/core/state/snapshot/iterator.go +++ b/core/state/snapshot/iterator.go @@ -133,7 +133,7 @@ func (it *diffAccountIterator) Hash() common.Hash { // Account returns the RLP encoded slim account the iterator is currently at. // This method may _fail_, if the underlying layer has been flattened between -// the call to Next and Acccount. That type of error will set it.Err. +// the call to Next and Account. That type of error will set it.Err. // This method assumes that flattening does not delete elements from // the accountdata mapping (writing nil into it is fine though), and will panic // if elements have been deleted. @@ -243,7 +243,7 @@ type diffStorageIterator struct { } // StorageIterator creates a storage iterator over a single diff layer. -// Execept the storage iterator is returned, there is an additional flag +// Except the storage iterator is returned, there is an additional flag // "destructed" returned. If it's true then it means the whole storage is // destructed in this layer(maybe recreated too), don't bother deeper layer // for storage retrieval. diff --git a/core/state/snapshot/iterator_binary.go b/core/state/snapshot/iterator_binary.go index f82f750029..22184b2545 100644 --- a/core/state/snapshot/iterator_binary.go +++ b/core/state/snapshot/iterator_binary.go @@ -37,7 +37,7 @@ type binaryIterator struct { } // initBinaryAccountIterator creates a simplistic iterator to step over all the -// accounts in a slow, but eaily verifiable way. Note this function is used for +// accounts in a slow, but easily verifiable way. Note this function is used for // initialization, use `newBinaryAccountIterator` as the API. func (dl *diffLayer) initBinaryAccountIterator() Iterator { parent, ok := dl.parent.(*diffLayer) @@ -62,7 +62,7 @@ func (dl *diffLayer) initBinaryAccountIterator() Iterator { } // initBinaryStorageIterator creates a simplistic iterator to step over all the -// storage slots in a slow, but eaily verifiable way. Note this function is used +// storage slots in a slow, but easily verifiable way. Note this function is used // for initialization, use `newBinaryStorageIterator` as the API. func (dl *diffLayer) initBinaryStorageIterator(account common.Hash) Iterator { parent, ok := dl.parent.(*diffLayer) @@ -199,14 +199,14 @@ func (it *binaryIterator) Release() { } // newBinaryAccountIterator creates a simplistic account iterator to step over -// all the accounts in a slow, but eaily verifiable way. +// all the accounts in a slow, but easily verifiable way. func (dl *diffLayer) newBinaryAccountIterator() AccountIterator { iter := dl.initBinaryAccountIterator() return iter.(AccountIterator) } // newBinaryStorageIterator creates a simplistic account iterator to step over -// all the storage slots in a slow, but eaily verifiable way. +// all the storage slots in a slow, but easily verifiable way. func (dl *diffLayer) newBinaryStorageIterator(account common.Hash) StorageIterator { iter := dl.initBinaryStorageIterator(account) return iter.(StorageIterator) diff --git a/core/state/snapshot/iterator_fast.go b/core/state/snapshot/iterator_fast.go index 291d52900d..48069b8fcf 100644 --- a/core/state/snapshot/iterator_fast.go +++ b/core/state/snapshot/iterator_fast.go @@ -75,7 +75,7 @@ type fastIterator struct { fail error } -// newFastIterator creates a new hierarhical account or storage iterator with one +// newFastIterator creates a new hierarchical account or storage iterator with one // element per diff layer. The returned combo iterator can be used to walk over // the entire snapshot diff stack simultaneously. func newFastIterator(tree *Tree, root common.Hash, account common.Hash, seek common.Hash, accountIterator bool) (*fastIterator, error) { @@ -335,14 +335,14 @@ func (fi *fastIterator) Debug() { fmt.Println() } -// newFastAccountIterator creates a new hierarhical account iterator with one +// newFastAccountIterator creates a new hierarchical account iterator with one // element per diff layer. The returned combo iterator can be used to walk over // the entire snapshot diff stack simultaneously. func newFastAccountIterator(tree *Tree, root common.Hash, seek common.Hash) (AccountIterator, error) { return newFastIterator(tree, root, common.Hash{}, seek, true) } -// newFastStorageIterator creates a new hierarhical storage iterator with one +// newFastStorageIterator creates a new hierarchical storage iterator with one // element per diff layer. The returned combo iterator can be used to walk over // the entire snapshot diff stack simultaneously. func newFastStorageIterator(tree *Tree, root common.Hash, account common.Hash, seek common.Hash) (StorageIterator, error) { diff --git a/core/state/snapshot/journal.go b/core/state/snapshot/journal.go index 178ba08902..d7e454cceb 100644 --- a/core/state/snapshot/journal.go +++ b/core/state/snapshot/journal.go @@ -441,6 +441,6 @@ func (dl *diffLayer) LegacyJournal(buffer *bytes.Buffer) (common.Hash, error) { if err := rlp.Encode(buffer, storage); err != nil { return common.Hash{}, err } - log.Debug("Legacy journalled disk layer", "root", dl.root, "parent", dl.parent.Root()) + log.Debug("Legacy journalled diff layer", "root", dl.root, "parent", dl.parent.Root()) return base, nil } diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index 60b4158b56..eccf377264 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -178,7 +178,7 @@ type Tree struct { // store, on a background thread. If the memory layers from the journal is not // continuous with disk layer or the journal is missing, all diffs will be discarded // iff it's in "recovery" mode, otherwise rebuild is mandatory. -func New(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, root common.Hash, async bool, recovery bool) *Tree { +func New(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, root common.Hash, async bool, rebuild bool, recovery bool) (*Tree, error) { // Create a new, empty snapshot tree snap := &Tree{ diskdb: diskdb, @@ -192,16 +192,19 @@ func New(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, root comm // Attempt to load a previously persisted snapshot and rebuild one if failed head, err := loadSnapshot(diskdb, triedb, cache, root, recovery) if err != nil { - log.Warn("Failed to load snapshot, regenerating", "err", err) - snap.Rebuild(root) - return snap + if rebuild { + log.Warn("Failed to load snapshot, regenerating", "err", err) + snap.Rebuild(root) + return snap, nil + } + return nil, err // Bail out the error, don't rebuild automatically. } // Existing snapshot loaded, seed all the layers for head != nil { snap.layers[head.Root()] = head head = head.Parent() } - return snap + return snap, nil } // waitBuild blocks until the snapshot finishes rebuilding. This method is meant @@ -234,6 +237,39 @@ func (t *Tree) Snapshot(blockRoot common.Hash) Snapshot { return t.layers[blockRoot] } +// Snapshots returns all visited layers from the topmost layer with specific +// root and traverses downward. The layer amount is limited by the given number. +// If nodisk is set, then disk layer is excluded. +func (t *Tree) Snapshots(root common.Hash, limits int, nodisk bool) []Snapshot { + t.lock.RLock() + defer t.lock.RUnlock() + + if limits == 0 { + return nil + } + layer := t.layers[root] + if layer == nil { + return nil + } + var ret []Snapshot + for { + if _, isdisk := layer.(*diskLayer); isdisk && nodisk { + break + } + ret = append(ret, layer) + limits -= 1 + if limits == 0 { + break + } + parent := layer.Parent() + if parent == nil { + break + } + layer = parent + } + return ret +} + // Update adds a new snapshot into the tree, if that can be linked to an existing // old parent. It is disallowed to insert a disk layer (the origin of all). func (t *Tree) Update(blockRoot common.Hash, parentRoot common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error { @@ -247,11 +283,11 @@ func (t *Tree) Update(blockRoot common.Hash, parentRoot common.Hash, destructs m return errSnapshotCycle } // Generate a new snapshot on top of the parent - parent := t.Snapshot(parentRoot).(snapshot) + parent := t.Snapshot(parentRoot) if parent == nil { return fmt.Errorf("parent [%#x] snapshot missing", parentRoot) } - snap := parent.Update(blockRoot, destructs, accounts, storage) + snap := parent.(snapshot).Update(blockRoot, destructs, accounts, storage) // Save the new snapshot for later t.lock.Lock() @@ -264,6 +300,12 @@ func (t *Tree) Update(blockRoot common.Hash, parentRoot common.Hash, destructs m // Cap traverses downwards the snapshot tree from a head block hash until the // number of allowed layers are crossed. All layers beyond the permitted number // are flattened downwards. +// +// Note, the final diff layer count in general will be one more than the amount +// requested. This happens because the bottom-most diff layer is the accumulator +// which may or may not overflow and cascade to disk. Since this last layer's +// survival is only known *after* capping, we need to omit it from the count if +// we want to ensure that *at least* the requested number of diff layers remain. func (t *Tree) Cap(root common.Hash, layers int) error { // Retrieve the head snapshot to cap from snap := t.Snapshot(root) @@ -288,10 +330,7 @@ func (t *Tree) Cap(root common.Hash, layers int) error { // Flattening the bottom-most diff layer requires special casing since there's // no child to rewire to the grandparent. In that case we can fake a temporary // child for the capping and then remove it. - var persisted *diskLayer - - switch layers { - case 0: + if layers == 0 { // If full commit was requested, flatten the diffs and merge onto disk diff.lock.RLock() base := diffToDisk(diff.flatten().(*diffLayer)) @@ -300,33 +339,9 @@ func (t *Tree) Cap(root common.Hash, layers int) error { // Replace the entire snapshot tree with the flat base t.layers = map[common.Hash]snapshot{base.root: base} return nil - - case 1: - // If full flattening was requested, flatten the diffs but only merge if the - // memory limit was reached - var ( - bottom *diffLayer - base *diskLayer - ) - diff.lock.RLock() - bottom = diff.flatten().(*diffLayer) - if bottom.memory >= aggregatorMemoryLimit { - base = diffToDisk(bottom) - } - diff.lock.RUnlock() - - // If all diff layers were removed, replace the entire snapshot tree - if base != nil { - t.layers = map[common.Hash]snapshot{base.root: base} - return nil - } - // Merge the new aggregated layer into the snapshot tree, clean stales below - t.layers[bottom.root] = bottom - - default: - // Many layers requested to be retained, cap normally - persisted = t.cap(diff, layers) } + persisted := t.cap(diff, layers) + // Remove any layer that is stale or links into a stale layer children := make(map[common.Hash][]common.Hash) for root, snap := range t.layers { @@ -368,10 +383,16 @@ func (t *Tree) Cap(root common.Hash, layers int) error { // crossed. All diffs beyond the permitted number are flattened downwards. If the // layer limit is reached, memory cap is also enforced (but not before). // -// The method returns the new disk layer if diffs were persistend into it. +// The method returns the new disk layer if diffs were persisted into it. +// +// Note, the final diff layer count in general will be one more than the amount +// requested. This happens because the bottom-most diff layer is the accumulator +// which may or may not overflow and cascade to disk. Since this last layer's +// survival is only known *after* capping, we need to omit it from the count if +// we want to ensure that *at least* the requested number of diff layers remain. func (t *Tree) cap(diff *diffLayer, layers int) *diskLayer { // Dive until we run out of layers or reach the persistent database - for ; layers > 2; layers-- { + for i := 0; i < layers-1; i++ { // If we still have diff layers below, continue down if parent, ok := diff.parent.(*diffLayer); ok { diff = parent @@ -463,8 +484,17 @@ func diffToDisk(bottom *diffLayer) *diskLayer { if key := it.Key(); len(key) == 65 { // TODO(karalabe): Yuck, we should move this into the iterator batch.Delete(key) base.cache.Del(key[1:]) - snapshotFlushStorageItemMeter.Mark(1) + + // Ensure we don't delete too much data blindly (contract can be + // huge). It's ok to flush, the root will go missing in case of a + // crash and we'll detect and regenerate the snapshot. + if batch.ValueSize() > ethdb.IdealBatchSize { + if err := batch.Write(); err != nil { + log.Crit("Failed to write storage deletions", "err", err) + } + batch.Reset() + } } } it.Release() @@ -482,6 +512,16 @@ func diffToDisk(bottom *diffLayer) *diskLayer { snapshotFlushAccountItemMeter.Mark(1) snapshotFlushAccountSizeMeter.Mark(int64(len(data))) + + // Ensure we don't write too much data blindly. It's ok to flush, the + // root will go missing in case of a crash and we'll detect and regen + // the snapshot. + if batch.ValueSize() > ethdb.IdealBatchSize { + if err := batch.Write(); err != nil { + log.Crit("Failed to write storage deletions", "err", err) + } + batch.Reset() + } } // Push all the storage slots into the database for accountHash, storage := range bottom.storageData { @@ -647,7 +687,7 @@ func (t *Tree) Rebuild(root common.Hash) { panic(fmt.Sprintf("unknown layer type: %T", layer)) } } - // Start generating a new snapshot from scratch on a backgroung thread. The + // Start generating a new snapshot from scratch on a background thread. The // generator will run a wiper first if there's not one running right now. log.Info("Rebuilding state snapshot") t.layers = map[common.Hash]snapshot{ @@ -681,6 +721,38 @@ func (t *Tree) StorageIterator(root common.Hash, account common.Hash, seek commo return newFastStorageIterator(t, root, account, seek) } +// Verify iterates the whole state(all the accounts as well as the corresponding storages) +// with the specific root and compares the re-computed hash with the original one. +func (t *Tree) Verify(root common.Hash) error { + acctIt, err := t.AccountIterator(root, common.Hash{}) + if err != nil { + return err + } + defer acctIt.Release() + + got, err := generateTrieRoot(nil, acctIt, common.Hash{}, stackTrieGenerate, func(db ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) { + storageIt, err := t.StorageIterator(root, accountHash, common.Hash{}) + if err != nil { + return common.Hash{}, err + } + defer storageIt.Release() + + hash, err := generateTrieRoot(nil, storageIt, accountHash, stackTrieGenerate, nil, stat, false) + if err != nil { + return common.Hash{}, err + } + return hash, nil + }, newGenerateStats(), true) + + if err != nil { + return err + } + if got != root { + return fmt.Errorf("state root hash mismatch: got %x, want %x", got, root) + } + return nil +} + // disklayer is an internal helper function to return the disk layer. // The lock of snapTree is assumed to be held already. func (t *Tree) disklayer() *diskLayer { diff --git a/core/state/snapshot/snapshot_test.go b/core/state/snapshot/snapshot_test.go index ca4fa0a055..4b787cfe2e 100644 --- a/core/state/snapshot/snapshot_test.go +++ b/core/state/snapshot/snapshot_test.go @@ -17,6 +17,7 @@ package snapshot import ( + "encoding/binary" "fmt" "math/big" "math/rand" @@ -161,57 +162,10 @@ func TestDiskLayerExternalInvalidationPartialFlatten(t *testing.T) { defer func(memcap uint64) { aggregatorMemoryLimit = memcap }(aggregatorMemoryLimit) aggregatorMemoryLimit = 0 - if err := snaps.Cap(common.HexToHash("0x03"), 2); err != nil { - t.Fatalf("failed to merge diff layer onto disk: %v", err) - } - // Since the base layer was modified, ensure that data retrievald on the external reference fail - if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale { - t.Errorf("stale reference returned account: %#x (err: %v)", acc, err) - } - if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale { - t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err) - } - if n := len(snaps.layers); n != 2 { - t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 2) - fmt.Println(snaps.layers) - } -} - -// Tests that if a diff layer becomes stale, no active external references will -// be returned with junk data. This version of the test flattens every diff layer -// to check internal corner case around the bottom-most memory accumulator. -func TestDiffLayerExternalInvalidationFullFlatten(t *testing.T) { - // Create an empty base layer and a snapshot tree out of it - base := &diskLayer{ - diskdb: rawdb.NewMemoryDatabase(), - root: common.HexToHash("0x01"), - cache: fastcache.New(1024 * 500), - } - snaps := &Tree{ - layers: map[common.Hash]snapshot{ - base.root: base, - }, - } - // Commit two diffs on top and retrieve a reference to the bottommost - accounts := map[common.Hash][]byte{ - common.HexToHash("0xa1"): randomAccount(), - } - if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil { - t.Fatalf("failed to create a diff layer: %v", err) - } - if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil { - t.Fatalf("failed to create a diff layer: %v", err) - } - if n := len(snaps.layers); n != 3 { - t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 3) - } - ref := snaps.Snapshot(common.HexToHash("0x02")) - - // Flatten the diff layer into the bottom accumulator if err := snaps.Cap(common.HexToHash("0x03"), 1); err != nil { - t.Fatalf("failed to flatten diff layer into accumulator: %v", err) + t.Fatalf("failed to merge accumulator onto disk: %v", err) } - // Since the accumulator diff layer was modified, ensure that data retrievald on the external reference fail + // Since the base layer was modified, ensure that data retrievald on the external reference fail if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale { t.Errorf("stale reference returned account: %#x (err: %v)", acc, err) } @@ -266,7 +220,7 @@ func TestDiffLayerExternalInvalidationPartialFlatten(t *testing.T) { t.Errorf("layers modified, got %d exp %d", got, exp) } // Flatten the diff layer into the bottom accumulator - if err := snaps.Cap(common.HexToHash("0x04"), 2); err != nil { + if err := snaps.Cap(common.HexToHash("0x04"), 1); err != nil { t.Fatalf("failed to flatten diff layer into accumulator: %v", err) } // Since the accumulator diff layer was modified, ensure that data retrievald on the external reference fail @@ -369,3 +323,103 @@ func TestPostCapBasicDataAccess(t *testing.T) { t.Error("expected error capping the disk layer, got none") } } + +// TestSnaphots tests the functionality for retrieveing the snapshot +// with given head root and the desired depth. +func TestSnaphots(t *testing.T) { + // setAccount is a helper to construct a random account entry and assign it to + // an account slot in a snapshot + setAccount := func(accKey string) map[common.Hash][]byte { + return map[common.Hash][]byte{ + common.HexToHash(accKey): randomAccount(), + } + } + makeRoot := func(height uint64) common.Hash { + var buffer [8]byte + binary.BigEndian.PutUint64(buffer[:], height) + return common.BytesToHash(buffer[:]) + } + // Create a starting base layer and a snapshot tree out of it + base := &diskLayer{ + diskdb: rawdb.NewMemoryDatabase(), + root: makeRoot(1), + cache: fastcache.New(1024 * 500), + } + snaps := &Tree{ + layers: map[common.Hash]snapshot{ + base.root: base, + }, + } + // Construct the snapshots with 129 layers, flattening whatever's above that + var ( + last = common.HexToHash("0x01") + head common.Hash + ) + for i := 0; i < 129; i++ { + head = makeRoot(uint64(i + 2)) + snaps.Update(head, last, nil, setAccount(fmt.Sprintf("%d", i+2)), nil) + last = head + snaps.Cap(head, 128) // 130 layers (128 diffs + 1 accumulator + 1 disk) + } + var cases = []struct { + headRoot common.Hash + limit int + nodisk bool + expected int + expectBottom common.Hash + }{ + {head, 0, false, 0, common.Hash{}}, + {head, 64, false, 64, makeRoot(129 + 2 - 64)}, + {head, 128, false, 128, makeRoot(3)}, // Normal diff layers, no accumulator + {head, 129, true, 129, makeRoot(2)}, // All diff layers, including accumulator + {head, 130, false, 130, makeRoot(1)}, // All diff layers + disk layer + } + for i, c := range cases { + layers := snaps.Snapshots(c.headRoot, c.limit, c.nodisk) + if len(layers) != c.expected { + t.Errorf("non-overflow test %d: returned snapshot layers are mismatched, want %v, got %v", i, c.expected, len(layers)) + } + if len(layers) == 0 { + continue + } + bottommost := layers[len(layers)-1] + if bottommost.Root() != c.expectBottom { + t.Errorf("non-overflow test %d: snapshot mismatch, want %v, get %v", i, c.expectBottom, bottommost.Root()) + } + } + // Above we've tested the normal capping, which leaves the accumulator live. + // Test that if the bottommost accumulator diff layer overflows the allowed + // memory limit, the snapshot tree gets capped to one less layer. + // Commit the diff layer onto the disk and ensure it's persisted + defer func(memcap uint64) { aggregatorMemoryLimit = memcap }(aggregatorMemoryLimit) + aggregatorMemoryLimit = 0 + + snaps.Cap(head, 128) // 129 (128 diffs + 1 overflown accumulator + 1 disk) + + cases = []struct { + headRoot common.Hash + limit int + nodisk bool + expected int + expectBottom common.Hash + }{ + {head, 0, false, 0, common.Hash{}}, + {head, 64, false, 64, makeRoot(129 + 2 - 64)}, + {head, 128, false, 128, makeRoot(3)}, // All diff layers, accumulator was flattened + {head, 129, true, 128, makeRoot(3)}, // All diff layers, accumulator was flattened + {head, 130, false, 129, makeRoot(2)}, // All diff layers + disk layer + } + for i, c := range cases { + layers := snaps.Snapshots(c.headRoot, c.limit, c.nodisk) + if len(layers) != c.expected { + t.Errorf("overflow test %d: returned snapshot layers are mismatched, want %v, got %v", i, c.expected, len(layers)) + } + if len(layers) == 0 { + continue + } + bottommost := layers[len(layers)-1] + if bottommost.Root() != c.expectBottom { + t.Errorf("overflow test %d: snapshot mismatch, want %v, get %v", i, c.expectBottom, bottommost.Root()) + } + } +} diff --git a/core/state/state_object.go b/core/state/state_object.go index d0d3b4513e..f93f47d5f5 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -157,11 +157,20 @@ func (s *stateObject) touch() { func (s *stateObject) getTrie(db Database) Trie { if s.trie == nil { - var err error - s.trie, err = db.OpenStorageTrie(s.addrHash, s.data.Root) - if err != nil { - s.trie, _ = db.OpenStorageTrie(s.addrHash, common.Hash{}) - s.setError(fmt.Errorf("can't create storage trie: %v", err)) + // Try fetching from prefetcher first + // We don't prefetch empty tries + if s.data.Root != emptyRoot && s.db.prefetcher != nil { + // When the miner is creating the pending state, there is no + // prefetcher + s.trie = s.db.prefetcher.trie(s.data.Root) + } + if s.trie == nil { + var err error + s.trie, err = db.OpenStorageTrie(s.addrHash, s.data.Root) + if err != nil { + s.trie, _ = db.OpenStorageTrie(s.addrHash, common.Hash{}) + s.setError(fmt.Errorf("can't create storage trie: %v", err)) + } } } return s.trie @@ -197,12 +206,24 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has } // If no live objects are available, attempt to use snapshots var ( - enc []byte - err error + enc []byte + err error + meter *time.Duration ) + readStart := time.Now() + if metrics.EnabledExpensive { + // If the snap is 'under construction', the first lookup may fail. If that + // happens, we don't want to double-count the time elapsed. Thus this + // dance with the metering. + defer func() { + if meter != nil { + *meter += time.Since(readStart) + } + }() + } if s.db.snap != nil { if metrics.EnabledExpensive { - defer func(start time.Time) { s.db.SnapshotStorageReads += time.Since(start) }(time.Now()) + meter = &s.db.SnapshotStorageReads } // If the object was destructed in *this* block (and potentially resurrected), // the storage has been cleared out, and we should *not* consult the previous @@ -217,8 +238,14 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has } // If snapshot unavailable or reading from it failed, load from the database if s.db.snap == nil || err != nil { + if meter != nil { + // If we already spent time checking the snapshot, account for it + // and reset the readStart + *meter += time.Since(readStart) + readStart = time.Now() + } if metrics.EnabledExpensive { - defer func(start time.Time) { s.db.StorageReads += time.Since(start) }(time.Now()) + meter = &s.db.StorageReads } if enc, err = s.getTrie(db).TryGet(key.Bytes()); err != nil { s.setError(err) @@ -282,9 +309,16 @@ func (s *stateObject) setState(key, value common.Hash) { // finalise moves all dirty storage slots into the pending area to be hashed or // committed later. It is invoked at the end of every transaction. -func (s *stateObject) finalise() { +func (s *stateObject) finalise(prefetch bool) { + slotsToPrefetch := make([][]byte, 0, len(s.dirtyStorage)) for key, value := range s.dirtyStorage { s.pendingStorage[key] = value + if value != s.originStorage[key] { + slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure + } + } + if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != emptyRoot { + s.db.prefetcher.prefetch(s.data.Root, slotsToPrefetch) } if len(s.dirtyStorage) > 0 { s.dirtyStorage = make(Storage) @@ -295,7 +329,7 @@ func (s *stateObject) finalise() { // It will return nil if the trie has not been loaded and no changes have been made func (s *stateObject) updateTrie(db Database) Trie { // Make sure all dirty slots are finalized into the pending storage area - s.finalise() + s.finalise(false) // Don't prefetch any more, pull directly if need be if len(s.pendingStorage) == 0 { return s.trie } @@ -303,18 +337,13 @@ func (s *stateObject) updateTrie(db Database) Trie { if metrics.EnabledExpensive { defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now()) } - // Retrieve the snapshot storage map for the object + // The snapshot storage map for the object var storage map[common.Hash][]byte - if s.db.snap != nil { - // Retrieve the old storage map, if available, create a new one otherwise - storage = s.db.snapStorage[s.addrHash] - if storage == nil { - storage = make(map[common.Hash][]byte) - s.db.snapStorage[s.addrHash] = storage - } - } // Insert all the pending updates into the trie tr := s.getTrie(db) + hasher := s.db.hasher + + usedStorage := make([][]byte, 0, len(s.pendingStorage)) for key, value := range s.pendingStorage { // Skip noop changes, persist actual changes if value == s.originStorage[key] { @@ -331,9 +360,20 @@ func (s *stateObject) updateTrie(db Database) Trie { s.setError(tr.TryUpdate(key[:], v)) } // If state snapshotting is active, cache the data til commit - if storage != nil { - storage[crypto.Keccak256Hash(key[:])] = v // v will be nil if value is 0x00 + if s.db.snap != nil { + if storage == nil { + // Retrieve the old storage map, if available, create a new one otherwise + if storage = s.db.snapStorage[s.addrHash]; storage == nil { + storage = make(map[common.Hash][]byte) + s.db.snapStorage[s.addrHash] = storage + } + } + storage[crypto.HashData(hasher, key[:])] = v // v will be nil if value is 0x00 } + usedStorage = append(usedStorage, common.CopyBytes(key[:])) // Copy needed for closure + } + if s.db.prefetcher != nil { + s.db.prefetcher.used(s.data.Root, usedStorage) } if len(s.pendingStorage) > 0 { s.pendingStorage = make(Storage) diff --git a/core/state/state_test.go b/core/state/state_test.go index 0dc4c0ad63..22e93d7a95 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -41,7 +41,9 @@ func newStateTest() *stateTest { } func TestDump(t *testing.T) { - s := newStateTest() + db := rawdb.NewMemoryDatabase() + sdb, _ := New(common.Hash{}, NewDatabaseWithConfig(db, nil), nil) + s := &stateTest{db: db, state: sdb} // generate a few entries obj1 := s.state.GetOrNewStateObject(toAddr([]byte{0x01})) @@ -168,7 +170,7 @@ func TestSnapshot2(t *testing.T) { state.setStateObject(so0) root, _ := state.Commit(false) - state.Reset(root) + state, _ = New(root, state.db, state.snaps) // and one with deleted == true so1 := state.getStateObject(stateobjaddr1) diff --git a/core/state/statedb.go b/core/state/statedb.go index f66e6330d7..4a01db91d4 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -62,8 +62,11 @@ func (n *proofList) Delete(key []byte) error { // * Contracts // * Accounts type StateDB struct { - db Database - trie Trie + db Database + prefetcher *triePrefetcher + originalRoot common.Hash // The pre-state root, before any changes were made + trie Trie + hasher crypto.KeccakState snaps *snapshot.Tree snap snapshot.Snapshot @@ -125,6 +128,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) sdb := &StateDB{ db: db, trie: tr, + originalRoot: root, snaps: snaps, stateObjects: make(map[common.Address]*stateObject), stateObjectsPending: make(map[common.Address]struct{}), @@ -133,6 +137,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) preimages: make(map[common.Hash][]byte), journal: newJournal(), accessList: newAccessList(), + hasher: crypto.NewKeccakState(), } if sdb.snaps != nil { if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap != nil { @@ -144,6 +149,28 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) return sdb, nil } +// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the +// state trie concurrently while the state is mutated so that when we reach the +// commit phase, most of the needed data is already hot. +func (s *StateDB) StartPrefetcher(namespace string) { + if s.prefetcher != nil { + s.prefetcher.close() + s.prefetcher = nil + } + if s.snap != nil { + s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, namespace) + } +} + +// StopPrefetcher terminates a running prefetcher and reports any leftover stats +// from the gathered metrics. +func (s *StateDB) StopPrefetcher() { + if s.prefetcher != nil { + s.prefetcher.close() + s.prefetcher = nil + } +} + // setError remembers the first non-nil error it is called with. func (s *StateDB) setError(err error) { if s.dbErr == nil { @@ -155,37 +182,6 @@ func (s *StateDB) Error() error { return s.dbErr } -// Reset clears out all ephemeral state objects from the state db, but keeps -// the underlying state trie to avoid reloading data for the next operations. -func (s *StateDB) Reset(root common.Hash) error { - tr, err := s.db.OpenTrie(root) - if err != nil { - return err - } - s.trie = tr - s.stateObjects = make(map[common.Address]*stateObject) - s.stateObjectsPending = make(map[common.Address]struct{}) - s.stateObjectsDirty = make(map[common.Address]struct{}) - s.thash = common.Hash{} - s.bhash = common.Hash{} - s.txIndex = 0 - s.logs = make(map[common.Hash][]*types.Log) - s.logSize = 0 - s.preimages = make(map[common.Hash][]byte) - s.clearJournalAndRefund() - - if s.snaps != nil { - s.snapAccounts, s.snapDestructs, s.snapStorage = nil, nil, nil - if s.snap = s.snaps.Snapshot(root); s.snap != nil { - s.snapDestructs = make(map[common.Hash]struct{}) - s.snapAccounts = make(map[common.Hash][]byte) - s.snapStorage = make(map[common.Hash]map[common.Hash][]byte) - } - } - s.accessList = newAccessList() - return nil -} - func (s *StateDB) AddLog(log *types.Log) { s.journal.append(addLogChange{txhash: s.thash}) @@ -314,14 +310,19 @@ func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { return common.Hash{} } -// GetProof returns the MerkleProof for a given Account -func (s *StateDB) GetProof(a common.Address) ([][]byte, error) { +// GetProof returns the Merkle proof for a given account. +func (s *StateDB) GetProof(addr common.Address) ([][]byte, error) { + return s.GetProofByHash(crypto.Keccak256Hash(addr.Bytes())) +} + +// GetProofByHash returns the Merkle proof for a given account. +func (s *StateDB) GetProofByHash(addrHash common.Hash) ([][]byte, error) { var proof proofList - err := s.trie.Prove(crypto.Keccak256(a.Bytes()), 0, &proof) - return [][]byte(proof), err + err := s.trie.Prove(addrHash[:], 0, &proof) + return proof, err } -// GetStorageProof returns the StorageProof for given key +// GetStorageProof returns the Merkle proof for given storage slot. func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) { var proof proofList trie := s.StorageTrie(a) @@ -329,7 +330,18 @@ func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, return proof, errors.New("storage trie for requested address does not exist") } err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof) - return [][]byte(proof), err + return proof, err +} + +// GetStorageProofByHash returns the Merkle proof for given storage slot. +func (s *StateDB) GetStorageProofByHash(a common.Address, key common.Hash) ([][]byte, error) { + var proof proofList + trie := s.StorageTrie(a) + if trie == nil { + return proof, errors.New("storage trie for requested address does not exist") + } + err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof) + return proof, err } // GetCommittedState retrieves a value from the given account's committed storage trie. @@ -516,7 +528,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { defer func(start time.Time) { s.SnapshotAccountReads += time.Since(start) }(time.Now()) } var acc *snapshot.Account - if acc, err = s.snap.Account(crypto.Keccak256Hash(addr.Bytes())); err == nil { + if acc, err = s.snap.Account(crypto.HashData(s.hasher, addr.Bytes())); err == nil { if acc == nil { return nil } @@ -659,6 +671,7 @@ func (s *StateDB) Copy() *StateDB { logSize: s.logSize, preimages: make(map[common.Hash][]byte, len(s.preimages)), journal: newJournal(), + hasher: crypto.NewKeccakState(), } // Copy the dirty states, logs, and preimages for addr := range s.journal.dirties { @@ -708,6 +721,38 @@ func (s *StateDB) Copy() *StateDB { // However, it doesn't cost us much to copy an empty list, so we do it anyway // to not blow up if we ever decide copy it in the middle of a transaction state.accessList = s.accessList.Copy() + + // If there's a prefetcher running, make an inactive copy of it that can + // only access data but does not actively preload (since the user will not + // know that they need to explicitly terminate an active copy). + if s.prefetcher != nil { + state.prefetcher = s.prefetcher.copy() + } + if s.snaps != nil { + // In order for the miner to be able to use and make additions + // to the snapshot tree, we need to copy that aswell. + // Otherwise, any block mined by ourselves will cause gaps in the tree, + // and force the miner to operate trie-backed only + state.snaps = s.snaps + state.snap = s.snap + // deep copy needed + state.snapDestructs = make(map[common.Hash]struct{}) + for k, v := range s.snapDestructs { + state.snapDestructs[k] = v + } + state.snapAccounts = make(map[common.Hash][]byte) + for k, v := range s.snapAccounts { + state.snapAccounts[k] = v + } + state.snapStorage = make(map[common.Hash]map[common.Hash][]byte) + for k, v := range s.snapStorage { + temp := make(map[common.Hash][]byte) + for kk, vv := range v { + temp[kk] = vv + } + state.snapStorage[k] = temp + } + } return state } @@ -745,6 +790,7 @@ func (s *StateDB) GetRefund() uint64 { // into the tries just yet. Only IntermediateRoot or Commit will do that. // NOTE: EIP161d func (s *StateDB) Finalise(deleteEmptyObjects bool) { + addressesToPrefetch := make([][]byte, 0, len(s.journal.dirties)) for addr := range s.journal.dirties { obj, exist := s.stateObjects[addr] if !exist { @@ -769,10 +815,18 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { delete(s.snapStorage, obj.addrHash) // Clear out any previously updated storage data (may be recreated via a ressurrect) } } else { - obj.finalise() + obj.finalise(true) // Prefetch slots in the background } s.stateObjectsPending[addr] = struct{}{} s.stateObjectsDirty[addr] = struct{}{} + + // At this point, also ship the address off to the precacher. The precacher + // will start loading tries, and when the change is eventually committed, + // the commit-phase will be a lot faster + addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) // Copy needed for closure + } + if s.prefetcher != nil && len(addressesToPrefetch) > 0 { + s.prefetcher.prefetch(s.originalRoot, addressesToPrefetch) } // Invalidate journal because reverting across transactions is not allowed. s.clearJournalAndRefund() @@ -785,14 +839,49 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // Finalise all the dirty storage states and write them into the tries s.Finalise(deleteEmptyObjects) + // If there was a trie prefetcher operating, it gets aborted and irrevocably + // modified after we start retrieving tries. Remove it from the statedb after + // this round of use. + // + // This is weird pre-byzantium since the first tx runs with a prefetcher and + // the remainder without, but pre-byzantium even the initial prefetcher is + // useless, so no sleep lost. + prefetcher := s.prefetcher + if s.prefetcher != nil { + defer func() { + s.prefetcher.close() + s.prefetcher = nil + }() + } + // Although naively it makes sense to retrieve the account trie and then do + // the contract storage and account updates sequentially, that short circuits + // the account prefetcher. Instead, let's process all the storage updates + // first, giving the account prefeches just a few more milliseconds of time + // to pull useful data from disk. + for addr := range s.stateObjectsPending { + if obj := s.stateObjects[addr]; !obj.deleted { + obj.updateRoot(s.db) + } + } + // Now we're about to start to write changes to the trie. The trie is so far + // _untouched_. We can check with the prefetcher, if it can give us a trie + // which has the same root, but also has some content loaded into it. + if prefetcher != nil { + if trie := prefetcher.trie(s.originalRoot); trie != nil { + s.trie = trie + } + } + usedAddrs := make([][]byte, 0, len(s.stateObjectsPending)) for addr := range s.stateObjectsPending { - obj := s.stateObjects[addr] - if obj.deleted { + if obj := s.stateObjects[addr]; obj.deleted { s.deleteStateObject(obj) } else { - obj.updateRoot(s.db) s.updateStateObject(obj) } + usedAddrs = append(usedAddrs, common.CopyBytes(addr[:])) // Copy needed for closure + } + if prefetcher != nil { + prefetcher.used(s.originalRoot, usedAddrs) } if len(s.stateObjectsPending) > 0 { s.stateObjectsPending = make(map[common.Address]struct{}) @@ -895,6 +984,32 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { return root, err } +// PrepareAccessList handles the preparatory steps for executing a state transition with +// regards to both EIP-2929 and EIP-2930: +// +// - Add sender to access list (2929) +// - Add destination to access list (2929) +// - Add precompiles to access list (2929) +// - Add the contents of the optional tx access list (2930) +// +// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number. +func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { + s.AddAddressToAccessList(sender) + if dst != nil { + s.AddAddressToAccessList(*dst) + // If it's a create-tx, the destination will be added inside evm.create + } + for _, addr := range precompiles { + s.AddAddressToAccessList(addr) + } + for _, el := range list { + s.AddAddressToAccessList(el.Address) + for _, key := range el.StorageKeys { + s.AddSlotToAccessList(el.Address, key) + } + } +} + // AddAddressToAccessList adds the given address to the access list func (s *StateDB) AddAddressToAccessList(addr common.Address) { if s.accessList.AddAddress(addr) { diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 70d01ff3dd..220e28525c 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -474,7 +474,7 @@ func TestTouchDelete(t *testing.T) { s := newStateTest() s.state.GetOrNewStateObject(common.Address{}) root, _ := s.state.Commit(false) - s.state.Reset(root) + s.state, _ = New(root, s.state.db, s.state.snaps) snapshot := s.state.Snapshot() s.state.AddBalance(common.Address{}, new(big.Int)) @@ -676,7 +676,7 @@ func TestDeleteCreateRevert(t *testing.T) { state.SetBalance(addr, big.NewInt(1)) root, _ := state.Commit(false) - state.Reset(root) + state, _ = New(root, state.db, state.snaps) // Simulate self-destructing in one transaction, then create-reverting in another state.Suicide(addr) @@ -688,7 +688,7 @@ func TestDeleteCreateRevert(t *testing.T) { // Commit the entire state and make sure we don't crash and have the correct state root, _ = state.Commit(true) - state.Reset(root) + state, _ = New(root, state.db, state.snaps) if state.getStateObject(addr) != nil { t.Fatalf("self-destructed contract came alive") diff --git a/core/state/sync_test.go b/core/state/sync_test.go index deb4b52b4c..9c4867093d 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -62,7 +62,8 @@ func makeTestState() (Database, common.Hash, []*testAccount) { } if i%5 == 0 { for j := byte(0); j < 5; j++ { - obj.SetState(db, crypto.Keccak256Hash([]byte{i, i, i, i, i, j, j}), crypto.Keccak256Hash([]byte{i, i, i, i, i, j, j})) + hash := crypto.Keccak256Hash([]byte{i, i, i, i, i, j, j}) + obj.SetState(db, hash, hash) } } state.updateStateObject(obj) @@ -401,15 +402,14 @@ func TestIncompleteStateSync(t *testing.T) { // Create a random state to copy srcDb, srcRoot, srcAccounts := makeTestState() - // isCode reports whether the hash is contract code hash. - isCode := func(hash common.Hash) bool { - for _, acc := range srcAccounts { - if hash == crypto.Keccak256Hash(acc.code) { - return true - } + // isCodeLookup to save some hashing + var isCode = make(map[common.Hash]struct{}) + for _, acc := range srcAccounts { + if len(acc.code) > 0 { + isCode[crypto.Keccak256Hash(acc.code)] = struct{}{} } - return false } + isCode[common.BytesToHash(emptyCodeHash)] = struct{}{} checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot) // Create a destination state and sync with the scheduler @@ -447,15 +447,13 @@ func TestIncompleteStateSync(t *testing.T) { batch.Write() for _, result := range results { added = append(added, result.Hash) - } - // Check that all known sub-tries added so far are complete or missing entirely. - for _, hash := range added { - if isCode(hash) { + // Check that all known sub-tries added so far are complete or missing entirely. + if _, ok := isCode[result.Hash]; ok { continue } // Can't use checkStateConsistency here because subtrie keys may have odd // length and crash in LeafKey. - if err := checkTrieConsistency(dstDb, hash); err != nil { + if err := checkTrieConsistency(dstDb, result.Hash); err != nil { t.Fatalf("state inconsistent: %v", err) } } @@ -466,9 +464,9 @@ func TestIncompleteStateSync(t *testing.T) { // Sanity check that removing any node from the database is detected for _, node := range added[1:] { var ( - key = node.Bytes() - code = isCode(node) - val []byte + key = node.Bytes() + _, code = isCode[node] + val []byte ) if code { val = rawdb.ReadCode(dstDb, node) diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go new file mode 100644 index 0000000000..ac5e95c5c2 --- /dev/null +++ b/core/state/trie_prefetcher.go @@ -0,0 +1,334 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" +) + +var ( + // triePrefetchMetricsPrefix is the prefix under which to publis the metrics. + triePrefetchMetricsPrefix = "trie/prefetch/" +) + +// triePrefetcher is an active prefetcher, which receives accounts or storage +// items and does trie-loading of them. The goal is to get as much useful content +// into the caches as possible. +// +// Note, the prefetcher's API is not thread safe. +type triePrefetcher struct { + db Database // Database to fetch trie nodes through + root common.Hash // Root hash of theaccount trie for metrics + fetches map[common.Hash]Trie // Partially or fully fetcher tries + fetchers map[common.Hash]*subfetcher // Subfetchers for each trie + + deliveryMissMeter metrics.Meter + accountLoadMeter metrics.Meter + accountDupMeter metrics.Meter + accountSkipMeter metrics.Meter + accountWasteMeter metrics.Meter + storageLoadMeter metrics.Meter + storageDupMeter metrics.Meter + storageSkipMeter metrics.Meter + storageWasteMeter metrics.Meter +} + +// newTriePrefetcher +func newTriePrefetcher(db Database, root common.Hash, namespace string) *triePrefetcher { + prefix := triePrefetchMetricsPrefix + namespace + p := &triePrefetcher{ + db: db, + root: root, + fetchers: make(map[common.Hash]*subfetcher), // Active prefetchers use the fetchers map + + deliveryMissMeter: metrics.GetOrRegisterMeter(prefix+"/deliverymiss", nil), + accountLoadMeter: metrics.GetOrRegisterMeter(prefix+"/account/load", nil), + accountDupMeter: metrics.GetOrRegisterMeter(prefix+"/account/dup", nil), + accountSkipMeter: metrics.GetOrRegisterMeter(prefix+"/account/skip", nil), + accountWasteMeter: metrics.GetOrRegisterMeter(prefix+"/account/waste", nil), + storageLoadMeter: metrics.GetOrRegisterMeter(prefix+"/storage/load", nil), + storageDupMeter: metrics.GetOrRegisterMeter(prefix+"/storage/dup", nil), + storageSkipMeter: metrics.GetOrRegisterMeter(prefix+"/storage/skip", nil), + storageWasteMeter: metrics.GetOrRegisterMeter(prefix+"/storage/waste", nil), + } + return p +} + +// close iterates over all the subfetchers, aborts any that were left spinning +// and reports the stats to the metrics subsystem. +func (p *triePrefetcher) close() { + for _, fetcher := range p.fetchers { + fetcher.abort() // safe to do multiple times + + if metrics.Enabled { + if fetcher.root == p.root { + p.accountLoadMeter.Mark(int64(len(fetcher.seen))) + p.accountDupMeter.Mark(int64(fetcher.dups)) + p.accountSkipMeter.Mark(int64(len(fetcher.tasks))) + + for _, key := range fetcher.used { + delete(fetcher.seen, string(key)) + } + p.accountWasteMeter.Mark(int64(len(fetcher.seen))) + } else { + p.storageLoadMeter.Mark(int64(len(fetcher.seen))) + p.storageDupMeter.Mark(int64(fetcher.dups)) + p.storageSkipMeter.Mark(int64(len(fetcher.tasks))) + + for _, key := range fetcher.used { + delete(fetcher.seen, string(key)) + } + p.storageWasteMeter.Mark(int64(len(fetcher.seen))) + } + } + } + // Clear out all fetchers (will crash on a second call, deliberate) + p.fetchers = nil +} + +// copy creates a deep-but-inactive copy of the trie prefetcher. Any trie data +// already loaded will be copied over, but no goroutines will be started. This +// is mostly used in the miner which creates a copy of it's actively mutated +// state to be sealed while it may further mutate the state. +func (p *triePrefetcher) copy() *triePrefetcher { + copy := &triePrefetcher{ + db: p.db, + root: p.root, + fetches: make(map[common.Hash]Trie), // Active prefetchers use the fetches map + + deliveryMissMeter: p.deliveryMissMeter, + accountLoadMeter: p.accountLoadMeter, + accountDupMeter: p.accountDupMeter, + accountSkipMeter: p.accountSkipMeter, + accountWasteMeter: p.accountWasteMeter, + storageLoadMeter: p.storageLoadMeter, + storageDupMeter: p.storageDupMeter, + storageSkipMeter: p.storageSkipMeter, + storageWasteMeter: p.storageWasteMeter, + } + // If the prefetcher is already a copy, duplicate the data + if p.fetches != nil { + for root, fetch := range p.fetches { + copy.fetches[root] = p.db.CopyTrie(fetch) + } + return copy + } + // Otherwise we're copying an active fetcher, retrieve the current states + for root, fetcher := range p.fetchers { + copy.fetches[root] = fetcher.peek() + } + return copy +} + +// prefetch schedules a batch of trie items to prefetch. +func (p *triePrefetcher) prefetch(root common.Hash, keys [][]byte) { + // If the prefetcher is an inactive one, bail out + if p.fetches != nil { + return + } + // Active fetcher, schedule the retrievals + fetcher := p.fetchers[root] + if fetcher == nil { + fetcher = newSubfetcher(p.db, root) + p.fetchers[root] = fetcher + } + fetcher.schedule(keys) +} + +// trie returns the trie matching the root hash, or nil if the prefetcher doesn't +// have it. +func (p *triePrefetcher) trie(root common.Hash) Trie { + // If the prefetcher is inactive, return from existing deep copies + if p.fetches != nil { + trie := p.fetches[root] + if trie == nil { + p.deliveryMissMeter.Mark(1) + return nil + } + return p.db.CopyTrie(trie) + } + // Otherwise the prefetcher is active, bail if no trie was prefetched for this root + fetcher := p.fetchers[root] + if fetcher == nil { + p.deliveryMissMeter.Mark(1) + return nil + } + // Interrupt the prefetcher if it's by any chance still running and return + // a copy of any pre-loaded trie. + fetcher.abort() // safe to do multiple times + + trie := fetcher.peek() + if trie == nil { + p.deliveryMissMeter.Mark(1) + return nil + } + return trie +} + +// used marks a batch of state items used to allow creating statistics as to +// how useful or wasteful the prefetcher is. +func (p *triePrefetcher) used(root common.Hash, used [][]byte) { + if fetcher := p.fetchers[root]; fetcher != nil { + fetcher.used = used + } +} + +// subfetcher is a trie fetcher goroutine responsible for pulling entries for a +// single trie. It is spawned when a new root is encountered and lives until the +// main prefetcher is paused and either all requested items are processed or if +// the trie being worked on is retrieved from the prefetcher. +type subfetcher struct { + db Database // Database to load trie nodes through + root common.Hash // Root hash of the trie to prefetch + trie Trie // Trie being populated with nodes + + tasks [][]byte // Items queued up for retrieval + lock sync.Mutex // Lock protecting the task queue + + wake chan struct{} // Wake channel if a new task is scheduled + stop chan struct{} // Channel to interrupt processing + term chan struct{} // Channel to signal iterruption + copy chan chan Trie // Channel to request a copy of the current trie + + seen map[string]struct{} // Tracks the entries already loaded + dups int // Number of duplicate preload tasks + used [][]byte // Tracks the entries used in the end +} + +// newSubfetcher creates a goroutine to prefetch state items belonging to a +// particular root hash. +func newSubfetcher(db Database, root common.Hash) *subfetcher { + sf := &subfetcher{ + db: db, + root: root, + wake: make(chan struct{}, 1), + stop: make(chan struct{}), + term: make(chan struct{}), + copy: make(chan chan Trie), + seen: make(map[string]struct{}), + } + go sf.loop() + return sf +} + +// schedule adds a batch of trie keys to the queue to prefetch. +func (sf *subfetcher) schedule(keys [][]byte) { + // Append the tasks to the current queue + sf.lock.Lock() + sf.tasks = append(sf.tasks, keys...) + sf.lock.Unlock() + + // Notify the prefetcher, it's fine if it's already terminated + select { + case sf.wake <- struct{}{}: + default: + } +} + +// peek tries to retrieve a deep copy of the fetcher's trie in whatever form it +// is currently. +func (sf *subfetcher) peek() Trie { + ch := make(chan Trie) + select { + case sf.copy <- ch: + // Subfetcher still alive, return copy from it + return <-ch + + case <-sf.term: + // Subfetcher already terminated, return a copy directly + if sf.trie == nil { + return nil + } + return sf.db.CopyTrie(sf.trie) + } +} + +// abort interrupts the subfetcher immediately. It is safe to call abort multiple +// times but it is not thread safe. +func (sf *subfetcher) abort() { + select { + case <-sf.stop: + default: + close(sf.stop) + } + <-sf.term +} + +// loop waits for new tasks to be scheduled and keeps loading them until it runs +// out of tasks or its underlying trie is retrieved for committing. +func (sf *subfetcher) loop() { + // No matter how the loop stops, signal anyone waiting that it's terminated + defer close(sf.term) + + // Start by opening the trie and stop processing if it fails + trie, err := sf.db.OpenTrie(sf.root) + if err != nil { + log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err) + return + } + sf.trie = trie + + // Trie opened successfully, keep prefetching items + for { + select { + case <-sf.wake: + // Subfetcher was woken up, retrieve any tasks to avoid spinning the lock + sf.lock.Lock() + tasks := sf.tasks + sf.tasks = nil + sf.lock.Unlock() + + // Prefetch any tasks until the loop is interrupted + for i, task := range tasks { + select { + case <-sf.stop: + // If termination is requested, add any leftover back and return + sf.lock.Lock() + sf.tasks = append(sf.tasks, tasks[i:]...) + sf.lock.Unlock() + return + + case ch := <-sf.copy: + // Somebody wants a copy of the current trie, grant them + ch <- sf.db.CopyTrie(sf.trie) + + default: + // No termination request yet, prefetch the next entry + taskid := string(task) + if _, ok := sf.seen[taskid]; ok { + sf.dups++ + } else { + sf.trie.TryGet(task) + sf.seen[taskid] = struct{}{} + } + } + } + + case ch := <-sf.copy: + // Somebody wants a copy of the current trie, grant them + ch <- sf.db.CopyTrie(sf.trie) + + case <-sf.stop: + // Termination is requested, abort and leave remaining tasks + return + } + } +} diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 53fc79cdf1..5a8abc4850 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -19,7 +19,6 @@ package core import ( "sync/atomic" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -50,8 +49,11 @@ func newStatePrefetcher(config ctypes.ChainConfigurator, bc *BlockChain, engine // only goal is to pre-cache transaction signatures and state trie nodes. func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *uint32) { var ( - header = block.Header() - gaspool = new(GasPool).AddGas(block.GasLimit()) + header = block.Header() + gaspool = new(GasPool).AddGas(block.GasLimit()) + blockContext = NewEVMBlockContext(header, p.bc, nil) + evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) + signer = types.MakeSigner(p.config, header.Number) ) // Iterate over and process the individual transactions byzantium := p.config.IsEnabled(p.config.GetEIP161abcTransition, block.Number()) @@ -60,9 +62,13 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c if interrupt != nil && atomic.LoadUint32(interrupt) == 1 { return } - // Block precaching permitted to continue, execute the transaction + // Convert the transaction into an executable message and pre-cache its sender + msg, err := tx.AsMessage(signer) + if err != nil { + return // Also invalid block, bail out + } statedb.Prepare(tx.Hash(), block.Hash(), i) - if err := precacheTransaction(p.config, p.bc, nil, gaspool, statedb, header, tx, cfg); err != nil { + if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm); err != nil { return // Ugh, something went horribly wrong, bail out } // If we're pre-byzantium, pre-load trie nodes for the intermediate root @@ -79,16 +85,10 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c // precacheTransaction attempts to apply a transaction to the given state database // and uses the input parameters for its environment. The goal is not to execute // the transaction successfully, rather to warm up touched data slots. -func precacheTransaction(config ctypes.ChainConfigurator, bc ChainContext, author *common.Address, gaspool *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, cfg vm.Config) error { - // Convert the transaction into an executable message and pre-cache its sender - msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) - if err != nil { - return err - } - // Create the EVM and execute the transaction - context := NewEVMContext(msg, header, bc, author) - vm := vm.NewEVM(context, statedb, config, cfg) - - _, err = ApplyMessage(vm, msg, gaspool) +func precacheTransaction(msg types.Message, config ctypes.ChainConfigurator, gaspool *GasPool, statedb *state.StateDB, header *types.Header, evm *vm.EVM) error { + // Update the evm with the new transaction context. + evm.Reset(NewEVMTxContext(msg), statedb) + // Add addresses to access list if applicable + _, err := ApplyMessage(evm, msg, gaspool) return err } diff --git a/core/state_processor.go b/core/state_processor.go index 8e386d7c17..1c7a72621b 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -17,6 +17,8 @@ package core import ( + "fmt" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" @@ -68,13 +70,19 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg misc.ApplyDAOHardFork(statedb) } } + blockContext := NewEVMBlockContext(header, p.bc, nil) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) // Iterate over and process the individual transactions for i, tx := range block.Transactions() { - statedb.Prepare(tx.Hash(), block.Hash(), i) - receipt, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg) + msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number)) if err != nil { return nil, nil, 0, err } + statedb.Prepare(tx.Hash(), block.Hash(), i) + receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, header, tx, usedGas, vmenv) + if err != nil { + return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + } receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) } @@ -84,38 +92,18 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg return receipts, allLogs, *usedGas, 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 ctypes.ChainConfigurator, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) { - msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) - if err != nil { - return nil, err - } - // Create a new context to be used in the EVM environment - 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) - - if config.IsEnabled(config.GetEIP2929Transition, header.Number) { - statedb.AddAddressToAccessList(msg.From()) - if dst := msg.To(); dst != nil { - statedb.AddAddressToAccessList(*dst) - // If it's a create-tx, the destination will be added inside evm.create - } - for addr := range vm.PrecompiledContractsForConfig(config, header.Number) { - statedb.AddAddressToAccessList(addr) - } - } +func applyTransaction(msg types.Message, config ctypes.ChainConfigurator, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { + // Create a new context to be used in the EVM environment. + txContext := NewEVMTxContext(msg) + evm.Reset(txContext, statedb) - // Apply the transaction to the current state (included in the env) - result, err := ApplyMessage(vmenv, msg, gp) + // Apply the transaction to the current state (included in the env). + result, err := ApplyMessage(evm, msg, gp) if err != nil { return nil, err } - // Update the state with pending changes + + // Update the state with pending changes. var root []byte eip161d := config.IsEnabled(config.GetEIP161dTransition, header.Number) if config.IsEnabled(config.GetEIP658Transition, header.Number) { @@ -125,21 +113,42 @@ func ApplyTransaction(config ctypes.ChainConfigurator, bc ChainContext, author * } *usedGas += result.UsedGas - // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx - // based on the eip phase, we're passing whether the root touch-delete accounts. - receipt := types.NewReceipt(root, result.Failed(), *usedGas) + // Create a new receipt for the transaction, storing the intermediate root and gas used + // by the tx. + receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas} + if result.Failed() { + receipt.Status = types.ReceiptStatusFailed + } else { + receipt.Status = types.ReceiptStatusSuccessful + } receipt.TxHash = tx.Hash() receipt.GasUsed = result.UsedGas - // if the transaction created a contract, store the creation address in the receipt. + + // If the transaction created a contract, store the creation address in the receipt. if msg.To() == nil { - receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce()) + receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) } - // Set the receipt logs and create a bloom for filtering + + // Set the receipt logs and create the bloom filter. receipt.Logs = statedb.GetLogs(tx.Hash()) receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) receipt.BlockHash = statedb.BlockHash() receipt.BlockNumber = header.Number receipt.TransactionIndex = uint(statedb.TxIndex()) - return receipt, err } + +// 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 ctypes.ChainConfigurator, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) { + msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) + if err != nil { + return nil, err + } + // Create a new context to be used in the EVM environment + blockContext := NewEVMBlockContext(header, bc, author) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) + return applyTransaction(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv) +} diff --git a/core/state_processor_test.go b/core/state_processor_test.go new file mode 100644 index 0000000000..3714cd6f3c --- /dev/null +++ b/core/state_processor_test.go @@ -0,0 +1,154 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "math/big" + "testing" + + "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/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/params/types/genesisT" + "github.com/ethereum/go-ethereum/params/vars" + "github.com/ethereum/go-ethereum/trie" + "golang.org/x/crypto/sha3" +) + +// TestStateProcessorErrors tests the output from the 'core' errors +// as defined in core/error.go. These errors are generated when the +// blockchain imports bad blocks, meaning blocks which have valid headers but +// contain invalid transactions +func TestStateProcessorErrors(t *testing.T) { + var ( + signer = types.HomesteadSigner{} + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + db = rawdb.NewMemoryDatabase() + gspec = &genesisT.Genesis{ + Config: params.TestChainConfig, + } + genesis = MustCommitGenesis(db, gspec) + blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + ) + defer blockchain.Stop() + var makeTx = func(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction { + tx, _ := types.SignTx(types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data), signer, testKey) + return tx + } + for i, tt := range []struct { + txs []*types.Transaction + want string + }{ + { + txs: []*types.Transaction{ + makeTx(0, common.Address{}, big.NewInt(0), vars.TxGas, nil, nil), + makeTx(0, common.Address{}, big.NewInt(0), vars.TxGas, nil, nil), + }, + want: "could not apply tx 1 [0x36bfa6d14f1cd35a1be8cc2322982a595fabc0e799f09c1de3bad7bd5b1f7626]: nonce too low: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1", + }, + { + txs: []*types.Transaction{ + makeTx(100, common.Address{}, big.NewInt(0), vars.TxGas, nil, nil), + }, + want: "could not apply tx 0 [0x51cd272d41ef6011d8138e18bf4043797aca9b713c7d39a97563f9bbe6bdbe6f]: nonce too high: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0", + }, + { + txs: []*types.Transaction{ + makeTx(0, common.Address{}, big.NewInt(0), 21000000, nil, nil), + }, + want: "could not apply tx 0 [0x54c58b530824b0bb84b7a98183f08913b5d74e1cebc368515ef3c65edf8eb56a]: gas limit reached", + }, + { + txs: []*types.Transaction{ + makeTx(0, common.Address{}, big.NewInt(1), vars.TxGas, nil, nil), + }, + want: "could not apply tx 0 [0x3094b17498940d92b13baccf356ce8bfd6f221e926abc903d642fa1466c5b50e]: insufficient funds for transfer: address 0x71562b71999873DB5b286dF957af199Ec94617F7", + }, + { + txs: []*types.Transaction{ + makeTx(0, common.Address{}, big.NewInt(0), vars.TxGas, big.NewInt(0xffffff), nil), + }, + want: "could not apply tx 0 [0xaa3f7d86802b1f364576d9071bf231e31d61b392d306831ac9cf706ff5371ce0]: insufficient funds for gas * price + value", + }, + { + txs: []*types.Transaction{ + makeTx(0, common.Address{}, big.NewInt(0), vars.TxGas, nil, nil), + makeTx(1, common.Address{}, big.NewInt(0), vars.TxGas, nil, nil), + makeTx(2, common.Address{}, big.NewInt(0), vars.TxGas, nil, nil), + makeTx(3, common.Address{}, big.NewInt(0), vars.TxGas-1000, big.NewInt(0), nil), + }, + want: "could not apply tx 3 [0x836fab5882205362680e49b311a20646de03b630920f18ec6ee3b111a2cf6835]: intrinsic gas too low: have 20000, want 21000", + }, + // The last 'core' error is ErrGasUintOverflow: "gas uint64 overflow", but in order to + // trigger that one, we'd have to allocate a _huge_ chunk of data, such that the + // multiplication len(data) +gas_per_byte overflows uint64. Not testable at the moment + } { + block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs) + _, err := blockchain.InsertChain(types.Blocks{block}) + if err == nil { + t.Fatal("block imported without errors") + } + if have, want := err.Error(), tt.want; have != want { + t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want) + } + } +} + +// GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be +// valid, and no proper post-state can be made. But from the perspective of the blockchain, the block is sufficiently +// valid to be considered for import: +// - valid pow (fake), ancestry, difficulty, gaslimit etc +func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions) *types.Block { + header := &types.Header{ + ParentHash: parent.Hash(), + Coinbase: parent.Coinbase(), + Difficulty: engine.CalcDifficulty(&fakeChainReader{params.TestChainConfig}, parent.Time()+10, &types.Header{ + Number: parent.Number(), + Time: parent.Time(), + Difficulty: parent.Difficulty(), + UncleHash: parent.UncleHash(), + }), + GasLimit: CalcGasLimit(parent, parent.GasLimit(), parent.GasLimit()), + Number: new(big.Int).Add(parent.Number(), common.Big1), + Time: parent.Time() + 10, + UncleHash: types.EmptyUncleHash, + } + var receipts []*types.Receipt + + // The post-state result doesn't need to be correct (this is a bad block), but we do need something there + // Preferably something unique. So let's use a combo of blocknum + txhash + hasher := sha3.NewLegacyKeccak256() + hasher.Write(header.Number.Bytes()) + var cumulativeGas uint64 + for _, tx := range txs { + txh := tx.Hash() + hasher.Write(txh[:]) + receipt := types.NewReceipt(nil, false, cumulativeGas+tx.Gas()) + receipt.TxHash = tx.Hash() + receipt.GasUsed = tx.Gas() + receipts = append(receipts, receipt) + cumulativeGas += tx.Gas() + } + header.Root = common.BytesToHash(hasher.Sum(nil)) + // Assemble and return the final block for sealing + return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)) +} diff --git a/core/state_transition.go b/core/state_transition.go index e3e2830b3b..841a7f2de4 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -17,10 +17,12 @@ package core import ( + "fmt" "math" "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params/vars" ) @@ -66,6 +68,7 @@ type Message interface { Nonce() uint64 CheckNonce() bool Data() []byte + AccessList() types.AccessList } // ExecutionResult includes all output after executing given evm @@ -104,10 +107,10 @@ func (result *ExecutionResult) Revert() []byte { } // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, contractCreation, isEIP2 bool, isEIP2028 bool) (uint64, error) { +func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isEIP2, isEIP2028 bool) (uint64, error) { // Set the starting gas for the raw transaction var gas uint64 - if contractCreation && isEIP2 { + if isContractCreation && isEIP2 { gas = vars.TxGasContractCreation } else { gas = vars.TxGas @@ -137,6 +140,10 @@ func IntrinsicGas(data []byte, contractCreation, isEIP2 bool, isEIP2028 bool) (u } gas += z * vars.TxDataZeroGas } + if accessList != nil { + gas += uint64(len(accessList)) * vars.TxAccessListAddressGas + gas += uint64(accessList.StorageKeys()) * vars.TxAccessListStorageKeyGas + } return gas, nil } @@ -174,7 +181,7 @@ func (st *StateTransition) to() common.Address { func (st *StateTransition) buyGas() error { mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice) - if !st.evm.CanTransfer(st.state, st.msg.From(), mgval) { + if !st.evm.Context.CanTransfer(st.state, st.msg.From(), mgval) { return ErrInsufficientFunds } if err := st.gp.SubGas(st.msg.Gas()); err != nil { @@ -190,11 +197,13 @@ func (st *StateTransition) buyGas() error { func (st *StateTransition) preCheck() error { // Make sure this transaction's nonce is correct. if st.msg.CheckNonce() { - nonce := st.state.GetNonce(st.msg.From()) - if nonce < st.msg.Nonce() { - return ErrNonceTooHigh - } else if nonce > st.msg.Nonce() { - return ErrNonceTooLow + stNonce := st.state.GetNonce(st.msg.From()) + if msgNonce := st.msg.Nonce(); stNonce < msgNonce { + return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh, + st.msg.From().Hex(), msgNonce, stNonce) + } else if stNonce > msgNonce { + return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow, + st.msg.From().Hex(), msgNonce, stNonce) } } return st.buyGas() @@ -230,23 +239,28 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } msg := st.msg sender := vm.AccountRef(msg.From()) - eip2f := st.evm.ChainConfig().IsEnabled(st.evm.ChainConfig().GetEIP2Transition, st.evm.BlockNumber) - eip2028f := st.evm.ChainConfig().IsEnabled(st.evm.ChainConfig().GetEIP2028Transition, st.evm.BlockNumber) + eip2f := st.evm.ChainConfig().IsEnabled(st.evm.ChainConfig().GetEIP2Transition, st.evm.Context.BlockNumber) + eip2028f := st.evm.ChainConfig().IsEnabled(st.evm.ChainConfig().GetEIP2028Transition, st.evm.Context.BlockNumber) contractCreation := msg.To() == nil // Pay intrinsic gas - gas, err := IntrinsicGas(st.data, contractCreation, eip2f, eip2028f) + gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, eip2f, eip2028f) if err != nil { return nil, err } if st.gas < gas { - return nil, ErrIntrinsicGas + return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas) } st.gas -= gas // Check clause 6 - if msg.Value().Sign() > 0 && !st.evm.CanTransfer(st.state, msg.From(), msg.Value()) { - return nil, ErrInsufficientFundsForTransfer + if msg.Value().Sign() > 0 && !st.evm.Context.CanTransfer(st.state, msg.From(), msg.Value()) { + return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex()) + } + + // Set up the initial access list. + if st.evm.ChainConfig().IsEnabled(st.evm.ChainConfig().GetEIP2929Transition, st.evm.Context.BlockNumber) { + st.state.PrepareAccessList(msg.From(), msg.To(), st.evm.ActivePrecompiles(), msg.AccessList()) } var ( ret []byte @@ -260,7 +274,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value) } st.refundGas() - st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) + st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) return &ExecutionResult{ UsedGas: st.gasUsed(), diff --git a/core/tx_list.go b/core/tx_list.go index cdd3df14c5..894640d570 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -24,7 +24,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/log" ) // nonceHeap is a heap.Interface implementation over 64bit unsigned integers for @@ -439,24 +438,29 @@ func (h *priceHeap) Pop() interface{} { } // txPricedList is a price-sorted heap to allow operating on transactions pool -// contents in a price-incrementing way. +// contents in a price-incrementing way. It's built opon the all transactions +// in txpool but only interested in the remote part. It means only remote transactions +// will be considered for tracking, sorting, eviction, etc. type txPricedList struct { - all *txLookup // Pointer to the map of all transactions - items *priceHeap // Heap of prices of all the stored transactions - stales int // Number of stale price points to (re-heap trigger) + all *txLookup // Pointer to the map of all transactions + remotes *priceHeap // Heap of prices of all the stored **remote** transactions + stales int // Number of stale price points to (re-heap trigger) } // newTxPricedList creates a new price-sorted transaction heap. func newTxPricedList(all *txLookup) *txPricedList { return &txPricedList{ - all: all, - items: new(priceHeap), + all: all, + remotes: new(priceHeap), } } // Put inserts a new transaction into the heap. -func (l *txPricedList) Put(tx *types.Transaction) { - heap.Push(l.items, tx) +func (l *txPricedList) Put(tx *types.Transaction, local bool) { + if local { + return + } + heap.Push(l.remotes, tx) } // Removed notifies the prices transaction list that an old transaction dropped @@ -465,121 +469,95 @@ func (l *txPricedList) Put(tx *types.Transaction) { func (l *txPricedList) Removed(count int) { // Bump the stale counter, but exit if still too low (< 25%) l.stales += count - if l.stales <= len(*l.items)/4 { + if l.stales <= len(*l.remotes)/4 { return } // Seems we've reached a critical number of stale transactions, reheap - reheap := make(priceHeap, 0, l.all.Count()) - - l.stales, l.items = 0, &reheap - l.all.Range(func(hash common.Hash, tx *types.Transaction) bool { - *l.items = append(*l.items, tx) - return true - }) - heap.Init(l.items) + l.Reheap() } // Cap finds all the transactions below the given price threshold, drops them // from the priced list and returns them for further removal from the entire pool. -func (l *txPricedList) Cap(threshold *big.Int, local *accountSet) types.Transactions { +// +// Note: only remote transactions will be considered for eviction. +func (l *txPricedList) Cap(threshold *big.Int) types.Transactions { drop := make(types.Transactions, 0, 128) // Remote underpriced transactions to drop - save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep - - for len(*l.items) > 0 { + for len(*l.remotes) > 0 { // Discard stale transactions if found during cleanup - tx := heap.Pop(l.items).(*types.Transaction) - if l.all.Get(tx.Hash()) == nil { + cheapest := (*l.remotes)[0] + if l.all.GetRemote(cheapest.Hash()) == nil { // Removed or migrated + heap.Pop(l.remotes) l.stales-- continue } // Stop the discards if we've reached the threshold - if tx.GasPriceIntCmp(threshold) >= 0 { - save = append(save, tx) + if cheapest.GasPriceIntCmp(threshold) >= 0 { break } - // Non stale transaction found, discard unless local - if local.containsTx(tx) { - save = append(save, tx) - } else { - drop = append(drop, tx) - } - } - for _, tx := range save { - heap.Push(l.items, tx) + heap.Pop(l.remotes) + drop = append(drop, cheapest) } return drop } // Underpriced checks whether a transaction is cheaper than (or as cheap as) the -// lowest priced transaction currently being tracked. -func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) bool { - // Local transactions cannot be underpriced - if local.containsTx(tx) { - return false - } +// lowest priced (remote) transaction currently being tracked. +func (l *txPricedList) Underpriced(tx *types.Transaction) bool { // Discard stale price points if found at the heap start - for len(*l.items) > 0 { - head := []*types.Transaction(*l.items)[0] - if l.all.Get(head.Hash()) == nil { + for len(*l.remotes) > 0 { + head := []*types.Transaction(*l.remotes)[0] + if l.all.GetRemote(head.Hash()) == nil { // Removed or migrated l.stales-- - heap.Pop(l.items) + heap.Pop(l.remotes) continue } break } // Check if the transaction is underpriced or not - if len(*l.items) == 0 { - log.Error("Pricing query for empty pool") // This cannot happen, print to catch programming errors - return false + if len(*l.remotes) == 0 { + return false // There is no remote transaction at all. } - cheapest := []*types.Transaction(*l.items)[0] + // If the remote transaction is even cheaper than the + // cheapest one tracked locally, reject it. + cheapest := []*types.Transaction(*l.remotes)[0] return cheapest.GasPriceCmp(tx) >= 0 } // Discard finds a number of most underpriced transactions, removes them from the // priced list and returns them for further removal from the entire pool. -func (l *txPricedList) Discard(slots int, local *accountSet) types.Transactions { - // If we have some local accountset, those will not be discarded - if !local.empty() { - // In case the list is filled to the brim with 'local' txs, we do this - // little check to avoid unpacking / repacking the heap later on, which - // is very expensive - discardable := 0 - for _, tx := range *l.items { - if !local.containsTx(tx) { - discardable++ - } - if discardable >= slots { - break - } - } - if slots > discardable { - slots = discardable - } - } - if slots == 0 { - return nil - } - drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop - save := make(types.Transactions, 0, len(*l.items)-slots) // Local underpriced transactions to keep - - for len(*l.items) > 0 && slots > 0 { +// +// Note local transaction won't be considered for eviction. +func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool) { + drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop + for len(*l.remotes) > 0 && slots > 0 { // Discard stale transactions if found during cleanup - tx := heap.Pop(l.items).(*types.Transaction) - if l.all.Get(tx.Hash()) == nil { + tx := heap.Pop(l.remotes).(*types.Transaction) + if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated l.stales-- continue } - // Non stale transaction found, discard unless local - if local.containsTx(tx) { - save = append(save, tx) - } else { - drop = append(drop, tx) - slots -= numSlots(tx) + // Non stale transaction found, discard it + drop = append(drop, tx) + slots -= numSlots(tx) + } + // If we still can't make enough room for the new transaction + if slots > 0 && !force { + for _, tx := range drop { + heap.Push(l.remotes, tx) } + return nil, false } - for _, tx := range save { - heap.Push(l.items, tx) - } - return drop + return drop, true +} + +// Reheap forcibly rebuilds the heap based on the current remote transaction set. +func (l *txPricedList) Reheap() { + reheap := make(priceHeap, 0, l.all.RemoteCount()) + + l.stales, l.remotes = 0, &reheap + l.all.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool { + *l.remotes = append(*l.remotes, tx) + return true + }, false, true) // Only iterate remotes + heap.Init(l.remotes) } diff --git a/core/tx_pool.go b/core/tx_pool.go index 1203024130..640a89ee4e 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -63,6 +63,10 @@ var ( // configured for the transaction pool. ErrUnderpriced = errors.New("transaction underpriced") + // ErrTxPoolOverflow is returned if the transaction pool is full and can't accpet + // another remote transaction. + ErrTxPoolOverflow = errors.New("txpool is full") + // ErrReplaceUnderpriced is returned if a transaction is attempted to be replaced // with a different one without the required price bump. ErrReplaceUnderpriced = errors.New("replacement transaction underpriced") @@ -105,6 +109,7 @@ var ( validTxMeter = metrics.NewRegisteredMeter("txpool/valid", nil) invalidTxMeter = metrics.NewRegisteredMeter("txpool/invalid", nil) underpricedTxMeter = metrics.NewRegisteredMeter("txpool/underpriced", nil) + overflowedTxMeter = metrics.NewRegisteredMeter("txpool/overflowed", nil) pendingGauge = metrics.NewRegisteredGauge("txpool/pending", nil) queuedGauge = metrics.NewRegisteredGauge("txpool/queued", nil) @@ -247,6 +252,7 @@ type TxPool struct { eip2f bool eip2028f bool + eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions. } type txpoolResetRequest struct { @@ -264,7 +270,7 @@ func NewTxPool(config TxPoolConfig, chainconfig ctypes.ChainConfigurator, chain config: config, chainconfig: chainconfig, chain: chain, - signer: types.NewEIP155Signer(chainconfig.GetChainID()), + signer: types.LatestSigner(chainconfig), pending: make(map[common.Address]*txList), queue: make(map[common.Address]*txList), beats: make(map[common.Address]time.Time), @@ -428,7 +434,7 @@ func (pool *TxPool) SetGasPrice(price *big.Int) { defer pool.mu.Unlock() pool.gasPrice = price - for _, tx := range pool.priced.Cap(price, pool.locals) { + for _, tx := range pool.priced.Cap(price) { pool.removeTx(tx.Hash(), false) } log.Info("Transaction pool price threshold updated", "price", price) @@ -524,6 +530,10 @@ func (pool *TxPool) local() map[common.Address]types.Transactions { // validateTx checks whether a transaction is valid according to the consensus // rules and adheres to some heuristic limits of the local node (price and size). func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { + // Accept only legacy transactions until EIP-2718/2930 activates. + if !pool.eip2718 && tx.Type() != types.LegacyTxType { + return ErrTxTypeNotSupported + } // Reject transactions over defined size to prevent DOS attacks if uint64(tx.Size()) > txMaxSize { return ErrOversizedData @@ -537,13 +547,12 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if pool.currentMaxGas < tx.Gas() { return ErrGasLimit } - // Make sure the transaction is signed properly + // Make sure the transaction is signed properly. from, err := types.Sender(pool.signer, tx) if err != nil { return ErrInvalidSender } // Drop non-local transactions under our own minimal accepted gas price - local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network if !local && tx.GasPriceIntCmp(pool.gasPrice) < 0 { return ErrUnderpriced } @@ -557,7 +566,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrInsufficientFunds } // Ensure the transaction has more gas than the basic tx fee. - intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, pool.eip2f, pool.eip2028f) + intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, pool.eip2f, pool.eip2028f) if err != nil { return err } @@ -582,22 +591,36 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e knownTxMeter.Mark(1) return false, ErrAlreadyKnown } + // Make the local flag. If it's from local source or it's from the network but + // the sender is marked as local previously, treat it as the local transaction. + isLocal := local || pool.locals.containsTx(tx) + // If the transaction fails basic validation, discard it - if err := pool.validateTx(tx, local); err != nil { + if err := pool.validateTx(tx, isLocal); err != nil { log.Trace("Discarding invalid transaction", "hash", hash, "err", err) invalidTxMeter.Mark(1) return false, err } // If the transaction pool is full, discard underpriced transactions - if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue { + if uint64(pool.all.Count()+numSlots(tx)) > pool.config.GlobalSlots+pool.config.GlobalQueue { // If the new transaction is underpriced, don't accept it - if !local && pool.priced.Underpriced(tx, pool.locals) { + if !isLocal && pool.priced.Underpriced(tx) { log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice()) underpricedTxMeter.Mark(1) return false, ErrUnderpriced } - // New transaction is better than our worse ones, make room for it - drop := pool.priced.Discard(pool.all.Slots()-int(pool.config.GlobalSlots+pool.config.GlobalQueue)+numSlots(tx), pool.locals) + // New transaction is better than our worse ones, make room for it. + // If it's a local transaction, forcibly discard all available transactions. + // Otherwise if we can't make enough room for new one, abort the operation. + drop, success := pool.priced.Discard(pool.all.Slots()-int(pool.config.GlobalSlots+pool.config.GlobalQueue)+numSlots(tx), isLocal) + + // Special case, we still can't make the room for the new remote one. + if !isLocal && !success { + log.Trace("Discarding overflown transaction", "hash", hash) + overflowedTxMeter.Mark(1) + return false, ErrTxPoolOverflow + } + // Kick out the underpriced remote transactions. for _, tx := range drop { log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice()) underpricedTxMeter.Mark(1) @@ -619,8 +642,8 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e pool.priced.Removed(1) pendingReplaceMeter.Mark(1) } - pool.all.Add(tx) - pool.priced.Put(tx) + pool.all.Add(tx, isLocal) + pool.priced.Put(tx, isLocal) pool.journalTx(from, tx) pool.queueTxEvent(tx) log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To()) @@ -630,18 +653,17 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e return old != nil, nil } // New transaction isn't replacing a pending one, push into queue - replaced, err = pool.enqueueTx(hash, tx) + replaced, err = pool.enqueueTx(hash, tx, isLocal, true) if err != nil { return false, err } // Mark local addresses and journal local transactions - if local { - if !pool.locals.contains(from) { - log.Info("Setting new local account", "address", from) - pool.locals.add(from) - } + if local && !pool.locals.contains(from) { + log.Info("Setting new local account", "address", from) + pool.locals.add(from) + pool.priced.Removed(pool.all.RemoteToLocals(pool.locals)) // Migrate the remotes if it's marked as local first time. } - if local || pool.locals.contains(from) { + if isLocal { localGauge.Inc(1) } pool.journalTx(from, tx) @@ -653,7 +675,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e // enqueueTx inserts a new transaction into the non-executable transaction queue. // // Note, this method assumes the pool lock is held! -func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, error) { +func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction, local bool, addAll bool) (bool, error) { // Try to insert the transaction into the future queue from, _ := types.Sender(pool.signer, tx) // already validated if pool.queue[from] == nil { @@ -674,9 +696,14 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, er // Nothing was replaced, bump the queued counter queuedGauge.Inc(1) } - if pool.all.Get(hash) == nil { - pool.all.Add(tx) - pool.priced.Put(tx) + // If the transaction isn't in lookup set but it's expected to be there, + // show the error log. + if pool.all.Get(hash) == nil && !addAll { + log.Error("Missing transaction in lookup set, please report the issue", "hash", hash) + } + if addAll { + pool.all.Add(tx, local) + pool.priced.Put(tx, local) } // If we never record the heartbeat, do it right now. if _, exist := pool.beats[from]; !exist { @@ -725,11 +752,6 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T // Nothing was replaced, bump the pending counter pendingGauge.Inc(1) } - // Failsafe to work around direct pending inserts (tests) - if pool.all.Get(hash) == nil { - pool.all.Add(tx) - pool.priced.Put(tx) - } // Set the potentially new pending nonce and notify any subsystems of the new tx pool.pendingNonces.set(addr, tx.Nonce()+1) @@ -911,7 +933,8 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) { } // Postpone any invalidated transactions for _, tx := range invalids { - pool.enqueueTx(tx.Hash(), tx) + // Internal shuffle shouldn't touch the lookup set. + pool.enqueueTx(tx.Hash(), tx, false, false) } // Update the account nonce if needed pool.pendingNonces.setIfLower(addr, tx.Nonce()) @@ -942,7 +965,7 @@ func (pool *TxPool) RemoveTx(hash common.Hash) *types.Transaction { return tx } -// requestPromoteExecutables requests a pool reset to the new head block. +// requestReset requests a pool reset to the new head block. // The returned channel is closed when the reset has occurred. func (pool *TxPool) requestReset(oldHead *types.Header, newHead *types.Header) chan struct{} { select { @@ -1135,44 +1158,45 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { // head from the chain. // If that is the case, we don't have the lost transactions any more, and // there's nothing to add - if newNum < oldNum { - // If the reorg ended up on a lower number, it's indicative of setHead being the cause - log.Debug("Skipping transaction reset caused by setHead", - "old", oldHead.Hash(), "oldnum", oldNum, "new", newHead.Hash(), "newnum", newNum) - } else { + if newNum >= oldNum { // If we reorged to a same or higher number, then it's not a case of setHead log.Warn("Transaction pool reset with missing oldhead", "old", oldHead.Hash(), "oldnum", oldNum, "new", newHead.Hash(), "newnum", newNum) - } - return - } - for rem.NumberU64() > add.NumberU64() { - discarded = append(discarded, rem.Transactions()...) - if rem = pool.chain.GetBlock(rem.ParentHash(), rem.NumberU64()-1); rem == nil { - log.Error("Unrooted old chain seen by tx pool", "block", oldHead.Number, "hash", oldHead.Hash()) return } - } - for add.NumberU64() > rem.NumberU64() { - included = append(included, add.Transactions()...) - if add = pool.chain.GetBlock(add.ParentHash(), add.NumberU64()-1); add == nil { - log.Error("Unrooted new chain seen by tx pool", "block", newHead.Number, "hash", newHead.Hash()) - return + // If the reorg ended up on a lower number, it's indicative of setHead being the cause + log.Debug("Skipping transaction reset caused by setHead", + "old", oldHead.Hash(), "oldnum", oldNum, "new", newHead.Hash(), "newnum", newNum) + // We still need to update the current state s.th. the lost transactions can be readded by the user + } else { + for rem.NumberU64() > add.NumberU64() { + discarded = append(discarded, rem.Transactions()...) + if rem = pool.chain.GetBlock(rem.ParentHash(), rem.NumberU64()-1); rem == nil { + log.Error("Unrooted old chain seen by tx pool", "block", oldHead.Number, "hash", oldHead.Hash()) + return + } } - } - for rem.Hash() != add.Hash() { - discarded = append(discarded, rem.Transactions()...) - if rem = pool.chain.GetBlock(rem.ParentHash(), rem.NumberU64()-1); rem == nil { - log.Error("Unrooted old chain seen by tx pool", "block", oldHead.Number, "hash", oldHead.Hash()) - return + for add.NumberU64() > rem.NumberU64() { + included = append(included, add.Transactions()...) + if add = pool.chain.GetBlock(add.ParentHash(), add.NumberU64()-1); add == nil { + log.Error("Unrooted new chain seen by tx pool", "block", newHead.Number, "hash", newHead.Hash()) + return + } } - included = append(included, add.Transactions()...) - if add = pool.chain.GetBlock(add.ParentHash(), add.NumberU64()-1); add == nil { - log.Error("Unrooted new chain seen by tx pool", "block", newHead.Number, "hash", newHead.Hash()) - return + for rem.Hash() != add.Hash() { + discarded = append(discarded, rem.Transactions()...) + if rem = pool.chain.GetBlock(rem.ParentHash(), rem.NumberU64()-1); rem == nil { + log.Error("Unrooted old chain seen by tx pool", "block", oldHead.Number, "hash", oldHead.Hash()) + return + } + included = append(included, add.Transactions()...) + if add = pool.chain.GetBlock(add.ParentHash(), add.NumberU64()-1); add == nil { + log.Error("Unrooted new chain seen by tx pool", "block", newHead.Number, "hash", newHead.Hash()) + return + } } + reinject = types.TxDifference(discarded, included) } - reinject = types.TxDifference(discarded, included) } } // Initialize the internal state to the current head @@ -1196,6 +1220,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { // Update all fork indicator by next pending block number. next := new(big.Int).Add(newHead.Number, big.NewInt(1)) pool.eip2028f = pool.chainconfig.IsEnabled(pool.chainconfig.GetEIP2028Transition, next) + pool.eip2718 = pool.chainconfig.IsEnabled(pool.chainconfig.GetEIP2718Transition, next) } // promoteExecutables moves transactions that have become processable from the @@ -1424,7 +1449,9 @@ func (pool *TxPool) demoteUnexecutables() { for _, tx := range invalids { hash := tx.Hash() log.Trace("Demoting pending transaction", "hash", hash) - pool.enqueueTx(hash, tx) + + // Internal shuffle shouldn't touch the lookup set. + pool.enqueueTx(hash, tx, false, false) } pendingGauge.Dec(int64(len(olds) + len(drops) + len(invalids))) if pool.locals.contains(addr) { @@ -1436,7 +1463,9 @@ func (pool *TxPool) demoteUnexecutables() { for _, tx := range gapped { hash := tx.Hash() log.Error("Demoting invalidated transaction", "hash", hash) - pool.enqueueTx(hash, tx) + + // Internal shuffle shouldn't touch the lookup set. + pool.enqueueTx(hash, tx, false, false) } pendingGauge.Dec(int64(len(gapped))) // This might happen in a reorg, so log it to the metering @@ -1535,8 +1564,8 @@ func (as *accountSet) merge(other *accountSet) { as.cache = nil } -// txLookup is used internally by TxPool to track transactions while allowing lookup without -// mutex contention. +// txLookup is used internally by TxPool to track transactions while allowing +// lookup without mutex contention. // // Note, although this type is properly protected against concurrent access, it // is **not** a type that should ever be mutated or even exposed outside of the @@ -1544,27 +1573,43 @@ func (as *accountSet) merge(other *accountSet) { // internal mechanisms. The sole purpose of the type is to permit out-of-bound // peeking into the pool in TxPool.Get without having to acquire the widely scoped // TxPool.mu mutex. +// +// This lookup set combines the notion of "local transactions", which is useful +// to build upper-level structure. type txLookup struct { - all map[common.Hash]*types.Transaction - slots int - lock sync.RWMutex + slots int + lock sync.RWMutex + locals map[common.Hash]*types.Transaction + remotes map[common.Hash]*types.Transaction } // newTxLookup returns a new txLookup structure. func newTxLookup() *txLookup { return &txLookup{ - all: make(map[common.Hash]*types.Transaction), + locals: make(map[common.Hash]*types.Transaction), + remotes: make(map[common.Hash]*types.Transaction), } } -// Range calls f on each key and value present in the map. -func (t *txLookup) Range(f func(hash common.Hash, tx *types.Transaction) bool) { +// Range calls f on each key and value present in the map. The callback passed +// should return the indicator whether the iteration needs to be continued. +// Callers need to specify which set (or both) to be iterated. +func (t *txLookup) Range(f func(hash common.Hash, tx *types.Transaction, local bool) bool, local bool, remote bool) { t.lock.RLock() defer t.lock.RUnlock() - for key, value := range t.all { - if !f(key, value) { - break + if local { + for key, value := range t.locals { + if !f(key, value, true) { + return + } + } + } + if remote { + for key, value := range t.remotes { + if !f(key, value, false) { + return + } } } } @@ -1574,15 +1619,50 @@ func (t *txLookup) Get(hash common.Hash) *types.Transaction { t.lock.RLock() defer t.lock.RUnlock() - return t.all[hash] + if tx := t.locals[hash]; tx != nil { + return tx + } + return t.remotes[hash] +} + +// GetLocal returns a transaction if it exists in the lookup, or nil if not found. +func (t *txLookup) GetLocal(hash common.Hash) *types.Transaction { + t.lock.RLock() + defer t.lock.RUnlock() + + return t.locals[hash] } -// Count returns the current number of items in the lookup. +// GetRemote returns a transaction if it exists in the lookup, or nil if not found. +func (t *txLookup) GetRemote(hash common.Hash) *types.Transaction { + t.lock.RLock() + defer t.lock.RUnlock() + + return t.remotes[hash] +} + +// Count returns the current number of transactions in the lookup. func (t *txLookup) Count() int { t.lock.RLock() defer t.lock.RUnlock() - return len(t.all) + return len(t.locals) + len(t.remotes) +} + +// LocalCount returns the current number of local transactions in the lookup. +func (t *txLookup) LocalCount() int { + t.lock.RLock() + defer t.lock.RUnlock() + + return len(t.locals) +} + +// RemoteCount returns the current number of remote transactions in the lookup. +func (t *txLookup) RemoteCount() int { + t.lock.RLock() + defer t.lock.RUnlock() + + return len(t.remotes) } // Slots returns the current number of slots used in the lookup. @@ -1594,14 +1674,18 @@ func (t *txLookup) Slots() int { } // Add adds a transaction to the lookup. -func (t *txLookup) Add(tx *types.Transaction) { +func (t *txLookup) Add(tx *types.Transaction, local bool) { t.lock.Lock() defer t.lock.Unlock() t.slots += numSlots(tx) slotsGauge.Update(int64(t.slots)) - t.all[tx.Hash()] = tx + if local { + t.locals[tx.Hash()] = tx + } else { + t.remotes[tx.Hash()] = tx + } } // Remove removes a transaction from the lookup. @@ -1609,10 +1693,36 @@ func (t *txLookup) Remove(hash common.Hash) { t.lock.Lock() defer t.lock.Unlock() - t.slots -= numSlots(t.all[hash]) + tx, ok := t.locals[hash] + if !ok { + tx, ok = t.remotes[hash] + } + if !ok { + log.Error("No transaction found to be deleted", "hash", hash) + return + } + t.slots -= numSlots(tx) slotsGauge.Update(int64(t.slots)) - delete(t.all, hash) + delete(t.locals, hash) + delete(t.remotes, hash) +} + +// RemoteToLocals migrates the transactions belongs to the given locals to locals +// set. The assumption is held the locals set is thread-safe to be used. +func (t *txLookup) RemoteToLocals(locals *accountSet) int { + t.lock.Lock() + defer t.lock.Unlock() + + var migrated int + for hash, tx := range t.remotes { + if locals.containsTx(tx) { + t.locals[hash] = tx + delete(t.remotes, hash) + migrated += 1 + } + } + return migrated } // numSlots calculates the number of slots needed for a single transaction. diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index b8c61dc418..58b190459f 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -56,7 +56,7 @@ type testBlockChain struct { func (bc *testBlockChain) CurrentBlock() *types.Block { return types.NewBlock(&types.Header{ GasLimit: bc.gasLimit, - }, nil, nil, nil, new(trie.Trie)) + }, nil, nil, nil, trie.NewStackTrie(nil)) } func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { @@ -108,10 +108,11 @@ func validateTxPoolInternals(pool *TxPool) error { if total := pool.all.Count(); total != pending+queued { return fmt.Errorf("total transaction count %d != %d pending + %d queued", total, pending, queued) } - if priced := pool.priced.items.Len() - pool.priced.stales; priced != pending+queued { - return fmt.Errorf("total priced transaction count %d != %d pending + %d queued", priced, pending, queued) + pool.priced.Reheap() + priced, remote := pool.priced.remotes.Len(), pool.all.RemoteCount() + if priced != remote { + return fmt.Errorf("total priced transaction count %d != %d", priced, remote) } - // Ensure the next nonce to assign is the correct one for addr, txs := range pool.pending { // Find the last transaction @@ -243,7 +244,7 @@ func TestInvalidTransactions(t *testing.T) { from, _ := deriveSender(tx) pool.currentState.AddBalance(from, big.NewInt(1)) - if err := pool.AddRemote(tx); err != ErrInsufficientFunds { + if err := pool.AddRemote(tx); !errors.Is(err, ErrInsufficientFunds) { t.Error("expected", ErrInsufficientFunds) } @@ -256,7 +257,7 @@ func TestInvalidTransactions(t *testing.T) { pool.currentState.SetNonce(from, 1) pool.currentState.AddBalance(from, big.NewInt(0xffffffffffffff)) tx = transaction(0, 100000, key) - if err := pool.AddRemote(tx); err != ErrNonceTooLow { + if err := pool.AddRemote(tx); !errors.Is(err, ErrNonceTooLow) { t.Error("expected", ErrNonceTooLow) } @@ -281,7 +282,7 @@ func TestTransactionQueue(t *testing.T) { pool.currentState.AddBalance(from, big.NewInt(1000)) <-pool.requestReset(nil, nil) - pool.enqueueTx(tx.Hash(), tx) + pool.enqueueTx(tx.Hash(), tx, false, true) <-pool.requestPromoteExecutables(newAccountSet(pool.signer, from)) if len(pool.pending) != 1 { t.Error("expected valid txs to be 1 is", len(pool.pending)) @@ -290,7 +291,7 @@ func TestTransactionQueue(t *testing.T) { tx = transaction(1, 100, key) from, _ = deriveSender(tx) pool.currentState.SetNonce(from, 2) - pool.enqueueTx(tx.Hash(), tx) + pool.enqueueTx(tx.Hash(), tx, false, true) <-pool.requestPromoteExecutables(newAccountSet(pool.signer, from)) if _, ok := pool.pending[from].txs.items[tx.Nonce()]; ok { @@ -314,9 +315,9 @@ func TestTransactionQueue2(t *testing.T) { pool.currentState.AddBalance(from, big.NewInt(1000)) pool.reset(nil, nil) - pool.enqueueTx(tx1.Hash(), tx1) - pool.enqueueTx(tx2.Hash(), tx2) - pool.enqueueTx(tx3.Hash(), tx3) + pool.enqueueTx(tx1.Hash(), tx1, false, true) + pool.enqueueTx(tx2.Hash(), tx2, false, true) + pool.enqueueTx(tx3.Hash(), tx3, false, true) pool.promoteExecutables([]common.Address{from}) if len(pool.pending) != 1 { @@ -489,12 +490,21 @@ func TestTransactionDropping(t *testing.T) { tx11 = transaction(11, 200, key) tx12 = transaction(12, 300, key) ) + pool.all.Add(tx0, false) + pool.priced.Put(tx0, false) pool.promoteTx(account, tx0.Hash(), tx0) + + pool.all.Add(tx1, false) + pool.priced.Put(tx1, false) pool.promoteTx(account, tx1.Hash(), tx1) + + pool.all.Add(tx2, false) + pool.priced.Put(tx2, false) pool.promoteTx(account, tx2.Hash(), tx2) - pool.enqueueTx(tx10.Hash(), tx10) - pool.enqueueTx(tx11.Hash(), tx11) - pool.enqueueTx(tx12.Hash(), tx12) + + pool.enqueueTx(tx10.Hash(), tx10, false, true) + pool.enqueueTx(tx11.Hash(), tx11, false, true) + pool.enqueueTx(tx12.Hash(), tx12, false, true) // Check that pre and post validations leave the pool as is if pool.pending[account].Len() != 3 { @@ -1140,7 +1150,7 @@ func TestTransactionAllowedTxSize(t *testing.T) { t.Fatalf("expected rejection on slightly oversize transaction") } // Try adding a transaction of random not allowed size - if err := pool.addRemoteSync(pricedDataTransaction(2, pool.currentMaxGas, big.NewInt(1), key, dataSize+1+uint64(rand.Intn(int(10*txMaxSize))))); err == nil { + if err := pool.addRemoteSync(pricedDataTransaction(2, pool.currentMaxGas, big.NewInt(1), key, dataSize+1+uint64(rand.Intn(10*txMaxSize)))); err == nil { t.Fatalf("expected rejection on oversize transaction") } // Run some sanity checks on the pool internals @@ -1965,7 +1975,7 @@ func benchmarkFuturePromotion(b *testing.B, size int) { for i := 0; i < size; i++ { tx := transaction(uint64(1+i), 100000, key) - pool.enqueueTx(tx.Hash(), tx) + pool.enqueueTx(tx.Hash(), tx, false, true) } // Benchmark the speed of pool validation b.ResetTimer() @@ -2008,3 +2018,38 @@ func benchmarkPoolBatchInsert(b *testing.B, size int, local bool) { } } } + +func BenchmarkInsertRemoteWithAllLocals(b *testing.B) { + // Allocate keys for testing + key, _ := crypto.GenerateKey() + account := crypto.PubkeyToAddress(key.PublicKey) + + remoteKey, _ := crypto.GenerateKey() + remoteAddr := crypto.PubkeyToAddress(remoteKey.PublicKey) + + locals := make([]*types.Transaction, 4096+1024) // Occupy all slots + for i := 0; i < len(locals); i++ { + locals[i] = transaction(uint64(i), 100000, key) + } + remotes := make([]*types.Transaction, 1000) + for i := 0; i < len(remotes); i++ { + remotes[i] = pricedTransaction(uint64(i), 100000, big.NewInt(2), remoteKey) // Higher gasprice + } + // Benchmark importing the transactions into the queue + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + pool, _ := setupTxPool() + pool.currentState.AddBalance(account, big.NewInt(100000000)) + for _, local := range locals { + pool.AddLocal(local) + } + b.StartTimer() + // Assign a high enough balance for testing + pool.currentState.AddBalance(remoteAddr, big.NewInt(100000000)) + for i := 0; i < len(remotes); i++ { + pool.AddRemotes([]*types.Transaction{remotes[i]}) + } + pool.Stop() + } +} diff --git a/core/types/access_list_tx.go b/core/types/access_list_tx.go new file mode 100644 index 0000000000..65ee95adf6 --- /dev/null +++ b/core/types/access_list_tx.go @@ -0,0 +1,115 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +//go:generate gencodec -type AccessTuple -out gen_access_tuple.go + +// AccessList is an EIP-2930 access list. +type AccessList []AccessTuple + +// AccessTuple is the element type of an access list. +type AccessTuple struct { + Address common.Address `json:"address" gencodec:"required"` + StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"` +} + +// StorageKeys returns the total number of storage keys in the access list. +func (al AccessList) StorageKeys() int { + sum := 0 + for _, tuple := range al { + sum += len(tuple.StorageKeys) + } + return sum +} + +// AccessListTx is the data of EIP-2930 access list transactions. +type AccessListTx struct { + ChainID *big.Int // destination chain ID + Nonce uint64 // nonce of sender account + GasPrice *big.Int // wei per gas + Gas uint64 // gas limit + To *common.Address `rlp:"nil"` // nil means contract creation + Value *big.Int // wei amount + Data []byte // contract invocation input data + AccessList AccessList // EIP-2930 access list + V, R, S *big.Int // signature values +} + +// copy creates a deep copy of the transaction data and initializes all fields. +func (tx *AccessListTx) copy() TxData { + cpy := &AccessListTx{ + Nonce: tx.Nonce, + To: tx.To, // TODO: copy pointed-to address + Data: common.CopyBytes(tx.Data), + Gas: tx.Gas, + // These are copied below. + AccessList: make(AccessList, len(tx.AccessList)), + Value: new(big.Int), + ChainID: new(big.Int), + GasPrice: new(big.Int), + V: new(big.Int), + R: new(big.Int), + S: new(big.Int), + } + copy(cpy.AccessList, tx.AccessList) + if tx.Value != nil { + cpy.Value.Set(tx.Value) + } + if tx.ChainID != nil { + cpy.ChainID.Set(tx.ChainID) + } + if tx.GasPrice != nil { + cpy.GasPrice.Set(tx.GasPrice) + } + if tx.V != nil { + cpy.V.Set(tx.V) + } + if tx.R != nil { + cpy.R.Set(tx.R) + } + if tx.S != nil { + cpy.S.Set(tx.S) + } + return cpy +} + +// accessors for innerTx. + +func (tx *AccessListTx) txType() byte { return AccessListTxType } +func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID } +func (tx *AccessListTx) protected() bool { return true } +func (tx *AccessListTx) accessList() AccessList { return tx.AccessList } +func (tx *AccessListTx) data() []byte { return tx.Data } +func (tx *AccessListTx) gas() uint64 { return tx.Gas } +func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice } +func (tx *AccessListTx) value() *big.Int { return tx.Value } +func (tx *AccessListTx) nonce() uint64 { return tx.Nonce } +func (tx *AccessListTx) to() *common.Address { return tx.To } + +func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) { + return tx.V, tx.R, tx.S +} + +func (tx *AccessListTx) setSignatureValues(chainID, v, r, s *big.Int) { + tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s +} diff --git a/core/types/block.go b/core/types/block.go index 8096ebb755..553db003bb 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -23,15 +23,12 @@ import ( "io" "math/big" "reflect" - "sync" "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/crypto/sha3" ) var ( @@ -131,22 +128,6 @@ func (h *Header) SanityCheck() error { return nil } -// hasherPool holds LegacyKeccak hashers. -var hasherPool = sync.Pool{ - New: func() interface{} { - return sha3.NewLegacyKeccak256() - }, -} - -func rlpHash(x interface{}) (h common.Hash) { - sha := hasherPool.Get().(crypto.KeccakState) - defer hasherPool.Put(sha) - sha.Reset() - rlp.Encode(sha, x) - sha.Read(h[:]) - return h -} - // EmptyBody returns true if there is no additional 'body' to complete the header // that is: no transactions and no uncles. func (h *Header) EmptyBody() bool { @@ -221,7 +202,7 @@ type storageblock struct { // The values of TxHash, UncleHash, ReceiptHash and Bloom in header // are ignored and set to values derived from the given txs, uncles // and receipts. -func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher Hasher) *Block { +func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher TrieHasher) *Block { b := &Block{header: CopyHeader(header), td: new(big.Int)} // TODO: panic if len(txs) != len(receipts) diff --git a/core/types/block_test.go b/core/types/block_test.go index 4dfdcf9545..63904f882c 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -59,6 +59,66 @@ func TestBlockEncoding(t *testing.T) { tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100")) check("len(Transactions)", len(block.Transactions()), 1) check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash()) + ourBlockEnc, err := rlp.EncodeToBytes(&block) + if err != nil { + t.Fatal("encode error: ", err) + } + if !bytes.Equal(ourBlockEnc, blockEnc) { + t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc) + } +} + +func TestEIP2718BlockEncoding(t *testing.T) { + blockEnc := common.FromHex("f90319f90211a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a0e6e49996c7ec59f7a23d22b83239a60151512c65613bf84a0d7da336399ebc4aa0cafe75574d59780665a97fbfd11365c7545aa8f1abf4e5e12e8243334ef7286bb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000820200832fefd882a410845506eb0796636f6f6c65737420626c6f636b206f6e20636861696ea0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f90101f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b89e01f89b01800a8301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000001a03dbacc8d0259f2508625e97fdfc57cd85fdd16e5821bc2c10bdd1a52649e8335a0476e10695b183a87b0aa292a7f4b78ef0c3fbe62aa2c42c84e1d9c3da159ef14c0") + var block Block + if err := rlp.DecodeBytes(blockEnc, &block); err != nil { + t.Fatal("decode error: ", err) + } + + check := func(f string, got, want interface{}) { + if !reflect.DeepEqual(got, want) { + t.Errorf("%s mismatch: got %v, want %v", f, got, want) + } + } + check("Difficulty", block.Difficulty(), big.NewInt(131072)) + check("GasLimit", block.GasLimit(), uint64(3141592)) + check("GasUsed", block.GasUsed(), uint64(42000)) + check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1")) + check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498")) + check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017")) + check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4)) + check("Time", block.Time(), uint64(1426516743)) + check("Size", block.Size(), common.StorageSize(len(blockEnc))) + + // Create legacy tx. + to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87") + tx1 := NewTx(&LegacyTx{ + Nonce: 0, + To: &to, + Value: big.NewInt(10), + Gas: 50000, + GasPrice: big.NewInt(10), + }) + sig := common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100") + tx1, _ = tx1.WithSignature(HomesteadSigner{}, sig) + + // Create ACL tx. + addr := common.HexToAddress("0x0000000000000000000000000000000000000001") + tx2 := NewTx(&AccessListTx{ + ChainID: big.NewInt(1), + Nonce: 0, + To: &to, + Gas: 123457, + GasPrice: big.NewInt(10), + AccessList: AccessList{{Address: addr, StorageKeys: []common.Hash{{0}}}}, + }) + sig2 := common.Hex2Bytes("3dbacc8d0259f2508625e97fdfc57cd85fdd16e5821bc2c10bdd1a52649e8335476e10695b183a87b0aa292a7f4b78ef0c3fbe62aa2c42c84e1d9c3da159ef1401") + tx2, _ = tx2.WithSignature(NewEIP2930Signer(big.NewInt(1)), sig2) + + check("len(Transactions)", len(block.Transactions()), 2) + check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash()) + check("Transactions[1].Hash", block.Transactions()[1].Hash(), tx2.Hash()) + check("Transactions[1].Type()", block.Transactions()[1].Type(), uint8(AccessListTxType)) ourBlockEnc, err := rlp.EncodeToBytes(&block) if err != nil { @@ -121,7 +181,7 @@ func makeBenchBlock() *Block { key, _ = crypto.GenerateKey() txs = make([]*Transaction, 70) receipts = make([]*Receipt, len(txs)) - signer = NewEIP155Signer(params.TestChainConfig.ChainID) + signer = LatestSigner(params.TestChainConfig) uncles = make([]*Header, 3) ) header := &Header{ diff --git a/core/types/derive_sha.go b/core/types/derive_sha.go deleted file mode 100644 index 51a10f3f3d..0000000000 --- a/core/types/derive_sha.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 . - -package types - -import ( - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/rlp" -) - -type DerivableList interface { - Len() int - GetRlp(i int) []byte -} - -// Hasher is the tool used to calculate the hash of derivable list. -type Hasher interface { - Reset() - Update([]byte, []byte) - Hash() common.Hash -} - -func DeriveSha(list DerivableList, hasher Hasher) common.Hash { - hasher.Reset() - - // StackTrie requires values to be inserted in increasing - // hash order, which is not the order that `list` provides - // hashes in. This insertion sequence ensures that the - // order is correct. - - var buf []byte - for i := 1; i < list.Len() && i <= 0x7f; i++ { - buf = rlp.AppendUint64(buf[:0], uint64(i)) - hasher.Update(buf, list.GetRlp(i)) - } - if list.Len() > 0 { - buf = rlp.AppendUint64(buf[:0], 0) - hasher.Update(buf, list.GetRlp(0)) - } - for i := 0x80; i < list.Len(); i++ { - buf = rlp.AppendUint64(buf[:0], uint64(i)) - hasher.Update(buf, list.GetRlp(i)) - } - return hasher.Hash() -} diff --git a/core/types/gen_access_tuple.go b/core/types/gen_access_tuple.go new file mode 100644 index 0000000000..fc48a84cc0 --- /dev/null +++ b/core/types/gen_access_tuple.go @@ -0,0 +1,43 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package types + +import ( + "encoding/json" + "errors" + + "github.com/ethereum/go-ethereum/common" +) + +// MarshalJSON marshals as JSON. +func (a AccessTuple) MarshalJSON() ([]byte, error) { + type AccessTuple struct { + Address common.Address `json:"address" gencodec:"required"` + StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"` + } + var enc AccessTuple + enc.Address = a.Address + enc.StorageKeys = a.StorageKeys + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (a *AccessTuple) UnmarshalJSON(input []byte) error { + type AccessTuple struct { + Address *common.Address `json:"address" gencodec:"required"` + StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"` + } + var dec AccessTuple + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Address == nil { + return errors.New("missing required field 'address' for AccessTuple") + } + a.Address = *dec.Address + if dec.StorageKeys == nil { + return errors.New("missing required field 'storageKeys' for AccessTuple") + } + a.StorageKeys = dec.StorageKeys + return nil +} diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go index 790ed65b58..bb892f85be 100644 --- a/core/types/gen_receipt_json.go +++ b/core/types/gen_receipt_json.go @@ -16,6 +16,7 @@ var _ = (*receiptMarshaling)(nil) // MarshalJSON marshals as JSON. func (r Receipt) MarshalJSON() ([]byte, error) { type Receipt struct { + Type hexutil.Uint64 `json:"type,omitempty"` PostState hexutil.Bytes `json:"root"` Status hexutil.Uint64 `json:"status"` CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"` @@ -29,6 +30,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) { TransactionIndex hexutil.Uint `json:"transactionIndex"` } var enc Receipt + enc.Type = hexutil.Uint64(r.Type) enc.PostState = r.PostState enc.Status = hexutil.Uint64(r.Status) enc.CumulativeGasUsed = hexutil.Uint64(r.CumulativeGasUsed) @@ -46,6 +48,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (r *Receipt) UnmarshalJSON(input []byte) error { type Receipt struct { + Type *hexutil.Uint64 `json:"type,omitempty"` PostState *hexutil.Bytes `json:"root"` Status *hexutil.Uint64 `json:"status"` CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"` @@ -62,6 +65,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { if err := json.Unmarshal(input, &dec); err != nil { return err } + if dec.Type != nil { + r.Type = uint8(*dec.Type) + } if dec.PostState != nil { r.PostState = *dec.PostState } diff --git a/core/types/gen_tx_json.go b/core/types/gen_tx_json.go deleted file mode 100644 index e676058ecc..0000000000 --- a/core/types/gen_tx_json.go +++ /dev/null @@ -1,101 +0,0 @@ -// 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" -) - -var _ = (*txdataMarshaling)(nil) - -// MarshalJSON marshals as JSON. -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.Uint64 `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.Uint64(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) -} - -// UnmarshalJSON unmarshals from JSON. -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.Uint64 `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 = uint64(*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/hashing.go b/core/types/hashing.go new file mode 100644 index 0000000000..3227cf8a72 --- /dev/null +++ b/core/types/hashing.go @@ -0,0 +1,113 @@ +// 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 . + +package types + +import ( + "bytes" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/crypto/sha3" +) + +// hasherPool holds LegacyKeccak256 hashers for rlpHash. +var hasherPool = sync.Pool{ + New: func() interface{} { return sha3.NewLegacyKeccak256() }, +} + +// deriveBufferPool holds temporary encoder buffers for DeriveSha and TX encoding. +var encodeBufferPool = sync.Pool{ + New: func() interface{} { return new(bytes.Buffer) }, +} + +// rlpHash encodes x and hashes the encoded bytes. +func rlpHash(x interface{}) (h common.Hash) { + sha := hasherPool.Get().(crypto.KeccakState) + defer hasherPool.Put(sha) + sha.Reset() + rlp.Encode(sha, x) + sha.Read(h[:]) + return h +} + +// prefixedRlpHash writes the prefix into the hasher before rlp-encoding x. +// It's used for typed transactions. +func prefixedRlpHash(prefix byte, x interface{}) (h common.Hash) { + sha := hasherPool.Get().(crypto.KeccakState) + defer hasherPool.Put(sha) + sha.Reset() + sha.Write([]byte{prefix}) + rlp.Encode(sha, x) + sha.Read(h[:]) + return h +} + +// TrieHasher is the tool used to calculate the hash of derivable list. +// This is internal, do not use. +type TrieHasher interface { + Reset() + Update([]byte, []byte) + Hash() common.Hash +} + +// DerivableList is the input to DeriveSha. +// It is implemented by the 'Transactions' and 'Receipts' types. +// This is internal, do not use these methods. +type DerivableList interface { + Len() int + EncodeIndex(int, *bytes.Buffer) +} + +func encodeForDerive(list DerivableList, i int, buf *bytes.Buffer) []byte { + buf.Reset() + list.EncodeIndex(i, buf) + // It's really unfortunate that we need to do perform this copy. + // StackTrie holds onto the values until Hash is called, so the values + // written to it must not alias. + return common.CopyBytes(buf.Bytes()) +} + +// DeriveSha creates the tree hashes of transactions and receipts in a block header. +func DeriveSha(list DerivableList, hasher TrieHasher) common.Hash { + hasher.Reset() + + valueBuf := encodeBufferPool.Get().(*bytes.Buffer) + defer encodeBufferPool.Put(valueBuf) + + // StackTrie requires values to be inserted in increasing hash order, which is not the + // order that `list` provides hashes in. This insertion sequence ensures that the + // order is correct. + var indexBuf []byte + for i := 1; i < list.Len() && i <= 0x7f; i++ { + indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i)) + value := encodeForDerive(list, i, valueBuf) + hasher.Update(indexBuf, value) + } + if list.Len() > 0 { + indexBuf = rlp.AppendUint64(indexBuf[:0], 0) + value := encodeForDerive(list, 0, valueBuf) + hasher.Update(indexBuf, value) + } + for i := 0x80; i < list.Len(); i++ { + indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i)) + value := encodeForDerive(list, i, valueBuf) + hasher.Update(indexBuf, value) + } + return hasher.Hash() +} diff --git a/core/types/hashing_test.go b/core/types/hashing_test.go new file mode 100644 index 0000000000..6d1ebf897c --- /dev/null +++ b/core/types/hashing_test.go @@ -0,0 +1,228 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types_test + +import ( + "bytes" + "fmt" + "io" + "math/big" + mrand "math/rand" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +func TestDeriveSha(t *testing.T) { + txs, err := genTxs(0) + if err != nil { + t.Fatal(err) + } + for len(txs) < 1000 { + exp := types.DeriveSha(txs, new(trie.Trie)) + got := types.DeriveSha(txs, trie.NewStackTrie(nil)) + if !bytes.Equal(got[:], exp[:]) { + t.Fatalf("%d txs: got %x exp %x", len(txs), got, exp) + } + newTxs, err := genTxs(uint64(len(txs) + 1)) + if err != nil { + t.Fatal(err) + } + txs = append(txs, newTxs...) + } +} + +// TestEIP2718DeriveSha tests that the input to the DeriveSha function is correct. +func TestEIP2718DeriveSha(t *testing.T) { + for _, tc := range []struct { + rlpData string + exp string + }{ + { + rlpData: "0xb8a701f8a486796f6c6f763380843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0775101f92dcca278a56bfe4d613428624a1ebfc3cd9e0bcc1de80c41455b9021a06c9deac205afe7b124907d4ba54a9f46161498bd3990b90d175aac12c9a40ee9", + exp: "01 01f8a486796f6c6f763380843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0775101f92dcca278a56bfe4d613428624a1ebfc3cd9e0bcc1de80c41455b9021a06c9deac205afe7b124907d4ba54a9f46161498bd3990b90d175aac12c9a40ee9\n80 01f8a486796f6c6f763380843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0775101f92dcca278a56bfe4d613428624a1ebfc3cd9e0bcc1de80c41455b9021a06c9deac205afe7b124907d4ba54a9f46161498bd3990b90d175aac12c9a40ee9\n", + }, + } { + d := &hashToHumanReadable{} + var t1, t2 types.Transaction + rlp.DecodeBytes(common.FromHex(tc.rlpData), &t1) + rlp.DecodeBytes(common.FromHex(tc.rlpData), &t2) + txs := types.Transactions{&t1, &t2} + types.DeriveSha(txs, d) + if tc.exp != string(d.data) { + t.Fatalf("Want\n%v\nhave:\n%v", tc.exp, string(d.data)) + } + } +} + +func BenchmarkDeriveSha200(b *testing.B) { + txs, err := genTxs(200) + if err != nil { + b.Fatal(err) + } + var exp common.Hash + var got common.Hash + b.Run("std_trie", func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + exp = types.DeriveSha(txs, new(trie.Trie)) + } + }) + + b.Run("stack_trie", func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + got = types.DeriveSha(txs, trie.NewStackTrie(nil)) + } + }) + if got != exp { + b.Errorf("got %x exp %x", got, exp) + } +} + +func TestFuzzDeriveSha(t *testing.T) { + // increase this for longer runs -- it's set to quite low for travis + rndSeed := mrand.Int() + for i := 0; i < 10; i++ { + seed := rndSeed + i + exp := types.DeriveSha(newDummy(i), new(trie.Trie)) + got := types.DeriveSha(newDummy(i), trie.NewStackTrie(nil)) + if !bytes.Equal(got[:], exp[:]) { + printList(newDummy(seed)) + t.Fatalf("seed %d: got %x exp %x", seed, got, exp) + } + } +} + +// TestDerivableList contains testcases found via fuzzing +func TestDerivableList(t *testing.T) { + type tcase []string + tcs := []tcase{ + { + "0xc041", + }, + { + "0xf04cf757812428b0763112efb33b6f4fad7deb445e", + "0xf04cf757812428b0763112efb33b6f4fad7deb445e", + }, + { + "0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d", + "0x6cd850eca0a7ac46bb1748d7b9cb88aa3bd21c57d852c28198ad8fa422c4595032e88a4494b4778b36b944fe47a52b8c5cd312910139dfcb4147ab8e972cc456bcb063f25dd78f54c4d34679e03142c42c662af52947d45bdb6e555751334ace76a5080ab5a0256a1d259855dfc5c0b8023b25befbb13fd3684f9f755cbd3d63544c78ee2001452dd54633a7593ade0b183891a0a4e9c7844e1254005fbe592b1b89149a502c24b6e1dca44c158aebedf01beae9c30cabe16a", + "0x14abd5c47c0be87b0454596baad2", + "0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d", + }, + } + for i, tc := range tcs[1:] { + exp := types.DeriveSha(flatList(tc), new(trie.Trie)) + got := types.DeriveSha(flatList(tc), trie.NewStackTrie(nil)) + if !bytes.Equal(got[:], exp[:]) { + t.Fatalf("case %d: got %x exp %x", i, got, exp) + } + } +} + +func genTxs(num uint64) (types.Transactions, error) { + key, err := crypto.HexToECDSA("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") + if err != nil { + return nil, err + } + var addr = crypto.PubkeyToAddress(key.PublicKey) + newTx := func(i uint64) (*types.Transaction, error) { + signer := types.NewEIP155Signer(big.NewInt(18)) + utx := types.NewTransaction(i, addr, new(big.Int), 0, new(big.Int).SetUint64(10000000), nil) + tx, err := types.SignTx(utx, signer, key) + return tx, err + } + var txs types.Transactions + for i := uint64(0); i < num; i++ { + tx, err := newTx(i) + if err != nil { + return nil, err + } + txs = append(txs, tx) + } + return txs, nil +} + +type dummyDerivableList struct { + len int + seed int +} + +func newDummy(seed int) *dummyDerivableList { + d := &dummyDerivableList{} + src := mrand.NewSource(int64(seed)) + // don't use lists longer than 4K items + d.len = int(src.Int63() & 0x0FFF) + d.seed = seed + return d +} + +func (d *dummyDerivableList) Len() int { + return d.len +} + +func (d *dummyDerivableList) EncodeIndex(i int, w *bytes.Buffer) { + src := mrand.NewSource(int64(d.seed + i)) + // max item size 256, at least 1 byte per item + size := 1 + src.Int63()&0x00FF + io.CopyN(w, mrand.New(src), size) +} + +func printList(l types.DerivableList) { + fmt.Printf("list length: %d\n", l.Len()) + fmt.Printf("{\n") + for i := 0; i < l.Len(); i++ { + var buf bytes.Buffer + l.EncodeIndex(i, &buf) + fmt.Printf("\"0x%x\",\n", buf.Bytes()) + } + fmt.Printf("},\n") +} + +type flatList []string + +func (f flatList) Len() int { + return len(f) +} +func (f flatList) EncodeIndex(i int, w *bytes.Buffer) { + w.Write(hexutil.MustDecode(f[i])) +} + +type hashToHumanReadable struct { + data []byte +} + +func (d *hashToHumanReadable) Reset() { + d.data = make([]byte, 0) +} + +func (d *hashToHumanReadable) Update(i []byte, i2 []byte) { + l := fmt.Sprintf("%x %x\n", i, i2) + d.data = append(d.data, []byte(l)...) +} + +func (d *hashToHumanReadable) Hash() common.Hash { + return common.Hash{} +} diff --git a/core/types/legacy_tx.go b/core/types/legacy_tx.go new file mode 100644 index 0000000000..41ad44f379 --- /dev/null +++ b/core/types/legacy_tx.go @@ -0,0 +1,111 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// LegacyTx is the transaction data of regular Ethereum transactions. +type LegacyTx struct { + Nonce uint64 // nonce of sender account + GasPrice *big.Int // wei per gas + Gas uint64 // gas limit + To *common.Address `rlp:"nil"` // nil means contract creation + Value *big.Int // wei amount + Data []byte // contract invocation input data + V, R, S *big.Int // signature values +} + +// NewTransaction creates an unsigned legacy transaction. +// Deprecated: use NewTx instead. +func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { + return NewTx(&LegacyTx{ + Nonce: nonce, + To: &to, + Value: amount, + Gas: gasLimit, + GasPrice: gasPrice, + Data: data, + }) +} + +// NewContractCreation creates an unsigned legacy transaction. +// Deprecated: use NewTx instead. +func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { + return NewTx(&LegacyTx{ + Nonce: nonce, + Value: amount, + Gas: gasLimit, + GasPrice: gasPrice, + Data: data, + }) +} + +// copy creates a deep copy of the transaction data and initializes all fields. +func (tx *LegacyTx) copy() TxData { + cpy := &LegacyTx{ + Nonce: tx.Nonce, + To: tx.To, // TODO: copy pointed-to address + Data: common.CopyBytes(tx.Data), + Gas: tx.Gas, + // These are initialized below. + Value: new(big.Int), + GasPrice: new(big.Int), + V: new(big.Int), + R: new(big.Int), + S: new(big.Int), + } + if tx.Value != nil { + cpy.Value.Set(tx.Value) + } + if tx.GasPrice != nil { + cpy.GasPrice.Set(tx.GasPrice) + } + if tx.V != nil { + cpy.V.Set(tx.V) + } + if tx.R != nil { + cpy.R.Set(tx.R) + } + if tx.S != nil { + cpy.S.Set(tx.S) + } + return cpy +} + +// accessors for innerTx. + +func (tx *LegacyTx) txType() byte { return LegacyTxType } +func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) } +func (tx *LegacyTx) accessList() AccessList { return nil } +func (tx *LegacyTx) data() []byte { return tx.Data } +func (tx *LegacyTx) gas() uint64 { return tx.Gas } +func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice } +func (tx *LegacyTx) value() *big.Int { return tx.Value } +func (tx *LegacyTx) nonce() uint64 { return tx.Nonce } +func (tx *LegacyTx) to() *common.Address { return tx.To } + +func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) { + return tx.V, tx.R, tx.S +} + +func (tx *LegacyTx) setSignatureValues(chainID, v, r, s *big.Int) { + tx.V, tx.R, tx.S = v, r, s +} diff --git a/core/types/receipt.go b/core/types/receipt.go index 9e74000b15..7c0f8d1efb 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -38,6 +38,9 @@ var ( receiptStatusSuccessfulRLP = []byte{0x01} ) +// This error is returned when a typed receipt is decoded, but the string is empty. +var errEmptyTypedReceipt = errors.New("empty typed receipt bytes") + const ( // ReceiptStatusFailed is the status code of a transaction if execution failed. ReceiptStatusFailed = uint64(0) @@ -49,6 +52,7 @@ const ( // Receipt represents the results of a transaction. type Receipt struct { // Consensus fields: These fields are defined by the Yellow Paper + Type uint8 `json:"type,omitempty"` PostState []byte `json:"root"` Status uint64 `json:"status"` CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"` @@ -69,6 +73,7 @@ type Receipt struct { } type receiptMarshaling struct { + Type hexutil.Uint64 PostState hexutil.Bytes Status hexutil.Uint64 CumulativeGasUsed hexutil.Uint64 @@ -114,8 +119,13 @@ type v3StoredReceiptRLP struct { } // NewReceipt creates a barebone transaction receipt, copying the init fields. +// Deprecated: create receipts using a struct literal instead. func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt { - r := &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: cumulativeGasUsed} + r := &Receipt{ + Type: LegacyTxType, + PostState: common.CopyBytes(root), + CumulativeGasUsed: cumulativeGasUsed, + } if failed { r.Status = ReceiptStatusFailed } else { @@ -127,21 +137,65 @@ func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt { // EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt // into an RLP stream. If no post state is present, byzantium fork is assumed. func (r *Receipt) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}) + data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} + if r.Type == LegacyTxType { + return rlp.Encode(w, data) + } + // It's an EIP-2718 typed TX receipt. + if r.Type != AccessListTxType { + return ErrTxTypeNotSupported + } + buf := encodeBufferPool.Get().(*bytes.Buffer) + defer encodeBufferPool.Put(buf) + buf.Reset() + buf.WriteByte(r.Type) + if err := rlp.Encode(buf, data); err != nil { + return err + } + return rlp.Encode(w, buf.Bytes()) } // DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt // from an RLP stream. func (r *Receipt) DecodeRLP(s *rlp.Stream) error { - var dec receiptRLP - if err := s.Decode(&dec); err != nil { - return err - } - if err := r.setStatus(dec.PostStateOrStatus); err != nil { + kind, _, err := s.Kind() + switch { + case err != nil: return err + case kind == rlp.List: + // It's a legacy receipt. + var dec receiptRLP + if err := s.Decode(&dec); err != nil { + return err + } + r.Type = LegacyTxType + return r.setFromRLP(dec) + case kind == rlp.String: + // It's an EIP-2718 typed tx receipt. + b, err := s.Bytes() + if err != nil { + return err + } + if len(b) == 0 { + return errEmptyTypedReceipt + } + r.Type = b[0] + if r.Type == AccessListTxType { + var dec receiptRLP + if err := rlp.DecodeBytes(b[1:], &dec); err != nil { + return err + } + return r.setFromRLP(dec) + } + return ErrTxTypeNotSupported + default: + return rlp.ErrExpectedList } - r.CumulativeGasUsed, r.Bloom, r.Logs = dec.CumulativeGasUsed, dec.Bloom, dec.Logs - return nil +} + +func (r *Receipt) setFromRLP(data receiptRLP) error { + r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs + return r.setStatus(data.PostStateOrStatus) } func (r *Receipt) setStatus(postStateOrStatus []byte) error { @@ -172,7 +226,6 @@ func (r *Receipt) statusEncoding() []byte { // to approximate and limit the memory consumption of various caches. func (r *Receipt) Size() common.StorageSize { size := common.StorageSize(unsafe.Sizeof(*r)) + common.StorageSize(len(r.PostState)) - size += common.StorageSize(len(r.Logs)) * common.StorageSize(unsafe.Sizeof(Log{})) for _, log := range r.Logs { size += common.StorageSize(len(log.Topics)*common.HashLength + len(log.Data)) @@ -277,19 +330,27 @@ func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { return nil } -// Receipts is a wrapper around a Receipt array to implement DerivableList. +// Receipts implements DerivableList for receipts. type Receipts []*Receipt // Len returns the number of receipts in this list. -func (r Receipts) Len() int { return len(r) } - -// GetRlp returns the RLP encoding of one receipt from the list. -func (r Receipts) GetRlp(i int) []byte { - bytes, err := rlp.EncodeToBytes(r[i]) - if err != nil { - panic(err) +func (rs Receipts) Len() int { return len(rs) } + +// EncodeIndex encodes the i'th receipt to w. +func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { + r := rs[i] + data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} + switch r.Type { + case LegacyTxType: + rlp.Encode(w, data) + case AccessListTxType: + w.WriteByte(AccessListTxType) + rlp.Encode(w, data) + default: + // For unsupported types, write nothing. Since this is for + // DeriveSha, the error will be caught matching the derived hash + // to the block. } - return bytes } // DeriveFields fills the receipts with their computed fields based on consensus @@ -302,7 +363,8 @@ func (r Receipts) DeriveFields(config ctypes.ChainConfigurator, hash common.Hash return errors.New("transaction and receipt count mismatch") } for i := 0; i < len(r); i++ { - // The transaction hash can be retrieved from the transaction itself + // The transaction type and hash can be retrieved from the transaction itself + r[i].Type = txs[i].Type() r[i].TxHash = txs[i].Hash() // block location fields diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index 806b3dd2ab..22a316c237 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -29,6 +29,15 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +func TestDecodeEmptyTypedReceipt(t *testing.T) { + input := []byte{0x80} + var r Receipt + err := rlp.DecodeBytes(input, &r) + if err != errEmptyTypedReceipt { + t.Fatal("wrong error:", err) + } +} + func TestLegacyReceiptDecoding(t *testing.T) { tests := []struct { name string @@ -154,9 +163,29 @@ func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) { // Tests that receipt data can be correctly derived from the contextual infos func TestDeriveFields(t *testing.T) { // Create a few transactions to have receipts for + to2 := common.HexToAddress("0x2") + to3 := common.HexToAddress("0x3") txs := Transactions{ - NewContractCreation(1, big.NewInt(1), 1, big.NewInt(1), nil), - NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil), + NewTx(&LegacyTx{ + Nonce: 1, + Value: big.NewInt(1), + Gas: 1, + GasPrice: big.NewInt(1), + }), + NewTx(&LegacyTx{ + To: &to2, + Nonce: 2, + Value: big.NewInt(2), + Gas: 2, + GasPrice: big.NewInt(2), + }), + NewTx(&AccessListTx{ + To: &to3, + Nonce: 3, + Value: big.NewInt(3), + Gas: 3, + GasPrice: big.NewInt(3), + }), } // Create the corresponding receipts receipts := Receipts{ @@ -182,6 +211,18 @@ func TestDeriveFields(t *testing.T) { ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}), GasUsed: 2, }, + &Receipt{ + Type: AccessListTxType, + PostState: common.Hash{3}.Bytes(), + CumulativeGasUsed: 6, + Logs: []*Log{ + {Address: common.BytesToAddress([]byte{0x33})}, + {Address: common.BytesToAddress([]byte{0x03, 0x33})}, + }, + TxHash: txs[2].Hash(), + ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}), + GasUsed: 3, + }, } // Clear all the computed fields and re-derive them number := big.NewInt(1) @@ -196,6 +237,9 @@ func TestDeriveFields(t *testing.T) { logIndex := uint(0) for i := range receipts { + if receipts[i].Type != txs[i].Type() { + t.Errorf("receipts[%d].Type = %d, want %d", i, receipts[i].Type, txs[i].Type()) + } if receipts[i].TxHash != txs[i].Hash() { t.Errorf("receipts[%d].TxHash = %s, want %s", i, receipts[i].TxHash.String(), txs[i].Hash().String()) } @@ -243,6 +287,34 @@ func TestDeriveFields(t *testing.T) { } } +// TestTypedReceiptEncodingDecoding reproduces a flaw that existed in the receipt +// rlp decoder, which failed due to a shadowing error. +func TestTypedReceiptEncodingDecoding(t *testing.T) { + var payload = common.FromHex("f9043eb9010c01f90108018262d4b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0b9010c01f901080182cd14b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0b9010d01f901090183013754b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0b9010d01f90109018301a194b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0") + check := func(bundle []*Receipt) { + t.Helper() + for i, receipt := range bundle { + if got, want := receipt.Type, uint8(1); got != want { + t.Fatalf("bundle %d: got %x, want %x", i, got, want) + } + } + } + { + var bundle []*Receipt + rlp.DecodeBytes(payload, &bundle) + check(bundle) + } + { + var bundle []*Receipt + r := bytes.NewReader(payload) + s := rlp.NewStream(r, uint64(len(payload))) + if err := s.Decode(&bundle); err != nil { + t.Fatal(err) + } + check(bundle) + } +} + func clearComputedFieldsOnReceipts(t *testing.T, receipts Receipts) { t.Helper() diff --git a/core/types/transaction.go b/core/types/transaction.go index ec19744881..a35e07a5a3 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -17,6 +17,7 @@ package types import ( + "bytes" "container/heap" "errors" "io" @@ -25,20 +26,28 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" ) -//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go - var ( - ErrInvalidSig = errors.New("invalid transaction v, r, s values") + ErrInvalidSig = errors.New("invalid transaction v, r, s values") + ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures") + ErrInvalidTxType = errors.New("transaction type not valid in this context") + ErrTxTypeNotSupported = errors.New("transaction type not supported") + errEmptyTypedTx = errors.New("empty typed transaction bytes") +) + +// Transaction types. +const ( + LegacyTxType = iota + AccessListTxType ) +// Transaction is an Ethereum transaction. type Transaction struct { - data txdata // Consensus contents of a transaction - time time.Time // Time first seen locally (spam avoidance) + inner TxData // Consensus contents of a transaction + time time.Time // Time first seen locally (spam avoidance) // caches hash atomic.Value @@ -46,205 +55,280 @@ type Transaction struct { from atomic.Value } -type txdata struct { - AccountNonce uint64 `json:"nonce" gencodec:"required"` - Price *big.Int `json:"gasPrice" gencodec:"required"` - GasLimit uint64 `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"` +// NewTx creates a new transaction. +func NewTx(inner TxData) *Transaction { + tx := new(Transaction) + tx.setDecoded(inner.copy(), 0) + return tx +} + +// TxData is the underlying data of a transaction. +// +// This is implemented by LegacyTx and AccessListTx. +type TxData interface { + txType() byte // returns the type ID + copy() TxData // creates a deep copy and initializes all fields - // Signature values - V *big.Int `json:"v" gencodec:"required"` - R *big.Int `json:"r" gencodec:"required"` - S *big.Int `json:"s" gencodec:"required"` + chainID() *big.Int + accessList() AccessList + data() []byte + gas() uint64 + gasPrice() *big.Int + value() *big.Int + nonce() uint64 + to() *common.Address - // This is only used when marshaling to JSON. - Hash *common.Hash `json:"hash" rlp:"-"` + rawSignatureValues() (v, r, s *big.Int) + setSignatureValues(chainID, v, r, s *big.Int) } -type txdataMarshaling struct { - AccountNonce hexutil.Uint64 - Price *hexutil.Big - GasLimit hexutil.Uint64 - Amount *hexutil.Big - Payload hexutil.Bytes - V *hexutil.Big - R *hexutil.Big - S *hexutil.Big +// EncodeRLP implements rlp.Encoder +func (tx *Transaction) EncodeRLP(w io.Writer) error { + if tx.Type() == LegacyTxType { + return rlp.Encode(w, tx.inner) + } + // It's an EIP-2718 typed TX envelope. + buf := encodeBufferPool.Get().(*bytes.Buffer) + defer encodeBufferPool.Put(buf) + buf.Reset() + if err := tx.encodeTyped(buf); err != nil { + return err + } + return rlp.Encode(w, buf.Bytes()) } -func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { - return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data) +// encodeTyped writes the canonical encoding of a typed transaction to w. +func (tx *Transaction) encodeTyped(w *bytes.Buffer) error { + w.WriteByte(tx.Type()) + return rlp.Encode(w, tx.inner) } -func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { - return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data) +// MarshalBinary returns the canonical encoding of the transaction. +// For legacy transactions, it returns the RLP encoding. For EIP-2718 typed +// transactions, it returns the type and payload. +func (tx *Transaction) MarshalBinary() ([]byte, error) { + if tx.Type() == LegacyTxType { + return rlp.EncodeToBytes(tx.inner) + } + var buf bytes.Buffer + err := tx.encodeTyped(&buf) + return buf.Bytes(), err } -func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { - if len(data) > 0 { - data = common.CopyBytes(data) +// DecodeRLP implements rlp.Decoder +func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { + kind, size, err := s.Kind() + switch { + case err != nil: + return err + case kind == rlp.List: + // It's a legacy transaction. + var inner LegacyTx + err := s.Decode(&inner) + if err == nil { + tx.setDecoded(&inner, int(rlp.ListSize(size))) + } + return err + case kind == rlp.String: + // It's an EIP-2718 typed TX envelope. + var b []byte + if b, err = s.Bytes(); err != nil { + return err + } + inner, err := tx.decodeTyped(b) + if err == nil { + tx.setDecoded(inner, len(b)) + } + return err + default: + return rlp.ErrExpectedList } - d := txdata{ - AccountNonce: nonce, - Recipient: to, - Payload: data, - Amount: new(big.Int), - GasLimit: gasLimit, - Price: new(big.Int), - V: new(big.Int), - R: new(big.Int), - S: new(big.Int), +} + +// UnmarshalBinary decodes the canonical encoding of transactions. +// It supports legacy RLP transactions and EIP2718 typed transactions. +func (tx *Transaction) UnmarshalBinary(b []byte) error { + if len(b) > 0 && b[0] > 0x7f { + // It's a legacy transaction. + var data LegacyTx + err := rlp.DecodeBytes(b, &data) + if err != nil { + return err + } + tx.setDecoded(&data, len(b)) + return nil } - if amount != nil { - d.Amount.Set(amount) + // It's an EIP2718 typed transaction envelope. + inner, err := tx.decodeTyped(b) + if err != nil { + return err } - if gasPrice != nil { - d.Price.Set(gasPrice) + tx.setDecoded(inner, len(b)) + return nil +} + +// decodeTyped decodes a typed transaction from the canonical format. +func (tx *Transaction) decodeTyped(b []byte) (TxData, error) { + if len(b) == 0 { + return nil, errEmptyTypedTx } - return &Transaction{ - data: d, - time: time.Now(), + switch b[0] { + case AccessListTxType: + var inner AccessListTx + err := rlp.DecodeBytes(b[1:], &inner) + return &inner, err + default: + return nil, ErrTxTypeNotSupported } } -// ChainId returns which chain id this transaction was signed for (if at all) -func (tx *Transaction) ChainId() *big.Int { - return deriveChainId(tx.data.V) +// setDecoded sets the inner transaction and size after decoding. +func (tx *Transaction) setDecoded(inner TxData, size int) { + tx.inner = inner + tx.time = time.Now() + if size > 0 { + tx.size.Store(common.StorageSize(size)) + } } -// Protected returns whether the transaction is protected from replay protection. -func (tx *Transaction) Protected() bool { - return isProtectedV(tx.data.V) +func sanityCheckSignature(v *big.Int, r *big.Int, s *big.Int, maybeProtected bool) error { + if isProtectedV(v) && !maybeProtected { + return ErrUnexpectedProtection + } + + var plainV byte + if isProtectedV(v) { + chainID := deriveChainId(v).Uint64() + plainV = byte(v.Uint64() - 35 - 2*chainID) + } else if maybeProtected { + // Only EIP-155 signatures can be optionally protected. Since + // we determined this v value is not protected, it must be a + // raw 27 or 28. + plainV = byte(v.Uint64() - 27) + } else { + // If the signature is not optionally protected, we assume it + // must already be equal to the recovery id. + plainV = byte(v.Uint64()) + } + if !crypto.ValidateSignatureValues(plainV, r, s, false) { + return ErrInvalidSig + } + + return nil } func isProtectedV(V *big.Int) bool { if V.BitLen() <= 8 { v := V.Uint64() - return v != 27 && v != 28 + return v != 27 && v != 28 && v != 1 && v != 0 } // anything not 27 or 28 is considered protected return true } -// EncodeRLP implements rlp.Encoder -func (tx *Transaction) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, &tx.data) -} - -// DecodeRLP implements rlp.Decoder -func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { - _, size, _ := s.Kind() - err := s.Decode(&tx.data) - if err == nil { - tx.size.Store(common.StorageSize(rlp.ListSize(size))) - tx.time = time.Now() +// Protected says whether the transaction is replay-protected. +func (tx *Transaction) Protected() bool { + switch tx := tx.inner.(type) { + case *LegacyTx: + return tx.V != nil && isProtectedV(tx.V) + default: + return true } - return err } -// MarshalJSON encodes the web3 RPC transaction format. -func (tx *Transaction) MarshalJSON() ([]byte, error) { - hash := tx.Hash() - data := tx.data - data.Hash = &hash - return data.MarshalJSON() +// Type returns the transaction type. +func (tx *Transaction) Type() uint8 { + return tx.inner.txType() } -// UnmarshalJSON decodes the web3 RPC transaction format. -func (tx *Transaction) UnmarshalJSON(input []byte) error { - var dec txdata - if err := dec.UnmarshalJSON(input); err != nil { - return err - } - withSignature := dec.V.Sign() != 0 || dec.R.Sign() != 0 || dec.S.Sign() != 0 - if withSignature { - var V byte - if isProtectedV(dec.V) { - chainID := deriveChainId(dec.V).Uint64() - V = byte(dec.V.Uint64() - 35 - 2*chainID) - } else { - V = byte(dec.V.Uint64() - 27) - } - if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) { - return ErrInvalidSig - } - } - *tx = Transaction{ - data: dec, - time: time.Now(), - } - return nil +// ChainId returns the EIP155 chain ID of the transaction. The return value will always be +// non-nil. For legacy transactions which are not replay-protected, the return value is +// zero. +func (tx *Transaction) ChainId() *big.Int { + return tx.inner.chainID() } -func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } -func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit } -func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) } -func (tx *Transaction) GasPriceCmp(other *Transaction) int { - return tx.data.Price.Cmp(other.data.Price) -} -func (tx *Transaction) GasPriceIntCmp(other *big.Int) int { - return tx.data.Price.Cmp(other) -} -func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) } -func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce } -func (tx *Transaction) CheckNonce() bool { return true } +// Data returns the input data of the transaction. +func (tx *Transaction) Data() []byte { return tx.inner.data() } + +// AccessList returns the access list of the transaction. +func (tx *Transaction) AccessList() AccessList { return tx.inner.accessList() } + +// Gas returns the gas limit of the transaction. +func (tx *Transaction) Gas() uint64 { return tx.inner.gas() } + +// GasPrice returns the gas price of the transaction. +func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.inner.gasPrice()) } + +// Value returns the ether amount of the transaction. +func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) } + +// Nonce returns the sender account nonce of the transaction. +func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() } // To returns the recipient address of the transaction. -// It returns nil if the transaction is a contract creation. +// For contract-creation transactions, To returns nil. func (tx *Transaction) To() *common.Address { - if tx.data.Recipient == nil { + // Copy the pointed-to address. + ito := tx.inner.to() + if ito == nil { return nil } - to := *tx.data.Recipient - return &to + cpy := *ito + return &cpy +} + +// Cost returns gas * gasPrice + value. +func (tx *Transaction) Cost() *big.Int { + total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas())) + total.Add(total, tx.Value()) + return total +} + +// RawSignatureValues returns the V, R, S signature values of the transaction. +// The return values should not be modified by the caller. +func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) { + return tx.inner.rawSignatureValues() +} + +// GasPriceCmp compares the gas prices of two transactions. +func (tx *Transaction) GasPriceCmp(other *Transaction) int { + return tx.inner.gasPrice().Cmp(other.inner.gasPrice()) } -// Hash hashes the RLP encoding of tx. -// It uniquely identifies the transaction. +// GasPriceIntCmp compares the gas price of the transaction against the given price. +func (tx *Transaction) GasPriceIntCmp(other *big.Int) int { + return tx.inner.gasPrice().Cmp(other) +} + +// Hash returns the transaction hash. func (tx *Transaction) Hash() common.Hash { if hash := tx.hash.Load(); hash != nil { return hash.(common.Hash) } - v := rlpHash(tx) - tx.hash.Store(v) - return v + + var h common.Hash + if tx.Type() == LegacyTxType { + h = rlpHash(tx.inner) + } else { + h = prefixedRlpHash(tx.Type(), tx.inner) + } + tx.hash.Store(h) + return h } // Size returns the true RLP encoded storage size of the transaction, either by -// encoding and returning it, or returning a previsouly cached value. +// encoding and returning it, or returning a previously cached value. func (tx *Transaction) Size() common.StorageSize { if size := tx.size.Load(); size != nil { return size.(common.StorageSize) } c := writeCounter(0) - rlp.Encode(&c, &tx.data) + rlp.Encode(&c, &tx.inner) tx.size.Store(common.StorageSize(c)) return common.StorageSize(c) } -// AsMessage returns the transaction as a core.Message. -// -// AsMessage requires a signer to derive the sender. -// -// XXX Rename message to something less arbitrary? -func (tx *Transaction) AsMessage(s Signer) (Message, error) { - msg := Message{ - nonce: tx.data.AccountNonce, - gasLimit: tx.data.GasLimit, - gasPrice: new(big.Int).Set(tx.data.Price), - to: tx.data.Recipient, - amount: tx.data.Amount, - data: tx.data.Payload, - checkNonce: true, - } - - var err error - msg.from, err = Sender(s, tx) - return msg, err -} - // WithSignature returns a new transaction with the given signature. // This signature needs to be in the [R || S || V] format where V is 0 or 1. func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) { @@ -252,40 +336,27 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e if err != nil { return nil, err } - cpy := &Transaction{ - data: tx.data, - time: tx.time, - } - cpy.data.R, cpy.data.S, cpy.data.V = r, s, v - return cpy, nil + cpy := tx.inner.copy() + cpy.setSignatureValues(signer.ChainID(), v, r, s) + return &Transaction{inner: cpy, time: tx.time}, nil } -// Cost returns amount + gasprice * gaslimit. -func (tx *Transaction) Cost() *big.Int { - total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit)) - total.Add(total, tx.data.Amount) - return total -} - -// RawSignatureValues returns the V, R, S signature values of the transaction. -// The return values should not be modified by the caller. -func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) { - return tx.data.V, tx.data.R, tx.data.S -} - -// Transactions is a Transaction slice type for basic sorting. +// Transactions implements DerivableList for transactions. type Transactions []*Transaction // Len returns the length of s. func (s Transactions) Len() int { return len(s) } -// Swap swaps the i'th and the j'th element in s. -func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -// GetRlp implements Rlpable and returns the i'th element of s in rlp. -func (s Transactions) GetRlp(i int) []byte { - enc, _ := rlp.EncodeToBytes(s[i]) - return enc +// EncodeIndex encodes the i'th transaction to w. Note that this does not check for errors +// because we assume that *Transaction will only ever contain valid txs that were either +// constructed by decoding or via public API in this package. +func (s Transactions) EncodeIndex(i int, w *bytes.Buffer) { + tx := s[i] + if tx.Type() == LegacyTxType { + rlp.Encode(w, tx.inner) + } else { + tx.encodeTyped(w) + } } // TxDifference returns a new set which is the difference between a and b. @@ -312,7 +383,7 @@ func TxDifference(a, b Transactions) Transactions { type TxByNonce Transactions func (s TxByNonce) Len() int { return len(s) } -func (s TxByNonce) Less(i, j int) bool { return s[i].data.AccountNonce < s[j].data.AccountNonce } +func (s TxByNonce) Less(i, j int) bool { return s[i].Nonce() < s[j].Nonce() } func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // TxByPriceAndTime implements both the sort and the heap interface, making it useful @@ -323,7 +394,7 @@ func (s TxByPriceAndTime) Len() int { return len(s) } func (s TxByPriceAndTime) Less(i, j int) bool { // If the prices are equal, use the time the transaction was first seen for // deterministic sorting - cmp := s[i].data.Price.Cmp(s[j].data.Price) + cmp := s[i].GasPrice().Cmp(s[j].GasPrice()) if cmp == 0 { return s[i].time.Before(s[j].time) } @@ -361,13 +432,13 @@ func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transa // Initialize a price and received time based heap with the head transactions heads := make(TxByPriceAndTime, 0, len(txs)) for from, accTxs := range txs { - heads = append(heads, accTxs[0]) // Ensure the sender address is from the signer - acc, _ := Sender(signer, accTxs[0]) - txs[acc] = accTxs[1:] - if from != acc { + if acc, _ := Sender(signer, accTxs[0]); acc != from { delete(txs, from) + continue } + heads = append(heads, accTxs[0]) + txs[from] = accTxs[1:] } heap.Init(&heads) @@ -416,10 +487,11 @@ type Message struct { gasLimit uint64 gasPrice *big.Int data []byte + accessList AccessList checkNonce bool } -func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool) Message { +func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, accessList AccessList, checkNonce bool) Message { return Message{ from: from, to: to, @@ -428,15 +500,35 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b gasLimit: gasLimit, gasPrice: gasPrice, data: data, + accessList: accessList, checkNonce: checkNonce, } } -func (m Message) From() common.Address { return m.from } -func (m Message) To() *common.Address { return m.to } -func (m Message) GasPrice() *big.Int { return m.gasPrice } -func (m Message) Value() *big.Int { return m.amount } -func (m Message) Gas() uint64 { return m.gasLimit } -func (m Message) Nonce() uint64 { return m.nonce } -func (m Message) Data() []byte { return m.data } -func (m Message) CheckNonce() bool { return m.checkNonce } +// AsMessage returns the transaction as a core.Message. +func (tx *Transaction) AsMessage(s Signer) (Message, error) { + msg := Message{ + nonce: tx.Nonce(), + gasLimit: tx.Gas(), + gasPrice: new(big.Int).Set(tx.GasPrice()), + to: tx.To(), + amount: tx.Value(), + data: tx.Data(), + accessList: tx.AccessList(), + checkNonce: true, + } + + var err error + msg.from, err = Sender(s, tx) + return msg, err +} + +func (m Message) From() common.Address { return m.from } +func (m Message) To() *common.Address { return m.to } +func (m Message) GasPrice() *big.Int { return m.gasPrice } +func (m Message) Value() *big.Int { return m.amount } +func (m Message) Gas() uint64 { return m.gasLimit } +func (m Message) Nonce() uint64 { return m.nonce } +func (m Message) Data() []byte { return m.data } +func (m Message) AccessList() AccessList { return m.accessList } +func (m Message) CheckNonce() bool { return m.checkNonce } diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go new file mode 100644 index 0000000000..184a17d5b5 --- /dev/null +++ b/core/types/transaction_marshalling.go @@ -0,0 +1,187 @@ +package types + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// txJSON is the JSON representation of transactions. +type txJSON struct { + Type hexutil.Uint64 `json:"type"` + + // Common transaction fields: + Nonce *hexutil.Uint64 `json:"nonce"` + GasPrice *hexutil.Big `json:"gasPrice"` + Gas *hexutil.Uint64 `json:"gas"` + Value *hexutil.Big `json:"value"` + Data *hexutil.Bytes `json:"input"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` + To *common.Address `json:"to"` + + // Access list transaction fields: + ChainID *hexutil.Big `json:"chainId,omitempty"` + AccessList *AccessList `json:"accessList,omitempty"` + + // Only used for encoding: + Hash common.Hash `json:"hash"` +} + +// MarshalJSON marshals as JSON with a hash. +func (t *Transaction) MarshalJSON() ([]byte, error) { + var enc txJSON + // These are set for all tx types. + enc.Hash = t.Hash() + enc.Type = hexutil.Uint64(t.Type()) + + // Other fields are set conditionally depending on tx type. + switch tx := t.inner.(type) { + case *LegacyTx: + enc.Nonce = (*hexutil.Uint64)(&tx.Nonce) + enc.Gas = (*hexutil.Uint64)(&tx.Gas) + enc.GasPrice = (*hexutil.Big)(tx.GasPrice) + enc.Value = (*hexutil.Big)(tx.Value) + enc.Data = (*hexutil.Bytes)(&tx.Data) + enc.To = t.To() + enc.V = (*hexutil.Big)(tx.V) + enc.R = (*hexutil.Big)(tx.R) + enc.S = (*hexutil.Big)(tx.S) + case *AccessListTx: + enc.ChainID = (*hexutil.Big)(tx.ChainID) + enc.AccessList = &tx.AccessList + enc.Nonce = (*hexutil.Uint64)(&tx.Nonce) + enc.Gas = (*hexutil.Uint64)(&tx.Gas) + enc.GasPrice = (*hexutil.Big)(tx.GasPrice) + enc.Value = (*hexutil.Big)(tx.Value) + enc.Data = (*hexutil.Bytes)(&tx.Data) + enc.To = t.To() + enc.V = (*hexutil.Big)(tx.V) + enc.R = (*hexutil.Big)(tx.R) + enc.S = (*hexutil.Big)(tx.S) + } + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (t *Transaction) UnmarshalJSON(input []byte) error { + var dec txJSON + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + + // Decode / verify fields according to transaction type. + var inner TxData + switch dec.Type { + case LegacyTxType: + var itx LegacyTx + inner = &itx + if dec.To != nil { + itx.To = dec.To + } + if dec.Nonce == nil { + return errors.New("missing required field 'nonce' in transaction") + } + itx.Nonce = uint64(*dec.Nonce) + if dec.GasPrice == nil { + return errors.New("missing required field 'gasPrice' in transaction") + } + itx.GasPrice = (*big.Int)(dec.GasPrice) + if dec.Gas == nil { + return errors.New("missing required field 'gas' in transaction") + } + itx.Gas = uint64(*dec.Gas) + if dec.Value == nil { + return errors.New("missing required field 'value' in transaction") + } + itx.Value = (*big.Int)(dec.Value) + if dec.Data == nil { + return errors.New("missing required field 'input' in transaction") + } + itx.Data = *dec.Data + if dec.V == nil { + return errors.New("missing required field 'v' in transaction") + } + itx.V = (*big.Int)(dec.V) + if dec.R == nil { + return errors.New("missing required field 'r' in transaction") + } + itx.R = (*big.Int)(dec.R) + if dec.S == nil { + return errors.New("missing required field 's' in transaction") + } + itx.S = (*big.Int)(dec.S) + withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 + if withSignature { + if err := sanityCheckSignature(itx.V, itx.R, itx.S, true); err != nil { + return err + } + } + + case AccessListTxType: + var itx AccessListTx + inner = &itx + // Access list is optional for now. + if dec.AccessList != nil { + itx.AccessList = *dec.AccessList + } + if dec.ChainID == nil { + return errors.New("missing required field 'chainId' in transaction") + } + itx.ChainID = (*big.Int)(dec.ChainID) + if dec.To != nil { + itx.To = dec.To + } + if dec.Nonce == nil { + return errors.New("missing required field 'nonce' in transaction") + } + itx.Nonce = uint64(*dec.Nonce) + if dec.GasPrice == nil { + return errors.New("missing required field 'gasPrice' in transaction") + } + itx.GasPrice = (*big.Int)(dec.GasPrice) + if dec.Gas == nil { + return errors.New("missing required field 'gas' in transaction") + } + itx.Gas = uint64(*dec.Gas) + if dec.Value == nil { + return errors.New("missing required field 'value' in transaction") + } + itx.Value = (*big.Int)(dec.Value) + if dec.Data == nil { + return errors.New("missing required field 'input' in transaction") + } + itx.Data = *dec.Data + if dec.V == nil { + return errors.New("missing required field 'v' in transaction") + } + itx.V = (*big.Int)(dec.V) + if dec.R == nil { + return errors.New("missing required field 'r' in transaction") + } + itx.R = (*big.Int)(dec.R) + if dec.S == nil { + return errors.New("missing required field 's' in transaction") + } + itx.S = (*big.Int)(dec.S) + withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 + if withSignature { + if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil { + return err + } + } + + default: + return ErrTxTypeNotSupported + } + + // Now set the inner transaction. + t.setDecoded(inner, 0) + + // TODO: check hash here? + return nil +} diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index cf754856fb..92ba21296f 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -27,9 +27,7 @@ import ( "github.com/ethereum/go-ethereum/params/types/ctypes" ) -var ( - ErrInvalidChainId = errors.New("invalid chain id for signer") -) +var ErrInvalidChainId = errors.New("invalid chain id for signer") // sigCache is used to cache the derived sender and contains // the signer used to derive it. @@ -42,6 +40,8 @@ type sigCache struct { func MakeSigner(config ctypes.ChainConfigurator, blockNumber *big.Int) Signer { var signer Signer switch { + case config.IsEnabled(config.GetEIP2930Transition, blockNumber): + signer = NewEIP2930Signer(config.GetChainID()) case config.IsEnabled(config.GetEIP155Transition, blockNumber): signer = NewEIP155Signer(config.GetChainID()) case config.IsEnabled(config.GetEIP2Transition, blockNumber): @@ -52,7 +52,40 @@ func MakeSigner(config ctypes.ChainConfigurator, blockNumber *big.Int) Signer { return signer } -// SignTx signs the transaction using the given signer and private key +// LatestSigner returns the 'most permissive' Signer available for the given chain +// configuration. Specifically, this enables support of EIP-155 replay protection and +// EIP-2930 access list transactions when their respective forks are scheduled to occur at +// any block number in the chain config. +// +// Use this in transaction-handling code where the current block number is unknown. If you +// have the current block number available, use MakeSigner instead. +func LatestSigner(config ctypes.ChainConfigurator) Signer { + if chainID := config.GetChainID(); chainID != nil { + if config.GetEIP2930Transition() != nil { + return NewEIP2930Signer(chainID) + } + if config.GetEIP155Transition() != nil { + return NewEIP155Signer(chainID) + } + } + return HomesteadSigner{} +} + +// LatestSignerForChainID returns the 'most permissive' Signer available. Specifically, +// this enables support for EIP-155 replay protection and all implemented EIP-2718 +// transaction types if chainID is non-nil. +// +// Use this in transaction-handling code where the current block number and fork +// configuration are unknown. If you have a ChainConfig, use LatestSigner instead. +// If you have a ChainConfig and know the current block number, use MakeSigner instead. +func LatestSignerForChainID(chainID *big.Int) Signer { + if chainID == nil { + return HomesteadSigner{} + } + return NewEIP2930Signer(chainID) +} + +// SignTx signs the transaction using the given signer and private key. func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, error) { h := s.Hash(tx) sig, err := crypto.Sign(h[:], prv) @@ -62,6 +95,27 @@ func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, err return tx.WithSignature(s, sig) } +// SignNewTx creates a transaction and signs it. +func SignNewTx(prv *ecdsa.PrivateKey, s Signer, txdata TxData) (*Transaction, error) { + tx := NewTx(txdata) + h := s.Hash(tx) + sig, err := crypto.Sign(h[:], prv) + if err != nil { + return nil, err + } + return tx.WithSignature(s, sig) +} + +// MustSignNewTx creates a transaction and signs it. +// This panics if the transaction cannot be signed. +func MustSignNewTx(prv *ecdsa.PrivateKey, s Signer, txdata TxData) *Transaction { + tx, err := SignNewTx(prv, s, txdata) + if err != nil { + panic(fmt.Errorf("%w: signer=%T", err, s)) + } + return tx +} + // Sender returns the address derived from the signature (V, R, S) using secp256k1 // elliptic curve and an error if it failed deriving or upon an incorrect // signature. @@ -88,21 +142,124 @@ func Sender(signer Signer, tx *Transaction) (common.Address, error) { return addr, nil } -// Signer encapsulates transaction signature handling. Note that this interface is not a -// stable API and may change at any time to accommodate new protocol rules. +// Signer encapsulates transaction signature handling. The name of this type is slightly +// misleading because Signers don't actually sign, they're just for validating and +// processing of signatures. +// +// Note that this interface is not a stable API and may change at any time to accommodate +// new protocol rules. type Signer interface { // Sender returns the sender address of the transaction. Sender(tx *Transaction) (common.Address, error) + // SignatureValues returns the raw R, S, V values corresponding to the // given signature. SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) - // Hash returns the hash to be signed. + ChainID() *big.Int + + // Hash returns 'signature hash', i.e. the transaction hash that is signed by the + // private key. This hash does not uniquely identify the transaction. Hash(tx *Transaction) common.Hash + // Equal returns true if the given signer is the same as the receiver. Equal(Signer) bool } -// EIP155Transaction implements Signer using the EIP155 rules. +type eip2930Signer struct{ EIP155Signer } + +// NewEIP2930Signer returns a signer that accepts EIP-2930 access list transactions, +// EIP-155 replay protected transactions, and legacy Homestead transactions. +func NewEIP2930Signer(chainId *big.Int) Signer { + return eip2930Signer{NewEIP155Signer(chainId)} +} + +func (s eip2930Signer) ChainID() *big.Int { + return s.chainId +} + +func (s eip2930Signer) Equal(s2 Signer) bool { + x, ok := s2.(eip2930Signer) + return ok && x.chainId.Cmp(s.chainId) == 0 +} + +func (s eip2930Signer) Sender(tx *Transaction) (common.Address, error) { + V, R, S := tx.RawSignatureValues() + switch tx.Type() { + case LegacyTxType: + if !tx.Protected() { + return HomesteadSigner{}.Sender(tx) + } + V = new(big.Int).Sub(V, s.chainIdMul) + V.Sub(V, big8) + case AccessListTxType: + // ACL txs are defined to use 0 and 1 as their recovery id, add + // 27 to become equivalent to unprotected Homestead signatures. + V = new(big.Int).Add(V, big.NewInt(27)) + default: + return common.Address{}, ErrTxTypeNotSupported + } + if tx.ChainId().Cmp(s.chainId) != 0 { + return common.Address{}, fmt.Errorf("%w: want: %d, got: %d", ErrInvalidChainId, s.chainId, tx.ChainId()) + } + return recoverPlain(s.Hash(tx), R, S, V, true) +} + +func (s eip2930Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { + switch txdata := tx.inner.(type) { + case *LegacyTx: + return s.EIP155Signer.SignatureValues(tx, sig) + case *AccessListTx: + // Check that chain ID of tx matches the signer. We also accept ID zero here, + // because it indicates that the chain ID was not specified in the tx. + if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 { + return nil, nil, nil, ErrInvalidChainId + } + R, S, _ = decodeSignature(sig) + V = big.NewInt(int64(sig[64])) + default: + return nil, nil, nil, ErrTxTypeNotSupported + } + return R, S, V, nil +} + +// Hash returns the hash to be signed by the sender. +// It does not uniquely identify the transaction. +func (s eip2930Signer) Hash(tx *Transaction) common.Hash { + switch tx.Type() { + case LegacyTxType: + return rlpHash([]interface{}{ + tx.Nonce(), + tx.GasPrice(), + tx.Gas(), + tx.To(), + tx.Value(), + tx.Data(), + s.chainId, uint(0), uint(0), + }) + case AccessListTxType: + return prefixedRlpHash( + tx.Type(), + []interface{}{ + s.chainId, + tx.Nonce(), + tx.GasPrice(), + tx.Gas(), + tx.To(), + tx.Value(), + tx.Data(), + tx.AccessList(), + }) + default: + // This _should_ not happen, but in case someone sends in a bad + // json struct via RPC, it's probably more prudent to return an + // empty hash instead of killing the node with a panic + //panic("Unsupported transaction type: %d", tx.typ) + return common.Hash{} + } +} + +// EIP155Signer implements Signer using the EIP-155 rules. This accepts transactions which +// are replay-protected as well as unprotected homestead transactions. type EIP155Signer struct { chainId, chainIdMul *big.Int } @@ -117,6 +274,10 @@ func NewEIP155Signer(chainId *big.Int) EIP155Signer { } } +func (s EIP155Signer) ChainID() *big.Int { + return s.chainId +} + func (s EIP155Signer) Equal(s2 Signer) bool { eip155, ok := s2.(EIP155Signer) return ok && eip155.chainId.Cmp(s.chainId) == 0 @@ -125,24 +286,28 @@ func (s EIP155Signer) Equal(s2 Signer) bool { var big8 = big.NewInt(8) func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) { + if tx.Type() != LegacyTxType { + return common.Address{}, ErrTxTypeNotSupported + } if !tx.Protected() { return HomesteadSigner{}.Sender(tx) } if tx.ChainId().Cmp(s.chainId) != 0 { return common.Address{}, ErrInvalidChainId } - V := new(big.Int).Sub(tx.data.V, s.chainIdMul) + V, R, S := tx.RawSignatureValues() + V = new(big.Int).Sub(V, s.chainIdMul) V.Sub(V, big8) - return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true) + return recoverPlain(s.Hash(tx), R, S, V, true) } // SignatureValues returns signature values. This signature // needs to be in the [R || S || V] format where V is 0 or 1. func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { - R, S, V, err = HomesteadSigner{}.SignatureValues(tx, sig) - if err != nil { - return nil, nil, nil, err + if tx.Type() != LegacyTxType { + return nil, nil, nil, ErrTxTypeNotSupported } + R, S, V = decodeSignature(sig) if s.chainId.Sign() != 0 { V = big.NewInt(int64(sig[64] + 35)) V.Add(V, s.chainIdMul) @@ -154,12 +319,12 @@ func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big // It does not uniquely identify the transaction. func (s EIP155Signer) Hash(tx *Transaction) common.Hash { return rlpHash([]interface{}{ - tx.data.AccountNonce, - tx.data.Price, - tx.data.GasLimit, - tx.data.Recipient, - tx.data.Amount, - tx.data.Payload, + tx.Nonce(), + tx.GasPrice(), + tx.Gas(), + tx.To(), + tx.Value(), + tx.Data(), s.chainId, uint(0), uint(0), }) } @@ -168,6 +333,10 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash { // homestead rules. type HomesteadSigner struct{ FrontierSigner } +func (s HomesteadSigner) ChainID() *big.Int { + return nil +} + func (s HomesteadSigner) Equal(s2 Signer) bool { _, ok := s2.(HomesteadSigner) return ok @@ -180,25 +349,39 @@ func (hs HomesteadSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v } func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) { - return recoverPlain(hs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, true) + if tx.Type() != LegacyTxType { + return common.Address{}, ErrTxTypeNotSupported + } + v, r, s := tx.RawSignatureValues() + return recoverPlain(hs.Hash(tx), r, s, v, true) } type FrontierSigner struct{} +func (s FrontierSigner) ChainID() *big.Int { + return nil +} + func (s FrontierSigner) Equal(s2 Signer) bool { _, ok := s2.(FrontierSigner) return ok } +func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) { + if tx.Type() != LegacyTxType { + return common.Address{}, ErrTxTypeNotSupported + } + v, r, s := tx.RawSignatureValues() + return recoverPlain(fs.Hash(tx), r, s, v, false) +} + // SignatureValues returns signature values. This signature // needs to be in the [R || S || V] format where V is 0 or 1. func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) { - if len(sig) != crypto.SignatureLength { - panic(fmt.Sprintf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength)) + if tx.Type() != LegacyTxType { + return nil, nil, nil, ErrTxTypeNotSupported } - r = new(big.Int).SetBytes(sig[:32]) - s = new(big.Int).SetBytes(sig[32:64]) - v = new(big.Int).SetBytes([]byte{sig[64] + 27}) + r, s, v = decodeSignature(sig) return r, s, v, nil } @@ -206,17 +389,23 @@ func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v * // It does not uniquely identify the transaction. func (fs FrontierSigner) Hash(tx *Transaction) common.Hash { return rlpHash([]interface{}{ - tx.data.AccountNonce, - tx.data.Price, - tx.data.GasLimit, - tx.data.Recipient, - tx.data.Amount, - tx.data.Payload, + tx.Nonce(), + tx.GasPrice(), + tx.Gas(), + tx.To(), + tx.Value(), + tx.Data(), }) } -func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) { - return recoverPlain(fs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, false) +func decodeSignature(sig []byte) (r, s, v *big.Int) { + if len(sig) != crypto.SignatureLength { + panic(fmt.Sprintf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength)) + } + r = new(big.Int).SetBytes(sig[:32]) + s = new(big.Int).SetBytes(sig[32:64]) + v = new(big.Int).SetBytes([]byte{sig[64] + 27}) + return r, s, v } func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) { diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index 159cb0c4c4..b45e5836c7 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -20,7 +20,10 @@ import ( "bytes" "crypto/ecdsa" "encoding/json" + "errors" + "fmt" "math/big" + "reflect" "testing" "time" @@ -32,6 +35,8 @@ import ( // The values in those tests are from the Transaction Tests // at github.com/ethereum/tests. var ( + testAddr = common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b") + emptyTx = NewTransaction( 0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), @@ -41,7 +46,7 @@ var ( rightvrsTx, _ = NewTransaction( 3, - common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"), + testAddr, big.NewInt(10), 2000, big.NewInt(1), @@ -50,8 +55,32 @@ var ( HomesteadSigner{}, common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a301"), ) + + emptyEip2718Tx = NewTx(&AccessListTx{ + ChainID: big.NewInt(1), + Nonce: 3, + To: &testAddr, + Value: big.NewInt(10), + Gas: 25000, + GasPrice: big.NewInt(1), + Data: common.FromHex("5544"), + }) + + signedEip2718Tx, _ = emptyEip2718Tx.WithSignature( + NewEIP2930Signer(big.NewInt(1)), + common.Hex2Bytes("c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b266032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d3752101"), + ) ) +func TestDecodeEmptyTypedTx(t *testing.T) { + input := []byte{0x80} + var tx Transaction + err := rlp.DecodeBytes(input, &tx) + if err != errEmptyTypedTx { + t.Fatal("wrong error:", err) + } +} + func TestTransactionSigHash(t *testing.T) { var homestead HomesteadSigner if homestead.Hash(emptyTx) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") { @@ -73,10 +102,121 @@ func TestTransactionEncode(t *testing.T) { } } +func TestEIP2718TransactionSigHash(t *testing.T) { + s := NewEIP2930Signer(big.NewInt(1)) + if s.Hash(emptyEip2718Tx) != common.HexToHash("49b486f0ec0a60dfbbca2d30cb07c9e8ffb2a2ff41f29a1ab6737475f6ff69f3") { + t.Errorf("empty EIP-2718 transaction hash mismatch, got %x", s.Hash(emptyEip2718Tx)) + } + if s.Hash(signedEip2718Tx) != common.HexToHash("49b486f0ec0a60dfbbca2d30cb07c9e8ffb2a2ff41f29a1ab6737475f6ff69f3") { + t.Errorf("signed EIP-2718 transaction hash mismatch, got %x", s.Hash(signedEip2718Tx)) + } +} + +// This test checks signature operations on access list transactions. +func TestEIP2930Signer(t *testing.T) { + + var ( + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + keyAddr = crypto.PubkeyToAddress(key.PublicKey) + signer1 = NewEIP2930Signer(big.NewInt(1)) + signer2 = NewEIP2930Signer(big.NewInt(2)) + tx0 = NewTx(&AccessListTx{Nonce: 1}) + tx1 = NewTx(&AccessListTx{ChainID: big.NewInt(1), Nonce: 1}) + tx2, _ = SignNewTx(key, signer2, &AccessListTx{ChainID: big.NewInt(2), Nonce: 1}) + ) + + tests := []struct { + tx *Transaction + signer Signer + wantSignerHash common.Hash + wantSenderErr error + wantSignErr error + wantHash common.Hash // after signing + }{ + { + tx: tx0, + signer: signer1, + wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"), + wantSenderErr: ErrInvalidChainId, + wantHash: common.HexToHash("1ccd12d8bbdb96ea391af49a35ab641e219b2dd638dea375f2bc94dd290f2549"), + }, + { + tx: tx1, + signer: signer1, + wantSenderErr: ErrInvalidSig, + wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"), + wantHash: common.HexToHash("1ccd12d8bbdb96ea391af49a35ab641e219b2dd638dea375f2bc94dd290f2549"), + }, + { + // This checks what happens when trying to sign an unsigned tx for the wrong chain. + tx: tx1, + signer: signer2, + wantSenderErr: ErrInvalidChainId, + wantSignerHash: common.HexToHash("367967247499343401261d718ed5aa4c9486583e4d89251afce47f4a33c33362"), + wantSignErr: ErrInvalidChainId, + }, + { + // This checks what happens when trying to re-sign a signed tx for the wrong chain. + tx: tx2, + signer: signer1, + wantSenderErr: ErrInvalidChainId, + wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"), + wantSignErr: ErrInvalidChainId, + }, + } + + for i, test := range tests { + sigHash := test.signer.Hash(test.tx) + if sigHash != test.wantSignerHash { + t.Errorf("test %d: wrong sig hash: got %x, want %x", i, sigHash, test.wantSignerHash) + } + sender, err := Sender(test.signer, test.tx) + if !errors.Is(err, test.wantSenderErr) { + t.Errorf("test %d: wrong Sender error: got: %q, want: %q", i, err, test.wantSenderErr) + } + if err == nil && sender != keyAddr { + t.Errorf("test %d: wrong sender address %x", i, sender) + } + signedTx, err := SignTx(test.tx, test.signer, key) + if err != test.wantSignErr { + t.Fatalf("test %d: wrong SignTx error %q", i, err) + } + if signedTx != nil { + if signedTx.Hash() != test.wantHash { + t.Errorf("test %d: wrong tx hash after signing: got %x, want %x", i, signedTx.Hash(), test.wantHash) + } + } + } +} + +func TestEIP2718TransactionEncode(t *testing.T) { + // RLP representation + { + have, err := rlp.EncodeToBytes(signedEip2718Tx) + if err != nil { + t.Fatalf("encode error: %v", err) + } + want := common.FromHex("b86601f8630103018261a894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a825544c001a0c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b2660a032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d37521") + if !bytes.Equal(have, want) { + t.Errorf("encoded RLP mismatch, got %x", have) + } + } + // Binary representation + { + have, err := signedEip2718Tx.MarshalBinary() + if err != nil { + t.Fatalf("encode error: %v", err) + } + want := common.FromHex("01f8630103018261a894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a825544c001a0c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b2660a032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d37521") + if !bytes.Equal(have, want) { + t.Errorf("encoded RLP mismatch, got %x", have) + } + } +} + func decodeTx(data []byte) (*Transaction, error) { var tx Transaction t, err := &tx, rlp.Decode(bytes.NewReader(data), &tx) - return t, err } @@ -219,50 +359,125 @@ func TestTransactionTimeSort(t *testing.T) { } } -// TestTransactionJSON tests serializing/de-serializing to/from JSON. -func TestTransactionJSON(t *testing.T) { +// TestTransactionCoding tests serializing/de-serializing to/from rlp and JSON. +func TestTransactionCoding(t *testing.T) { key, err := crypto.GenerateKey() if err != nil { t.Fatalf("could not generate key: %v", err) } - signer := NewEIP155Signer(common.Big1) - - transactions := make([]*Transaction, 0, 50) - for i := uint64(0); i < 25; i++ { - var tx *Transaction - switch i % 2 { + var ( + signer = NewEIP2930Signer(common.Big1) + addr = common.HexToAddress("0x0000000000000000000000000000000000000001") + recipient = common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87") + accesses = AccessList{{Address: addr, StorageKeys: []common.Hash{{0}}}} + ) + for i := uint64(0); i < 500; i++ { + var txdata TxData + switch i % 5 { case 0: - tx = NewTransaction(i, common.Address{1}, common.Big0, 1, common.Big2, []byte("abcdef")) + // Legacy tx. + txdata = &LegacyTx{ + Nonce: i, + To: &recipient, + Gas: 1, + GasPrice: big.NewInt(2), + Data: []byte("abcdef"), + } case 1: - tx = NewContractCreation(i, common.Big0, 1, common.Big2, []byte("abcdef")) + // Legacy tx contract creation. + txdata = &LegacyTx{ + Nonce: i, + Gas: 1, + GasPrice: big.NewInt(2), + Data: []byte("abcdef"), + } + case 2: + // Tx with non-zero access list. + txdata = &AccessListTx{ + ChainID: big.NewInt(1), + Nonce: i, + To: &recipient, + Gas: 123457, + GasPrice: big.NewInt(10), + AccessList: accesses, + Data: []byte("abcdef"), + } + case 3: + // Tx with empty access list. + txdata = &AccessListTx{ + ChainID: big.NewInt(1), + Nonce: i, + To: &recipient, + Gas: 123457, + GasPrice: big.NewInt(10), + Data: []byte("abcdef"), + } + case 4: + // Contract creation with access list. + txdata = &AccessListTx{ + ChainID: big.NewInt(1), + Nonce: i, + Gas: 123457, + GasPrice: big.NewInt(10), + AccessList: accesses, + } } - transactions = append(transactions, tx) - - signedTx, err := SignTx(tx, signer, key) + tx, err := SignNewTx(key, signer, txdata) if err != nil { t.Fatalf("could not sign transaction: %v", err) } - - transactions = append(transactions, signedTx) - } - - for _, tx := range transactions { - data, err := json.Marshal(tx) + // RLP + parsedTx, err := encodeDecodeBinary(tx) if err != nil { - t.Fatalf("json.Marshal failed: %v", err) + t.Fatal(err) } + assertEqual(parsedTx, tx) - var parsedTx *Transaction - if err := json.Unmarshal(data, &parsedTx); err != nil { - t.Fatalf("json.Unmarshal failed: %v", err) + // JSON + parsedTx, err = encodeDecodeJSON(tx) + if err != nil { + t.Fatal(err) } + assertEqual(parsedTx, tx) + } +} - // compare nonce, price, gaslimit, recipient, amount, payload, V, R, S - if tx.Hash() != parsedTx.Hash() { - t.Errorf("parsed tx differs from original tx, want %v, got %v", tx, parsedTx) - } - if tx.ChainId().Cmp(parsedTx.ChainId()) != 0 { - t.Errorf("invalid chain id, want %d, got %d", tx.ChainId(), parsedTx.ChainId()) +func encodeDecodeJSON(tx *Transaction) (*Transaction, error) { + data, err := json.Marshal(tx) + if err != nil { + return nil, fmt.Errorf("json encoding failed: %v", err) + } + var parsedTx = &Transaction{} + if err := json.Unmarshal(data, &parsedTx); err != nil { + return nil, fmt.Errorf("json decoding failed: %v", err) + } + return parsedTx, nil +} + +func encodeDecodeBinary(tx *Transaction) (*Transaction, error) { + data, err := tx.MarshalBinary() + if err != nil { + return nil, fmt.Errorf("rlp encoding failed: %v", err) + } + var parsedTx = &Transaction{} + if err := parsedTx.UnmarshalBinary(data); err != nil { + return nil, fmt.Errorf("rlp decoding failed: %v", err) + } + return parsedTx, nil +} + +func assertEqual(orig *Transaction, cpy *Transaction) error { + // compare nonce, price, gaslimit, recipient, amount, payload, V, R, S + if want, got := orig.Hash(), cpy.Hash(); want != got { + return fmt.Errorf("parsed tx differs from original tx, want %v, got %v", want, got) + } + if want, got := orig.ChainId(), cpy.ChainId(); want.Cmp(got) != 0 { + return fmt.Errorf("invalid chain id, want %d, got %d", want, got) + } + if orig.AccessList() != nil { + if !reflect.DeepEqual(orig.AccessList(), cpy.AccessList()) { + return fmt.Errorf("access list wrong!") } } + return nil } diff --git a/core/vm/access_list_tracer.go b/core/vm/access_list_tracer.go new file mode 100644 index 0000000000..7388ade041 --- /dev/null +++ b/core/vm/access_list_tracer.go @@ -0,0 +1,181 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// accessList is an accumulator for the set of accounts and storage slots an EVM +// contract execution touches. +type accessList map[common.Address]accessListSlots + +// accessListSlots is an accumulator for the set of storage slots within a single +// contract that an EVM contract execution touches. +type accessListSlots map[common.Hash]struct{} + +// newAccessList creates a new accessList. +func newAccessList() accessList { + return make(map[common.Address]accessListSlots) +} + +// addAddress adds an address to the accesslist. +func (al accessList) addAddress(address common.Address) { + // Set address if not previously present + if _, present := al[address]; !present { + al[address] = make(map[common.Hash]struct{}) + } +} + +// addSlot adds a storage slot to the accesslist. +func (al accessList) addSlot(address common.Address, slot common.Hash) { + // Set address if not previously present + al.addAddress(address) + + // Set the slot on the surely existent storage set + al[address][slot] = struct{}{} +} + +// equal checks if the content of the current access list is the same as the +// content of the other one. +func (al accessList) equal(other accessList) bool { + // Cross reference the accounts first + if len(al) != len(other) { + return false + } + for addr := range al { + if _, ok := other[addr]; !ok { + return false + } + } + for addr := range other { + if _, ok := al[addr]; !ok { + return false + } + } + // Accounts match, cross reference the storage slots too + for addr, slots := range al { + otherslots := other[addr] + + if len(slots) != len(otherslots) { + return false + } + for hash := range slots { + if _, ok := otherslots[hash]; !ok { + return false + } + } + for hash := range otherslots { + if _, ok := slots[hash]; !ok { + return false + } + } + } + return true +} + +// accesslist converts the accesslist to a types.AccessList. +func (al accessList) accessList() types.AccessList { + acl := make(types.AccessList, 0, len(al)) + for addr, slots := range al { + tuple := types.AccessTuple{Address: addr} + for slot := range slots { + tuple.StorageKeys = append(tuple.StorageKeys, slot) + } + acl = append(acl, tuple) + } + return acl +} + +// AccessListTracer is a tracer that accumulates touched accounts and storage +// slots into an internal set. +type AccessListTracer struct { + excl map[common.Address]struct{} // Set of account to exclude from the list + list accessList // Set of accounts and storage slots touched +} + +// NewAccessListTracer creates a new tracer that can generate AccessLists. +// An optional AccessList can be specified to occupy slots and addresses in +// the resulting accesslist. +func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompiles []common.Address) *AccessListTracer { + excl := map[common.Address]struct{}{ + from: {}, to: {}, + } + for _, addr := range precompiles { + excl[addr] = struct{}{} + } + list := newAccessList() + for _, al := range acl { + if _, ok := excl[al.Address]; !ok { + list.addAddress(al.Address) + } + for _, slot := range al.StorageKeys { + list.addSlot(al.Address, slot) + } + } + return &AccessListTracer{ + excl: excl, + list: list, + } +} + +func (a *AccessListTracer) CapturePreEVM(env *EVM, inputs map[string]interface{}) { +} + +func (a *AccessListTracer) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +} + +// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist. +func (a *AccessListTracer) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { + stack := scope.Stack + if (op == SLOAD || op == SSTORE) && stack.len() >= 1 { + slot := common.Hash(stack.data[stack.len()-1].Bytes32()) + a.list.addSlot(scope.Contract.Address(), slot) + } + if (op == EXTCODECOPY || op == EXTCODEHASH || op == EXTCODESIZE || op == BALANCE || op == SELFDESTRUCT) && stack.len() >= 1 { + addr := common.Address(stack.data[stack.len()-1].Bytes20()) + if _, ok := a.excl[addr]; !ok { + a.list.addAddress(addr) + } + } + if (op == DELEGATECALL || op == CALL || op == STATICCALL || op == CALLCODE) && stack.len() >= 5 { + addr := common.Address(stack.data[stack.len()-2].Bytes20()) + if _, ok := a.excl[addr]; !ok { + a.list.addAddress(addr) + } + } +} + +func (*AccessListTracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { +} + +func (*AccessListTracer) CaptureEnd(env *EVM, output []byte, gasUsed uint64, t time.Duration, err error) { +} + +// AccessList returns the current accesslist maintained by the tracer. +func (a *AccessListTracer) AccessList() types.AccessList { + return a.list.accessList() +} + +// Equal returns if the content of two access list traces are equal. +func (a *AccessListTracer) Equal(other *AccessListTracer) bool { + return a.list.equal(other.list) +} diff --git a/core/vm/contract.go b/core/vm/contract.go index 915193d137..61dbd5007a 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -96,19 +96,6 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool { return c.isCode(udest) } -func (c *Contract) validJumpSubdest(udest uint64) bool { - // PC cannot go beyond len(code) and certainly can't be bigger than 63 bits. - // Don't bother checking for BEGINSUB in that case. - if int64(udest) < 0 || udest >= uint64(len(c.Code)) { - return false - } - // Only BEGINSUBs allowed for destinations - if OpCode(c.Code[udest]) != BEGINSUB { - return false - } - return c.isCode(udest) -} - // isCode returns true if the provided PC location is an actual opcode, as // opposed to a data-segment following a PUSHN operation. func (c *Contract) isCode(udest uint64) bool { diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 2aa2d392d2..b721c09a19 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -50,17 +50,34 @@ var basePrecompiledContracts = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{4}): &dataCopy{}, } +var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{ + common.BytesToAddress([]byte{10}): &bls12381G1Add{}, + common.BytesToAddress([]byte{11}): &bls12381G1Mul{}, + common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{}, + common.BytesToAddress([]byte{13}): &bls12381G2Add{}, + common.BytesToAddress([]byte{14}): &bls12381G2Mul{}, + common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{}, + common.BytesToAddress([]byte{16}): &bls12381Pairing{}, + common.BytesToAddress([]byte{17}): &bls12381MapG1{}, + common.BytesToAddress([]byte{18}): &bls12381MapG2{}, +} + +func mergeContracts(base, target map[common.Address]PrecompiledContract) { + for k, v := range target { + base[k] = v + } +} + // PrecompiledContractsForConfig returns a map containing valid precompiled contracts for a given point in a chain config. func PrecompiledContractsForConfig(config ctypes.ChainConfigurator, bn *big.Int) map[common.Address]PrecompiledContract { // Copying to a new map is necessary because assigning to the original map // creates a memory reference. Further, setting the vals to nil in case of nonconfiguration causes // a panic during tests because they run asynchronously (also a valid reason for using an explicit copy). precompileds := make(map[common.Address]PrecompiledContract) - for k, v := range basePrecompiledContracts { - precompileds[k] = v - } + mergeContracts(precompileds, basePrecompiledContracts) + if config.IsEnabled(config.GetEIP198Transition, bn) { - precompileds[common.BytesToAddress([]byte{5})] = &bigModExp{} + precompileds[common.BytesToAddress([]byte{5})] = &bigModExp{eip2565: config.IsEnabled(config.GetEIP2565Transition, bn)} } if config.IsEnabled(config.GetEIP213Transition, bn) { if config.IsEnabled(config.GetEIP1108Transition, bn) { @@ -82,15 +99,7 @@ func PrecompiledContractsForConfig(config ctypes.ChainConfigurator, bn *big.Int) precompileds[common.BytesToAddress([]byte{9})] = &blake2F{} } if config.IsEnabled(config.GetEIP2537Transition, bn) { - precompileds[common.BytesToAddress([]byte{10})] = &bls12381G1Add{} - precompileds[common.BytesToAddress([]byte{11})] = &bls12381G1Mul{} - precompileds[common.BytesToAddress([]byte{12})] = &bls12381G1MultiExp{} - precompileds[common.BytesToAddress([]byte{13})] = &bls12381G2Add{} - precompileds[common.BytesToAddress([]byte{14})] = &bls12381G2Mul{} - precompileds[common.BytesToAddress([]byte{15})] = &bls12381G2MultiExp{} - precompileds[common.BytesToAddress([]byte{16})] = &bls12381Pairing{} - precompileds[common.BytesToAddress([]byte{17})] = &bls12381MapG1{} - precompileds[common.BytesToAddress([]byte{18})] = &bls12381MapG2{} + mergeContracts(precompileds, PrecompiledContractsBLS) } return precompileds @@ -195,14 +204,19 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) { } // bigModExp implements a native big integer exponential modular operation. -type bigModExp struct{} +type bigModExp struct { + eip2565 bool +} var ( big0 = big.NewInt(0) big1 = big.NewInt(1) + big3 = big.NewInt(3) big4 = big.NewInt(4) + big7 = big.NewInt(7) big8 = big.NewInt(8) big16 = big.NewInt(16) + big20 = big.NewInt(20) big32 = big.NewInt(32) big64 = big.NewInt(64) big96 = big.NewInt(96) @@ -212,6 +226,34 @@ var ( big199680 = big.NewInt(199680) ) +// modexpMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198 +// +// def mult_complexity(x): +// if x <= 64: return x ** 2 +// elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072 +// else: return x ** 2 // 16 + 480 * x - 199680 +// +// where is x is max(length_of_MODULUS, length_of_BASE) +func modexpMultComplexity(x *big.Int) *big.Int { + switch { + case x.Cmp(big64) <= 0: + x.Mul(x, x) // x ** 2 + case x.Cmp(big1024) <= 0: + // (x ** 2 // 4 ) + ( 96 * x - 3072) + x = new(big.Int).Add( + new(big.Int).Div(new(big.Int).Mul(x, x), big4), + new(big.Int).Sub(new(big.Int).Mul(big96, x), big3072), + ) + default: + // (x ** 2 // 16) + (480 * x - 199680) + x = new(big.Int).Add( + new(big.Int).Div(new(big.Int).Mul(x, x), big16), + new(big.Int).Sub(new(big.Int).Mul(big480, x), big199680), + ) + } + return x +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bigModExp) RequiredGas(input []byte) uint64 { var ( @@ -246,25 +288,36 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { adjExpLen.Mul(big8, adjExpLen) } adjExpLen.Add(adjExpLen, big.NewInt(int64(msb))) - // Calculate the gas cost of the operation gas := new(big.Int).Set(math.BigMax(modLen, baseLen)) - switch { - case gas.Cmp(big64) <= 0: + if c.eip2565 { + // EIP-2565 has three changes + // 1. Different multComplexity (inlined here) + // in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565): + // + // def mult_complexity(x): + // ceiling(x/8)^2 + // + //where is x is max(length_of_MODULUS, length_of_BASE) + gas = gas.Add(gas, big7) + gas = gas.Div(gas, big8) gas.Mul(gas, gas) - case gas.Cmp(big1024) <= 0: - gas = new(big.Int).Add( - new(big.Int).Div(new(big.Int).Mul(gas, gas), big4), - new(big.Int).Sub(new(big.Int).Mul(big96, gas), big3072), - ) - default: - gas = new(big.Int).Add( - new(big.Int).Div(new(big.Int).Mul(gas, gas), big16), - new(big.Int).Sub(new(big.Int).Mul(big480, gas), big199680), - ) + + gas.Mul(gas, math.BigMax(adjExpLen, big1)) + // 2. Different divisor (`GQUADDIVISOR`) (3) + gas.Div(gas, big3) + if gas.BitLen() > 64 { + return math.MaxUint64 + } + // 3. Minimum price of 200 gas + if gas.Uint64() < 200 { + return 200 + } + return gas.Uint64() } + gas = modexpMultComplexity(gas) gas.Mul(gas, math.BigMax(adjExpLen, big1)) - gas.Div(gas, new(big.Int).SetUint64(vars.ModExpQuadCoeffDiv)) + gas.Div(gas, big20) if gas.BitLen() > 64 { return math.MaxUint64 diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 13164752d8..b726a5b945 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -21,7 +21,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "log" "math/big" "testing" "time" @@ -72,23 +71,35 @@ var blake2FMalformedInputTests = []precompiledFailureTest{ }, } -// allPrecompileds overrides the AllEthashProtocolChanges config. -// YoloV2 includes EIP2537. -// This config is Clique-based, for one. -// For two, the chain feature isn't installed in all CliqueChainConfigs either. -// So we override to ALL of the precompileds gathered, however hacky it may be. -var allPrecompiles = func() map[common.Address]PrecompiledContract { - conf := params.AllEthashProtocolChanges - zero := uint64(0) - err := conf.SetEIP2537Transition(&zero) - if err != nil { - log.Fatalln(err) - } - return PrecompiledContractsForConfig(conf, big.NewInt(0)) -}() +// allPrecompiles does not map to the actual set of precompiles, as it also contains +// repriced versions of precompiles at certain slots +var allPrecompiles = map[common.Address]PrecompiledContract{ + common.BytesToAddress([]byte{1}): &ecrecover{}, + common.BytesToAddress([]byte{2}): &sha256hash{}, + common.BytesToAddress([]byte{3}): &ripemd160hash{}, + common.BytesToAddress([]byte{4}): &dataCopy{}, + common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false}, // NOTE(ia): This isn't tested at 0x5 with 2565 repricing. + common.BytesToAddress([]byte{0xf5}): &bigModExp{eip2565: true}, + common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, + common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, + common.BytesToAddress([]byte{9}): &blake2F{}, + common.BytesToAddress([]byte{10}): &bls12381G1Add{}, + common.BytesToAddress([]byte{11}): &bls12381G1Mul{}, + common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{}, + common.BytesToAddress([]byte{13}): &bls12381G2Add{}, + common.BytesToAddress([]byte{14}): &bls12381G2Mul{}, + common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{}, + common.BytesToAddress([]byte{16}): &bls12381Pairing{}, + common.BytesToAddress([]byte{17}): &bls12381MapG1{}, + common.BytesToAddress([]byte{18}): &bls12381MapG2{}, +} func testPrecompiled(addr string, test precompiledTest, t *testing.T) { p := allPrecompiles[common.HexToAddress(addr)] + if p == nil { + t.Fatalf("precompiled == nil, addr: %s", addr) + } in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { @@ -320,6 +331,9 @@ func BenchmarkPrecompiledIdentity(bench *testing.B) { func TestPrecompiledModExp(t *testing.T) { testJson("modexp", "05", t) } func BenchmarkPrecompiledModExp(b *testing.B) { benchJson("modexp", "05", b) } +func TestPrecompiledModExpEip2565(t *testing.T) { testJson("modexp_eip2565", "f5", t) } +func BenchmarkPrecompiledModExpEip2565(b *testing.B) { benchJson("modexp_eip2565", "f5", b) } + // Tests the sample inputs from the elliptic curve addition EIP 213. func TestPrecompiledBn256Add(t *testing.T) { testJson("bn256Add", "06", t) } func BenchmarkPrecompiledBn256Add(b *testing.B) { benchJson("bn256Add", "06", b) } diff --git a/core/vm/eips.go b/core/vm/eips.go index 0755024ee1..ef1d12c9cc 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -29,7 +29,6 @@ var activators = map[int]func(*JumpTable){ 2200: enable2200, 1884: enable1884, 1344: enable1344, - 2315: enable2315, } // EnableEIP enables the given EIP on the config. @@ -81,9 +80,9 @@ func enable1884(jt *JumpTable) { enableSelfBalance(jt) } -func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(callContext.contract.Address())) - callContext.stack.push(balance) +func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(scope.Contract.Address())) + scope.Stack.push(balance) return nil, nil } @@ -100,9 +99,9 @@ func enable1344(jt *JumpTable) { } // opChainID implements CHAINID opcode -func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opChainID(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.GetChainID()) - callContext.stack.push(chainId) + scope.Stack.push(chainId) return nil, nil } @@ -112,34 +111,6 @@ func enable2200(jt *JumpTable) { jt[SSTORE].dynamicGas = gasSStoreEIP2200 } -// enable2315 applies EIP-2315 (Simple Subroutines) -// - Adds opcodes that jump to and return from subroutines -func enable2315(jt *JumpTable) { - // New opcode - jt[BEGINSUB] = &operation{ - execute: opBeginSub, - constantGas: GasQuickStep, - minStack: minStack(0, 0), - maxStack: maxStack(0, 0), - } - // New opcode - jt[JUMPSUB] = &operation{ - execute: opJumpSub, - constantGas: GasSlowStep, - minStack: minStack(1, 0), - maxStack: maxStack(1, 0), - jumps: true, - } - // New opcode - jt[RETURNSUB] = &operation{ - execute: opReturnSub, - constantGas: GasFastStep, - minStack: minStack(0, 0), - maxStack: maxStack(0, 0), - jumps: true, - } -} - // enable2929 enables "EIP-2929: Gas cost increases for state access opcodes" // https://eips.ethereum.org/EIPS/eip-2929 func enable2929(jt *JumpTable) { diff --git a/core/vm/evm.go b/core/vm/evm.go index 5f4aa43337..506016039a 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -44,8 +44,19 @@ type ( GetHashFunc func(uint64) common.Hash ) +// ActivePrecompiles returns the addresses of the precompiles enabled with the current +// configuration +func (evm *EVM) ActivePrecompiles() []common.Address { + p := PrecompiledContractsForConfig(evm.chainConfig, evm.Context.BlockNumber) + addresses := []common.Address{} + for k := range p { + addresses = append(addresses, k) + } + return addresses +} + func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { - var precompiles = PrecompiledContractsForConfig(evm.ChainConfig(), evm.BlockNumber) + var precompiles = PrecompiledContractsForConfig(evm.ChainConfig(), evm.Context.BlockNumber) p, ok := precompiles[addr] return p, ok } @@ -68,9 +79,9 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err return nil, errors.New("no compatible interpreter") } -// Context provides the EVM with auxiliary information. Once provided +// BlockContext provides the EVM with auxiliary information. Once provided // it shouldn't be modified. -type Context struct { +type BlockContext struct { // CanTransfer returns whether the account contains // sufficient ether to transfer the value CanTransfer CanTransferFunc @@ -79,10 +90,6 @@ type Context struct { // GetHash returns the hash corresponding to n GetHash GetHashFunc - // Message information - Origin common.Address // Provides information for ORIGIN - GasPrice *big.Int // Provides information for GASPRICE - // Block information Coinbase common.Address // Provides information for COINBASE GasLimit uint64 // Provides information for GASLIMIT @@ -91,6 +98,14 @@ type Context struct { Difficulty *big.Int // Provides information for DIFFICULTY } +// TxContext provides the EVM with information about a transaction. +// All fields can change between transactions. +type TxContext struct { + // Message information + Origin common.Address // Provides information for ORIGIN + GasPrice *big.Int // Provides information for GASPRICE +} + // EVM is the Ethereum Virtual Machine base object and provides // the necessary tools to run a contract on the given state with // the provided context. It should be noted that any error @@ -102,7 +117,8 @@ type Context struct { // The EVM should never be reused and is not thread safe. type EVM struct { // Context provides auxiliary blockchain related information - Context + Context BlockContext + TxContext // StateDB gives access to the underlying state StateDB StateDB // Depth is the current call stack @@ -131,9 +147,10 @@ type EVM struct { // NewEVM returns a new EVM. The returned EVM is not thread safe and should // only ever be used *once*. -func NewEVM(ctx Context, statedb StateDB, chainConfig ctypes.ChainConfigurator, vmConfig Config) *EVM { +func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig ctypes.ChainConfigurator, vmConfig Config) *EVM { evm := &EVM{ - Context: ctx, + Context: blockCtx, + TxContext: txCtx, StateDB: statedb, vmConfig: vmConfig, chainConfig: chainConfig, @@ -158,6 +175,13 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig ctypes.ChainConfigurator, return evm } +// Reset resets the EVM with a new transaction context.Reset +// This is not threadsafe and should only be done very cautiously. +func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) { + evm.TxContext = txCtx + evm.StateDB = statedb +} + // Cancel cancels any running EVM operation. This may be called concurrently and // it's safe to be called multiple times. func (evm *EVM) Cancel() { @@ -194,21 +218,21 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas p, isPrecompile := evm.precompile(addr) if !evm.StateDB.Exist(addr) { - if !isPrecompile && evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP161abcTransition, evm.BlockNumber) && value.Sign() == 0 { + if !isPrecompile && evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP161abcTransition, evm.Context.BlockNumber) && value.Sign() == 0 { // Calling a non existing account, don't do anything, but ping the tracer if evm.vmConfig.Debug && evm.depth == 0 { - evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) + evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) evm.vmConfig.Tracer.CaptureEnd(evm, ret, 0, 0, nil) } return nil, gas, nil } evm.StateDB.CreateAccount(addr) } - evm.Transfer(evm.StateDB, caller.Address(), addr, value) + evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value) // Capture the tracer start/end events in debug mode if evm.vmConfig.Debug && evm.depth == 0 { - evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) + evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters evm.vmConfig.Tracer.CaptureEnd(evm, ret, startGas-gas, time.Since(startTime), err) }(gas, time.Now()) @@ -397,7 +421,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.depth > int(vars.CallCreateDepth) { return nil, common.Address{}, gas, ErrDepth } - if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { + if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, common.Address{}, gas, ErrInsufficientBalance } nonce := evm.StateDB.GetNonce(caller.Address()) @@ -405,7 +429,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // We add this to the access list _before_ taking a snapshot. Even if the creation fails, // the access-list change should not be rolled back - if evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP2929Transition, evm.BlockNumber) { + if evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP2929Transition, evm.Context.BlockNumber) { evm.StateDB.AddAddressToAccessList(address) } @@ -417,10 +441,10 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Create a new account on the state snapshot := evm.StateDB.Snapshot() evm.StateDB.CreateAccount(address) - if evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP161abcTransition, evm.BlockNumber) { + if evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP161abcTransition, evm.Context.BlockNumber) { evm.StateDB.SetNonce(address, 1) } - evm.Transfer(evm.StateDB, caller.Address(), address, value) + evm.Context.Transfer(evm.StateDB, caller.Address(), address, value) // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. @@ -432,14 +456,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } if evm.vmConfig.Debug && evm.depth == 0 { - evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value) + evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value) } start := time.Now() ret, err := run(evm, contract, nil, false) // check whether the max code size has been exceeded - maxCodeSizeExceeded := evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP170Transition, evm.BlockNumber) && uint64(len(ret)) > vars.MaxCodeSize + maxCodeSizeExceeded := evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP170Transition, evm.Context.BlockNumber) && uint64(len(ret)) > vars.MaxCodeSize // if the contract creation ran successfully and no errors were returned // calculate the gas required to store the code. If the code could not // be stored due to not enough gas set an error and let it be handled @@ -456,7 +480,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. - if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP2Transition, evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) { + if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP2Transition, evm.Context.BlockNumber) || err != ErrCodeStoreOutOfGas)) { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { contract.UseGas(contract.Gas) @@ -485,7 +509,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { codeAndHash := &codeAndHash{code: code} - contractAddr = crypto.CreateAddress2(caller.Address(), common.Hash(salt.Bytes32()), codeAndHash.Hash().Bytes()) + contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes()) return evm.create(caller, codeAndHash, gas, endowment, contractAddr) } diff --git a/core/vm/evmc.go b/core/vm/evmc.go index d7fd8a4b01..cbc3cf031b 100644 --- a/core/vm/evmc.go +++ b/core/vm/evmc.go @@ -112,7 +112,7 @@ type hostContext struct { func (host *hostContext) AccountExists(evmcAddr evmc.Address) bool { addr := common.Address(evmcAddr) - if host.env.ChainConfig().IsEnabled(host.env.ChainConfig().GetEIP161dTransition, host.env.BlockNumber) { + if host.env.ChainConfig().IsEnabled(host.env.ChainConfig().GetEIP161dTransition, host.env.Context.BlockNumber) { if !host.env.StateDB.Empty(addr) { return true } @@ -150,8 +150,8 @@ func (host *hostContext) SetStorage(evmcAddr evmc.Address, evmcKey evmc.Hash, ev // or should we handle the question where we handle the rest of the questions like this, since this logic is // REALLY logic that belongs to the abstract idea of a chainconfiguration (aka chainconfig), which makes sense // but depends on ECIPs having steadier and more predictable logic. - hasEIP2200 := host.env.ChainConfig().IsEnabled(host.env.ChainConfig().GetEIP2200Transition, host.env.BlockNumber) - hasEIP1884 := host.env.ChainConfig().IsEnabled(host.env.ChainConfig().GetEIP1884Transition, host.env.BlockNumber) + hasEIP2200 := host.env.ChainConfig().IsEnabled(host.env.ChainConfig().GetEIP2200Transition, host.env.Context.BlockNumber) + hasEIP1884 := host.env.ChainConfig().IsEnabled(host.env.ChainConfig().GetEIP1884Transition, host.env.Context.BlockNumber) // Here's an example where to me, it makes sense to use individual EIPs in the code... // when they don't specify each other in the spec. @@ -236,20 +236,20 @@ func (host *hostContext) Selfdestruct(evmcAddr evmc.Address, evmcBeneficiary evm func (host *hostContext) GetTxContext() evmc.TxContext { return evmc.TxContext{ GasPrice: evmc.Hash(common.BigToHash(host.env.GasPrice)), - Origin: evmc.Address(host.env.Origin), - Coinbase: evmc.Address(host.env.Coinbase), - Number: host.env.BlockNumber.Int64(), - Timestamp: host.env.Time.Int64(), - GasLimit: int64(host.env.GasLimit), - Difficulty: evmc.Hash(common.BigToHash(host.env.Difficulty)), + Origin: evmc.Address(host.env.TxContext.Origin), + Coinbase: evmc.Address(host.env.Context.Coinbase), + Number: host.env.Context.BlockNumber.Int64(), + Timestamp: host.env.Context.Time.Int64(), + GasLimit: int64(host.env.Context.GasLimit), + Difficulty: evmc.Hash(common.BigToHash(host.env.Context.Difficulty)), ChainID: evmc.Hash(common.BigToHash(host.env.chainConfig.GetChainID())), } } func (host *hostContext) GetBlockHash(number int64) evmc.Hash { - b := host.env.BlockNumber.Int64() + b := host.env.Context.BlockNumber.Int64() if number >= (b-256) && number < b { - return evmc.Hash(host.env.GetHash(uint64(number))) + return evmc.Hash(host.env.Context.GetHash(uint64(number))) } return evmc.Hash{} } @@ -263,7 +263,7 @@ func (host *hostContext) EmitLog(addr evmc.Address, evmcTopics []evmc.Hash, data Address: common.Address(addr), Topics: topics, Data: data, - BlockNumber: host.env.BlockNumber.Uint64(), + BlockNumber: host.env.Context.BlockNumber.Uint64(), }) } @@ -299,7 +299,7 @@ func (host *hostContext) Call(kind evmc.CallKind, var createOutput []byte createOutput, createAddr, gasLeftU, err = host.env.Create(host.contract, input, gasU, value.ToBig()) createAddrEvmc = evmc.Address(createAddr) - isHomestead := host.env.ChainConfig().IsEnabled(host.env.ChainConfig().GetEIP7Transition, host.env.BlockNumber) + isHomestead := host.env.ChainConfig().IsEnabled(host.env.ChainConfig().GetEIP7Transition, host.env.Context.BlockNumber) if !isHomestead && err == ErrCodeStoreOutOfGas { err = nil } @@ -340,13 +340,15 @@ func (host *hostContext) Call(kind evmc.CallKind, // getRevision translates ChainConfig's HF block information into EVMC revision. func getRevision(env *EVM) evmc.Revision { - n := env.BlockNumber + n := env.Context.BlockNumber conf := env.ChainConfig() switch { // This is an example of choosing to use an "abstracted" idea // about chain config, where I'm choosing to prioritize "indicative" features // as identifiers for Fork-Feature-Groups. Note that this is very different // than using Feature-complete sets to assert "did Forkage." + case conf.IsEnabled(conf.GetEIP2565Transition, n): + panic("berlin is unsupported by EVMCv7") case conf.IsEnabled(conf.GetEIP1884Transition, n): return evmc.Istanbul case conf.IsEnabled(conf.GetEIP1283DisableTransition, n): diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 5632d4fac4..cd1361d463 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -96,11 +96,11 @@ var ( func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( y, x = stack.Back(1), stack.Back(0) - current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32())) + current = evm.StateDB.GetState(contract.Address(), x.Bytes32()) ) // The legacy gas metering only takes into consideration the current state - neip1283t := !evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP1283Transition, evm.BlockNumber) - deip1283t := evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP1283DisableTransition, evm.BlockNumber) + neip1283t := !evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP1283Transition, evm.Context.BlockNumber) + deip1283t := evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP1283DisableTransition, evm.Context.BlockNumber) if neip1283t || deip1283t { // This checks for 3 scenario's and calculates gas accordingly: // @@ -133,14 +133,14 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi // 2.2.2.2. Otherwise, add 4800 gas to refund counter. // Enforce sufficient gas left, per EIP1706, guarding against reentrancy attacks. - if contract.Gas <= vars.CallStipend && evm.chainConfig.IsEnabled(evm.chainConfig.GetEIP1706Transition, evm.BlockNumber) { + if contract.Gas <= vars.CallStipend && evm.chainConfig.IsEnabled(evm.chainConfig.GetEIP1706Transition, evm.Context.BlockNumber) { return 0, ErrOutOfGas } value := common.Hash(y.Bytes32()) if current == value { // noop (1) return vars.NetSstoreNoopGas, nil } - original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) + original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32()) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) return vars.NetSstoreInitGas, nil @@ -188,14 +188,14 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m // Gas sentry honoured, do the actual gas calculation based on the stored value var ( y, x = stack.Back(1), stack.Back(0) - current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32())) + current = evm.StateDB.GetState(contract.Address(), x.Bytes32()) ) value := common.Hash(y.Bytes32()) if current == value { // noop (1) return vars.SloadGasEIP2200, nil } - original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) + original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32()) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) return vars.SstoreSetGasEIP2200, nil @@ -295,7 +295,7 @@ func gasCreate(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi if overflow { return 0, ErrGasUintOverflow } - evm.CallGasTemp, err = createGasEip150(evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.BlockNumber), remainingGasTemp) + evm.CallGasTemp, err = createGasEip150(evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.Context.BlockNumber), remainingGasTemp) if err != nil { return 0, err } @@ -324,7 +324,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS if overflow { return 0, ErrGasUintOverflow } - evm.CallGasTemp, err = createGasEip150(evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.BlockNumber), remainingGasTemp) + evm.CallGasTemp, err = createGasEip150(evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.Context.BlockNumber), remainingGasTemp) if err != nil { return 0, err } @@ -367,7 +367,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize address = common.Address(stack.Back(1).Bytes20()) ) // EIP161b - if evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP161abcTransition, evm.BlockNumber) { + if evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP161abcTransition, evm.Context.BlockNumber) { if transfersValue && evm.StateDB.Empty(address) { gas += vars.CallNewAccountGas } @@ -386,7 +386,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize return 0, ErrGasUintOverflow } - evm.CallGasTemp, err = callGas(evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.BlockNumber), contract.Gas, gas, stack.Back(0)) + evm.CallGasTemp, err = callGas(evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.Context.BlockNumber), contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } @@ -411,7 +411,7 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { return 0, ErrGasUintOverflow } - evm.CallGasTemp, err = callGas(evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.BlockNumber), contract.Gas, gas, stack.Back(0)) + evm.CallGasTemp, err = callGas(evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.Context.BlockNumber), contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } @@ -426,7 +426,7 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me if err != nil { return 0, err } - evm.CallGasTemp, err = callGas(evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.BlockNumber), contract.Gas, gas, stack.Back(0)) + evm.CallGasTemp, err = callGas(evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.Context.BlockNumber), contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } @@ -442,7 +442,7 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo if err != nil { return 0, err } - evm.CallGasTemp, err = callGas(evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.BlockNumber), contract.Gas, gas, stack.Back(0)) + evm.CallGasTemp, err = callGas(evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.Context.BlockNumber), contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } @@ -456,11 +456,11 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var gas uint64 // EIP150 homestead gas reprice fork: - if evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.BlockNumber) { + if evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP150Transition, evm.Context.BlockNumber) { gas = vars.SelfdestructGasEIP150 var address = common.Address(stack.Back(0).Bytes20()) - if evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP161abcTransition, evm.BlockNumber) { + if evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP161abcTransition, evm.Context.BlockNumber) { // if empty and transfers value if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 { gas += vars.CreateBySelfdestructGas diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index 55b8a52e76..6cd126c9b4 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -87,12 +87,11 @@ func TestEIP2200(t *testing.T) { statedb.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original})) statedb.Finalise(true) // Push the state into the "original" slot - vmctx := Context{ + vmctx := BlockContext{ CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, } - - vmenv := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) + vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int)) if err != tt.failure { diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go index 44da014de9..ac04afe8b7 100644 --- a/core/vm/gen_structlog.go +++ b/core/vm/gen_structlog.go @@ -45,12 +45,6 @@ func (s StructLog) MarshalJSON() ([]byte, error) { enc.Stack[k] = (*math.HexOrDecimal256)(v) } } - if s.ReturnStack != nil { - enc.ReturnStack = make([]math.HexOrDecimal64, len(s.ReturnStack)) - for k, v := range s.ReturnStack { - enc.ReturnStack[k] = math.HexOrDecimal64(v) - } - } enc.ReturnData = s.ReturnData enc.Storage = s.Storage enc.Depth = s.Depth @@ -71,7 +65,6 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { Memory *hexutil.Bytes `json:"memory"` MemorySize *int `json:"memSize"` Stack []*math.HexOrDecimal256 `json:"stack"` - ReturnStack []math.HexOrDecimal64 `json:"returnStack"` ReturnData *hexutil.Bytes `json:"returnData"` Storage map[common.Hash]common.Hash `json:"-"` Depth *int `json:"depth"` @@ -106,12 +99,6 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { s.Stack[k] = (*big.Int)(v) } } - if dec.ReturnStack != nil { - s.ReturnStack = make([]uint32, len(dec.ReturnStack)) - for k, v := range dec.ReturnStack { - s.ReturnStack[k] = uint32(v) - } - } if dec.ReturnData != nil { s.ReturnData = *dec.ReturnData } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 0355ac4e99..f83400cdc6 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -24,68 +24,68 @@ import ( "golang.org/x/crypto/sha3" ) -func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() y.Add(&x, y) return nil, nil } -func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opSub(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() y.Sub(&x, y) return nil, nil } -func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opMul(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() y.Mul(&x, y) return nil, nil } -func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opDiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() y.Div(&x, y) return nil, nil } -func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opSdiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() y.SDiv(&x, y) return nil, nil } -func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opMod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() y.Mod(&x, y) return nil, nil } -func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opSmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() y.SMod(&x, y) return nil, nil } -func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - base, exponent := callContext.stack.pop(), callContext.stack.peek() +func opExp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + base, exponent := scope.Stack.pop(), scope.Stack.peek() exponent.Exp(&base, exponent) return nil, nil } -func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - back, num := callContext.stack.pop(), callContext.stack.peek() +func opSignExtend(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + back, num := scope.Stack.pop(), scope.Stack.peek() num.ExtendSign(num, &back) return nil, nil } -func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x := callContext.stack.peek() +func opNot(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x := scope.Stack.peek() x.Not(x) return nil, nil } -func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opLt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() if x.Lt(y) { y.SetOne() } else { @@ -94,8 +94,8 @@ func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte return nil, nil } -func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opGt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() if x.Gt(y) { y.SetOne() } else { @@ -104,8 +104,8 @@ func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte return nil, nil } -func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opSlt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() if x.Slt(y) { y.SetOne() } else { @@ -114,8 +114,8 @@ func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt return nil, nil } -func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opSgt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() if x.Sgt(y) { y.SetOne() } else { @@ -124,8 +124,8 @@ func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt return nil, nil } -func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opEq(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() if x.Eq(y) { y.SetOne() } else { @@ -134,8 +134,8 @@ func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte return nil, nil } -func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x := callContext.stack.peek() +func opIszero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x := scope.Stack.peek() if x.IsZero() { x.SetOne() } else { @@ -144,32 +144,32 @@ func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] return nil, nil } -func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opAnd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() y.And(&x, y) return nil, nil } -func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opOr(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() y.Or(&x, y) return nil, nil } -func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.peek() +func opXor(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.peek() y.Xor(&x, y) return nil, nil } -func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - th, val := callContext.stack.pop(), callContext.stack.peek() +func opByte(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + th, val := scope.Stack.pop(), scope.Stack.peek() val.Byte(&th) return nil, nil } -func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek() +func opAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() if z.IsZero() { z.Clear() } else { @@ -178,8 +178,8 @@ func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] return nil, nil } -func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek() +func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() z.MulMod(&x, &y, z) return nil, nil } @@ -187,9 +187,9 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] // opSHL implements Shift Left // The SHL instruction (shift left) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the left by arg1 number of bits. -func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opSHL(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards - shift, value := callContext.stack.pop(), callContext.stack.peek() + shift, value := scope.Stack.pop(), scope.Stack.peek() if shift.LtUint64(256) { value.Lsh(value, uint(shift.Uint64())) } else { @@ -201,9 +201,9 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt // opSHR implements Logical Shift Right // The SHR instruction (logical shift right) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill. -func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opSHR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards - shift, value := callContext.stack.pop(), callContext.stack.peek() + shift, value := scope.Stack.pop(), scope.Stack.peek() if shift.LtUint64(256) { value.Rsh(value, uint(shift.Uint64())) } else { @@ -215,8 +215,8 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt // opSAR implements Arithmetic Shift Right // The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension. -func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - shift, value := callContext.stack.pop(), callContext.stack.peek() +func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + shift, value := scope.Stack.pop(), scope.Stack.peek() if shift.GtUint64(256) { if value.Sign() >= 0 { value.Clear() @@ -231,9 +231,9 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt return nil, nil } -func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - offset, size := callContext.stack.pop(), callContext.stack.peek() - data := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) +func opSha3(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + offset, size := scope.Stack.pop(), scope.Stack.peek() + data := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) if interpreter.hasher == nil { interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState) @@ -251,37 +251,37 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by size.SetBytes(interpreter.hasherBuf[:]) return nil, nil } -func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Address().Bytes())) +func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes())) return nil, nil } -func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - slot := callContext.stack.peek() +func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + slot := scope.Stack.peek() address := common.Address(slot.Bytes20()) slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address)) return nil, nil } -func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes())) +func opOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes())) return nil, nil } -func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Caller().Bytes())) +func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes())) return nil, nil } -func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - v, _ := uint256.FromBig(callContext.contract.value) - callContext.stack.push(v) +func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + v, _ := uint256.FromBig(scope.Contract.value) + scope.Stack.push(v) return nil, nil } -func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x := callContext.stack.peek() +func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x := scope.Stack.peek() if offset, overflow := x.Uint64WithOverflow(); !overflow { - data := getData(callContext.contract.Input, offset, 32) + data := getData(scope.Contract.Input, offset, 32) x.SetBytes(data) } else { x.Clear() @@ -289,16 +289,16 @@ func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCt return nil, nil } -func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(callContext.contract.Input)))) +func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input)))) return nil, nil } -func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - memOffset = callContext.stack.pop() - dataOffset = callContext.stack.pop() - length = callContext.stack.pop() + memOffset = scope.Stack.pop() + dataOffset = scope.Stack.pop() + length = scope.Stack.pop() ) dataOffset64, overflow := dataOffset.Uint64WithOverflow() if overflow { @@ -307,21 +307,21 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCt // These values are checked for overflow during gas cost calculation memOffset64 := memOffset.Uint64() length64 := length.Uint64() - callContext.memory.Set(memOffset64, length64, getData(callContext.contract.Input, dataOffset64, length64)) + scope.Memory.Set(memOffset64, length64, getData(scope.Contract.Input, dataOffset64, length64)) return nil, nil } -func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData)))) +func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData)))) return nil, nil } -func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - memOffset = callContext.stack.pop() - dataOffset = callContext.stack.pop() - length = callContext.stack.pop() + memOffset = scope.Stack.pop() + dataOffset = scope.Stack.pop() + length = scope.Stack.pop() ) offset64, overflow := dataOffset.Uint64WithOverflow() @@ -335,42 +335,42 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call if overflow || uint64(len(interpreter.returnData)) < end64 { return nil, ErrReturnDataOutOfBounds } - callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64]) + scope.Memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64]) return nil, nil } -func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - slot := callContext.stack.peek() - slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.Address(slot.Bytes20())))) +func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + slot := scope.Stack.peek() + slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))) return nil, nil } -func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { l := new(uint256.Int) - l.SetUint64(uint64(len(callContext.contract.Code))) - callContext.stack.push(l) + l.SetUint64(uint64(len(scope.Contract.Code))) + scope.Stack.push(l) return nil, nil } -func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - memOffset = callContext.stack.pop() - codeOffset = callContext.stack.pop() - length = callContext.stack.pop() + memOffset = scope.Stack.pop() + codeOffset = scope.Stack.pop() + length = scope.Stack.pop() ) uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() if overflow { uint64CodeOffset = 0xffffffffffffffff } - codeCopy := getData(callContext.contract.Code, uint64CodeOffset, length.Uint64()) - callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) + codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64()) + scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) return nil, nil } -func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - stack = callContext.stack + stack = scope.Stack a = stack.pop() memOffset = stack.pop() codeOffset = stack.pop() @@ -382,7 +382,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx } addr := common.Address(a.Bytes20()) codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) - callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) + scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) return nil, nil } @@ -413,8 +413,8 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx // // (6) Caller tries to get the code hash for an account which is marked as deleted, // this account should be regarded as a non-existent account and zero should be returned. -func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - slot := callContext.stack.peek() +func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + slot := scope.Stack.peek() address := common.Address(slot.Bytes20()) if interpreter.evm.StateDB.Empty(address) { slot.Clear() @@ -424,116 +424,116 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx return nil, nil } -func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { v, _ := uint256.FromBig(interpreter.evm.GasPrice) - callContext.stack.push(v) + scope.Stack.push(v) return nil, nil } -func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - num := callContext.stack.peek() +func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + num := scope.Stack.peek() num64, overflow := num.Uint64WithOverflow() if overflow { num.Clear() return nil, nil } var upper, lower uint64 - upper = interpreter.evm.BlockNumber.Uint64() + upper = interpreter.evm.Context.BlockNumber.Uint64() if upper < 257 { lower = 0 } else { lower = upper - 256 } if num64 >= lower && num64 < upper { - num.SetBytes(interpreter.evm.GetHash(num64).Bytes()) + num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes()) } else { num.Clear() } return nil, nil } -func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes())) +func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes())) return nil, nil } -func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - v, _ := uint256.FromBig(interpreter.evm.Time) - callContext.stack.push(v) +func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + v, _ := uint256.FromBig(interpreter.evm.Context.Time) + scope.Stack.push(v) return nil, nil } -func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - v, _ := uint256.FromBig(interpreter.evm.BlockNumber) - callContext.stack.push(v) +func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + v, _ := uint256.FromBig(interpreter.evm.Context.BlockNumber) + scope.Stack.push(v) return nil, nil } -func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - v, _ := uint256.FromBig(interpreter.evm.Difficulty) - callContext.stack.push(v) +func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + v, _ := uint256.FromBig(interpreter.evm.Context.Difficulty) + scope.Stack.push(v) return nil, nil } -func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit)) +func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit)) return nil, nil } -func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.pop() +func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.pop() return nil, nil } -func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - v := callContext.stack.peek() +func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + v := scope.Stack.peek() offset := int64(v.Uint64()) - v.SetBytes(callContext.memory.GetPtr(offset, 32)) + v.SetBytes(scope.Memory.GetPtr(offset, 32)) return nil, nil } -func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { // pop value of the stack - mStart, val := callContext.stack.pop(), callContext.stack.pop() - callContext.memory.Set32(mStart.Uint64(), &val) + mStart, val := scope.Stack.pop(), scope.Stack.pop() + scope.Memory.Set32(mStart.Uint64(), &val) return nil, nil } -func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - off, val := callContext.stack.pop(), callContext.stack.pop() - callContext.memory.store[off.Uint64()] = byte(val.Uint64()) +func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + off, val := scope.Stack.pop(), scope.Stack.pop() + scope.Memory.store[off.Uint64()] = byte(val.Uint64()) return nil, nil } -func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - loc := callContext.stack.peek() +func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + loc := scope.Stack.peek() hash := common.Hash(loc.Bytes32()) - val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), hash) + val := interpreter.evm.StateDB.GetState(scope.Contract.Address(), hash) loc.SetBytes(val.Bytes()) return nil, nil } -func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - loc := callContext.stack.pop() - val := callContext.stack.pop() - interpreter.evm.StateDB.SetState(callContext.contract.Address(), - common.Hash(loc.Bytes32()), common.Hash(val.Bytes32())) +func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + loc := scope.Stack.pop() + val := scope.Stack.pop() + interpreter.evm.StateDB.SetState(scope.Contract.Address(), + loc.Bytes32(), val.Bytes32()) return nil, nil } -func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - pos := callContext.stack.pop() - if !callContext.contract.validJumpdest(&pos) { +func opJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + pos := scope.Stack.pop() + if !scope.Contract.validJumpdest(&pos) { return nil, ErrInvalidJump } *pc = pos.Uint64() return nil, nil } -func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - pos, cond := callContext.stack.pop(), callContext.stack.pop() +func opJumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + pos, cond := scope.Stack.pop(), scope.Stack.pop() if !cond.IsZero() { - if !callContext.contract.validJumpdest(&pos) { + if !scope.Contract.validJumpdest(&pos) { return nil, ErrInvalidJump } *pc = pos.Uint64() @@ -543,62 +543,30 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b return nil, nil } -func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opJumpdest(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { return nil, nil } -func opBeginSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - return nil, ErrInvalidSubroutineEntry -} - -func opJumpSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - if len(callContext.rstack.data) >= 1023 { - return nil, ErrReturnStackExceeded - } - pos := callContext.stack.pop() - if !pos.IsUint64() { - return nil, ErrInvalidJump - } - posU64 := pos.Uint64() - if !callContext.contract.validJumpSubdest(posU64) { - return nil, ErrInvalidJump - } - callContext.rstack.push(uint32(*pc)) - *pc = posU64 + 1 +func opPc(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.push(new(uint256.Int).SetUint64(*pc)) return nil, nil } -func opReturnSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - if len(callContext.rstack.data) == 0 { - return nil, ErrInvalidRetsub - } - // Other than the check that the return stack is not empty, there is no - // need to validate the pc from 'returns', since we only ever push valid - //values onto it via jumpsub. - *pc = uint64(callContext.rstack.pop()) + 1 +func opMsize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len()))) return nil, nil } -func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(new(uint256.Int).SetUint64(*pc)) +func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas)) return nil, nil } -func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(new(uint256.Int).SetUint64(uint64(callContext.memory.Len()))) - return nil, nil -} - -func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(new(uint256.Int).SetUint64(callContext.contract.Gas)) - return nil, nil -} - -func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - value = callContext.stack.pop() - offset, size = callContext.stack.pop(), callContext.stack.pop() - input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) + value = scope.Stack.pop() + offset, size = scope.Stack.pop(), scope.Stack.pop() + input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = interpreter.evm.CallGasTemp ) // reuse size int for stackvalue @@ -610,12 +578,12 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] bigVal = value.ToBig() } - res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, bigVal) + res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, bigVal) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must // ignore this error and pretend the operation was successful. - if interpreter.evm.ChainConfig().IsEnabled(interpreter.evm.chainConfig.GetEIP2Transition, interpreter.evm.BlockNumber) && suberr == ErrCodeStoreOutOfGas { + if interpreter.evm.ChainConfig().IsEnabled(interpreter.evm.chainConfig.GetEIP2Transition, interpreter.evm.Context.BlockNumber) && suberr == ErrCodeStoreOutOfGas { stackvalue.Clear() interpreter.evm.CallErrorTemp = suberr // temp storage, for debug tracing } else if suberr != nil && suberr != ErrCodeStoreOutOfGas { @@ -624,8 +592,8 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] } else { stackvalue.SetBytes(addr.Bytes()) } - callContext.stack.push(&stackvalue) - callContext.contract.Gas += returnGas + scope.Stack.push(&stackvalue) + scope.Contract.Gas += returnGas if suberr == ErrExecutionReverted { return res, nil @@ -633,14 +601,15 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] return nil, nil } -func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - endowment = callContext.stack.pop() - offset, size = callContext.stack.pop(), callContext.stack.pop() - salt = callContext.stack.pop() - input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) + endowment = scope.Stack.pop() + offset, size = scope.Stack.pop(), scope.Stack.pop() + salt = scope.Stack.pop() + input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = interpreter.evm.CallGasTemp ) + // reuse size int for stackvalue stackvalue := size //TODO: use uint256.Int instead of converting with toBig() @@ -648,7 +617,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ if !endowment.IsZero() { bigEndowment = endowment.ToBig() } - res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas, + res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas, bigEndowment, &salt) // Push item on the stack based on the returned error. if suberr != nil { @@ -657,8 +626,8 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ } else { stackvalue.SetBytes(addr.Bytes()) } - callContext.stack.push(&stackvalue) - callContext.contract.Gas += returnGas + scope.Stack.push(&stackvalue) + scope.Contract.Gas += returnGas if suberr == ErrExecutionReverted { return res, nil @@ -666,9 +635,9 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ return nil, nil } -func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - stack := callContext.stack - // Pop gas. The actual gas in interpreter.evm.CallGasTemp. +func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + stack := scope.Stack + // Pop gas. The actual gas in interpreter.evm.callGasTemp. // We can use this as a temporary value temp := stack.pop() gas := interpreter.evm.CallGasTemp @@ -676,7 +645,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.Address(addr.Bytes20()) // Get the arguments from the memory. - args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) + args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) var bigVal = big0 //TODO: use uint256.Int instead of converting with toBig() @@ -687,7 +656,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by bigVal = value.ToBig() } - ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, bigVal) + ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal) if err != nil { temp.Clear() @@ -697,16 +666,16 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by } stack.push(&temp) if err == nil || err == ErrExecutionReverted { - callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) + scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - callContext.contract.Gas += returnGas + scope.Contract.Gas += returnGas return ret, nil } -func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - // Pop gas. The actual gas is in interpreter.evm.CallGasTemp. - stack := callContext.stack +func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + // Pop gas. The actual gas is in interpreter.evm.callGasTemp. + stack := scope.Stack // We use it as a temporary value temp := stack.pop() gas := interpreter.evm.CallGasTemp @@ -714,7 +683,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ( addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. - args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) + args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) //TODO: use uint256.Int instead of converting with toBig() var bigVal = big0 @@ -723,7 +692,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ( bigVal = value.ToBig() } - ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, bigVal) + ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, bigVal) if err != nil { temp.Clear() interpreter.evm.CallErrorTemp = err // temp storage, for debug tracing @@ -732,16 +701,16 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ( } stack.push(&temp) if err == nil || err == ErrExecutionReverted { - callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) + scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - callContext.contract.Gas += returnGas + scope.Contract.Gas += returnGas return ret, nil } -func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - stack := callContext.stack - // Pop gas. The actual gas is in interpreter.evm.CallGasTemp. +func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + stack := scope.Stack + // Pop gas. The actual gas is in interpreter.evm.callGasTemp. // We use it as a temporary value temp := stack.pop() gas := interpreter.evm.CallGasTemp @@ -749,9 +718,9 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCt addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. - args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) + args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) - ret, returnGas, err := interpreter.evm.DelegateCall(callContext.contract, toAddr, args, gas) + ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas) if err != nil { temp.Clear() interpreter.evm.CallErrorTemp = err // temp storage, for debug tracing @@ -760,16 +729,16 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCt } stack.push(&temp) if err == nil || err == ErrExecutionReverted { - callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) + scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - callContext.contract.Gas += returnGas + scope.Contract.Gas += returnGas return ret, nil } -func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - // Pop gas. The actual gas is in interpreter.evm.CallGasTemp. - stack := callContext.stack +func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + // Pop gas. The actual gas is in interpreter.evm.callGasTemp. + stack := scope.Stack // We use it as a temporary value temp := stack.pop() gas := interpreter.evm.CallGasTemp @@ -777,9 +746,9 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. - args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) + args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) - ret, returnGas, err := interpreter.evm.StaticCall(callContext.contract, toAddr, args, gas) + ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas) if err != nil { temp.Clear() interpreter.evm.CallErrorTemp = err // temp storage, for debug tracing @@ -788,36 +757,36 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) } stack.push(&temp) if err == nil || err == ErrExecutionReverted { - callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) + scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - callContext.contract.Gas += returnGas + scope.Contract.Gas += returnGas return ret, nil } -func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - offset, size := callContext.stack.pop(), callContext.stack.pop() - ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) +func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + offset, size := scope.Stack.pop(), scope.Stack.pop() + ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) return ret, nil } -func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - offset, size := callContext.stack.pop(), callContext.stack.pop() - ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) +func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + offset, size := scope.Stack.pop(), scope.Stack.pop() + ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) return ret, nil } -func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { return nil, nil } -func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - beneficiary := callContext.stack.pop() - balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address()) - interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance) - interpreter.evm.StateDB.Suicide(callContext.contract.Address()) +func opSuicide(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + beneficiary := scope.Stack.pop() + balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) + interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance) + interpreter.evm.StateDB.Suicide(scope.Contract.Address()) return nil, nil } @@ -825,23 +794,23 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ // make log instruction function func makeLog(size int) executionFunc { - return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { topics := make([]common.Hash, size) - stack := callContext.stack + stack := scope.Stack mStart, mSize := stack.pop(), stack.pop() for i := 0; i < size; i++ { addr := stack.pop() - topics[i] = common.Hash(addr.Bytes32()) + topics[i] = addr.Bytes32() } - d := callContext.memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64())) + d := scope.Memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64())) interpreter.evm.StateDB.AddLog(&types.Log{ - Address: callContext.contract.Address(), + Address: scope.Contract.Address(), Topics: topics, Data: d, // This is a non-consensus field, but assigned here because // core/state doesn't know the current block number. - BlockNumber: interpreter.evm.BlockNumber.Uint64(), + BlockNumber: interpreter.evm.Context.BlockNumber.Uint64(), }) return nil, nil @@ -849,24 +818,24 @@ func makeLog(size int) executionFunc { } // opPush1 is a specialized version of pushN -func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - codeLen = uint64(len(callContext.contract.Code)) + codeLen = uint64(len(scope.Contract.Code)) integer = new(uint256.Int) ) *pc += 1 if *pc < codeLen { - callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc]))) + scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) } else { - callContext.stack.push(integer.Clear()) + scope.Stack.push(integer.Clear()) } return nil, nil } // make push instruction function func makePush(size uint64, pushByteSize int) executionFunc { - return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - codeLen := len(callContext.contract.Code) + return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + codeLen := len(scope.Contract.Code) startMin := codeLen if int(*pc+1) < startMin { @@ -879,8 +848,8 @@ func makePush(size uint64, pushByteSize int) executionFunc { } integer := new(uint256.Int) - callContext.stack.push(integer.SetBytes(common.RightPadBytes( - callContext.contract.Code[startMin:endMin], pushByteSize))) + scope.Stack.push(integer.SetBytes(common.RightPadBytes( + scope.Contract.Code[startMin:endMin], pushByteSize))) *pc += size return nil, nil @@ -889,8 +858,8 @@ func makePush(size uint64, pushByteSize int) executionFunc { // make dup instruction function func makeDup(size int64) executionFunc { - return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.dup(int(size)) + return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.dup(int(size)) return nil, nil } } @@ -899,8 +868,8 @@ func makeDup(size int64) executionFunc { func makeSwap(size int64) executionFunc { // switch n + 1 otherwise n would be swapped with n size++ - return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.swap(int(size)) + return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap(int(size)) return nil, nil } } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index fd9f8b97e3..6e8dbcac96 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -92,9 +92,8 @@ func init() { func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) { var ( - env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) stack = newstack() - rstack = newReturnStack() pc = uint64(0) evmInterpreter = env.interpreter.(*EVMInterpreter) ) @@ -105,7 +104,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected)) stack.push(x) stack.push(y) - opFn(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil}) + opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) if len(stack.data) != 1 { t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data)) } @@ -192,7 +191,7 @@ func TestSAR(t *testing.T) { func TestAddMod(t *testing.T) { var ( - env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) stack = newstack() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) pc = uint64(0) @@ -220,7 +219,7 @@ func TestAddMod(t *testing.T) { stack.push(z) stack.push(y) stack.push(x) - opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil, nil}) + opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) actual := stack.pop() if actual.Cmp(expected) != 0 { t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual) @@ -231,10 +230,10 @@ func TestAddMod(t *testing.T) { // getResult is a convenience function to generate the expected values func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { var ( - env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) - stack, rstack = newstack(), newReturnStack() - pc = uint64(0) - interpreter = env.interpreter.(*EVMInterpreter) + env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) + interpreter = env.interpreter.(*EVMInterpreter) ) result := make([]TwoOperandTestcase, len(args)) for i, param := range args { @@ -242,7 +241,7 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) stack.push(x) stack.push(y) - opFn(&pc, interpreter, &callCtx{nil, stack, rstack, nil}) + opFn(&pc, interpreter, &ScopeContext{nil, stack, nil}) actual := stack.pop() result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} } @@ -281,8 +280,8 @@ func TestJsonTestcases(t *testing.T) { func opBenchmark(bench *testing.B, op executionFunc, args ...string) { var ( - env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) - stack, rstack = newstack(), newReturnStack() + env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) @@ -300,7 +299,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { a.SetBytes(arg) stack.push(a) } - op(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil}) + op(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) stack.pop() } } @@ -519,8 +518,8 @@ func BenchmarkOpIsZero(b *testing.B) { func TestOpMstore(t *testing.T) { var ( - env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) - stack, rstack = newstack(), newReturnStack() + env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) @@ -530,12 +529,12 @@ func TestOpMstore(t *testing.T) { pc := uint64(0) v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int)) - opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) + opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v { t.Fatalf("Mstore fail, got %v, expected %v", got, v) } stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int)) - opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) + opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { t.Fatalf("Mstore failed to overwrite previous value") } @@ -543,8 +542,8 @@ func TestOpMstore(t *testing.T) { func BenchmarkOpMstore(bench *testing.B) { var ( - env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) - stack, rstack = newstack(), newReturnStack() + env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) @@ -558,14 +557,14 @@ func BenchmarkOpMstore(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { stack.pushN(*value, *memStart) - opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) + opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) } } func BenchmarkOpSHA3(bench *testing.B) { var ( - env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) - stack, rstack = newstack(), newReturnStack() + env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) @@ -577,7 +576,7 @@ func BenchmarkOpSHA3(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { stack.pushN(*uint256.NewInt().SetUint64(32), *start) - opSha3(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) + opSha3(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) } } diff --git a/core/vm/interface.go b/core/vm/interface.go index fb5bbca48f..ad9b05d666 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -57,6 +57,7 @@ type StateDB interface { // is defined according to EIP161 (balance = nonce = code = 0). Empty(common.Address) bool + PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) AddressInAccessList(addr common.Address) bool SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) // AddAddressToAccessList adds the given address to the access list. This operation is safe to perform diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 0df124224a..d95704c2b1 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -62,13 +62,12 @@ type Interpreter interface { CanRun([]byte) bool } -// callCtx contains the things that are per-call, such as stack and memory, +// ScopeContext contains the things that are per-call, such as stack and memory, // but not transients like pc and gas -type callCtx struct { - memory *Memory - stack *Stack - rstack *ReturnStack - contract *Contract +type ScopeContext struct { + Memory *Memory + Stack *Stack + Contract *Contract } // keccakState wraps sha3.state. In addition to the usual hash methods, it also supports @@ -97,7 +96,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // the jump table was initialised. If it was not // we'll set the default jump table. if cfg.JumpTable[STOP] == nil { - var jt = instructionSetForConfig(evm.chainConfig, evm.BlockNumber) + var jt = instructionSetForConfig(evm.chainConfig, evm.Context.BlockNumber) for i, eip := range cfg.ExtraEips { if err := EnableEIP(eip, &jt); err != nil { @@ -144,15 +143,13 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } var ( - op OpCode // current opcode - mem = NewMemory() // bound memory - stack = newstack() // local stack - returns = newReturnStack() // local returns stack - callContext = &callCtx{ - memory: mem, - stack: stack, - rstack: returns, - contract: contract, + op OpCode // current opcode + mem = NewMemory() // bound memory + stack = newstack() // local stack + callContext = &ScopeContext{ + Memory: mem, + Stack: stack, + Contract: contract, } // For optimisation reason we're using uint64 as the program counter. // It's theoretically possible to go above 2^64. The YP defines the PC @@ -170,7 +167,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // they are returned to the pools defer func() { returnStack(stack) - returnRStack(returns) }() contract.Input = input @@ -178,9 +174,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( defer func() { if err != nil { if !logged { - in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err) + in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) } else { - in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err) + in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err) } } }() @@ -217,7 +213,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } // If the operation is valid, enforce and write restrictions - if in.readOnly && in.evm.ChainConfig().IsEnabled(in.evm.chainConfig.GetEIP214Transition, in.evm.BlockNumber) { + if in.readOnly && in.evm.ChainConfig().IsEnabled(in.evm.chainConfig.GetEIP214Transition, in.evm.Context.BlockNumber) { // If the interpreter is operating in readonly mode, make sure no // state-modifying operation is performed. The 3rd stack item // for a call operation is the value. Transferring value from one @@ -266,7 +262,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } if in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err) + in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) logged = true } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 0a3cda5141..2d43ac9372 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -24,7 +24,7 @@ import ( ) type ( - executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) + executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 // memorySizeFunc returns the required size, and whether the operation overflowed a uint64 memorySizeFunc func(*Stack) (size uint64, overflow bool) @@ -174,19 +174,12 @@ func instructionSetForConfig(config ctypes.ChainConfigurator, bn *big.Int) JumpT if config.IsEnabled(config.GetECIP1080Transition, bn) { enableSelfBalance(&instructionSet) } - if config.IsEnabled(config.GetEIP2200Transition, bn) && !config.IsEnabled(config.GetEIP2200DisableTransition, bn) { enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200 } - - if config.IsEnabled(config.GetEIP2315Transition, bn) { - enable2315(&instructionSet) // Subroutines - https://eips.ethereum.org/EIPS/eip-2315 - } - if config.IsEnabled(config.GetEIP2929Transition, bn) { enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929 } - return instructionSet } diff --git a/core/vm/logger.go b/core/vm/logger.go index e661a73ad6..c64526f81e 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -18,7 +18,6 @@ package vm import ( "encoding/hex" - "errors" "fmt" "io" "math/big" @@ -32,8 +31,6 @@ import ( "github.com/ethereum/go-ethereum/params/types/ctypes" ) -var errTraceLimitReached = errors.New("the number of logs reached the specified limit") - // Storage represents a contract's storage. type Storage map[common.Hash]common.Hash @@ -70,7 +67,6 @@ type StructLog struct { Memory []byte `json:"memory"` MemorySize int `json:"memSize"` Stack []*big.Int `json:"stack"` - ReturnStack []uint32 `json:"returnStack"` ReturnData []byte `json:"returnData"` Storage map[common.Hash]common.Hash `json:"-"` Depth int `json:"depth"` @@ -81,7 +77,6 @@ type StructLog struct { // overrides for gencodec type structLogMarshaling struct { Stack []*math.HexOrDecimal256 - ReturnStack []math.HexOrDecimal64 Gas math.HexOrDecimal64 GasCost math.HexOrDecimal64 Memory hexutil.Bytes @@ -110,11 +105,11 @@ func (s *StructLog) ErrorString() string { // Note that reference types are actual VM data structures; make copies // if you need to retain them beyond the current call. type Tracer interface { - CapturePreEVM(env *EVM, inputs map[string]interface{}) error - CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error - CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error - CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error - CaptureEnd(env *EVM, output []byte, gasUsed uint64, t time.Duration, err error) error + CapturePreEVM(env *EVM, inputs map[string]interface{}) + CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) + CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) + CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) + CaptureEnd(env *EVM, output []byte, gasUsed uint64, t time.Duration, err error) } // StructLogger is an EVM state logger and implements Tracer. @@ -144,22 +139,23 @@ func NewStructLogger(cfg *LogConfig) *StructLogger { // CapturePreEVM implements the Tracer interface to bootstrap the tracing context, // before EVM init. This is useful for reading initial balance, state, etc. -func (l *StructLogger) CapturePreEVM(env *EVM, inputs map[string]interface{}) error { - return nil +func (l *StructLogger) CapturePreEVM(env *EVM, inputs map[string]interface{}) { } // CaptureStart implements the Tracer interface to initialize the tracing operation. -func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { - return nil +func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { } // CaptureState logs a new structured log message and pushes it out to the environment // // CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error { +func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { + memory := scope.Memory + stack := scope.Stack + contract := scope.Contract // check if already accumulated the specified number of logs if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { - return errTraceLimitReached + return } // Copy a snapshot of the current memory state to a new buffer var mem []byte @@ -175,11 +171,6 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui stck[i] = new(big.Int).Set(item.ToBig()) } } - var rstack []uint32 - if !l.cfg.DisableStack && rStack != nil { - rstck := make([]uint32, len(rStack.data)) - copy(rstck, rStack.data) - } // Copy a snapshot of the current storage to a new container var storage Storage if !l.cfg.DisableStorage { @@ -212,19 +203,17 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui copy(rdata, rData) } // create a new snapshot of the EVM. - log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, rdata, storage, depth, env.StateDB.GetRefund(), err} + log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, env.StateDB.GetRefund(), err} l.logs = append(l.logs, log) - return nil } // CaptureFault implements the Tracer interface to trace an execution fault // while running an opcode. -func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error { - return nil +func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { } // CaptureEnd is called after the call finishes to finalize the tracing. -func (l *StructLogger) CaptureEnd(env *EVM, output []byte, gasUsed uint64, t time.Duration, err error) error { +func (l *StructLogger) CaptureEnd(env *EVM, output []byte, gasUsed uint64, t time.Duration, err error) { l.output = output l.err = err if l.cfg.Debug { @@ -233,7 +222,6 @@ func (l *StructLogger) CaptureEnd(env *EVM, output []byte, gasUsed uint64, t tim fmt.Printf(" error: %v\n", err) } } - return nil } // StructLogs returns the captured log entries. @@ -260,12 +248,6 @@ func WriteTrace(writer io.Writer, logs []StructLog) { fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32)) } } - if len(log.ReturnStack) > 0 { - fmt.Fprintln(writer, "ReturnStack:") - for i := len(log.Stack) - 1; i >= 0; i-- { - fmt.Fprintf(writer, "%08d 0x%x (%d)\n", len(log.Stack)-i-1, log.ReturnStack[i], log.ReturnStack[i]) - } - } if len(log.Memory) > 0 { fmt.Fprintln(writer, "Memory:") fmt.Fprint(writer, hex.Dump(log.Memory)) @@ -313,11 +295,10 @@ func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger { return l } -func (t *mdLogger) CapturePreEVM(env *EVM, inputs map[string]interface{}) error { - return nil +func (t *mdLogger) CapturePreEVM(env *EVM, inputs map[string]interface{}) { } -func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { +func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { if !create { fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n", from.String(), to.String(), @@ -332,10 +313,11 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b | Pc | Op | Cost | Stack | RStack | Refund | |-------|-------------|------|-----------|-----------|---------| `) - return nil } -func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error { +// CaptureState also tracks SLOAD/SSTORE ops to track storage change. +func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { + stack := scope.Stack fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) if !t.cfg.DisableStack { @@ -346,32 +328,19 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64 } b := fmt.Sprintf("[%v]", strings.Join(a, ",")) fmt.Fprintf(t.out, "%10v |", b) - - // format return stack - a = a[:0] - for _, elem := range rStack.data { - a = append(a, fmt.Sprintf("%2d", elem)) - } - b = fmt.Sprintf("[%v]", strings.Join(a, ",")) - fmt.Fprintf(t.out, "%10v |", b) } fmt.Fprintf(t.out, "%10v |", env.StateDB.GetRefund()) fmt.Fprintln(t.out, "") if err != nil { fmt.Fprintf(t.out, "Error: %v\n", err) } - return nil } -func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error { - +func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) - - return nil } -func (t *mdLogger) CaptureEnd(env *EVM, output []byte, gasUsed uint64, tm time.Duration, err error) error { +func (t *mdLogger) CaptureEnd(env *EVM, output []byte, gasUsed uint64, tm time.Duration, err error) { fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n", output, gasUsed, err) - return nil } diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index cb72bfda5e..fd53924adb 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -41,16 +41,19 @@ func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger { return l } -func (l *JSONLogger) CapturePreEVM(env *EVM, inputs map[string]interface{}) error { - return nil +func (l *JSONLogger) CapturePreEVM(env *EVM, inputs map[string]interface{}) { } -func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { - return nil +func (l *JSONLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { } +func (l *JSONLogger) CaptureFault(*EVM, uint64, OpCode, uint64, uint64, *ScopeContext, int, error) {} + // CaptureState outputs state information on the logger. -func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error { +func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { + memory := scope.Memory + stack := scope.Stack + log := StructLog{ Pc: pc, Op: op, @@ -72,21 +75,15 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint logstack[i] = item.ToBig() } log.Stack = logstack - log.ReturnStack = rStack.data } if !l.cfg.DisableReturnData { log.ReturnData = rData } - return l.encoder.Encode(log) -} - -// CaptureFault outputs state information on the logger. -func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error { - return nil + l.encoder.Encode(log) } // CaptureEnd is triggered at end of execution. -func (l *JSONLogger) CaptureEnd(env *EVM, output []byte, gasUsed uint64, t time.Duration, err error) error { +func (l *JSONLogger) CaptureEnd(env *EVM, output []byte, gasUsed uint64, t time.Duration, err error) { type endLog struct { Output string `json:"output"` GasUsed math.HexOrDecimal64 `json:"gasUsed"` @@ -94,7 +91,7 @@ func (l *JSONLogger) CaptureEnd(env *EVM, output []byte, gasUsed uint64, t time. Err string `json:"error,omitempty"` } if err != nil { - return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()}) + l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()}) } - return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""}) + l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""}) } diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index e287f0c7aa..9c936af36a 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -51,19 +51,22 @@ func (*dummyStatedb) GetRefund() uint64 { return 1337 } func TestStoreCapture(t *testing.T) { var ( - env = NewEVM(Context{}, &dummyStatedb{}, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, &dummyStatedb{}, params.TestChainConfig, Config{}) logger = NewStructLogger(nil) - mem = NewMemory() - stack = newstack() - rstack = newReturnStack() contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) + scope = &ScopeContext{ + Memory: NewMemory(), + Stack: newstack(), + Contract: contract, + } ) - stack.push(uint256.NewInt().SetUint64(1)) - stack.push(uint256.NewInt()) + scope.Stack.push(uint256.NewInt().SetUint64(1)) + scope.Stack.push(uint256.NewInt()) var index common.Hash - logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, nil, contract, 0, nil) + logger.CaptureState(env, 0, SSTORE, 0, 0, scope, nil, 0, nil) if len(logger.storage[contract.Address()]) == 0 { - t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()])) + t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), + len(logger.storage[contract.Address()])) } exp := common.BigToHash(big.NewInt(1)) if logger.storage[contract.Address()][index] != exp { diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index da7b2ee4aa..b0adf37d0c 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -107,21 +107,18 @@ const ( // 0x50 range - 'storage' and execution. const ( - POP OpCode = 0x50 - MLOAD OpCode = 0x51 - MSTORE OpCode = 0x52 - MSTORE8 OpCode = 0x53 - SLOAD OpCode = 0x54 - SSTORE OpCode = 0x55 - JUMP OpCode = 0x56 - JUMPI OpCode = 0x57 - PC OpCode = 0x58 - MSIZE OpCode = 0x59 - GAS OpCode = 0x5a - JUMPDEST OpCode = 0x5b - BEGINSUB OpCode = 0x5c - RETURNSUB OpCode = 0x5d - JUMPSUB OpCode = 0x5e + POP OpCode = 0x50 + MLOAD OpCode = 0x51 + MSTORE OpCode = 0x52 + MSTORE8 OpCode = 0x53 + SLOAD OpCode = 0x54 + SSTORE OpCode = 0x55 + JUMP OpCode = 0x56 + JUMPI OpCode = 0x57 + PC OpCode = 0x58 + MSIZE OpCode = 0x59 + GAS OpCode = 0x5a + JUMPDEST OpCode = 0x5b ) // 0x60 range. @@ -300,10 +297,6 @@ var opCodeToString = map[OpCode]string{ GAS: "GAS", JUMPDEST: "JUMPDEST", - BEGINSUB: "BEGINSUB", - JUMPSUB: "JUMPSUB", - RETURNSUB: "RETURNSUB", - // 0x60 range - push. PUSH1: "PUSH1", PUSH2: "PUSH2", @@ -468,9 +461,6 @@ var stringToOp = map[string]OpCode{ "MSIZE": MSIZE, "GAS": GAS, "JUMPDEST": JUMPDEST, - "BEGINSUB": BEGINSUB, - "RETURNSUB": RETURNSUB, - "JUMPSUB": JUMPSUB, "PUSH1": PUSH1, "PUSH2": PUSH2, "PUSH3": PUSH3, diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 20983788af..24946d80f3 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -73,7 +73,7 @@ func gasSStoreEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m // return params.SloadGasEIP2200, nil return cost + WarmStorageReadCostEIP2929, nil // SLOAD_GAS } - original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) + original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32()) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) return cost + vars.SstoreSetGasEIP2200, nil diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 38ee448904..6c4c72eeac 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -22,18 +22,20 @@ import ( ) func NewEnv(cfg *Config) *vm.EVM { - context := vm.Context{ + txContext := vm.TxContext{ + Origin: cfg.Origin, + GasPrice: cfg.GasPrice, + } + blockContext := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, GetHash: cfg.GetHashFn, - Origin: cfg.Origin, Coinbase: cfg.Coinbase, BlockNumber: cfg.BlockNumber, Time: cfg.Time, Difficulty: cfg.Difficulty, GasLimit: cfg.GasLimit, - GasPrice: cfg.GasPrice, } - return vm.NewEVM(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig) + return vm.NewEVM(blockContext, txContext, cfg.State, cfg.ChainConfig, cfg.EVMConfig) } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 321f11e82c..7aab05bb17 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -65,7 +65,8 @@ func setDefaults(cfg *Config) { PetersburgBlock: new(big.Int), IstanbulBlock: new(big.Int), MuirGlacierBlock: new(big.Int), - YoloV2Block: nil, + BerlinBlock: new(big.Int), + YoloV3Block: nil, } } @@ -113,12 +114,8 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) ) - if cfg.ChainConfig.IsEnabled(cfg.ChainConfig.GetEIP2929Transition, vmenv.BlockNumber) { - cfg.State.AddAddressToAccessList(cfg.Origin) - cfg.State.AddAddressToAccessList(address) - for addr := range vm.PrecompiledContractsForConfig(cfg.ChainConfig, vmenv.BlockNumber) { - cfg.State.AddAddressToAccessList(addr) - } + if cfg.ChainConfig.IsEnabled(cfg.ChainConfig.GetEIP2929Transition, vmenv.Context.BlockNumber) { + cfg.State.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil) } cfg.State.CreateAccount(address) @@ -150,13 +147,9 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) ) - if cfg.ChainConfig.IsEnabled(cfg.ChainConfig.GetEIP2929Transition, vmenv.BlockNumber) { - cfg.State.AddAddressToAccessList(cfg.Origin) - for addr := range vm.PrecompiledContractsForConfig(cfg.ChainConfig, vmenv.BlockNumber) { - cfg.State.AddAddressToAccessList(addr) - } + if cfg.ChainConfig.IsEnabled(cfg.ChainConfig.GetEIP2929Transition, vmenv.Context.BlockNumber) { + cfg.State.PrepareAccessList(cfg.Origin, nil, vmenv.ActivePrecompiles(), nil) } - // Call the code with the given configuration. code, address, leftOverGas, err := vmenv.Create( sender, @@ -178,14 +171,10 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er vmenv := NewEnv(cfg) sender := cfg.State.GetOrNewStateObject(cfg.Origin) - if cfg.ChainConfig.IsEnabled(cfg.ChainConfig.GetEIP2929Transition, vmenv.BlockNumber) { - cfg.State.AddAddressToAccessList(cfg.Origin) - cfg.State.AddAddressToAccessList(address) - for addr := range vm.PrecompiledContractsForConfig(cfg.ChainConfig, vmenv.BlockNumber) { - cfg.State.AddAddressToAccessList(addr) - } + statedb := cfg.State + if cfg.ChainConfig.IsEnabled(cfg.ChainConfig.GetEIP2929Transition, vmenv.Context.BlockNumber) { + statedb.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil) } - // Call the code with the given configuration. ret, leftOverGas, err := vmenv.Call( sender, @@ -194,6 +183,5 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er cfg.GasLimit, cfg.Value, ) - return ret, leftOverGas, err } diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 96525291bc..78918bdf55 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -33,7 +33,6 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/types/goethereum" ) @@ -326,22 +325,20 @@ type stepCounter struct { steps int } -func (s *stepCounter) CapturePreEVM(env *vm.EVM, inputs map[string]interface{}) error { - return nil +func (s *stepCounter) CapturePreEVM(env *vm.EVM, inputs map[string]interface{}) { } func (s *stepCounter) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { return nil } -func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rData []byte, contract *vm.Contract, depth int, err error) error { +func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rData []byte, contract *vm.Contract, depth int, err error) error { s.steps++ // Enable this for more output //s.inner.CaptureState(env, pc, op, gas, cost, memory, stack, rStack, contract, depth, err) return nil } - -func (s *stepCounter) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error { +func (s *stepCounter) CaptureFault(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 { return nil } @@ -349,227 +346,6 @@ func (s *stepCounter) CaptureEnd(env *vm.EVM, output []byte, gasUsed uint64, t t return nil } -func TestJumpSub1024Limit(t *testing.T) { - state, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - address := common.HexToAddress("0x0a") - // Code is - // 0 beginsub - // 1 push 0 - // 3 jumpsub - // - // The code recursively calls itself. It should error when the returns-stack - // grows above 1023 - state.SetCode(address, []byte{ - byte(vm.PUSH1), 3, - byte(vm.JUMPSUB), - byte(vm.BEGINSUB), - byte(vm.PUSH1), 3, - byte(vm.JUMPSUB), - }) - tracer := stepCounter{inner: vm.NewJSONLogger(nil, os.Stdout)} - // Enable 2315 - _, _, err := Call(address, nil, &Config{State: state, - GasLimit: 20000, - ChainConfig: params.AllEthashProtocolChanges, - EVMConfig: vm.Config{ - ExtraEips: []int{2315}, - Debug: true, - //Tracer: vm.NewJSONLogger(nil, os.Stdout), - Tracer: &tracer, - }}) - exp := "return stack limit reached" - if err.Error() != exp { - t.Fatalf("expected %v, got %v", exp, err) - } - if exp, got := 2048, tracer.steps; exp != got { - t.Fatalf("expected %d steps, got %d", exp, got) - } -} - -func TestReturnSubShallow(t *testing.T) { - state, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - address := common.HexToAddress("0x0a") - // The code does returnsub without having anything on the returnstack. - // It should not panic, but just fail after one step - state.SetCode(address, []byte{ - byte(vm.PUSH1), 5, - byte(vm.JUMPSUB), - byte(vm.RETURNSUB), - byte(vm.PC), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - byte(vm.PC), - }) - tracer := stepCounter{} - - // Enable 2315 - _, _, err := Call(address, nil, &Config{State: state, - GasLimit: 10000, - ChainConfig: params.AllEthashProtocolChanges, - EVMConfig: vm.Config{ - ExtraEips: []int{2315}, - Debug: true, - Tracer: &tracer, - }}) - - exp := "invalid retsub" - if err.Error() != exp { - t.Fatalf("expected %v, got %v", exp, err) - } - if exp, got := 4, tracer.steps; exp != got { - t.Fatalf("expected %d steps, got %d", exp, got) - } -} - -// disabled -- only used for generating markdown -func DisabledTestReturnCases(t *testing.T) { - cfg := &Config{ - EVMConfig: vm.Config{ - Debug: true, - Tracer: vm.NewMarkdownLogger(nil, os.Stdout), - ExtraEips: []int{2315}, - }, - } - // This should fail at first opcode - Execute([]byte{ - byte(vm.RETURNSUB), - byte(vm.PC), - byte(vm.PC), - }, nil, cfg) - - // Should also fail - Execute([]byte{ - byte(vm.PUSH1), 5, - byte(vm.JUMPSUB), - byte(vm.RETURNSUB), - byte(vm.PC), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - byte(vm.PC), - }, nil, cfg) - - // This should complete - Execute([]byte{ - byte(vm.PUSH1), 0x4, - byte(vm.JUMPSUB), - byte(vm.STOP), - byte(vm.BEGINSUB), - byte(vm.PUSH1), 0x9, - byte(vm.JUMPSUB), - byte(vm.RETURNSUB), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - }, nil, cfg) -} - -// DisabledTestEipExampleCases contains various testcases that are used for the -// EIP examples -// This test is disabled, as it's only used for generating markdown -func DisabledTestEipExampleCases(t *testing.T) { - cfg := &Config{ - EVMConfig: vm.Config{ - Debug: true, - Tracer: vm.NewMarkdownLogger(nil, os.Stdout), - ExtraEips: []int{2315}, - }, - } - prettyPrint := func(comment string, code []byte) { - instrs := make([]string, 0) - it := asm.NewInstructionIterator(code) - for it.Next() { - if it.Arg() != nil && 0 < len(it.Arg()) { - instrs = append(instrs, fmt.Sprintf("%v 0x%x", it.Op(), it.Arg())) - } else { - instrs = append(instrs, fmt.Sprintf("%v", it.Op())) - } - } - ops := strings.Join(instrs, ", ") - - fmt.Printf("%v\nBytecode: `0x%x` (`%v`)\n", - comment, - code, ops) - Execute(code, nil, cfg) - } - - { // First eip testcase - code := []byte{ - byte(vm.PUSH1), 4, - byte(vm.JUMPSUB), - byte(vm.STOP), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - } - prettyPrint("This should jump into a subroutine, back out and stop.", code) - } - - { - code := []byte{ - byte(vm.PUSH9), 0x00, 0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, (4 + 8), - byte(vm.JUMPSUB), - byte(vm.STOP), - byte(vm.BEGINSUB), - byte(vm.PUSH1), 8 + 9, - byte(vm.JUMPSUB), - byte(vm.RETURNSUB), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - } - prettyPrint("This should execute fine, going into one two depths of subroutines", code) - } - // TODO(@holiman) move this test into an actual test, which not only prints - // out the trace. - { - code := []byte{ - byte(vm.PUSH9), 0x01, 0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, (4 + 8), - byte(vm.JUMPSUB), - byte(vm.STOP), - byte(vm.BEGINSUB), - byte(vm.PUSH1), 8 + 9, - byte(vm.JUMPSUB), - byte(vm.RETURNSUB), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - } - prettyPrint("This should fail, since the given location is outside of the "+ - "code-range. The code is the same as previous example, except that the "+ - "pushed location is `0x01000000000000000c` instead of `0x0c`.", code) - } - { - // This should fail at first opcode - code := []byte{ - byte(vm.RETURNSUB), - byte(vm.PC), - byte(vm.PC), - } - prettyPrint("This should fail at first opcode, due to shallow `return_stack`", code) - - } - { - code := []byte{ - byte(vm.PUSH1), 5, // Jump past the subroutine - byte(vm.JUMP), - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - byte(vm.JUMPDEST), - byte(vm.PUSH1), 3, // Now invoke the subroutine - byte(vm.JUMPSUB), - } - prettyPrint("In this example. the JUMPSUB is on the last byte of code. When the "+ - "subroutine returns, it should hit the 'virtual stop' _after_ the bytecode, "+ - "and not exit with error", code) - } - - { - code := []byte{ - byte(vm.BEGINSUB), - byte(vm.RETURNSUB), - byte(vm.STOP), - } - prettyPrint("In this example, the code 'walks' into a subroutine, which is not "+ - "allowed, and causes an error", code) - } -} - // benchmarkNonModifyingCode benchmarks code, but if the code modifies the // state, this should not be used, since it does not reset the state between runs. func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing.B) { diff --git a/core/vm/stack.go b/core/vm/stack.go index af27d6552c..c71d2653a5 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -98,34 +98,3 @@ func (st *Stack) Print() { } fmt.Println("#############") } - -var rStackPool = sync.Pool{ - New: func() interface{} { - return &ReturnStack{data: make([]uint32, 0, 10)} - }, -} - -// ReturnStack is an object for basic return stack operations. -type ReturnStack struct { - data []uint32 -} - -func newReturnStack() *ReturnStack { - return rStackPool.Get().(*ReturnStack) -} - -func returnRStack(rs *ReturnStack) { - rs.data = rs.data[:0] - rStackPool.Put(rs) -} - -func (st *ReturnStack) push(d uint32) { - st.data = append(st.data, d) -} - -// A uint32 is sufficient as for code below 4.2G -func (st *ReturnStack) pop() (ret uint32) { - ret = st.data[len(st.data)-1] - st.data = st.data[:len(st.data)-1] - return -} diff --git a/core/vm/testdata/precompiles/modexp_eip2565.json b/core/vm/testdata/precompiles/modexp_eip2565.json new file mode 100644 index 0000000000..c55441439e --- /dev/null +++ b/core/vm/testdata/precompiles/modexp_eip2565.json @@ -0,0 +1,121 @@ +[ + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002003fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "eip_example1", + "Gas": 1360, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "Expected": "0000000000000000000000000000000000000000000000000000000000000000", + "Name": "eip_example2", + "Gas": 1360, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb502fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + "Expected": "60008f1614cc01dcfb6bfb09c625cf90b47d4468db81b5f8b7a39d42f332eab9b2da8f2d95311648a8f243f4bb13cfb3d8f7f2a3c014122ebb3ed41b02783adc", + "Name": "nagydani-1-square", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb503fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + "Expected": "4834a46ba565db27903b1c720c9d593e84e4cbd6ad2e64b31885d944f68cd801f92225a8961c952ddf2797fa4701b330c85c4b363798100b921a1a22a46a7fec", + "Name": "nagydani-1-qube", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5010001fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + "Expected": "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2", + "Name": "nagydani-1-pow0x10001", + "Gas": 341, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5102e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "Expected": "981dd99c3b113fae3e3eaa9435c0dc96779a23c12a53d1084b4f67b0b053a27560f627b873e3f16ad78f28c94f14b6392def26e4d8896c5e3c984e50fa0b3aa44f1da78b913187c6128baa9340b1e9c9a0fd02cb78885e72576da4a8f7e5a113e173a7a2889fde9d407bd9f06eb05bc8fc7b4229377a32941a02bf4edcc06d70", + "Name": "nagydani-2-square", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "Expected": "d89ceb68c32da4f6364978d62aaa40d7b09b59ec61eb3c0159c87ec3a91037f7dc6967594e530a69d049b64adfa39c8fa208ea970cfe4b7bcd359d345744405afe1cbf761647e32b3184c7fbe87cee8c6c7ff3b378faba6c68b83b6889cb40f1603ee68c56b4c03d48c595c826c041112dc941878f8c5be828154afd4a16311f", + "Name": "nagydani-2-qube", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51010001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "Expected": "ad85e8ef13fd1dd46eae44af8b91ad1ccae5b7a1c92944f92a19f21b0b658139e0cabe9c1f679507c2de354bf2c91ebd965d1e633978a830d517d2f6f8dd5fd58065d58559de7e2334a878f8ec6992d9b9e77430d4764e863d77c0f87beede8f2f7f2ab2e7222f85cc9d98b8467f4bb72e87ef2882423ebdb6daf02dddac6db2", + "Name": "nagydani-2-pow0x10001", + "Gas": 1365, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "Expected": "affc7507ea6d84751ec6b3f0d7b99dbcc263f33330e450d1b3ff0bc3d0874320bf4edd57debd587306988157958cb3cfd369cc0c9c198706f635c9e0f15d047df5cb44d03e2727f26b083c4ad8485080e1293f171c1ed52aef5993a5815c35108e848c951cf1e334490b4a539a139e57b68f44fee583306f5b85ffa57206b3ee5660458858534e5386b9584af3c7f67806e84c189d695e5eb96e1272d06ec2df5dc5fabc6e94b793718c60c36be0a4d031fc84cd658aa72294b2e16fc240aef70cb9e591248e38bd49c5a554d1afa01f38dab72733092f7555334bbef6c8c430119840492380aa95fa025dcf699f0a39669d812b0c6946b6091e6e235337b6f8", + "Name": "nagydani-3-square", + "Gas": 341, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "Expected": "1b280ecd6a6bf906b806d527c2a831e23b238f89da48449003a88ac3ac7150d6a5e9e6b3be4054c7da11dd1e470ec29a606f5115801b5bf53bc1900271d7c3ff3cd5ed790d1c219a9800437a689f2388ba1a11d68f6a8e5b74e9a3b1fac6ee85fc6afbac599f93c391f5dc82a759e3c6c0ab45ce3f5d25d9b0c1bf94cf701ea6466fc9a478dacc5754e593172b5111eeba88557048bceae401337cd4c1182ad9f700852bc8c99933a193f0b94cf1aedbefc48be3bc93ef5cb276d7c2d5462ac8bb0c8fe8923a1db2afe1c6b90d59c534994a6a633f0ead1d638fdc293486bb634ff2c8ec9e7297c04241a61c37e3ae95b11d53343d4ba2b4cc33d2cfa7eb705e", + "Name": "nagydani-3-qube", + "Gas": 341, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "Expected": "37843d7c67920b5f177372fa56e2a09117df585f81df8b300fba245b1175f488c99476019857198ed459ed8d9799c377330e49f4180c4bf8e8f66240c64f65ede93d601f957b95b83efdee1e1bfde74169ff77002eaf078c71815a9220c80b2e3b3ff22c2f358111d816ebf83c2999026b6de50bfc711ff68705d2f40b753424aefc9f70f08d908b5a20276ad613b4ab4309a3ea72f0c17ea9df6b3367d44fb3acab11c333909e02e81ea2ed404a712d3ea96bba87461720e2d98723e7acd0520ac1a5212dbedcd8dc0c1abf61d4719e319ff4758a774790b8d463cdfe131d1b2dcfee52d002694e98e720cb6ae7ccea353bc503269ba35f0f63bf8d7b672a76", + "Name": "nagydani-3-pow0x10001", + "Gas": 5461, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "Expected": "8a5aea5f50dcc03dc7a7a272b5aeebc040554dbc1ffe36753c4fc75f7ed5f6c2cc0de3a922bf96c78bf0643a73025ad21f45a4a5cadd717612c511ab2bff1190fe5f1ae05ba9f8fe3624de1de2a817da6072ddcdb933b50216811dbe6a9ca79d3a3c6b3a476b079fd0d05f04fb154e2dd3e5cb83b148a006f2bcbf0042efb2ae7b916ea81b27aac25c3bf9a8b6d35440062ad8eae34a83f3ffa2cc7b40346b62174a4422584f72f95316f6b2bee9ff232ba9739301c97c99a9ded26c45d72676eb856ad6ecc81d36a6de36d7f9dafafee11baa43a4b0d5e4ecffa7b9b7dcefd58c397dd373e6db4acd2b2c02717712e6289bed7c813b670c4a0c6735aa7f3b0f1ce556eae9fcc94b501b2c8781ba50a8c6220e8246371c3c7359fe4ef9da786ca7d98256754ca4e496be0a9174bedbecb384bdf470779186d6a833f068d2838a88d90ef3ad48ff963b67c39cc5a3ee123baf7bf3125f64e77af7f30e105d72c4b9b5b237ed251e4c122c6d8c1405e736299c3afd6db16a28c6a9cfa68241e53de4cd388271fe534a6a9b0dbea6171d170db1b89858468885d08fecbd54c8e471c3e25d48e97ba450b96d0d87e00ac732aaa0d3ce4309c1064bd8a4c0808a97e0143e43a24cfa847635125cd41c13e0574487963e9d725c01375db99c31da67b4cf65eff555f0c0ac416c727ff8d438ad7c42030551d68c2e7adda0abb1ca7c10", + "Name": "nagydani-4-square", + "Gas": 1365, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "Expected": "5a2664252aba2d6e19d9600da582cdd1f09d7a890ac48e6b8da15ae7c6ff1856fc67a841ac2314d283ffa3ca81a0ecf7c27d89ef91a5a893297928f5da0245c99645676b481b7e20a566ee6a4f2481942bee191deec5544600bb2441fd0fb19e2ee7d801ad8911c6b7750affec367a4b29a22942c0f5f4744a4e77a8b654da2a82571037099e9c6d930794efe5cdca73c7b6c0844e386bdca8ea01b3d7807146bb81365e2cdc6475f8c23e0ff84463126189dc9789f72bbce2e3d2d114d728a272f1345122de23df54c922ec7a16e5c2a8f84da8871482bd258c20a7c09bbcd64c7a96a51029bbfe848736a6ba7bf9d931a9b7de0bcaf3635034d4958b20ae9ab3a95a147b0421dd5f7ebff46c971010ebfc4adbbe0ad94d5498c853e7142c450d8c71de4b2f84edbf8acd2e16d00c8115b150b1c30e553dbb82635e781379fe2a56360420ff7e9f70cc64c00aba7e26ed13c7c19622865ae07248daced36416080f35f8cc157a857ed70ea4f347f17d1bee80fa038abd6e39b1ba06b97264388b21364f7c56e192d4b62d9b161405f32ab1e2594e86243e56fcf2cb30d21adef15b9940f91af681da24328c883d892670c6aa47940867a81830a82b82716895db810df1b834640abefb7db2092dd92912cb9a735175bc447be40a503cf22dfe565b4ed7a3293ca0dfd63a507430b323ee248ec82e843b673c97ad730728cebc", + "Name": "nagydani-4-qube", + "Gas": 1365, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "Expected": "bed8b970c4a34849fc6926b08e40e20b21c15ed68d18f228904878d4370b56322d0da5789da0318768a374758e6375bfe4641fca5285ec7171828922160f48f5ca7efbfee4d5148612c38ad683ae4e3c3a053d2b7c098cf2b34f2cb19146eadd53c86b2d7ccf3d83b2c370bfb840913ee3879b1057a6b4e07e110b6bcd5e958bc71a14798c91d518cc70abee264b0d25a4110962a764b364ac0b0dd1ee8abc8426d775ec0f22b7e47b32576afaf1b5a48f64573ed1c5c29f50ab412188d9685307323d990802b81dacc06c6e05a1e901830ba9fcc67688dc29c5e27bde0a6e845ca925f5454b6fb3747edfaa2a5820838fb759eadf57f7cb5cec57fc213ddd8a4298fa079c3c0f472b07fb15aa6a7f0a3780bd296ff6a62e58ef443870b02260bd4fd2bbc98255674b8e1f1f9f8d33c7170b0ebbea4523b695911abbf26e41885344823bd0587115fdd83b721a4e8457a31c9a84b3d3520a07e0e35df7f48e5a9d534d0ec7feef1ff74de6a11e7f93eab95175b6ce22c68d78a642ad642837897ec11349205d8593ac19300207572c38d29ca5dfa03bc14cdbc32153c80e5cc3e739403d34c75915e49beb43094cc6dcafb3665b305ddec9286934ae66ec6b777ca528728c851318eb0f207b39f1caaf96db6eeead6b55ed08f451939314577d42bcc9f97c0b52d0234f88fd07e4c1d7780fdebc025cfffcb572cb27a8c33963", + "Name": "nagydani-4-pow0x10001", + "Gas": 21845, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "Expected": "d61fe4e3f32ac260915b5b03b78a86d11bfc41d973fce5b0cc59035cf8289a8a2e3878ea15fa46565b0d806e2f85b53873ea20ed653869b688adf83f3ef444535bf91598ff7e80f334fb782539b92f39f55310cc4b35349ab7b278346eda9bc37c0d8acd3557fae38197f412f8d9e57ce6a76b7205c23564cab06e5615be7c6f05c3d05ec690cba91da5e89d55b152ff8dd2157dc5458190025cf94b1ad98f7cbe64e9482faba95e6b33844afc640892872b44a9932096508f4a782a4805323808f23e54b6ff9b841dbfa87db3505ae4f687972c18ea0f0d0af89d36c1c2a5b14560c153c3fee406f5cf15cfd1c0bb45d767426d465f2f14c158495069d0c5955a00150707862ecaae30624ebacdd8ac33e4e6aab3ff90b6ba445a84689386b9e945d01823a65874444316e83767290fcff630d2477f49d5d8ffdd200e08ee1274270f86ed14c687895f6caf5ce528bd970c20d2408a9ba66216324c6a011ac4999098362dbd98a038129a2d40c8da6ab88318aa3046cb660327cc44236d9e5d2163bd0959062195c51ed93d0088b6f92051fc99050ece2538749165976233697ab4b610385366e5ce0b02ad6b61c168ecfbedcdf74278a38de340fd7a5fead8e588e294795f9b011e2e60377a89e25c90e145397cdeabc60fd32444a6b7642a611a83c464d8b8976666351b4865c37b02e6dc21dbcdf5f930341707b618cc0f03c3122646b3385c9df9f2ec730eec9d49e7dfc9153b6e6289da8c4f0ebea9ccc1b751948e3bb7171c9e4d57423b0eeeb79095c030cb52677b3f7e0b45c30f645391f3f9c957afa549c4e0b2465b03c67993cd200b1af01035962edbc4c9e89b31c82ac121987d6529dafdeef67a132dc04b6dc68e77f22862040b75e2ceb9ff16da0fca534e6db7bd12fa7b7f51b6c08c1e23dfcdb7acbd2da0b51c87ffbced065a612e9b1c8bba9b7e2d8d7a2f04fcc4aaf355b60d764879a76b5e16762d5f2f55d585d0c8e82df6940960cddfb72c91dfa71f6b4e1c6ca25dfc39a878e998a663c04fe29d5e83b9586d047b4d7ff70a9f0d44f127e7d741685ca75f11629128d916a0ffef4be586a30c4b70389cc746e84ebf177c01ee8a4511cfbb9d1ecf7f7b33c7dd8177896e10bbc82f838dcd6db7ac67de62bf46b6a640fb580c5d1d2708f3862e3d2b645d0d18e49ef088053e3a220adc0e033c2afcfe61c90e32151152eb3caaf746c5e377d541cafc6cbb0cc0fa48b5caf1728f2e1957f5addfc234f1a9d89e40d49356c9172d0561a695fce6dab1d412321bbf407f63766ffd7b6b3d79bcfa07991c5a9709849c1008689e3b47c50d613980bec239fb64185249d055b30375ccb4354d71fe4d05648fbf6c80634dfc3575f2f24abb714c1e4c95e8896763bf4316e954c7ad19e5780ab7a040ca6fb9271f90a8b22ae738daf6cb", + "Name": "nagydani-5-square", + "Gas": 5461, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "Expected": "5f9c70ec884926a89461056ad20ac4c30155e817f807e4d3f5bb743d789c83386762435c3627773fa77da5144451f2a8aad8adba88e0b669f5377c5e9bad70e45c86fe952b613f015a9953b8a5de5eaee4566acf98d41e327d93a35bd5cef4607d025e58951167957df4ff9b1627649d3943805472e5e293d3efb687cfd1e503faafeb2840a3e3b3f85d016051a58e1c9498aab72e63b748d834b31eb05d85dcde65e27834e266b85c75cc4ec0135135e0601cb93eeeb6e0010c8ceb65c4c319623c5e573a2c8c9fbbf7df68a930beb412d3f4dfd146175484f45d7afaa0d2e60684af9b34730f7c8438465ad3e1d0c3237336722f2aa51095bd5759f4b8ab4dda111b684aa3dac62a761722e7ae43495b7709933512c81c4e3c9133a51f7ce9f2b51fcec064f65779666960b4e45df3900f54311f5613e8012dd1b8efd359eda31a778264c72aa8bb419d862734d769076bce2810011989a45374e5c5d8729fec21427f0bf397eacbb4220f603cf463a4b0c94efd858ffd9768cd60d6ce68d755e0fbad007ce5c2223d70c7018345a102e4ab3c60a13a9e7794303156d4c2063e919f2153c13961fb324c80b240742f47773a7a8e25b3e3fb19b00ce839346c6eb3c732fbc6b888df0b1fe0a3d07b053a2e9402c267b2d62f794d8a2840526e3ade15ce2264496ccd7519571dfde47f7a4bb16292241c20b2be59f3f8fb4f6383f232d838c5a22d8c95b6834d9d2ca493f5a505ebe8899503b0e8f9b19e6e2dd81c1628b80016d02097e0134de51054c4e7674824d4d758760fc52377d2cad145e259aa2ffaf54139e1a66b1e0c1c191e32ac59474c6b526f5b3ba07d3e5ec286eddf531fcd5292869be58c9f22ef91026159f7cf9d05ef66b4299f4da48cc1635bf2243051d342d378a22c83390553e873713c0454ce5f3234397111ac3fe3207b86f0ed9fc025c81903e1748103692074f83824fda6341be4f95ff00b0a9a208c267e12fa01825054cc0513629bf3dbb56dc5b90d4316f87654a8be18227978ea0a8a522760cad620d0d14fd38920fb7321314062914275a5f99f677145a6979b156bd82ecd36f23f8e1273cc2759ecc0b2c69d94dad5211d1bed939dd87ed9e07b91d49713a6e16ade0a98aea789f04994e318e4ff2c8a188cd8d43aeb52c6daa3bc29b4af50ea82a247c5cd67b573b34cbadcc0a376d3bbd530d50367b42705d870f2e27a8197ef46070528bfe408360faa2ebb8bf76e9f388572842bcb119f4d84ee34ae31f5cc594f23705a49197b181fb78ed1ec99499c690f843a4d0cf2e226d118e9372271054fbabdcc5c92ae9fefaef0589cd0e722eaf30c1703ec4289c7fd81beaa8a455ccee5298e31e2080c10c366a6fcf56f7d13582ad0bcad037c612b710fc595b70fbefaaca23623b60c6c39b11beb8e5843b6b3dac60f", + "Name": "nagydani-5-qube", + "Gas": 5461, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "Expected": "5a0eb2bdf0ac1cae8e586689fa16cd4b07dfdedaec8a110ea1fdb059dd5253231b6132987598dfc6e11f86780428982d50cf68f67ae452622c3b336b537ef3298ca645e8f89ee39a26758206a5a3f6409afc709582f95274b57b71fae5c6b74619ae6f089a5393c5b79235d9caf699d23d88fb873f78379690ad8405e34c19f5257d596580c7a6a7206a3712825afe630c76b31cdb4a23e7f0632e10f14f4e282c81a66451a26f8df2a352b5b9f607a7198449d1b926e27036810368e691a74b91c61afa73d9d3b99453e7c8b50fd4f09c039a2f2feb5c419206694c31b92df1d9586140cb3417b38d0c503c7b508cc2ed12e813a1c795e9829eb39ee78eeaf360a169b491a1d4e419574e712402de9d48d54c1ae5e03739b7156615e8267e1fb0a897f067afd11fb33f6e24182d7aaaaa18fe5bc1982f20d6b871e5a398f0f6f718181d31ec225cfa9a0a70124ed9a70031bdf0c1c7829f708b6e17d50419ef361cf77d99c85f44607186c8d683106b8bd38a49b5d0fb503b397a83388c5678dcfcc737499d84512690701ed621a6f0172aecf037184ddf0f2453e4053024018e5ab2e30d6d5363b56e8b41509317c99042f517247474ab3abc848e00a07f69c254f46f2a05cf6ed84e5cc906a518fdcfdf2c61ce731f24c5264f1a25fc04934dc28aec112134dd523f70115074ca34e3807aa4cb925147f3a0ce152d323bd8c675ace446d0fd1ae30c4b57f0eb2c23884bc18f0964c0114796c5b6d080c3d89175665fbf63a6381a6a9da39ad070b645c8bb1779506da14439a9f5b5d481954764ea114fac688930bc68534d403cff4210673b6a6ff7ae416b7cd41404c3d3f282fcd193b86d0f54d0006c2a503b40d5c3930da980565b8f9630e9493a79d1c03e74e5f93ac8e4dc1a901ec5e3b3e57049124c7b72ea345aa359e782285d9e6a5c144a378111dd02c40855ff9c2be9b48425cb0b2fd62dc8678fd151121cf26a65e917d65d8e0dacfae108eb5508b601fb8ffa370be1f9a8b749a2d12eeab81f41079de87e2d777994fa4d28188c579ad327f9957fb7bdecec5c680844dd43cb57cf87aeb763c003e65011f73f8c63442df39a92b946a6bd968a1c1e4d5fa7d88476a68bd8e20e5b70a99259c7d3f85fb1b65cd2e93972e6264e74ebf289b8b6979b9b68a85cd5b360c1987f87235c3c845d62489e33acf85d53fa3561fe3a3aee18924588d9c6eba4edb7a4d106b31173e42929f6f0c48c80ce6a72d54eca7c0fe870068b7a7c89c63cdda593f5b32d3cb4ea8a32c39f00ab449155757172d66763ed9527019d6de6c9f2416aa6203f4d11c9ebee1e1d3845099e55504446448027212616167eb36035726daa7698b075286f5379cd3e93cb3e0cf4f9cb8d017facbb5550ed32d5ec5400ae57e47e2bf78d1eaeff9480cc765ceff39db500", + "Name": "nagydani-5-pow0x10001", + "Gas": 87381, + "NoBenchmark": false + } +] \ No newline at end of file diff --git a/crypto/bls12381/arithmetic_fallback.go b/crypto/bls12381/arithmetic_fallback.go index 19fb589104..91cabf4f3d 100644 --- a/crypto/bls12381/arithmetic_fallback.go +++ b/crypto/bls12381/arithmetic_fallback.go @@ -207,7 +207,7 @@ func lsubAssign(z, x *fe) { z[2], b = bits.Sub64(z[2], x[2], b) z[3], b = bits.Sub64(z[3], x[3], b) z[4], b = bits.Sub64(z[4], x[4], b) - z[5], b = bits.Sub64(z[5], x[5], b) + z[5], _ = bits.Sub64(z[5], x[5], b) } func neg(z *fe, x *fe) { diff --git a/crypto/bls12381/bls12_381_test.go b/crypto/bls12381/bls12_381_test.go index 51523c9ee7..6bf5834105 100644 --- a/crypto/bls12381/bls12_381_test.go +++ b/crypto/bls12381/bls12_381_test.go @@ -5,7 +5,7 @@ import ( "math/big" ) -var fuz int = 10 +var fuz = 10 func randScalar(max *big.Int) *big.Int { a, _ := rand.Int(rand.Reader, max) diff --git a/crypto/bls12381/fp_test.go b/crypto/bls12381/fp_test.go index 14bb4d7d65..97528d9db3 100644 --- a/crypto/bls12381/fp_test.go +++ b/crypto/bls12381/fp_test.go @@ -1393,6 +1393,15 @@ func BenchmarkMultiplication(t *testing.B) { } } +func BenchmarkInverse(t *testing.B) { + a, _ := new(fe).rand(rand.Reader) + b, _ := new(fe).rand(rand.Reader) + t.ResetTimer() + for i := 0; i < t.N; i++ { + inverse(a, b) + } +} + func padBytes(in []byte, size int) []byte { out := make([]byte, size) if len(in) > size { diff --git a/crypto/bls12381/g1.go b/crypto/bls12381/g1.go index 63d38f90c2..d853823cd2 100644 --- a/crypto/bls12381/g1.go +++ b/crypto/bls12381/g1.go @@ -266,9 +266,8 @@ func (g *G1) Add(r, p1, p2 *PointG1) *PointG1 { if t[1].equal(t[3]) { if t[0].equal(t[2]) { return g.Double(r, p1) - } else { - return r.Zero() } + return r.Zero() } sub(t[1], t[1], t[3]) double(t[4], t[1]) diff --git a/crypto/bls12381/g2.go b/crypto/bls12381/g2.go index 1d1a3258f7..fa110e3edf 100644 --- a/crypto/bls12381/g2.go +++ b/crypto/bls12381/g2.go @@ -287,9 +287,8 @@ func (g *G2) Add(r, p1, p2 *PointG2) *PointG2 { if t[1].equal(t[3]) { if t[0].equal(t[2]) { return g.Double(r, p1) - } else { - return r.Zero() } + return r.Zero() } g.f.sub(t[1], t[1], t[3]) g.f.double(t[4], t[1]) diff --git a/crypto/bn256/bn256_fuzz.go b/crypto/bn256/bn256_fuzz.go deleted file mode 100644 index 6aa1421170..0000000000 --- a/crypto/bn256/bn256_fuzz.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2018 Péter Szilágyi. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -// +build gofuzz - -package bn256 - -import ( - "bytes" - "math/big" - - cloudflare "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" - google "github.com/ethereum/go-ethereum/crypto/bn256/google" -) - -// FuzzAdd fuzzez bn256 addition between the Google and Cloudflare libraries. -func FuzzAdd(data []byte) int { - // Ensure we have enough data in the first place - if len(data) != 128 { - return 0 - } - // Ensure both libs can parse the first curve point - xc := new(cloudflare.G1) - _, errc := xc.Unmarshal(data[:64]) - - xg := new(google.G1) - _, errg := xg.Unmarshal(data[:64]) - - if (errc == nil) != (errg == nil) { - panic("parse mismatch") - } else if errc != nil { - return 0 - } - // Ensure both libs can parse the second curve point - yc := new(cloudflare.G1) - _, errc = yc.Unmarshal(data[64:]) - - yg := new(google.G1) - _, errg = yg.Unmarshal(data[64:]) - - if (errc == nil) != (errg == nil) { - panic("parse mismatch") - } else if errc != nil { - return 0 - } - // Add the two points and ensure they result in the same output - rc := new(cloudflare.G1) - rc.Add(xc, yc) - - rg := new(google.G1) - rg.Add(xg, yg) - - if !bytes.Equal(rc.Marshal(), rg.Marshal()) { - panic("add mismatch") - } - return 0 -} - -// FuzzMul fuzzez bn256 scalar multiplication between the Google and Cloudflare -// libraries. -func FuzzMul(data []byte) int { - // Ensure we have enough data in the first place - if len(data) != 96 { - return 0 - } - // Ensure both libs can parse the curve point - pc := new(cloudflare.G1) - _, errc := pc.Unmarshal(data[:64]) - - pg := new(google.G1) - _, errg := pg.Unmarshal(data[:64]) - - if (errc == nil) != (errg == nil) { - panic("parse mismatch") - } else if errc != nil { - return 0 - } - // Add the two points and ensure they result in the same output - rc := new(cloudflare.G1) - rc.ScalarMult(pc, new(big.Int).SetBytes(data[64:])) - - rg := new(google.G1) - rg.ScalarMult(pg, new(big.Int).SetBytes(data[64:])) - - if !bytes.Equal(rc.Marshal(), rg.Marshal()) { - panic("scalar mul mismatch") - } - return 0 -} - -func FuzzPair(data []byte) int { - // Ensure we have enough data in the first place - if len(data) != 192 { - return 0 - } - // Ensure both libs can parse the curve point - pc := new(cloudflare.G1) - _, errc := pc.Unmarshal(data[:64]) - - pg := new(google.G1) - _, errg := pg.Unmarshal(data[:64]) - - if (errc == nil) != (errg == nil) { - panic("parse mismatch") - } else if errc != nil { - return 0 - } - // Ensure both libs can parse the twist point - tc := new(cloudflare.G2) - _, errc = tc.Unmarshal(data[64:]) - - tg := new(google.G2) - _, errg = tg.Unmarshal(data[64:]) - - if (errc == nil) != (errg == nil) { - panic("parse mismatch") - } else if errc != nil { - return 0 - } - // Pair the two points and ensure thet result in the same output - if cloudflare.PairingCheck([]*cloudflare.G1{pc}, []*cloudflare.G2{tc}) != google.PairingCheck([]*google.G1{pg}, []*google.G2{tg}) { - panic("pair mismatch") - } - return 0 -} diff --git a/crypto/bn256/cloudflare/bn256.go b/crypto/bn256/cloudflare/bn256.go index 38822a76bf..4f607af2ad 100644 --- a/crypto/bn256/cloudflare/bn256.go +++ b/crypto/bn256/cloudflare/bn256.go @@ -9,8 +9,13 @@ // // This package specifically implements the Optimal Ate pairing over a 256-bit // Barreto-Naehrig curve as described in -// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is compatible -// with the implementation described in that paper. +// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is not +// compatible with the implementation described in that paper, as different +// parameters are chosen. +// +// (This package previously claimed to operate at a 128-bit security level. +// However, recent improvements in attacks mean that is no longer true. See +// https://moderncrypto.org/mail-archive/curves/2016/000740.html.) package bn256 import ( @@ -23,7 +28,7 @@ import ( func randomK(r io.Reader) (k *big.Int, err error) { for { k, err = rand.Int(r, Order) - if k.Sign() > 0 || err != nil { + if err != nil || k.Sign() > 0 { return } } diff --git a/crypto/bn256/google/bn256.go b/crypto/bn256/google/bn256.go index e0402e51f0..0a9d5cd35d 100644 --- a/crypto/bn256/google/bn256.go +++ b/crypto/bn256/google/bn256.go @@ -12,8 +12,9 @@ // // This package specifically implements the Optimal Ate pairing over a 256-bit // Barreto-Naehrig curve as described in -// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is compatible -// with the implementation described in that paper. +// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is not +// compatible with the implementation described in that paper, as different +// parameters are chosen. // // (This package previously claimed to operate at a 128-bit security level. // However, recent improvements in attacks mean that is no longer true. See diff --git a/crypto/bn256/google/constants.go b/crypto/bn256/google/constants.go index ab649d7f3f..2990bd9512 100644 --- a/crypto/bn256/google/constants.go +++ b/crypto/bn256/google/constants.go @@ -13,13 +13,16 @@ func bigFromBase10(s string) *big.Int { return n } -// u is the BN parameter that determines the prime: 1868033³. +// u is the BN parameter that determines the prime. var u = bigFromBase10("4965661367192848881") -// p is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1. +// P is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1. var P = bigFromBase10("21888242871839275222246405745257275088696311157297823662689037894645226208583") // Order is the number of elements in both G₁ and G₂: 36u⁴+36u³+18u²+6u+1. +// Needs to be highly 2-adic for efficient SNARK key and proof generation. +// Order - 1 = 2^28 * 3^2 * 13 * 29 * 983 * 11003 * 237073 * 405928799 * 1670836401704629 * 13818364434197438864469338081. +// Refer to https://eprint.iacr.org/2013/879.pdf and https://eprint.iacr.org/2013/507.pdf for more information on these parameters. var Order = bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495617") // xiToPMinus1Over6 is ξ^((p-1)/6) where ξ = i+9. diff --git a/crypto/crypto.go b/crypto/crypto.go index a4a49136a8..40969a2895 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -60,10 +60,23 @@ type KeccakState interface { Read([]byte) (int, error) } +// NewKeccakState creates a new KeccakState +func NewKeccakState() KeccakState { + return sha3.NewLegacyKeccak256().(KeccakState) +} + +// HashData hashes the provided data using the KeccakState and returns a 32 byte hash +func HashData(kh KeccakState, data []byte) (h common.Hash) { + kh.Reset() + kh.Write(data) + kh.Read(h[:]) + return h +} + // Keccak256 calculates and returns the Keccak256 hash of the input data. func Keccak256(data ...[]byte) []byte { b := make([]byte, 32) - d := sha3.NewLegacyKeccak256().(KeccakState) + d := NewKeccakState() for _, b := range data { d.Write(b) } @@ -74,7 +87,7 @@ func Keccak256(data ...[]byte) []byte { // Keccak256Hash calculates and returns the Keccak256 hash of the input data, // converting it to an internal Hash data structure. func Keccak256Hash(data ...[]byte) (h common.Hash) { - d := sha3.NewLegacyKeccak256().(KeccakState) + d := NewKeccakState() for _, b := range data { d.Write(b) } diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index f71ae8232a..f9b0d3e834 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -42,6 +42,13 @@ func TestKeccak256Hash(t *testing.T) { checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := Keccak256Hash(in); return h[:] }, msg, exp) } +func TestKeccak256Hasher(t *testing.T) { + msg := []byte("abc") + exp, _ := hex.DecodeString("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45") + hasher := NewKeccakState() + checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := HashData(hasher, in); return h[:] }, msg, exp) +} + func TestToECDSAErrors(t *testing.T) { if _, err := HexToECDSA("0000000000000000000000000000000000000000000000000000000000000000"); err == nil { t.Fatal("HexToECDSA should've returned error") diff --git a/crypto/secp256k1/curve.go b/crypto/secp256k1/curve.go index 5409ee1d2c..8f83cccad9 100644 --- a/crypto/secp256k1/curve.go +++ b/crypto/secp256k1/curve.go @@ -116,6 +116,10 @@ func (BitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool { // affineFromJacobian reverses the Jacobian transform. See the comment at the // top of the file. func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { + if z.Sign() == 0 { + return new(big.Int), new(big.Int) + } + zinv := new(big.Int).ModInverse(z, BitCurve.P) zinvsq := new(big.Int).Mul(zinv, zinv) diff --git a/crypto/secp256k1/dummy.go b/crypto/secp256k1/dummy.go new file mode 100644 index 0000000000..c0f2ee52c0 --- /dev/null +++ b/crypto/secp256k1/dummy.go @@ -0,0 +1,20 @@ +// +build dummy + +// This file is part of a workaround for `go mod vendor` which won't vendor +// C files if there's no Go file in the same directory. +// This would prevent the crypto/secp256k1/libsecp256k1/include/secp256k1.h file to be vendored. +// +// This Go file imports the c directory where there is another dummy.go file which +// is the second part of this workaround. +// +// These two files combined make it so `go mod vendor` behaves correctly. +// +// See this issue for reference: https://github.com/golang/go/issues/26366 + +package secp256k1 + +import ( + _ "github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include" + _ "github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src" + _ "github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery" +) diff --git a/crypto/secp256k1/libsecp256k1/contrib/dummy.go b/crypto/secp256k1/libsecp256k1/contrib/dummy.go new file mode 100644 index 0000000000..fda594be99 --- /dev/null +++ b/crypto/secp256k1/libsecp256k1/contrib/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package contrib diff --git a/crypto/secp256k1/libsecp256k1/dummy.go b/crypto/secp256k1/libsecp256k1/dummy.go new file mode 100644 index 0000000000..379b16992f --- /dev/null +++ b/crypto/secp256k1/libsecp256k1/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package libsecp256k1 diff --git a/crypto/secp256k1/libsecp256k1/include/dummy.go b/crypto/secp256k1/libsecp256k1/include/dummy.go new file mode 100644 index 0000000000..5af540c73c --- /dev/null +++ b/crypto/secp256k1/libsecp256k1/include/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package include diff --git a/crypto/secp256k1/libsecp256k1/src/dummy.go b/crypto/secp256k1/libsecp256k1/src/dummy.go new file mode 100644 index 0000000000..65868f38a8 --- /dev/null +++ b/crypto/secp256k1/libsecp256k1/src/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package src diff --git a/crypto/secp256k1/libsecp256k1/src/modules/dummy.go b/crypto/secp256k1/libsecp256k1/src/modules/dummy.go new file mode 100644 index 0000000000..3c7a696439 --- /dev/null +++ b/crypto/secp256k1/libsecp256k1/src/modules/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package module diff --git a/crypto/secp256k1/libsecp256k1/src/modules/ecdh/dummy.go b/crypto/secp256k1/libsecp256k1/src/modules/ecdh/dummy.go new file mode 100644 index 0000000000..b6fc38327e --- /dev/null +++ b/crypto/secp256k1/libsecp256k1/src/modules/ecdh/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package ecdh diff --git a/crypto/secp256k1/libsecp256k1/src/modules/recovery/dummy.go b/crypto/secp256k1/libsecp256k1/src/modules/recovery/dummy.go new file mode 100644 index 0000000000..b9491f0cb9 --- /dev/null +++ b/crypto/secp256k1/libsecp256k1/src/modules/recovery/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package recovery diff --git a/crypto/signify/signify.go b/crypto/signify/signify.go new file mode 100644 index 0000000000..e280f87268 --- /dev/null +++ b/crypto/signify/signify.go @@ -0,0 +1,100 @@ +// Copyright 2020 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 . + +// signFile reads the contents of an input file and signs it (in armored format) +// with the key provided, placing the signature into the output file. + +package signify + +import ( + "bytes" + "crypto/ed25519" + "encoding/base64" + "errors" + "fmt" + "io/ioutil" + "strings" + "time" +) + +var ( + errInvalidKeyHeader = errors.New("incorrect key header") + errInvalidKeyLength = errors.New("invalid, key length != 104") +) + +func parsePrivateKey(key string) (k ed25519.PrivateKey, header []byte, keyNum []byte, err error) { + keydata, err := base64.StdEncoding.DecodeString(key) + if err != nil { + return nil, nil, nil, err + } + if len(keydata) != 104 { + return nil, nil, nil, errInvalidKeyLength + } + if string(keydata[:2]) != "Ed" { + return nil, nil, nil, errInvalidKeyHeader + } + return keydata[40:], keydata[:2], keydata[32:40], nil +} + +// SignFile creates a signature of the input file. +// +// This accepts base64 keys in the format created by the 'signify' tool. +// The signature is written to the 'output' file. +func SignFile(input string, output string, key string, untrustedComment string, trustedComment string) error { + // Pre-check comments and ensure they're set to something. + if strings.IndexByte(untrustedComment, '\n') >= 0 { + return errors.New("untrusted comment must not contain newline") + } + if strings.IndexByte(trustedComment, '\n') >= 0 { + return errors.New("trusted comment must not contain newline") + } + if untrustedComment == "" { + untrustedComment = "verify with " + input + ".pub" + } + if trustedComment == "" { + trustedComment = fmt.Sprintf("timestamp:%d", time.Now().Unix()) + } + + filedata, err := ioutil.ReadFile(input) + if err != nil { + return err + } + skey, header, keyNum, err := parsePrivateKey(key) + if err != nil { + return err + } + + // Create the main data signature. + rawSig := ed25519.Sign(skey, filedata) + var dataSig []byte + dataSig = append(dataSig, header...) + dataSig = append(dataSig, keyNum...) + dataSig = append(dataSig, rawSig...) + + // Create the comment signature. + var commentSigInput []byte + commentSigInput = append(commentSigInput, rawSig...) + commentSigInput = append(commentSigInput, []byte(trustedComment)...) + commentSig := ed25519.Sign(skey, commentSigInput) + + // Create the output file. + var out = new(bytes.Buffer) + fmt.Fprintln(out, "untrusted comment:", untrustedComment) + fmt.Fprintln(out, base64.StdEncoding.EncodeToString(dataSig)) + fmt.Fprintln(out, "trusted comment:", trustedComment) + fmt.Fprintln(out, base64.StdEncoding.EncodeToString(commentSig)) + return ioutil.WriteFile(output, out.Bytes(), 0644) +} diff --git a/crypto/signify/signify_fuzz.go b/crypto/signify/signify_fuzz.go new file mode 100644 index 0000000000..f9167900ad --- /dev/null +++ b/crypto/signify/signify_fuzz.go @@ -0,0 +1,150 @@ +// Copyright 2020 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 . + +// +build gofuzz + +package signify + +import ( + "bufio" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + + fuzz "github.com/google/gofuzz" + "github.com/jedisct1/go-minisign" +) + +func Fuzz(data []byte) int { + if len(data) < 32 { + return -1 + } + tmpFile, err := ioutil.TempFile("", "") + if err != nil { + panic(err) + } + defer os.Remove(tmpFile.Name()) + defer tmpFile.Close() + + testSecKey, testPubKey := createKeyPair() + // Create message + tmpFile.Write(data) + if err = tmpFile.Close(); err != nil { + panic(err) + } + // Fuzz comments + var untrustedComment string + var trustedComment string + f := fuzz.NewFromGoFuzz(data) + f.Fuzz(&untrustedComment) + f.Fuzz(&trustedComment) + fmt.Printf("untrusted: %v\n", untrustedComment) + fmt.Printf("trusted: %v\n", trustedComment) + + err = SignifySignFile(tmpFile.Name(), tmpFile.Name()+".sig", testSecKey, untrustedComment, trustedComment) + if err != nil { + panic(err) + } + defer os.Remove(tmpFile.Name() + ".sig") + + signify := "signify" + path := os.Getenv("SIGNIFY") + if path != "" { + signify = path + } + + _, err := exec.LookPath(signify) + if err != nil { + panic(err) + } + + // Write the public key into the file to pass it as + // an argument to signify-openbsd + pubKeyFile, err := ioutil.TempFile("", "") + if err != nil { + panic(err) + } + defer os.Remove(pubKeyFile.Name()) + defer pubKeyFile.Close() + pubKeyFile.WriteString("untrusted comment: signify public key\n") + pubKeyFile.WriteString(testPubKey) + pubKeyFile.WriteString("\n") + + cmd := exec.Command(signify, "-V", "-p", pubKeyFile.Name(), "-x", tmpFile.Name()+".sig", "-m", tmpFile.Name()) + if output, err := cmd.CombinedOutput(); err != nil { + panic(fmt.Sprintf("could not verify the file: %v, output: \n%s", err, output)) + } + + // Verify the signature using a golang library + sig, err := minisign.NewSignatureFromFile(tmpFile.Name() + ".sig") + if err != nil { + panic(err) + } + + pKey, err := minisign.NewPublicKey(testPubKey) + if err != nil { + panic(err) + } + + valid, err := pKey.VerifyFromFile(tmpFile.Name(), sig) + if err != nil { + panic(err) + } + if !valid { + panic("invalid signature") + } + return 1 +} + +func getKey(fileS string) (string, error) { + file, err := os.Open(fileS) + if err != nil { + log.Fatal(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + // Discard the first line + scanner.Scan() + scanner.Scan() + return scanner.Text(), scanner.Err() +} + +func createKeyPair() (string, string) { + // Create key and put it in correct format + tmpKey, err := ioutil.TempFile("", "") + if err != nil { + panic(err) + } + defer os.Remove(tmpKey.Name()) + defer os.Remove(tmpKey.Name() + ".pub") + defer os.Remove(tmpKey.Name() + ".sec") + cmd := exec.Command("signify", "-G", "-n", "-p", tmpKey.Name()+".pub", "-s", tmpKey.Name()+".sec") + if output, err := cmd.CombinedOutput(); err != nil { + panic(fmt.Sprintf("could not verify the file: %v, output: \n%s", err, output)) + } + secKey, err := getKey(tmpKey.Name() + ".sec") + if err != nil { + panic(err) + } + pubKey, err := getKey(tmpKey.Name() + ".pub") + if err != nil { + panic(err) + } + return secKey, pubKey +} diff --git a/crypto/signify/signify_test.go b/crypto/signify/signify_test.go new file mode 100644 index 0000000000..615d4e6527 --- /dev/null +++ b/crypto/signify/signify_test.go @@ -0,0 +1,154 @@ +// Copyright 2020 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 . + +// signFile reads the contents of an input file and signs it (in armored format) +// with the key provided, placing the signature into the output file. + +package signify + +import ( + "io/ioutil" + "math/rand" + "os" + "testing" + "time" + + "github.com/jedisct1/go-minisign" +) + +var ( + testSecKey = "RWRCSwAAAABVN5lr2JViGBN8DhX3/Qb/0g0wBdsNAR/APRW2qy9Fjsfr12sK2cd3URUFis1jgzQzaoayK8x4syT4G3Gvlt9RwGIwUYIQW/0mTeI+ECHu1lv5U4Wa2YHEPIesVPyRm5M=" + testPubKey = "RWTAPRW2qy9FjsBiMFGCEFv9Jk3iPhAh7tZb+VOFmtmBxDyHrFT8kZuT" +) + +func TestSignify(t *testing.T) { + tmpFile, err := ioutil.TempFile("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpFile.Name()) + defer tmpFile.Close() + + rand.Seed(time.Now().UnixNano()) + + data := make([]byte, 1024) + rand.Read(data) + tmpFile.Write(data) + + if err = tmpFile.Close(); err != nil { + t.Fatal(err) + } + + err = SignFile(tmpFile.Name(), tmpFile.Name()+".sig", testSecKey, "clé", "croissants") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpFile.Name() + ".sig") + + // Verify the signature using a golang library + sig, err := minisign.NewSignatureFromFile(tmpFile.Name() + ".sig") + if err != nil { + t.Fatal(err) + } + + pKey, err := minisign.NewPublicKey(testPubKey) + if err != nil { + t.Fatal(err) + } + + valid, err := pKey.VerifyFromFile(tmpFile.Name(), sig) + if err != nil { + t.Fatal(err) + } + if !valid { + t.Fatal("invalid signature") + } +} + +func TestSignifyTrustedCommentTooManyLines(t *testing.T) { + tmpFile, err := ioutil.TempFile("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpFile.Name()) + defer tmpFile.Close() + + rand.Seed(time.Now().UnixNano()) + + data := make([]byte, 1024) + rand.Read(data) + tmpFile.Write(data) + + if err = tmpFile.Close(); err != nil { + t.Fatal(err) + } + + err = SignFile(tmpFile.Name(), tmpFile.Name()+".sig", testSecKey, "", "crois\nsants") + if err == nil || err.Error() == "" { + t.Fatalf("should have errored on a multi-line trusted comment, got %v", err) + } + defer os.Remove(tmpFile.Name() + ".sig") +} + +func TestSignifyTrustedCommentTooManyLinesLF(t *testing.T) { + tmpFile, err := ioutil.TempFile("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpFile.Name()) + defer tmpFile.Close() + + rand.Seed(time.Now().UnixNano()) + + data := make([]byte, 1024) + rand.Read(data) + tmpFile.Write(data) + + if err = tmpFile.Close(); err != nil { + t.Fatal(err) + } + + err = SignFile(tmpFile.Name(), tmpFile.Name()+".sig", testSecKey, "crois\rsants", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpFile.Name() + ".sig") +} + +func TestSignifyTrustedCommentEmpty(t *testing.T) { + tmpFile, err := ioutil.TempFile("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpFile.Name()) + defer tmpFile.Close() + + rand.Seed(time.Now().UnixNano()) + + data := make([]byte, 1024) + rand.Read(data) + tmpFile.Write(data) + + if err = tmpFile.Close(); err != nil { + t.Fatal(err) + } + + err = SignFile(tmpFile.Name(), tmpFile.Name()+".sig", testSecKey, "", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpFile.Name() + ".sig") +} diff --git a/docs/JSON-RPC-API/modules/admin.md b/docs/JSON-RPC-API/modules/admin.md index 75a9bd20e8..fda3c27801 100755 --- a/docs/JSON-RPC-API/modules/admin.md +++ b/docs/JSON-RPC-API/modules/admin.md @@ -7,7 +7,7 @@ | Entity | Version | | --- | --- | -| Source | 1.11.22-unstable/generated-at:2021-01-23T04:50:40-06:00 | +| Source | 1.11.23-unstable/generated-at:2021-04-30T19:24:24+03:00 | | OpenRPC | 1.2.6 | --- @@ -23,7 +23,7 @@ connection at all times, even reconnecting if it is lost. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -50,12 +50,24 @@ url string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_addPeer", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_addPeer", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_addPeer", "params": []}' ``` + === "Javascript Console" ``` js @@ -63,6 +75,7 @@ url string ``` +
Source code

```go @@ -81,7 +94,7 @@ func (api *privateAdminAPI) AddPeer(url string) (bool, error) { // connection at all times, even reconnecting if it is lost. ``` -View on GitHub → +View on GitHub →

@@ -96,7 +109,7 @@ AddTrustedPeer allows a remote node to always connect, even if slots are full #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -123,12 +136,24 @@ url string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_addTrustedPeer", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_addTrustedPeer", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_addTrustedPeer", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -136,6 +161,7 @@ url string ``` +
Source code

```go @@ -153,7 +179,7 @@ func (api *privateAdminAPI) AddTrustedPeer(url string) (bool, error) { }// AddTrustedPeer allows a remote node to always connect, even if slots are full ``` -View on GitHub → +View on GitHub →

@@ -184,12 +210,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_datadir", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_datadir", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_datadir", "params": []}' ``` + === "Javascript Console" ``` js @@ -197,6 +235,7 @@ _None_ ``` +
Source code

```go @@ -205,7 +244,7 @@ func (api *publicAdminAPI) Datadir() string { }// Datadir retrieves the current data directory the node is using. ``` -View on GitHub → +View on GitHub →

@@ -219,7 +258,7 @@ func (api *publicAdminAPI) Datadir() string { #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -227,7 +266,7 @@ blockNr rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -299,12 +338,24 @@ blockNr rpc.BlockNumber #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_ecbp1100", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_ecbp1100", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_ecbp1100", "params": []}' ``` + === "Javascript Console" ``` js @@ -312,6 +363,7 @@ blockNr rpc.BlockNumber ``` +
Source code

```go @@ -321,7 +373,7 @@ func (api *PrivateAdminAPI) Ecbp1100(blockNr rpc.BlockNumber) (bool, error) { return api.eth.blockchain.IsArtificialFinalityEnabled() && api.eth.blockchain.Config().IsEnabled(api.eth.blockchain.Config().GetECBP1100Transition, api.eth.blockchain.CurrentBlock().Number()), err } ``` -View on GitHub → +View on GitHub →

@@ -337,7 +389,7 @@ or a range of blocks if first and last are non-nil #### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -354,7 +406,7 @@ first *uint64 + Required: ✓ Yes - + === "Schema" ``` Schema @@ -388,7 +440,7 @@ last *uint64 + Required: ✓ Yes - + === "Schema" ``` Schema @@ -432,12 +484,24 @@ last *uint64 #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_exportChain", "params": [, , ]}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_exportChain", "params": [, , ]}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_exportChain", "params": [, , ]}' + ``` + + === "Javascript Console" ``` js @@ -445,6 +509,7 @@ last *uint64 ``` +
Source code

```go @@ -481,7 +546,7 @@ func (api *PrivateAdminAPI) ExportChain(file string, first *uint64, last *uint64 // or a range of blocks if first and last are non-nil ``` -View on GitHub → +View on GitHub →

@@ -496,7 +561,7 @@ ImportChain imports a blockchain from a local file. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -523,12 +588,24 @@ file string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_importChain", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_importChain", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_importChain", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -536,6 +613,7 @@ file string ``` +
Source code

```go @@ -580,7 +658,7 @@ func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) { return true, nil } ``` -View on GitHub → +View on GitHub →

@@ -595,7 +673,7 @@ MaxPeers sets the maximum peer limit for the protocol manager and the p2p server #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -603,7 +681,7 @@ n int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -647,12 +725,24 @@ n int #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_maxPeers", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_maxPeers", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_maxPeers", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -660,24 +750,25 @@ n int ``` +
Source code

```go func (api *PrivateAdminAPI) MaxPeers(n int) (bool, error) { - api.eth.protocolManager.maxPeers = n + api.eth.handler.maxPeers = n api.eth.p2pServer.MaxPeers = n - for i := api.eth.protocolManager.peers.Len(); i > n; i = api.eth.protocolManager.peers.Len() { - p := api.eth.protocolManager.peers.WorstPeer() + for i := api.eth.handler.peers.len(); i > n; i = api.eth.handler.peers.len() { + p := api.eth.handler.peers.WorstPeer() if p == nil { break } - api.eth.protocolManager.removePeer(p.id) + api.eth.handler.removePeer(p.ID()) } return true, nil }// MaxPeers sets the maximum peer limit for the protocol manager and the p2p server. ``` -View on GitHub → +View on GitHub →

@@ -704,7 +795,7 @@ _None_ + Required: ✓ Yes - + === "Schema" ``` Schema @@ -834,12 +925,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_nodeInfo", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_nodeInfo", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_nodeInfo", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -847,6 +950,7 @@ _None_ ``` +
Source code

```go @@ -860,7 +964,113 @@ func (api *publicAdminAPI) NodeInfo() (*p2p.NodeInfo, error) { // protocol granularity. ``` -View on GitHub → +View on GitHub → +

+
+ +--- + + + +### admin_peerEvents + +PeerEvents creates an RPC subscription which receives peer events from the +node's p2p.Server + + +#### Params (0) + +_None_ + +#### Result + + + + +*rpc.Subscription + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + + + +#### Client Method Invocation Examples + + + + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_subscribe", "params": ["peerEvents"]}' + ``` + + + + +
Source code +

+```go +func (api *privateAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, error) { + server := api.node.Server() + if server == nil { + return nil, ErrNodeStopped + } + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return nil, rpc.ErrNotificationsUnsupported + } + rpcSub := notifier.CreateSubscription() + go func() { + events := make(chan *p2p.PeerEvent) + sub := server.SubscribeEvents(events) + defer sub.Unsubscribe() + for { + select { + case event := <-events: + notifier.Notify(rpcSub.ID, event) + case <-sub.Err(): + return + case <-rpcSub.Err(): + return + case <-notifier.Closed(): + return + } + } + }() + return rpcSub, nil +}// PeerEvents creates an RPC subscription which receives peer events from the +// node's p2p.Server + +``` +View on GitHub →

@@ -886,7 +1096,7 @@ p2pPeerInfo []*p2p.PeerInfo + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1048,12 +1258,24 @@ p2pPeerInfo []*p2p.PeerInfo #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_peers", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_peers", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_peers", "params": []}' ``` + === "Javascript Console" ``` js @@ -1061,6 +1283,7 @@ p2pPeerInfo []*p2p.PeerInfo ``` +
Source code

```go @@ -1074,7 +1297,7 @@ func (api *publicAdminAPI) Peers() ([ // Peers retrieves all the information we return server.PeersInfo(), nil } ``` -View on GitHub → +View on GitHub →

@@ -1089,7 +1312,7 @@ RemovePeer disconnects from a remote node if the connection exists #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1116,12 +1339,24 @@ url string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_removePeer", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_removePeer", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_removePeer", "params": []}' ``` + === "Javascript Console" ``` js @@ -1129,6 +1364,7 @@ url string ``` +
Source code

```go @@ -1146,7 +1382,7 @@ func (api *privateAdminAPI) RemovePeer(url string) (bool, error) { }// RemovePeer disconnects from a remote node if the connection exists ``` -View on GitHub → +View on GitHub →

@@ -1162,7 +1398,7 @@ does not disconnect it automatically. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1189,12 +1425,24 @@ url string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_removeTrustedPeer", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_removeTrustedPeer", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_removeTrustedPeer", "params": []}' ``` + === "Javascript Console" ``` js @@ -1202,6 +1450,7 @@ url string ``` +
Source code

```go @@ -1220,7 +1469,7 @@ func (api *privateAdminAPI) RemoveTrustedPeer(url string) (bool, error) { // does not disconnect it automatically. ``` -View on GitHub → +View on GitHub →

@@ -1235,7 +1484,7 @@ StartRPC starts the HTTP RPC API server. #### Params (5) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1252,7 +1501,7 @@ port *int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1323,12 +1572,24 @@ vhosts *string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_startRPC", "params": [, , , , ]}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_startRPC", "params": [, , , , ]}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_startRPC", "params": [, , , , ]}' + ``` + + === "Javascript Console" ``` js @@ -1336,6 +1597,7 @@ vhosts *string ``` +
Source code

```go @@ -1384,7 +1646,7 @@ func (api *privateAdminAPI) StartRPC(host *string, port *int, cors *string, apis return true, nil } ``` -View on GitHub → +View on GitHub →

@@ -1399,7 +1661,7 @@ StartWS starts the websocket RPC API server. #### Params (4) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1416,7 +1678,7 @@ port *int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1478,12 +1740,24 @@ apis *string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_startWS", "params": [, , , ]}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_startWS", "params": [, , , ]}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_startWS", "params": [, , , ]}' + ``` + + === "Javascript Console" ``` js @@ -1491,6 +1765,7 @@ apis *string ``` +
Source code

```go @@ -1535,7 +1810,7 @@ func (api *privateAdminAPI) StartWS(host *string, port *int, allowedOrigins *str return true, nil } ``` -View on GitHub → +View on GitHub →

@@ -1566,12 +1841,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_stopRPC", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_stopRPC", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_stopRPC", "params": []}' ``` + === "Javascript Console" ``` js @@ -1579,6 +1866,7 @@ _None_ ``` +
Source code

```go @@ -1588,7 +1876,7 @@ func (api *privateAdminAPI) StopRPC() (bool, error) { }// StopRPC shuts down the HTTP server. ``` -View on GitHub → +View on GitHub →

@@ -1619,12 +1907,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "admin_stopWS", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "admin_stopWS", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "admin_stopWS", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -1632,6 +1932,7 @@ _None_ ``` +
Source code

```go @@ -1642,7 +1943,7 @@ func (api *privateAdminAPI) StopWS() (bool, error) { }// StopWS terminates all WebSocket servers. ``` -View on GitHub → +View on GitHub →

diff --git a/docs/JSON-RPC-API/modules/debug.md b/docs/JSON-RPC-API/modules/debug.md index ecf15d36ad..84eb562db5 100755 --- a/docs/JSON-RPC-API/modules/debug.md +++ b/docs/JSON-RPC-API/modules/debug.md @@ -7,7 +7,7 @@ | Entity | Version | | --- | --- | -| Source | 1.11.22-unstable/generated-at:2021-01-23T04:50:40-06:00 | +| Source | 1.11.23-unstable/generated-at:2021-04-30T19:24:24+03:00 | | OpenRPC | 1.2.6 | --- @@ -22,7 +22,7 @@ AccountRange enumerates all accounts in the given block and start point in pagin #### Params (6) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -39,7 +39,7 @@ start []byte + Required: ✓ Yes - + === "Schema" ``` Schema @@ -73,7 +73,7 @@ maxResults int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -139,7 +139,7 @@ incompletes bool + Required: ✓ Yes - + === "Schema" ``` Schema @@ -277,12 +277,24 @@ incompletes bool #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_accountRange", "params": [, , , , , ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_accountRange", "params": [, , , , , ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_accountRange", "params": [, , , , , ]}' ``` + === "Javascript Console" ``` js @@ -290,6 +302,7 @@ incompletes bool ``` +
Source code

```go @@ -333,7 +346,7 @@ func (api *PublicDebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, sta return stateDb.IteratorDump(nocode, nostorage, incompletes, start, maxResults), nil } ``` -View on GitHub → +View on GitHub →

@@ -349,7 +362,7 @@ the pattern syntax. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -368,12 +381,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_backtraceAt", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_backtraceAt", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_backtraceAt", "params": []}' ``` + === "Javascript Console" ``` js @@ -381,6 +406,7 @@ _None_ ``` +
Source code

```go @@ -390,7 +416,7 @@ func (*HandlerT) BacktraceAt(location string) error { // the pattern syntax. ``` -View on GitHub → +View on GitHub →

@@ -407,7 +433,7 @@ desired, set the rate and write the profile manually. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -424,7 +450,7 @@ nsec uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -460,12 +486,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_blockProfile", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_blockProfile", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_blockProfile", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -473,6 +511,7 @@ _None_ ``` +
Source code

```go @@ -486,7 +525,7 @@ func (*HandlerT) BlockProfile(file string, nsec uint) error { // desired, set the rate and write the profile manually. ``` -View on GitHub → +View on GitHub →

@@ -510,12 +549,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_chaindbCompact", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_chaindbCompact", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_chaindbCompact", "params": []}' ``` + === "Javascript Console" ``` js @@ -523,6 +574,7 @@ _None_ ``` +
Source code

```go @@ -539,7 +591,7 @@ func (api *PrivateDebugAPI) ChaindbCompact() error { return nil } ``` -View on GitHub → +View on GitHub →

@@ -554,7 +606,7 @@ ChaindbProperty returns leveldb properties of the key-value database. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -581,12 +633,24 @@ property string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_chaindbProperty", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_chaindbProperty", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_chaindbProperty", "params": []}' ``` + === "Javascript Console" ``` js @@ -594,6 +658,7 @@ property string ``` +
Source code

```go @@ -607,7 +672,7 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { }// ChaindbProperty returns leveldb properties of the key-value database. ``` -View on GitHub → +View on GitHub →

@@ -623,7 +688,7 @@ profile data to file. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -640,7 +705,7 @@ nsec uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -676,12 +741,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_cpuProfile", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_cpuProfile", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_cpuProfile", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -689,6 +766,7 @@ _None_ ``` +
Source code

```go @@ -703,7 +781,7 @@ func (h *HandlerT) CpuProfile(file string, nsec uint) error { // profile data to file. ``` -View on GitHub → +View on GitHub →

@@ -718,7 +796,7 @@ DumpBlock retrieves the entire state of the database at a given block. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -726,7 +804,7 @@ blockNr rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -793,7 +871,7 @@ blockNr rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -921,12 +999,24 @@ blockNr rpc.BlockNumber #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_dumpBlock", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_dumpBlock", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_dumpBlock", "params": []}' ``` + === "Javascript Console" ``` js @@ -934,6 +1024,7 @@ blockNr rpc.BlockNumber ``` +
Source code

```go @@ -959,7 +1050,7 @@ func (api *PublicDebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error }// DumpBlock retrieves the entire state of the database at a given block. ``` -View on GitHub → +View on GitHub →

@@ -982,12 +1073,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_freeOSMemory", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_freeOSMemory", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_freeOSMemory", "params": []}' ``` + === "Javascript Console" ``` js @@ -995,6 +1098,7 @@ _None_ ``` +
Source code

```go @@ -1003,7 +1107,7 @@ func (*HandlerT) FreeOSMemory() { }// FreeOSMemory forces a garbage collection. ``` -View on GitHub → +View on GitHub →

@@ -1029,7 +1133,7 @@ _None_ + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1137,12 +1241,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_gcStats", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_gcStats", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_gcStats", "params": []}' ``` + === "Javascript Console" ``` js @@ -1150,6 +1266,7 @@ _None_ ``` +
Source code

```go @@ -1160,7 +1277,7 @@ func (*HandlerT) GcStats() *debug.GCStats { }// GcStats returns GC statistics. ``` -View on GitHub → +View on GitHub →

@@ -1186,7 +1303,7 @@ BadBlockArgs []*BadBlockArgs + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1484,12 +1601,24 @@ BadBlockArgs []*BadBlockArgs #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_getBadBlocks", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_getBadBlocks", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_getBadBlocks", "params": []}' ``` + === "Javascript Console" ``` js @@ -1497,30 +1626,37 @@ BadBlockArgs []*BadBlockArgs ``` +
Source code

```go func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([ // GetBadBlocks returns a list of the last 'bad blocks' that the client has seen on the network // and returns them as a JSON list of block-hashes ]*BadBlockArgs, error) { - blocks := api.eth.BlockChain().BadBlocks() - results := make([]*BadBlockArgs, len(blocks)) - var err error - for i, block := range blocks { - results[i] = &BadBlockArgs{Hash: block.Hash()} + var ( + err error + blocks = rawdb.ReadAllBadBlocks(api.eth.chainDb) + results = make([]*BadBlockArgs, 0, len(blocks)) + ) + for _, block := range blocks { + var ( + blockRlp string + blockJSON *ethapi.RPCMarshalBlockT + ) if rlpBytes, err := rlp.EncodeToBytes(block); err != nil { - results[i].RLP = err.Error() + blockRlp = err.Error() } else { - results[i].RLP = fmt.Sprintf("0x%x", rlpBytes) + blockRlp = fmt.Sprintf("0x%x", rlpBytes) } - if results[i].Block, err = ethapi.RPCMarshalBlock(block, true, true); err != nil { - results[i].Block = ðapi.RPCMarshalBlockT{Error: err.Error()} + if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true); err != nil { + blockJSON = ðapi.RPCMarshalBlockT{Error: err.Error()} } + results = append(results, &BadBlockArgs{Hash: block.Hash(), RLP: blockRlp, Block: blockJSON}) } return results, nil } ``` -View on GitHub → +View on GitHub →

@@ -1535,7 +1671,7 @@ GetBlockRlp retrieves the RLP encoded for of a single block. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1543,7 +1679,7 @@ number uint64 + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1587,12 +1723,24 @@ number uint64 #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_getBlockRlp", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_getBlockRlp", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_getBlockRlp", "params": []}' ``` + === "Javascript Console" ``` js @@ -1600,6 +1748,7 @@ number uint64 ``` +
Source code

```go @@ -1616,7 +1765,7 @@ func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (stri }// GetBlockRlp retrieves the RLP encoded for of a single block. ``` -View on GitHub → +View on GitHub →

@@ -1635,7 +1784,7 @@ With one parameter, returns the list of accounts modified in the specified block #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1643,7 +1792,7 @@ startHash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1677,7 +1826,7 @@ endHash *common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1715,7 +1864,7 @@ commonAddress []common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1757,12 +1906,24 @@ commonAddress []common.Address #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_getModifiedAccountsByHash", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_getModifiedAccountsByHash", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_getModifiedAccountsByHash", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -1770,6 +1931,7 @@ commonAddress []common.Address ``` +
Source code

```go @@ -1799,7 +1961,7 @@ func (api *PrivateDebugAPI) GetModifiedAccountsByHash(startHash common.Hash, end return api.getModifiedAccounts(startBlock, endBlock) } ``` -View on GitHub → +View on GitHub →

@@ -1818,7 +1980,7 @@ With one parameter, returns the list of accounts modified in the specified block #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1826,7 +1988,7 @@ startNum uint64 + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1860,7 +2022,7 @@ endNum *uint64 + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1898,7 +2060,7 @@ commonAddress []common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1940,12 +2102,24 @@ commonAddress []common.Address #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_getModifiedAccountsByNumber", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_getModifiedAccountsByNumber", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_getModifiedAccountsByNumber", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -1953,6 +2127,7 @@ commonAddress []common.Address ``` +
Source code

```go @@ -1982,7 +2157,7 @@ func (api *PrivateDebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum return api.getModifiedAccounts(startBlock, endBlock) } ``` -View on GitHub → +View on GitHub →

@@ -1998,7 +2173,7 @@ trace data to file. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -2015,7 +2190,7 @@ nsec uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2051,12 +2226,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_goTrace", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_goTrace", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_goTrace", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -2064,6 +2251,7 @@ _None_ ``` +
Source code

```go @@ -2078,7 +2266,7 @@ func (h *HandlerT) GoTrace(file string, nsec uint) error { // trace data to file. ``` -View on GitHub → +View on GitHub →

@@ -2104,7 +2292,7 @@ _None_ + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2508,12 +2696,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_memStats", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_memStats", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_memStats", "params": []}' ``` + === "Javascript Console" ``` js @@ -2521,6 +2721,7 @@ _None_ ``` +
Source code

```go @@ -2531,7 +2732,7 @@ func (*HandlerT) MemStats() *runtime.MemStats { }// MemStats returns detailed runtime memory statistics. ``` -View on GitHub → +View on GitHub →

@@ -2548,7 +2749,7 @@ desired, set the rate and write the profile manually. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -2565,7 +2766,7 @@ nsec uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2601,12 +2802,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_mutexProfile", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_mutexProfile", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_mutexProfile", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -2614,6 +2827,7 @@ _None_ ``` +
Source code

```go @@ -2627,7 +2841,7 @@ func (*HandlerT) MutexProfile(file string, nsec uint) error { // desired, set the rate and write the profile manually. ``` -View on GitHub → +View on GitHub →

@@ -2642,7 +2856,7 @@ Preimage is a debug API function that returns the preimage for a sha3 hash, if k #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -2650,7 +2864,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2689,7 +2903,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2719,12 +2933,24 @@ hash common.Hash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_preimage", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_preimage", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_preimage", "params": []}' ``` + === "Javascript Console" ``` js @@ -2732,6 +2958,7 @@ hash common.Hash ``` +
Source code

```go @@ -2743,7 +2970,7 @@ func (api *PrivateDebugAPI) Preimage(ctx context.Context, hash common.Hash) (hex }// Preimage is a debug API function that returns the preimage for a sha3 hash, if known. ``` -View on GitHub → +View on GitHub →

@@ -2758,7 +2985,7 @@ PrintBlock retrieves a block and returns its pretty printed form. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -2766,7 +2993,7 @@ number uint64 + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2810,12 +3037,24 @@ number uint64 #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_printBlock", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_printBlock", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_printBlock", "params": []}' ``` + === "Javascript Console" ``` js @@ -2823,6 +3062,7 @@ number uint64 ``` +
Source code

```go @@ -2835,7 +3075,7 @@ func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number uint64) (strin }// PrintBlock retrieves a block and returns its pretty printed form. ``` -View on GitHub → +View on GitHub →

@@ -2851,7 +3091,7 @@ It returns the transaction removed, if any. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -2859,7 +3099,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2898,7 +3138,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2924,12 +3164,24 @@ hash common.Hash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_removePendingTransaction", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_removePendingTransaction", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_removePendingTransaction", "params": []}' ``` + === "Javascript Console" ``` js @@ -2937,6 +3189,7 @@ hash common.Hash ``` +
Source code

```go @@ -2946,7 +3199,7 @@ func (api *PrivateDebugAPI) RemovePendingTransaction(hash common.Hash) (*types.T // It returns the transaction removed, if any. ``` -View on GitHub → +View on GitHub →

@@ -2961,7 +3214,7 @@ SeedHash retrieves the seed hash of a block. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -2969,7 +3222,7 @@ number uint64 + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3013,12 +3266,24 @@ number uint64 #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_seedHash", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_seedHash", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_seedHash", "params": []}' ``` + === "Javascript Console" ``` js @@ -3026,6 +3291,7 @@ number uint64 ``` +
Source code

```go @@ -3041,7 +3307,7 @@ func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string, }// SeedHash retrieves the seed hash of a block. ``` -View on GitHub → +View on GitHub →

@@ -3057,7 +3323,7 @@ rate 0 disables block profiling. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -3065,7 +3331,7 @@ rate int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3101,29 +3367,42 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_setBlockProfileRate", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_setBlockProfileRate", "params": []}' ``` -=== "Javascript Console" - ``` js - debug.setBlockProfileRate(rate); - ``` -
Source code -

-```go + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_setBlockProfileRate", "params": []}' + ``` + + +=== "Javascript Console" + + ``` js + debug.setBlockProfileRate(rate); + ``` + + + +

Source code +

+```go func (*HandlerT) SetBlockProfileRate(rate int) { runtime.SetBlockProfileRate(rate) }// SetBlockProfileRate sets the rate of goroutine block profile data collection. // rate 0 disables block profiling. ``` -View on GitHub → +View on GitHub →

@@ -3139,7 +3418,7 @@ setting. A negative value disables GC. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -3147,7 +3426,7 @@ v int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3186,7 +3465,7 @@ v int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3216,12 +3495,24 @@ v int #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_setGCPercent", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_setGCPercent", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_setGCPercent", "params": []}' ``` + === "Javascript Console" ``` js @@ -3229,6 +3520,7 @@ v int ``` +
Source code

```go @@ -3238,7 +3530,7 @@ func (*HandlerT) SetGCPercent(v int) int { // setting. A negative value disables GC. ``` -View on GitHub → +View on GitHub →

@@ -3253,7 +3545,7 @@ SetHead rewinds the head of the blockchain to a previous block. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -3261,7 +3553,7 @@ number hexutil.Uint64 + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3297,12 +3589,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_setHead", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_setHead", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_setHead", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -3310,6 +3614,7 @@ _None_ ``` +
Source code

```go @@ -3318,7 +3623,7 @@ func (api *PrivateDebugAPI) SetHead(number hexutil.Uint64) { }// SetHead rewinds the head of the blockchain to a previous block. ``` -View on GitHub → +View on GitHub →

@@ -3333,7 +3638,7 @@ SetMutexProfileFraction sets the rate of mutex profiling. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -3341,7 +3646,7 @@ rate int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3377,12 +3682,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_setMutexProfileFraction", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_setMutexProfileFraction", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_setMutexProfileFraction", "params": []}' ``` + === "Javascript Console" ``` js @@ -3390,6 +3707,7 @@ _None_ ``` +
Source code

```go @@ -3398,7 +3716,7 @@ func (*HandlerT) SetMutexProfileFraction(rate int) { }// SetMutexProfileFraction sets the rate of mutex profiling. ``` -View on GitHub → +View on GitHub →

@@ -3429,12 +3747,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_stacks", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_stacks", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_stacks", "params": []}' ``` + === "Javascript Console" ``` js @@ -3442,6 +3772,7 @@ _None_ ``` +
Source code

```go @@ -3452,7 +3783,7 @@ func (*HandlerT) Stacks() string { }// Stacks returns a printed representation of the stacks of all goroutines. ``` -View on GitHub → +View on GitHub →

@@ -3469,7 +3800,7 @@ local file system and returns a list of files to the caller. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -3477,7 +3808,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3511,7 +3842,7 @@ config *StdTraceConfig + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3615,7 +3946,7 @@ string []string + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3651,12 +3982,24 @@ string []string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_standardTraceBadBlockToFile", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_standardTraceBadBlockToFile", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_standardTraceBadBlockToFile", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -3664,15 +4007,15 @@ string []string ``` +
Source code

```go -func (api *PrivateDebugAPI) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([ // StandardTraceBadBlockToFile dumps the structured logs created during the +func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([ // StandardTraceBadBlockToFile dumps the structured logs created during the // execution of EVM against a block pulled from the pool of bad ones to the // local file system and returns a list of files to the caller. ]string, error) { - blocks := api.eth.blockchain.BadBlocks() - for _, block := range blocks { + for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) { if block.Hash() == hash { return api.standardTraceBlockToFile(ctx, block, config) } @@ -3680,7 +4023,7 @@ func (api *PrivateDebugAPI) StandardTraceBadBlockToFile(ctx context.Context, has return nil, fmt.Errorf("bad block %#x not found", hash) } ``` -View on GitHub → +View on GitHub →

@@ -3697,7 +4040,7 @@ to the caller. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -3705,7 +4048,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3739,7 +4082,7 @@ config *StdTraceConfig + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3843,7 +4186,7 @@ string []string + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3879,12 +4222,24 @@ string []string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_standardTraceBlockToFile", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_standardTraceBlockToFile", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_standardTraceBlockToFile", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -3892,21 +4247,22 @@ string []string ``` +
Source code

```go -func (api *PrivateDebugAPI) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([ // StandardTraceBlockToFile dumps the structured logs created during the +func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([ // StandardTraceBlockToFile dumps the structured logs created during the // execution of EVM to the local file system and returns a list of files // to the caller. ]string, error) { - block := api.eth.blockchain.GetBlockByHash(hash) - if block == nil { - return nil, fmt.Errorf("block %#x not found", hash) + block, err := api.blockByHash(ctx, hash) + if err != nil { + return nil, err } return api.standardTraceBlockToFile(ctx, block, config) } ``` -View on GitHub → +View on GitHub →

@@ -3921,7 +4277,7 @@ StartCPUProfile turns on CPU profiling, writing to the given file. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -3940,12 +4296,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_startCPUProfile", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_startCPUProfile", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_startCPUProfile", "params": []}' ``` + === "Javascript Console" ``` js @@ -3953,6 +4321,7 @@ _None_ ``` +
Source code

```go @@ -3977,7 +4346,7 @@ func (h *HandlerT) StartCPUProfile(file string) error { }// StartCPUProfile turns on CPU profiling, writing to the given file. ``` -View on GitHub → +View on GitHub →

@@ -3992,7 +4361,7 @@ StartGoTrace turns on tracing, writing to the given file. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -4011,12 +4380,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_startGoTrace", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_startGoTrace", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_startGoTrace", "params": []}' ``` + === "Javascript Console" ``` js @@ -4024,6 +4405,7 @@ _None_ ``` +
Source code

```go @@ -4048,7 +4430,7 @@ func (h *HandlerT) StartGoTrace(file string) error { }// StartGoTrace turns on tracing, writing to the given file. ``` -View on GitHub → +View on GitHub →

@@ -4071,12 +4453,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_stopCPUProfile", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_stopCPUProfile", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_stopCPUProfile", "params": []}' ``` + === "Javascript Console" ``` js @@ -4084,6 +4478,7 @@ _None_ ``` +
Source code

```go @@ -4102,7 +4497,7 @@ func (h *HandlerT) StopCPUProfile() error { }// StopCPUProfile stops an ongoing CPU profile. ``` -View on GitHub → +View on GitHub →

@@ -4125,12 +4520,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_stopGoTrace", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_stopGoTrace", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_stopGoTrace", "params": []}' ``` + === "Javascript Console" ``` js @@ -4138,6 +4545,7 @@ _None_ ``` +
Source code

```go @@ -4156,7 +4564,7 @@ func (h *HandlerT) StopGoTrace() error { }// StopTrace stops an ongoing trace. ``` -View on GitHub → +View on GitHub →

@@ -4171,7 +4579,7 @@ StorageRangeAt returns the storage at the given block height and transaction ind #### Params (5) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -4179,7 +4587,7 @@ blockHash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4213,7 +4621,7 @@ txIndex int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4247,7 +4655,7 @@ contractAddress common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4281,7 +4689,7 @@ keyStart hexutil.Bytes + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4315,7 +4723,7 @@ maxResult int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4354,7 +4762,7 @@ maxResult int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4436,12 +4844,24 @@ maxResult int #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_storageRangeAt", "params": [, , , , ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_storageRangeAt", "params": [, , , , ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_storageRangeAt", "params": [, , , , ]}' ``` + === "Javascript Console" ``` js @@ -4449,6 +4869,7 @@ maxResult int ``` +
Source code

```go @@ -4457,10 +4878,11 @@ func (api *PrivateDebugAPI) StorageRangeAt(blockHash common.Hash, txIndex int, c if block == nil { return StorageRangeResult{}, fmt.Errorf("block %#x not found", blockHash) } - _, _, statedb, err := api.computeTxEnv(block, txIndex, 0) + _, _, statedb, release, err := api.eth.stateAtTransaction(block, txIndex, 0) if err != nil { return StorageRangeResult{}, err } + defer release() st := statedb.StorageTrie(contractAddress) if st == nil { return StorageRangeResult{}, fmt.Errorf("account %x doesn't exist", contractAddress) @@ -4469,7 +4891,7 @@ func (api *PrivateDebugAPI) StorageRangeAt(blockHash common.Hash, txIndex int, c }// StorageRangeAt returns the storage at the given block height and transaction index. ``` -View on GitHub → +View on GitHub →

@@ -4477,34 +4899,35 @@ func (api *PrivateDebugAPI) StorageRangeAt(blockHash common.Hash, txIndex int, c -### debug_testSignCliqueBlock - -TestSignCliqueBlock fetches the given block number, and attempts to sign it as a clique header with the -given address, returning the address of the recovered signature +### debug_subscribe -This is a temporary method to debug the externalsigner integration, -TODO: Remove this method when the integration is mature +Subscribe creates a subscription to an event channel. +Subscriptions are not available over HTTP; they are only available over WS, IPC, and Process connections. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ -address common.Address +subscriptionName RPCDebugSubscriptionParamsName + Required: ✓ Yes - + === "Schema" ``` Schema - - description: `Hex representation of a Keccak 256 hash POINTER` - - pattern: `^0x[a-fA-F\d]{64}$` - - title: `keccak` - - type: string + - oneOf: + + - description: `Returns transaction traces within a range of blocks.` + - enum: traceChain + - type: string + + + - title: `subscriptionName` ``` @@ -4513,12 +4936,18 @@ address common.Address ``` Raw { - "description": "Hex representation of a Keccak 256 hash POINTER", - "pattern": "^0x[a-fA-F\\d]{64}$", - "title": "keccak", - "type": [ - "string" - ] + "oneOf": [ + { + "description": "Returns transaction traces within a range of blocks.", + "enum": [ + "traceChain" + ], + "type": [ + "string" + ] + } + ], + "title": "subscriptionName" } ``` @@ -4526,18 +4955,30 @@ address common.Address __2:__ -number uint64 +subscriptionOptions interface{} + + + Required: No + + + + + + +#### Result + + + +subscriptionID rpc.ID + Required: ✓ Yes - + === "Schema" ``` Schema - - description: `Hex representation of the integer` - - pattern: `^0x[a-fA-F0-9]+$` - - title: `integer` + - description: `Subscription identifier` + - title: `subscriptionID` - type: string @@ -4547,9 +4988,8 @@ number uint64 ``` Raw { - "description": "Hex representation of the integer", - "pattern": "^0x[a-fA-F0-9]+$", - "title": "integer", + "description": "Subscription identifier", + "title": "subscriptionID", "type": [ "string" ] @@ -4558,18 +4998,59 @@ number uint64 +#### Client Method Invocation Examples -#### Result -common.Address +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_subscribe", "params": [, ]}' + ``` + + + + +
Source code +

+```go +func (sub *RPCDebugSubscription) Subscribe(subscriptionName RPCDebugSubscriptionParamsName, subscriptionOptions interface{}) (subscriptionID rpc.ID, err error) { + return +}// Subscribe creates a subscription to an event channel. +// Subscriptions are not available over HTTP; they are only available over WS, IPC, and Process connections. + +``` +View on GitHub → +

+
+ +--- + + + +### debug_testSignCliqueBlock + +TestSignCliqueBlock fetches the given block number, and attempts to sign it as a clique header with the +given address, returning the address of the recovered signature + +This is a temporary method to debug the externalsigner integration, +TODO: Remove this method when the integration is mature + + +#### Params (2) + +Parameters must be given _by position_. + + +__1:__ +address common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4597,19 +5078,105 @@ number uint64 -#### Client Method Invocation Examples -=== "Shell" +__2:__ +number uint64 - ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_testSignCliqueBlock", "params": [
, ]}' - ``` + + Required: ✓ Yes -=== "Javascript Console" - ``` js - debug.testSignCliqueBlock(address,number); - ``` +=== "Schema" + + ``` Schema + + - description: `Hex representation of the integer` + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Hex representation of the integer", + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": [ + "string" + ] + } + ``` + + + + + +#### Result + + + + +common.Address + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Hex representation of a Keccak 256 hash POINTER` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Hex representation of a Keccak 256 hash POINTER", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": [ + "string" + ] + } + ``` + + + +#### Client Method Invocation Examples + + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_testSignCliqueBlock", "params": [
, ]}' + ``` + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_testSignCliqueBlock", "params": [
, ]}' + ``` + + +=== "Javascript Console" + + ``` js + debug.testSignCliqueBlock(address,number); + ``` +
Source code @@ -4648,7 +5215,7 @@ func (api *PublicDebugAPI) TestSignCliqueBlock(ctx context.Context, address comm return signer, nil } ``` -View on GitHub → +View on GitHub →

@@ -4665,7 +5232,7 @@ object. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -4673,7 +5240,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4707,7 +5274,7 @@ config *TraceConfig + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4734,6 +5301,9 @@ config *TraceConfig - title: `integer` - type: `string` + - NestedTraceOutput: + - type: `boolean` + - Reexec: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -4780,6 +5350,9 @@ config *TraceConfig "title": "integer", "type": "string" }, + "NestedTraceOutput": { + "type": "boolean" + }, "Reexec": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -4813,7 +5386,7 @@ txTraceResult []*txTraceResult + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4867,12 +5440,24 @@ txTraceResult []*txTraceResult #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_traceBadBlock", "params": [, ]}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_traceBadBlock", "params": [, ]}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_traceBadBlock", "params": [, ]}' + ``` + + === "Javascript Console" ``` js @@ -4880,15 +5465,15 @@ txTraceResult []*txTraceResult ``` +
Source code

```go -func (api *PrivateDebugAPI) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([ // TraceBadBlock returns the structured logs created during the execution of +func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([ // TraceBadBlock returns the structured logs created during the execution of // EVM against a block pulled from the pool of bad ones and returns them as a JSON // object. ]*txTraceResult, error) { - blocks := api.eth.blockchain.BadBlocks() - for _, block := range blocks { + for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) { if block.Hash() == hash { return api.traceBlock(ctx, block, config) } @@ -4896,7 +5481,7 @@ func (api *PrivateDebugAPI) TraceBadBlock(ctx context.Context, hash common.Hash, return nil, fmt.Errorf("bad block %#x not found", hash) } ``` -View on GitHub → +View on GitHub →

@@ -4912,7 +5497,7 @@ and returns them as a JSON object. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -4920,7 +5505,7 @@ blob []byte + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4954,7 +5539,7 @@ config *TraceConfig + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4981,6 +5566,9 @@ config *TraceConfig - title: `integer` - type: `string` + - NestedTraceOutput: + - type: `boolean` + - Reexec: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -5027,6 +5615,9 @@ config *TraceConfig "title": "integer", "type": "string" }, + "NestedTraceOutput": { + "type": "boolean" + }, "Reexec": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -5060,7 +5651,7 @@ txTraceResult []*txTraceResult + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5114,12 +5705,24 @@ txTraceResult []*txTraceResult #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_traceBlock", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_traceBlock", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_traceBlock", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -5127,16 +5730,21 @@ txTraceResult []*txTraceResult ``` +
Source code

```go -func (api *PrivateDebugAPI) TraceBlock(ctx context.Context, blob [ // TraceBlock returns the structured logs created during the execution of EVM +func (api *API) TraceBlock(ctx context.Context, blob [ // TraceBlock returns the structured logs created during the execution of EVM // and returns them as a JSON object. ]byte, config *TraceConfig) ([]*txTraceResult, error) { - return traceBlockRLP(ctx, api.eth, blob, config) + block := new(types.Block) + if err := rlp.Decode(bytes.NewReader(blob), block); err != nil { + return nil, fmt.Errorf("could not decode block: %v", err) + } + return api.traceBlock(ctx, block, config) } ``` -View on GitHub → +View on GitHub →

@@ -5152,7 +5760,7 @@ EVM and returns them as a JSON object. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -5160,7 +5768,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5194,7 +5802,7 @@ config *TraceConfig + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5221,6 +5829,9 @@ config *TraceConfig - title: `integer` - type: `string` + - NestedTraceOutput: + - type: `boolean` + - Reexec: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -5267,6 +5878,9 @@ config *TraceConfig "title": "integer", "type": "string" }, + "NestedTraceOutput": { + "type": "boolean" + }, "Reexec": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -5300,7 +5914,7 @@ txTraceResult []*txTraceResult + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5354,12 +5968,24 @@ txTraceResult []*txTraceResult #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_traceBlockByHash", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_traceBlockByHash", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_traceBlockByHash", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -5367,20 +5993,21 @@ txTraceResult []*txTraceResult ``` +
Source code

```go -func (api *PrivateDebugAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([ // TraceBlockByHash returns the structured logs created during the execution of +func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([ // TraceBlockByHash returns the structured logs created during the execution of // EVM and returns them as a JSON object. ]*txTraceResult, error) { - block := api.eth.blockchain.GetBlockByHash(hash) - if block == nil { - return nil, fmt.Errorf("block %#x not found", hash) + block, err := api.blockByHash(ctx, hash) + if err != nil { + return nil, err } return api.traceBlock(ctx, block, config) } ``` -View on GitHub → +View on GitHub →

@@ -5396,7 +6023,7 @@ EVM and returns them as a JSON object. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -5404,7 +6031,7 @@ number rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5466,7 +6093,7 @@ config *TraceConfig + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5493,6 +6120,9 @@ config *TraceConfig - title: `integer` - type: `string` + - NestedTraceOutput: + - type: `boolean` + - Reexec: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -5539,6 +6169,9 @@ config *TraceConfig "title": "integer", "type": "string" }, + "NestedTraceOutput": { + "type": "boolean" + }, "Reexec": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -5572,7 +6205,7 @@ txTraceResult []*txTraceResult + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5626,12 +6259,24 @@ txTraceResult []*txTraceResult #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_traceBlockByNumber", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_traceBlockByNumber", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_traceBlockByNumber", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -5639,16 +6284,21 @@ txTraceResult []*txTraceResult ``` +
Source code

```go -func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([ // TraceBlockByNumber returns the structured logs created during the execution of +func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([ // TraceBlockByNumber returns the structured logs created during the execution of // EVM and returns them as a JSON object. ]*txTraceResult, error) { - return traceBlockByNumber(ctx, api.eth, number, config) + block, err := api.blockByNumber(ctx, number) + if err != nil { + return nil, err + } + return api.traceBlock(ctx, block, config) } ``` -View on GitHub → +View on GitHub →

@@ -5664,7 +6314,7 @@ EVM and returns them as a JSON object. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -5681,7 +6331,7 @@ config *TraceConfig + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5708,6 +6358,9 @@ config *TraceConfig - title: `integer` - type: `string` + - NestedTraceOutput: + - type: `boolean` + - Reexec: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -5754,6 +6407,9 @@ config *TraceConfig "title": "integer", "type": "string" }, + "NestedTraceOutput": { + "type": "boolean" + }, "Reexec": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -5787,7 +6443,7 @@ txTraceResult []*txTraceResult + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5841,12 +6497,24 @@ txTraceResult []*txTraceResult #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_traceBlockFromFile", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_traceBlockFromFile", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_traceBlockFromFile", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -5854,10 +6522,11 @@ txTraceResult []*txTraceResult ``` +
Source code

```go -func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([ // TraceBlockFromFile returns the structured logs created during the execution of +func (api *API) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([ // TraceBlockFromFile returns the structured logs created during the execution of // EVM and returns them as a JSON object. ]*txTraceResult, error) { blob, err := ioutil.ReadFile(file) @@ -5867,7 +6536,7 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string, return api.TraceBlock(ctx, blob, config) } ``` -View on GitHub → +View on GitHub →

@@ -5877,14 +6546,15 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string, ### debug_traceCall -TraceCall lets you trace a given eth_call. It collects the structured logs created during the execution of EVM -if the given transaction was added on top of the provided block and returns them as a JSON object. +TraceCall lets you trace a given eth_call. It collects the structured logs +created during the execution of EVM if the given transaction was added on +top of the provided block and returns them as a JSON object. You can provide -2 as a block number to trace on top of the pending block. #### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -5892,13 +6562,36 @@ args ethapi.CallArgs + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + - data: - pattern: `^0x([a-fA-F\d])+$` - title: `dataWord` @@ -5941,6 +6634,29 @@ args ethapi.CallArgs { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, "data": { "pattern": "^0x([a-fA-F\\d])+$", "title": "dataWord", @@ -5995,7 +6711,7 @@ config *TraceConfig + Required: ✓ Yes - + === "Schema" ``` Schema @@ -6022,6 +6738,9 @@ config *TraceConfig - title: `integer` - type: `string` + - NestedTraceOutput: + - type: `boolean` + - Reexec: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -6068,6 +6787,9 @@ config *TraceConfig "title": "integer", "type": "string" }, + "NestedTraceOutput": { + "type": "boolean" + }, "Reexec": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -6106,12 +6828,24 @@ interface interface{} #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_traceCall", "params": [, , ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_traceCall", "params": [, , ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_traceCall", "params": [, , ]}' ``` + === "Javascript Console" ``` js @@ -6119,40 +6853,65 @@ interface interface{} ``` +
Source code

```go -func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { - statedb, header, err := api.eth.APIBackend.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) +func (api *API) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { + var ( + err error + block *types.Block + ) + if hash, ok := blockNrOrHash.Hash(); ok { + block, err = api.blockByHash(ctx, hash) + } else if number, ok := blockNrOrHash.Number(); ok { + block, err = api.blockByNumber(ctx, number) + } if err != nil { - var block *types.Block - if hash, ok := blockNrOrHash.Hash(); ok { - block = api.eth.blockchain.GetBlockByHash(hash) - } else if number, ok := blockNrOrHash.Number(); ok { - block = api.eth.blockchain.GetBlockByNumber(uint64(number)) - } - if block == nil { - return nil, fmt.Errorf("block %v not found: %v", blockNrOrHash, err) - } - reexec := defaultTraceReexec - if config != nil && config.Reexec != nil { - reexec = *config.Reexec + return nil, err + } + reexec := defaultTraceReexec + if config != nil && config.Reexec != nil { + reexec = *config.Reexec + } + statedb, release, err := api.backend.StateAtBlock(ctx, block, reexec) + if err != nil { + return nil, err + } + defer release() + msg := args.ToMessage(api.backend.RPCGasCap()) + vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + originalCanTransfer := vmctx.CanTransfer + originalTransfer := vmctx.Transfer + gasCost := new(big.Int).Mul(new(big.Int).SetUint64(msg.Gas()), msg.GasPrice()) + totalCost := new(big.Int).Add(gasCost, msg.Value()) + hasFromSufficientBalanceForValueAndGasCost := vmctx.CanTransfer(statedb, msg.From(), totalCost) + hasFromSufficientBalanceForGasCost := vmctx.CanTransfer(statedb, msg.From(), gasCost) + vmctx.CanTransfer = func(db vm.StateDB, sender common.Address, amount *big.Int) bool { + if msg.From() == sender { + return true } - _, _, statedb, err = api.computeTxEnv(block, 0, reexec) - if err != nil { - return nil, err + res := originalCanTransfer(db, sender, amount) + return res + } + vmctx.Transfer = func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { + toAmount := new(big.Int).Set(amount) + senderBalance := db.GetBalance(sender) + if senderBalance.Cmp(toAmount) < 0 { + toAmount.Set(big.NewInt(0)) } + originalTransfer(db, sender, recipient, toAmount) } - msg := args.ToMessage(api.eth.APIBackend.RPCGasCap()) - vmctx := core.NewEVMContext(msg, header, api.eth.blockchain, nil) - return api.traceTx(ctx, msg, vmctx, statedb, config) -}// TraceCall lets you trace a given eth_call. It collects the structured logs created during the execution of EVM -// if the given transaction was added on top of the provided block and returns them as a JSON object. -// You can provide -2 as a block number to trace on top of the pending block. -// Try to retrieve the specified block - + taskExtraContext := map // TraceCall lets you trace a given eth_call. It collects the structured logs + // created during the execution of EVM if the given transaction was added on + // top of the provided block and returns them as a JSON object. + // You can provide -2 as a block number to trace on top of the pending block. + // Try to retrieve the specified block + [string]interface{}{"hasFromSufficientBalanceForValueAndGasCost": hasFromSufficientBalanceForValueAndGasCost, "hasFromSufficientBalanceForGasCost": hasFromSufficientBalanceForGasCost} + return api.traceTx(ctx, msg, vmctx, statedb, taskExtraContext, config) +} ``` -View on GitHub → +View on GitHub →

@@ -6160,23 +6919,746 @@ func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.CallArgs, -### debug_traceTransaction +### debug_traceCallMany -TraceTransaction returns the structured logs created during the execution of EVM -and returns them as a JSON object. +TraceCallMany lets you trace a given eth_call. It collects the structured logs created during the execution of EVM +if the given transaction was added on top of the provided block and returns them as a JSON object. +You can provide -2 as a block number to trace on top of the pending block. -#### Params (2) +#### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ -hash common.Hash +txs []ethapi.CallArgs + Required: ✓ Yes - + +=== "Schema" + + ``` Schema + + - items: + + - additionalProperties: `false` + - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + + - data: + - pattern: `^0x([a-fA-F\d])+$` + - title: `dataWord` + - type: `string` + + - from: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - gas: + - pattern: `^0x([a-fA-F\d])+$` + - title: `uint64` + - type: `string` + + - gasPrice: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + - to: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - value: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + + - type: object + + + - type: array + + + ``` + +=== "Raw" + + ``` Raw + { + "items": [ + { + "additionalProperties": false, + "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "data": { + "pattern": "^0x([a-fA-F\\d])+$", + "title": "dataWord", + "type": "string" + }, + "from": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "gas": { + "pattern": "^0x([a-fA-F\\d])+$", + "title": "uint64", + "type": "string" + }, + "gasPrice": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "to": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "value": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + } + ``` + + + + +__2:__ +blockNrOrHash rpc.BlockNumberOrHash + + + Required: ✓ Yes + + + + + +__3:__ +config *TraceConfig + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - additionalProperties: `false` + - properties: + - Debug: + - type: `boolean` + + - DisableMemory: + - type: `boolean` + + - DisableReturnData: + - type: `boolean` + + - DisableStack: + - type: `boolean` + + - DisableStorage: + - type: `boolean` + + - Limit: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + - NestedTraceOutput: + - type: `boolean` + + - Reexec: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + - Timeout: + - type: `string` + + - Tracer: + - type: `string` + + - overrides: + - additionalProperties: `true` + + + - type: object + + + ``` + +=== "Raw" + + ``` Raw + { + "additionalProperties": false, + "properties": { + "Debug": { + "type": "boolean" + }, + "DisableMemory": { + "type": "boolean" + }, + "DisableReturnData": { + "type": "boolean" + }, + "DisableStack": { + "type": "boolean" + }, + "DisableStorage": { + "type": "boolean" + }, + "Limit": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "NestedTraceOutput": { + "type": "boolean" + }, + "Reexec": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "Timeout": { + "type": "string" + }, + "Tracer": { + "type": "string" + }, + "overrides": { + "additionalProperties": true + } + }, + "type": [ + "object" + ] + } + ``` + + + + + +#### Result + + + +interface interface{} + + + Required: ✓ Yes + + + + +#### Client Method Invocation Examples + + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_traceCallMany", "params": [, , ]}' + ``` + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_traceCallMany", "params": [, , ]}' + ``` + + +=== "Javascript Console" + + ``` js + debug.traceCallMany(txs,blockNrOrHash,config); + ``` + + + +
Source code +

+```go +func (api *API) TraceCallMany(ctx context.Context, txs [ // TraceCallMany lets you trace a given eth_call. It collects the structured logs created during the execution of EVM +// if the given transaction was added on top of the provided block and returns them as a JSON object. +// You can provide -2 as a block number to trace on top of the pending block. +]ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { + var ( + err error + block *types.Block + ) + if hash, ok := blockNrOrHash.Hash(); ok { + block, err = api.blockByHash(ctx, hash) + } else if number, ok := blockNrOrHash.Number(); ok { + block, err = api.blockByNumber(ctx, number) + } + if err != nil { + return nil, err + } + reexec := defaultTraceReexec + if config != nil && config.Reexec != nil { + reexec = *config.Reexec + } + statedb, release, err := api.backend.StateAtBlock(ctx, block, reexec) + if err != nil { + return nil, err + } + defer release() + var results = make([ // Try to retrieve the specified block + ]interface{}, len(txs)) + for idx, args := range txs { + msg := args.ToMessage(api.backend.RPCGasCap()) + vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + originalCanTransfer := vmctx.CanTransfer + originalTransfer := vmctx.Transfer + gasCost := new(big.Int).Mul(new(big.Int).SetUint64(msg.Gas()), msg.GasPrice()) + totalCost := new(big.Int).Add(gasCost, msg.Value()) + hasFromSufficientBalanceForValueAndGasCost := vmctx.CanTransfer(statedb, msg.From(), totalCost) + hasFromSufficientBalanceForGasCost := vmctx.CanTransfer(statedb, msg.From(), gasCost) + vmctx.CanTransfer = func(db vm.StateDB, sender common.Address, amount *big.Int) bool { + if msg.From() == sender { + return true + } + res := originalCanTransfer(db, sender, amount) + return res + } + vmctx.Transfer = func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { + toAmount := new(big.Int).Set(amount) + senderBalance := db.GetBalance(sender) + if senderBalance.Cmp(toAmount) < 0 { + toAmount.Set(big.NewInt(0)) + } + originalTransfer(db, sender, recipient, toAmount) + } + taskExtraContext := map[string]interface{}{"hasFromSufficientBalanceForValueAndGasCost": hasFromSufficientBalanceForValueAndGasCost, "hasFromSufficientBalanceForGasCost": hasFromSufficientBalanceForGasCost} + res, err := api.traceTx(ctx, msg, vmctx, statedb, taskExtraContext, config) + if err != nil { + results[idx] = &txTraceResult{Error: err.Error()} + continue + } + res, err = decorateResponse(res, config) + if err != nil { + return nil, fmt.Errorf("failed to decorate response for transaction at index %d with error %v", idx, err) + } + results[idx] = res + } + return results, nil +} +``` +View on GitHub → +

+
+ +--- + + + +### debug_traceChain + +TraceChain returns the structured logs created during the execution of EVM +between two blocks (excluding start) and returns them as a JSON object. + + +#### Params (3) + +Parameters must be given _by position_. + + +__1:__ +start rpc.BlockNumber + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - oneOf: + + - description: `The block height description` + - enum: earliest, latest, pending + - title: `blockNumberTag` + - type: string + + + - description: `Hex representation of a uint64` + - pattern: `^0x([a-fA-F\d])+$` + - title: `uint64` + - type: string + + + - title: `blockNumberIdentifier` + + + ``` + +=== "Raw" + + ``` Raw + { + "oneOf": [ + { + "description": "The block height description", + "enum": [ + "earliest", + "latest", + "pending" + ], + "title": "blockNumberTag", + "type": [ + "string" + ] + }, + { + "description": "Hex representation of a uint64", + "pattern": "^0x([a-fA-F\\d])+$", + "title": "uint64", + "type": [ + "string" + ] + } + ], + "title": "blockNumberIdentifier" + } + ``` + + + + +__2:__ +end rpc.BlockNumber + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - oneOf: + + - description: `The block height description` + - enum: earliest, latest, pending + - title: `blockNumberTag` + - type: string + + + - description: `Hex representation of a uint64` + - pattern: `^0x([a-fA-F\d])+$` + - title: `uint64` + - type: string + + + - title: `blockNumberIdentifier` + + + ``` + +=== "Raw" + + ``` Raw + { + "oneOf": [ + { + "description": "The block height description", + "enum": [ + "earliest", + "latest", + "pending" + ], + "title": "blockNumberTag", + "type": [ + "string" + ] + }, + { + "description": "Hex representation of a uint64", + "pattern": "^0x([a-fA-F\\d])+$", + "title": "uint64", + "type": [ + "string" + ] + } + ], + "title": "blockNumberIdentifier" + } + ``` + + + + +__3:__ +config *TraceConfig + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - additionalProperties: `false` + - properties: + - Debug: + - type: `boolean` + + - DisableMemory: + - type: `boolean` + + - DisableReturnData: + - type: `boolean` + + - DisableStack: + - type: `boolean` + + - DisableStorage: + - type: `boolean` + + - Limit: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + - NestedTraceOutput: + - type: `boolean` + + - Reexec: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + - Timeout: + - type: `string` + + - Tracer: + - type: `string` + + - overrides: + - additionalProperties: `true` + + + - type: object + + + ``` + +=== "Raw" + + ``` Raw + { + "additionalProperties": false, + "properties": { + "Debug": { + "type": "boolean" + }, + "DisableMemory": { + "type": "boolean" + }, + "DisableReturnData": { + "type": "boolean" + }, + "DisableStack": { + "type": "boolean" + }, + "DisableStorage": { + "type": "boolean" + }, + "Limit": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "NestedTraceOutput": { + "type": "boolean" + }, + "Reexec": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "Timeout": { + "type": "string" + }, + "Tracer": { + "type": "string" + }, + "overrides": { + "additionalProperties": true + } + }, + "type": [ + "object" + ] + } + ``` + + + + + +#### Result + + + + +*rpc.Subscription + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + + + +#### Client Method Invocation Examples + + + + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_subscribe", "params": ["traceChain", , , ]}' + ``` + + + + +
Source code +

+```go +func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) { + from, err := api.blockByNumber(ctx, start) + if err != nil { + return nil, err + } + to, err := api.blockByNumber(ctx, end) + if err != nil { + return nil, err + } + if from.Number().Cmp(to.Number()) >= 0 { + return nil, fmt.Errorf("end block (#%d) needs to come after start block (#%d)", end, start) + } + return api.traceChain(ctx, from, to, config) +}// TraceChain returns the structured logs created during the execution of EVM +// between two blocks (excluding start) and returns them as a JSON object. + +``` +View on GitHub → +

+
+ +--- + + + +### debug_traceTransaction + +TraceTransaction returns the structured logs created during the execution of EVM +and returns them as a JSON object. + + +#### Params (2) + +Parameters must be given _by position_. + + +__1:__ +hash common.Hash + + + Required: ✓ Yes + + === "Schema" ``` Schema @@ -6210,7 +7692,7 @@ config *TraceConfig + Required: ✓ Yes - + === "Schema" ``` Schema @@ -6237,6 +7719,9 @@ config *TraceConfig - title: `integer` - type: `string` + - NestedTraceOutput: + - type: `boolean` + - Reexec: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -6283,6 +7768,9 @@ config *TraceConfig "title": "integer", "type": "string" }, + "NestedTraceOutput": { + "type": "boolean" + }, "Reexec": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -6321,12 +7809,24 @@ interface interface{} #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_traceTransaction", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_traceTransaction", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_traceTransaction", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -6334,16 +7834,129 @@ interface interface{} ``` +
Source code

```go -func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { - return traceTransaction(ctx, api.eth, hash, config) -}// TraceTransaction returns the structured logs created during the execution of EVM -// and returns them as a JSON object. +func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { + tx, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) + if err != nil { + return nil, err + } + if blockNumber == 0 { + return nil, errors.New("genesis is not traceable") + } + reexec := defaultTraceReexec + if config != nil && config.Reexec != nil { + reexec = *config.Reexec + } + block, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNumber), blockHash) + if err != nil { + return nil, err + } + msg, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec) + if err != nil { + return nil, err + } + defer release() + taskExtraContext := map // TraceTransaction returns the structured logs created during the execution of EVM + // and returns them as a JSON object. + [string]interface{}{"blockNumber": block.NumberU64(), "blockHash": blockHash.Hex(), "transactionHash": tx.Hash().Hex(), "transactionPosition": index} + return api.traceTx(ctx, msg, vmctx, statedb, taskExtraContext, config) +} +``` +View on GitHub → +

+
+ +--- + + + +### debug_unsubscribe + +Unsubscribe terminates an existing subscription by ID. + + +#### Params (1) + +Parameters must be given _by position_. + + +__1:__ +id rpc.ID + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + + + + + +#### Result + +_None_ + +#### Client Method Invocation Examples + + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_unsubscribe", "params": []}' + ``` + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_unsubscribe", "params": []}' + ``` + + +=== "Javascript Console" + + ``` js + debug.unsubscribe(id); + ``` + + + +
Source code +

+```go +func (sub *RPCDebugSubscription) Unsubscribe(id rpc.ID) error { + return nil +}// Unsubscribe terminates an existing subscription by ID. ``` -View on GitHub → +View on GitHub →

@@ -6359,7 +7972,7 @@ and source files can be raised using Vmodule. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -6367,7 +7980,7 @@ level int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -6403,12 +8016,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_verbosity", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_verbosity", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_verbosity", "params": []}' ``` + === "Javascript Console" ``` js @@ -6416,6 +8041,7 @@ _None_ ``` +
Source code

```go @@ -6425,7 +8051,7 @@ func (*HandlerT) Verbosity(level int) { // and source files can be raised using Vmodule. ``` -View on GitHub → +View on GitHub →

@@ -6441,7 +8067,7 @@ pattern syntax. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -6460,12 +8086,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_vmodule", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_vmodule", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_vmodule", "params": []}' ``` + === "Javascript Console" ``` js @@ -6473,6 +8111,7 @@ _None_ ``` +
Source code

```go @@ -6482,7 +8121,7 @@ func (*HandlerT) Vmodule(pattern string) error { // pattern syntax. ``` -View on GitHub → +View on GitHub →

@@ -6497,7 +8136,7 @@ WriteBlockProfile writes a goroutine blocking profile to the given file. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -6516,12 +8155,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_writeBlockProfile", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_writeBlockProfile", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_writeBlockProfile", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -6529,6 +8180,7 @@ _None_ ``` +
Source code

```go @@ -6537,7 +8189,7 @@ func (*HandlerT) WriteBlockProfile(file string) error { }// WriteBlockProfile writes a goroutine blocking profile to the given file. ``` -View on GitHub → +View on GitHub →

@@ -6554,7 +8206,7 @@ it must be set on the command line. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -6573,12 +8225,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_writeMemProfile", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_writeMemProfile", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_writeMemProfile", "params": []}' ``` + === "Javascript Console" ``` js @@ -6586,6 +8250,7 @@ _None_ ``` +
Source code

```go @@ -6596,7 +8261,7 @@ func (*HandlerT) WriteMemProfile(file string) error { // it must be set on the command line. ``` -View on GitHub → +View on GitHub →

@@ -6611,7 +8276,7 @@ WriteMutexProfile writes a goroutine blocking profile to the given file. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -6630,12 +8295,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "debug_writeMutexProfile", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "debug_writeMutexProfile", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "debug_writeMutexProfile", "params": []}' ``` + === "Javascript Console" ``` js @@ -6643,6 +8320,7 @@ _None_ ``` +
Source code

```go @@ -6651,7 +8329,7 @@ func (*HandlerT) WriteMutexProfile(file string) error { }// WriteMutexProfile writes a goroutine blocking profile to the given file. ``` -View on GitHub → +View on GitHub →

diff --git a/docs/JSON-RPC-API/modules/eth.md b/docs/JSON-RPC-API/modules/eth.md index 7ecf4b8928..342bf84fb9 100755 --- a/docs/JSON-RPC-API/modules/eth.md +++ b/docs/JSON-RPC-API/modules/eth.md @@ -7,7 +7,7 @@ | Entity | Version | | --- | --- | -| Source | 1.11.22-unstable/generated-at:2021-01-23T04:50:40-06:00 | +| Source | 1.11.23-unstable/generated-at:2021-04-30T19:24:24+03:00 | | OpenRPC | 1.2.6 | --- @@ -32,7 +32,7 @@ commonAddress []common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -74,12 +74,24 @@ commonAddress []common.Address #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_accounts", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_accounts", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_accounts", "params": []}' ``` + === "Javascript Console" ``` js @@ -87,6 +99,7 @@ commonAddress []common.Address ``` +
Source code

```go @@ -95,7 +108,7 @@ func (s *PublicAccountAPI) Accounts() [ // Accounts returns the collection of ac return s.am.Accounts() } ``` -View on GitHub → +View on GitHub →

@@ -121,7 +134,7 @@ _None_ + Required: ✓ Yes - + === "Schema" ``` Schema @@ -151,12 +164,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_blockNumber", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_blockNumber", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber", "params": []}' ``` + === "Javascript Console" ``` js @@ -164,6 +189,7 @@ _None_ ``` +
Source code

```go @@ -173,7 +199,7 @@ func (s *PublicBlockChainAPI) BlockNumber() hexutil.Uint64 { }// BlockNumber returns the block number of the chain head. ``` -View on GitHub → +View on GitHub →

@@ -193,7 +219,7 @@ useful to execute and retrieve values. #### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -201,13 +227,36 @@ args CallArgs + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + - data: - pattern: `^0x([a-fA-F\d])+$` - title: `dataWord` @@ -250,6 +299,29 @@ args CallArgs { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, "data": { "pattern": "^0x([a-fA-F\\d])+$", "title": "dataWord", @@ -304,7 +376,7 @@ overrides *map[common.Address]account + Required: ✓ Yes - + === "Schema" ``` Schema @@ -427,7 +499,7 @@ overrides *map[common.Address]account + Required: ✓ Yes - + === "Schema" ``` Schema @@ -457,12 +529,24 @@ overrides *map[common.Address]account #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_call", "params": [, , ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_call", "params": [, , ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [, , ]}' ``` + === "Javascript Console" ``` js @@ -470,6 +554,7 @@ overrides *map[common.Address]account ``` +
Source code

```go @@ -494,7 +579,7 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNrOr return result.Return(), result.Err } ``` -View on GitHub → +View on GitHub →

@@ -504,7 +589,7 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNrOr ### eth_chainId -ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config. +ChainId returns the chainID value for transaction replay protection. #### Params (0) @@ -516,18 +601,18 @@ _None_ -hexutil.Uint64 +*hexutil.Big + Required: ✓ Yes - + === "Schema" ``` Schema - - description: `Hex representation of a uint64` - - pattern: `^0x([a-fA-F\d])+$` - - title: `uint64` + - description: `Hex representation of the integer` + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` - type: string @@ -537,9 +622,9 @@ _None_ ``` Raw { - "description": "Hex representation of a uint64", - "pattern": "^0x([a-fA-F\\d])+$", - "title": "uint64", + "description": "Hex representation of the integer", + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", "type": [ "string" ] @@ -550,12 +635,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_chainId", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_chainId", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_chainId", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -563,19 +660,16 @@ _None_ ``` +
Source code

```go -func (api *PublicEthereumAPI) ChainId() hexutil.Uint64 { - chainID := new(big.Int) - if config := api.e.blockchain.Config(); config.IsEnabled(config.GetEIP155Transition, api.e.blockchain.CurrentBlock().Number()) { - chainID = config.GetChainID() - } - return (hexutil.Uint64)(chainID.Uint64()) -}// ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config. +func (s *PublicBlockChainAPI) ChainId() *hexutil.Big { + return (*hexutil.Big)(s.b.ChainConfig().GetChainID()) +}// ChainId returns the chainID value for transaction replay protection. ``` -View on GitHub → +View on GitHub →

@@ -585,7 +679,7 @@ func (api *PublicEthereumAPI) ChainId() hexutil.Uint64 { ### eth_chainId -ChainId returns the chainID value for transaction replay protection. +ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config. #### Params (0) @@ -597,18 +691,18 @@ _None_ -*hexutil.Big +hexutil.Uint64 + Required: ✓ Yes - + === "Schema" ``` Schema - - description: `Hex representation of the integer` - - pattern: `^0x[a-fA-F0-9]+$` - - title: `integer` + - description: `Hex representation of a uint64` + - pattern: `^0x([a-fA-F\d])+$` + - title: `uint64` - type: string @@ -618,9 +712,9 @@ _None_ ``` Raw { - "description": "Hex representation of the integer", - "pattern": "^0x[a-fA-F0-9]+$", - "title": "integer", + "description": "Hex representation of a uint64", + "pattern": "^0x([a-fA-F\\d])+$", + "title": "uint64", "type": [ "string" ] @@ -631,12 +725,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_chainId", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_chainId", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_chainId", "params": []}' ``` + === "Javascript Console" ``` js @@ -644,15 +750,19 @@ _None_ ``` +
Source code

```go -func (s *PublicBlockChainAPI) ChainId() *hexutil.Big { - return (*hexutil.Big)(s.b.ChainConfig().GetChainID()) -}// ChainId returns the chainID value for transaction replay protection. +func (api *PublicEthereumAPI) ChainId() (hexutil.Uint64, error) { + if config := api.e.blockchain.Config(); config.IsEnabled(config.GetEIP155Transition, api.e.blockchain.CurrentBlock().Number()) { + return (hexutil.Uint64)(api.e.blockchain.Config().GetChainID().Uint64()), nil + } + return hexutil.Uint64(0), fmt.Errorf("chain not synced beyond EIP-155 replay-protection fork block") +}// ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config. ``` -View on GitHub → +View on GitHub →

@@ -678,7 +788,7 @@ _None_ + Required: ✓ Yes - + === "Schema" ``` Schema @@ -708,12 +818,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_coinbase", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_coinbase", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_coinbase", "params": []}' ``` + === "Javascript Console" ``` js @@ -721,6 +843,7 @@ _None_ ``` +
Source code

```go @@ -729,7 +852,7 @@ func (api *PublicEthereumAPI) Coinbase() (common.Address, error) { }// Coinbase is the address that mining rewards will be send to (alias for Etherbase) ``` -View on GitHub → +View on GitHub →

@@ -745,7 +868,7 @@ given transaction against the current pending block. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -753,13 +876,36 @@ args CallArgs + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + - data: - pattern: `^0x([a-fA-F\d])+$` - title: `dataWord` @@ -802,6 +948,29 @@ args CallArgs { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, "data": { "pattern": "^0x([a-fA-F\\d])+$", "title": "dataWord", @@ -861,7 +1030,7 @@ blockNrOrHash *rpc.BlockNumberOrHash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -891,12 +1060,24 @@ blockNrOrHash *rpc.BlockNumberOrHash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_estimateGas", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_estimateGas", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_estimateGas", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -904,6 +1085,7 @@ blockNrOrHash *rpc.BlockNumberOrHash ``` +
Source code

```go @@ -917,7 +1099,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs, bl // given transaction against the current pending block. ``` -View on GitHub → +View on GitHub →

@@ -943,7 +1125,7 @@ _None_ + Required: ✓ Yes - + === "Schema" ``` Schema @@ -973,12 +1155,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_etherbase", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_etherbase", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_etherbase", "params": []}' ``` + === "Javascript Console" ``` js @@ -986,6 +1180,7 @@ _None_ ``` +
Source code

```go @@ -994,7 +1189,7 @@ func (api *PublicEthereumAPI) Etherbase() (common.Address, error) { }// Etherbase is the address that mining rewards will be send to ``` -View on GitHub → +View on GitHub →

@@ -1010,7 +1205,7 @@ and returns it to the caller for further processing (signing + broadcast) #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1018,13 +1213,41 @@ args SendTxArgs + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + + - chainId: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + - data: - pattern: `^0x([a-fA-F\d])+$` - title: `dataWord` @@ -1077,14 +1300,42 @@ args SendTxArgs { "additionalProperties": false, "properties": { - "data": { - "pattern": "^0x([a-fA-F\\d])+$", - "title": "dataWord", - "type": "string" - }, - "from": { - "pattern": "^0x[a-fA-F\\d]{64}$", - "title": "keccak", + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "chainId": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "data": { + "pattern": "^0x([a-fA-F\\d])+$", + "title": "dataWord", + "type": "string" + }, + "from": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", "type": "string" }, "gas": { @@ -1137,7 +1388,7 @@ args SendTxArgs + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1185,12 +1436,24 @@ args SendTxArgs #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_fillTransaction", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_fillTransaction", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_fillTransaction", "params": []}' ``` + === "Javascript Console" ``` js @@ -1198,6 +1461,7 @@ args SendTxArgs ``` +
Source code

```go @@ -1206,7 +1470,7 @@ func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args Sen return nil, err } tx := args.toTransaction() - data, err := rlp.EncodeToBytes(tx) + data, err := tx.MarshalBinary() if err != nil { return nil, err } @@ -1215,7 +1479,7 @@ func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args Sen // and returns it to the caller for further processing (signing + broadcast) ``` -View on GitHub → +View on GitHub →

@@ -1241,7 +1505,7 @@ _None_ + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1271,12 +1535,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_gasPrice", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_gasPrice", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_gasPrice", "params": []}' ``` + === "Javascript Console" ``` js @@ -1284,6 +1560,7 @@ _None_ ``` +
Source code

```go @@ -1293,7 +1570,7 @@ func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) }// GasPrice returns a suggestion for a gas price. ``` -View on GitHub → +View on GitHub →

@@ -1310,7 +1587,7 @@ block numbers are also allowed. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1318,7 +1595,7 @@ address common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1366,7 +1643,7 @@ blockNrOrHash rpc.BlockNumberOrHash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1396,12 +1673,24 @@ blockNrOrHash rpc.BlockNumberOrHash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getBalance", "params": [
, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getBalance", "params": [
, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getBalance", "params": [
, ]}' ``` + === "Javascript Console" ``` js @@ -1409,6 +1698,7 @@ blockNrOrHash rpc.BlockNumberOrHash ``` +
Source code

```go @@ -1423,7 +1713,7 @@ func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Add // block numbers are also allowed. ``` -View on GitHub → +View on GitHub →

@@ -1439,7 +1729,7 @@ detail, otherwise only the transaction hash is returned. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1447,7 +1737,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1495,7 +1785,7 @@ fullTx bool + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1753,12 +2043,24 @@ fullTx bool #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getBlockByHash", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getBlockByHash", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getBlockByHash", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -1766,6 +2068,7 @@ fullTx bool ``` +
Source code

```go @@ -1779,7 +2082,7 @@ func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Ha // detail, otherwise only the transaction hash is returned. ``` -View on GitHub → +View on GitHub →

@@ -1798,7 +2101,7 @@ GetBlockByNumber returns the requested canonical block. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1806,7 +2109,7 @@ number rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1882,7 +2185,7 @@ fullTx bool + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2140,12 +2443,24 @@ fullTx bool #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getBlockByNumber", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getBlockByNumber", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getBlockByNumber", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -2153,6 +2468,7 @@ fullTx bool ``` +
Source code

```go @@ -2173,7 +2489,7 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.B // only the transaction hash is returned. ``` -View on GitHub → +View on GitHub →

@@ -2188,7 +2504,7 @@ GetBlockTransactionCountByHash returns the number of transactions in the block w #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -2196,7 +2512,7 @@ blockHash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2235,7 +2551,7 @@ blockHash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2265,12 +2581,24 @@ blockHash common.Hash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getBlockTransactionCountByHash", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getBlockTransactionCountByHash", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getBlockTransactionCountByHash", "params": []}' ``` + === "Javascript Console" ``` js @@ -2278,6 +2606,7 @@ blockHash common.Hash ``` +
Source code

```go @@ -2290,7 +2619,7 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Co }// GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash. ``` -View on GitHub → +View on GitHub →

@@ -2305,7 +2634,7 @@ GetBlockTransactionCountByNumber returns the number of transactions in the block #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -2313,7 +2642,7 @@ blockNr rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2380,7 +2709,7 @@ blockNr rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2410,12 +2739,24 @@ blockNr rpc.BlockNumber #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getBlockTransactionCountByNumber", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getBlockTransactionCountByNumber", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getBlockTransactionCountByNumber", "params": []}' ``` + === "Javascript Console" ``` js @@ -2423,6 +2764,7 @@ blockNr rpc.BlockNumber ``` +
Source code

```go @@ -2435,7 +2777,7 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context. }// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. ``` -View on GitHub → +View on GitHub →

@@ -2450,7 +2792,7 @@ GetCode returns the code stored at the given address in the state for the given #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -2458,7 +2800,7 @@ address common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2506,7 +2848,7 @@ blockNrOrHash rpc.BlockNumberOrHash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2536,12 +2878,24 @@ blockNrOrHash rpc.BlockNumberOrHash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getCode", "params": [
, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getCode", "params": [
, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getCode", "params": [
, ]}' ``` + === "Javascript Console" ``` js @@ -2549,6 +2903,7 @@ blockNrOrHash rpc.BlockNumberOrHash ``` +
Source code

```go @@ -2562,7 +2917,7 @@ func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Addres }// GetCode returns the code stored at the given address in the state for the given block number. ``` -View on GitHub → +View on GitHub →

@@ -2578,12 +2933,12 @@ last time it was called. This can be used for polling. For pending transaction and block filters the result is []common.Hash. (pending)Log filters return []Log. -https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges +https://eth.wiki/json-rpc/API#eth_getfilterchanges #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -2592,6 +2947,29 @@ id rpc.ID + Required: ✓ Yes +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + @@ -2609,12 +2987,24 @@ interface interface{} #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getFilterChanges", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getFilterChanges", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getFilterChanges", "params": []}' ``` + === "Javascript Console" ``` js @@ -2622,6 +3012,7 @@ interface interface{} ``` +
Source code

```go @@ -2632,7 +3023,7 @@ func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) { if !f.deadline.Stop() { <-f.deadline.C } - f.deadline.Reset(deadline) + f.deadline.Reset(api.timeout) switch f.typ { case PendingTransactionsSubscription, BlocksSubscription, SideBlocksSubscription: hashes := f.hashes @@ -2650,11 +3041,11 @@ func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) { // For pending transaction and block filters the result is []common.Hash. // (pending)Log filters return []Log. // - // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges + // https://eth.wiki/json-rpc/API#eth_getfilterchanges ]interface{}{}, fmt.Errorf("filter not found") } ``` -View on GitHub → +View on GitHub →

@@ -2667,12 +3058,12 @@ func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) { GetFilterLogs returns the logs for the filter with the given id. If the filter could not be found an empty array of logs is returned. -https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs +https://eth.wiki/json-rpc/API#eth_getfilterlogs #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -2681,6 +3072,29 @@ id rpc.ID + Required: ✓ Yes +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + @@ -2693,7 +3107,7 @@ typesLog []*types.Log + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2829,12 +3243,24 @@ typesLog []*types.Log #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getFilterLogs", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getFilterLogs", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getFilterLogs", "params": []}' ``` + === "Javascript Console" ``` js @@ -2842,13 +3268,14 @@ typesLog []*types.Log ``` +
Source code

```go func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([ // GetFilterLogs returns the logs for the filter with the given id. // If the filter could not be found an empty array of logs is returned. // -// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs +// https://eth.wiki/json-rpc/API#eth_getfilterlogs ]*types.Log, error) { api.filtersMu.Lock() f, found := api.filters[id] @@ -2877,7 +3304,7 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([ // return returnLogs(logs), nil } ``` -View on GitHub → +View on GitHub →

@@ -2903,7 +3330,7 @@ _None_ + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2933,12 +3360,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getHashrate", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getHashrate", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getHashrate", "params": []}' ``` + === "Javascript Console" ``` js @@ -2946,6 +3385,7 @@ _None_ ``` +
Source code

```go @@ -2954,7 +3394,7 @@ func (api *API) GetHashrate() uint64 { }// GetHashrate returns the current hashrate for local CPU miner and remote miner. ``` -View on GitHub → +View on GitHub →

@@ -2969,7 +3409,7 @@ GetHeaderByHash returns the requested header by hash. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -2977,7 +3417,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3016,7 +3456,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3238,12 +3678,24 @@ hash common.Hash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getHeaderByHash", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getHeaderByHash", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getHeaderByHash", "params": []}' ``` + === "Javascript Console" ``` js @@ -3251,6 +3703,7 @@ hash common.Hash ``` +
Source code

```go @@ -3263,7 +3716,7 @@ func (s *PublicBlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.H }// GetHeaderByHash returns the requested header by hash. ``` -View on GitHub → +View on GitHub →

@@ -3280,7 +3733,7 @@ GetHeaderByNumber returns the requested canonical block header. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -3288,7 +3741,7 @@ number rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3355,7 +3808,7 @@ number rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3577,12 +4030,24 @@ number rpc.BlockNumber #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getHeaderByNumber", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getHeaderByNumber", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getHeaderByNumber", "params": []}' ``` + === "Javascript Console" ``` js @@ -3590,6 +4055,7 @@ number rpc.BlockNumber ``` +
Source code

```go @@ -3608,7 +4074,7 @@ func (s *PublicBlockChainAPI) GetHeaderByNumber(ctx context.Context, number rpc. // * When blockNr is -2 the pending chain head is returned. ``` -View on GitHub → +View on GitHub →

@@ -3620,12 +4086,12 @@ func (s *PublicBlockChainAPI) GetHeaderByNumber(ctx context.Context, number rpc. GetLogs returns logs matching the given argument that are stored within the state. -https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs +https://eth.wiki/json-rpc/API#eth_getlogs #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -3633,7 +4099,7 @@ crit FilterCriteria + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3743,7 +4209,7 @@ typesLog []*types.Log + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3879,12 +4345,24 @@ typesLog []*types.Log #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getLogs", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getLogs", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getLogs", "params": []}' ``` + === "Javascript Console" ``` js @@ -3892,12 +4370,13 @@ typesLog []*types.Log ``` +
Source code

```go func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([ // GetLogs returns logs matching the given argument that are stored within the state. // -// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs +// https://eth.wiki/json-rpc/API#eth_getlogs ]*types.Log, error) { var filter *Filter if crit.BlockHash != nil { @@ -3920,7 +4399,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([ return returnLogs(logs), err } ``` -View on GitHub → +View on GitHub →

@@ -3935,7 +4414,7 @@ GetProof returns the Merkle-proof for a given account and optionally some storag #### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -3943,7 +4422,7 @@ address common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -3977,7 +4456,7 @@ storageKeys []string + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4031,7 +4510,7 @@ blockNrOrHash rpc.BlockNumberOrHash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4169,21 +4648,34 @@ blockNrOrHash rpc.BlockNumberOrHash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getProof", "params": [
, , ]}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getProof", "params": [
, , ]}' ``` -=== "Javascript Console" - ``` js - eth.getProof(address,storageKeys,blockNrOrHash); - ``` -
Source code -

+ +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getProof", "params": [

, , ]}' + ``` + + +=== "Javascript Console" + + ``` js + eth.getProof(address,storageKeys,blockNrOrHash); + ``` + + + +
Source code +

```go func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys [ // GetProof returns the Merkle-proof for a given account and optionally some storage keys. ]string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) { @@ -4218,7 +4710,7 @@ func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Addre return &AccountResult{Address: address, AccountProof: toHexSlice(accountProof), Balance: (*hexutil.Big)(state.GetBalance(address)), CodeHash: codeHash, Nonce: hexutil.Uint64(state.GetNonce(address)), StorageHash: storageHash, StorageProof: storageProof}, state.Error() } ``` -View on GitHub → +View on GitHub →

@@ -4233,7 +4725,7 @@ GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for th #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -4241,7 +4733,7 @@ blockHash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4275,7 +4767,7 @@ index hexutil.Uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4314,7 +4806,7 @@ index hexutil.Uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4344,12 +4836,24 @@ index hexutil.Uint #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getRawTransactionByBlockHashAndIndex", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getRawTransactionByBlockHashAndIndex", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getRawTransactionByBlockHashAndIndex", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -4357,6 +4861,7 @@ index hexutil.Uint ``` +
Source code

```go @@ -4368,7 +4873,7 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx cont }// GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for the given block hash and index. ``` -View on GitHub → +View on GitHub →

@@ -4383,7 +4888,7 @@ GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -4391,7 +4896,7 @@ blockNr rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4453,7 +4958,7 @@ index hexutil.Uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4492,7 +4997,7 @@ index hexutil.Uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4522,12 +5027,24 @@ index hexutil.Uint #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getRawTransactionByBlockNumberAndIndex", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getRawTransactionByBlockNumberAndIndex", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getRawTransactionByBlockNumberAndIndex", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -4535,6 +5052,7 @@ index hexutil.Uint ``` +
Source code

```go @@ -4546,7 +5064,7 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx co }// GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index. ``` -View on GitHub → +View on GitHub →

@@ -4561,7 +5079,7 @@ GetRawTransactionByHash returns the bytes of the transaction for the given hash. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -4569,7 +5087,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4608,7 +5126,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4638,12 +5156,24 @@ hash common.Hash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getRawTransactionByHash", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getRawTransactionByHash", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getRawTransactionByHash", "params": []}' ``` + === "Javascript Console" ``` js @@ -4651,6 +5181,7 @@ hash common.Hash ``` +
Source code

```go @@ -4664,11 +5195,11 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, return nil, nil } } - return rlp.EncodeToBytes(tx) + return tx.MarshalBinary() }// GetRawTransactionByHash returns the bytes of the transaction for the given hash. ``` -View on GitHub → +View on GitHub →

@@ -4685,7 +5216,7 @@ numbers are also allowed. #### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -4693,7 +5224,7 @@ address common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4750,7 +5281,7 @@ blockNrOrHash rpc.BlockNumberOrHash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4780,12 +5311,24 @@ blockNrOrHash rpc.BlockNumberOrHash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getStorageAt", "params": [
, , ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getStorageAt", "params": [
, , ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getStorageAt", "params": [
, , ]}' ``` + === "Javascript Console" ``` js @@ -4793,6 +5336,7 @@ blockNrOrHash rpc.BlockNumberOrHash ``` +
Source code

```go @@ -4808,7 +5352,7 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A :], state.Error() } ``` -View on GitHub → +View on GitHub →

@@ -4823,7 +5367,7 @@ GetTransactionByBlockHashAndIndex returns the transaction for the given block ha #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -4831,7 +5375,7 @@ blockHash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4865,7 +5409,7 @@ index hexutil.Uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -4904,13 +5448,36 @@ index hexutil.Uint + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + - blockHash: - pattern: `^0x[a-fA-F\d]{64}$` - title: `keccak` @@ -4921,6 +5488,11 @@ index hexutil.Uint - title: `integer` - type: `string` + - chainId: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + - from: - pattern: `^0x[a-fA-F\d]{64}$` - title: `keccak` @@ -4971,6 +5543,11 @@ index hexutil.Uint - title: `uint64` - type: `string` + - type: + - pattern: `^0x([a-fA-F\d])+$` + - title: `uint64` + - type: `string` + - v: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -4993,6 +5570,29 @@ index hexutil.Uint { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, "blockHash": { "pattern": "^0x[a-fA-F\\d]{64}$", "title": "keccak", @@ -5003,6 +5603,11 @@ index hexutil.Uint "title": "integer", "type": "string" }, + "chainId": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, "from": { "pattern": "^0x[a-fA-F\\d]{64}$", "title": "keccak", @@ -5053,6 +5658,11 @@ index hexutil.Uint "title": "uint64", "type": "string" }, + "type": { + "pattern": "^0x([a-fA-F\\d])+$", + "title": "uint64", + "type": "string" + }, "v": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -5074,12 +5684,24 @@ index hexutil.Uint #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getTransactionByBlockHashAndIndex", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getTransactionByBlockHashAndIndex", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getTransactionByBlockHashAndIndex", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -5087,6 +5709,7 @@ index hexutil.Uint ``` +
Source code

```go @@ -5098,7 +5721,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context }// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. ``` -View on GitHub → +View on GitHub →

@@ -5113,7 +5736,7 @@ GetTransactionByBlockNumberAndIndex returns the transaction for the given block #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -5121,7 +5744,7 @@ blockNr rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5183,7 +5806,7 @@ index hexutil.Uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5222,13 +5845,36 @@ index hexutil.Uint + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + - blockHash: - pattern: `^0x[a-fA-F\d]{64}$` - title: `keccak` @@ -5239,6 +5885,11 @@ index hexutil.Uint - title: `integer` - type: `string` + - chainId: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + - from: - pattern: `^0x[a-fA-F\d]{64}$` - title: `keccak` @@ -5289,6 +5940,11 @@ index hexutil.Uint - title: `uint64` - type: `string` + - type: + - pattern: `^0x([a-fA-F\d])+$` + - title: `uint64` + - type: `string` + - v: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -5311,6 +5967,29 @@ index hexutil.Uint { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, "blockHash": { "pattern": "^0x[a-fA-F\\d]{64}$", "title": "keccak", @@ -5321,6 +6000,11 @@ index hexutil.Uint "title": "integer", "type": "string" }, + "chainId": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, "from": { "pattern": "^0x[a-fA-F\\d]{64}$", "title": "keccak", @@ -5371,6 +6055,11 @@ index hexutil.Uint "title": "uint64", "type": "string" }, + "type": { + "pattern": "^0x([a-fA-F\\d])+$", + "title": "uint64", + "type": "string" + }, "v": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -5392,12 +6081,24 @@ index hexutil.Uint #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getTransactionByBlockNumberAndIndex", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getTransactionByBlockNumberAndIndex", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getTransactionByBlockNumberAndIndex", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -5405,6 +6106,7 @@ index hexutil.Uint ``` +
Source code

```go @@ -5416,7 +6118,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx conte }// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. ``` -View on GitHub → +View on GitHub →

@@ -5431,7 +6133,7 @@ GetTransactionByHash returns the transaction for the given hash #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -5439,7 +6141,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5478,13 +6180,36 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + - blockHash: - pattern: `^0x[a-fA-F\d]{64}$` - title: `keccak` @@ -5495,6 +6220,11 @@ hash common.Hash - title: `integer` - type: `string` + - chainId: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + - from: - pattern: `^0x[a-fA-F\d]{64}$` - title: `keccak` @@ -5545,6 +6275,11 @@ hash common.Hash - title: `uint64` - type: `string` + - type: + - pattern: `^0x([a-fA-F\d])+$` + - title: `uint64` + - type: `string` + - v: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -5567,22 +6302,50 @@ hash common.Hash { "additionalProperties": false, "properties": { - "blockHash": { - "pattern": "^0x[a-fA-F\\d]{64}$", - "title": "keccak", - "type": "string" - }, - "blockNumber": { - "pattern": "^0x[a-fA-F0-9]+$", - "title": "integer", - "type": "string" - }, - "from": { - "pattern": "^0x[a-fA-F\\d]{64}$", - "title": "keccak", - "type": "string" - }, - "gas": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "blockHash": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "blockNumber": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "chainId": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "from": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "gas": { "pattern": "^0x([a-fA-F\\d])+$", "title": "uint64", "type": "string" @@ -5627,6 +6390,11 @@ hash common.Hash "title": "uint64", "type": "string" }, + "type": { + "pattern": "^0x([a-fA-F\\d])+$", + "title": "uint64", + "type": "string" + }, "v": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -5648,12 +6416,24 @@ hash common.Hash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getTransactionByHash", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getTransactionByHash", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getTransactionByHash", "params": []}' ``` + === "Javascript Console" ``` js @@ -5661,6 +6441,7 @@ hash common.Hash ``` +
Source code

```go @@ -5679,7 +6460,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, has }// GetTransactionByHash returns the transaction for the given hash ``` -View on GitHub → +View on GitHub →

@@ -5694,7 +6475,7 @@ GetTransactionCount returns the number of transactions the given address has sen #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -5702,7 +6483,7 @@ address common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5750,7 +6531,7 @@ blockNrOrHash rpc.BlockNumberOrHash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5780,12 +6561,24 @@ blockNrOrHash rpc.BlockNumberOrHash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getTransactionCount", "params": [
, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getTransactionCount", "params": [
, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getTransactionCount", "params": [
, ]}' ``` + === "Javascript Console" ``` js @@ -5793,6 +6586,7 @@ blockNrOrHash rpc.BlockNumberOrHash ``` +
Source code

```go @@ -5813,7 +6607,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, addr }// GetTransactionCount returns the number of transactions the given address has sent for the given block number ``` -View on GitHub → +View on GitHub →

@@ -5828,7 +6622,7 @@ GetTransactionReceipt returns the transaction receipt for the given transaction #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -5836,7 +6630,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5874,7 +6668,7 @@ mapstringinterface map[string]interface{} + Required: ✓ Yes - + === "Schema" ``` Schema @@ -5908,12 +6702,24 @@ mapstringinterface map[string]interface{} #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getTransactionReceipt", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getTransactionReceipt", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getTransactionReceipt", "params": []}' ``` + === "Javascript Console" ``` js @@ -5921,6 +6727,7 @@ mapstringinterface map[string]interface{} ``` +
Source code

```go @@ -5938,12 +6745,10 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha return nil, nil } receipt := receipts[index] - var signer types.Signer = types.FrontierSigner{} - if tx.Protected() { - signer = types.NewEIP155Signer(tx.ChainId()) - } + bigblock := new(big.Int).SetUint64(blockNumber) + signer := types.MakeSigner(s.b.ChainConfig(), bigblock) from, _ := types.Sender(signer, tx) - fields := map[string]interface{}{"blockHash": blockHash, "blockNumber": hexutil.Uint64(blockNumber), "transactionHash": hash, "transactionIndex": hexutil.Uint64(index), "from": from, "to": tx.To(), "gasUsed": hexutil.Uint64(receipt.GasUsed), "cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed), "contractAddress": nil, "logs": receipt.Logs, "logsBloom": receipt.Bloom} + fields := map[string]interface{}{"blockHash": blockHash, "blockNumber": hexutil.Uint64(blockNumber), "transactionHash": hash, "transactionIndex": hexutil.Uint64(index), "from": from, "to": tx.To(), "gasUsed": hexutil.Uint64(receipt.GasUsed), "cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed), "contractAddress": nil, "logs": receipt.Logs, "logsBloom": receipt.Bloom, "type": hexutil.Uint(tx.Type())} if len(receipt.PostState) > 0 { fields["root"] = hexutil.Bytes(receipt.PostState) } else { @@ -5958,7 +6763,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha return fields, nil } ``` -View on GitHub → +View on GitHub →

@@ -5974,7 +6779,7 @@ all transactions in the block are returned in full detail, otherwise only the tr #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -5982,7 +6787,7 @@ blockHash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -6016,7 +6821,7 @@ index hexutil.Uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -6055,7 +6860,7 @@ index hexutil.Uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -6313,12 +7118,24 @@ index hexutil.Uint #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getUncleByBlockHashAndIndex", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getUncleByBlockHashAndIndex", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getUncleByBlockHashAndIndex", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -6326,6 +7143,7 @@ index hexutil.Uint ``` +
Source code

```go @@ -6345,7 +7163,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, b // all transactions in the block are returned in full detail, otherwise only the transaction hash is returned. ``` -View on GitHub → +View on GitHub →

@@ -6361,7 +7179,7 @@ all transactions in the block are returned in full detail, otherwise only the tr #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -6369,7 +7187,7 @@ blockNr rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -6431,7 +7249,7 @@ index hexutil.Uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -6470,7 +7288,7 @@ index hexutil.Uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -6728,12 +7546,24 @@ index hexutil.Uint #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getUncleByBlockNumberAndIndex", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getUncleByBlockNumberAndIndex", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getUncleByBlockNumberAndIndex", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -6741,6 +7571,7 @@ index hexutil.Uint ``` +
Source code

```go @@ -6760,7 +7591,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, // all transactions in the block are returned in full detail, otherwise only the transaction hash is returned. ``` -View on GitHub → +View on GitHub →

@@ -6775,7 +7606,7 @@ GetUncleCountByBlockHash returns number of uncles in the block for the given blo #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -6783,7 +7614,7 @@ blockHash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -6822,7 +7653,7 @@ blockHash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -6852,12 +7683,24 @@ blockHash common.Hash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getUncleCountByBlockHash", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getUncleCountByBlockHash", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getUncleCountByBlockHash", "params": []}' ``` + === "Javascript Console" ``` js @@ -6865,6 +7708,7 @@ blockHash common.Hash ``` +
Source code

```go @@ -6877,7 +7721,7 @@ func (s *PublicBlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, bloc }// GetUncleCountByBlockHash returns number of uncles in the block for the given block hash ``` -View on GitHub → +View on GitHub →

@@ -6892,7 +7736,7 @@ GetUncleCountByBlockNumber returns number of uncles in the block for the given b #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -6900,7 +7744,7 @@ blockNr rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -6967,7 +7811,7 @@ blockNr rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -6997,12 +7841,24 @@ blockNr rpc.BlockNumber #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getUncleCountByBlockNumber", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getUncleCountByBlockNumber", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getUncleCountByBlockNumber", "params": []}' ``` + === "Javascript Console" ``` js @@ -7010,6 +7866,7 @@ blockNr rpc.BlockNumber ``` +
Source code

```go @@ -7022,7 +7879,7 @@ func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, bl }// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number ``` -View on GitHub → +View on GitHub →

@@ -7053,7 +7910,7 @@ num4string [4]string + Required: ✓ Yes - + === "Schema" ``` Schema @@ -7093,12 +7950,24 @@ num4string [4]string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_getWork", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_getWork", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_getWork", "params": []}' ``` + === "Javascript Console" ``` js @@ -7106,6 +7975,7 @@ num4string [4]string ``` +
Source code

```go @@ -7137,7 +8007,7 @@ func (api *API) GetWork() ([4]string, error) { // result[3] - hex encoded block number ``` -View on GitHub → +View on GitHub →

@@ -7163,7 +8033,7 @@ _None_ + Required: ✓ Yes - + === "Schema" ``` Schema @@ -7193,80 +8063,41 @@ _None_ #### Client Method Invocation Examples -=== "Shell" - - ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_hashrate", "params": []}' - ``` -=== "Javascript Console" +=== "Shell HTTP" - ``` js - eth.hashrate(); + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_hashrate", "params": []}' ``` -
Source code -

-```go -func (api *PublicEthereumAPI) Hashrate() hexutil.Uint64 { - return hexutil.Uint64(api.e.Miner().HashRate()) -}// Hashrate returns the POW hashrate - -``` -View on GitHub → -

-
- ---- - - - -### eth_mining - -Mining returns an indication if this node is currently mining. - - -#### Params (0) - -_None_ - -#### Result - - - - -bool - - + Required: ✓ Yes - -#### Client Method Invocation Examples - -=== "Shell" +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_mining", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_hashrate", "params": []}' ``` + === "Javascript Console" ``` js - eth.mining(); + eth.hashrate(); ``` +
Source code

```go -func (api *PublicMinerAPI) Mining() bool { - return api.e.IsMining() -}// Mining returns an indication if this node is currently mining. +func (api *PublicEthereumAPI) Hashrate() hexutil.Uint64 { + return hexutil.Uint64(api.e.Miner().HashRate()) +}// Hashrate returns the POW hashrate ``` -View on GitHub → +View on GitHub →

@@ -7274,46 +8105,349 @@ func (api *PublicMinerAPI) Mining() bool { -### eth_newBlockFilter +### eth_logs -NewBlockFilter creates a filter that fetches blocks that are imported into the chain. -It is part of the filter package since polling goes with eth_getFilterChanges. +Logs creates a subscription that fires for all new logs that match the given filter criteria. -https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newblockfilter +#### Params (1) -#### Params (0) +Parameters must be given _by position_. -_None_ -#### Result +__1:__ +crit FilterCriteria + + Required: ✓ Yes +=== "Schema" -rpc.ID + ``` Schema + + - additionalProperties: `false` + - properties: + - Addresses: + - items: + - description: `Hex representation of a Keccak 256 hash POINTER` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` - + Required: ✓ Yes + - type: `array` + - BlockHash: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + - FromBlock: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + - ToBlock: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` -#### Client Method Invocation Examples + - Topics: + - items: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` -=== "Shell" + - type: `array` - ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_newBlockFilter", "params": []}' - ``` + - type: `array` + + + - type: object -=== "Javascript Console" - ``` js - eth.newBlockFilter(); ``` +=== "Raw" -
Source code + ``` Raw + { + "additionalProperties": false, + "properties": { + "Addresses": { + "items": { + "description": "Hex representation of a Keccak 256 hash POINTER", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + }, + "BlockHash": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "FromBlock": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "ToBlock": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "Topics": { + "items": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + } + ``` + + + + + +#### Result + + + + +*rpc.Subscription + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + + + +#### Client Method Invocation Examples + + + + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["logs", ]}' + ``` + + + + +
Source code +

+```go +func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + var ( + rpcSub = notifier.CreateSubscription() + matchedLogs = make(chan [ // Logs creates a subscription that fires for all new logs that match the given filter criteria. + ]*types.Log) + ) + logsSub, err := api.events.SubscribeLogs(ethereum.FilterQuery(crit), matchedLogs) + if err != nil { + return nil, err + } + go func() { + for { + select { + case logs := <-matchedLogs: + for _, log := range logs { + notifier.Notify(rpcSub.ID, &log) + } + case <-rpcSub.Err(): + logsSub.Unsubscribe() + return + case <-notifier.Closed(): + logsSub.Unsubscribe() + return + } + } + }() + return rpcSub, nil +} +``` +View on GitHub → +

+
+ +--- + + + +### eth_mining + +Mining returns an indication if this node is currently mining. + + +#### Params (0) + +_None_ + +#### Result + + + + +bool + + + Required: ✓ Yes + + + + +#### Client Method Invocation Examples + + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_mining", "params": []}' + ``` + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_mining", "params": []}' + ``` + + +=== "Javascript Console" + + ``` js + eth.mining(); + ``` + + + +
Source code +

+```go +func (api *PublicMinerAPI) Mining() bool { + return api.e.IsMining() +}// Mining returns an indication if this node is currently mining. + +``` +View on GitHub → +

+
+ +--- + + + +### eth_newBlockFilter + +NewBlockFilter creates a filter that fetches blocks that are imported into the chain. +It is part of the filter package since polling goes with eth_getFilterChanges. + +https://eth.wiki/json-rpc/API#eth_newblockfilter + + +#### Params (0) + +_None_ + +#### Result + + + + +rpc.ID + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + + + +#### Client Method Invocation Examples + + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_newBlockFilter", "params": []}' + ``` + + + + +
Source code

```go func (api *PublicFilterAPI) NewBlockFilter() rpc.ID { @@ -7322,10 +8456,10 @@ func (api *PublicFilterAPI) NewBlockFilter() rpc.ID { headerSub = api.events.SubscribeNewHeads(headers) ) api.filtersMu.Lock() - api.filters[headerSub.ID] = &filter{typ: BlocksSubscription, deadline: time.NewTimer(deadline), hashes: make([ // NewBlockFilter creates a filter that fetches blocks that are imported into the chain. + api.filters[headerSub.ID] = &filter{typ: BlocksSubscription, deadline: time.NewTimer(api.timeout), hashes: make([ // NewBlockFilter creates a filter that fetches blocks that are imported into the chain. // It is part of the filter package since polling goes with eth_getFilterChanges. // - // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newblockfilter + // https://eth.wiki/json-rpc/API#eth_newblockfilter ]common.Hash, 0), s: headerSub} api.filtersMu.Unlock() go func() { @@ -7348,7 +8482,7 @@ func (api *PublicFilterAPI) NewBlockFilter() rpc.ID { return headerSub.ID } ``` -View on GitHub → +View on GitHub →

@@ -7370,12 +8504,12 @@ again but with the removed property set to true. In case "fromBlock" > "toBlock" an error is returned. -https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter +https://eth.wiki/json-rpc/API#eth_newfilter #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -7383,7 +8517,7 @@ crit FilterCriteria + Required: ✓ Yes - + === "Schema" ``` Schema @@ -7495,24 +8629,48 @@ crit FilterCriteria + Required: ✓ Yes +=== "Schema" + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string -#### Client Method Invocation Examples - -=== "Shell" - - ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_newFilter", "params": []}' - ``` - -=== "Javascript Console" - ``` js - eth.newFilter(crit); ``` +=== "Raw" -
Source code + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + + + +#### Client Method Invocation Examples + + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_newFilter", "params": []}' + ``` + + + + +
Source code

```go func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) { @@ -7528,36 +8686,350 @@ func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) { // // In case "fromBlock" > "toBlock" an error is returned. // - // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter + // https://eth.wiki/json-rpc/API#eth_newfilter ]*types.Log) logsSub, err := api.events.SubscribeLogs(ethereum.FilterQuery(crit), logs) if err != nil { - return rpc.ID(""), err + return "", err + } + api.filtersMu.Lock() + api.filters[logsSub.ID] = &filter{typ: LogsSubscription, crit: crit, deadline: time.NewTimer(api.timeout), logs: make([]*types.Log, 0), s: logsSub} + api.filtersMu.Unlock() + go func() { + for { + select { + case l := <-logs: + api.filtersMu.Lock() + if f, found := api.filters[logsSub.ID]; found { + f.logs = append(f.logs, l...) + } + api.filtersMu.Unlock() + case <-logsSub.Err(): + api.filtersMu.Lock() + delete(api.filters, logsSub.ID) + api.filtersMu.Unlock() + return + } + } + }() + return logsSub.ID, nil +} +``` +View on GitHub → +

+
+ +--- + + + +### eth_newHeads + +NewHeads send a notification each time a new (header) block is appended to the chain. + + +#### Params (0) + +_None_ + +#### Result + + + + +*rpc.Subscription + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + + + +#### Client Method Invocation Examples + + + + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["newHeads"]}' + ``` + + + + +
Source code +

+```go +func (api *PublicFilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + rpcSub := notifier.CreateSubscription() + go func() { + headers := make(chan *types.Header) + headersSub := api.events.SubscribeNewHeads(headers) + for { + select { + case h := <-headers: + notifier.Notify(rpcSub.ID, h) + case <-rpcSub.Err(): + headersSub.Unsubscribe() + return + case <-notifier.Closed(): + headersSub.Unsubscribe() + return + } + } + }() + return rpcSub, nil +}// NewHeads send a notification each time a new (header) block is appended to the chain. + +``` +View on GitHub → +

+
+ +--- + + + +### eth_newPendingTransactionFilter + +NewPendingTransactionFilter creates a filter that fetches pending transaction hashes +as transactions enter the pending state. + +It is part of the filter package because this filter can be used through the +`eth_getFilterChanges` polling method that is also used for log filters. + +https://eth.wiki/json-rpc/API#eth_newpendingtransactionfilter + + +#### Params (0) + +_None_ + +#### Result + + + + +rpc.ID + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + + + +#### Client Method Invocation Examples + + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_newPendingTransactionFilter", "params": []}' + ``` + + + + +
Source code +

+```go +func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID { + var ( + pendingTxs = make(chan [ // NewPendingTransactionFilter creates a filter that fetches pending transaction hashes + // as transactions enter the pending state. + // + // It is part of the filter package because this filter can be used through the + // `eth_getFilterChanges` polling method that is also used for log filters. + // + // https://eth.wiki/json-rpc/API#eth_newpendingtransactionfilter + ]common.Hash) + pendingTxSub = api.events.SubscribePendingTxs(pendingTxs) + ) + api.filtersMu.Lock() + api.filters[pendingTxSub.ID] = &filter{typ: PendingTransactionsSubscription, deadline: time.NewTimer(api.timeout), hashes: make([]common.Hash, 0), s: pendingTxSub} + api.filtersMu.Unlock() + go func() { + for { + select { + case ph := <-pendingTxs: + api.filtersMu.Lock() + if f, found := api.filters[pendingTxSub.ID]; found { + f.hashes = append(f.hashes, ph...) + } + api.filtersMu.Unlock() + case <-pendingTxSub.Err(): + api.filtersMu.Lock() + delete(api.filters, pendingTxSub.ID) + api.filtersMu.Unlock() + return + } + } + }() + return pendingTxSub.ID +} +``` +View on GitHub → +

+
+ +--- + + + +### eth_newPendingTransactions + +NewPendingTransactions creates a subscription that is triggered each time a transaction +enters the transaction pool and was signed from one of the transactions this nodes manages. + + +#### Params (0) + +_None_ + +#### Result + + + + +*rpc.Subscription + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + + + +#### Client Method Invocation Examples + + + + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["newPendingTransactions"]}' + ``` + + + + +
Source code +

+```go +func (api *PublicFilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported } - api.filtersMu.Lock() - api.filters[logsSub.ID] = &filter{typ: LogsSubscription, crit: crit, deadline: time.NewTimer(deadline), logs: make([]*types.Log, 0), s: logsSub} - api.filtersMu.Unlock() + rpcSub := notifier.CreateSubscription() go func() { + txHashes := make(chan [ // NewPendingTransactions creates a subscription that is triggered each time a transaction + // enters the transaction pool and was signed from one of the transactions this nodes manages. + ]common.Hash, 128) + pendingTxSub := api.events.SubscribePendingTxs(txHashes) for { select { - case l := <-logs: - api.filtersMu.Lock() - if f, found := api.filters[logsSub.ID]; found { - f.logs = append(f.logs, l...) + case hashes := <-txHashes: + for _, h := range hashes { + notifier.Notify(rpcSub.ID, h) } - api.filtersMu.Unlock() - case <-logsSub.Err(): - api.filtersMu.Lock() - delete(api.filters, logsSub.ID) - api.filtersMu.Unlock() + case <-rpcSub.Err(): + pendingTxSub.Unsubscribe() + return + case <-notifier.Closed(): + pendingTxSub.Unsubscribe() return } } }() - return logsSub.ID, nil + return rpcSub, nil } ``` -View on GitHub → +View on GitHub →

@@ -7565,15 +9037,10 @@ func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) { -### eth_newPendingTransactionFilter - -NewPendingTransactionFilter creates a filter that fetches pending transaction hashes -as transactions enter the pending state. - -It is part of the filter package because this filter can be used through the -`eth_getFilterChanges` polling method that is also used for log filters. +### eth_newSideBlockFilter -https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter +NewSideBlockFilter creates a filter that fetches blocks that are imported into the chain with a non-canonical status. +It is part of the filter package since polling goes with eth_getFilterChanges. #### Params (0) @@ -7590,62 +9057,81 @@ _None_ + Required: ✓ Yes +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + #### Client Method Invocation Examples -=== "Shell" + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_newPendingTransactionFilter", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_newSideBlockFilter", "params": []}' ``` -=== "Javascript Console" - ``` js - eth.newPendingTransactionFilter(); - ```
Source code

```go -func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID { +func (api *PublicFilterAPI) NewSideBlockFilter() rpc.ID { var ( - pendingTxs = make(chan [ // NewPendingTransactionFilter creates a filter that fetches pending transaction hashes - // as transactions enter the pending state. - // - // It is part of the filter package because this filter can be used through the - // `eth_getFilterChanges` polling method that is also used for log filters. - // - // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter - ]common.Hash) - pendingTxSub = api.events.SubscribePendingTxs(pendingTxs) + headers = make(chan *types.Header) + headerSub = api.events.SubscribeNewSideHeads(headers) ) api.filtersMu.Lock() - api.filters[pendingTxSub.ID] = &filter{typ: PendingTransactionsSubscription, deadline: time.NewTimer(deadline), hashes: make([]common.Hash, 0), s: pendingTxSub} + api.filters[headerSub.ID] = &filter{typ: SideBlocksSubscription, deadline: time.NewTimer(api.timeout), hashes: make([ // NewSideBlockFilter creates a filter that fetches blocks that are imported into the chain with a non-canonical status. + // It is part of the filter package since polling goes with eth_getFilterChanges. + ]common.Hash, 0), s: headerSub} api.filtersMu.Unlock() go func() { for { select { - case ph := <-pendingTxs: + case h := <-headers: api.filtersMu.Lock() - if f, found := api.filters[pendingTxSub.ID]; found { - f.hashes = append(f.hashes, ph...) + if f, found := api.filters[headerSub.ID]; found { + f.hashes = append(f.hashes, h.Hash()) } api.filtersMu.Unlock() - case <-pendingTxSub.Err(): + case <-headerSub.Err(): api.filtersMu.Lock() - delete(api.filters, pendingTxSub.ID) + delete(api.filters, headerSub.ID) api.filtersMu.Unlock() return } } }() - return pendingTxSub.ID + return headerSub.ID } ``` -View on GitHub → +View on GitHub →

@@ -7653,10 +9139,9 @@ func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID { -### eth_newSideBlockFilter +### eth_newSideHeads -NewSideBlockFilter creates a filter that fetches blocks that are imported into the chain with a non-canonical status. -It is part of the filter package since polling goes with eth_getFilterChanges. +NewSideHeads send a notification each time a new non-canonical (header) block is written to the database. #### Params (0) @@ -7668,62 +9153,84 @@ _None_ -rpc.ID +*rpc.Subscription + Required: ✓ Yes +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + #### Client Method Invocation Examples -=== "Shell" + + + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_newSideBlockFilter", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["newSideHeads"]}' ``` -=== "Javascript Console" - ``` js - eth.newSideBlockFilter(); - ```
Source code

```go -func (api *PublicFilterAPI) NewSideBlockFilter() rpc.ID { - var ( - headers = make(chan *types.Header) - headerSub = api.events.SubscribeNewSideHeads(headers) - ) - api.filtersMu.Lock() - api.filters[headerSub.ID] = &filter{typ: SideBlocksSubscription, deadline: time.NewTimer(deadline), hashes: make([ // NewSideBlockFilter creates a filter that fetches blocks that are imported into the chain with a non-canonical status. - // It is part of the filter package since polling goes with eth_getFilterChanges. - ]common.Hash, 0), s: headerSub} - api.filtersMu.Unlock() +func (api *PublicFilterAPI) NewSideHeads(ctx context.Context) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + rpcSub := notifier.CreateSubscription() go func() { + headers := make(chan *types.Header) + headersSub := api.events.SubscribeNewSideHeads(headers) for { select { case h := <-headers: - api.filtersMu.Lock() - if f, found := api.filters[headerSub.ID]; found { - f.hashes = append(f.hashes, h.Hash()) - } - api.filtersMu.Unlock() - case <-headerSub.Err(): - api.filtersMu.Lock() - delete(api.filters, headerSub.ID) - api.filtersMu.Unlock() + notifier.Notify(rpcSub.ID, h) + case <-rpcSub.Err(): + headersSub.Unsubscribe() + return + case <-notifier.Closed(): + headersSub.Unsubscribe() return } } }() - return headerSub.ID -} + return rpcSub, nil +}// NewSideHeads send a notification each time a new non-canonical (header) block is written to the database. + ``` -View on GitHub → +View on GitHub →

@@ -7749,7 +9256,7 @@ RPCTransaction []*RPCTransaction + Required: ✓ Yes - + === "Schema" ``` Schema @@ -7758,6 +9265,29 @@ RPCTransaction []*RPCTransaction - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + - blockHash: - pattern: `^0x[a-fA-F\d]{64}$` - title: `keccak` @@ -7768,6 +9298,11 @@ RPCTransaction []*RPCTransaction - title: `integer` - type: `string` + - chainId: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + - from: - pattern: `^0x[a-fA-F\d]{64}$` - title: `keccak` @@ -7818,6 +9353,11 @@ RPCTransaction []*RPCTransaction - title: `uint64` - type: `string` + - type: + - pattern: `^0x([a-fA-F\d])+$` + - title: `uint64` + - type: `string` + - v: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -7845,6 +9385,29 @@ RPCTransaction []*RPCTransaction { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, "blockHash": { "pattern": "^0x[a-fA-F\\d]{64}$", "title": "keccak", @@ -7855,6 +9418,11 @@ RPCTransaction []*RPCTransaction "title": "integer", "type": "string" }, + "chainId": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, "from": { "pattern": "^0x[a-fA-F\\d]{64}$", "title": "keccak", @@ -7905,6 +9473,11 @@ RPCTransaction []*RPCTransaction "title": "uint64", "type": "string" }, + "type": { + "pattern": "^0x([a-fA-F\\d])+$", + "title": "uint64", + "type": "string" + }, "v": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -7931,127 +9504,59 @@ RPCTransaction []*RPCTransaction #### Client Method Invocation Examples -=== "Shell" - - ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_pendingTransactions", "params": []}' - ``` - -=== "Javascript Console" - - ``` js - eth.pendingTransactions(); - ``` - - -
Source code -

-```go -func (s *PublicTransactionPoolAPI) PendingTransactions() ([ // PendingTransactions returns the transactions that are in the transaction pool -// and have a from address that is one of the accounts this node manages. -]*RPCTransaction, error) { - pending, err := s.b.GetPoolTransactions() - if err != nil { - return nil, err - } - accounts := make(map[common.Address]struct{}) - for _, wallet := range s.b.AccountManager().Wallets() { - for _, account := range wallet.Accounts() { - accounts[account.Address] = struct{}{} - } - } - transactions := make([]*RPCTransaction, 0, len(pending)) - for _, tx := range pending { - var signer types.Signer = types.HomesteadSigner{} - if tx.Protected() { - signer = types.NewEIP155Signer(tx.ChainId()) - } - from, _ := types.Sender(signer, tx) - if _, exists := accounts[from]; exists { - transactions = append(transactions, newRPCPendingTransaction(tx)) - } - } - return transactions, nil -} -``` -View on GitHub → -

-
- ---- - - - -### eth_protocolVersion - -ProtocolVersion returns the current Ethereum protocol version this node supports - - -#### Params (0) - -_None_ - -#### Result - - - - -hexutil.Uint - - + Required: ✓ Yes - - -=== "Schema" - - ``` Schema - - - description: `Hex representation of a uint` - - pattern: `^0x([a-fA-F\d])+$` - - title: `uint` - - type: string - - - ``` - -=== "Raw" - - ``` Raw - { - "description": "Hex representation of a uint", - "pattern": "^0x([a-fA-F\\d])+$", - "title": "uint", - "type": [ - "string" - ] - } + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_pendingTransactions", "params": []}' ``` -#### Client Method Invocation Examples -=== "Shell" + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_protocolVersion", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_pendingTransactions", "params": []}' ``` + === "Javascript Console" ``` js - eth.protocolVersion(); + eth.pendingTransactions(); ``` +
Source code

```go -func (s *PublicEthereumAPI) ProtocolVersion() hexutil.Uint { - return hexutil.Uint(s.b.ProtocolVersion()) -}// ProtocolVersion returns the current Ethereum protocol version this node supports - +func (s *PublicTransactionPoolAPI) PendingTransactions() ([ // PendingTransactions returns the transactions that are in the transaction pool +// and have a from address that is one of the accounts this node manages. +]*RPCTransaction, error) { + pending, err := s.b.GetPoolTransactions() + if err != nil { + return nil, err + } + accounts := make(map[common.Address]struct{}) + for _, wallet := range s.b.AccountManager().Wallets() { + for _, account := range wallet.Accounts() { + accounts[account.Address] = struct{}{} + } + } + transactions := make([]*RPCTransaction, 0, len(pending)) + for _, tx := range pending { + from, _ := types.Sender(s.signer, tx) + if _, exists := accounts[from]; exists { + transactions = append(transactions, newRPCPendingTransaction(tx)) + } + } + return transactions, nil +} ``` -View on GitHub → +View on GitHub →

@@ -8067,7 +9572,7 @@ the given transaction from the pool and reinsert it with the new gas price and l #### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -8075,13 +9580,41 @@ sendArgs SendTxArgs + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + + - chainId: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + - data: - pattern: `^0x([a-fA-F\d])+$` - title: `dataWord` @@ -8134,6 +9667,34 @@ sendArgs SendTxArgs { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "chainId": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, "data": { "pattern": "^0x([a-fA-F\\d])+$", "title": "dataWord", @@ -8189,7 +9750,7 @@ gasPrice *hexutil.Big + Required: ✓ Yes - + === "Schema" ``` Schema @@ -8223,7 +9784,7 @@ gasLimit *hexutil.Uint64 + Required: ✓ Yes - + === "Schema" ``` Schema @@ -8262,7 +9823,7 @@ gasLimit *hexutil.Uint64 + Required: ✓ Yes - + === "Schema" ``` Schema @@ -8292,12 +9853,24 @@ gasLimit *hexutil.Uint64 #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_resend", "params": [, , ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_resend", "params": [, , ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_resend", "params": [, , ]}' ``` + === "Javascript Console" ``` js @@ -8305,6 +9878,7 @@ gasLimit *hexutil.Uint64 ``` +
Source code

```go @@ -8335,12 +9909,9 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr // the given transaction from the pool and reinsert it with the new gas price and limit. // Before replacing the old transaction, ensure the _new_ transaction fee is reasonable. range pending { - var signer types.Signer = types.HomesteadSigner{} - if p.Protected() { - signer = types.NewEIP155Signer(p.ChainId()) - } - wantSigHash := signer.Hash(matchTx) - if pFrom, err := types.Sender(signer, p); err == nil && pFrom == sendArgs.From && signer.Hash(p) == wantSigHash { + wantSigHash := s.signer.Hash(matchTx) + pFrom, err := types.Sender(s.signer, p) + if err == nil && pFrom == sendArgs.From && s.signer.Hash(p) == wantSigHash { if gasPrice != nil && (*big.Int)(gasPrice).Sign() != 0 { sendArgs.GasPrice = gasPrice } @@ -8360,7 +9931,7 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr return common.Hash{}, fmt.Errorf("transaction %#x not found", matchTx.Hash()) } ``` -View on GitHub → +View on GitHub →

@@ -8376,15 +9947,15 @@ The sender is responsible for signing the transaction and using the correct nonc #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ -encodedTx hexutil.Bytes +input hexutil.Bytes + Required: ✓ Yes - + === "Schema" ``` Schema @@ -8423,7 +9994,7 @@ encodedTx hexutil.Bytes + Required: ✓ Yes - + === "Schema" ``` Schema @@ -8453,25 +10024,38 @@ encodedTx hexutil.Bytes #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_sendRawTransaction", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_sendRawTransaction", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_sendRawTransaction", "params": []}' ``` + === "Javascript Console" ``` js - eth.sendRawTransaction(encodedTx); + eth.sendRawTransaction(input); ``` +
Source code

```go -func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) { +func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, input hexutil.Bytes) (common.Hash, error) { tx := new(types.Transaction) - if err := rlp.DecodeBytes(encodedTx, tx); err != nil { + if err := tx.UnmarshalBinary(input); err != nil { return common.Hash{}, err } return SubmitTransaction(ctx, s.b, tx) @@ -8479,7 +10063,7 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod // The sender is responsible for signing the transaction and using the correct nonce. ``` -View on GitHub → +View on GitHub →

@@ -8495,7 +10079,7 @@ transaction pool. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -8503,13 +10087,41 @@ args SendTxArgs + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + + - chainId: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + - data: - pattern: `^0x([a-fA-F\d])+$` - title: `dataWord` @@ -8562,6 +10174,34 @@ args SendTxArgs { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "chainId": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, "data": { "pattern": "^0x([a-fA-F\\d])+$", "title": "dataWord", @@ -8622,7 +10262,7 @@ args SendTxArgs + Required: ✓ Yes - + === "Schema" ``` Schema @@ -8652,12 +10292,24 @@ args SendTxArgs #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_sendTransaction", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_sendTransaction", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_sendTransaction", "params": []}' ``` + === "Javascript Console" ``` js @@ -8665,6 +10317,7 @@ args SendTxArgs ``` +
Source code

```go @@ -8691,7 +10344,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen // transaction pool. ``` -View on GitHub → +View on GitHub →

@@ -8714,7 +10367,7 @@ https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -8722,7 +10375,7 @@ addr common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -8756,7 +10409,7 @@ data hexutil.Bytes + Required: ✓ Yes - + === "Schema" ``` Schema @@ -8795,7 +10448,7 @@ data hexutil.Bytes + Required: ✓ Yes - + === "Schema" ``` Schema @@ -8825,12 +10478,24 @@ data hexutil.Bytes #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_sign", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_sign", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_sign", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -8838,6 +10503,7 @@ data hexutil.Bytes ``` +
Source code

```go @@ -8863,7 +10529,7 @@ func (s *PublicTransactionPoolAPI) Sign(addr common.Address, data hexutil.Bytes) // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign ``` -View on GitHub → +View on GitHub →

@@ -8880,7 +10546,7 @@ the given from address and it needs to be unlocked. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -8888,13 +10554,41 @@ args SendTxArgs + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + + - chainId: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + - data: - pattern: `^0x([a-fA-F\d])+$` - title: `dataWord` @@ -8947,6 +10641,34 @@ args SendTxArgs { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "chainId": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, "data": { "pattern": "^0x([a-fA-F\\d])+$", "title": "dataWord", @@ -9007,7 +10729,7 @@ args SendTxArgs + Required: ✓ Yes - + === "Schema" ``` Schema @@ -9055,12 +10777,24 @@ args SendTxArgs #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_signTransaction", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_signTransaction", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_signTransaction", "params": []}' ``` + === "Javascript Console" ``` js @@ -9068,6 +10802,7 @@ args SendTxArgs ``` +
Source code

```go @@ -9091,7 +10826,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sen if err != nil { return nil, err } - data, err := rlp.EncodeToBytes(tx) + data, err := tx.MarshalBinary() if err != nil { return nil, err } @@ -9101,7 +10836,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sen // the given from address and it needs to be unlocked. ``` -View on GitHub → +View on GitHub →

@@ -9121,7 +10856,7 @@ between nodes. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -9129,7 +10864,7 @@ rate hexutil.Uint64 + Required: ✓ Yes - + === "Schema" ``` Schema @@ -9163,7 +10898,7 @@ id common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -9207,12 +10942,24 @@ id common.Hash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_submitHashRate", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_submitHashRate", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_submitHashRate", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -9220,6 +10967,7 @@ id common.Hash ``` +
Source code

```go @@ -9243,7 +10991,7 @@ func (api *API) SubmitHashRate(rate hexutil.Uint64, id common.Hash) bool { // between nodes. ``` -View on GitHub → +View on GitHub →

@@ -9260,7 +11008,7 @@ Note either an invalid solution, a stale work a non-existent work will return fa #### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -9268,7 +11016,7 @@ nonce types.BlockNonce + Required: ✓ Yes - + === "Schema" ``` Schema @@ -9302,7 +11050,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -9336,7 +11084,7 @@ digest common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -9380,12 +11128,24 @@ digest common.Hash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_submitWork", "params": [, , ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_submitWork", "params": [, , ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_submitWork", "params": [, , ]}' ``` + === "Javascript Console" ``` js @@ -9393,6 +11153,7 @@ digest common.Hash ``` +
Source code

```go @@ -9413,7 +11174,7 @@ func (api *API) SubmitWork(nonce types.BlockNonce, hash, digest common.Hash) boo // Note either an invalid solution, a stale work a non-existent work will return false. ``` -View on GitHub → +View on GitHub →

@@ -9429,15 +11190,106 @@ Subscriptions are not available over HTTP; they are only available over WS, IPC, #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ -subscriptionName RPCSubscriptionParamsName +subscriptionName RPCEthSubscriptionParamsName + Required: ✓ Yes +=== "Schema" + + ``` Schema + + - oneOf: + + - description: `Fires a notification each time a new header is appended to the chain, including chain reorganizations.` + - enum: newHeads + - type: string + + + - description: `Fires a notification each time a new header is appended to the non-canonical (side) chain, including chain reorganizations.` + - enum: newSideHeads + - type: string + + + - description: `Returns logs that are included in new imported blocks and match the given filter criteria.` + - enum: logs + - type: string + + + - description: `Returns the hash for all transactions that are added to the pending state and are signed with a key that is available in the node.` + - enum: newPendingTransactions + - type: string + + + - description: `Indicates when the node starts or stops synchronizing. The result can either be a boolean indicating that the synchronization has started (true), finished (false) or an object with various progress indicators.` + - enum: syncing + - type: string + + + - title: `subscriptionName` + + + ``` + +=== "Raw" + + ``` Raw + { + "oneOf": [ + { + "description": "Fires a notification each time a new header is appended to the chain, including chain reorganizations.", + "enum": [ + "newHeads" + ], + "type": [ + "string" + ] + }, + { + "description": "Fires a notification each time a new header is appended to the non-canonical (side) chain, including chain reorganizations.", + "enum": [ + "newSideHeads" + ], + "type": [ + "string" + ] + }, + { + "description": "Returns logs that are included in new imported blocks and match the given filter criteria.", + "enum": [ + "logs" + ], + "type": [ + "string" + ] + }, + { + "description": "Returns the hash for all transactions that are added to the pending state and are signed with a key that is available in the node.", + "enum": [ + "newPendingTransactions" + ], + "type": [ + "string" + ] + }, + { + "description": "Indicates when the node starts or stops synchronizing. The result can either be a boolean indicating that the synchronization has started (true), finished (false) or an object with various progress indicators.", + "enum": [ + "syncing" + ], + "type": [ + "string" + ] + } + ], + "title": "subscriptionName" + } + ``` + @@ -9460,33 +11312,156 @@ subscriptionID rpc.ID + Required: ✓ Yes +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + #### Client Method Invocation Examples -=== "Shell" + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_subscribe", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": [, ]}' ``` -=== "Javascript Console" - ``` js - eth.subscribe(subscriptionName,subscriptionOptions); - ```
Source code

```go -func (sub *RPCSubscription) Subscribe(subscriptionName RPCSubscriptionParamsName, subscriptionOptions interface{}) (subscriptionID rpc.ID, err error) { +func (sub *RPCEthSubscription) Subscribe(subscriptionName RPCEthSubscriptionParamsName, subscriptionOptions interface{}) (subscriptionID rpc.ID, err error) { return }// Subscribe creates a subscription to an event channel. // Subscriptions are not available over HTTP; they are only available over WS, IPC, and Process connections. ``` -View on GitHub → +View on GitHub → +

+
+ +--- + + + +### eth_syncing + +Syncing provides information when this nodes starts synchronising with the Ethereum network and when it's finished. + + +#### Params (0) + +_None_ + +#### Result + + + + +*rpc.Subscription + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + + + +#### Client Method Invocation Examples + + + + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["syncing"]}' + ``` + + + + +
Source code +

+```go +func (api *PublicDownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + rpcSub := notifier.CreateSubscription() + go func() { + statuses := make(chan interface{}) + sub := api.SubscribeSyncStatus(statuses) + for { + select { + case status := <-statuses: + notifier.Notify(rpcSub.ID, status) + case <-rpcSub.Err(): + sub.Unsubscribe() + return + case <-notifier.Closed(): + sub.Unsubscribe() + return + } + } + }() + return rpcSub, nil +}// Syncing provides information when this nodes starts synchronising with the Ethereum network and when it's finished. + +``` +View on GitHub →

@@ -9522,12 +11497,24 @@ interface interface{} #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_syncing", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_syncing", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_syncing", "params": []}' ``` + === "Javascript Console" ``` js @@ -9535,6 +11522,7 @@ interface interface{} ``` +
Source code

```go @@ -9553,7 +11541,7 @@ func (s *PublicEthereumAPI) Syncing() (interface{}, error) { [string]interface{}{"startingBlock": hexutil.Uint64(progress.StartingBlock), "currentBlock": hexutil.Uint64(progress.CurrentBlock), "highestBlock": hexutil.Uint64(progress.HighestBlock), "pulledStates": hexutil.Uint64(progress.PulledStates), "knownStates": hexutil.Uint64(progress.KnownStates)}, nil } ``` -View on GitHub → +View on GitHub →

@@ -9565,12 +11553,12 @@ func (s *PublicEthereumAPI) Syncing() (interface{}, error) { UninstallFilter removes the filter with the given filter id. -https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_uninstallfilter +https://eth.wiki/json-rpc/API#eth_uninstallfilter #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -9579,6 +11567,29 @@ id rpc.ID + Required: ✓ Yes +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + @@ -9597,12 +11608,24 @@ id rpc.ID #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_uninstallFilter", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_uninstallFilter", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_uninstallFilter", "params": []}' ``` + === "Javascript Console" ``` js @@ -9610,6 +11633,7 @@ id rpc.ID ``` +
Source code

```go @@ -9626,10 +11650,10 @@ func (api *PublicFilterAPI) UninstallFilter(id rpc.ID) bool { return found }// UninstallFilter removes the filter with the given filter id. // -// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_uninstallfilter +// https://eth.wiki/json-rpc/API#eth_uninstallfilter ``` -View on GitHub → +View on GitHub →

@@ -9644,7 +11668,7 @@ Unsubscribe terminates an existing subscription by ID. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -9653,6 +11677,29 @@ id rpc.ID + Required: ✓ Yes +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + @@ -9663,12 +11710,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "eth_unsubscribe", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "eth_unsubscribe", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "eth_unsubscribe", "params": []}' ``` + === "Javascript Console" ``` js @@ -9676,15 +11735,16 @@ _None_ ``` +
Source code

```go -func (sub *RPCSubscription) Unsubscribe(id rpc.ID) error { +func (sub *RPCEthSubscription) Unsubscribe(id rpc.ID) error { return nil }// Unsubscribe terminates an existing subscription by ID. ``` -View on GitHub → +View on GitHub →

diff --git a/docs/JSON-RPC-API/modules/ethash.md b/docs/JSON-RPC-API/modules/ethash.md index 4bd3371876..2746f36ece 100755 --- a/docs/JSON-RPC-API/modules/ethash.md +++ b/docs/JSON-RPC-API/modules/ethash.md @@ -7,7 +7,7 @@ | Entity | Version | | --- | --- | -| Source | 1.11.22-unstable/generated-at:2021-01-23T04:50:40-06:00 | +| Source | 1.11.23-unstable/generated-at:2021-04-30T19:24:24+03:00 | | OpenRPC | 1.2.6 | --- @@ -33,7 +33,7 @@ _None_ + Required: ✓ Yes - + === "Schema" ``` Schema @@ -63,12 +63,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "ethash_getHashrate", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "ethash_getHashrate", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "ethash_getHashrate", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -76,6 +88,7 @@ _None_ ``` +
Source code

```go @@ -84,7 +97,7 @@ func (api *API) GetHashrate() uint64 { }// GetHashrate returns the current hashrate for local CPU miner and remote miner. ``` -View on GitHub → +View on GitHub →

@@ -115,7 +128,7 @@ num4string [4]string + Required: ✓ Yes - + === "Schema" ``` Schema @@ -155,12 +168,24 @@ num4string [4]string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "ethash_getWork", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "ethash_getWork", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "ethash_getWork", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -168,6 +193,7 @@ num4string [4]string ``` +
Source code

```go @@ -199,7 +225,7 @@ func (api *API) GetWork() ([4]string, error) { // result[3] - hex encoded block number ``` -View on GitHub → +View on GitHub →

@@ -219,7 +245,7 @@ between nodes. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -227,7 +253,7 @@ rate hexutil.Uint64 + Required: ✓ Yes - + === "Schema" ``` Schema @@ -261,7 +287,7 @@ id common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -305,12 +331,24 @@ id common.Hash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "ethash_submitHashRate", "params": [, ]}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "ethash_submitHashRate", "params": [, ]}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "ethash_submitHashRate", "params": [, ]}' + ``` + + === "Javascript Console" ``` js @@ -318,6 +356,7 @@ id common.Hash ``` +
Source code

```go @@ -341,7 +380,7 @@ func (api *API) SubmitHashRate(rate hexutil.Uint64, id common.Hash) bool { // between nodes. ``` -View on GitHub → +View on GitHub →

@@ -358,7 +397,7 @@ Note either an invalid solution, a stale work a non-existent work will return fa #### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -366,7 +405,7 @@ nonce types.BlockNonce + Required: ✓ Yes - + === "Schema" ``` Schema @@ -400,7 +439,7 @@ hash common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -434,7 +473,7 @@ digest common.Hash + Required: ✓ Yes - + === "Schema" ``` Schema @@ -478,12 +517,24 @@ digest common.Hash #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "ethash_submitWork", "params": [, , ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "ethash_submitWork", "params": [, , ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "ethash_submitWork", "params": [, , ]}' ``` + === "Javascript Console" ``` js @@ -491,6 +542,7 @@ digest common.Hash ``` +
Source code

```go @@ -511,7 +563,7 @@ func (api *API) SubmitWork(nonce types.BlockNonce, hash, digest common.Hash) boo // Note either an invalid solution, a stale work a non-existent work will return false. ``` -View on GitHub → +View on GitHub →

diff --git a/docs/JSON-RPC-API/modules/miner.md b/docs/JSON-RPC-API/modules/miner.md index dfcc3537a9..17aff8b356 100755 --- a/docs/JSON-RPC-API/modules/miner.md +++ b/docs/JSON-RPC-API/modules/miner.md @@ -7,7 +7,7 @@ | Entity | Version | | --- | --- | -| Source | 1.11.22-unstable/generated-at:2021-01-23T04:50:40-06:00 | +| Source | 1.11.23-unstable/generated-at:2021-04-30T19:24:24+03:00 | | OpenRPC | 1.2.6 | --- @@ -33,7 +33,7 @@ _None_ + Required: ✓ Yes - + === "Schema" ``` Schema @@ -63,12 +63,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "miner_getHashrate", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "miner_getHashrate", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "miner_getHashrate", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -76,6 +88,7 @@ _None_ ``` +
Source code

```go @@ -84,7 +97,7 @@ func (api *PrivateMinerAPI) GetHashrate() uint64 { }// GetHashrate returns the current hashrate of the miner. ``` -View on GitHub → +View on GitHub →

@@ -99,7 +112,7 @@ SetEtherbase sets the etherbase of the miner #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -107,7 +120,7 @@ etherbase common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -151,12 +164,24 @@ etherbase common.Address #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "miner_setEtherbase", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "miner_setEtherbase", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "miner_setEtherbase", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -164,6 +189,7 @@ etherbase common.Address ``` +
Source code

```go @@ -173,7 +199,7 @@ func (api *PrivateMinerAPI) SetEtherbase(etherbase common.Address) bool { }// SetEtherbase sets the etherbase of the miner ``` -View on GitHub → +View on GitHub →

@@ -188,7 +214,7 @@ SetExtra sets the extra data string that is included when this miner mines a blo #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -215,12 +241,24 @@ extra string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "miner_setExtra", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "miner_setExtra", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "miner_setExtra", "params": []}' ``` + === "Javascript Console" ``` js @@ -228,6 +266,7 @@ extra string ``` +
Source code

```go @@ -239,7 +278,7 @@ func (api *PrivateMinerAPI) SetExtra(extra string) (bool, error) { return true, nil } ``` -View on GitHub → +View on GitHub →

@@ -254,7 +293,7 @@ SetGasPrice sets the minimum accepted gas price for the miner. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -262,7 +301,7 @@ gasPrice hexutil.Big + Required: ✓ Yes - + === "Schema" ``` Schema @@ -306,12 +345,24 @@ gasPrice hexutil.Big #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "miner_setGasPrice", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "miner_setGasPrice", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "miner_setGasPrice", "params": []}' ``` + === "Javascript Console" ``` js @@ -319,6 +370,7 @@ gasPrice hexutil.Big ``` +
Source code

```go @@ -331,7 +383,7 @@ func (api *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool { }// SetGasPrice sets the minimum accepted gas price for the miner. ``` -View on GitHub → +View on GitHub →

@@ -346,7 +398,7 @@ SetRecommitInterval updates the interval for miner sealing work recommitting. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -354,7 +406,7 @@ interval int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -390,12 +442,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "miner_setRecommitInterval", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "miner_setRecommitInterval", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "miner_setRecommitInterval", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -403,6 +467,7 @@ _None_ ``` +
Source code

```go @@ -411,7 +476,7 @@ func (api *PrivateMinerAPI) SetRecommitInterval(interval int) { }// SetRecommitInterval updates the interval for miner sealing work recommitting. ``` -View on GitHub → +View on GitHub →

@@ -430,7 +495,7 @@ transaction pool. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -438,7 +503,7 @@ threads *int + Required: ✓ Yes - + === "Schema" ``` Schema @@ -474,12 +539,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "miner_start", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "miner_start", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "miner_start", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -487,6 +564,7 @@ _None_ ``` +
Source code

```go @@ -502,7 +580,7 @@ func (api *PrivateMinerAPI) Start(threads *int) error { // transaction pool. ``` -View on GitHub → +View on GitHub →

@@ -526,12 +604,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "miner_stop", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "miner_stop", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "miner_stop", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -539,6 +629,7 @@ _None_ ``` +
Source code

```go @@ -548,7 +639,7 @@ func (api *PrivateMinerAPI) Stop() { // the block creation level. ``` -View on GitHub → +View on GitHub →

diff --git a/docs/JSON-RPC-API/modules/net.md b/docs/JSON-RPC-API/modules/net.md index f9c0501601..377514341d 100755 --- a/docs/JSON-RPC-API/modules/net.md +++ b/docs/JSON-RPC-API/modules/net.md @@ -7,7 +7,7 @@ | Entity | Version | | --- | --- | -| Source | 1.11.22-unstable/generated-at:2021-01-23T04:50:40-06:00 | +| Source | 1.11.23-unstable/generated-at:2021-04-30T19:24:24+03:00 | | OpenRPC | 1.2.6 | --- @@ -38,12 +38,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "net_listening", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "net_listening", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "net_listening", "params": []}' ``` + === "Javascript Console" ``` js @@ -51,6 +63,7 @@ _None_ ``` +
Source code

```go @@ -59,7 +72,7 @@ func (s *PublicNetAPI) Listening() bool { }// Listening returns an indication if the node is listening for network connections. ``` -View on GitHub → +View on GitHub →

@@ -85,7 +98,7 @@ _None_ + Required: ✓ Yes - + === "Schema" ``` Schema @@ -115,12 +128,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "net_peerCount", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "net_peerCount", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "net_peerCount", "params": []}' ``` + === "Javascript Console" ``` js @@ -128,6 +153,7 @@ _None_ ``` +
Source code

```go @@ -136,7 +162,7 @@ func (s *PublicNetAPI) PeerCount() hexutil.Uint { }// PeerCount returns the number of connected peers ``` -View on GitHub → +View on GitHub →

@@ -167,12 +193,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "net_version", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "net_version", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "net_version", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -180,6 +218,7 @@ _None_ ``` +
Source code

```go @@ -188,7 +227,7 @@ func (s *PublicNetAPI) Version() string { }// Version returns the current ethereum protocol version. ``` -View on GitHub → +View on GitHub →

diff --git a/docs/JSON-RPC-API/modules/personal.md b/docs/JSON-RPC-API/modules/personal.md index dd2dc538a3..b5f4fb1a07 100755 --- a/docs/JSON-RPC-API/modules/personal.md +++ b/docs/JSON-RPC-API/modules/personal.md @@ -7,7 +7,7 @@ | Entity | Version | | --- | --- | -| Source | 1.11.22-unstable/generated-at:2021-01-23T04:50:40-06:00 | +| Source | 1.11.23-unstable/generated-at:2021-04-30T19:24:24+03:00 | | OpenRPC | 1.2.6 | --- @@ -23,7 +23,7 @@ it for later reuse. #### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -63,7 +63,7 @@ pin *bool + Required: ✓ Yes - + === "Schema" ``` Schema @@ -127,12 +127,24 @@ pin *bool #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_deriveAccount", "params": [, , ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_deriveAccount", "params": [, , ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_deriveAccount", "params": [, , ]}' ``` + === "Javascript Console" ``` js @@ -140,6 +152,7 @@ pin *bool ``` +
Source code

```go @@ -160,7 +173,7 @@ func (s *PrivateAccountAPI) DeriveAccount(url string, path string, pin *bool) (a // it for later reuse. ``` -View on GitHub → +View on GitHub →

@@ -184,7 +197,7 @@ https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -192,7 +205,7 @@ data hexutil.Bytes + Required: ✓ Yes - + === "Schema" ``` Schema @@ -226,7 +239,7 @@ sig hexutil.Bytes + Required: ✓ Yes - + === "Schema" ``` Schema @@ -265,7 +278,7 @@ sig hexutil.Bytes + Required: ✓ Yes - + === "Schema" ``` Schema @@ -295,12 +308,24 @@ sig hexutil.Bytes #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_ecRecover", "params": [, ]}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_ecRecover", "params": [, ]}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_ecRecover", "params": [, ]}' + ``` + + === "Javascript Console" ``` js @@ -308,6 +333,7 @@ sig hexutil.Bytes ``` +
Source code

```go @@ -336,7 +362,7 @@ func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Byt // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover ``` -View on GitHub → +View on GitHub →

@@ -352,7 +378,7 @@ encrypting it with the passphrase. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -383,7 +409,7 @@ password string + Required: ✓ Yes - + === "Schema" ``` Schema @@ -413,12 +439,24 @@ password string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_importRawKey", "params": [, ]}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_importRawKey", "params": [, ]}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_importRawKey", "params": [, ]}' + ``` + + === "Javascript Console" ``` js @@ -426,6 +464,7 @@ password string ``` +
Source code

```go @@ -444,7 +483,7 @@ func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (commo // encrypting it with the passphrase. ``` -View on GitHub → +View on GitHub →

@@ -459,7 +498,7 @@ InitializeWallet initializes a new wallet at the provided URL, by generating and #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -486,12 +525,24 @@ url string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_initializeWallet", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_initializeWallet", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_initializeWallet", "params": []}' ``` + === "Javascript Console" ``` js @@ -499,6 +550,7 @@ url string ``` +
Source code

```go @@ -525,7 +577,7 @@ func (s *PrivateAccountAPI) InitializeWallet(ctx context.Context, url string) (s } } ``` -View on GitHub → +View on GitHub →

@@ -550,7 +602,7 @@ commonAddress []common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -592,12 +644,24 @@ commonAddress []common.Address #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_listAccounts", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_listAccounts", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_listAccounts", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -605,6 +669,7 @@ commonAddress []common.Address ``` +
Source code

```go @@ -613,7 +678,7 @@ func (s *PrivateAccountAPI) ListAccounts() [ // listAccounts will return a list return s.am.Accounts() } ``` -View on GitHub → +View on GitHub →

@@ -638,7 +703,7 @@ rawWallet []rawWallet + Required: ✓ Yes - + === "Schema" ``` Schema @@ -750,12 +815,24 @@ rawWallet []rawWallet #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_listWallets", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_listWallets", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_listWallets", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -763,6 +840,7 @@ rawWallet []rawWallet ``` +
Source code

```go @@ -780,7 +858,7 @@ func (s *PrivateAccountAPI) ListWallets() [ // ListWallets will return a list of return wallets } ``` -View on GitHub → +View on GitHub →

@@ -795,7 +873,7 @@ LockAccount will lock the account associated with the given address when it's un #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -803,7 +881,7 @@ addr common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -847,12 +925,24 @@ addr common.Address #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_lockAccount", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_lockAccount", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_lockAccount", "params": []}' ``` + === "Javascript Console" ``` js @@ -860,6 +950,7 @@ addr common.Address ``` +
Source code

```go @@ -871,7 +962,7 @@ func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool { }// LockAccount will lock the account associated with the given address when it's unlocked. ``` -View on GitHub → +View on GitHub →

@@ -886,7 +977,7 @@ NewAccount will create a new account and returns the address for the new account #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -908,7 +999,7 @@ password string + Required: ✓ Yes - + === "Schema" ``` Schema @@ -938,12 +1029,24 @@ password string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_newAccount", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_newAccount", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_newAccount", "params": []}' ``` + === "Javascript Console" ``` js @@ -951,6 +1054,7 @@ password string ``` +
Source code

```go @@ -970,7 +1074,7 @@ func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) }// NewAccount will create a new account and returns the address for the new account. ``` -View on GitHub → +View on GitHub →

@@ -988,7 +1092,7 @@ Trezor PIN matrix challenge). #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1016,12 +1120,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_openWallet", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_openWallet", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_openWallet", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -1029,6 +1145,7 @@ _None_ ``` +
Source code

```go @@ -1048,7 +1165,7 @@ func (s *PrivateAccountAPI) OpenWallet(url string, passphrase *string) error { // Trezor PIN matrix challenge). ``` -View on GitHub → +View on GitHub →

@@ -1059,13 +1176,13 @@ func (s *PrivateAccountAPI) OpenWallet(url string, passphrase *string) error { ### personal_sendTransaction SendTransaction will create a transaction from the given arguments and -tries to sign it with the key associated with args.To. If the given passwd isn't +tries to sign it with the key associated with args.From. If the given passwd isn't able to decrypt the key it fails. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1073,13 +1190,41 @@ args SendTxArgs + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + + - chainId: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + - data: - pattern: `^0x([a-fA-F\d])+$` - title: `dataWord` @@ -1132,6 +1277,34 @@ args SendTxArgs { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "chainId": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, "data": { "pattern": "^0x([a-fA-F\\d])+$", "title": "dataWord", @@ -1201,7 +1374,7 @@ passwd string + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1231,12 +1404,24 @@ passwd string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_sendTransaction", "params": [, ]}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_sendTransaction", "params": [, ]}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_sendTransaction", "params": [, ]}' + ``` + + === "Javascript Console" ``` js @@ -1244,6 +1429,7 @@ passwd string ``` +
Source code

```go @@ -1259,11 +1445,11 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs } return SubmitTransaction(ctx, s.b, signed) }// SendTransaction will create a transaction from the given arguments and -// tries to sign it with the key associated with args.To. If the given passwd isn't +// tries to sign it with the key associated with args.From. If the given passwd isn't // able to decrypt the key it fails. ``` -View on GitHub → +View on GitHub →

@@ -1286,7 +1472,7 @@ https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign #### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1294,7 +1480,7 @@ data hexutil.Bytes + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1328,7 +1514,7 @@ addr common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1376,7 +1562,7 @@ passwd string + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1406,12 +1592,24 @@ passwd string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_sign", "params": [, , ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_sign", "params": [, , ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_sign", "params": [, , ]}' ``` + === "Javascript Console" ``` js @@ -1419,6 +1617,7 @@ passwd string ``` +
Source code

```go @@ -1446,7 +1645,7 @@ func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr c // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign ``` -View on GitHub → +View on GitHub →

@@ -1462,7 +1661,7 @@ and will be removed in the future. It primary goal is to give clients time to up #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1470,13 +1669,41 @@ args SendTxArgs + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + + - chainId: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + - data: - pattern: `^0x([a-fA-F\d])+$` - title: `dataWord` @@ -1529,6 +1756,34 @@ args SendTxArgs { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "chainId": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, "data": { "pattern": "^0x([a-fA-F\\d])+$", "title": "dataWord", @@ -1598,7 +1853,7 @@ passwd string + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1628,12 +1883,24 @@ passwd string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_signAndSendTransaction", "params": [, ]}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_signAndSendTransaction", "params": [, ]}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_signAndSendTransaction", "params": [, ]}' + ``` + + === "Javascript Console" ``` js @@ -1641,6 +1908,7 @@ passwd string ``` +
Source code

```go @@ -1650,7 +1918,7 @@ func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args Sen // and will be removed in the future. It primary goal is to give clients time to update. ``` -View on GitHub → +View on GitHub →

@@ -1661,14 +1929,14 @@ func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args Sen ### personal_signTransaction SignTransaction will create a transaction from the given arguments and -tries to sign it with the key associated with args.To. If the given passwd isn't +tries to sign it with the key associated with args.From. If the given passwd isn't able to decrypt the key it fails. The transaction is returned in RLP-form, not broadcast to other nodes #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1676,13 +1944,41 @@ args SendTxArgs + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + + - chainId: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + - data: - pattern: `^0x([a-fA-F\d])+$` - title: `dataWord` @@ -1735,6 +2031,34 @@ args SendTxArgs { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "chainId": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, "data": { "pattern": "^0x([a-fA-F\\d])+$", "title": "dataWord", @@ -1804,7 +2128,7 @@ passwd string + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1852,12 +2176,24 @@ passwd string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_signTransaction", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_signTransaction", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_signTransaction", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -1865,6 +2201,7 @@ passwd string ``` +
Source code

```go @@ -1886,18 +2223,18 @@ func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args SendTxArgs log.Warn("Failed transaction sign attempt", "from", args.From, "to", args.To, "value", args.Value.ToInt(), "err", err) return nil, err } - data, err := rlp.EncodeToBytes(signed) + data, err := signed.MarshalBinary() if err != nil { return nil, err } return &SignTransactionResult{data, signed}, nil }// SignTransaction will create a transaction from the given arguments and -// tries to sign it with the key associated with args.To. If the given passwd isn't +// tries to sign it with the key associated with args.From. If the given passwd isn't // able to decrypt the key it fails. The transaction is returned in RLP-form, not broadcast // to other nodes ``` -View on GitHub → +View on GitHub →

@@ -1914,7 +2251,7 @@ default of 300 seconds. It returns an indication if the account was unlocked. #### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -1922,7 +2259,7 @@ addr common.Address + Required: ✓ Yes - + === "Schema" ``` Schema @@ -1965,7 +2302,7 @@ duration *uint64 + Required: ✓ Yes - + === "Schema" ``` Schema @@ -2009,12 +2346,24 @@ duration *uint64 #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_unlockAccount", "params": [, , ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_unlockAccount", "params": [, , ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_unlockAccount", "params": [, , ]}' ``` + === "Javascript Console" ``` js @@ -2022,6 +2371,7 @@ duration *uint64 ``` +
Source code

```go @@ -2052,7 +2402,7 @@ func (s *PrivateAccountAPI) UnlockAccount(ctx context.Context, addr common.Addre // default of 300 seconds. It returns an indication if the account was unlocked. ``` -View on GitHub → +View on GitHub →

@@ -2067,7 +2417,7 @@ Unpair deletes a pairing between wallet and geth. #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -2095,12 +2445,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "personal_unpair", "params": [, ]}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "personal_unpair", "params": [, ]}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "personal_unpair", "params": [, ]}' + ``` + + === "Javascript Console" ``` js @@ -2108,6 +2470,7 @@ _None_ ``` +
Source code

```go @@ -2125,7 +2488,7 @@ func (s *PrivateAccountAPI) Unpair(ctx context.Context, url string, pin string) } } ``` -View on GitHub → +View on GitHub →

diff --git a/docs/JSON-RPC-API/modules/trace.md b/docs/JSON-RPC-API/modules/trace.md index c182c1c806..f7294ad52f 100755 --- a/docs/JSON-RPC-API/modules/trace.md +++ b/docs/JSON-RPC-API/modules/trace.md @@ -7,7 +7,7 @@ | Entity | Version | | --- | --- | -| Source | 1.11.22-unstable/generated-at:2021-01-23T04:50:40-06:00 | +| Source | 1.11.23-unstable/generated-at:2021-04-30T19:24:24+03:00 | | OpenRPC | 1.2.6 | --- @@ -24,7 +24,7 @@ The correct name will be TraceBlockByNumber, though we want to be compatible wit #### Params (2) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -32,7 +32,7 @@ number rpc.BlockNumber + Required: ✓ Yes - + === "Schema" ``` Schema @@ -94,7 +94,7 @@ config *TraceConfig + Required: ✓ Yes - + === "Schema" ``` Schema @@ -121,6 +121,9 @@ config *TraceConfig - title: `integer` - type: `string` + - NestedTraceOutput: + - type: `boolean` + - Reexec: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -167,6 +170,9 @@ config *TraceConfig "title": "integer", "type": "string" }, + "NestedTraceOutput": { + "type": "boolean" + }, "Reexec": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -200,7 +206,7 @@ interface []interface{} + Required: ✓ Yes - + === "Schema" ``` Schema @@ -234,12 +240,24 @@ interface []interface{} #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "trace_block", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "trace_block", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "trace_block", "params": [, ]}' ``` + === "Javascript Console" ``` js @@ -247,40 +265,32 @@ interface []interface{} ``` +
Source code

```go -func (api *PrivateTraceAPI) Block(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([ // Block returns the structured logs created during the execution of +func (api *TraceAPI) Block(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([ // Block returns the structured logs created during the execution of // EVM and returns them as a JSON object. // The correct name will be TraceBlockByNumber, though we want to be compatible with Parity trace module. ]interface{}, error) { - var block *types.Block - switch number { - case rpc.PendingBlockNumber: - block = api.eth.miner.PendingBlock() - case rpc.LatestBlockNumber: - block = api.eth.blockchain.CurrentBlock() - default: - block = api.eth.blockchain.GetBlockByNumber(uint64(number)) - } - if block == nil { - return nil, fmt.Errorf("block #%d not found", number) + config = setTraceConfigDefaultTracer(config) + block, err := api.debugAPI.blockByNumber(ctx, number) + if err != nil { + return nil, err } - config = setConfigTracerToParity(config) - traceResults, err := traceBlockByNumber(ctx, api.eth, number, config) + traceResults, err := api.debugAPI.traceBlock(ctx, block, config) if err != nil { return nil, err } - traceReward, err := traceBlockReward(ctx, api.eth, block, config) + traceReward, err := api.traceBlockReward(ctx, block, config) if err != nil { return nil, err } - traceUncleRewards, err := traceBlockUncleRewards(ctx, api.eth, block, config) + traceUncleRewards, err := api.traceBlockUncleRewards(ctx, block, config) if err != nil { return nil, err } - results := [ // Fetch the block that we want to trace - ]interface{}{} + results := []interface{}{} for _, result := range traceResults { var tmp []interface{} if err := json.Unmarshal(result.Result.(json.RawMessage), &tmp); err != nil { @@ -295,7 +305,7 @@ func (api *PrivateTraceAPI) Block(ctx context.Context, number rpc.BlockNumber, c return results, nil } ``` -View on GitHub → +View on GitHub →

@@ -303,13 +313,16 @@ func (api *PrivateTraceAPI) Block(ctx context.Context, number rpc.BlockNumber, c -### trace_filter +### trace_call +Call lets you trace a given eth_call. It collects the structured logs created during the execution of EVM +if the given transaction was added on top of the provided block and returns them as a JSON object. +You can provide -2 as a block number to trace on top of the pending block. -#### Params (2) +#### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -317,13 +330,36 @@ args ethapi.CallArgs + Required: ✓ Yes - + === "Schema" ``` Schema - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + - data: - pattern: `^0x([a-fA-F\d])+$` - title: `dataWord` @@ -366,6 +402,29 @@ args ethapi.CallArgs { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, "data": { "pattern": "^0x([a-fA-F\\d])+$", "title": "dataWord", @@ -407,11 +466,20 @@ args ethapi.CallArgs __2:__ +blockNrOrHash rpc.BlockNumberOrHash + + + Required: ✓ Yes + + + + + +__3:__ config *TraceConfig + Required: ✓ Yes - + === "Schema" ``` Schema @@ -438,6 +506,9 @@ config *TraceConfig - title: `integer` - type: `string` + - NestedTraceOutput: + - type: `boolean` + - Reexec: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -484,6 +555,9 @@ config *TraceConfig "title": "integer", "type": "string" }, + "NestedTraceOutput": { + "type": "boolean" + }, "Reexec": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -513,85 +587,57 @@ config *TraceConfig -txTraceResult []*txTraceResult +interface interface{} + Required: ✓ Yes - -=== "Schema" - - ``` Schema - - - items: - - - additionalProperties: `false` - - properties: - - error: - - type: `string` - - - result: - - additionalProperties: `true` - - type: object +#### Client Method Invocation Examples - - type: array +=== "Shell HTTP" + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "trace_call", "params": [, , ]}' ``` -=== "Raw" - - ``` Raw - { - "items": [ - { - "additionalProperties": false, - "properties": { - "error": { - "type": "string" - }, - "result": { - "additionalProperties": true - } - }, - "type": [ - "object" - ] - } - ], - "type": [ - "array" - ] - } - ``` -#### Client Method Invocation Examples -=== "Shell" +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "trace_filter", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "trace_call", "params": [, , ]}' ``` + === "Javascript Console" ``` js - trace.filter(args,config); + trace.call(args,blockNrOrHash,config); ``` +
Source code

```go -func (api *PrivateTraceAPI) Filter(ctx context.Context, args ethapi.CallArgs, config *TraceConfig) ([]*txTraceResult, error) { - return nil, nil -} +func (api *TraceAPI) Call(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { + config = setTraceConfigDefaultTracer(config) + res, err := api.debugAPI.TraceCall(ctx, args, blockNrOrHash, config) + if err != nil { + return nil, err + } + return decorateResponse(res, config) +}// Call lets you trace a given eth_call. It collects the structured logs created during the execution of EVM +// if the given transaction was added on top of the provided block and returns them as a JSON object. +// You can provide -2 as a block number to trace on top of the pending block. + ``` -View on GitHub → +View on GitHub →

@@ -599,31 +645,90 @@ func (api *PrivateTraceAPI) Filter(ctx context.Context, args ethapi.CallArgs, co -### trace_transaction +### trace_callMany -Transaction returns the structured logs created during the execution of EVM -and returns them as a JSON object. +CallMany lets you trace a given eth_call. It collects the structured logs created during the execution of EVM +if the given transaction was added on top of the provided block and returns them as a JSON object. +You can provide -2 as a block number to trace on top of the pending block. -#### Params (2) +#### Params (3) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ -hash common.Hash +txs []ethapi.CallArgs + Required: ✓ Yes - + === "Schema" ``` Schema - - description: `Hex representation of a Keccak 256 hash` - - pattern: `^0x[a-fA-F\d]{64}$` - - title: `keccak` - - type: string + - items: + + - additionalProperties: `false` + - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + + - data: + - pattern: `^0x([a-fA-F\d])+$` + - title: `dataWord` + - type: `string` + + - from: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - gas: + - pattern: `^0x([a-fA-F\d])+$` + - title: `uint64` + - type: `string` + + - gasPrice: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + - to: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - value: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + + - type: object + + + - type: array ``` @@ -632,11 +737,71 @@ hash common.Hash ``` Raw { - "description": "Hex representation of a Keccak 256 hash", - "pattern": "^0x[a-fA-F\\d]{64}$", - "title": "keccak", + "items": [ + { + "additionalProperties": false, + "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "data": { + "pattern": "^0x([a-fA-F\\d])+$", + "title": "dataWord", + "type": "string" + }, + "from": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "gas": { + "pattern": "^0x([a-fA-F\\d])+$", + "title": "uint64", + "type": "string" + }, + "gasPrice": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "to": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "value": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + } + }, + "type": [ + "object" + ] + } + ], "type": [ - "string" + "array" ] } ``` @@ -645,11 +810,20 @@ hash common.Hash __2:__ +blockNrOrHash rpc.BlockNumberOrHash + + + Required: ✓ Yes + + + + + +__3:__ config *TraceConfig + Required: ✓ Yes - + === "Schema" ``` Schema @@ -676,6 +850,9 @@ config *TraceConfig - title: `integer` - type: `string` + - NestedTraceOutput: + - type: `boolean` + - Reexec: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -722,6 +899,9 @@ config *TraceConfig "title": "integer", "type": "string" }, + "NestedTraceOutput": { + "type": "boolean" + }, "Reexec": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -760,30 +940,767 @@ interface interface{} #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "trace_callMany", "params": [, , ]}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "trace_transaction", "params": [, ]}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "trace_callMany", "params": [, , ]}' ``` + === "Javascript Console" ``` js - trace.transaction(hash,config); + trace.callMany(txs,blockNrOrHash,config); ``` +
Source code

```go -func (api *PrivateTraceAPI) Transaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { - config = setConfigTracerToParity(config) - return traceTransaction(ctx, api.eth, hash, config) -}// Transaction returns the structured logs created during the execution of EVM -// and returns them as a JSON object. +func (api *TraceAPI) CallMany(ctx context.Context, txs [ // CallMany lets you trace a given eth_call. It collects the structured logs created during the execution of EVM +// if the given transaction was added on top of the provided block and returns them as a JSON object. +// You can provide -2 as a block number to trace on top of the pending block. +]ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { + config = setTraceConfigDefaultTracer(config) + return api.debugAPI.TraceCallMany(ctx, txs, blockNrOrHash, config) +} +``` +View on GitHub → +

+
+ +--- + + + +### trace_filter + +Filter configures a new tracer according to the provided configuration, and +executes all the transactions contained within. The return value will be one item +per transaction, dependent on the requested tracer. + + +#### Params (2) + +Parameters must be given _by position_. + + +__1:__ +args TraceFilterArgs + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - additionalProperties: `false` + - properties: + - after: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + - count: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + - fromAddress: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - fromBlock: + - pattern: `^0x([a-fA-F\d])+$` + - title: `uint64` + - type: `string` + + - toAddress: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - toBlock: + - pattern: `^0x([a-fA-F\d])+$` + - title: `uint64` + - type: `string` + + + - type: object + + + ``` + +=== "Raw" + + ``` Raw + { + "additionalProperties": false, + "properties": { + "after": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "count": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "fromAddress": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "fromBlock": { + "pattern": "^0x([a-fA-F\\d])+$", + "title": "uint64", + "type": "string" + }, + "toAddress": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "toBlock": { + "pattern": "^0x([a-fA-F\\d])+$", + "title": "uint64", + "type": "string" + } + }, + "type": [ + "object" + ] + } + ``` + + + + +__2:__ +config *TraceConfig + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - additionalProperties: `false` + - properties: + - Debug: + - type: `boolean` + + - DisableMemory: + - type: `boolean` + + - DisableReturnData: + - type: `boolean` + + - DisableStack: + - type: `boolean` + + - DisableStorage: + - type: `boolean` + + - Limit: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + - NestedTraceOutput: + - type: `boolean` + + - Reexec: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + - Timeout: + - type: `string` + + - Tracer: + - type: `string` + + - overrides: + - additionalProperties: `true` + + + - type: object + + + ``` + +=== "Raw" + + ``` Raw + { + "additionalProperties": false, + "properties": { + "Debug": { + "type": "boolean" + }, + "DisableMemory": { + "type": "boolean" + }, + "DisableReturnData": { + "type": "boolean" + }, + "DisableStack": { + "type": "boolean" + }, + "DisableStorage": { + "type": "boolean" + }, + "Limit": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "NestedTraceOutput": { + "type": "boolean" + }, + "Reexec": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "Timeout": { + "type": "string" + }, + "Tracer": { + "type": "string" + }, + "overrides": { + "additionalProperties": true + } + }, + "type": [ + "object" + ] + } + ``` + + + + + +#### Result + + + + +*rpc.Subscription + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + + + +#### Client Method Invocation Examples + + + + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "trace_subscribe", "params": ["filter", , ]}' + ``` + + + + +
Source code +

+```go +func (api *TraceAPI) Filter(ctx context.Context, args TraceFilterArgs, config *TraceConfig) (*rpc.Subscription, error) { + config = setTraceConfigDefaultTracer(config) + start := rpc.BlockNumber(args.FromBlock) + end := rpc.BlockNumber(args.ToBlock) + return api.debugAPI.TraceChain(ctx, start, end, config) +}// Filter configures a new tracer according to the provided configuration, and +// executes all the transactions contained within. The return value will be one item +// per transaction, dependent on the requested tracer. + +``` +View on GitHub → +

+
+ +--- + + + +### trace_subscribe + +Subscribe creates a subscription to an event channel. +Subscriptions are not available over HTTP; they are only available over WS, IPC, and Process connections. + + +#### Params (2) + +Parameters must be given _by position_. + + +__1:__ +subscriptionName RPCTraceSubscriptionParamsName + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - oneOf: + + - description: `Returns transaction traces for the filtered addresses within a range of blocks.` + - enum: filter + - type: string + + + - title: `subscriptionName` + + + ``` + +=== "Raw" + + ``` Raw + { + "oneOf": [ + { + "description": "Returns transaction traces for the filtered addresses within a range of blocks.", + "enum": [ + "filter" + ], + "type": [ + "string" + ] + } + ], + "title": "subscriptionName" + } + ``` + + + + +__2:__ +subscriptionOptions interface{} + + + Required: No + + + + + + +#### Result + + + +subscriptionID rpc.ID + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + + + +#### Client Method Invocation Examples + + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "trace_subscribe", "params": [, ]}' + ``` + + + + +
Source code +

+```go +func (sub *RPCTraceSubscription) Subscribe(subscriptionName RPCTraceSubscriptionParamsName, subscriptionOptions interface{}) (subscriptionID rpc.ID, err error) { + return +}// Subscribe creates a subscription to an event channel. +// Subscriptions are not available over HTTP; they are only available over WS, IPC, and Process connections. + +``` +View on GitHub → +

+
+ +--- + + + +### trace_transaction + +Transaction returns the structured logs created during the execution of EVM +and returns them as a JSON object. + + +#### Params (2) + +Parameters must be given _by position_. + + +__1:__ +hash common.Hash + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": [ + "string" + ] + } + ``` + + + + +__2:__ +config *TraceConfig + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - additionalProperties: `false` + - properties: + - Debug: + - type: `boolean` + + - DisableMemory: + - type: `boolean` + + - DisableReturnData: + - type: `boolean` + + - DisableStack: + - type: `boolean` + + - DisableStorage: + - type: `boolean` + + - Limit: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + - NestedTraceOutput: + - type: `boolean` + + - Reexec: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + + - Timeout: + - type: `string` + + - Tracer: + - type: `string` + + - overrides: + - additionalProperties: `true` + + + - type: object + + + ``` + +=== "Raw" + + ``` Raw + { + "additionalProperties": false, + "properties": { + "Debug": { + "type": "boolean" + }, + "DisableMemory": { + "type": "boolean" + }, + "DisableReturnData": { + "type": "boolean" + }, + "DisableStack": { + "type": "boolean" + }, + "DisableStorage": { + "type": "boolean" + }, + "Limit": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "NestedTraceOutput": { + "type": "boolean" + }, + "Reexec": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, + "Timeout": { + "type": "string" + }, + "Tracer": { + "type": "string" + }, + "overrides": { + "additionalProperties": true + } + }, + "type": [ + "object" + ] + } + ``` + + + + + +#### Result + + + +interface interface{} + + + Required: ✓ Yes + + + + +#### Client Method Invocation Examples + + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "trace_transaction", "params": [, ]}' + ``` + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "trace_transaction", "params": [, ]}' + ``` + + +=== "Javascript Console" + + ``` js + trace.transaction(hash,config); + ``` + + + +
Source code +

+```go +func (api *TraceAPI) Transaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { + config = setTraceConfigDefaultTracer(config) + return api.debugAPI.TraceTransaction(ctx, hash, config) +}// Transaction returns the structured logs created during the execution of EVM +// and returns them as a JSON object. + +``` +View on GitHub → +

+
+ +--- + + + +### trace_unsubscribe + +Unsubscribe terminates an existing subscription by ID. + + +#### Params (1) + +Parameters must be given _by position_. + + +__1:__ +id rpc.ID + + + Required: ✓ Yes + + +=== "Schema" + + ``` Schema + + - description: `Subscription identifier` + - title: `subscriptionID` + - type: string + + + ``` + +=== "Raw" + + ``` Raw + { + "description": "Subscription identifier", + "title": "subscriptionID", + "type": [ + "string" + ] + } + ``` + + + + + +#### Result + +_None_ + +#### Client Method Invocation Examples + + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "trace_unsubscribe", "params": []}' + ``` + + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "trace_unsubscribe", "params": []}' + ``` + + +=== "Javascript Console" + + ``` js + trace.unsubscribe(id); + ``` + + + +
Source code +

+```go +func (sub *RPCTraceSubscription) Unsubscribe(id rpc.ID) error { + return nil +}// Unsubscribe terminates an existing subscription by ID. ``` -View on GitHub → +View on GitHub →

diff --git a/docs/JSON-RPC-API/modules/txpool.md b/docs/JSON-RPC-API/modules/txpool.md index 8cd8063507..be8e36a6eb 100755 --- a/docs/JSON-RPC-API/modules/txpool.md +++ b/docs/JSON-RPC-API/modules/txpool.md @@ -7,7 +7,7 @@ | Entity | Version | | --- | --- | -| Source | 1.11.22-unstable/generated-at:2021-01-23T04:50:40-06:00 | +| Source | 1.11.23-unstable/generated-at:2021-04-30T19:24:24+03:00 | | OpenRPC | 1.2.6 | --- @@ -32,7 +32,7 @@ mapstringmapstringmapstringRPCTransaction map[string]map[string]map[string + Required: ✓ Yes - + === "Schema" ``` Schema @@ -45,6 +45,29 @@ mapstringmapstringmapstringRPCTransaction map[string]map[string]map[string - .*: - additionalProperties: `false` - properties: + - accessList: + - items: + - additionalProperties: `false` + - properties: + - address: + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - storageKeys: + - items: + - description: `Hex representation of a Keccak 256 hash` + - pattern: `^0x[a-fA-F\d]{64}$` + - title: `keccak` + - type: `string` + + - type: `array` + + + - type: `object` + + - type: `array` + - blockHash: - pattern: `^0x[a-fA-F\d]{64}$` - title: `keccak` @@ -55,6 +78,11 @@ mapstringmapstringmapstringRPCTransaction map[string]map[string]map[string - title: `integer` - type: `string` + - chainId: + - pattern: `^0x[a-fA-F0-9]+$` + - title: `integer` + - type: `string` + - from: - pattern: `^0x[a-fA-F\d]{64}$` - title: `keccak` @@ -105,6 +133,11 @@ mapstringmapstringmapstringRPCTransaction map[string]map[string]map[string - title: `uint64` - type: `string` + - type: + - pattern: `^0x([a-fA-F\d])+$` + - title: `uint64` + - type: `string` + - v: - pattern: `^0x[a-fA-F0-9]+$` - title: `integer` @@ -142,6 +175,29 @@ mapstringmapstringmapstringRPCTransaction map[string]map[string]map[string ".*": { "additionalProperties": false, "properties": { + "accessList": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "storageKeys": { + "items": { + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$", + "title": "keccak", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, "blockHash": { "pattern": "^0x[a-fA-F\\d]{64}$", "title": "keccak", @@ -152,6 +208,11 @@ mapstringmapstringmapstringRPCTransaction map[string]map[string]map[string "title": "integer", "type": "string" }, + "chainId": { + "pattern": "^0x[a-fA-F0-9]+$", + "title": "integer", + "type": "string" + }, "from": { "pattern": "^0x[a-fA-F\\d]{64}$", "title": "keccak", @@ -202,6 +263,11 @@ mapstringmapstringmapstringRPCTransaction map[string]map[string]map[string "title": "uint64", "type": "string" }, + "type": { + "pattern": "^0x([a-fA-F\\d])+$", + "title": "uint64", + "type": "string" + }, "v": { "pattern": "^0x[a-fA-F0-9]+$", "title": "integer", @@ -232,12 +298,24 @@ mapstringmapstringmapstringRPCTransaction map[string]map[string]map[string #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "txpool_content", "params": []}' + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "txpool_content", "params": []}' ``` + + + + +=== "Shell WebSocket" + + ``` shell + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "txpool_content", "params": []}' + ``` + + === "Javascript Console" ``` js @@ -245,6 +323,7 @@ mapstringmapstringmapstringRPCTransaction map[string]map[string]map[string ``` +
Source code

```go @@ -269,7 +348,7 @@ func (s *PublicTxPoolAPI) Content() map // Content returns the transactions cont return content } ``` -View on GitHub → +View on GitHub →

@@ -295,7 +374,7 @@ mapstringmapstringmapstringstring map[string]map[string]map[string]string< + Required: ✓ Yes - + === "Schema" ``` Schema @@ -349,12 +428,24 @@ mapstringmapstringmapstringstring map[string]map[string]map[string]string< #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "txpool_inspect", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "txpool_inspect", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "txpool_inspect", "params": []}' ``` + === "Javascript Console" ``` js @@ -362,6 +453,7 @@ mapstringmapstringmapstringstring map[string]map[string]map[string]string< ``` +
Source code

```go @@ -394,7 +486,7 @@ func (s *PublicTxPoolAPI) Inspect() map // Inspect retrieves the content of the return content } ``` -View on GitHub → +View on GitHub →

@@ -419,7 +511,7 @@ mapstringhexutilUint map[string]hexutil.Uint + Required: ✓ Yes - + === "Schema" ``` Schema @@ -459,12 +551,24 @@ mapstringhexutilUint map[string]hexutil.Uint #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "txpool_status", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "txpool_status", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "txpool_status", "params": []}' ``` + === "Javascript Console" ``` js @@ -472,6 +576,7 @@ mapstringhexutilUint map[string]hexutil.Uint ``` +
Source code

```go @@ -481,7 +586,7 @@ func (s *PublicTxPoolAPI) Status() map // Status returns the number of pending a return map[string]hexutil.Uint{"pending": hexutil.Uint(pending), "queued": hexutil.Uint(queue)} } ``` -View on GitHub → +View on GitHub →

diff --git a/docs/JSON-RPC-API/modules/web3.md b/docs/JSON-RPC-API/modules/web3.md index 8492cdef78..894bd787dd 100755 --- a/docs/JSON-RPC-API/modules/web3.md +++ b/docs/JSON-RPC-API/modules/web3.md @@ -7,7 +7,7 @@ | Entity | Version | | --- | --- | -| Source | 1.11.22-unstable/generated-at:2021-01-23T04:50:40-06:00 | +| Source | 1.11.23-unstable/generated-at:2021-04-30T19:24:24+03:00 | | OpenRPC | 1.2.6 | --- @@ -38,12 +38,24 @@ _None_ #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "web3_clientVersion", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "web3_clientVersion", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "web3_clientVersion", "params": []}' ``` + === "Javascript Console" ``` js @@ -51,6 +63,7 @@ _None_ ``` +
Source code

```go @@ -59,7 +72,7 @@ func (s *publicWeb3API) ClientVersion() string { }// ClientVersion returns the node name ``` -View on GitHub → +View on GitHub →

@@ -75,7 +88,7 @@ It assumes the input is hex encoded. #### Params (1) -Parameters must be given _by position_. +Parameters must be given _by position_. __1:__ @@ -83,7 +96,7 @@ input hexutil.Bytes + Required: ✓ Yes - + === "Schema" ``` Schema @@ -122,7 +135,7 @@ input hexutil.Bytes + Required: ✓ Yes - + === "Schema" ``` Schema @@ -152,12 +165,24 @@ input hexutil.Bytes #### Client Method Invocation Examples -=== "Shell" + +=== "Shell HTTP" + + ``` shell + curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "web3_sha3", "params": []}' + ``` + + + + + +=== "Shell WebSocket" ``` shell - curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", id": 42, "method": "web3_sha3", "params": []}' + wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "web3_sha3", "params": []}' ``` + === "Javascript Console" ``` js @@ -165,6 +190,7 @@ input hexutil.Bytes ``` +
Source code

```go @@ -174,7 +200,7 @@ func (s *publicWeb3API) Sha3(input hexutil.Bytes) hexutil.Bytes { // It assumes the input is hex encoded. ``` -View on GitHub → +View on GitHub →

diff --git a/eth/api.go b/eth/api.go index dd4485fc51..2121767f85 100644 --- a/eth/api.go +++ b/eth/api.go @@ -61,20 +61,6 @@ func (api *PublicEthereumAPI) Coinbase() (common.Address, error) { return api.Etherbase() } -// Hashrate returns the POW hashrate -func (api *PublicEthereumAPI) Hashrate() hexutil.Uint64 { - return hexutil.Uint64(api.e.Miner().HashRate()) -} - -// ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config. -func (api *PublicEthereumAPI) ChainId() hexutil.Uint64 { - chainID := new(big.Int) - if config := api.e.blockchain.Config(); config.IsEnabled(config.GetEIP155Transition, api.e.blockchain.CurrentBlock().Number()) { - chainID = config.GetChainID() - } - return (hexutil.Uint64)(chainID.Uint64()) -} - // PublicMinerAPI provides an API to control the miner. // It offers only methods that operate on data that pose no security risk when it is publicly accessible. type PublicMinerAPI struct { @@ -149,11 +135,6 @@ func (api *PrivateMinerAPI) SetRecommitInterval(interval int) { api.e.Miner().SetRecommitInterval(time.Duration(interval) * time.Millisecond) } -// GetHashrate returns the current hashrate of the miner. -func (api *PrivateMinerAPI) GetHashrate() uint64 { - return api.e.miner.HashRate() -} - // PrivateAdminAPI is the collection of Ethereum full node-related APIs // exposed over the private admin endpoint. type PrivateAdminAPI struct { @@ -275,15 +256,15 @@ func (api *PrivateAdminAPI) Ecbp1100(blockNr rpc.BlockNumber) (bool, error) { // MaxPeers sets the maximum peer limit for the protocol manager and the p2p server. func (api *PrivateAdminAPI) MaxPeers(n int) (bool, error) { - api.eth.protocolManager.maxPeers = n + api.eth.handler.maxPeers = n api.eth.p2pServer.MaxPeers = n - for i := api.eth.protocolManager.peers.Len(); i > n; i = api.eth.protocolManager.peers.Len() { - p := api.eth.protocolManager.peers.WorstPeer() + for i := api.eth.handler.peers.len(); i > n; i = api.eth.handler.peers.len() { + p := api.eth.handler.peers.WorstPeer() if p == nil { break } - api.eth.protocolManager.removePeer(p.id) + api.eth.handler.removePeer(p.ID()) } return true, nil } @@ -355,22 +336,29 @@ type BadBlockArgs struct { // GetBadBlocks returns a list of the last 'bad blocks' that the client has seen on the network // and returns them as a JSON list of block-hashes func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs, error) { - blocks := api.eth.BlockChain().BadBlocks() - results := make([]*BadBlockArgs, len(blocks)) - - var err error - for i, block := range blocks { - results[i] = &BadBlockArgs{ - Hash: block.Hash(), - } + var ( + err error + blocks = rawdb.ReadAllBadBlocks(api.eth.chainDb) + results = make([]*BadBlockArgs, 0, len(blocks)) + ) + for _, block := range blocks { + var ( + blockRlp string + blockJSON *ethapi.RPCMarshalBlockT + ) if rlpBytes, err := rlp.EncodeToBytes(block); err != nil { - results[i].RLP = err.Error() // Hacky, but hey, it works + blockRlp = err.Error() // Hacky, but hey, it works } else { - results[i].RLP = fmt.Sprintf("0x%x", rlpBytes) + blockRlp = fmt.Sprintf("0x%x", rlpBytes) } - if results[i].Block, err = ethapi.RPCMarshalBlock(block, true, true); err != nil { - results[i].Block = ðapi.RPCMarshalBlockT{Error: err.Error()} + if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true); err != nil { + blockJSON = ðapi.RPCMarshalBlockT{Error: err.Error()} } + results = append(results, &BadBlockArgs{ + Hash: block.Hash(), + RLP: blockRlp, + Block: blockJSON, + }) } return results, nil } @@ -443,7 +431,7 @@ func (api *PrivateDebugAPI) StorageRangeAt(blockHash common.Hash, txIndex int, c if block == nil { return StorageRangeResult{}, fmt.Errorf("block %#x not found", blockHash) } - _, _, statedb, err := api.computeTxEnv(block, txIndex, 0) + _, _, statedb, err := api.eth.stateAtTransaction(block, txIndex, 0) if err != nil { return StorageRangeResult{}, err } diff --git a/eth/api_backend.go b/eth/api_backend.go index 16689c72d6..4f30d6ff64 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -42,9 +42,10 @@ import ( // EthAPIBackend implements ethapi.Backend for full nodes type EthAPIBackend struct { - extRPCEnabled bool - eth *Ethereum - gpo *gasprice.Oracle + extRPCEnabled bool + allowUnprotectedTxs bool + eth *Ethereum + gpo *gasprice.Oracle } // ChainConfig returns the active chain configuration. @@ -57,7 +58,7 @@ func (b *EthAPIBackend) CurrentBlock() *types.Block { } func (b *EthAPIBackend) SetHead(number uint64) { - b.eth.protocolManager.downloader.Cancel() + b.eth.handler.downloader.Cancel() b.eth.blockchain.SetHead(number) } @@ -192,11 +193,14 @@ func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int { return b.eth.blockchain.GetTdByHash(hash) } -func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) { +func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) { vmError := func() error { return nil } - - context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil) - return vm.NewEVM(context, state, b.eth.blockchain.Config(), *b.eth.blockchain.GetVMConfig()), vmError, nil + if vmConfig == nil { + vmConfig = b.eth.blockchain.GetVMConfig() + } + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil) + return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig), vmError, nil } func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { @@ -272,10 +276,6 @@ func (b *EthAPIBackend) Downloader() *downloader.Downloader { return b.eth.Downloader() } -func (b *EthAPIBackend) ProtocolVersion() int { - return b.eth.EthVersion() -} - func (b *EthAPIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { return b.gpo.SuggestPrice(ctx) } @@ -296,6 +296,10 @@ func (b *EthAPIBackend) ExtRPCEnabled() bool { return b.extRPCEnabled } +func (b *EthAPIBackend) UnprotectedAllowed() bool { + return b.allowUnprotectedTxs +} + func (b *EthAPIBackend) RPCGasCap() uint64 { return b.eth.config.RPCGasCap } @@ -330,3 +334,11 @@ func (b *EthAPIBackend) Miner() *miner.Miner { func (b *EthAPIBackend) StartMining(threads int) error { return b.eth.StartMining(threads) } + +func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) { + return b.eth.stateAtBlock(block, reexec, base, checkLive) +} + +func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { + return b.eth.stateAtTransaction(block, txIndex, reexec) +} diff --git a/eth/api_test.go b/eth/api_test.go index d0b820c578..1c921e8997 100644 --- a/eth/api_test.go +++ b/eth/api_test.go @@ -60,8 +60,10 @@ func (h resultHash) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (h resultHash) Less(i, j int) bool { return bytes.Compare(h[i].Bytes(), h[j].Bytes()) < 0 } func TestAccountRange(t *testing.T) { + t.Parallel() + var ( - statedb = state.NewDatabase(rawdb.NewMemoryDatabase()) + statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), nil) state, _ = state.New(common.Hash{}, statedb, nil) addrs = [AccountRangeMaxResults * 2]common.Address{} m = map[common.Address]bool{} @@ -129,6 +131,8 @@ func TestAccountRange(t *testing.T) { } func TestEmptyAccountRange(t *testing.T) { + t.Parallel() + var ( statedb = state.NewDatabase(rawdb.NewMemoryDatabase()) state, _ = state.New(common.Hash{}, statedb, nil) @@ -145,6 +149,8 @@ func TestEmptyAccountRange(t *testing.T) { } func TestStorageRangeAt(t *testing.T) { + t.Parallel() + // Create a state where account 0x010000... has a few storage entries. var ( state, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) @@ -258,7 +264,7 @@ func BenchmarkCopyConfiguratorInterface(b *testing.B) { func BenchmarkTestValueEquivalenceAlot(b *testing.B) { for i := 0; i < b.N; i++ { overrideEIP2929 := params.ClassicChainConfig.GetEIP2929Transition() - existingEIP2929 := params.YoloV2ChainConfig.GetEIP2929Transition() + existingEIP2929 := params.YoloV3ChainConfig.GetEIP2929Transition() if (overrideEIP2929 == nil && existingEIP2929 != nil) || (overrideEIP2929 != nil && existingEIP2929 == nil) || (overrideEIP2929 != nil && existingEIP2929 != nil && *overrideEIP2929 != *existingEIP2929) { diff --git a/eth/backend.go b/eth/backend.go index bfc214690c..8fefe1da90 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -20,25 +20,30 @@ package eth import ( "errors" "fmt" + "math" "math/big" "runtime" "sync" "sync/atomic" + "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/consensus/clique" - "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state/pruner" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -47,26 +52,29 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/confp" "github.com/ethereum/go-ethereum/params/types/coregeth" - "github.com/ethereum/go-ethereum/params/types/ctypes" "github.com/ethereum/go-ethereum/params/types/goethereum" "github.com/ethereum/go-ethereum/params/vars" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" ) +// Config contains the configuration options of the ETH protocol. +// Deprecated: use ethconfig.Config instead. +type Config = ethconfig.Config + // Ethereum implements the Ethereum full node service. type Ethereum struct { - config *Config + config *ethconfig.Config // Handlers - txPool *core.TxPool - blockchain *core.BlockChain - protocolManager *ProtocolManager - dialCandidates enode.Iterator + txPool *core.TxPool + blockchain *core.BlockChain + handler *handler + ethDialCandidates enode.Iterator + snapDialCandidates enode.Iterator // DB interfaces chainDb ethdb.Database // Block chain database @@ -95,7 +103,7 @@ type Ethereum struct { // New creates a new Ethereum object (including the // initialisation of the common Ethereum object) -func New(stack *node.Node, config *Config) (*Ethereum, error) { +func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { // Ensure configuration values are compatible and sane var ( chainDb ethdb.Database @@ -108,8 +116,8 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) { return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode) } if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(common.Big0) <= 0 { - log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", DefaultConfig.Miner.GasPrice) - config.Miner.GasPrice = new(big.Int).Set(DefaultConfig.Miner.GasPrice) + log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice) + config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice) } if config.NoPruning && config.TrieDirtyCache > 0 { if config.SnapshotCache > 0 { @@ -122,33 +130,40 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) { } log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024) + // Transfer mining-related config to the ethash config. + ethashConfig := config.Ethash + ethashConfig.NotifyFull = config.Miner.NotifyFull + // Assemble the Ethereum object if config.DatabaseFreezerRemote != "" { - chainDb, err = stack.OpenDatabaseWithFreezerRemote("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezerRemote) + chainDb, err = stack.OpenDatabaseWithFreezerRemote("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezerRemote, false) } else { - chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/") + chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/", false) } if err != nil { return nil, err } - chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.ECBP1100) + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideMagneto) if _, ok := genesisErr.(*confp.ConfigCompatError); genesisErr != nil && !ok { return nil, genesisErr } log.Info("Initialised chain configuration", "config", chainConfig) + if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb, stack.ResolvePath(config.TrieCleanCacheJournal)); err != nil { + log.Error("Failed to recover state", "error", err) + } eth := &Ethereum{ config: config, chainDb: chainDb, eventMux: stack.EventMux(), accountManager: stack.AccountManager(), - engine: CreateConsensusEngine(stack, chainConfig, &config.Ethash, config.Miner.Notify, config.Miner.Noverify, chainDb), + engine: ethconfig.CreateConsensusEngine(stack, chainConfig, ðashConfig, config.Miner.Notify, config.Miner.Noverify, chainDb), closeBloomHandler: make(chan struct{}), networkID: config.NetworkId, gasPrice: config.Miner.GasPrice, etherbase: config.Miner.Etherbase, bloomRequests: make(chan chan *bloombits.Retrieval), - bloomIndexer: NewBloomIndexer(chainDb, vars.BloomBitsBlocks, vars.BloomConfirms), + bloomIndexer: core.NewBloomIndexer(chainDb, vars.BloomBitsBlocks, vars.BloomConfirms), p2pServer: stack.Server(), } @@ -157,7 +172,7 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) { if bcVersion != nil { dbVer = fmt.Sprintf("%d", *bcVersion) } - log.Info("Initialising Ethereum protocol", "versions", config.ProtocolVersions, "network", config.NetworkId, "dbversion", dbVer) + log.Info("Initialising Ethereum protocol", "network", config.NetworkId, "dbversion", dbVer) if !config.SkipBcVersionCheck { if bcVersion != nil && *bcVersion > core.BlockChainVersion { @@ -182,6 +197,7 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) { TrieDirtyDisabled: config.NoPruning, TrieTimeLimit: config.TrieTimeout, SnapshotLimit: config.SnapshotCache, + Preimages: config.Preimages, } ) eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit) @@ -196,6 +212,13 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) { } eth.bloomIndexer.Start(eth.blockchain) // Handle artificial finality config override cases. + if config.ECBP1100 != nil { + if n := config.ECBP1100.Uint64(); n != math.MaxUint64 { + if err := eth.blockchain.Config().SetECBP1100Transition(&n); err != nil { + return nil, err + } + } + } if config.ECBP1100NoDisable != nil { if *config.ECBP1100NoDisable { eth.blockchain.ArtificialFinalityNoDisable(1) @@ -217,31 +240,60 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) { checkpoint = p.TrustedCheckpoint } } - if eth.protocolManager, err = NewProtocolManager(chainConfig, checkpoint, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb, cacheLimit, config.Whitelist); err != nil { + if eth.handler, err = newHandler(&handlerConfig{ + Database: chainDb, + Chain: eth.blockchain, + TxPool: eth.txPool, + Network: config.NetworkId, + Sync: config.SyncMode, + BloomCache: uint64(cacheLimit), + EventMux: eth.eventMux, + Checkpoint: checkpoint, + Whitelist: config.Whitelist, + }); err != nil { return nil, err } eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) - eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), eth, nil} + eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil} + if eth.APIBackend.allowUnprotectedTxs { + log.Info("Unprotected transactions allowed") + } gpoParams := config.GPO if gpoParams.Default == nil { gpoParams.Default = config.Miner.GasPrice } eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams) - eth.dialCandidates, err = eth.setupDiscovery(&stack.Config().P2P) + eth.ethDialCandidates, err = setupDiscovery(eth.config.EthDiscoveryURLs) + if err != nil { + return nil, err + } + eth.snapDialCandidates, err = setupDiscovery(eth.config.SnapDiscoveryURLs) if err != nil { return nil, err } - // Start the RPC service - eth.netRPCService = ethapi.NewPublicNetAPI(eth.p2pServer, eth.NetVersion()) + eth.netRPCService = ethapi.NewPublicNetAPI(eth.p2pServer, config.NetworkId) // Register the backend on the node stack.RegisterAPIs(eth.APIs()) stack.RegisterProtocols(eth.Protocols()) stack.RegisterLifecycle(eth) + // Check for unclean shutdown + if uncleanShutdowns, discards, err := rawdb.PushUncleanShutdownMarker(chainDb); err != nil { + log.Error("Could not update unclean-shutdown-marker list", "error", err) + } else { + if discards > 0 { + log.Warn("Old unclean shutdowns found", "count", discards) + } + for _, tstamp := range uncleanShutdowns { + t := time.Unix(int64(tstamp), 0) + log.Warn("Unclean shutdown detected", "booted", t, + "age", common.PrettyAge(t)) + } + } return eth, nil } @@ -262,46 +314,6 @@ func makeExtraData(extra []byte) []byte { return extra } -// CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service -func CreateConsensusEngine(stack *node.Node, chainConfig ctypes.ChainConfigurator, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine { - // If proof-of-authority is requested, set it up - if chainConfig.GetConsensusEngineType().IsClique() { - return clique.New(&ctypes.CliqueConfig{ - Period: chainConfig.GetCliquePeriod(), - Epoch: chainConfig.GetCliqueEpoch(), - }, db) - } - // Otherwise assume proof-of-work - switch config.PowMode { - case ethash.ModeFake: - log.Warn("Ethash used in fake mode") - return ethash.NewFaker() - case ethash.ModePoissonFake: - log.Warn("Ethash used in fake Poisson mode") - return ethash.NewPoissonFaker() - case ethash.ModeTest: - log.Warn("Ethash used in test mode") - return ethash.NewTester(nil, noverify) - case ethash.ModeShared: - log.Warn("Ethash used in shared mode") - return ethash.NewShared() - default: - engine := ethash.New(ethash.Config{ - CacheDir: stack.ResolvePath(config.CacheDir), - CachesInMem: config.CachesInMem, - CachesOnDisk: config.CachesOnDisk, - CachesLockMmap: config.CachesLockMmap, - DatasetDir: config.DatasetDir, - DatasetsInMem: config.DatasetsInMem, - DatasetsOnDisk: config.DatasetsOnDisk, - DatasetsLockMmap: config.DatasetsLockMmap, - ECIP1099Block: chainConfig.GetEthashECIP1099Transition(), - }, notify, noverify) - engine.SetThreads(-1) // Disable CPU mining - return engine - } -} - // APIs return 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 { @@ -325,7 +337,7 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), + Service: downloader.NewPublicDownloaderAPI(s.handler.downloader, s.eventMux), Public: true, }, { Namespace: "miner", @@ -335,7 +347,7 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.APIBackend, false), + Service: filters.NewPublicFilterAPI(s.APIBackend, false, 5*time.Minute), Public: true, }, { Namespace: "admin", @@ -350,10 +362,6 @@ func (s *Ethereum) APIs() []rpc.API { Namespace: "debug", Version: "1.0", Service: NewPrivateDebugAPI(s), - }, { - Namespace: "trace", - Version: "1.0", - Service: NewPrivateTraceAPI(s), }, { Namespace: "net", Version: "1.0", @@ -492,7 +500,7 @@ func (s *Ethereum) StartMining(threads int) error { } // If mining is started, we can disable the transaction rejection mechanism // introduced to speed sync times. - atomic.StoreUint32(&s.protocolManager.acceptTxs, 1) + atomic.StoreUint32(&s.handler.acceptTxs, 1) go s.miner.Start(eb) } @@ -516,28 +524,28 @@ func (s *Ethereum) StopMining() { func (s *Ethereum) IsMining() bool { return s.miner.Mining() } func (s *Ethereum) Miner() *miner.Miner { return s.miner } -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) 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.config.ProtocolVersions[0]) } +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) 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(eth.MakeProtocols((*ethHandler)(s.handler), s.networkID, s.config.ProtocolVersions, s.ethDialCandidates)[0].Version) +} func (s *Ethereum) NetVersion() uint64 { return s.networkID } -func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } -func (s *Ethereum) Synced() bool { return atomic.LoadUint32(&s.protocolManager.acceptTxs) == 1 } +func (s *Ethereum) Downloader() *downloader.Downloader { return s.handler.downloader } +func (s *Ethereum) Synced() bool { return atomic.LoadUint32(&s.handler.acceptTxs) == 1 } func (s *Ethereum) ArchiveMode() bool { return s.config.NoPruning } func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer } // Protocols returns all the currently configured // network protocols to start. func (s *Ethereum) Protocols() []p2p.Protocol { - protos := make([]p2p.Protocol, len(s.config.ProtocolVersions)) - for i, vsn := range s.config.ProtocolVersions { - protos[i] = s.protocolManager.makeProtocol(vsn) - protos[i].Attributes = []enr.Entry{s.currentEthEntry()} - protos[i].DialCandidates = s.dialCandidates + protos := eth.MakeProtocols((*ethHandler)(s.handler), s.networkID, s.config.ProtocolVersions, s.ethDialCandidates) + if s.config.SnapshotCache > 0 { + protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...) } return protos } @@ -545,7 +553,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol { // Start implements node.Lifecycle, starting all internal goroutines needed by the // Ethereum protocol implementation. func (s *Ethereum) Start() error { - s.startEthEntryUpdate(s.p2pServer.LocalNode()) + eth.StartENRUpdater(s.blockchain, s.p2pServer.LocalNode()) // Start the bloom bits servicing goroutines s.startBloomHandlers(vars.BloomBitsBlocks) @@ -559,7 +567,7 @@ func (s *Ethereum) Start() error { maxPeers -= s.config.LightPeers } // Start the networking layer and the light server if requested - s.protocolManager.Start(maxPeers) + s.handler.Start(maxPeers) return nil } @@ -567,7 +575,7 @@ func (s *Ethereum) Start() error { // Ethereum protocol. func (s *Ethereum) Stop() error { // Stop all the peer-related stuff first. - s.protocolManager.Stop() + s.handler.Stop() // Then stop everything else. s.bloomIndexer.Close() @@ -576,7 +584,9 @@ func (s *Ethereum) Stop() error { s.miner.Stop() s.blockchain.Stop() s.engine.Close() + rawdb.PopUncleanShutdownMarker(s.chainDb) s.chainDb.Close() s.eventMux.Stop() + return nil } diff --git a/eth/bloombits.go b/eth/bloombits.go index bd34bd7b69..0cb7050d23 100644 --- a/eth/bloombits.go +++ b/eth/bloombits.go @@ -17,16 +17,10 @@ package eth import ( - "context" "time" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/bitutil" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" ) const ( @@ -78,66 +72,3 @@ func (eth *Ethereum) startBloomHandlers(sectionSize uint64) { }() } } - -const ( - // bloomThrottling is the time to wait between processing two consecutive index - // sections. It's useful during chain upgrades to prevent disk overload. - bloomThrottling = 100 * time.Millisecond -) - -// BloomIndexer implements a core.ChainIndexer, building up a rotated bloom bits index -// for the Ethereum header bloom filters, permitting blazing fast filtering. -type BloomIndexer struct { - size uint64 // section size to generate bloombits for - db ethdb.Database // database instance to write index data and metadata into - gen *bloombits.Generator // generator to rotate the bloom bits crating the bloom index - section uint64 // Section is the section number being processed currently - head common.Hash // Head is the hash of the last header processed -} - -// NewBloomIndexer returns a chain indexer that generates bloom bits data for the -// canonical chain for fast logs filtering. -func NewBloomIndexer(db ethdb.Database, size, confirms uint64) *core.ChainIndexer { - backend := &BloomIndexer{ - db: db, - size: size, - } - table := rawdb.NewTable(db, string(rawdb.BloomBitsIndexPrefix)) - - return core.NewChainIndexer(db, table, backend, size, confirms, bloomThrottling, "bloombits") -} - -// Reset implements core.ChainIndexerBackend, starting a new bloombits index -// section. -func (b *BloomIndexer) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error { - gen, err := bloombits.NewGenerator(uint(b.size)) - b.gen, b.section, b.head = gen, section, common.Hash{} - return err -} - -// Process implements core.ChainIndexerBackend, adding a new header's bloom into -// the index. -func (b *BloomIndexer) Process(ctx context.Context, header *types.Header) error { - b.gen.AddBloom(uint(header.Number.Uint64()-b.section*b.size), header.Bloom) - b.head = header.Hash() - return nil -} - -// Commit implements core.ChainIndexerBackend, finalizing the bloom section and -// writing it out into the database. -func (b *BloomIndexer) Commit() error { - batch := b.db.NewBatch() - for i := 0; i < types.BloomBitLength; i++ { - bits, err := b.gen.Bitset(uint(i)) - if err != nil { - return err - } - rawdb.WriteBloomBits(batch, uint(i), b.section, b.head, bitutil.CompressBytes(bits)) - } - return batch.Write() -} - -// Prune returns an empty error since we don't support pruning here. -func (b *BloomIndexer) Prune(threshold uint64) error { - return nil -} diff --git a/eth/discovery.go b/eth/discovery.go index 48f6159017..855ce3b0e1 100644 --- a/eth/discovery.go +++ b/eth/discovery.go @@ -19,7 +19,6 @@ package eth import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/forkid" - "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/dnsdisc" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rlp" @@ -64,11 +63,12 @@ func (eth *Ethereum) currentEthEntry() *ethEntry { eth.blockchain.CurrentHeader().Number.Uint64())} } -// setupDiscovery creates the node discovery source for the eth protocol. -func (eth *Ethereum) setupDiscovery(cfg *p2p.Config) (enode.Iterator, error) { - if cfg.NoDiscovery || len(eth.config.DiscoveryURLs) == 0 { +// setupDiscovery creates the node discovery source for the `eth` and `snap` +// protocols. +func setupDiscovery(urls []string) (enode.Iterator, error) { + if len(urls) == 0 { return nil, nil } client := dnsdisc.NewClient(dnsdisc.Config{}) - return client.NewIterator(eth.config.DiscoveryURLs...) + return client.NewIterator(urls...) } diff --git a/eth/downloader/api.go b/eth/downloader/api.go index 57ff3d71af..2024d23dea 100644 --- a/eth/downloader/api.go +++ b/eth/downloader/api.go @@ -20,7 +20,7 @@ import ( "context" "sync" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" ) diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 0aa046a567..7986b1708d 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -20,7 +20,6 @@ package downloader import ( "errors" "fmt" - "math" "math/big" "sync" "sync/atomic" @@ -30,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" @@ -39,7 +39,6 @@ import ( ) var ( - MaxHashFetch = 512 // Amount of hashes to be fetched per retrieval request MaxBlockFetch = 128 // Amount of blocks to be fetched per retrieval request MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request MaxSkeletonSize = 128 // Number of header fetches to need for a skeleton assembly @@ -82,7 +81,6 @@ var ( errTimeout = errors.New("timeout") errEmptyHeaderSet = errors.New("empty header set by peer") errPeersUnavailable = errors.New("no peers available or all tried for download") - errNoAncestor = errors.New("no common ancestor") errInvalidAncestor = errors.New("retrieved ancestor is invalid") errInvalidChain = errors.New("retrieved hash chain is invalid") errInvalidBody = errors.New("retrieved block body is invalid") @@ -91,7 +89,8 @@ var ( errCancelContentProcessing = errors.New("content processing canceled (requested)") errCanceled = errors.New("syncing canceled (requested)") errNoSyncActive = errors.New("no sync active") - errTooOld = errors.New("peer doesn't speak recent enough protocol version (need version >= 63)") + errTooOld = errors.New("peer's protocol version too old") + errNoAncestorFound = errors.New("no common ancestor found") ) type Downloader struct { @@ -133,20 +132,22 @@ type Downloader struct { ancientLimit uint64 // The maximum block number which can be regarded as ancient data. // Channels - headerCh chan dataPack // [eth/62] Channel receiving inbound block headers - bodyCh chan dataPack // [eth/62] Channel receiving inbound block bodies - receiptCh chan dataPack // [eth/63] Channel receiving inbound receipts - bodyWakeCh chan bool // [eth/62] Channel to signal the block body fetcher of new tasks - receiptWakeCh chan bool // [eth/63] Channel to signal the receipt fetcher of new tasks - headerProcCh chan []*types.Header // [eth/62] Channel to feed the header processor new tasks + headerCh chan dataPack // Channel receiving inbound block headers + bodyCh chan dataPack // Channel receiving inbound block bodies + receiptCh chan dataPack // Channel receiving inbound receipts + bodyWakeCh chan bool // Channel to signal the block body fetcher of new tasks + receiptWakeCh chan bool // Channel to signal the receipt fetcher of new tasks + headerProcCh chan []*types.Header // Channel to feed the header processor new tasks // State sync pivotHeader *types.Header // Pivot block header to dynamically push the syncing state root pivotLock sync.RWMutex // Lock protecting pivot header reads from updates + snapSync bool // Whether to run state sync over the snap protocol + SnapSyncer *snap.Syncer // TODO(karalabe): make private! hack for now stateSyncStart chan *stateSync trackStateReq chan *stateReq - stateCh chan dataPack // [eth/63] Channel receiving inbound node state data + stateCh chan dataPack // Channel receiving inbound node state data // Cancellation and termination cancelPeer string // Identifier of the peer currently being used as the master (cancel on drop) @@ -239,6 +240,7 @@ func New(checkpoint uint64, stateDb ethdb.Database, stateBloom *trie.SyncBloom, headerProcCh: make(chan []*types.Header, 1), quitCh: make(chan struct{}), stateCh: make(chan dataPack), + SnapSyncer: snap.NewSyncer(stateDb), stateSyncStart: make(chan *stateSync), syncStatsState: stateSyncStats{ processed: rawdb.ReadFastTrieProgress(stateDb), @@ -288,19 +290,16 @@ func (d *Downloader) Synchronising() bool { return atomic.LoadInt32(&d.synchronising) > 0 } -// SyncBloomContains tests if the syncbloom filter contains the given hash: -// - false: the bloom definitely does not contain hash -// - true: the bloom maybe contains hash -// -// While the bloom is being initialized (or is closed), all queries will return true. -func (d *Downloader) SyncBloomContains(hash []byte) bool { - return d.stateBloom == nil || d.stateBloom.Contains(hash) -} - // RegisterPeer injects a new download peer into the set of block source to be // used for fetching hashes and blocks from. -func (d *Downloader) RegisterPeer(id string, version int, peer Peer) error { - logger := log.New("peer", id) +func (d *Downloader) RegisterPeer(id string, version uint, peer Peer) error { + var logger log.Logger + if len(id) < 16 { + // Tests use short IDs, don't choke on them + logger = log.New("peer", id) + } else { + logger = log.New("peer", id[:8]) + } logger.Trace("Registering sync peer") if err := d.peers.Register(newPeerConnection(id, version, peer, logger)); err != nil { logger.Error("Failed to register sync peer", "err", err) @@ -312,7 +311,7 @@ func (d *Downloader) RegisterPeer(id string, version int, peer Peer) error { } // RegisterLightPeer injects a light client peer, wrapping it so it appears as a regular peer. -func (d *Downloader) RegisterLightPeer(id string, version int, peer LightPeer) error { +func (d *Downloader) RegisterLightPeer(id string, version uint, peer LightPeer) error { return d.RegisterPeer(id, version, &lightPeerWrapper{peer}) } @@ -321,7 +320,13 @@ func (d *Downloader) RegisterLightPeer(id string, version int, peer LightPeer) e // the queue. func (d *Downloader) UnregisterPeer(id string) error { // Unregister the peer from the active peer set and revoke any fetch tasks - logger := log.New("peer", id) + var logger log.Logger + if len(id) < 16 { + // Tests use short IDs, don't choke on them + logger = log.New("peer", id) + } else { + logger = log.New("peer", id[:8]) + } logger.Trace("Unregistering sync peer") if err := d.peers.Unregister(id); err != nil { logger.Error("Failed to unregister sync peer", "err", err) @@ -341,7 +346,6 @@ func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode case nil, errBusy, errCanceled: return err } - if errors.Is(err, errInvalidChain) || errors.Is(err, errBadPeer) || errors.Is(err, errTimeout) || errors.Is(err, errStallingPeer) || errors.Is(err, errUnsyncedPeer) || errors.Is(err, errEmptyHeaderSet) || errors.Is(err, errPeersUnavailable) || errors.Is(err, errTooOld) || errors.Is(err, errInvalidAncestor) { @@ -383,6 +387,16 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode if mode == FullSync && d.stateBloom != nil { d.stateBloom.Close() } + // If snap sync was requested, create the snap scheduler and switch to fast + // sync mode. Long term we could drop fast sync or merge the two together, + // but until snap becomes prevalent, we should support both. TODO(karalabe). + if mode == SnapSync { + if !d.snapSync { + log.Warn("Enabling snapshot sync prototype") + d.snapSync = true + } + mode = FastSync + } // Reset the queue, peer set and wake channels to clean any internal leftover state d.queue.Reset(blockCacheMaxItems, blockCacheInitialItems) d.peers.Reset() @@ -445,8 +459,8 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I d.mux.Post(DoneEvent{latest}) } }() - if p.version < 63 { - return errTooOld + if p.version < 64 { + return fmt.Errorf("%w: advertized %d < required %d", errTooOld, p.version, 64) } mode := d.getMode() @@ -808,32 +822,26 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) } } - // Only attempt span search if local head is "close" to reported remote - if math.Abs(float64(remoteHeight-localHeight)) < float64(MaxHeaderFetch) { - a, err := d.findAncestorSpanSearch(p, remoteHeight, localHeight, floor) - if err == nil { - if a > localHeight { - a = localHeight - } - return a, nil - } else if err != errNoAncestor { - return 0, err - } + ancestor, err := d.findAncestorSpanSearch(p, mode, remoteHeight, localHeight, floor) + if err == nil { + return ancestor, nil + } + // The returned error was not nil. + // If the error returned does not reflect that a common ancestor was not found, return it. + // If the error reflects that a common ancestor was not found, continue to binary search, + // where the error value will be reassigned. + if !errors.Is(err, errNoAncestorFound) { + return 0, err } - // Ancestor not found, we need to binary search over our chain - a, err := d.findAncestorBinarySearch(p, remoteHeight, floor) + ancestor, err = d.findAncestorBinarySearch(p, mode, remoteHeight, floor) if err != nil { return 0, err } - if a > localHeight { - a = localHeight - } - return a, nil + return ancestor, nil } -// findAncestorSpanSearch looks for a common ancestor by requesting a spaced set of headers between ours and theirs. -func (d *Downloader) findAncestorSpanSearch(p *peerConnection, remoteHeight, localHeight uint64, floor int64) (uint64, error) { +func (d *Downloader) findAncestorSpanSearch(p *peerConnection, mode SyncMode, remoteHeight, localHeight uint64, floor int64) (commonAncestor uint64, err error) { from, count, skip, max := calculateRequestSpan(remoteHeight, localHeight) p.log.Debug("Span searching for common ancestor", "count", count, "from", from, "skip", skip) @@ -917,11 +925,13 @@ func (d *Downloader) findAncestorSpanSearch(p *peerConnection, remoteHeight, loc p.log.Debug("Found common ancestor", "number", number, "hash", hash) return number, nil } - return 0, errNoAncestor + return 0, errNoAncestorFound } -// findAncestorBinarySearch negotiates a binary search using their reported chain. -func (d *Downloader) findAncestorBinarySearch(p *peerConnection, remoteHeight uint64, floor int64) (uint64, error) { +func (d *Downloader) findAncestorBinarySearch(p *peerConnection, mode SyncMode, remoteHeight uint64, floor int64) (commonAncestor uint64, err error) { + hash := common.Hash{} + + // Ancestor not found, we need to binary search over our chain start, end := uint64(0), remoteHeight if floor > 0 { start = uint64(floor) @@ -929,7 +939,6 @@ func (d *Downloader) findAncestorBinarySearch(p *peerConnection, remoteHeight ui p.log.Debug("Binary searching for common ancestor", "start", start, "end", end) // Wait for the remote response to the head fetch - hash := common.Hash{} for start+1 < end { // Split our chain interval in two, and request the hash to cross check check := (start + end) / 2 @@ -1391,7 +1400,7 @@ func (d *Downloader) fetchParts(deliveryCh chan dataPack, deliver func(dataPack) case err == nil: peer.log.Trace("Delivered new batch of data", "type", kind, "count", packet.Stats()) default: - peer.log.Trace("Failed to deliver retrieved data", "type", kind, "err", err) + peer.log.Debug("Failed to deliver retrieved data", "type", kind, "err", err) } } // Blocks assembled, try to update the progress @@ -1767,7 +1776,7 @@ func (d *Downloader) processFastSyncContent() error { }() closeOnErr := func(s *stateSync) { - if err := s.Wait(); err != nil && err != errCancelStateFetch && err != errCanceled { + if err := s.Wait(); err != nil && err != errCancelStateFetch && err != errCanceled && err != snap.ErrCancelled { d.queue.Close() // wake up Results } } @@ -1956,27 +1965,53 @@ func (d *Downloader) commitPivotBlock(result *fetchResult) error { // DeliverHeaders injects a new batch of block headers received from a remote // node into the download schedule. -func (d *Downloader) DeliverHeaders(id string, headers []*types.Header) (err error) { - return d.deliver(id, d.headerCh, &headerPack{id, headers}, headerInMeter, headerDropMeter) +func (d *Downloader) DeliverHeaders(id string, headers []*types.Header) error { + return d.deliver(d.headerCh, &headerPack{id, headers}, headerInMeter, headerDropMeter) } // DeliverBodies injects a new batch of block bodies received from a remote node. -func (d *Downloader) DeliverBodies(id string, transactions [][]*types.Transaction, uncles [][]*types.Header) (err error) { - return d.deliver(id, d.bodyCh, &bodyPack{id, transactions, uncles}, bodyInMeter, bodyDropMeter) +func (d *Downloader) DeliverBodies(id string, transactions [][]*types.Transaction, uncles [][]*types.Header) error { + return d.deliver(d.bodyCh, &bodyPack{id, transactions, uncles}, bodyInMeter, bodyDropMeter) } // DeliverReceipts injects a new batch of receipts received from a remote node. -func (d *Downloader) DeliverReceipts(id string, receipts [][]*types.Receipt) (err error) { - return d.deliver(id, d.receiptCh, &receiptPack{id, receipts}, receiptInMeter, receiptDropMeter) +func (d *Downloader) DeliverReceipts(id string, receipts [][]*types.Receipt) error { + return d.deliver(d.receiptCh, &receiptPack{id, receipts}, receiptInMeter, receiptDropMeter) } // DeliverNodeData injects a new batch of node state data received from a remote node. -func (d *Downloader) DeliverNodeData(id string, data [][]byte) (err error) { - return d.deliver(id, d.stateCh, &statePack{id, data}, stateInMeter, stateDropMeter) +func (d *Downloader) DeliverNodeData(id string, data [][]byte) error { + return d.deliver(d.stateCh, &statePack{id, data}, stateInMeter, stateDropMeter) +} + +// DeliverSnapPacket is invoked from a peer's message handler when it transmits a +// data packet for the local node to consume. +func (d *Downloader) DeliverSnapPacket(peer *snap.Peer, packet snap.Packet) error { + switch packet := packet.(type) { + case *snap.AccountRangePacket: + hashes, accounts, err := packet.Unpack() + if err != nil { + return err + } + return d.SnapSyncer.OnAccounts(peer, packet.ID, hashes, accounts, packet.Proof) + + case *snap.StorageRangesPacket: + hashset, slotset := packet.Unpack() + return d.SnapSyncer.OnStorage(peer, packet.ID, hashset, slotset, packet.Proof) + + case *snap.ByteCodesPacket: + return d.SnapSyncer.OnByteCodes(peer, packet.ID, packet.Codes) + + case *snap.TrieNodesPacket: + return d.SnapSyncer.OnTrieNodes(peer, packet.ID, packet.Nodes) + + default: + return fmt.Errorf("unexpected snap packet type: %T", packet) + } } // deliver injects a new batch of data received from a remote node. -func (d *Downloader) deliver(id string, destCh chan dataPack, packet dataPack, inMeter, dropMeter metrics.Meter) (err error) { +func (d *Downloader) deliver(destCh chan dataPack, packet dataPack, inMeter, dropMeter metrics.Meter) (err error) { // Update the delivery metrics for both good and failed deliveries inMeter.Mark(int64(packet.Items())) defer func() { diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 7645f04e4f..2917116144 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -390,7 +390,7 @@ func (dl *downloadTester) Rollback(hashes []common.Hash) { } // newPeer registers a new block download source into the downloader. -func (dl *downloadTester) newPeer(id string, version int, chain *testChain) error { +func (dl *downloadTester) newPeer(id string, version uint, chain *testChain) error { dl.lock.Lock() defer dl.lock.Unlock() @@ -515,20 +515,18 @@ func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, leng } } -// Tests that simple synchronization against a canonical chain works correctly. -// In this test common ancestor lookup should be short circuited and not require -// binary searching. -func TestCanonicalSynchronisation63Full(t *testing.T) { testCanonicalSynchronisation(t, 63, FullSync) } -func TestCanonicalSynchronisation63Fast(t *testing.T) { testCanonicalSynchronisation(t, 63, FastSync) } -func TestCanonicalSynchronisation64Full(t *testing.T) { testCanonicalSynchronisation(t, 64, FullSync) } -func TestCanonicalSynchronisation64Fast(t *testing.T) { testCanonicalSynchronisation(t, 64, FastSync) } -func TestCanonicalSynchronisation65Full(t *testing.T) { testCanonicalSynchronisation(t, 65, FullSync) } -func TestCanonicalSynchronisation65Fast(t *testing.T) { testCanonicalSynchronisation(t, 65, FastSync) } -func TestCanonicalSynchronisation65Light(t *testing.T) { - testCanonicalSynchronisation(t, 65, LightSync) -} +func TestCanonicalSynchronisation64Full(t *testing.T) { testCanonSync(t, 64, FullSync) } +func TestCanonicalSynchronisation64Fast(t *testing.T) { testCanonSync(t, 64, FastSync) } + +func TestCanonicalSynchronisation65Full(t *testing.T) { testCanonSync(t, 65, FullSync) } +func TestCanonicalSynchronisation65Fast(t *testing.T) { testCanonSync(t, 65, FastSync) } +func TestCanonicalSynchronisation65Light(t *testing.T) { testCanonSync(t, 65, LightSync) } + +func TestCanonicalSynchronisation66Full(t *testing.T) { testCanonSync(t, 66, FullSync) } +func TestCanonicalSynchronisation66Fast(t *testing.T) { testCanonSync(t, 66, FastSync) } +func TestCanonicalSynchronisation66Light(t *testing.T) { testCanonSync(t, 66, LightSync) } -func testCanonicalSynchronisation(t *testing.T, protocol int, mode SyncMode) { +func testCanonSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -547,14 +545,16 @@ func testCanonicalSynchronisation(t *testing.T, protocol int, mode SyncMode) { // Tests that if a large batch of blocks are being downloaded, it is throttled // until the cached blocks are retrieved. -func TestThrottling63Full(t *testing.T) { testThrottling(t, 63, FullSync) } -func TestThrottling63Fast(t *testing.T) { testThrottling(t, 63, FastSync) } func TestThrottling64Full(t *testing.T) { testThrottling(t, 64, FullSync) } func TestThrottling64Fast(t *testing.T) { testThrottling(t, 64, FastSync) } + func TestThrottling65Full(t *testing.T) { testThrottling(t, 65, FullSync) } func TestThrottling65Fast(t *testing.T) { testThrottling(t, 65, FastSync) } -func testThrottling(t *testing.T, protocol int, mode SyncMode) { +func TestThrottling66Full(t *testing.T) { testThrottling(t, 66, FullSync) } +func TestThrottling66Fast(t *testing.T) { testThrottling(t, 66, FastSync) } + +func testThrottling(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -569,7 +569,7 @@ func testThrottling(t *testing.T, protocol int, mode SyncMode) { <-proceed } // Start a synchronisation concurrently - errc := make(chan error) + errc := make(chan error, 1) go func() { errc <- tester.sync("peer", nil, mode) }() @@ -588,14 +588,15 @@ func testThrottling(t *testing.T, protocol int, mode SyncMode) { time.Sleep(25 * time.Millisecond) tester.lock.Lock() + tester.downloader.queue.lock.Lock() + tester.downloader.queue.resultCache.lock.Lock() { - tester.downloader.queue.resultCache.lock.Lock() cached = tester.downloader.queue.resultCache.countCompleted() - tester.downloader.queue.resultCache.lock.Unlock() frozen = int(atomic.LoadUint32(&blocked)) retrieved = len(tester.ownBlocks) - } + tester.downloader.queue.resultCache.lock.Unlock() + tester.downloader.queue.lock.Unlock() tester.lock.Unlock() if cached == blockCacheMaxItems || @@ -632,15 +633,18 @@ func testThrottling(t *testing.T, protocol int, mode SyncMode) { // Tests that simple synchronization against a forked chain works correctly. In // this test common ancestor lookup should *not* be short circuited, and a full // binary search should be executed. -func TestForkedSync63Full(t *testing.T) { testForkedSync(t, 63, FullSync) } -func TestForkedSync63Fast(t *testing.T) { testForkedSync(t, 63, FastSync) } -func TestForkedSync64Full(t *testing.T) { testForkedSync(t, 64, FullSync) } -func TestForkedSync64Fast(t *testing.T) { testForkedSync(t, 64, FastSync) } +func TestForkedSync64Full(t *testing.T) { testForkedSync(t, 64, FullSync) } +func TestForkedSync64Fast(t *testing.T) { testForkedSync(t, 64, FastSync) } + func TestForkedSync65Full(t *testing.T) { testForkedSync(t, 65, FullSync) } func TestForkedSync65Fast(t *testing.T) { testForkedSync(t, 65, FastSync) } func TestForkedSync65Light(t *testing.T) { testForkedSync(t, 65, LightSync) } -func testForkedSync(t *testing.T, protocol int, mode SyncMode) { +func TestForkedSync66Full(t *testing.T) { testForkedSync(t, 66, FullSync) } +func TestForkedSync66Fast(t *testing.T) { testForkedSync(t, 66, FastSync) } +func TestForkedSync66Light(t *testing.T) { testForkedSync(t, 66, LightSync) } + +func testForkedSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -665,15 +669,18 @@ func testForkedSync(t *testing.T, protocol int, mode SyncMode) { // Tests that synchronising against a much shorter but much heavyer fork works // corrently and is not dropped. -func TestHeavyForkedSync63Full(t *testing.T) { testHeavyForkedSync(t, 63, FullSync) } -func TestHeavyForkedSync63Fast(t *testing.T) { testHeavyForkedSync(t, 63, FastSync) } -func TestHeavyForkedSync64Full(t *testing.T) { testHeavyForkedSync(t, 64, FullSync) } -func TestHeavyForkedSync64Fast(t *testing.T) { testHeavyForkedSync(t, 64, FastSync) } +func TestHeavyForkedSync64Full(t *testing.T) { testHeavyForkedSync(t, 64, FullSync) } +func TestHeavyForkedSync64Fast(t *testing.T) { testHeavyForkedSync(t, 64, FastSync) } + func TestHeavyForkedSync65Full(t *testing.T) { testHeavyForkedSync(t, 65, FullSync) } func TestHeavyForkedSync65Fast(t *testing.T) { testHeavyForkedSync(t, 65, FastSync) } func TestHeavyForkedSync65Light(t *testing.T) { testHeavyForkedSync(t, 65, LightSync) } -func testHeavyForkedSync(t *testing.T, protocol int, mode SyncMode) { +func TestHeavyForkedSync66Full(t *testing.T) { testHeavyForkedSync(t, 66, FullSync) } +func TestHeavyForkedSync66Fast(t *testing.T) { testHeavyForkedSync(t, 66, FastSync) } +func TestHeavyForkedSync66Light(t *testing.T) { testHeavyForkedSync(t, 66, LightSync) } + +func testHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -700,15 +707,18 @@ func testHeavyForkedSync(t *testing.T, protocol int, mode SyncMode) { // Tests that chain forks are contained within a certain interval of the current // chain head, ensuring that malicious peers cannot waste resources by feeding // long dead chains. -func TestBoundedForkedSync63Full(t *testing.T) { testBoundedForkedSync(t, 63, FullSync) } -func TestBoundedForkedSync63Fast(t *testing.T) { testBoundedForkedSync(t, 63, FastSync) } -func TestBoundedForkedSync64Full(t *testing.T) { testBoundedForkedSync(t, 64, FullSync) } -func TestBoundedForkedSync64Fast(t *testing.T) { testBoundedForkedSync(t, 64, FastSync) } +func TestBoundedForkedSync64Full(t *testing.T) { testBoundedForkedSync(t, 64, FullSync) } +func TestBoundedForkedSync64Fast(t *testing.T) { testBoundedForkedSync(t, 64, FastSync) } + func TestBoundedForkedSync65Full(t *testing.T) { testBoundedForkedSync(t, 65, FullSync) } func TestBoundedForkedSync65Fast(t *testing.T) { testBoundedForkedSync(t, 65, FastSync) } func TestBoundedForkedSync65Light(t *testing.T) { testBoundedForkedSync(t, 65, LightSync) } -func testBoundedForkedSync(t *testing.T, protocol int, mode SyncMode) { +func TestBoundedForkedSync66Full(t *testing.T) { testBoundedForkedSync(t, 66, FullSync) } +func TestBoundedForkedSync66Fast(t *testing.T) { testBoundedForkedSync(t, 66, FastSync) } +func TestBoundedForkedSync66Light(t *testing.T) { testBoundedForkedSync(t, 66, LightSync) } + +func testBoundedForkedSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -734,15 +744,18 @@ func testBoundedForkedSync(t *testing.T, protocol int, mode SyncMode) { // Tests that chain forks are contained within a certain interval of the current // chain head for short but heavy forks too. These are a bit special because they // take different ancestor lookup paths. -func TestBoundedHeavyForkedSync63Full(t *testing.T) { testBoundedHeavyForkedSync(t, 63, FullSync) } -func TestBoundedHeavyForkedSync63Fast(t *testing.T) { testBoundedHeavyForkedSync(t, 63, FastSync) } -func TestBoundedHeavyForkedSync64Full(t *testing.T) { testBoundedHeavyForkedSync(t, 64, FullSync) } -func TestBoundedHeavyForkedSync64Fast(t *testing.T) { testBoundedHeavyForkedSync(t, 64, FastSync) } +func TestBoundedHeavyForkedSync64Full(t *testing.T) { testBoundedHeavyForkedSync(t, 64, FullSync) } +func TestBoundedHeavyForkedSync64Fast(t *testing.T) { testBoundedHeavyForkedSync(t, 64, FastSync) } + func TestBoundedHeavyForkedSync65Full(t *testing.T) { testBoundedHeavyForkedSync(t, 65, FullSync) } func TestBoundedHeavyForkedSync65Fast(t *testing.T) { testBoundedHeavyForkedSync(t, 65, FastSync) } func TestBoundedHeavyForkedSync65Light(t *testing.T) { testBoundedHeavyForkedSync(t, 65, LightSync) } -func testBoundedHeavyForkedSync(t *testing.T, protocol int, mode SyncMode) { +func TestBoundedHeavyForkedSync66Full(t *testing.T) { testBoundedHeavyForkedSync(t, 66, FullSync) } +func TestBoundedHeavyForkedSync66Fast(t *testing.T) { testBoundedHeavyForkedSync(t, 66, FastSync) } +func TestBoundedHeavyForkedSync66Light(t *testing.T) { testBoundedHeavyForkedSync(t, 66, LightSync) } + +func testBoundedHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -786,15 +799,18 @@ func TestInactiveDownloader63(t *testing.T) { } // Tests that a canceled download wipes all previously accumulated state. -func TestCancel63Full(t *testing.T) { testCancel(t, 63, FullSync) } -func TestCancel63Fast(t *testing.T) { testCancel(t, 63, FastSync) } -func TestCancel64Full(t *testing.T) { testCancel(t, 64, FullSync) } -func TestCancel64Fast(t *testing.T) { testCancel(t, 64, FastSync) } +func TestCancel64Full(t *testing.T) { testCancel(t, 64, FullSync) } +func TestCancel64Fast(t *testing.T) { testCancel(t, 64, FastSync) } + func TestCancel65Full(t *testing.T) { testCancel(t, 65, FullSync) } func TestCancel65Fast(t *testing.T) { testCancel(t, 65, FastSync) } func TestCancel65Light(t *testing.T) { testCancel(t, 65, LightSync) } -func testCancel(t *testing.T, protocol int, mode SyncMode) { +func TestCancel66Full(t *testing.T) { testCancel(t, 66, FullSync) } +func TestCancel66Fast(t *testing.T) { testCancel(t, 66, FastSync) } +func TestCancel66Light(t *testing.T) { testCancel(t, 66, LightSync) } + +func testCancel(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -819,15 +835,18 @@ func testCancel(t *testing.T, protocol int, mode SyncMode) { } // Tests that synchronisation from multiple peers works as intended (multi thread sanity test). -func TestMultiSynchronisation63Full(t *testing.T) { testMultiSynchronisation(t, 63, FullSync) } -func TestMultiSynchronisation63Fast(t *testing.T) { testMultiSynchronisation(t, 63, FastSync) } -func TestMultiSynchronisation64Full(t *testing.T) { testMultiSynchronisation(t, 64, FullSync) } -func TestMultiSynchronisation64Fast(t *testing.T) { testMultiSynchronisation(t, 64, FastSync) } +func TestMultiSynchronisation64Full(t *testing.T) { testMultiSynchronisation(t, 64, FullSync) } +func TestMultiSynchronisation64Fast(t *testing.T) { testMultiSynchronisation(t, 64, FastSync) } + func TestMultiSynchronisation65Full(t *testing.T) { testMultiSynchronisation(t, 65, FullSync) } func TestMultiSynchronisation65Fast(t *testing.T) { testMultiSynchronisation(t, 65, FastSync) } func TestMultiSynchronisation65Light(t *testing.T) { testMultiSynchronisation(t, 65, LightSync) } -func testMultiSynchronisation(t *testing.T, protocol int, mode SyncMode) { +func TestMultiSynchronisation66Full(t *testing.T) { testMultiSynchronisation(t, 66, FullSync) } +func TestMultiSynchronisation66Fast(t *testing.T) { testMultiSynchronisation(t, 66, FastSync) } +func TestMultiSynchronisation66Light(t *testing.T) { testMultiSynchronisation(t, 66, LightSync) } + +func testMultiSynchronisation(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -849,15 +868,18 @@ func testMultiSynchronisation(t *testing.T, protocol int, mode SyncMode) { // Tests that synchronisations behave well in multi-version protocol environments // and not wreak havoc on other nodes in the network. -func TestMultiProtoSynchronisation63Full(t *testing.T) { testMultiProtoSync(t, 63, FullSync) } -func TestMultiProtoSynchronisation63Fast(t *testing.T) { testMultiProtoSync(t, 63, FastSync) } -func TestMultiProtoSynchronisation64Full(t *testing.T) { testMultiProtoSync(t, 64, FullSync) } -func TestMultiProtoSynchronisation64Fast(t *testing.T) { testMultiProtoSync(t, 64, FastSync) } +func TestMultiProtoSynchronisation64Full(t *testing.T) { testMultiProtoSync(t, 64, FullSync) } +func TestMultiProtoSynchronisation64Fast(t *testing.T) { testMultiProtoSync(t, 64, FastSync) } + func TestMultiProtoSynchronisation65Full(t *testing.T) { testMultiProtoSync(t, 65, FullSync) } func TestMultiProtoSynchronisation65Fast(t *testing.T) { testMultiProtoSync(t, 65, FastSync) } func TestMultiProtoSynchronisation65Light(t *testing.T) { testMultiProtoSync(t, 65, LightSync) } -func testMultiProtoSync(t *testing.T, protocol int, mode SyncMode) { +func TestMultiProtoSynchronisation66Full(t *testing.T) { testMultiProtoSync(t, 66, FullSync) } +func TestMultiProtoSynchronisation66Fast(t *testing.T) { testMultiProtoSync(t, 66, FastSync) } +func TestMultiProtoSynchronisation66Light(t *testing.T) { testMultiProtoSync(t, 66, LightSync) } + +func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -867,9 +889,9 @@ func testMultiProtoSync(t *testing.T, protocol int, mode SyncMode) { chain := testChainBase.shorten(blockCacheMaxItems - 15) // Create peers of every type - tester.newPeer("peer 63", 63, chain) tester.newPeer("peer 64", 64, chain) tester.newPeer("peer 65", 65, chain) + tester.newPeer("peer 66", 66, chain) // Synchronise with the requested peer and make sure all blocks were retrieved if err := tester.sync(fmt.Sprintf("peer %d", protocol), nil, mode); err != nil { @@ -878,7 +900,7 @@ func testMultiProtoSync(t *testing.T, protocol int, mode SyncMode) { assertOwnChain(t, tester, chain.len()) // Check that no peers have been dropped off - for _, version := range []int{63, 64, 65} { + for _, version := range []int{64, 65, 66} { peer := fmt.Sprintf("peer %d", version) if _, ok := tester.peers[peer]; !ok { t.Errorf("%s dropped", peer) @@ -888,15 +910,18 @@ func testMultiProtoSync(t *testing.T, protocol int, mode SyncMode) { // Tests that if a block is empty (e.g. header only), no body request should be // made, and instead the header should be assembled into a whole block in itself. -func TestEmptyShortCircuit63Full(t *testing.T) { testEmptyShortCircuit(t, 63, FullSync) } -func TestEmptyShortCircuit63Fast(t *testing.T) { testEmptyShortCircuit(t, 63, FastSync) } -func TestEmptyShortCircuit64Full(t *testing.T) { testEmptyShortCircuit(t, 64, FullSync) } -func TestEmptyShortCircuit64Fast(t *testing.T) { testEmptyShortCircuit(t, 64, FastSync) } +func TestEmptyShortCircuit64Full(t *testing.T) { testEmptyShortCircuit(t, 64, FullSync) } +func TestEmptyShortCircuit64Fast(t *testing.T) { testEmptyShortCircuit(t, 64, FastSync) } + func TestEmptyShortCircuit65Full(t *testing.T) { testEmptyShortCircuit(t, 65, FullSync) } func TestEmptyShortCircuit65Fast(t *testing.T) { testEmptyShortCircuit(t, 65, FastSync) } func TestEmptyShortCircuit65Light(t *testing.T) { testEmptyShortCircuit(t, 65, LightSync) } -func testEmptyShortCircuit(t *testing.T, protocol int, mode SyncMode) { +func TestEmptyShortCircuit66Full(t *testing.T) { testEmptyShortCircuit(t, 66, FullSync) } +func TestEmptyShortCircuit66Fast(t *testing.T) { testEmptyShortCircuit(t, 66, FastSync) } +func TestEmptyShortCircuit66Light(t *testing.T) { testEmptyShortCircuit(t, 66, LightSync) } + +func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -942,15 +967,18 @@ func testEmptyShortCircuit(t *testing.T, protocol int, mode SyncMode) { // Tests that headers are enqueued continuously, preventing malicious nodes from // stalling the downloader by feeding gapped header chains. -func TestMissingHeaderAttack63Full(t *testing.T) { testMissingHeaderAttack(t, 63, FullSync) } -func TestMissingHeaderAttack63Fast(t *testing.T) { testMissingHeaderAttack(t, 63, FastSync) } -func TestMissingHeaderAttack64Full(t *testing.T) { testMissingHeaderAttack(t, 64, FullSync) } -func TestMissingHeaderAttack64Fast(t *testing.T) { testMissingHeaderAttack(t, 64, FastSync) } +func TestMissingHeaderAttack64Full(t *testing.T) { testMissingHeaderAttack(t, 64, FullSync) } +func TestMissingHeaderAttack64Fast(t *testing.T) { testMissingHeaderAttack(t, 64, FastSync) } + func TestMissingHeaderAttack65Full(t *testing.T) { testMissingHeaderAttack(t, 65, FullSync) } func TestMissingHeaderAttack65Fast(t *testing.T) { testMissingHeaderAttack(t, 65, FastSync) } func TestMissingHeaderAttack65Light(t *testing.T) { testMissingHeaderAttack(t, 65, LightSync) } -func testMissingHeaderAttack(t *testing.T, protocol int, mode SyncMode) { +func TestMissingHeaderAttack66Full(t *testing.T) { testMissingHeaderAttack(t, 66, FullSync) } +func TestMissingHeaderAttack66Fast(t *testing.T) { testMissingHeaderAttack(t, 66, FastSync) } +func TestMissingHeaderAttack66Light(t *testing.T) { testMissingHeaderAttack(t, 66, LightSync) } + +func testMissingHeaderAttack(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -974,15 +1002,18 @@ func testMissingHeaderAttack(t *testing.T, protocol int, mode SyncMode) { // Tests that if requested headers are shifted (i.e. first is missing), the queue // detects the invalid numbering. -func TestShiftedHeaderAttack63Full(t *testing.T) { testShiftedHeaderAttack(t, 63, FullSync) } -func TestShiftedHeaderAttack63Fast(t *testing.T) { testShiftedHeaderAttack(t, 63, FastSync) } -func TestShiftedHeaderAttack64Full(t *testing.T) { testShiftedHeaderAttack(t, 64, FullSync) } -func TestShiftedHeaderAttack64Fast(t *testing.T) { testShiftedHeaderAttack(t, 64, FastSync) } +func TestShiftedHeaderAttack64Full(t *testing.T) { testShiftedHeaderAttack(t, 64, FullSync) } +func TestShiftedHeaderAttack64Fast(t *testing.T) { testShiftedHeaderAttack(t, 64, FastSync) } + func TestShiftedHeaderAttack65Full(t *testing.T) { testShiftedHeaderAttack(t, 65, FullSync) } func TestShiftedHeaderAttack65Fast(t *testing.T) { testShiftedHeaderAttack(t, 65, FastSync) } func TestShiftedHeaderAttack65Light(t *testing.T) { testShiftedHeaderAttack(t, 65, LightSync) } -func testShiftedHeaderAttack(t *testing.T, protocol int, mode SyncMode) { +func TestShiftedHeaderAttack66Full(t *testing.T) { testShiftedHeaderAttack(t, 66, FullSync) } +func TestShiftedHeaderAttack66Fast(t *testing.T) { testShiftedHeaderAttack(t, 66, FastSync) } +func TestShiftedHeaderAttack66Light(t *testing.T) { testShiftedHeaderAttack(t, 66, LightSync) } + +func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -1011,11 +1042,11 @@ func testShiftedHeaderAttack(t *testing.T, protocol int, mode SyncMode) { // Tests that upon detecting an invalid header, the recent ones are rolled back // for various failure scenarios. Afterwards a full sync is attempted to make // sure no state was corrupted. -func TestInvalidHeaderRollback63Fast(t *testing.T) { testInvalidHeaderRollback(t, 63, FastSync) } func TestInvalidHeaderRollback64Fast(t *testing.T) { testInvalidHeaderRollback(t, 64, FastSync) } func TestInvalidHeaderRollback65Fast(t *testing.T) { testInvalidHeaderRollback(t, 65, FastSync) } +func TestInvalidHeaderRollback66Fast(t *testing.T) { testInvalidHeaderRollback(t, 66, FastSync) } -func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) { +func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -1103,15 +1134,18 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) { // Tests that a peer advertising a high TD doesn't get to stall the downloader // afterwards by not sending any useful hashes. -func TestHighTDStarvationAttack63Full(t *testing.T) { testHighTDStarvationAttack(t, 63, FullSync) } -func TestHighTDStarvationAttack63Fast(t *testing.T) { testHighTDStarvationAttack(t, 63, FastSync) } -func TestHighTDStarvationAttack64Full(t *testing.T) { testHighTDStarvationAttack(t, 64, FullSync) } -func TestHighTDStarvationAttack64Fast(t *testing.T) { testHighTDStarvationAttack(t, 64, FastSync) } +func TestHighTDStarvationAttack64Full(t *testing.T) { testHighTDStarvationAttack(t, 64, FullSync) } +func TestHighTDStarvationAttack64Fast(t *testing.T) { testHighTDStarvationAttack(t, 64, FastSync) } + func TestHighTDStarvationAttack65Full(t *testing.T) { testHighTDStarvationAttack(t, 65, FullSync) } func TestHighTDStarvationAttack65Fast(t *testing.T) { testHighTDStarvationAttack(t, 65, FastSync) } func TestHighTDStarvationAttack65Light(t *testing.T) { testHighTDStarvationAttack(t, 65, LightSync) } -func testHighTDStarvationAttack(t *testing.T, protocol int, mode SyncMode) { +func TestHighTDStarvationAttack66Full(t *testing.T) { testHighTDStarvationAttack(t, 66, FullSync) } +func TestHighTDStarvationAttack66Fast(t *testing.T) { testHighTDStarvationAttack(t, 66, FastSync) } +func TestHighTDStarvationAttack66Light(t *testing.T) { testHighTDStarvationAttack(t, 66, LightSync) } + +func testHighTDStarvationAttack(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -1125,11 +1159,11 @@ func testHighTDStarvationAttack(t *testing.T, protocol int, mode SyncMode) { } // Tests that misbehaving peers are disconnected, whilst behaving ones are not. -func TestBlockHeaderAttackerDropping63(t *testing.T) { testBlockHeaderAttackerDropping(t, 63) } func TestBlockHeaderAttackerDropping64(t *testing.T) { testBlockHeaderAttackerDropping(t, 64) } func TestBlockHeaderAttackerDropping65(t *testing.T) { testBlockHeaderAttackerDropping(t, 65) } +func TestBlockHeaderAttackerDropping66(t *testing.T) { testBlockHeaderAttackerDropping(t, 66) } -func testBlockHeaderAttackerDropping(t *testing.T, protocol int) { +func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) { t.Parallel() // Define the disconnection requirement for individual hash fetch errors @@ -1179,15 +1213,18 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) { // Tests that synchronisation progress (origin block number, current block number // and highest block number) is tracked and updated correctly. -func TestSyncProgress63Full(t *testing.T) { testSyncProgress(t, 63, FullSync) } -func TestSyncProgress63Fast(t *testing.T) { testSyncProgress(t, 63, FastSync) } -func TestSyncProgress64Full(t *testing.T) { testSyncProgress(t, 64, FullSync) } -func TestSyncProgress64Fast(t *testing.T) { testSyncProgress(t, 64, FastSync) } +func TestSyncProgress64Full(t *testing.T) { testSyncProgress(t, 64, FullSync) } +func TestSyncProgress64Fast(t *testing.T) { testSyncProgress(t, 64, FastSync) } + func TestSyncProgress65Full(t *testing.T) { testSyncProgress(t, 65, FullSync) } func TestSyncProgress65Fast(t *testing.T) { testSyncProgress(t, 65, FastSync) } func TestSyncProgress65Light(t *testing.T) { testSyncProgress(t, 65, LightSync) } -func testSyncProgress(t *testing.T, protocol int, mode SyncMode) { +func TestSyncProgress66Full(t *testing.T) { testSyncProgress(t, 66, FullSync) } +func TestSyncProgress66Fast(t *testing.T) { testSyncProgress(t, 66, FastSync) } +func TestSyncProgress66Light(t *testing.T) { testSyncProgress(t, 66, LightSync) } + +func testSyncProgress(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -1263,21 +1300,24 @@ func checkProgress(t *testing.T, d *Downloader, stage string, want ethereum.Sync // Tests that synchronisation progress (origin block number and highest block // number) is tracked and updated correctly in case of a fork (or manual head // revertal). -func TestForkedSyncProgress63Full(t *testing.T) { testForkedSyncProgress(t, 63, FullSync) } -func TestForkedSyncProgress63Fast(t *testing.T) { testForkedSyncProgress(t, 63, FastSync) } -func TestForkedSyncProgress64Full(t *testing.T) { testForkedSyncProgress(t, 64, FullSync) } -func TestForkedSyncProgress64Fast(t *testing.T) { testForkedSyncProgress(t, 64, FastSync) } +func TestForkedSyncProgress64Full(t *testing.T) { testForkedSyncProgress(t, 64, FullSync) } +func TestForkedSyncProgress64Fast(t *testing.T) { testForkedSyncProgress(t, 64, FastSync) } + func TestForkedSyncProgress65Full(t *testing.T) { testForkedSyncProgress(t, 65, FullSync) } func TestForkedSyncProgress65Fast(t *testing.T) { testForkedSyncProgress(t, 65, FastSync) } func TestForkedSyncProgress65Light(t *testing.T) { testForkedSyncProgress(t, 65, LightSync) } -func testForkedSyncProgress(t *testing.T, protocol int, mode SyncMode) { +func TestForkedSyncProgress66Full(t *testing.T) { testForkedSyncProgress(t, 66, FullSync) } +func TestForkedSyncProgress66Fast(t *testing.T) { testForkedSyncProgress(t, 66, FastSync) } +func TestForkedSyncProgress66Light(t *testing.T) { testForkedSyncProgress(t, 66, LightSync) } + +func testForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() defer tester.terminate() - chainA := testChainForkLightA.shorten(testChainBase.len() + MaxHashFetch) - chainB := testChainForkLightB.shorten(testChainBase.len() + MaxHashFetch) + chainA := testChainForkLightA.shorten(testChainBase.len() + MaxHeaderFetch) + chainB := testChainForkLightB.shorten(testChainBase.len() + MaxHeaderFetch) // Set a sync init hook to catch progress changes starting := make(chan struct{}) @@ -1339,15 +1379,18 @@ func testForkedSyncProgress(t *testing.T, protocol int, mode SyncMode) { // Tests that if synchronisation is aborted due to some failure, then the progress // origin is not updated in the next sync cycle, as it should be considered the // continuation of the previous sync and not a new instance. -func TestFailedSyncProgress63Full(t *testing.T) { testFailedSyncProgress(t, 63, FullSync) } -func TestFailedSyncProgress63Fast(t *testing.T) { testFailedSyncProgress(t, 63, FastSync) } -func TestFailedSyncProgress64Full(t *testing.T) { testFailedSyncProgress(t, 64, FullSync) } -func TestFailedSyncProgress64Fast(t *testing.T) { testFailedSyncProgress(t, 64, FastSync) } +func TestFailedSyncProgress64Full(t *testing.T) { testFailedSyncProgress(t, 64, FullSync) } +func TestFailedSyncProgress64Fast(t *testing.T) { testFailedSyncProgress(t, 64, FastSync) } + func TestFailedSyncProgress65Full(t *testing.T) { testFailedSyncProgress(t, 65, FullSync) } func TestFailedSyncProgress65Fast(t *testing.T) { testFailedSyncProgress(t, 65, FastSync) } func TestFailedSyncProgress65Light(t *testing.T) { testFailedSyncProgress(t, 65, LightSync) } -func testFailedSyncProgress(t *testing.T, protocol int, mode SyncMode) { +func TestFailedSyncProgress66Full(t *testing.T) { testFailedSyncProgress(t, 66, FullSync) } +func TestFailedSyncProgress66Fast(t *testing.T) { testFailedSyncProgress(t, 66, FastSync) } +func TestFailedSyncProgress66Light(t *testing.T) { testFailedSyncProgress(t, 66, LightSync) } + +func testFailedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -1412,15 +1455,18 @@ func testFailedSyncProgress(t *testing.T, protocol int, mode SyncMode) { // Tests that if an attacker fakes a chain height, after the attack is detected, // the progress height is successfully reduced at the next sync invocation. -func TestFakedSyncProgress63Full(t *testing.T) { testFakedSyncProgress(t, 63, FullSync) } -func TestFakedSyncProgress63Fast(t *testing.T) { testFakedSyncProgress(t, 63, FastSync) } -func TestFakedSyncProgress64Full(t *testing.T) { testFakedSyncProgress(t, 64, FullSync) } -func TestFakedSyncProgress64Fast(t *testing.T) { testFakedSyncProgress(t, 64, FastSync) } +func TestFakedSyncProgress64Full(t *testing.T) { testFakedSyncProgress(t, 64, FullSync) } +func TestFakedSyncProgress64Fast(t *testing.T) { testFakedSyncProgress(t, 64, FastSync) } + func TestFakedSyncProgress65Full(t *testing.T) { testFakedSyncProgress(t, 65, FullSync) } func TestFakedSyncProgress65Fast(t *testing.T) { testFakedSyncProgress(t, 65, FastSync) } func TestFakedSyncProgress65Light(t *testing.T) { testFakedSyncProgress(t, 65, LightSync) } -func testFakedSyncProgress(t *testing.T, protocol int, mode SyncMode) { +func TestFakedSyncProgress66Full(t *testing.T) { testFakedSyncProgress(t, 66, FullSync) } +func TestFakedSyncProgress66Fast(t *testing.T) { testFakedSyncProgress(t, 66, FastSync) } +func TestFakedSyncProgress66Light(t *testing.T) { testFakedSyncProgress(t, 66, LightSync) } + +func testFakedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() tester := newTester() @@ -1489,31 +1535,20 @@ func testFakedSyncProgress(t *testing.T, protocol int, mode SyncMode) { // This test reproduces an issue where unexpected deliveries would // block indefinitely if they arrived at the right time. -func TestDeliverHeadersHang(t *testing.T) { - t.Parallel() +func TestDeliverHeadersHang64Full(t *testing.T) { testDeliverHeadersHang(t, 64, FullSync) } +func TestDeliverHeadersHang64Fast(t *testing.T) { testDeliverHeadersHang(t, 64, FastSync) } - testCases := []struct { - protocol int - syncMode SyncMode - }{ - {63, FullSync}, - {63, FastSync}, - {64, FullSync}, - {64, FastSync}, - {64, LightSync}, - {65, FullSync}, - {65, FastSync}, - {65, LightSync}, - } - for _, tc := range testCases { - t.Run(fmt.Sprintf("protocol %d mode %v", tc.protocol, tc.syncMode), func(t *testing.T) { - t.Parallel() - testDeliverHeadersHang(t, tc.protocol, tc.syncMode) - }) - } -} +func TestDeliverHeadersHang65Full(t *testing.T) { testDeliverHeadersHang(t, 65, FullSync) } +func TestDeliverHeadersHang65Fast(t *testing.T) { testDeliverHeadersHang(t, 65, FastSync) } +func TestDeliverHeadersHang65Light(t *testing.T) { testDeliverHeadersHang(t, 65, LightSync) } + +func TestDeliverHeadersHang66Full(t *testing.T) { testDeliverHeadersHang(t, 66, FullSync) } +func TestDeliverHeadersHang66Fast(t *testing.T) { testDeliverHeadersHang(t, 66, FastSync) } +func TestDeliverHeadersHang66Light(t *testing.T) { testDeliverHeadersHang(t, 66, LightSync) } + +func testDeliverHeadersHang(t *testing.T, protocol uint, mode SyncMode) { + t.Parallel() -func testDeliverHeadersHang(t *testing.T, protocol int, mode SyncMode) { master := newTester() defer master.terminate() chain := testChainBase.shorten(15) @@ -1664,15 +1699,18 @@ func TestRemoteHeaderRequestSpan(t *testing.T) { // Tests that peers below a pre-configured checkpoint block are prevented from // being fast-synced from, avoiding potential cheap eclipse attacks. -func TestCheckpointEnforcement63Full(t *testing.T) { testCheckpointEnforcement(t, 63, FullSync) } -func TestCheckpointEnforcement63Fast(t *testing.T) { testCheckpointEnforcement(t, 63, FastSync) } -func TestCheckpointEnforcement64Full(t *testing.T) { testCheckpointEnforcement(t, 64, FullSync) } -func TestCheckpointEnforcement64Fast(t *testing.T) { testCheckpointEnforcement(t, 64, FastSync) } +func TestCheckpointEnforcement64Full(t *testing.T) { testCheckpointEnforcement(t, 64, FullSync) } +func TestCheckpointEnforcement64Fast(t *testing.T) { testCheckpointEnforcement(t, 64, FastSync) } + func TestCheckpointEnforcement65Full(t *testing.T) { testCheckpointEnforcement(t, 65, FullSync) } func TestCheckpointEnforcement65Fast(t *testing.T) { testCheckpointEnforcement(t, 65, FastSync) } func TestCheckpointEnforcement65Light(t *testing.T) { testCheckpointEnforcement(t, 65, LightSync) } -func testCheckpointEnforcement(t *testing.T, protocol int, mode SyncMode) { +func TestCheckpointEnforcement66Full(t *testing.T) { testCheckpointEnforcement(t, 66, FullSync) } +func TestCheckpointEnforcement66Fast(t *testing.T) { testCheckpointEnforcement(t, 66, FastSync) } +func TestCheckpointEnforcement66Light(t *testing.T) { testCheckpointEnforcement(t, 66, LightSync) } + +func testCheckpointEnforcement(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() // Create a new tester with a particular hard coded checkpoint block diff --git a/eth/downloader/fakepeer.go b/eth/downloader/fakepeer.go deleted file mode 100644 index 3ec90bc9ef..0000000000 --- a/eth/downloader/fakepeer.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package downloader - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" -) - -// FakePeer is a mock downloader peer that operates on a local database instance -// instead of being an actual live node. It's useful for testing and to implement -// sync commands from an existing local database. -type FakePeer struct { - id string - db ethdb.Database - hc *core.HeaderChain - dl *Downloader -} - -// NewFakePeer creates a new mock downloader peer with the given data sources. -func NewFakePeer(id string, db ethdb.Database, hc *core.HeaderChain, dl *Downloader) *FakePeer { - return &FakePeer{id: id, db: db, hc: hc, dl: dl} -} - -// Head implements downloader.Peer, returning the current head hash and number -// of the best known header. -func (p *FakePeer) Head() (common.Hash, *big.Int) { - header := p.hc.CurrentHeader() - return header.Hash(), header.Number -} - -// RequestHeadersByHash implements downloader.Peer, returning a batch of headers -// defined by the origin hash and the associated query parameters. -func (p *FakePeer) RequestHeadersByHash(hash common.Hash, amount int, skip int, reverse bool) error { - var ( - headers []*types.Header - unknown bool - ) - for !unknown && len(headers) < amount { - origin := p.hc.GetHeaderByHash(hash) - if origin == nil { - break - } - number := origin.Number.Uint64() - headers = append(headers, origin) - if reverse { - for i := 0; i <= skip; i++ { - if header := p.hc.GetHeader(hash, number); header != nil { - hash = header.ParentHash - number-- - } else { - unknown = true - break - } - } - } else { - var ( - current = origin.Number.Uint64() - next = current + uint64(skip) + 1 - ) - if header := p.hc.GetHeaderByNumber(next); header != nil { - if p.hc.GetBlockHashesFromHash(header.Hash(), uint64(skip+1))[skip] == hash { - hash = header.Hash() - } else { - unknown = true - } - } else { - unknown = true - } - } - } - p.dl.DeliverHeaders(p.id, headers) - return nil -} - -// RequestHeadersByNumber implements downloader.Peer, returning a batch of headers -// defined by the origin number and the associated query parameters. -func (p *FakePeer) RequestHeadersByNumber(number uint64, amount int, skip int, reverse bool) error { - var ( - headers []*types.Header - unknown bool - ) - for !unknown && len(headers) < amount { - origin := p.hc.GetHeaderByNumber(number) - if origin == nil { - break - } - if reverse { - if number >= uint64(skip+1) { - number -= uint64(skip + 1) - } else { - unknown = true - } - } else { - number += uint64(skip + 1) - } - headers = append(headers, origin) - } - p.dl.DeliverHeaders(p.id, headers) - return nil -} - -// RequestBodies implements downloader.Peer, returning a batch of block bodies -// corresponding to the specified block hashes. -func (p *FakePeer) RequestBodies(hashes []common.Hash) error { - var ( - txs [][]*types.Transaction - uncles [][]*types.Header - ) - for _, hash := range hashes { - block := rawdb.ReadBlock(p.db, hash, *p.hc.GetBlockNumber(hash)) - - txs = append(txs, block.Transactions()) - uncles = append(uncles, block.Uncles()) - } - p.dl.DeliverBodies(p.id, txs, uncles) - return nil -} - -// RequestReceipts implements downloader.Peer, returning a batch of transaction -// receipts corresponding to the specified block hashes. -func (p *FakePeer) RequestReceipts(hashes []common.Hash) error { - var receipts [][]*types.Receipt - for _, hash := range hashes { - receipts = append(receipts, rawdb.ReadRawReceipts(p.db, hash, *p.hc.GetBlockNumber(hash))) - } - p.dl.DeliverReceipts(p.id, receipts) - return nil -} - -// RequestNodeData implements downloader.Peer, returning a batch of state trie -// nodes corresponding to the specified trie hashes. -func (p *FakePeer) RequestNodeData(hashes []common.Hash) error { - var data [][]byte - for _, hash := range hashes { - if entry, err := p.db.Get(hash.Bytes()); err == nil { - data = append(data, entry) - } - } - p.dl.DeliverNodeData(p.id, data) - return nil -} diff --git a/eth/downloader/modes.go b/eth/downloader/modes.go index d866ceabce..3ea14d22d7 100644 --- a/eth/downloader/modes.go +++ b/eth/downloader/modes.go @@ -24,7 +24,8 @@ type SyncMode uint32 const ( FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks - FastSync // Quickly download the headers, full sync only at the chain head + FastSync // Quickly download the headers, full sync only at the chain + SnapSync // Download the chain and the state via compact snapshots LightSync // Download only the headers and terminate afterwards ) @@ -39,6 +40,8 @@ func (mode SyncMode) String() string { return "full" case FastSync: return "fast" + case SnapSync: + return "snap" case LightSync: return "light" default: @@ -52,6 +55,8 @@ func (mode SyncMode) MarshalText() ([]byte, error) { return []byte("full"), nil case FastSync: return []byte("fast"), nil + case SnapSync: + return []byte("snap"), nil case LightSync: return []byte("light"), nil default: @@ -65,6 +70,8 @@ func (mode *SyncMode) UnmarshalText(text []byte) error { *mode = FullSync case "fast": *mode = FastSync + case "snap": + *mode = SnapSync case "light": *mode = LightSync default: diff --git a/eth/downloader/peer.go b/eth/downloader/peer.go index c6671436f9..7852569d8e 100644 --- a/eth/downloader/peer.go +++ b/eth/downloader/peer.go @@ -29,6 +29,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" ) @@ -69,7 +70,7 @@ type peerConnection struct { peer Peer - version int // Eth protocol version number to switch strategies + version uint // Eth protocol version number to switch strategies log log.Logger // Contextual logger to add extra infos to peer logs lock sync.RWMutex } @@ -112,7 +113,7 @@ func (w *lightPeerWrapper) RequestNodeData([]common.Hash) error { } // newPeerConnection creates a new downloader peer. -func newPeerConnection(id string, version int, peer Peer, logger log.Logger) *peerConnection { +func newPeerConnection(id string, version uint, peer Peer, logger log.Logger) *peerConnection { return &peerConnection{ id: id, lacking: make(map[common.Hash]struct{}), @@ -457,7 +458,7 @@ func (ps *peerSet) HeaderIdlePeers() ([]*peerConnection, int) { defer p.lock.RUnlock() return p.headerThroughput } - return ps.idlePeers(63, 65, idle, throughput) + return ps.idlePeers(eth.ETH64, eth.ETH66, idle, throughput) } // BodyIdlePeers retrieves a flat list of all the currently body-idle peers within @@ -471,7 +472,7 @@ func (ps *peerSet) BodyIdlePeers() ([]*peerConnection, int) { defer p.lock.RUnlock() return p.blockThroughput } - return ps.idlePeers(63, 65, idle, throughput) + return ps.idlePeers(eth.ETH64, eth.ETH66, idle, throughput) } // ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers @@ -485,7 +486,7 @@ func (ps *peerSet) ReceiptIdlePeers() ([]*peerConnection, int) { defer p.lock.RUnlock() return p.receiptThroughput } - return ps.idlePeers(63, 65, idle, throughput) + return ps.idlePeers(eth.ETH64, eth.ETH66, idle, throughput) } // NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle @@ -499,13 +500,13 @@ func (ps *peerSet) NodeDataIdlePeers() ([]*peerConnection, int) { defer p.lock.RUnlock() return p.stateThroughput } - return ps.idlePeers(63, 65, idle, throughput) + return ps.idlePeers(eth.ETH64, eth.ETH66, idle, throughput) } // idlePeers retrieves a flat list of all currently idle peers satisfying the // protocol version constraints, using the provided function to check idleness. // The resulting set of peers are sorted by their measure throughput. -func (ps *peerSet) idlePeers(minProtocol, maxProtocol int, idleCheck func(*peerConnection) bool, throughput func(*peerConnection) float64) ([]*peerConnection, int) { +func (ps *peerSet) idlePeers(minProtocol, maxProtocol uint, idleCheck func(*peerConnection) bool, throughput func(*peerConnection) float64) ([]*peerConnection, int) { ps.lock.RLock() defer ps.lock.RUnlock() diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index d2ec8ba694..ac7edc2c68 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -113,24 +113,24 @@ type queue struct { mode SyncMode // Synchronisation mode to decide on the block parts to schedule for fetching // Headers are "special", they download in batches, supported by a skeleton chain - headerHead common.Hash // [eth/62] Hash of the last queued header to verify order - headerTaskPool map[uint64]*types.Header // [eth/62] Pending header retrieval tasks, mapping starting indexes to skeleton headers - headerTaskQueue *prque.Prque // [eth/62] Priority queue of the skeleton indexes to fetch the filling headers for - headerPeerMiss map[string]map[uint64]struct{} // [eth/62] Set of per-peer header batches known to be unavailable - headerPendPool map[string]*fetchRequest // [eth/62] Currently pending header retrieval operations - headerResults []*types.Header // [eth/62] Result cache accumulating the completed headers - headerProced int // [eth/62] Number of headers already processed from the results - headerOffset uint64 // [eth/62] Number of the first header in the result cache - headerContCh chan bool // [eth/62] Channel to notify when header download finishes + headerHead common.Hash // Hash of the last queued header to verify order + headerTaskPool map[uint64]*types.Header // Pending header retrieval tasks, mapping starting indexes to skeleton headers + headerTaskQueue *prque.Prque // Priority queue of the skeleton indexes to fetch the filling headers for + headerPeerMiss map[string]map[uint64]struct{} // Set of per-peer header batches known to be unavailable + headerPendPool map[string]*fetchRequest // Currently pending header retrieval operations + headerResults []*types.Header // Result cache accumulating the completed headers + headerProced int // Number of headers already processed from the results + headerOffset uint64 // Number of the first header in the result cache + headerContCh chan bool // Channel to notify when header download finishes // All data retrievals below are based on an already assembles header chain - blockTaskPool map[common.Hash]*types.Header // [eth/62] Pending block (body) retrieval tasks, mapping hashes to headers - blockTaskQueue *prque.Prque // [eth/62] Priority queue of the headers to fetch the blocks (bodies) for - blockPendPool map[string]*fetchRequest // [eth/62] Currently pending block (body) retrieval operations + blockTaskPool map[common.Hash]*types.Header // Pending block (body) retrieval tasks, mapping hashes to headers + blockTaskQueue *prque.Prque // Priority queue of the headers to fetch the blocks (bodies) for + blockPendPool map[string]*fetchRequest // Currently pending block (body) retrieval operations - receiptTaskPool map[common.Hash]*types.Header // [eth/63] Pending receipt retrieval tasks, mapping hashes to headers - receiptTaskQueue *prque.Prque // [eth/63] Priority queue of the headers to fetch the receipts for - receiptPendPool map[string]*fetchRequest // [eth/63] Currently pending receipt retrieval operations + receiptTaskPool map[common.Hash]*types.Header // Pending receipt retrieval tasks, mapping hashes to headers + receiptTaskQueue *prque.Prque // Priority queue of the headers to fetch the receipts for + receiptPendPool map[string]*fetchRequest // Currently pending receipt retrieval operations resultCache *resultStore // Downloaded but not yet delivered fetch results resultSize common.StorageSize // Approximate size of a block (exponential moving average) @@ -690,6 +690,13 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh q.lock.Lock() defer q.lock.Unlock() + var logger log.Logger + if len(id) < 16 { + // Tests use short IDs, don't choke on them + logger = log.New("peer", id) + } else { + logger = log.New("peer", id[:16]) + } // Short circuit if the data was never requested request := q.headerPendPool[id] if request == nil { @@ -704,10 +711,10 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh accepted := len(headers) == MaxHeaderFetch if accepted { if headers[0].Number.Uint64() != request.From { - log.Trace("First header broke chain ordering", "peer", id, "number", headers[0].Number, "hash", headers[0].Hash(), request.From) + logger.Trace("First header broke chain ordering", "number", headers[0].Number, "hash", headers[0].Hash(), "expected", request.From) accepted = false } else if headers[len(headers)-1].Hash() != target { - log.Trace("Last header broke skeleton structure ", "peer", id, "number", headers[len(headers)-1].Number, "hash", headers[len(headers)-1].Hash(), "expected", target) + logger.Trace("Last header broke skeleton structure ", "number", headers[len(headers)-1].Number, "hash", headers[len(headers)-1].Hash(), "expected", target) accepted = false } } @@ -716,12 +723,12 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh for i, header := range headers[1:] { hash := header.Hash() if want := request.From + 1 + uint64(i); header.Number.Uint64() != want { - log.Warn("Header broke chain ordering", "peer", id, "number", header.Number, "hash", hash, "expected", want) + logger.Warn("Header broke chain ordering", "number", header.Number, "hash", hash, "expected", want) accepted = false break } if parentHash != header.ParentHash { - log.Warn("Header broke chain ancestry", "peer", id, "number", header.Number, "hash", hash) + logger.Warn("Header broke chain ancestry", "number", header.Number, "hash", hash) accepted = false break } @@ -731,7 +738,7 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh } // If the batch of headers wasn't accepted, mark as unavailable if !accepted { - log.Trace("Skeleton filling not accepted", "peer", id, "from", request.From) + logger.Trace("Skeleton filling not accepted", "from", request.From) miss := q.headerPeerMiss[id] if miss == nil { @@ -758,7 +765,7 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh select { case headerProcCh <- process: - log.Trace("Pre-scheduled new headers", "peer", id, "count", len(process), "from", process[0].Number) + logger.Trace("Pre-scheduled new headers", "count", len(process), "from", process[0].Number) q.headerProced += len(process) default: } @@ -886,9 +893,6 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, return accepted, nil } // If none of the data was good, it's a stale delivery - if errors.Is(failure, errInvalidChain) { - return accepted, failure - } if accepted > 0 { return accepted, fmt.Errorf("partial failure: %v", failure) } diff --git a/eth/downloader/queue_test.go b/eth/downloader/queue_test.go index 581e234969..9f8c898a79 100644 --- a/eth/downloader/queue_test.go +++ b/eth/downloader/queue_test.go @@ -98,6 +98,9 @@ func dummyPeer(id string) *peerConnection { } func TestBasics(t *testing.T) { + numOfBlocks := len(emptyChain.blocks) + numOfReceipts := len(emptyChain.blocks) / 2 + q := newQueue(10, 10) if !q.Idle() { t.Errorf("new queue should be idle") @@ -136,6 +139,12 @@ func TestBasics(t *testing.T) { t.Fatalf("expected header %d, got %d", exp, got) } } + if exp, got := q.blockTaskQueue.Size(), numOfBlocks-10; exp != got { + t.Errorf("expected block task queue to be %d, got %d", exp, got) + } + if exp, got := q.receiptTaskQueue.Size(), numOfReceipts; exp != got { + t.Errorf("expected receipt task queue to be %d, got %d", exp, got) + } { peer := dummyPeer("peer-2") fetchReq, _, throttle := q.ReserveBodies(peer, 50) @@ -149,8 +158,12 @@ func TestBasics(t *testing.T) { t.Fatalf("should have no fetches, got %d", len(fetchReq.Headers)) } } - //fmt.Printf("blockTaskQueue len: %d\n", q.blockTaskQueue.Size()) - //fmt.Printf("receiptTaskQueue len: %d\n", q.receiptTaskQueue.Size()) + if exp, got := q.blockTaskQueue.Size(), numOfBlocks-10; exp != got { + t.Errorf("expected block task queue to be %d, got %d", exp, got) + } + if exp, got := q.receiptTaskQueue.Size(), numOfReceipts; exp != got { + t.Errorf("expected receipt task queue to be %d, got %d", exp, got) + } { // The receipt delivering peer should not be affected // by the throttling of body deliveries @@ -169,12 +182,20 @@ func TestBasics(t *testing.T) { } } - //fmt.Printf("blockTaskQueue len: %d\n", q.blockTaskQueue.Size()) - //fmt.Printf("receiptTaskQueue len: %d\n", q.receiptTaskQueue.Size()) - //fmt.Printf("processable: %d\n", q.resultCache.countCompleted()) + if exp, got := q.blockTaskQueue.Size(), numOfBlocks-10; exp != got { + t.Errorf("expected block task queue to be %d, got %d", exp, got) + } + if exp, got := q.receiptTaskQueue.Size(), numOfReceipts-5; exp != got { + t.Errorf("expected receipt task queue to be %d, got %d", exp, got) + } + if got, exp := q.resultCache.countCompleted(), 0; got != exp { + t.Errorf("wrong processable count, got %d, exp %d", got, exp) + } } func TestEmptyBlocks(t *testing.T) { + numOfBlocks := len(emptyChain.blocks) + q := newQueue(10, 10) q.Prepare(1, FastSync) @@ -209,13 +230,12 @@ func TestEmptyBlocks(t *testing.T) { } } - if q.blockTaskQueue.Size() != len(emptyChain.blocks)-10 { - t.Errorf("expected block task queue to be 0, got %d", q.blockTaskQueue.Size()) + if q.blockTaskQueue.Size() != numOfBlocks-10 { + t.Errorf("expected block task queue to be %d, got %d", numOfBlocks-10, q.blockTaskQueue.Size()) } if q.receiptTaskQueue.Size() != 0 { - t.Errorf("expected receipt task queue to be 0, got %d", q.receiptTaskQueue.Size()) + t.Errorf("expected receipt task queue to be %d, got %d", 0, q.receiptTaskQueue.Size()) } - //fmt.Printf("receiptTaskQueue len: %d\n", q.receiptTaskQueue.Size()) { peer := dummyPeer("peer-3") fetchReq, _, _ := q.ReserveReceipts(peer, 50) @@ -225,6 +245,12 @@ func TestEmptyBlocks(t *testing.T) { t.Fatal("there should be no body fetch tasks remaining") } } + if q.blockTaskQueue.Size() != numOfBlocks-10 { + t.Errorf("expected block task queue to be %d, got %d", numOfBlocks-10, q.blockTaskQueue.Size()) + } + if q.receiptTaskQueue.Size() != 0 { + t.Errorf("expected receipt task queue to be %d, got %d", 0, q.receiptTaskQueue.Size()) + } if got, exp := q.resultCache.countCompleted(), 10; got != exp { t.Errorf("wrong processable count, got %d, exp %d", got, exp) } diff --git a/eth/downloader/statesync.go b/eth/downloader/statesync.go index 6745aa54ac..6231588ad2 100644 --- a/eth/downloader/statesync.go +++ b/eth/downloader/statesync.go @@ -18,13 +18,13 @@ package downloader import ( "fmt" - "hash" "sync" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie" @@ -101,8 +101,16 @@ func (d *Downloader) runStateSync(s *stateSync) *stateSync { finished []*stateReq // Completed or failed requests timeout = make(chan *stateReq) // Timed out active requests ) - // Run the state sync. log.Trace("State sync starting", "root", s.root) + + defer func() { + // Cancel active request timers on exit. Also set peers to idle so they're + // available for the next sync. + for _, req := range active { + req.timer.Stop() + req.peer.SetNodeDataIdle(int(req.nItems), time.Now()) + } + }() go s.run() defer s.Cancel() @@ -252,8 +260,9 @@ func (d *Downloader) spindownStateSync(active map[string]*stateReq, finished []* type stateSync struct { d *Downloader // Downloader instance to access and manage current peerset - sched *trie.Sync // State trie sync scheduler defining the tasks - keccak hash.Hash // Keccak256 hasher to verify deliveries with + root common.Hash // State root currently being synced + sched *trie.Sync // State trie sync scheduler defining the tasks + keccak crypto.KeccakState // Keccak256 hasher to verify deliveries with trieTasks map[common.Hash]*trieTask // Set of trie node tasks currently queued for retrieval codeTasks map[common.Hash]*codeTask // Set of byte code tasks currently queued for retrieval @@ -268,8 +277,6 @@ type stateSync struct { cancelOnce sync.Once // Ensures cancel only ever gets called once done chan struct{} // Channel to signal termination completion err error // Any error hit during sync (set before completion) - - root common.Hash } // trieTask represents a single trie node download task, containing a set of @@ -290,15 +297,15 @@ type codeTask struct { func newStateSync(d *Downloader, root common.Hash) *stateSync { return &stateSync{ d: d, + root: root, sched: state.NewStateSync(root, d.stateDB, d.stateBloom), - keccak: sha3.NewLegacyKeccak256(), + keccak: sha3.NewLegacyKeccak256().(crypto.KeccakState), trieTasks: make(map[common.Hash]*trieTask), codeTasks: make(map[common.Hash]*codeTask), deliver: make(chan *stateReq), cancel: make(chan struct{}), done: make(chan struct{}), started: make(chan struct{}), - root: root, } } @@ -306,7 +313,12 @@ func newStateSync(d *Downloader, root common.Hash) *stateSync { // it finishes, and finally notifying any goroutines waiting for the loop to // finish. func (s *stateSync) run() { - s.err = s.loop() + close(s.started) + if s.d.snapSync { + s.err = s.d.SnapSyncer.Sync(s.root, s.cancel) + } else { + s.err = s.loop() + } close(s.done) } @@ -318,7 +330,9 @@ func (s *stateSync) Wait() error { // Cancel cancels the sync and waits until it has shut down. func (s *stateSync) Cancel() error { - s.cancelOnce.Do(func() { close(s.cancel) }) + s.cancelOnce.Do(func() { + close(s.cancel) + }) return s.Wait() } @@ -329,7 +343,6 @@ func (s *stateSync) Cancel() error { // pushed here async. The reason is to decouple processing from data receipt // and timeouts. func (s *stateSync) loop() (err error) { - close(s.started) // Listen for new peer events to assign tasks to them newPeer := make(chan *peerConnection, 1024) peerSub := s.d.peers.SubscribeNewPeers(newPeer) @@ -577,7 +590,7 @@ func (s *stateSync) processNodeData(blob []byte) (common.Hash, error) { res := trie.SyncResult{Data: blob} s.keccak.Reset() s.keccak.Write(blob) - s.keccak.Sum(res.Hash[:0]) + s.keccak.Read(res.Hash[:]) err := s.sched.Process(res) return res.Hash, err } diff --git a/eth/config.go b/eth/ethconfig/config.go similarity index 61% rename from eth/config.go rename to eth/ethconfig/config.go index 8d812751fb..9feea766dd 100644 --- a/eth/config.go +++ b/eth/ethconfig/config.go @@ -14,7 +14,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package eth +// Package ethconfig contains the configuration of the ETH and LES protocols. +package ethconfig import ( "math/big" @@ -25,32 +26,37 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "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/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params/types/ctypes" "github.com/ethereum/go-ethereum/params/types/genesisT" "github.com/ethereum/go-ethereum/params/vars" ) -// DefaultFullGPOConfig contains default gasprice oracle settings for full node. -var DefaultFullGPOConfig = gasprice.Config{ +// FullNodeGPO contains default gasprice oracle settings for full node. +var FullNodeGPO = gasprice.Config{ Blocks: 20, Percentile: 60, MaxPrice: gasprice.DefaultMaxPrice, } -// DefaultLightGPOConfig contains default gasprice oracle settings for light client. -var DefaultLightGPOConfig = gasprice.Config{ +// LightClientGPO contains default gasprice oracle settings for light client. +var LightClientGPO = gasprice.Config{ Blocks: 2, Percentile: 60, MaxPrice: gasprice.DefaultMaxPrice, } -// DefaultConfig contains default settings for use on the Ethereum main net. -var DefaultConfig = Config{ +// Defaults contains default settings for use on the Ethereum main net. +var Defaults = Config{ SyncMode: downloader.FastSync, Ethash: ethash.Config{ CacheDir: "ethash", @@ -62,6 +68,7 @@ var DefaultConfig = Config{ DatasetsLockMmap: false, }, NetworkId: vars.DefaultNetworkID, + TxLookupLimit: 2350000, LightPeers: 100, UltraLightFraction: 75, DatabaseCache: 512, @@ -79,7 +86,7 @@ var DefaultConfig = Config{ }, TxPool: core.DefaultTxPoolConfig, RPCGasCap: 25000000, - GPO: DefaultFullGPOConfig, + GPO: FullNodeGPO, RPCTxFeeCap: 1, // 1 ether } @@ -91,21 +98,22 @@ func init() { } } if runtime.GOOS == "darwin" { - DefaultConfig.Ethash.DatasetDir = filepath.Join(home, "Library", "Ethash") + Defaults.Ethash.DatasetDir = filepath.Join(home, "Library", "Ethash") } else if runtime.GOOS == "windows" { localappdata := os.Getenv("LOCALAPPDATA") if localappdata != "" { - DefaultConfig.Ethash.DatasetDir = filepath.Join(localappdata, "Ethash") + Defaults.Ethash.DatasetDir = filepath.Join(localappdata, "Ethash") } else { - DefaultConfig.Ethash.DatasetDir = filepath.Join(home, "AppData", "Local", "Ethash") + Defaults.Ethash.DatasetDir = filepath.Join(home, "AppData", "Local", "Ethash") } } else { - DefaultConfig.Ethash.DatasetDir = filepath.Join(home, ".ethash") + Defaults.Ethash.DatasetDir = filepath.Join(home, ".ethash") } } //go:generate gencodec -type Config -formats toml -out gen_config.go +// Config contains configuration options for of the ETH and LES protocols. type Config struct { // The genesis block, which is inserted if the database is empty. // If nil, the Ethereum main net block is used. @@ -118,7 +126,8 @@ type Config struct { // This can be set to list of enrtree:// URLs which will be queried for // for nodes to connect to. - DiscoveryURLs []string + EthDiscoveryURLs []string + SnapDiscoveryURLs []string NoPruning bool // Whether to disable pruning and flush everything to disk NoPrefetch bool // Whether to disable prefetching and only load state on demand @@ -129,11 +138,13 @@ type Config struct { Whitelist map[uint64]common.Hash `toml:"-"` // Light client options - LightServ int `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests - LightIngress int `toml:",omitempty"` // Incoming bandwidth limit for light servers - LightEgress int `toml:",omitempty"` // Outgoing bandwidth limit for light servers - LightPeers int `toml:",omitempty"` // Maximum number of LES client peers - LightNoPrune bool `toml:",omitempty"` // Whether to disable light chain pruning + LightServ int `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests + LightIngress int `toml:",omitempty"` // Incoming bandwidth limit for light servers + LightEgress int `toml:",omitempty"` // Outgoing bandwidth limit for light servers + LightPeers int `toml:",omitempty"` // Maximum number of LES client peers + LightNoPrune bool `toml:",omitempty"` // Whether to disable light chain pruning + LightNoSyncServe bool `toml:",omitempty"` // Whether to serve light clients before syncing + SyncFromCheckpoint bool `toml:",omitempty"` // Whether to sync the header chain from the configured checkpoint // Ultra Light client options UltraLightServers []string `toml:",omitempty"` // List of trusted ultra light servers @@ -153,6 +164,7 @@ type Config struct { TrieDirtyCache int TrieTimeout time.Duration SnapshotCache int + Preimages bool // Mining options Miner miner.Config @@ -179,11 +191,11 @@ type Config struct { EVMInterpreter string // RPCGasCap is the global gas cap for eth-call variants. - RPCGasCap uint64 `toml:",omitempty"` + RPCGasCap uint64 // RPCTxFeeCap is the global transaction fee(price * gaslimit) cap for // send-transction variants. The unit is ether. - RPCTxFeeCap float64 `toml:",omitempty"` + RPCTxFeeCap float64 // Checkpoint is a hardcoded checkpoint which can be nil. Checkpoint *ctypes.TrustedCheckpoint `toml:",omitempty"` @@ -197,4 +209,49 @@ type Config struct { // ECBP1100NoDisable overrides // When this value is *true, ECBP100 will not (ever) be disabled; when *false, it will never be enabled. ECBP1100NoDisable *bool `toml:",omitempty"` + + // Magneto block override (TODO: remove after the fork) + OverrideMagneto *big.Int `toml:",omitempty"` +} + +// CreateConsensusEngine creates a consensus engine for the given chain configuration. +func CreateConsensusEngine(stack *node.Node, chainConfig ctypes.ChainConfigurator, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine { + // If proof-of-authority is requested, set it up + if chainConfig.GetConsensusEngineType().IsClique() { + return clique.New(&ctypes.CliqueConfig{ + Period: chainConfig.GetCliquePeriod(), + Epoch: chainConfig.GetCliqueEpoch(), + }, db) + } + // Otherwise assume proof-of-work + switch config.PowMode { + case ethash.ModeFake: + log.Warn("Ethash used in fake mode") + return ethash.NewFaker() + case ethash.ModeTest: + log.Warn("Ethash used in test mode") + return ethash.NewTester(nil, noverify) + case ethash.ModeShared: + log.Warn("Ethash used in shared mode") + return ethash.NewShared() + case ethash.ModePoissonFake: + log.Warn("Ethash used in fake Poisson mode") + return ethash.NewPoissonFaker() + default: + engine := ethash.New(ethash.Config{ + PowMode: config.PowMode, + CacheDir: stack.ResolvePath(config.CacheDir), + CachesInMem: config.CachesInMem, + CachesOnDisk: config.CachesOnDisk, + CachesLockMmap: config.CachesLockMmap, + DatasetDir: config.DatasetDir, + DatasetsInMem: config.DatasetsInMem, + DatasetsOnDisk: config.DatasetsOnDisk, + DatasetsLockMmap: config.DatasetsLockMmap, + NotifyFull: config.NotifyFull, + ECIP1099Block: chainConfig.GetEthashECIP1099Transition(), + }, notify, noverify) + engine.SetThreads(-1) // Disable CPU mining + return engine + } } diff --git a/eth/gen_config.go b/eth/ethconfig/gen_config.go similarity index 88% rename from eth/gen_config.go rename to eth/ethconfig/gen_config.go index 5c5c4f6e54..72cbe45d17 100644 --- a/eth/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -1,6 +1,6 @@ // Code generated by github.com/fjl/gencodec. DO NOT EDIT. -package eth +package ethconfig import ( "time" @@ -21,7 +21,8 @@ func (c Config) MarshalTOML() (interface{}, error) { Genesis *genesisT.Genesis `toml:",omitempty"` NetworkId uint64 SyncMode downloader.SyncMode - DiscoveryURLs []string + EthDiscoveryURLs []string + SnapDiscoveryURLs []string NoPruning bool NoPrefetch bool TxLookupLimit uint64 `toml:",omitempty"` @@ -31,6 +32,8 @@ func (c Config) MarshalTOML() (interface{}, error) { LightEgress int `toml:",omitempty"` LightPeers int `toml:",omitempty"` LightNoPrune bool `toml:",omitempty"` + LightNoSyncServe bool `toml:",omitempty"` + SyncFromCheckpoint bool `toml:",omitempty"` UltraLightServers []string `toml:",omitempty"` UltraLightFraction int `toml:",omitempty"` UltraLightOnlyAnnounce bool `toml:",omitempty"` @@ -44,6 +47,7 @@ func (c Config) MarshalTOML() (interface{}, error) { TrieDirtyCache int TrieTimeout time.Duration SnapshotCache int + Preimages bool Miner miner.Config Ethash ethash.Config TxPool core.TxPoolConfig @@ -61,7 +65,8 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.Genesis = c.Genesis enc.NetworkId = c.NetworkId enc.SyncMode = c.SyncMode - enc.DiscoveryURLs = c.DiscoveryURLs + enc.EthDiscoveryURLs = c.EthDiscoveryURLs + enc.SnapDiscoveryURLs = c.SnapDiscoveryURLs enc.NoPruning = c.NoPruning enc.NoPrefetch = c.NoPrefetch enc.TxLookupLimit = c.TxLookupLimit @@ -71,6 +76,8 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.LightEgress = c.LightEgress enc.LightPeers = c.LightPeers enc.LightNoPrune = c.LightNoPrune + enc.LightNoSyncServe = c.LightNoSyncServe + enc.SyncFromCheckpoint = c.SyncFromCheckpoint enc.UltraLightServers = c.UltraLightServers enc.UltraLightFraction = c.UltraLightFraction enc.UltraLightOnlyAnnounce = c.UltraLightOnlyAnnounce @@ -84,6 +91,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.TrieDirtyCache = c.TrieDirtyCache enc.TrieTimeout = c.TrieTimeout enc.SnapshotCache = c.SnapshotCache + enc.Preimages = c.Preimages enc.Miner = c.Miner enc.Ethash = c.Ethash enc.TxPool = c.TxPool @@ -105,7 +113,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { Genesis *genesisT.Genesis `toml:",omitempty"` NetworkId *uint64 SyncMode *downloader.SyncMode - DiscoveryURLs []string + EthDiscoveryURLs []string + SnapDiscoveryURLs []string NoPruning *bool NoPrefetch *bool TxLookupLimit *uint64 `toml:",omitempty"` @@ -115,6 +124,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { LightEgress *int `toml:",omitempty"` LightPeers *int `toml:",omitempty"` LightNoPrune *bool `toml:",omitempty"` + LightNoSyncServe *bool `toml:",omitempty"` + SyncFromCheckpoint *bool `toml:",omitempty"` UltraLightServers []string `toml:",omitempty"` UltraLightFraction *int `toml:",omitempty"` UltraLightOnlyAnnounce *bool `toml:",omitempty"` @@ -128,6 +139,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { TrieDirtyCache *int TrieTimeout *time.Duration SnapshotCache *int + Preimages *bool Miner *miner.Config Ethash *ethash.Config TxPool *core.TxPoolConfig @@ -154,8 +166,11 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.SyncMode != nil { c.SyncMode = *dec.SyncMode } - if dec.DiscoveryURLs != nil { - c.DiscoveryURLs = dec.DiscoveryURLs + if dec.EthDiscoveryURLs != nil { + c.EthDiscoveryURLs = dec.EthDiscoveryURLs + } + if dec.SnapDiscoveryURLs != nil { + c.SnapDiscoveryURLs = dec.SnapDiscoveryURLs } if dec.NoPruning != nil { c.NoPruning = *dec.NoPruning @@ -184,6 +199,12 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.LightNoPrune != nil { c.LightNoPrune = *dec.LightNoPrune } + if dec.LightNoSyncServe != nil { + c.LightNoSyncServe = *dec.LightNoSyncServe + } + if dec.SyncFromCheckpoint != nil { + c.SyncFromCheckpoint = *dec.SyncFromCheckpoint + } if dec.UltraLightServers != nil { c.UltraLightServers = dec.UltraLightServers } @@ -223,6 +244,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.SnapshotCache != nil { c.SnapshotCache = *dec.SnapshotCache } + if dec.Preimages != nil { + c.Preimages = *dec.Preimages + } if dec.Miner != nil { c.Miner = *dec.Miner } diff --git a/eth/fetcher/block_fetcher.go b/eth/fetcher/block_fetcher.go index 270aaf5918..5ea8a128d9 100644 --- a/eth/fetcher/block_fetcher.go +++ b/eth/fetcher/block_fetcher.go @@ -620,7 +620,7 @@ func (f *BlockFetcher) loop() { continue } if txnHash == (common.Hash{}) { - txnHash = types.DeriveSha(types.Transactions(task.transactions[i]), new(trie.Trie)) + txnHash = types.DeriveSha(types.Transactions(task.transactions[i]), trie.NewStackTrie(nil)) } if txnHash != announce.header.TxHash { continue diff --git a/eth/fetcher/block_fetcher_test.go b/eth/fetcher/block_fetcher_test.go index b19db24083..d18e2c5768 100644 --- a/eth/fetcher/block_fetcher_test.go +++ b/eth/fetcher/block_fetcher_test.go @@ -40,7 +40,7 @@ var ( testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") testAddress = crypto.PubkeyToAddress(testKey.PublicKey) genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000)) - unknownBlock = types.NewBlock(&types.Header{GasLimit: vars.GenesisGasLimit}, nil, nil, nil, new(trie.Trie)) + unknownBlock = types.NewBlock(&types.Header{GasLimit: vars.GenesisGasLimit}, nil, nil, nil, trie.NewStackTrie(nil)) ) // makeChain creates a chain of n blocks starting at and including parent. diff --git a/eth/filters/api.go b/eth/filters/api.go index 4e0ab1bbcd..ceaadfc9d9 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -25,7 +25,7 @@ import ( "sync" "time" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" @@ -34,10 +34,6 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) -var ( - deadline = 5 * time.Minute // consider a filter inactive if it has not been polled for within deadline -) - // filter is a helper struct that holds meta information over the filter type // and associated subscription in the event system. type filter struct { @@ -59,25 +55,28 @@ type PublicFilterAPI struct { events *EventSystem filtersMu sync.Mutex filters map[rpc.ID]*filter + timeout time.Duration } // NewPublicFilterAPI returns a new PublicFilterAPI instance. -func NewPublicFilterAPI(backend Backend, lightMode bool) *PublicFilterAPI { +func NewPublicFilterAPI(backend Backend, lightMode bool, timeout time.Duration) *PublicFilterAPI { api := &PublicFilterAPI{ backend: backend, chainDb: backend.ChainDb(), events: NewEventSystem(backend, lightMode), filters: make(map[rpc.ID]*filter), + timeout: timeout, } - go api.timeoutLoop() + go api.timeoutLoop(timeout) return api } // timeoutLoop runs every 5 minutes and deletes filters that have not been recently used. // Tt is started when the api is created. -func (api *PublicFilterAPI) timeoutLoop() { - ticker := time.NewTicker(5 * time.Minute) +func (api *PublicFilterAPI) timeoutLoop(timeout time.Duration) { + var toUninstall []*Subscription + ticker := time.NewTicker(timeout) defer ticker.Stop() for { <-ticker.C @@ -85,13 +84,21 @@ func (api *PublicFilterAPI) timeoutLoop() { for id, f := range api.filters { select { case <-f.deadline.C: - f.s.Unsubscribe() + toUninstall = append(toUninstall, f.s) delete(api.filters, id) default: continue } } api.filtersMu.Unlock() + + // Unsubscribes are processed outside the lock to avoid the following scenario: + // event loop attempts broadcasting events to still active filters while + // Unsubscribe is waiting for it to process the uninstall request. + for _, s := range toUninstall { + s.Unsubscribe() + } + toUninstall = nil } } @@ -101,7 +108,7 @@ func (api *PublicFilterAPI) timeoutLoop() { // It is part of the filter package because this filter can be used through the // `eth_getFilterChanges` polling method that is also used for log filters. // -// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter +// https://eth.wiki/json-rpc/API#eth_newpendingtransactionfilter func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID { var ( pendingTxs = make(chan []common.Hash) @@ -109,7 +116,7 @@ func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID { ) api.filtersMu.Lock() - api.filters[pendingTxSub.ID] = &filter{typ: PendingTransactionsSubscription, deadline: time.NewTimer(deadline), hashes: make([]common.Hash, 0), s: pendingTxSub} + api.filters[pendingTxSub.ID] = &filter{typ: PendingTransactionsSubscription, deadline: time.NewTimer(api.timeout), hashes: make([]common.Hash, 0), s: pendingTxSub} api.filtersMu.Unlock() go func() { @@ -171,7 +178,7 @@ func (api *PublicFilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Su // NewBlockFilter creates a filter that fetches blocks that are imported into the chain. // It is part of the filter package since polling goes with eth_getFilterChanges. // -// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newblockfilter +// https://eth.wiki/json-rpc/API#eth_newblockfilter func (api *PublicFilterAPI) NewBlockFilter() rpc.ID { var ( headers = make(chan *types.Header) @@ -179,7 +186,7 @@ func (api *PublicFilterAPI) NewBlockFilter() rpc.ID { ) api.filtersMu.Lock() - api.filters[headerSub.ID] = &filter{typ: BlocksSubscription, deadline: time.NewTimer(deadline), hashes: make([]common.Hash, 0), s: headerSub} + api.filters[headerSub.ID] = &filter{typ: BlocksSubscription, deadline: time.NewTimer(api.timeout), hashes: make([]common.Hash, 0), s: headerSub} api.filtersMu.Unlock() go func() { @@ -212,7 +219,7 @@ func (api *PublicFilterAPI) NewSideBlockFilter() rpc.ID { ) api.filtersMu.Lock() - api.filters[headerSub.ID] = &filter{typ: SideBlocksSubscription, deadline: time.NewTimer(deadline), hashes: make([]common.Hash, 0), s: headerSub} + api.filters[headerSub.ID] = &filter{typ: SideBlocksSubscription, deadline: time.NewTimer(api.timeout), hashes: make([]common.Hash, 0), s: headerSub} api.filtersMu.Unlock() go func() { @@ -350,16 +357,16 @@ type FilterCriteria ethereum.FilterQuery // // In case "fromBlock" > "toBlock" an error is returned. // -// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter +// https://eth.wiki/json-rpc/API#eth_newfilter func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) { logs := make(chan []*types.Log) logsSub, err := api.events.SubscribeLogs(ethereum.FilterQuery(crit), logs) if err != nil { - return rpc.ID(""), err + return "", err } api.filtersMu.Lock() - api.filters[logsSub.ID] = &filter{typ: LogsSubscription, crit: crit, deadline: time.NewTimer(deadline), logs: make([]*types.Log, 0), s: logsSub} + api.filters[logsSub.ID] = &filter{typ: LogsSubscription, crit: crit, deadline: time.NewTimer(api.timeout), logs: make([]*types.Log, 0), s: logsSub} api.filtersMu.Unlock() go func() { @@ -385,7 +392,7 @@ func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) { // GetLogs returns logs matching the given argument that are stored within the state. // -// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs +// https://eth.wiki/json-rpc/API#eth_getlogs func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*types.Log, error) { var filter *Filter if crit.BlockHash != nil { @@ -414,7 +421,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([ // UninstallFilter removes the filter with the given filter id. // -// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_uninstallfilter +// https://eth.wiki/json-rpc/API#eth_uninstallfilter func (api *PublicFilterAPI) UninstallFilter(id rpc.ID) bool { api.filtersMu.Lock() f, found := api.filters[id] @@ -432,7 +439,7 @@ func (api *PublicFilterAPI) UninstallFilter(id rpc.ID) bool { // GetFilterLogs returns the logs for the filter with the given id. // If the filter could not be found an empty array of logs is returned. // -// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs +// https://eth.wiki/json-rpc/API#eth_getfilterlogs func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*types.Log, error) { api.filtersMu.Lock() f, found := api.filters[id] @@ -473,7 +480,7 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*ty // For pending transaction and block filters the result is []common.Hash. // (pending)Log filters return []Log. // -// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges +// https://eth.wiki/json-rpc/API#eth_getfilterchanges func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) { api.filtersMu.Lock() defer api.filtersMu.Unlock() @@ -484,7 +491,7 @@ func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) { // receive timer value and reset timer <-f.deadline.C } - f.deadline.Reset(deadline) + f.deadline.Reset(api.timeout) switch f.typ { case PendingTransactionsSubscription, BlocksSubscription, SideBlocksSubscription: diff --git a/eth/filters/bench_test.go b/eth/filters/bench_test.go index 9fdaaf2255..e2bbc6f1dc 100644 --- a/eth/filters/bench_test.go +++ b/eth/filters/bench_test.go @@ -65,7 +65,7 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) { benchDataDir := vars.DefaultDataDir() + "/geth/chaindata" b.Log("Running bloombits benchmark section size:", sectionSize) - db, err := rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "") + db, err := rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false) if err != nil { b.Fatalf("error opening database at %v: %v", benchDataDir, err) } @@ -126,7 +126,7 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) { for i := 0; i < benchFilterCnt; i++ { if i%20 == 0 { db.Close() - db, _ = rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "") + db, _ = rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false) backend = &testBackend{db: db, sections: cnt} } var addr common.Address @@ -157,7 +157,7 @@ func clearBloomBits(db ethdb.Database) { func BenchmarkNoBloomBits(b *testing.B) { benchDataDir := vars.DefaultDataDir() + "/geth/chaindata" b.Log("Running benchmark without bloombits") - db, err := rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "") + db, err := rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false) if err != nil { b.Fatalf("error opening database at %v: %v", benchDataDir, err) } diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index bc29323f07..6fed5bf1af 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -24,7 +24,7 @@ import ( "sync" "time" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 732744cb81..730adceb6d 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -22,10 +22,11 @@ import ( "math/big" "math/rand" "reflect" + "runtime" "testing" "time" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" @@ -40,6 +41,10 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) +var ( + deadline = 5 * time.Minute +) + type testBackend struct { mux *event.TypeMux db ethdb.Database @@ -170,7 +175,7 @@ func TestBlockSubscription(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, deadline) genesis = core.MustCommitGenesis(db, new(genesisT.Genesis)) chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {}) chainEvents = []core.ChainEvent{} @@ -226,7 +231,7 @@ func TestSideBlockSubscription(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, deadline) genesis = core.MustCommitGenesis(db, new(genesisT.Genesis)) chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {}) chainSideEvents = []core.ChainSideEvent{} @@ -278,7 +283,7 @@ func TestPendingTxFilter(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, deadline) transactions = []*types.Transaction{ types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), @@ -333,7 +338,7 @@ func TestLogFilterCreation(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, deadline) testCases = []struct { crit FilterCriteria @@ -377,7 +382,7 @@ func TestInvalidLogFilterCreation(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, deadline) ) // different situations where log filter creation should fail. @@ -399,7 +404,7 @@ func TestInvalidGetLogsRequest(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, deadline) blockHash = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") ) @@ -424,7 +429,7 @@ func TestLogFilter(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, deadline) firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") @@ -538,7 +543,7 @@ func TestPendingLogsSubscription(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, false, deadline) firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") @@ -664,6 +669,73 @@ func TestPendingLogsSubscription(t *testing.T) { } } +// TestPendingTxFilterDeadlock tests if the event loop hangs when pending +// txes arrive at the same time that one of multiple filters is timing out. +// Please refer to #22131 for more details. +func TestPendingTxFilterDeadlock(t *testing.T) { + t.Parallel() + timeout := 100 * time.Millisecond + + var ( + db = rawdb.NewMemoryDatabase() + backend = &testBackend{db: db} + api = NewPublicFilterAPI(backend, false, timeout) + done = make(chan struct{}) + ) + + go func() { + // Bombard feed with txes until signal was received to stop + i := uint64(0) + for { + select { + case <-done: + return + default: + } + + tx := types.NewTransaction(i, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil) + backend.txFeed.Send(core.NewTxsEvent{Txs: []*types.Transaction{tx}}) + i++ + } + }() + + // Create a bunch of filters that will + // timeout either in 100ms or 200ms + fids := make([]rpc.ID, 20) + for i := 0; i < len(fids); i++ { + fid := api.NewPendingTransactionFilter() + fids[i] = fid + // Wait for at least one tx to arrive in filter + for { + hashes, err := api.GetFilterChanges(fid) + if err != nil { + t.Fatalf("Filter should exist: %v\n", err) + } + if len(hashes.([]common.Hash)) > 0 { + break + } + runtime.Gosched() + } + } + + // Wait until filters have timed out + time.Sleep(3 * timeout) + + // If tx loop doesn't consume `done` after a second + // it's hanging. + select { + case done <- struct{}{}: + // Check that all filters have been uninstalled + for _, fid := range fids { + if _, err := api.GetFilterChanges(fid); err == nil { + t.Errorf("Filter %s should have been uninstalled\n", fid) + } + } + case <-time.After(1 * time.Second): + t.Error("Tx sending loop hangs") + } +} + func flattenLogs(pl [][]*types.Log) []*types.Log { var logs []*types.Log for _, l := range pl { diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index f45720d5a9..3fc77bbc4d 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -49,7 +49,7 @@ func BenchmarkFilters(b *testing.B) { defer os.RemoveAll(dir) var ( - db, _ = rawdb.NewLevelDBDatabase(dir, 0, 0, "") + db, _ = rawdb.NewLevelDBDatabase(dir, 0, 0, "", false) backend = &testBackend{db: db} key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr1 = crypto.PubkeyToAddress(key1.PublicKey) @@ -103,7 +103,7 @@ func TestFilters(t *testing.T) { defer os.RemoveAll(dir) var ( - db, _ = rawdb.NewLevelDBDatabase(dir, 0, 0, "") + db, _ = rawdb.NewLevelDBDatabase(dir, 0, 0, "", false) backend = &testBackend{db: db} key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr = crypto.PubkeyToAddress(key1.PublicKey) diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index a1741fbdab..b7ae1f0927 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -66,7 +66,7 @@ func newTestBackend(t *testing.T) *testBackend { Config: params.TestChainConfig, Alloc: genesisT.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}}, } - signer = types.NewEIP155Signer(gspec.Config.GetChainID()) + signer = types.LatestSigner(gspec.Config) ) engine := ethash.NewFaker() db := rawdb.NewMemoryDatabase() diff --git a/eth/handler.go b/eth/handler.go index 132d7d98da..26e3e00121 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -17,9 +17,7 @@ package eth import ( - "encoding/json" "errors" - "fmt" "math" "math/big" "sync" @@ -27,27 +25,23 @@ 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/forkid" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/fetcher" + "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params/types/ctypes" "github.com/ethereum/go-ethereum/params/vars" - "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" ) const ( - softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data. - estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header - // txChanSize is the size of channel listening to NewTxsEvent. // The number is referenced from the size of tx pool. txChanSize = 4096 @@ -57,26 +51,65 @@ var ( syncChallengeTimeout = 15 * time.Second // Time allowance for a node to reply to the sync progress challenge ) -func errResp(code errCode, format string, v ...interface{}) error { - return fmt.Errorf("%v - %v", code, fmt.Sprintf(format, v...)) +// DefaultProtocolVersions are the supported versions of the `eth` protocol (first +// is primary). +var DefaultProtocolVersions = []uint{eth.ETH66, eth.ETH65, eth.ETH64} + +// txPool defines the methods needed from a transaction pool implementation to +// support all the operations needed by the Ethereum chain protocols. +type txPool interface { + // Has returns an indicator whether txpool has a transaction + // cached with the given hash. + Has(hash common.Hash) bool + + // Get retrieves the transaction from local txpool with given + // tx hash. + Get(hash common.Hash) *types.Transaction + + // AddRemotes should add the given transactions to the pool. + AddRemotes([]*types.Transaction) []error + + // Pending should return pending transactions. + // The slice should be modifiable by the caller. + Pending() (map[common.Address]types.Transactions, error) + + // SubscribeNewTxsEvent should return an event subscription of + // NewTxsEvent and send events to the given channel. + SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription } -type ProtocolManager struct { +// handlerConfig is the collection of initialization parameters to create a full +// node network handler. +type handlerConfig struct { + Database ethdb.Database // Database for direct sync insertions + Chain *core.BlockChain // Blockchain to serve data from + TxPool txPool // Transaction pool to propagate from + Network uint64 // Network identifier to adfvertise + Sync downloader.SyncMode // Whether to fast or full sync + BloomCache uint64 // Megabytes to alloc for fast sync bloom + EventMux *event.TypeMux // Legacy event mux, deprecate for `feed` + Checkpoint *ctypes.TrustedCheckpoint // Hard coded checkpoint for sync challenges + Whitelist map[uint64]common.Hash // Hard coded whitelist for sync challenged +} + +type handler struct { networkID uint64 forkFilter forkid.Filter // Fork ID filter, constant across the lifetime of the node fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks) + snapSync uint32 // Flag whether fast sync should operate on top of the snap protocol acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing) checkpointNumber uint64 // Block number for the sync progress validator to cross reference checkpointHash common.Hash // Block hash for the sync progress validator to cross reference - txpool txPool - blockchain *core.BlockChain - chaindb ethdb.Database - maxPeers int + database ethdb.Database + txpool txPool + chain *core.BlockChain + maxPeers int downloader *downloader.Downloader + stateBloom *trie.SyncBloom blockFetcher *fetcher.BlockFetcher txFetcher *fetcher.TxFetcher peers *peerSet @@ -95,29 +128,27 @@ type ProtocolManager struct { chainSync *chainSyncer wg sync.WaitGroup peerWG sync.WaitGroup - - // Test fields or hooks - broadcastTxAnnouncesOnly bool // Testing field, disable transaction propagation } -// NewProtocolManager returns a new Ethereum sub protocol manager. The Ethereum sub protocol manages peers capable -// with the Ethereum network. -func NewProtocolManager(config ctypes.ChainConfigurator, checkpoint *ctypes.TrustedCheckpoint, mode downloader.SyncMode, networkID uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database, cacheLimit int, whitelist map[uint64]common.Hash) (*ProtocolManager, error) { +// newHandler returns a handler for all Ethereum chain management protocol. +func newHandler(config *handlerConfig) (*handler, error) { // Create the protocol manager with the base fields - manager := &ProtocolManager{ - networkID: networkID, - forkFilter: forkid.NewFilter(blockchain), - eventMux: mux, - txpool: txpool, - blockchain: blockchain, - chaindb: chaindb, + if config.EventMux == nil { + config.EventMux = new(event.TypeMux) // Nicety initialization for tests + } + h := &handler{ + networkID: config.Network, + forkFilter: forkid.NewFilter(config.Chain), + eventMux: config.EventMux, + database: config.Database, + txpool: config.TxPool, + chain: config.Chain, peers: newPeerSet(), - whitelist: whitelist, + whitelist: config.Whitelist, txsyncCh: make(chan *txsync), quitSync: make(chan struct{}), } - - if mode == downloader.FullSync { + if config.Sync == downloader.FullSync { // The database seems empty as the current block is the genesis. Yet the fast // block is ahead, so fast sync was enabled for this node at a certain point. // The scenarios where this can happen is @@ -126,42 +157,46 @@ func NewProtocolManager(config ctypes.ChainConfigurator, checkpoint *ctypes.Trus // * the last fast sync is not finished while user specifies a full sync this // time. But we don't have any recent state for full sync. // In these cases however it's safe to reenable fast sync. - fullBlock, fastBlock := blockchain.CurrentBlock(), blockchain.CurrentFastBlock() + fullBlock, fastBlock := h.chain.CurrentBlock(), h.chain.CurrentFastBlock() if fullBlock.NumberU64() == 0 && fastBlock.NumberU64() > 0 { - manager.fastSync = uint32(1) + h.fastSync = uint32(1) log.Warn("Switch sync mode from full sync to fast sync") } } else { - if blockchain.CurrentBlock().NumberU64() > 0 { + if h.chain.CurrentBlock().NumberU64() > 0 { // Print warning log if database is not empty to run fast sync. log.Warn("Switch sync mode from fast sync to full sync") } else { // If fast sync was requested and our database is empty, grant it - manager.fastSync = uint32(1) + h.fastSync = uint32(1) + if config.Sync == downloader.SnapSync { + h.snapSync = uint32(1) + } } } - // If we have trusted checkpoints, enforce them on the chain - if checkpoint != nil { - manager.checkpointNumber = (checkpoint.SectionIndex+1)*vars.CHTFrequency - 1 - manager.checkpointHash = checkpoint.SectionHead + if config.Checkpoint != nil { + h.checkpointNumber = (config.Checkpoint.SectionIndex+1)*vars.CHTFrequency - 1 + h.checkpointHash = config.Checkpoint.SectionHead } - // Construct the downloader (long sync) and its backing state bloom if fast // sync is requested. The downloader is responsible for deallocating the state // bloom when it's done. - var stateBloom *trie.SyncBloom - if atomic.LoadUint32(&manager.fastSync) == 1 { - stateBloom = trie.NewSyncBloom(uint64(cacheLimit), chaindb) + // Note: we don't enable it if snap-sync is performed, since it's very heavy + // and the heal-portion of the snap sync is much lighter than fast. What we particularly + // want to avoid, is a 90%-finished (but restarted) snap-sync to begin + // indexing the entire trie + if atomic.LoadUint32(&h.fastSync) == 1 && atomic.LoadUint32(&h.snapSync) == 0 { + h.stateBloom = trie.NewSyncBloom(config.BloomCache, config.Database) } - manager.downloader = downloader.New(manager.checkpointNumber, chaindb, stateBloom, manager.eventMux, blockchain, nil, manager.removePeer) + h.downloader = downloader.New(h.checkpointNumber, config.Database, h.stateBloom, h.eventMux, h.chain, nil, h.removePeer) // Construct the fetcher (short sync) validator := func(header *types.Header) error { - return engine.VerifyHeader(blockchain, header, true) + return h.chain.Engine().VerifyHeader(h.chain, header, true) } heighter := func() uint64 { - return blockchain.CurrentBlock().NumberU64() + return h.chain.CurrentBlock().NumberU64() } inserter := func(blocks types.Blocks) (int, error) { // If sync hasn't reached the checkpoint yet, deny importing weird blocks. @@ -170,7 +205,7 @@ func NewProtocolManager(config ctypes.ChainConfigurator, checkpoint *ctypes.Trus // the propagated block if the head is too old. Unfortunately there is a corner // case when starting new networks, where the genesis might be ancient (0 unix) // which would prevent full nodes from accepting it. - if manager.blockchain.CurrentBlock().NumberU64() < manager.checkpointNumber { + if h.chain.CurrentBlock().NumberU64() < h.checkpointNumber { log.Warn("Unsynced yet, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash()) return 0, nil } @@ -179,182 +214,117 @@ func NewProtocolManager(config ctypes.ChainConfigurator, checkpoint *ctypes.Trus // accept each others' blocks until a restart. Unfortunately we haven't figured // out a way yet where nodes can decide unilaterally whether the network is new // or not. This should be fixed if we figure out a solution. - if atomic.LoadUint32(&manager.fastSync) == 1 { + if atomic.LoadUint32(&h.fastSync) == 1 { log.Warn("Fast syncing, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash()) return 0, nil } - n, err := manager.blockchain.InsertChain(blocks) + n, err := h.chain.InsertChain(blocks) if err == nil { - atomic.StoreUint32(&manager.acceptTxs, 1) // Mark initial sync done on any fetcher import + atomic.StoreUint32(&h.acceptTxs, 1) // Mark initial sync done on any fetcher import } return n, err } - manager.blockFetcher = fetcher.NewBlockFetcher(false, nil, blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, nil, inserter, manager.removePeer) + h.blockFetcher = fetcher.NewBlockFetcher(false, nil, h.chain.GetBlockByHash, validator, h.BroadcastBlock, heighter, nil, inserter, h.removePeer) fetchTx := func(peer string, hashes []common.Hash) error { - p := manager.peers.Peer(peer) + p := h.peers.peer(peer) if p == nil { return errors.New("unknown peer") } return p.RequestTxs(hashes) } - manager.txFetcher = fetcher.NewTxFetcher(txpool.Has, txpool.AddRemotes, fetchTx) - - manager.chainSync = newChainSyncer(manager) - - return manager, nil -} - -func (pm *ProtocolManager) makeProtocol(version uint) p2p.Protocol { - length, ok := protocolLengths[version] - if !ok { - panic("makeProtocol for unknown version") - } - - return p2p.Protocol{ - Name: protocolName, - Version: version, - Length: length, - Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { - return pm.runPeer(pm.newPeer(int(version), p, rw, pm.txpool.Get)) - }, - NodeInfo: func() interface{} { - return pm.NodeInfo() - }, - PeerInfo: func(id enode.ID) interface{} { - if p := pm.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil { - return p.Info() - } - return nil - }, - } + h.txFetcher = fetcher.NewTxFetcher(h.txpool.Has, h.txpool.AddRemotes, fetchTx) + h.chainSync = newChainSyncer(h) + return h, nil } -func (pm *ProtocolManager) removePeer(id string) { - // Short circuit if the peer was already removed - peer := pm.peers.Peer(id) - if peer == nil { - return - } - log.Debug("Removing Ethereum peer", "peer", id) - - // Unregister the peer from the downloader and Ethereum peer set - pm.downloader.UnregisterPeer(id) - pm.txFetcher.Drop(id) - - if err := pm.peers.Unregister(id); err != nil { - log.Error("Peer removal failed", "peer", id, "err", err) +// runEthPeer registers an eth peer into the joint eth/snap peerset, adds it to +// various subsistems and starts handling messages. +func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error { + // If the peer has a `snap` extension, wait for it to connect so we can have + // a uniform initialization/teardown mechanism + snap, err := h.peers.waitSnapExtension(peer) + if err != nil { + peer.Log().Error("Snapshot extension barrier failed", "err", err) + return err } - // Hard disconnect at the networking layer - peer.Peer.Disconnect(p2p.DiscUselessPeer) -} - -func (pm *ProtocolManager) Start(maxPeers int) { - pm.maxPeers = maxPeers - - // broadcast transactions - pm.wg.Add(1) - pm.txsCh = make(chan core.NewTxsEvent, txChanSize) - pm.txsSub = pm.txpool.SubscribeNewTxsEvent(pm.txsCh) - go pm.txBroadcastLoop() - - // broadcast mined blocks - pm.wg.Add(1) - pm.minedBlockSub = pm.eventMux.Subscribe(core.NewMinedBlockEvent{}) - go pm.minedBroadcastLoop() - - // start sync handlers - pm.wg.Add(2) - go pm.chainSync.loop() - go pm.txsyncLoop64() // TODO(karalabe): Legacy initial tx echange, drop with eth/64. - - // start artificial finality safety loop - pm.wg.Add(1) - go pm.artificialFinalitySafetyLoop() -} - -func (pm *ProtocolManager) Stop() { - pm.txsSub.Unsubscribe() // quits txBroadcastLoop - pm.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop - - // Quit chainSync and txsync64. - // After this is done, no new peers will be accepted. - close(pm.quitSync) - pm.wg.Wait() - - // Disconnect existing sessions. - // This also closes the gate for any new registrations on the peer set. - // sessions which are already established but not added to pm.peers yet - // will exit when they try to register. - pm.peers.Close() - pm.peerWG.Wait() - - log.Info("Ethereum protocol stopped") -} - -func (pm *ProtocolManager) newPeer(pv int, p *p2p.Peer, rw p2p.MsgReadWriter, getPooledTx func(hash common.Hash) *types.Transaction) *peer { - return newPeer(pv, p, rw, getPooledTx) -} - -func (pm *ProtocolManager) runPeer(p *peer) error { - if !pm.chainSync.handlePeerEvent(p) { + // TODO(karalabe): Not sure why this is needed + if !h.chainSync.handlePeerEvent(peer) { return p2p.DiscQuitting } - pm.peerWG.Add(1) - defer pm.peerWG.Done() - return pm.handle(p) -} - -// handle is the callback invoked to manage the life cycle of an eth peer. When -// this function terminates, the peer is disconnected. -func (pm *ProtocolManager) handle(p *peer) error { - // Ignore maxPeers if this is a trusted peer - if pm.peers.Len() >= pm.maxPeers && !p.Peer.Info().Network.Trusted { - return p2p.DiscTooManyPeers - } - p.Log().Debug("Ethereum peer connected", "name", p.Name()) + h.peerWG.Add(1) + defer h.peerWG.Done() // Execute the Ethereum handshake var ( - genesis = pm.blockchain.Genesis() - head = pm.blockchain.CurrentHeader() + genesis = h.chain.Genesis() + head = h.chain.CurrentHeader() hash = head.Hash() number = head.Number.Uint64() - td = pm.blockchain.GetTd(hash, number) + td = h.chain.GetTd(hash, number) ) - forkID := forkid.NewID(pm.blockchain.Config(), pm.blockchain.Genesis().Hash(), pm.blockchain.CurrentHeader().Number.Uint64()) - if err := p.Handshake(pm.networkID, td, hash, genesis.Hash(), forkID, pm.forkFilter); err != nil { - p.Log().Debug("Ethereum handshake failed", "err", err) + forkID := forkid.NewID(h.chain.Config(), h.chain.Genesis().Hash(), h.chain.CurrentHeader().Number.Uint64()) + if err := peer.Handshake(h.networkID, td, hash, genesis.Hash(), forkID, h.forkFilter); err != nil { + peer.Log().Debug("Ethereum handshake failed", "err", err) return err } + reject := false // reserved peer slots + if atomic.LoadUint32(&h.snapSync) == 1 { + if snap == nil { + // If we are running snap-sync, we want to reserve roughly half the peer + // slots for peers supporting the snap protocol. + // The logic here is; we only allow up to 5 more non-snap peers than snap-peers. + if all, snp := h.peers.len(), h.peers.snapLen(); all-snp > snp+5 { + reject = true + } + } + } + // Ignore maxPeers if this is a trusted peer + if !peer.Peer.Info().Network.Trusted { + if reject || h.peers.len() >= h.maxPeers { + return p2p.DiscTooManyPeers + } + } + peer.Log().Debug("Ethereum peer connected", "name", peer.Name()) // Register the peer locally - if err := pm.peers.Register(p, pm.removePeer); err != nil { - p.Log().Error("Ethereum peer registration failed", "err", err) + if err := h.peers.registerPeer(peer, snap); err != nil { + peer.Log().Error("Ethereum peer registration failed", "err", err) return err } - defer pm.removePeer(p.id) + defer h.removePeer(peer.ID()) + p := h.peers.peer(peer.ID()) + if p == nil { + return errors.New("peer dropped during handling") + } // Register the peer in the downloader. If the downloader considers it banned, we disconnect - if err := pm.downloader.RegisterPeer(p.id, p.version, p); err != nil { + if err := h.downloader.RegisterPeer(peer.ID(), peer.Version(), peer); err != nil { + peer.Log().Error("Failed to register peer in eth syncer", "err", err) return err } - pm.chainSync.handlePeerEvent(p) + if snap != nil { + if err := h.downloader.SnapSyncer.Register(snap); err != nil { + peer.Log().Error("Failed to register peer in snap syncer", "err", err) + return err + } + } + h.chainSync.handlePeerEvent(peer) // Propagate existing transactions. new transactions appearing // after this will be sent via broadcasts. - pm.syncTransactions(p) + h.syncTransactions(peer) // If we have a trusted CHT, reject all peers below that (avoid fast sync eclipse) - if pm.checkpointHash != (common.Hash{}) { + if h.checkpointHash != (common.Hash{}) { // Request the peer's checkpoint header for chain height/weight validation - if err := p.RequestHeadersByNumber(pm.checkpointNumber, 1, 0, false); err != nil { + if err := peer.RequestHeadersByNumber(h.checkpointNumber, 1, 0, false); err != nil { return err } // Start a timer to disconnect if the peer doesn't reply in time p.syncDrop = time.AfterFunc(syncChallengeTimeout, func() { - p.Log().Warn("Checkpoint challenge timed out, dropping", "addr", p.RemoteAddr(), "type", p.Name()) - pm.removePeer(p.id) + peer.Log().Warn("Checkpoint challenge timed out, dropping", "addr", peer.RemoteAddr(), "type", peer.Name()) + h.removePeer(peer.ID()) }) // Make sure it's cleaned up if the peer dies off defer func() { @@ -365,474 +335,115 @@ func (pm *ProtocolManager) handle(p *peer) error { }() } // If we have any explicit whitelist block hashes, request them - for number := range pm.whitelist { - if err := p.RequestHeadersByNumber(number, 1, 0, false); err != nil { + for number := range h.whitelist { + if err := peer.RequestHeadersByNumber(number, 1, 0, false); err != nil { return err } } // Handle incoming messages until the connection is torn down - for { - if err := pm.handleMsg(p); err != nil { - p.Log().Debug("Ethereum message handling failed", "err", err) - return err - } - } + return handler(peer) } -// handleMsg is invoked whenever an inbound message is received from a remote -// peer. The remote connection is torn down upon returning any error. -func (pm *ProtocolManager) handleMsg(p *peer) error { - // Read the next message from the remote peer, and ensure it's fully consumed - msg, err := p.rw.ReadMsg() - if err != nil { +// runSnapExtension registers a `snap` peer into the joint eth/snap peerset and +// starts handling inbound messages. As `snap` is only a satellite protocol to +// `eth`, all subsystem registrations and lifecycle management will be done by +// the main `eth` handler to prevent strange races. +func (h *handler) runSnapExtension(peer *snap.Peer, handler snap.Handler) error { + h.peerWG.Add(1) + defer h.peerWG.Done() + + if err := h.peers.registerSnapExtension(peer); err != nil { + peer.Log().Error("Snapshot extension registration failed", "err", err) return err } - if msg.Size > protocolMaxMsgSize { - return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, protocolMaxMsgSize) - } - defer msg.Discard() - - // Handle the message depending on its contents - switch { - case msg.Code == StatusMsg: - // Status messages should never arrive after the handshake - return errResp(ErrExtraStatusMsg, "uncontrolled status message") - - // Block header query, collect the requested headers and reply - case msg.Code == GetBlockHeadersMsg: - // Decode the complex header query - var query getBlockHeadersData - if err := msg.Decode(&query); err != nil { - return errResp(ErrDecode, "%v: %v", msg, err) - } - hashMode := query.Origin.Hash != (common.Hash{}) - first := true - maxNonCanonical := uint64(100) - - // Gather headers until the fetch or network limits is reached - var ( - bytes common.StorageSize - headers []*types.Header - unknown bool - ) - for !unknown && len(headers) < int(query.Amount) && bytes < softResponseLimit && len(headers) < downloader.MaxHeaderFetch { - // Retrieve the next header satisfying the query - var origin *types.Header - if hashMode { - if first { - first = false - origin = pm.blockchain.GetHeaderByHash(query.Origin.Hash) - if origin != nil { - query.Origin.Number = origin.Number.Uint64() - } - } else { - origin = pm.blockchain.GetHeader(query.Origin.Hash, query.Origin.Number) - } - } else { - origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number) - } - if origin == nil { - break - } - headers = append(headers, origin) - bytes += estHeaderRlpSize - - // Advance to the next header of the query - switch { - case hashMode && query.Reverse: - // Hash based traversal towards the genesis block - ancestor := query.Skip + 1 - if ancestor == 0 { - unknown = true - } else { - query.Origin.Hash, query.Origin.Number = pm.blockchain.GetAncestor(query.Origin.Hash, query.Origin.Number, ancestor, &maxNonCanonical) - unknown = (query.Origin.Hash == common.Hash{}) - } - case hashMode && !query.Reverse: - // Hash based traversal towards the leaf block - var ( - current = origin.Number.Uint64() - next = current + query.Skip + 1 - ) - if next <= current { - infos, _ := json.MarshalIndent(p.Peer.Info(), "", " ") - p.Log().Warn("GetBlockHeaders skip overflow attack", "current", current, "skip", query.Skip, "next", next, "attacker", infos) - unknown = true - } else { - if header := pm.blockchain.GetHeaderByNumber(next); header != nil { - nextHash := header.Hash() - expOldHash, _ := pm.blockchain.GetAncestor(nextHash, next, query.Skip+1, &maxNonCanonical) - if expOldHash == query.Origin.Hash { - query.Origin.Hash, query.Origin.Number = nextHash, next - } else { - unknown = true - } - } else { - unknown = true - } - } - case query.Reverse: - // Number based traversal towards the genesis block - if query.Origin.Number >= query.Skip+1 { - query.Origin.Number -= query.Skip + 1 - } else { - unknown = true - } - - case !query.Reverse: - // Number based traversal towards the leaf block - query.Origin.Number += query.Skip + 1 - } - } - return p.SendBlockHeaders(headers) - - case msg.Code == BlockHeadersMsg: - // A batch of headers arrived to one of our previous requests - var headers []*types.Header - if err := msg.Decode(&headers); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // If no headers were received, but we're expencting a checkpoint header, consider it that - if len(headers) == 0 && p.syncDrop != nil { - // Stop the timer either way, decide later to drop or not - p.syncDrop.Stop() - p.syncDrop = nil - - // If we're doing a fast sync, we must enforce the checkpoint block to avoid - // eclipse attacks. Unsynced nodes are welcome to connect after we're done - // joining the network - if atomic.LoadUint32(&pm.fastSync) == 1 { - p.Log().Warn("Dropping unsynced node during fast sync", "addr", p.RemoteAddr(), "type", p.Name()) - return errors.New("unsynced node cannot serve fast sync") - } - } - // Filter out any explicitly requested headers, deliver the rest to the downloader - filter := len(headers) == 1 - if filter { - // If it's a potential sync progress check, validate the content and advertised chain weight - if p.syncDrop != nil && headers[0].Number.Uint64() == pm.checkpointNumber { - // Disable the sync drop timer - p.syncDrop.Stop() - p.syncDrop = nil - - // Validate the header and either drop the peer or continue - if headers[0].Hash() != pm.checkpointHash { - return errors.New("checkpoint hash mismatch") - } - return nil - } - // Otherwise if it's a whitelisted block, validate against the set - if want, ok := pm.whitelist[headers[0].Number.Uint64()]; ok { - if hash := headers[0].Hash(); want != hash { - p.Log().Info("Whitelist mismatch, dropping peer", "number", headers[0].Number.Uint64(), "hash", hash, "want", want) - return errors.New("whitelist block mismatch") - } - p.Log().Debug("Whitelist block verified", "number", headers[0].Number.Uint64(), "hash", want) - } - // Irrelevant of the fork checks, send the header to the fetcher just in case - headers = pm.blockFetcher.FilterHeaders(p.id, headers, time.Now()) - } - if len(headers) > 0 || !filter { - err := pm.downloader.DeliverHeaders(p.id, headers) - if err != nil { - log.Debug("Failed to deliver headers", "err", err) - } - } - - case msg.Code == GetBlockBodiesMsg: - // Decode the retrieval message - msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) - if _, err := msgStream.List(); err != nil { - return err - } - // Gather blocks until the fetch or network limits is reached - var ( - hash common.Hash - bytes int - bodies []rlp.RawValue - ) - for bytes < softResponseLimit && len(bodies) < downloader.MaxBlockFetch { - // Retrieve the hash of the next block - if err := msgStream.Decode(&hash); err == rlp.EOL { - break - } else if err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Retrieve the requested block body, stopping if enough was found - if data := pm.blockchain.GetBodyRLP(hash); len(data) != 0 { - bodies = append(bodies, data) - bytes += len(data) - } - } - return p.SendBlockBodiesRLP(bodies) - - case msg.Code == BlockBodiesMsg: - // A batch of block bodies arrived to one of our previous requests - var request blockBodiesData - if err := msg.Decode(&request); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Deliver them all to the downloader for queuing - transactions := make([][]*types.Transaction, len(request)) - uncles := make([][]*types.Header, len(request)) + return handler(peer) +} - for i, body := range request { - transactions[i] = body.Transactions - uncles[i] = body.Uncles - } - // Filter out any explicitly requested bodies, deliver the rest to the downloader - filter := len(transactions) > 0 || len(uncles) > 0 - if filter { - transactions, uncles = pm.blockFetcher.FilterBodies(p.id, transactions, uncles, time.Now()) - } - if len(transactions) > 0 || len(uncles) > 0 || !filter { - err := pm.downloader.DeliverBodies(p.id, transactions, uncles) - if err != nil { - log.Debug("Failed to deliver bodies", "err", err) - } - } +// removePeer unregisters a peer from the downloader and fetchers, removes it from +// the set of tracked peers and closes the network connection to it. +func (h *handler) removePeer(id string) { + // Create a custom logger to avoid printing the entire id + var logger log.Logger + if len(id) < 16 { + // Tests use short IDs, don't choke on them + logger = log.New("peer", id) + } else { + logger = log.New("peer", id[:8]) + } + // Abort if the peer does not exist + peer := h.peers.peer(id) + if peer == nil { + logger.Error("Ethereum peer removal failed", "err", errPeerNotRegistered) + return + } + // Remove the `eth` peer if it exists + logger.Debug("Removing Ethereum peer", "snap", peer.snapExt != nil) - case p.version >= eth63 && msg.Code == GetNodeDataMsg: - // Decode the retrieval message - msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) - if _, err := msgStream.List(); err != nil { - return err - } - // Gather state data until the fetch or network limits is reached - var ( - hash common.Hash - bytes int - data [][]byte - ) - for bytes < softResponseLimit && len(data) < downloader.MaxStateFetch { - // Retrieve the hash of the next state entry - if err := msgStream.Decode(&hash); err == rlp.EOL { - break - } else if err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Retrieve the requested state entry, stopping if enough was found - // todo now the code and trienode is mixed in the protocol level, - // separate these two types. - if !pm.downloader.SyncBloomContains(hash[:]) { - // Only lookup the trie node if there's chance that we actually have it - continue - } - entry, err := pm.blockchain.TrieNode(hash) - if len(entry) == 0 || err != nil { - // Read the contract code with prefix only to save unnecessary lookups. - entry, err = pm.blockchain.ContractCodeWithPrefix(hash) - } - if err == nil && len(entry) > 0 { - data = append(data, entry) - bytes += len(entry) - } - } - return p.SendNodeData(data) + // Remove the `snap` extension if it exists + if peer.snapExt != nil { + h.downloader.SnapSyncer.Unregister(id) + } + h.downloader.UnregisterPeer(id) + h.txFetcher.Drop(id) - case p.version >= eth63 && msg.Code == NodeDataMsg: - // A batch of node state data arrived to one of our previous requests - var data [][]byte - if err := msg.Decode(&data); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Deliver all to the downloader - if err := pm.downloader.DeliverNodeData(p.id, data); err != nil { - log.Debug("Failed to deliver node state data", "err", err) - } + if err := h.peers.unregisterPeer(id); err != nil { + logger.Error("Ethereum peer removal failed", "err", err) + } + // Hard disconnect at the networking layer + peer.Peer.Disconnect(p2p.DiscUselessPeer) +} - case p.version >= eth63 && msg.Code == GetReceiptsMsg: - // Decode the retrieval message - msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) - if _, err := msgStream.List(); err != nil { - return err - } - // Gather state data until the fetch or network limits is reached - var ( - hash common.Hash - bytes int - receipts []rlp.RawValue - ) - for bytes < softResponseLimit && len(receipts) < downloader.MaxReceiptFetch { - // Retrieve the hash of the next block - if err := msgStream.Decode(&hash); err == rlp.EOL { - break - } else if err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Retrieve the requested block's receipts, skipping if unknown to us - results := pm.blockchain.GetReceiptsByHash(hash) - if results == nil { - if header := pm.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { - continue - } - } - // If known, encode and queue for response packet - if encoded, err := rlp.EncodeToBytes(results); err != nil { - log.Error("Failed to encode receipt", "err", err) - } else { - receipts = append(receipts, encoded) - bytes += len(encoded) - } - } - return p.SendReceiptsRLP(receipts) +func (h *handler) Start(maxPeers int) { + h.maxPeers = maxPeers - case p.version >= eth63 && msg.Code == ReceiptsMsg: - // A batch of receipts arrived to one of our previous requests - var receipts [][]*types.Receipt - if err := msg.Decode(&receipts); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Deliver all to the downloader - if err := pm.downloader.DeliverReceipts(p.id, receipts); err != nil { - log.Debug("Failed to deliver receipts", "err", err) - } + // broadcast transactions + h.wg.Add(1) + h.txsCh = make(chan core.NewTxsEvent, txChanSize) + h.txsSub = h.txpool.SubscribeNewTxsEvent(h.txsCh) + go h.txBroadcastLoop() - case msg.Code == NewBlockHashesMsg: - var announces newBlockHashesData - if err := msg.Decode(&announces); err != nil { - return errResp(ErrDecode, "%v: %v", msg, err) - } - // Mark the hashes as present at the remote node - for _, block := range announces { - p.MarkBlock(block.Hash) - } - // Schedule all the unknown hashes for retrieval - unknown := make(newBlockHashesData, 0, len(announces)) - for _, block := range announces { - if !pm.blockchain.HasBlock(block.Hash, block.Number) { - unknown = append(unknown, block) - } - } - for _, block := range unknown { - pm.blockFetcher.Notify(p.id, block.Hash, block.Number, time.Now(), p.RequestOneHeader, p.RequestBodies) - } + // broadcast mined blocks + h.wg.Add(1) + h.minedBlockSub = h.eventMux.Subscribe(core.NewMinedBlockEvent{}) + go h.minedBroadcastLoop() - case msg.Code == NewBlockMsg: - // Retrieve and decode the propagated block - var request newBlockData - if err := msg.Decode(&request); err != nil { - return errResp(ErrDecode, "%v: %v", msg, err) - } - if hash := types.CalcUncleHash(request.Block.Uncles()); hash != request.Block.UncleHash() { - log.Warn("Propagated block has invalid uncles", "have", hash, "exp", request.Block.UncleHash()) - break // TODO(karalabe): return error eventually, but wait a few releases - } - if hash := types.DeriveSha(request.Block.Transactions(), trie.NewStackTrie(nil)); hash != request.Block.TxHash() { - log.Warn("Propagated block has invalid body", "have", hash, "exp", request.Block.TxHash()) - break // TODO(karalabe): return error eventually, but wait a few releases - } - if err := request.sanityCheck(); err != nil { - return err - } - request.Block.ReceivedAt = msg.ReceivedAt - request.Block.ReceivedFrom = p - - // Mark the peer as owning the block and schedule it for import - p.MarkBlock(request.Block.Hash()) - pm.blockFetcher.Enqueue(p.id, request.Block) - - // Assuming the block is importable by the peer, but possibly not yet done so, - // calculate the head hash and TD that the peer truly must have. - var ( - trueHead = request.Block.ParentHash() - trueTD = new(big.Int).Sub(request.TD, request.Block.Difficulty()) - ) - // Update the peer's total difficulty if better than the previous - if _, td := p.Head(); trueTD.Cmp(td) > 0 { - p.SetHead(trueHead, trueTD) - pm.chainSync.handlePeerEvent(p) - } + // start sync handlers + h.wg.Add(2) + go h.chainSync.loop() + go h.txsyncLoop64() // TODO(karalabe): Legacy initial tx echange, drop with eth/64. +} - case msg.Code == NewPooledTransactionHashesMsg && p.version >= eth65: - // New transaction announcement arrived, make sure we have - // a valid and fresh chain to handle them - if atomic.LoadUint32(&pm.acceptTxs) == 0 { - break - } - var hashes []common.Hash - if err := msg.Decode(&hashes); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Schedule all the unknown hashes for retrieval - for _, hash := range hashes { - p.MarkTransaction(hash) - } - pm.txFetcher.Notify(p.id, hashes) +func (h *handler) Stop() { + h.txsSub.Unsubscribe() // quits txBroadcastLoop + h.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop - case msg.Code == GetPooledTransactionsMsg && p.version >= eth65: - // Decode the retrieval message - msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) - if _, err := msgStream.List(); err != nil { - return err - } - // Gather transactions until the fetch or network limits is reached - var ( - hash common.Hash - bytes int - hashes []common.Hash - txs []rlp.RawValue - ) - for bytes < softResponseLimit { - // Retrieve the hash of the next block - if err := msgStream.Decode(&hash); err == rlp.EOL { - break - } else if err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Retrieve the requested transaction, skipping if unknown to us - tx := pm.txpool.Get(hash) - if tx == nil { - continue - } - // If known, encode and queue for response packet - if encoded, err := rlp.EncodeToBytes(tx); err != nil { - log.Error("Failed to encode transaction", "err", err) - } else { - hashes = append(hashes, hash) - txs = append(txs, encoded) - bytes += len(encoded) - } - } - return p.SendPooledTransactionsRLP(hashes, txs) + // Quit chainSync and txsync64. + // After this is done, no new peers will be accepted. + close(h.quitSync) + h.wg.Wait() - case msg.Code == TransactionMsg || (msg.Code == PooledTransactionsMsg && p.version >= eth65): - // Transactions arrived, make sure we have a valid and fresh chain to handle them - if atomic.LoadUint32(&pm.acceptTxs) == 0 { - break - } - // Transactions can be processed, parse all of them and deliver to the pool - var txs []*types.Transaction - if err := msg.Decode(&txs); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - for i, tx := range txs { - // Validate and mark the remote transaction - if tx == nil { - return errResp(ErrDecode, "transaction %d is nil", i) - } - p.MarkTransaction(tx.Hash()) - } - pm.txFetcher.Enqueue(p.id, txs, msg.Code == PooledTransactionsMsg) + // Disconnect existing sessions. + // This also closes the gate for any new registrations on the peer set. + // sessions which are already established but not added to h.peers yet + // will exit when they try to register. + h.peers.close() + h.peerWG.Wait() - default: - return errResp(ErrInvalidMsgCode, "%v", msg.Code) - } - return nil + log.Info("Ethereum protocol stopped") } // BroadcastBlock will either propagate a block to a subset of its peers, or // will only announce its availability (depending what's requested). -func (pm *ProtocolManager) BroadcastBlock(block *types.Block, propagate bool) { +func (h *handler) BroadcastBlock(block *types.Block, propagate bool) { hash := block.Hash() - peers := pm.peers.PeersWithoutBlock(hash) + peers := h.peers.peersWithoutBlock(hash) // If propagation is requested, send to a subset of the peer if propagate { // Calculate the TD of the block (it's not imported yet, so block.Td is not valid) var td *big.Int - if parent := pm.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1); parent != nil { - td = new(big.Int).Add(block.Difficulty(), pm.blockchain.GetTd(block.ParentHash(), block.NumberU64()-1)) + if parent := h.chain.GetBlock(block.ParentHash(), block.NumberU64()-1); parent != nil { + td = new(big.Int).Add(block.Difficulty(), h.chain.GetTd(block.ParentHash(), block.NumberU64()-1)) } else { log.Error("Propagating dangling block", "number", block.Number(), "hash", hash) return @@ -846,7 +457,7 @@ func (pm *ProtocolManager) BroadcastBlock(block *types.Block, propagate bool) { return } // Otherwise if the block is indeed in out own chain, announce it - if pm.blockchain.HasBlock(hash, block.NumberU64()) { + if h.chain.HasBlock(hash, block.NumberU64()) { for _, peer := range peers { peer.AsyncSendNewBlockHash(block) } @@ -854,97 +465,74 @@ func (pm *ProtocolManager) BroadcastBlock(block *types.Block, propagate bool) { } } -// BroadcastTransactions will propagate a batch of transactions to all peers which are not known to +// BroadcastTransactions will propagate a batch of transactions +// - To a square root of all peers +// - And, separately, as announcements to all peers which are not known to // already have the given transaction. -func (pm *ProtocolManager) BroadcastTransactions(txs types.Transactions, propagate bool) { +func (h *handler) BroadcastTransactions(txs types.Transactions) { var ( - txset = make(map[*peer][]common.Hash) - annos = make(map[*peer][]common.Hash) + annoCount int // Count of announcements made + annoPeers int + directCount int // Count of the txs sent directly to peers + directPeers int // Count of the peers that were sent transactions directly + + txset = make(map[*ethPeer][]common.Hash) // Set peer->hash to transfer directly + annos = make(map[*ethPeer][]common.Hash) // Set peer->hash to announce + ) // Broadcast transactions to a batch of peers not knowing about it - if propagate { - for _, tx := range txs { - peers := pm.peers.PeersWithoutTx(tx.Hash()) - - // Send the block to a subset of our peers - transfer := peers[:int(math.Sqrt(float64(len(peers))))] - for _, peer := range transfer { - txset[peer] = append(txset[peer], tx.Hash()) - } - log.Trace("Broadcast transaction", "hash", tx.Hash(), "recipients", len(peers)) - } - for peer, hashes := range txset { - peer.AsyncSendTransactions(hashes) - } - return - } - // Otherwise only broadcast the announcement to peers for _, tx := range txs { - peers := pm.peers.PeersWithoutTx(tx.Hash()) - for _, peer := range peers { + peers := h.peers.peersWithoutTransaction(tx.Hash()) + // Send the tx unconditionally to a subset of our peers + numDirect := int(math.Sqrt(float64(len(peers)))) + for _, peer := range peers[:numDirect] { + txset[peer] = append(txset[peer], tx.Hash()) + } + // For the remaining peers, send announcement only + for _, peer := range peers[numDirect:] { annos[peer] = append(annos[peer], tx.Hash()) } } + for peer, hashes := range txset { + directPeers++ + directCount += len(hashes) + peer.AsyncSendTransactions(hashes) + } for peer, hashes := range annos { - if peer.version >= eth65 { + annoPeers++ + annoCount += len(hashes) + if peer.Version() >= eth.ETH65 { peer.AsyncSendPooledTransactionHashes(hashes) } else { peer.AsyncSendTransactions(hashes) } } + log.Debug("Transaction broadcast", "txs", len(txs), + "announce packs", annoPeers, "announced hashes", annoCount, + "tx packs", directPeers, "broadcast txs", directCount) } // minedBroadcastLoop sends mined blocks to connected peers. -func (pm *ProtocolManager) minedBroadcastLoop() { - defer pm.wg.Done() +func (h *handler) minedBroadcastLoop() { + defer h.wg.Done() - for obj := range pm.minedBlockSub.Chan() { + for obj := range h.minedBlockSub.Chan() { if ev, ok := obj.Data.(core.NewMinedBlockEvent); ok { - pm.BroadcastBlock(ev.Block, true) // First propagate block to peers - pm.BroadcastBlock(ev.Block, false) // Only then announce to the rest + h.BroadcastBlock(ev.Block, true) // First propagate block to peers + h.BroadcastBlock(ev.Block, false) // Only then announce to the rest } } } // txBroadcastLoop announces new transactions to connected peers. -func (pm *ProtocolManager) txBroadcastLoop() { - defer pm.wg.Done() - +func (h *handler) txBroadcastLoop() { + defer h.wg.Done() for { select { - case event := <-pm.txsCh: - // For testing purpose only, disable propagation - if pm.broadcastTxAnnouncesOnly { - pm.BroadcastTransactions(event.Txs, false) - continue - } - pm.BroadcastTransactions(event.Txs, true) // First propagate transactions to peers - pm.BroadcastTransactions(event.Txs, false) // Only then announce to the rest - - case <-pm.txsSub.Err(): + case event := <-h.txsCh: + h.BroadcastTransactions(event.Txs) + case <-h.txsSub.Err(): return } } } - -// NodeInfo represents a short summary of the Ethereum sub-protocol metadata -// known about the host peer. -type NodeInfo struct { - Network uint64 `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3, Rinkeby=4, Kotti=6) - Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain - Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block - Config ctypes.ChainConfigurator `json:"config"` // Chain configuration for the fork rules - Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block -} - -// NodeInfo retrieves some protocol metadata about the running host node. -func (pm *ProtocolManager) NodeInfo() *NodeInfo { - currentBlock := pm.blockchain.CurrentBlock() - return &NodeInfo{ - Network: pm.networkID, - Difficulty: pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()), - Genesis: pm.blockchain.Genesis().Hash(), - Config: pm.blockchain.Config(), - Head: currentBlock.Hash(), - } -} diff --git a/eth/handler_eth.go b/eth/handler_eth.go new file mode 100644 index 0000000000..3ff9f2245b --- /dev/null +++ b/eth/handler_eth.go @@ -0,0 +1,218 @@ +// 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 . + +package eth + +import ( + "errors" + "fmt" + "math/big" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/trie" +) + +// ethHandler implements the eth.Backend interface to handle the various network +// packets that are sent as replies or broadcasts. +type ethHandler handler + +func (h *ethHandler) Chain() *core.BlockChain { return h.chain } +func (h *ethHandler) StateBloom() *trie.SyncBloom { return h.stateBloom } +func (h *ethHandler) TxPool() eth.TxPool { return h.txpool } + +// RunPeer is invoked when a peer joins on the `eth` protocol. +func (h *ethHandler) RunPeer(peer *eth.Peer, hand eth.Handler) error { + return (*handler)(h).runEthPeer(peer, hand) +} + +// PeerInfo retrieves all known `eth` information about a peer. +func (h *ethHandler) PeerInfo(id enode.ID) interface{} { + if p := h.peers.peer(id.String()); p != nil { + return p.info() + } + return nil +} + +// AcceptTxs retrieves whether transaction processing is enabled on the node +// or if inbound transactions should simply be dropped. +func (h *ethHandler) AcceptTxs() bool { + return atomic.LoadUint32(&h.acceptTxs) == 1 +} + +// Handle is invoked from a peer's message handler when it receives a new remote +// message that the handler couldn't consume and serve itself. +func (h *ethHandler) Handle(peer *eth.Peer, packet eth.Packet) error { + // Consume any broadcasts and announces, forwarding the rest to the downloader + switch packet := packet.(type) { + case *eth.BlockHeadersPacket: + return h.handleHeaders(peer, *packet) + + case *eth.BlockBodiesPacket: + txset, uncleset := packet.Unpack() + return h.handleBodies(peer, txset, uncleset) + + case *eth.NodeDataPacket: + if err := h.downloader.DeliverNodeData(peer.ID(), *packet); err != nil { + log.Debug("Failed to deliver node state data", "err", err) + } + return nil + + case *eth.ReceiptsPacket: + if err := h.downloader.DeliverReceipts(peer.ID(), *packet); err != nil { + log.Debug("Failed to deliver receipts", "err", err) + } + return nil + + case *eth.NewBlockHashesPacket: + hashes, numbers := packet.Unpack() + return h.handleBlockAnnounces(peer, hashes, numbers) + + case *eth.NewBlockPacket: + return h.handleBlockBroadcast(peer, packet.Block, packet.TD) + + case *eth.NewPooledTransactionHashesPacket: + return h.txFetcher.Notify(peer.ID(), *packet) + + case *eth.TransactionsPacket: + return h.txFetcher.Enqueue(peer.ID(), *packet, false) + + case *eth.PooledTransactionsPacket: + return h.txFetcher.Enqueue(peer.ID(), *packet, true) + + default: + return fmt.Errorf("unexpected eth packet type: %T", packet) + } +} + +// handleHeaders is invoked from a peer's message handler when it transmits a batch +// of headers for the local node to process. +func (h *ethHandler) handleHeaders(peer *eth.Peer, headers []*types.Header) error { + p := h.peers.peer(peer.ID()) + if p == nil { + return errors.New("unregistered during callback") + } + // If no headers were received, but we're expencting a checkpoint header, consider it that + if len(headers) == 0 && p.syncDrop != nil { + // Stop the timer either way, decide later to drop or not + p.syncDrop.Stop() + p.syncDrop = nil + + // If we're doing a fast (or snap) sync, we must enforce the checkpoint block to avoid + // eclipse attacks. Unsynced nodes are welcome to connect after we're done + // joining the network + if atomic.LoadUint32(&h.fastSync) == 1 { + peer.Log().Warn("Dropping unsynced node during sync", "addr", peer.RemoteAddr(), "type", peer.Name()) + return errors.New("unsynced node cannot serve sync") + } + } + // Filter out any explicitly requested headers, deliver the rest to the downloader + filter := len(headers) == 1 + if filter { + // If it's a potential sync progress check, validate the content and advertised chain weight + if p.syncDrop != nil && headers[0].Number.Uint64() == h.checkpointNumber { + // Disable the sync drop timer + p.syncDrop.Stop() + p.syncDrop = nil + + // Validate the header and either drop the peer or continue + if headers[0].Hash() != h.checkpointHash { + return errors.New("checkpoint hash mismatch") + } + return nil + } + // Otherwise if it's a whitelisted block, validate against the set + if want, ok := h.whitelist[headers[0].Number.Uint64()]; ok { + if hash := headers[0].Hash(); want != hash { + peer.Log().Info("Whitelist mismatch, dropping peer", "number", headers[0].Number.Uint64(), "hash", hash, "want", want) + return errors.New("whitelist block mismatch") + } + peer.Log().Debug("Whitelist block verified", "number", headers[0].Number.Uint64(), "hash", want) + } + // Irrelevant of the fork checks, send the header to the fetcher just in case + headers = h.blockFetcher.FilterHeaders(peer.ID(), headers, time.Now()) + } + if len(headers) > 0 || !filter { + err := h.downloader.DeliverHeaders(peer.ID(), headers) + if err != nil { + log.Debug("Failed to deliver headers", "err", err) + } + } + return nil +} + +// handleBodies is invoked from a peer's message handler when it transmits a batch +// of block bodies for the local node to process. +func (h *ethHandler) handleBodies(peer *eth.Peer, txs [][]*types.Transaction, uncles [][]*types.Header) error { + // Filter out any explicitly requested bodies, deliver the rest to the downloader + filter := len(txs) > 0 || len(uncles) > 0 + if filter { + txs, uncles = h.blockFetcher.FilterBodies(peer.ID(), txs, uncles, time.Now()) + } + if len(txs) > 0 || len(uncles) > 0 || !filter { + err := h.downloader.DeliverBodies(peer.ID(), txs, uncles) + if err != nil { + log.Debug("Failed to deliver bodies", "err", err) + } + } + return nil +} + +// handleBlockAnnounces is invoked from a peer's message handler when it transmits a +// batch of block announcements for the local node to process. +func (h *ethHandler) handleBlockAnnounces(peer *eth.Peer, hashes []common.Hash, numbers []uint64) error { + // Schedule all the unknown hashes for retrieval + var ( + unknownHashes = make([]common.Hash, 0, len(hashes)) + unknownNumbers = make([]uint64, 0, len(numbers)) + ) + for i := 0; i < len(hashes); i++ { + if !h.chain.HasBlock(hashes[i], numbers[i]) { + unknownHashes = append(unknownHashes, hashes[i]) + unknownNumbers = append(unknownNumbers, numbers[i]) + } + } + for i := 0; i < len(unknownHashes); i++ { + h.blockFetcher.Notify(peer.ID(), unknownHashes[i], unknownNumbers[i], time.Now(), peer.RequestOneHeader, peer.RequestBodies) + } + return nil +} + +// handleBlockBroadcast is invoked from a peer's message handler when it transmits a +// block broadcast for the local node to process. +func (h *ethHandler) handleBlockBroadcast(peer *eth.Peer, block *types.Block, td *big.Int) error { + // Schedule the block for import + h.blockFetcher.Enqueue(peer.ID(), block) + + // Assuming the block is importable by the peer, but possibly not yet done so, + // calculate the head hash and TD that the peer truly must have. + var ( + trueHead = block.ParentHash() + trueTD = new(big.Int).Sub(td, block.Difficulty()) + ) + // Update the peer's total difficulty if better than the previous + if _, td := peer.Head(); trueTD.Cmp(td) > 0 { + peer.SetHead(trueHead, trueTD) + h.chainSync.handlePeerEvent(peer) + } + return nil +} diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go new file mode 100644 index 0000000000..d1a390fe85 --- /dev/null +++ b/eth/handler_eth_test.go @@ -0,0 +1,742 @@ +// 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 . + +package eth + +import ( + "fmt" + "math/big" + "math/rand" + "sync/atomic" + "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/forkid" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params/types/genesisT" + "github.com/ethereum/go-ethereum/params/types/goethereum" + "github.com/ethereum/go-ethereum/params/vars" + "github.com/ethereum/go-ethereum/trie" +) + +// testEthHandler is a mock event handler to listen for inbound network requests +// on the `eth` protocol and convert them into a more easily testable form. +type testEthHandler struct { + blockBroadcasts event.Feed + txAnnounces event.Feed + txBroadcasts event.Feed +} + +func (h *testEthHandler) Chain() *core.BlockChain { panic("no backing chain") } +func (h *testEthHandler) StateBloom() *trie.SyncBloom { panic("no backing state bloom") } +func (h *testEthHandler) TxPool() eth.TxPool { panic("no backing tx pool") } +func (h *testEthHandler) AcceptTxs() bool { return true } +func (h *testEthHandler) RunPeer(*eth.Peer, eth.Handler) error { panic("not used in tests") } +func (h *testEthHandler) PeerInfo(enode.ID) interface{} { panic("not used in tests") } + +func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error { + switch packet := packet.(type) { + case *eth.NewBlockPacket: + h.blockBroadcasts.Send(packet.Block) + return nil + + case *eth.NewPooledTransactionHashesPacket: + h.txAnnounces.Send(([]common.Hash)(*packet)) + return nil + + case *eth.TransactionsPacket: + h.txBroadcasts.Send(([]*types.Transaction)(*packet)) + return nil + + case *eth.PooledTransactionsPacket: + h.txBroadcasts.Send(([]*types.Transaction)(*packet)) + return nil + + default: + panic(fmt.Sprintf("unexpected eth packet type in tests: %T", packet)) + } +} + +// Tests that peers are correctly accepted (or rejected) based on the advertised +// fork IDs in the protocol handshake. +func TestForkIDSplit64(t *testing.T) { testForkIDSplit(t, 64) } +func TestForkIDSplit65(t *testing.T) { testForkIDSplit(t, 65) } + +func testForkIDSplit(t *testing.T, protocol uint) { + t.Parallel() + + var ( + engine = ethash.NewFaker() + + configNoFork = &goethereum.ChainConfig{HomesteadBlock: big.NewInt(1)} + configProFork = &goethereum.ChainConfig{ + HomesteadBlock: big.NewInt(1), + EIP150Block: big.NewInt(2), + EIP155Block: big.NewInt(2), + EIP158Block: big.NewInt(2), + ByzantiumBlock: big.NewInt(3), + } + dbNoFork = rawdb.NewMemoryDatabase() + dbProFork = rawdb.NewMemoryDatabase() + + gspecNoFork = &genesisT.Genesis{Config: configNoFork} + gspecProFork = &genesisT.Genesis{Config: configProFork} + + genesisNoFork = core.MustCommitGenesis(dbNoFork, gspecNoFork) + genesisProFork = core.MustCommitGenesis(dbProFork, gspecProFork) + + chainNoFork, _ = core.NewBlockChain(dbNoFork, nil, configNoFork, engine, vm.Config{}, nil, nil) + chainProFork, _ = core.NewBlockChain(dbProFork, nil, configProFork, engine, vm.Config{}, nil, nil) + + blocksNoFork, _ = core.GenerateChain(configNoFork, genesisNoFork, engine, dbNoFork, 2, nil) + blocksProFork, _ = core.GenerateChain(configProFork, genesisProFork, engine, dbProFork, 2, nil) + + ethNoFork, _ = newHandler(&handlerConfig{ + Database: dbNoFork, + Chain: chainNoFork, + TxPool: newTestTxPool(), + Network: 1, + Sync: downloader.FullSync, + BloomCache: 1, + }) + ethProFork, _ = newHandler(&handlerConfig{ + Database: dbProFork, + Chain: chainProFork, + TxPool: newTestTxPool(), + Network: 1, + Sync: downloader.FullSync, + BloomCache: 1, + }) + ) + ethNoFork.Start(1000) + ethProFork.Start(1000) + + // Clean up everything after ourselves + defer chainNoFork.Stop() + defer chainProFork.Stop() + + defer ethNoFork.Stop() + defer ethProFork.Stop() + + // Both nodes should allow the other to connect (same genesis, next fork is the same) + p2pNoFork, p2pProFork := p2p.MsgPipe() + defer p2pNoFork.Close() + defer p2pProFork.Close() + + peerNoFork := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil) + peerProFork := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil) + defer peerNoFork.Close() + defer peerProFork.Close() + + errc := make(chan error, 2) + go func(errc chan error) { + errc <- ethNoFork.runEthPeer(peerProFork, func(peer *eth.Peer) error { return nil }) + }(errc) + go func(errc chan error) { + errc <- ethProFork.runEthPeer(peerNoFork, func(peer *eth.Peer) error { return nil }) + }(errc) + + for i := 0; i < 2; i++ { + select { + case err := <-errc: + if err != nil { + t.Fatalf("frontier nofork <-> profork failed: %v", err) + } + case <-time.After(250 * time.Millisecond): + t.Fatalf("frontier nofork <-> profork handler timeout") + } + } + // Progress into Homestead. Fork's match, so we don't care what the future holds + chainNoFork.InsertChain(blocksNoFork[:1]) + chainProFork.InsertChain(blocksProFork[:1]) + + p2pNoFork, p2pProFork = p2p.MsgPipe() + defer p2pNoFork.Close() + defer p2pProFork.Close() + + peerNoFork = eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil) + peerProFork = eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil) + defer peerNoFork.Close() + defer peerProFork.Close() + + errc = make(chan error, 2) + go func(errc chan error) { + errc <- ethNoFork.runEthPeer(peerProFork, func(peer *eth.Peer) error { return nil }) + }(errc) + go func(errc chan error) { + errc <- ethProFork.runEthPeer(peerNoFork, func(peer *eth.Peer) error { return nil }) + }(errc) + + for i := 0; i < 2; i++ { + select { + case err := <-errc: + if err != nil { + t.Fatalf("homestead nofork <-> profork failed: %v", err) + } + case <-time.After(250 * time.Millisecond): + t.Fatalf("homestead nofork <-> profork handler timeout") + } + } + // Progress into Spurious. Forks mismatch, signalling differing chains, reject + chainNoFork.InsertChain(blocksNoFork[1:2]) + chainProFork.InsertChain(blocksProFork[1:2]) + + p2pNoFork, p2pProFork = p2p.MsgPipe() + defer p2pNoFork.Close() + defer p2pProFork.Close() + + peerNoFork = eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil) + peerProFork = eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil) + defer peerNoFork.Close() + defer peerProFork.Close() + + errc = make(chan error, 2) + go func(errc chan error) { + errc <- ethNoFork.runEthPeer(peerProFork, func(peer *eth.Peer) error { return nil }) + }(errc) + go func(errc chan error) { + errc <- ethProFork.runEthPeer(peerNoFork, func(peer *eth.Peer) error { return nil }) + }(errc) + + var successes int + for i := 0; i < 2; i++ { + select { + case err := <-errc: + if err == nil { + successes++ + if successes == 2 { // Only one side disconnects + t.Fatalf("fork ID rejection didn't happen") + } + } + case <-time.After(250 * time.Millisecond): + t.Fatalf("split peers not rejected") + } + } +} + +// Tests that received transactions are added to the local pool. +func TestRecvTransactions64(t *testing.T) { testRecvTransactions(t, 64) } +func TestRecvTransactions65(t *testing.T) { testRecvTransactions(t, 65) } + +func testRecvTransactions(t *testing.T, protocol uint) { + t.Parallel() + + // Create a message handler, configure it to accept transactions and watch them + handler := newTestHandler() + defer handler.close() + + handler.handler.acceptTxs = 1 // mark synced to accept transactions + + txs := make(chan core.NewTxsEvent) + sub := handler.txpool.SubscribeNewTxsEvent(txs) + defer sub.Unsubscribe() + + // Create a source peer to send messages through and a sink handler to receive them + p2pSrc, p2pSink := p2p.MsgPipe() + defer p2pSrc.Close() + defer p2pSink.Close() + + src := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pSrc, handler.txpool) + sink := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pSink, handler.txpool) + defer src.Close() + defer sink.Close() + + go handler.handler.runEthPeer(sink, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(handler.handler), peer) + }) + // Run the handshake locally to avoid spinning up a source handler + var ( + genesis = handler.chain.Genesis() + head = handler.chain.CurrentBlock() + td = handler.chain.GetTd(head.Hash(), head.NumberU64()) + ) + if err := src.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil { + t.Fatalf("failed to run protocol handshake") + } + // Send the transaction to the sink and verify that it's added to the tx pool + tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil) + tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) + + if err := src.SendTransactions([]*types.Transaction{tx}); err != nil { + t.Fatalf("failed to send transaction: %v", err) + } + select { + case event := <-txs: + if len(event.Txs) != 1 { + t.Errorf("wrong number of added transactions: got %d, want 1", len(event.Txs)) + } else if event.Txs[0].Hash() != tx.Hash() { + t.Errorf("added wrong tx hash: got %v, want %v", event.Txs[0].Hash(), tx.Hash()) + } + case <-time.After(2 * time.Second): + t.Errorf("no NewTxsEvent received within 2 seconds") + } +} + +// This test checks that pending transactions are sent. +func TestSendTransactions64(t *testing.T) { testSendTransactions(t, 64) } +func TestSendTransactions65(t *testing.T) { testSendTransactions(t, 65) } + +func testSendTransactions(t *testing.T, protocol uint) { + t.Parallel() + + // Create a message handler and fill the pool with big transactions + handler := newTestHandler() + defer handler.close() + + insert := make([]*types.Transaction, 100) + for nonce := range insert { + tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, txsyncPackSize/10)) + tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) + + insert[nonce] = tx + } + go handler.txpool.AddRemotes(insert) // Need goroutine to not block on feed + time.Sleep(250 * time.Millisecond) // Wait until tx events get out of the system (can't use events, tx broadcaster races with peer join) + + // Create a source handler to send messages through and a sink peer to receive them + p2pSrc, p2pSink := p2p.MsgPipe() + defer p2pSrc.Close() + defer p2pSink.Close() + + src := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pSrc, handler.txpool) + sink := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pSink, handler.txpool) + defer src.Close() + defer sink.Close() + + go handler.handler.runEthPeer(src, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(handler.handler), peer) + }) + // Run the handshake locally to avoid spinning up a source handler + var ( + genesis = handler.chain.Genesis() + head = handler.chain.CurrentBlock() + td = handler.chain.GetTd(head.Hash(), head.NumberU64()) + ) + if err := sink.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil { + t.Fatalf("failed to run protocol handshake") + } + // After the handshake completes, the source handler should stream the sink + // the transactions, subscribe to all inbound network events + backend := new(testEthHandler) + + anns := make(chan []common.Hash) + annSub := backend.txAnnounces.Subscribe(anns) + defer annSub.Unsubscribe() + + bcasts := make(chan []*types.Transaction) + bcastSub := backend.txBroadcasts.Subscribe(bcasts) + defer bcastSub.Unsubscribe() + + go eth.Handle(backend, sink) + + // Make sure we get all the transactions on the correct channels + seen := make(map[common.Hash]struct{}) + for len(seen) < len(insert) { + switch protocol { + case 63, 64: + select { + case <-anns: + t.Errorf("tx announce received on pre eth/65") + case txs := <-bcasts: + for _, tx := range txs { + if _, ok := seen[tx.Hash()]; ok { + t.Errorf("duplicate transaction announced: %x", tx.Hash()) + } + seen[tx.Hash()] = struct{}{} + } + } + case 65: + select { + case hashes := <-anns: + for _, hash := range hashes { + if _, ok := seen[hash]; ok { + t.Errorf("duplicate transaction announced: %x", hash) + } + seen[hash] = struct{}{} + } + case <-bcasts: + t.Errorf("initial tx broadcast received on post eth/65") + } + + default: + panic("unsupported protocol, please extend test") + } + } + for _, tx := range insert { + if _, ok := seen[tx.Hash()]; !ok { + t.Errorf("missing transaction: %x", tx.Hash()) + } + } +} + +// Tests that transactions get propagated to all attached peers, either via direct +// broadcasts or via announcements/retrievals. +func TestTransactionPropagation64(t *testing.T) { testTransactionPropagation(t, 64) } +func TestTransactionPropagation65(t *testing.T) { testTransactionPropagation(t, 65) } + +func testTransactionPropagation(t *testing.T, protocol uint) { + t.Parallel() + + // Create a source handler to send transactions from and a number of sinks + // to receive them. We need multiple sinks since a one-to-one peering would + // broadcast all transactions without announcement. + source := newTestHandler() + defer source.close() + + sinks := make([]*testHandler, 10) + for i := 0; i < len(sinks); i++ { + sinks[i] = newTestHandler() + defer sinks[i].close() + + sinks[i].handler.acceptTxs = 1 // mark synced to accept transactions + } + // Interconnect all the sink handlers with the source handler + for i, sink := range sinks { + sink := sink // Closure for gorotuine below + + sourcePipe, sinkPipe := p2p.MsgPipe() + defer sourcePipe.Close() + defer sinkPipe.Close() + + sourcePeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{byte(i)}, "", nil), sourcePipe, source.txpool) + sinkPeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{0}, "", nil), sinkPipe, sink.txpool) + defer sourcePeer.Close() + defer sinkPeer.Close() + + go source.handler.runEthPeer(sourcePeer, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(source.handler), peer) + }) + go sink.handler.runEthPeer(sinkPeer, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(sink.handler), peer) + }) + } + // Subscribe to all the transaction pools + txChs := make([]chan core.NewTxsEvent, len(sinks)) + for i := 0; i < len(sinks); i++ { + txChs[i] = make(chan core.NewTxsEvent, 1024) + + sub := sinks[i].txpool.SubscribeNewTxsEvent(txChs[i]) + defer sub.Unsubscribe() + } + // Fill the source pool with transactions and wait for them at the sinks + txs := make([]*types.Transaction, 1024) + for nonce := range txs { + tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil) + tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) + + txs[nonce] = tx + } + source.txpool.AddRemotes(txs) + + // Iterate through all the sinks and ensure they all got the transactions + for i := range sinks { + for arrived := 0; arrived < len(txs); { + select { + case event := <-txChs[i]: + arrived += len(event.Txs) + case <-time.NewTimer(time.Second).C: + t.Errorf("sink %d: transaction propagation timed out: have %d, want %d", i, arrived, len(txs)) + } + } + } +} + +// Tests that post eth protocol handshake, clients perform a mutual checkpoint +// challenge to validate each other's chains. Hash mismatches, or missing ones +// during a fast sync should lead to the peer getting dropped. +func TestCheckpointChallenge(t *testing.T) { + tests := []struct { + syncmode downloader.SyncMode + checkpoint bool + timeout bool + empty bool + match bool + drop bool + }{ + // If checkpointing is not enabled locally, don't challenge and don't drop + {downloader.FullSync, false, false, false, false, false}, + {downloader.FastSync, false, false, false, false, false}, + + // If checkpointing is enabled locally and remote response is empty, only drop during fast sync + {downloader.FullSync, true, false, true, false, false}, + {downloader.FastSync, true, false, true, false, true}, // Special case, fast sync, unsynced peer + + // If checkpointing is enabled locally and remote response mismatches, always drop + {downloader.FullSync, true, false, false, false, true}, + {downloader.FastSync, true, false, false, false, true}, + + // If checkpointing is enabled locally and remote response matches, never drop + {downloader.FullSync, true, false, false, true, false}, + {downloader.FastSync, true, false, false, true, false}, + + // If checkpointing is enabled locally and remote times out, always drop + {downloader.FullSync, true, true, false, true, true}, + {downloader.FastSync, true, true, false, true, true}, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("sync %v checkpoint %v timeout %v empty %v match %v", tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match), func(t *testing.T) { + testCheckpointChallenge(t, tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match, tt.drop) + }) + } +} + +func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpoint bool, timeout bool, empty bool, match bool, drop bool) { + // Reduce the checkpoint handshake challenge timeout + defer func(old time.Duration) { syncChallengeTimeout = old }(syncChallengeTimeout) + syncChallengeTimeout = 250 * time.Millisecond + + // Create a test handler and inject a CHT into it. The injection is a bit + // ugly, but it beats creating everything manually just to avoid reaching + // into the internals a bit. + handler := newTestHandler() + defer handler.close() + + if syncmode == downloader.FastSync { + atomic.StoreUint32(&handler.handler.fastSync, 1) + } else { + atomic.StoreUint32(&handler.handler.fastSync, 0) + } + var response *types.Header + if checkpoint { + number := (uint64(rand.Intn(500))+1)*vars.CHTFrequency - 1 + response = &types.Header{Number: big.NewInt(int64(number)), Extra: []byte("valid")} + + handler.handler.checkpointNumber = number + handler.handler.checkpointHash = response.Hash() + } + // Create a challenger peer and a challenged one + p2pLocal, p2pRemote := p2p.MsgPipe() + defer p2pLocal.Close() + defer p2pRemote.Close() + + local := eth.NewPeer(eth.ETH64, p2p.NewPeer(enode.ID{1}, "", nil), p2pLocal, handler.txpool) + remote := eth.NewPeer(eth.ETH64, p2p.NewPeer(enode.ID{2}, "", nil), p2pRemote, handler.txpool) + defer local.Close() + defer remote.Close() + + go handler.handler.runEthPeer(local, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(handler.handler), peer) + }) + // Run the handshake locally to avoid spinning up a remote handler + var ( + genesis = handler.chain.Genesis() + head = handler.chain.CurrentBlock() + td = handler.chain.GetTd(head.Hash(), head.NumberU64()) + ) + if err := remote.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil { + t.Fatalf("failed to run protocol handshake") + } + // Connect a new peer and check that we receive the checkpoint challenge + if checkpoint { + if err := remote.ExpectRequestHeadersByNumber(response.Number.Uint64(), 1, 0, false); err != nil { + t.Fatalf("challenge mismatch: %v", err) + } + // Create a block to reply to the challenge if no timeout is simulated + if !timeout { + if empty { + if err := remote.SendBlockHeaders([]*types.Header{}); err != nil { + t.Fatalf("failed to answer challenge: %v", err) + } + } else if match { + if err := remote.SendBlockHeaders([]*types.Header{response}); err != nil { + t.Fatalf("failed to answer challenge: %v", err) + } + } else { + if err := remote.SendBlockHeaders([]*types.Header{{Number: response.Number}}); err != nil { + t.Fatalf("failed to answer challenge: %v", err) + } + } + } + } + // Wait until the test timeout passes to ensure proper cleanup + time.Sleep(syncChallengeTimeout + 300*time.Millisecond) + + // Verify that the remote peer is maintained or dropped + if drop { + if peers := handler.handler.peers.len(); peers != 0 { + t.Fatalf("peer count mismatch: have %d, want %d", peers, 0) + } + } else { + if peers := handler.handler.peers.len(); peers != 1 { + t.Fatalf("peer count mismatch: have %d, want %d", peers, 1) + } + } +} + +// Tests that blocks are broadcast to a sqrt number of peers only. +func TestBroadcastBlock1Peer(t *testing.T) { testBroadcastBlock(t, 1, 1) } +func TestBroadcastBlock2Peers(t *testing.T) { testBroadcastBlock(t, 2, 1) } +func TestBroadcastBlock3Peers(t *testing.T) { testBroadcastBlock(t, 3, 1) } +func TestBroadcastBlock4Peers(t *testing.T) { testBroadcastBlock(t, 4, 2) } +func TestBroadcastBlock5Peers(t *testing.T) { testBroadcastBlock(t, 5, 2) } +func TestBroadcastBlock8Peers(t *testing.T) { testBroadcastBlock(t, 9, 3) } +func TestBroadcastBlock12Peers(t *testing.T) { testBroadcastBlock(t, 12, 3) } +func TestBroadcastBlock16Peers(t *testing.T) { testBroadcastBlock(t, 16, 4) } +func TestBroadcastBloc26Peers(t *testing.T) { testBroadcastBlock(t, 26, 5) } +func TestBroadcastBlock100Peers(t *testing.T) { testBroadcastBlock(t, 100, 10) } + +func testBroadcastBlock(t *testing.T, peers, bcasts int) { + t.Parallel() + + // Create a source handler to broadcast blocks from and a number of sinks + // to receive them. + source := newTestHandlerWithBlocks(1) + defer source.close() + + sinks := make([]*testEthHandler, peers) + for i := 0; i < len(sinks); i++ { + sinks[i] = new(testEthHandler) + } + // Interconnect all the sink handlers with the source handler + var ( + genesis = source.chain.Genesis() + td = source.chain.GetTd(genesis.Hash(), genesis.NumberU64()) + ) + for i, sink := range sinks { + sink := sink // Closure for gorotuine below + + sourcePipe, sinkPipe := p2p.MsgPipe() + defer sourcePipe.Close() + defer sinkPipe.Close() + + sourcePeer := eth.NewPeer(eth.ETH64, p2p.NewPeer(enode.ID{byte(i)}, "", nil), sourcePipe, nil) + sinkPeer := eth.NewPeer(eth.ETH64, p2p.NewPeer(enode.ID{0}, "", nil), sinkPipe, nil) + defer sourcePeer.Close() + defer sinkPeer.Close() + + go source.handler.runEthPeer(sourcePeer, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(source.handler), peer) + }) + if err := sinkPeer.Handshake(1, td, genesis.Hash(), genesis.Hash(), forkid.NewIDWithChain(source.chain), forkid.NewFilter(source.chain)); err != nil { + t.Fatalf("failed to run protocol handshake") + } + go eth.Handle(sink, sinkPeer) + } + // Subscribe to all the transaction pools + blockChs := make([]chan *types.Block, len(sinks)) + for i := 0; i < len(sinks); i++ { + blockChs[i] = make(chan *types.Block, 1) + defer close(blockChs[i]) + + sub := sinks[i].blockBroadcasts.Subscribe(blockChs[i]) + defer sub.Unsubscribe() + } + // Initiate a block propagation across the peers + time.Sleep(100 * time.Millisecond) + source.handler.BroadcastBlock(source.chain.CurrentBlock(), true) + + // Iterate through all the sinks and ensure the correct number got the block + done := make(chan struct{}, peers) + for _, ch := range blockChs { + ch := ch + go func() { + <-ch + done <- struct{}{} + }() + } + var received int + for { + select { + case <-done: + received++ + + case <-time.After(100 * time.Millisecond): + if received != bcasts { + t.Errorf("broadcast count mismatch: have %d, want %d", received, bcasts) + } + return + } + } +} + +// Tests that a propagated malformed block (uncles or transactions don't match +// with the hashes in the header) gets discarded and not broadcast forward. +func TestBroadcastMalformedBlock64(t *testing.T) { testBroadcastMalformedBlock(t, 64) } +func TestBroadcastMalformedBlock65(t *testing.T) { testBroadcastMalformedBlock(t, 65) } + +func testBroadcastMalformedBlock(t *testing.T, protocol uint) { + t.Parallel() + + // Create a source handler to broadcast blocks from and a number of sinks + // to receive them. + source := newTestHandlerWithBlocks(1) + defer source.close() + + // Create a source handler to send messages through and a sink peer to receive them + p2pSrc, p2pSink := p2p.MsgPipe() + defer p2pSrc.Close() + defer p2pSink.Close() + + src := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pSrc, source.txpool) + sink := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pSink, source.txpool) + defer src.Close() + defer sink.Close() + + go source.handler.runEthPeer(src, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(source.handler), peer) + }) + // Run the handshake locally to avoid spinning up a sink handler + var ( + genesis = source.chain.Genesis() + td = source.chain.GetTd(genesis.Hash(), genesis.NumberU64()) + ) + if err := sink.Handshake(1, td, genesis.Hash(), genesis.Hash(), forkid.NewIDWithChain(source.chain), forkid.NewFilter(source.chain)); err != nil { + t.Fatalf("failed to run protocol handshake") + } + // After the handshake completes, the source handler should stream the sink + // the blocks, subscribe to inbound network events + backend := new(testEthHandler) + + blocks := make(chan *types.Block, 1) + sub := backend.blockBroadcasts.Subscribe(blocks) + defer sub.Unsubscribe() + + go eth.Handle(backend, sink) + + // Create various combinations of malformed blocks + head := source.chain.CurrentBlock() + + malformedUncles := head.Header() + malformedUncles.UncleHash[0]++ + malformedTransactions := head.Header() + malformedTransactions.TxHash[0]++ + malformedEverything := head.Header() + malformedEverything.UncleHash[0]++ + malformedEverything.TxHash[0]++ + + // Try to broadcast all malformations and ensure they all get discarded + for _, header := range []*types.Header{malformedUncles, malformedTransactions, malformedEverything} { + block := types.NewBlockWithHeader(header).WithBody(head.Transactions(), head.Uncles()) + if err := src.SendNewBlock(block, big.NewInt(131136)); err != nil { + t.Fatalf("failed to broadcast block: %v", err) + } + select { + case <-blocks: + t.Fatalf("malformed block forwarded") + case <-time.After(100 * time.Millisecond): + } + } +} diff --git a/eth/handler_snap.go b/eth/handler_snap.go new file mode 100644 index 0000000000..767416ffd6 --- /dev/null +++ b/eth/handler_snap.go @@ -0,0 +1,50 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/eth/protocols/snap" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// snapHandler implements the snap.Backend interface to handle the various network +// packets that are sent as replies or broadcasts. +type snapHandler handler + +func (h *snapHandler) Chain() *core.BlockChain { return h.chain } + +// RunPeer is invoked when a peer joins on the `snap` protocol. +func (h *snapHandler) RunPeer(peer *snap.Peer, hand snap.Handler) error { + return (*handler)(h).runSnapExtension(peer, hand) +} + +// PeerInfo retrieves all known `snap` information about a peer. +func (h *snapHandler) PeerInfo(id enode.ID) interface{} { + if p := h.peers.peer(id.String()); p != nil { + if p.snapExt != nil { + return p.snapExt.info() + } + } + return nil +} + +// Handle is invoked from a peer's message handler when it receives a new remote +// message that the handler couldn't consume and serve itself. +func (h *snapHandler) Handle(peer *snap.Peer, packet snap.Packet) error { + return h.downloader.DeliverSnapPacket(peer, packet) +} diff --git a/eth/handler_test.go b/eth/handler_test.go index b40cced79f..3586d9c33f 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -17,681 +17,156 @@ package eth import ( - "fmt" - "math" "math/big" - "math/rand" - "testing" - "time" + "sort" + "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/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/params/types/coregeth" - "github.com/ethereum/go-ethereum/params/types/ctypes" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/types/genesisT" - "github.com/ethereum/go-ethereum/params/vars" ) -// Tests that block headers can be retrieved from a remote chain based on user queries. -func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) } -func TestGetBlockHeaders64(t *testing.T) { testGetBlockHeaders(t, 64) } +var ( + // testKey is a private key to use for funding a tester account. + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") -func testGetBlockHeaders(t *testing.T, protocol int) { - pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil) - peer, _ := newTestPeer("peer", protocol, pm, true) - defer peer.close() + // testAddr is the Ethereum address of the tester account. + testAddr = crypto.PubkeyToAddress(testKey.PublicKey) +) - // Create a "random" unknown hash for testing - var unknown common.Hash - for i := range unknown { - unknown[i] = byte(i) - } - // Create a batch of tests for various scenarios - limit := uint64(downloader.MaxHeaderFetch) - tests := []struct { - query *getBlockHeadersData // The query to execute for header retrieval - expect []common.Hash // The hashes of the block whose headers are expected - }{ - // A single random block should be retrievable by hash and number too - { - &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, - []common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, - []common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, - }, - // Multiple headers should be retrievable in both directions - { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, - []common.Hash{ - pm.blockchain.GetBlockByNumber(limit / 2).Hash(), - pm.blockchain.GetBlockByNumber(limit/2 + 1).Hash(), - pm.blockchain.GetBlockByNumber(limit/2 + 2).Hash(), - }, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, - []common.Hash{ - pm.blockchain.GetBlockByNumber(limit / 2).Hash(), - pm.blockchain.GetBlockByNumber(limit/2 - 1).Hash(), - pm.blockchain.GetBlockByNumber(limit/2 - 2).Hash(), - }, - }, - // Multiple headers with skip lists should be retrievable - { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, - []common.Hash{ - pm.blockchain.GetBlockByNumber(limit / 2).Hash(), - pm.blockchain.GetBlockByNumber(limit/2 + 4).Hash(), - pm.blockchain.GetBlockByNumber(limit/2 + 8).Hash(), - }, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, - []common.Hash{ - pm.blockchain.GetBlockByNumber(limit / 2).Hash(), - pm.blockchain.GetBlockByNumber(limit/2 - 4).Hash(), - pm.blockchain.GetBlockByNumber(limit/2 - 8).Hash(), - }, - }, - // The chain endpoints should be retrievable - { - &getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1}, - []common.Hash{pm.blockchain.GetBlockByNumber(0).Hash()}, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1}, - []common.Hash{pm.blockchain.CurrentBlock().Hash()}, - }, - // Ensure protocol limits are honored - { - &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, - pm.blockchain.GetBlockHashesFromHash(pm.blockchain.CurrentBlock().Hash(), limit), - }, - // Check that requesting more than available is handled gracefully - { - &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, - []common.Hash{ - pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), - pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()).Hash(), - }, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, - []common.Hash{ - pm.blockchain.GetBlockByNumber(4).Hash(), - pm.blockchain.GetBlockByNumber(0).Hash(), - }, - }, - // Check that requesting more than available is handled gracefully, even if mid skip - { - &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, - []common.Hash{ - pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), - pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 1).Hash(), - }, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, - []common.Hash{ - pm.blockchain.GetBlockByNumber(4).Hash(), - pm.blockchain.GetBlockByNumber(1).Hash(), - }, - }, - // Check a corner case where requesting more can iterate past the endpoints - { - &getBlockHeadersData{Origin: hashOrNumber{Number: 2}, Amount: 5, Reverse: true}, - []common.Hash{ - pm.blockchain.GetBlockByNumber(2).Hash(), - pm.blockchain.GetBlockByNumber(1).Hash(), - pm.blockchain.GetBlockByNumber(0).Hash(), - }, - }, - // Check a corner case where skipping overflow loops back into the chain start - { - &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1}, - []common.Hash{ - pm.blockchain.GetBlockByNumber(3).Hash(), - }, - }, - // Check a corner case where skipping overflow loops back to the same header - { - &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(1).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64}, - []common.Hash{ - pm.blockchain.GetBlockByNumber(1).Hash(), - }, - }, - // Check that non existing headers aren't returned - { - &getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1}, - []common.Hash{}, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() + 1}, Amount: 1}, - []common.Hash{}, - }, - } - // Run each of the tests and verify the results against the chain - for i, tt := range tests { - // Collect the headers to expect in the response - headers := []*types.Header{} - for _, hash := range tt.expect { - headers = append(headers, pm.blockchain.GetBlockByHash(hash).Header()) - } - // Send the hash request and verify the response - p2p.Send(peer.app, 0x03, tt.query) - if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil { - t.Errorf("test %d: headers mismatch: %v", i, err) - } - // If the test used number origins, repeat with hashes as the too - if tt.query.Origin.Hash == (common.Hash{}) { - if origin := pm.blockchain.GetBlockByNumber(tt.query.Origin.Number); origin != nil { - tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0 +// testTxPool is a mock transaction pool that blindly accepts all transactions. +// Its goal is to get around setting up a valid statedb for the balance and nonce +// checks. +type testTxPool struct { + pool map[common.Hash]*types.Transaction // Hash map of collected transactions - p2p.Send(peer.app, 0x03, tt.query) - if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil { - t.Errorf("test %d: headers mismatch: %v", i, err) - } - } - } - } + txFeed event.Feed // Notification feed to allow waiting for inclusion + lock sync.RWMutex // Protects the transaction pool } -// Tests that block contents can be retrieved from a remote chain based on their hashes. -func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) } -func TestGetBlockBodies64(t *testing.T) { testGetBlockBodies(t, 64) } - -func testGetBlockBodies(t *testing.T, protocol int) { - pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil) - peer, _ := newTestPeer("peer", protocol, pm, true) - defer peer.close() - - // Create a batch of tests for various scenarios - limit := downloader.MaxBlockFetch - tests := []struct { - random int // Number of blocks to fetch randomly from the chain - explicit []common.Hash // Explicitly requested blocks - available []bool // Availability of explicitly requested blocks - expected int // Total number of existing blocks to expect - }{ - {1, nil, nil, 1}, // A single random block should be retrievable - {10, nil, nil, 10}, // Multiple random blocks should be retrievable - {limit, nil, nil, limit}, // The maximum possible blocks should be retrievable - {limit + 1, nil, nil, limit}, // No more than the possible block count should be returned - {0, []common.Hash{pm.blockchain.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable - {0, []common.Hash{pm.blockchain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable - {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned - - // Existing and non-existing blocks interleaved should not cause problems - {0, []common.Hash{ - {}, - pm.blockchain.GetBlockByNumber(1).Hash(), - {}, - pm.blockchain.GetBlockByNumber(10).Hash(), - {}, - pm.blockchain.GetBlockByNumber(100).Hash(), - {}, - }, []bool{false, true, false, true, false, true, false}, 3}, - } - // Run each of the tests and verify the results against the chain - for i, tt := range tests { - // Collect the hashes to request, and the response to expect - hashes, seen := []common.Hash{}, make(map[int64]bool) - bodies := []*blockBody{} - - for j := 0; j < tt.random; j++ { - for { - num := rand.Int63n(int64(pm.blockchain.CurrentBlock().NumberU64())) - if !seen[num] { - seen[num] = true - - block := pm.blockchain.GetBlockByNumber(uint64(num)) - hashes = append(hashes, block.Hash()) - if len(bodies) < tt.expected { - bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) - } - break - } - } - } - for j, hash := range tt.explicit { - hashes = append(hashes, hash) - if tt.available[j] && len(bodies) < tt.expected { - block := pm.blockchain.GetBlockByHash(hash) - bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) - } - } - // Send the hash request and verify the response - p2p.Send(peer.app, 0x05, hashes) - if err := p2p.ExpectMsg(peer.app, 0x06, bodies); err != nil { - t.Errorf("test %d: bodies mismatch: %v", i, err) - } +// newTestTxPool creates a mock transaction pool. +func newTestTxPool() *testTxPool { + return &testTxPool{ + pool: make(map[common.Hash]*types.Transaction), } } -// Tests that the node state database can be retrieved based on hashes. -func TestGetNodeData63(t *testing.T) { testGetNodeData(t, 63) } -func TestGetNodeData64(t *testing.T) { testGetNodeData(t, 64) } +// Has returns an indicator whether txpool has a transaction +// cached with the given hash. +func (p *testTxPool) Has(hash common.Hash) bool { + p.lock.Lock() + defer p.lock.Unlock() -func testGetNodeData(t *testing.T, protocol int) { - // Define three accounts to simulate transactions with - acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") - acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") - acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) - acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) - - signer := types.HomesteadSigner{} - // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) - generator := func(i int, block *core.BlockGen) { - switch i { - case 0: - // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), vars.TxGas, 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), acc1Addr, big.NewInt(1000), vars.TxGas, nil, nil), signer, testBankKey) - tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), vars.TxGas, nil, nil), signer, acc1Key) - block.AddTx(tx1) - block.AddTx(tx2) - case 2: - // Block 3 is empty but was mined by account #2. - block.SetCoinbase(acc2Addr) - block.SetExtra([]byte("yeehaw")) - case 3: - // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). - b2 := block.PrevBlock(1).Header() - b2.Extra = []byte("foo") - block.AddUncle(b2) - b3 := block.PrevBlock(2).Header() - b3.Extra = []byte("foo") - block.AddUncle(b3) - } - } - // Assemble the test environment - pm, db := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil) - peer, _ := newTestPeer("peer", protocol, pm, true) - defer peer.close() - - // Fetch for now the entire chain db - hashes := []common.Hash{} - - it := db.NewIterator(nil, nil) - for it.Next() { - if key := it.Key(); len(key) == common.HashLength { - hashes = append(hashes, common.BytesToHash(key)) - } - } - it.Release() - - p2p.Send(peer.app, 0x0d, hashes) - msg, err := peer.app.ReadMsg() - if err != nil { - t.Fatalf("failed to read node data response: %v", err) - } - if msg.Code != 0x0e { - t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, 0x0c) - } - var data [][]byte - if err := msg.Decode(&data); err != nil { - t.Fatalf("failed to decode response node data: %v", err) - } - // Verify that all hashes correspond to the requested data, and reconstruct a state tree - for i, want := range hashes { - if hash := crypto.Keccak256Hash(data[i]); hash != want { - t.Errorf("data hash mismatch: have %x, want %x", hash, want) - } - } - statedb := rawdb.NewMemoryDatabase() - for i := 0; i < len(data); i++ { - statedb.Put(hashes[i].Bytes(), data[i]) - } - accounts := []common.Address{testBank, acc1Addr, acc2Addr} - for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { - trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), state.NewDatabase(statedb), nil) - - for j, acc := range accounts { - state, _ := pm.blockchain.State() - bw := state.GetBalance(acc) - bh := trie.GetBalance(acc) - - if (bw != nil && bh == nil) || (bw == nil && bh != nil) { - t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) - } - if bw != nil && bh != nil && bw.Cmp(bw) != 0 { - t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) - } - } - } + return p.pool[hash] != nil } -// Tests that the transaction receipts can be retrieved based on hashes. -func TestGetReceipt63(t *testing.T) { testGetReceipt(t, 63) } -func TestGetReceipt64(t *testing.T) { testGetReceipt(t, 64) } +// Get retrieves the transaction from local txpool with given +// tx hash. +func (p *testTxPool) Get(hash common.Hash) *types.Transaction { + p.lock.Lock() + defer p.lock.Unlock() -func testGetReceipt(t *testing.T, protocol int) { - // Define three accounts to simulate transactions with - acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") - acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") - acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) - acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) - - signer := types.HomesteadSigner{} - // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) - generator := func(i int, block *core.BlockGen) { - switch i { - case 0: - // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), vars.TxGas, 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), acc1Addr, big.NewInt(1000), vars.TxGas, nil, nil), signer, testBankKey) - tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), vars.TxGas, nil, nil), signer, acc1Key) - block.AddTx(tx1) - block.AddTx(tx2) - case 2: - // Block 3 is empty but was mined by account #2. - block.SetCoinbase(acc2Addr) - block.SetExtra([]byte("yeehaw")) - case 3: - // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). - b2 := block.PrevBlock(1).Header() - b2.Extra = []byte("foo") - block.AddUncle(b2) - b3 := block.PrevBlock(2).Header() - b3.Extra = []byte("foo") - block.AddUncle(b3) - } - } - // Assemble the test environment - pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil) - peer, _ := newTestPeer("peer", protocol, pm, true) - defer peer.close() - - // Collect the hashes to request, and the response to expect - hashes, receipts := []common.Hash{}, []types.Receipts{} - for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { - block := pm.blockchain.GetBlockByNumber(i) - - hashes = append(hashes, block.Hash()) - receipts = append(receipts, pm.blockchain.GetReceiptsByHash(block.Hash())) - } - // Send the hash request and verify the response - p2p.Send(peer.app, 0x0f, hashes) - if err := p2p.ExpectMsg(peer.app, 0x10, receipts); err != nil { - t.Errorf("receipts mismatch: %v", err) - } + return p.pool[hash] } -// Tests that post eth protocol handshake, clients perform a mutual checkpoint -// challenge to validate each other's chains. Hash mismatches, or missing ones -// during a fast sync should lead to the peer getting dropped. -func TestCheckpointChallenge(t *testing.T) { - tests := []struct { - syncmode downloader.SyncMode - checkpoint bool - timeout bool - empty bool - match bool - drop bool - }{ - // If checkpointing is not enabled locally, don't challenge and don't drop - {downloader.FullSync, false, false, false, false, false}, - {downloader.FastSync, false, false, false, false, false}, - - // If checkpointing is enabled locally and remote response is empty, only drop during fast sync - {downloader.FullSync, true, false, true, false, false}, - {downloader.FastSync, true, false, true, false, true}, // Special case, fast sync, unsynced peer - - // If checkpointing is enabled locally and remote response mismatches, always drop - {downloader.FullSync, true, false, false, false, true}, - {downloader.FastSync, true, false, false, false, true}, - - // If checkpointing is enabled locally and remote response matches, never drop - {downloader.FullSync, true, false, false, true, false}, - {downloader.FastSync, true, false, false, true, false}, +// AddRemotes appends a batch of transactions to the pool, and notifies any +// listeners if the addition channel is non nil +func (p *testTxPool) AddRemotes(txs []*types.Transaction) []error { + p.lock.Lock() + defer p.lock.Unlock() - // If checkpointing is enabled locally and remote times out, always drop - {downloader.FullSync, true, true, false, true, true}, - {downloader.FastSync, true, true, false, true, true}, - } - for _, tt := range tests { - t.Run(fmt.Sprintf("sync %v checkpoint %v timeout %v empty %v match %v", tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match), func(t *testing.T) { - testCheckpointChallenge(t, tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match, tt.drop) - }) + for _, tx := range txs { + p.pool[tx.Hash()] = tx } + p.txFeed.Send(core.NewTxsEvent{Txs: txs}) + return make([]error, len(txs)) } -func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpoint bool, timeout bool, empty bool, match bool, drop bool) { - // Reduce the checkpoint handshake challenge timeout - defer func(old time.Duration) { syncChallengeTimeout = old }(syncChallengeTimeout) - syncChallengeTimeout = 250 * time.Millisecond - - // Initialize a chain and generate a fake CHT if checkpointing is enabled - var ( - db = rawdb.NewMemoryDatabase() - config = new(coregeth.CoreGethChainConfig) - ) - core.MustCommitGenesis(db, &genesisT.Genesis{Config: config}) // Commit genesis block - // If checkpointing is enabled, create and inject a fake CHT and the corresponding - // chllenge response. - var response *types.Header - var cht *ctypes.TrustedCheckpoint - if checkpoint { - index := uint64(rand.Intn(500)) - number := (index+1)*vars.CHTFrequency - 1 - response = &types.Header{Number: big.NewInt(int64(number)), Extra: []byte("valid")} +// Pending returns all the transactions known to the pool +func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) { + p.lock.RLock() + defer p.lock.RUnlock() - cht = &ctypes.TrustedCheckpoint{ - SectionIndex: index, - SectionHead: response.Hash(), - } + batches := make(map[common.Address]types.Transactions) + for _, tx := range p.pool { + from, _ := types.Sender(types.HomesteadSigner{}, tx) + batches[from] = append(batches[from], tx) } - // Create a checkpoint aware protocol manager - blockchain, err := core.NewBlockChain(db, nil, config, ethash.NewFaker(), vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("failed to create new blockchain: %v", err) - } - pm, err := NewProtocolManager(config, cht, syncmode, DefaultConfig.NetworkId, new(event.TypeMux), &testTxPool{pool: make(map[common.Hash]*types.Transaction)}, ethash.NewFaker(), blockchain, db, 1, nil) - if err != nil { - t.Fatalf("failed to start test protocol manager: %v", err) - } - pm.Start(1000) - defer pm.Stop() - - // Connect a new peer and check that we receive the checkpoint challenge - peer, _ := newTestPeer("peer", eth63, pm, true) - defer peer.close() - - if checkpoint { - challenge := &getBlockHeadersData{ - Origin: hashOrNumber{Number: response.Number.Uint64()}, - Amount: 1, - Skip: 0, - Reverse: false, - } - if err := p2p.ExpectMsg(peer.app, GetBlockHeadersMsg, challenge); err != nil { - t.Fatalf("challenge mismatch: %v", err) - } - // Create a block to reply to the challenge if no timeout is simulated - if !timeout { - if empty { - if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{}); err != nil { - t.Fatalf("failed to answer challenge: %v", err) - } - } else if match { - if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{response}); err != nil { - t.Fatalf("failed to answer challenge: %v", err) - } - } else { - if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{{Number: response.Number}}); err != nil { - t.Fatalf("failed to answer challenge: %v", err) - } - } - } - } - // Wait until the test timeout passes to ensure proper cleanup - time.Sleep(syncChallengeTimeout + 300*time.Millisecond) - - // Verify that the remote peer is maintained or dropped - if drop { - if peers := pm.peers.Len(); peers != 0 { - t.Fatalf("peer count mismatch: have %d, want %d", peers, 0) - } - } else { - if peers := pm.peers.Len(); peers != 1 { - t.Fatalf("peer count mismatch: have %d, want %d", peers, 1) - } + for _, batch := range batches { + sort.Sort(types.TxByNonce(batch)) } + return batches, nil } -func TestBroadcastBlock(t *testing.T) { - var tests = []struct { - totalPeers int - broadcastExpected int - }{ - {1, 1}, - {2, 1}, - {3, 1}, - {4, 2}, - {5, 2}, - {9, 3}, - {12, 3}, - {16, 4}, - {26, 5}, - {100, 10}, - } - for _, test := range tests { - testBroadcastBlock(t, test.totalPeers, test.broadcastExpected) - } +// SubscribeNewTxsEvent should return an event subscription of NewTxsEvent and +// send events to the given channel. +func (p *testTxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { + return p.txFeed.Subscribe(ch) } -func testBroadcastBlock(t *testing.T, totalPeers, broadcastExpected int) { - var ( - evmux = new(event.TypeMux) - pow = ethash.NewFaker() - db = rawdb.NewMemoryDatabase() - config = &coregeth.CoreGethChainConfig{} - gspec = &genesisT.Genesis{Config: config} - genesis = core.MustCommitGenesis(db, gspec) - ) - blockchain, err := core.NewBlockChain(db, nil, config, pow, vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("failed to create new blockchain: %v", err) - } - pm, err := NewProtocolManager(config, nil, downloader.FullSync, DefaultConfig.NetworkId, evmux, &testTxPool{pool: make(map[common.Hash]*types.Transaction)}, pow, blockchain, db, 1, nil) - if err != nil { - t.Fatalf("failed to start test protocol manager: %v", err) - } - pm.Start(1000) - defer pm.Stop() - var peers []*testPeer - for i := 0; i < totalPeers; i++ { - peer, _ := newTestPeer(fmt.Sprintf("peer %d", i), eth63, pm, true) - defer peer.close() - - peers = append(peers, peer) - } - chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, func(i int, gen *core.BlockGen) {}) - pm.BroadcastBlock(chain[0], true /*propagate*/) - - errCh := make(chan error, totalPeers) - doneCh := make(chan struct{}, totalPeers) - for _, peer := range peers { - go func(p *testPeer) { - if err := p2p.ExpectMsg(p.app, NewBlockMsg, &newBlockData{Block: chain[0], TD: big.NewInt(131136)}); err != nil { - errCh <- err - } else { - doneCh <- struct{}{} - } - }(peer) - } - var received int - for { - select { - case <-doneCh: - received++ - if received > broadcastExpected { - // We can bail early here - t.Errorf("broadcast count mismatch: have %d > want %d", received, broadcastExpected) - return - } - case <-time.After(2 * time.Second): - if received != broadcastExpected { - t.Errorf("broadcast count mismatch: have %d, want %d", received, broadcastExpected) - } - return - case err = <-errCh: - t.Fatalf("broadcast failed: %v", err) - } - } +// testHandler is a live implementation of the Ethereum protocol handler, just +// preinitialized with some sane testing defaults and the transaction pool mocked +// out. +type testHandler struct { + db ethdb.Database + chain *core.BlockChain + txpool *testTxPool + handler *handler +} +// newTestHandler creates a new handler for testing purposes with no blocks. +func newTestHandler() *testHandler { + return newTestHandlerWithBlocks(0) } -// Tests that a propagated malformed block (uncles or transactions don't match -// with the hashes in the header) gets discarded and not broadcast forward. -func TestBroadcastMalformedBlock(t *testing.T) { - // Create a live node to test propagation with - var ( - engine = ethash.NewFaker() - db = rawdb.NewMemoryDatabase() - config = &coregeth.CoreGethChainConfig{} - gspec = &genesisT.Genesis{Config: config} - genesis = core.MustCommitGenesis(db, gspec) - ) - blockchain, err := core.NewBlockChain(db, nil, config, engine, vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("failed to create new blockchain: %v", err) +// newTestHandlerWithBlocks creates a new handler for testing purposes, with a +// given number of initial blocks. +func newTestHandlerWithBlocks(blocks int) *testHandler { + // Create a database pre-initialize with a genesis block + db := rawdb.NewMemoryDatabase() + gspec := &genesisT.Genesis{ + Config: params.TestChainConfig, + Alloc: genesisT.GenesisAlloc{testAddr: {Balance: big.NewInt(1000000)}}, + } + core.MustCommitGenesis(db, gspec) + + chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + + bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, nil) + if _, err := chain.InsertChain(bs); err != nil { + panic(err) + } + txpool := newTestTxPool() + + handler, _ := newHandler(&handlerConfig{ + Database: db, + Chain: chain, + TxPool: txpool, + Network: 1, + Sync: downloader.FastSync, + BloomCache: 1, + }) + handler.Start(1000) + + return &testHandler{ + db: db, + chain: chain, + txpool: txpool, + handler: handler, } - pm, err := NewProtocolManager(config, nil, downloader.FullSync, DefaultConfig.NetworkId, new(event.TypeMux), new(testTxPool), engine, blockchain, db, 1, nil) - if err != nil { - t.Fatalf("failed to start test protocol manager: %v", err) - } - pm.Start(2) - defer pm.Stop() - - // Create two peers, one to send the malformed block with and one to check - // propagation - source, _ := newTestPeer("source", eth63, pm, true) - defer source.close() - - sink, _ := newTestPeer("sink", eth63, pm, true) - defer sink.close() - - // Create various combinations of malformed blocks - chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, func(i int, gen *core.BlockGen) {}) - - malformedUncles := chain[0].Header() - malformedUncles.UncleHash[0]++ - malformedTransactions := chain[0].Header() - malformedTransactions.TxHash[0]++ - malformedEverything := chain[0].Header() - malformedEverything.UncleHash[0]++ - malformedEverything.TxHash[0]++ +} - // Keep listening to broadcasts and notify if any arrives - notify := make(chan struct{}, 1) - go func() { - if _, err := sink.app.ReadMsg(); err == nil { - notify <- struct{}{} - } - }() - // Try to broadcast all malformations and ensure they all get discarded - for _, header := range []*types.Header{malformedUncles, malformedTransactions, malformedEverything} { - block := types.NewBlockWithHeader(header).WithBody(chain[0].Transactions(), chain[0].Uncles()) - if err := p2p.Send(source.app, NewBlockMsg, []interface{}{block, big.NewInt(131136)}); err != nil { - t.Fatalf("failed to broadcast block: %v", err) - } - select { - case <-notify: - t.Fatalf("malformed block forwarded") - case <-time.After(100 * time.Millisecond): - } - } +// close tears down the handler and all its internal constructs. +func (b *testHandler) close() { + b.handler.Stop() + b.chain.Stop() } diff --git a/eth/helper_test.go b/eth/helper_test.go deleted file mode 100644 index eb70b8f32c..0000000000 --- a/eth/helper_test.go +++ /dev/null @@ -1,232 +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 . - -// This file contains some shares testing functionality, common to multiple -// different files and modules being tested. - -package eth - -import ( - "crypto/ecdsa" - "crypto/rand" - "fmt" - "math/big" - "sort" - "sync" - "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/forkid" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/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/enode" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/params/types/genesisT" -) - -var ( - testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - 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(mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, ethdb.Database, error) { - var ( - evmux = new(event.TypeMux) - engine = ethash.NewFaker() - db = rawdb.NewMemoryDatabase() - gspec = &genesisT.Genesis{ - Config: params.TestChainConfig, - Alloc: genesisT.GenesisAlloc{testBank: {Balance: big.NewInt(1000000)}}, - } - genesis = core.MustCommitGenesis(db, gspec) - blockchain, _ = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil) - ) - chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator) - if _, err := blockchain.InsertChain(chain); err != nil { - panic(err) - } - pm, err := NewProtocolManager(gspec.Config, nil, mode, DefaultConfig.NetworkId, evmux, &testTxPool{added: newtx, pool: make(map[common.Hash]*types.Transaction)}, engine, blockchain, db, 1, nil) - if err != nil { - return nil, nil, err - } - pm.Start(1000) - return pm, db, nil -} - -// newTestProtocolManagerMust creates a new protocol manager for testing purposes, -// 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, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, ethdb.Database) { - pm, db, err := newTestProtocolManager(mode, blocks, generator, newtx) - if err != nil { - t.Fatalf("Failed to create protocol manager: %v", err) - } - return pm, db -} - -// testTxPool is a fake, helper transaction pool for testing purposes -type testTxPool struct { - txFeed event.Feed - pool map[common.Hash]*types.Transaction // Hash map of collected transactions - added chan<- []*types.Transaction // Notification channel for new transactions - - lock sync.RWMutex // Protects the transaction pool -} - -// Has returns an indicator whether txpool has a transaction -// cached with the given hash. -func (p *testTxPool) Has(hash common.Hash) bool { - p.lock.Lock() - defer p.lock.Unlock() - - return p.pool[hash] != nil -} - -// Get retrieves the transaction from local txpool with given -// tx hash. -func (p *testTxPool) Get(hash common.Hash) *types.Transaction { - p.lock.Lock() - defer p.lock.Unlock() - - return p.pool[hash] -} - -// AddRemotes appends a batch of transactions to the pool, and notifies any -// listeners if the addition channel is non nil -func (p *testTxPool) AddRemotes(txs []*types.Transaction) []error { - p.lock.Lock() - defer p.lock.Unlock() - - for _, tx := range txs { - p.pool[tx.Hash()] = tx - } - if p.added != nil { - p.added <- txs - } - p.txFeed.Send(core.NewTxsEvent{Txs: txs}) - return make([]error, len(txs)) -} - -// Pending returns all the transactions known to the pool -func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) { - p.lock.RLock() - defer p.lock.RUnlock() - - batches := make(map[common.Address]types.Transactions) - for _, tx := range p.pool { - from, _ := types.Sender(types.HomesteadSigner{}, tx) - batches[from] = append(batches[from], tx) - } - for _, batch := range batches { - sort.Sort(types.TxByNonce(batch)) - } - return batches, nil -} - -func (p *testTxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { - return p.txFeed.Subscribe(ch) -} - -// newTestTransaction create a new dummy transaction. -func newTestTransaction(from *ecdsa.PrivateKey, nonce uint64, datasize int) *types.Transaction { - tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, datasize)) - tx, _ = types.SignTx(tx, types.HomesteadSigner{}, from) - return tx -} - -// testPeer is a simulated peer to allow testing direct network calls. -type testPeer struct { - net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging - app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side - *peer -} - -// newTestPeer creates a new peer registered at the given protocol manager. -func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*testPeer, <-chan error) { - // Create a message pipe to communicate through - app, net := p2p.MsgPipe() - - // Start the peer on a new thread - var id enode.ID - rand.Read(id[:]) - peer := pm.newPeer(version, p2p.NewPeer(id, name, nil), net, pm.txpool.Get) - errc := make(chan error, 1) - go func() { errc <- pm.runPeer(peer) }() - tp := &testPeer{app: app, net: net, peer: peer} - - // Execute any implicitly requested handshakes and return - if shake { - var ( - genesis = pm.blockchain.Genesis() - head = pm.blockchain.CurrentHeader() - td = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64()) - ) - forkID := forkid.NewID(pm.blockchain.Config(), pm.blockchain.Genesis().Hash(), pm.blockchain.CurrentHeader().Number.Uint64()) - tp.handshake(nil, td, head.Hash(), genesis.Hash(), forkID, forkid.NewFilter(pm.blockchain)) - } - return tp, errc -} - -// handshake simulates a trivial handshake that expects the same state from the -// remote side as we are simulating locally. -func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) { - var msg interface{} - switch { - case p.version == eth63: - msg = &statusData63{ - ProtocolVersion: uint32(p.version), - NetworkId: DefaultConfig.NetworkId, - TD: td, - CurrentBlock: head, - GenesisBlock: genesis, - } - case p.version >= eth64: - msg = &statusData{ - ProtocolVersion: uint32(p.version), - NetworkID: DefaultConfig.NetworkId, - TD: td, - Head: head, - Genesis: genesis, - ForkID: forkID, - } - default: - panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) - } - if err := p2p.ExpectMsg(p.app, StatusMsg, msg); err != nil { - t.Fatalf("status recv: %v", err) - } - if err := p2p.Send(p.app, StatusMsg, msg); err != nil { - t.Fatalf("status send: %v", err) - } -} - -// close terminates the local side of the peer, notifying the remote protocol -// manager of termination. -func (p *testPeer) close() { - p.app.Close() -} diff --git a/eth/peer.go b/eth/peer.go index ee3f28b43f..1cea9c640e 100644 --- a/eth/peer.go +++ b/eth/peer.go @@ -17,823 +17,57 @@ package eth import ( - "errors" - "fmt" "math/big" "sync" "time" - mapset "github.com/deckarep/golang-set" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/forkid" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/eth/protocols/snap" ) -var ( - errClosed = errors.New("peer set is closed") - errAlreadyRegistered = errors.New("peer is already registered") - errNotRegistered = errors.New("peer is not registered") -) - -const ( - maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS) - maxKnownBlocks = 1024 // Maximum block hashes to keep in the known list (prevent DOS) - - // maxQueuedTxs is the maximum number of transactions to queue up before dropping - // older broadcasts. - maxQueuedTxs = 4096 - - // maxQueuedTxAnns is the maximum number of transaction announcements to queue up - // before dropping older announcements. - maxQueuedTxAnns = 4096 - - // maxQueuedBlocks is the maximum number of block propagations to queue up before - // dropping broadcasts. There's not much point in queueing stale blocks, so a few - // that might cover uncles should be enough. - maxQueuedBlocks = 4 - - // maxQueuedBlockAnns is the maximum number of block announcements to queue up before - // dropping broadcasts. Similarly to block propagations, there's no point to queue - // above some healthy uncle limit, so use that. - maxQueuedBlockAnns = 4 - - handshakeTimeout = 5 * time.Second -) - -// max is a helper function which returns the larger of the two given integers. -func max(a, b int) int { - if a > b { - return a - } - return b -} - -// PeerInfo represents a short summary of the Ethereum sub-protocol metadata known +// ethPeerInfo represents a short summary of the `eth` sub-protocol metadata known // about a connected peer. -type PeerInfo struct { - Version int `json:"version"` // Ethereum protocol version negotiated +type ethPeerInfo struct { + Version uint `json:"version"` // Ethereum protocol version negotiated Difficulty *big.Int `json:"difficulty"` // Total difficulty of the peer's blockchain - Head string `json:"head"` // SHA3 hash of the peer's best owned block + Head string `json:"head"` // Hex hash of the peer's best owned block } -// propEvent is a block propagation, waiting for its turn in the broadcast queue. -type propEvent struct { - block *types.Block - td *big.Int -} - -type peer struct { - id string - - *p2p.Peer - rw p2p.MsgReadWriter - - version int // Protocol version negotiated - syncDrop *time.Timer // Timed connection dropper if sync progress isn't validated in time - - head common.Hash - td *big.Int - lock sync.RWMutex - - knownBlocks mapset.Set // Set of block hashes known to be known by this peer - queuedBlocks chan *propEvent // Queue of blocks to broadcast to the peer - queuedBlockAnns chan *types.Block // Queue of blocks to announce to the peer +// ethPeer is a wrapper around eth.Peer to maintain a few extra metadata. +type ethPeer struct { + *eth.Peer + snapExt *snapPeer // Satellite `snap` connection - knownTxs mapset.Set // Set of transaction hashes known to be known by this peer - txBroadcast chan []common.Hash // Channel used to queue transaction propagation requests - txAnnounce chan []common.Hash // Channel used to queue transaction announcement requests - getPooledTx func(common.Hash) *types.Transaction // Callback used to retrieve transaction from txpool - - term chan struct{} // Termination channel to stop the broadcaster + syncDrop *time.Timer // Connection dropper if `eth` sync progress isn't validated in time + snapWait chan struct{} // Notification channel for snap connections + lock sync.RWMutex // Mutex protecting the internal fields } -func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter, getPooledTx func(hash common.Hash) *types.Transaction) *peer { - return &peer{ - Peer: p, - rw: rw, - version: version, - id: fmt.Sprintf("%x", p.ID().Bytes()[:8]), - knownTxs: mapset.NewSet(), - knownBlocks: mapset.NewSet(), - queuedBlocks: make(chan *propEvent, maxQueuedBlocks), - queuedBlockAnns: make(chan *types.Block, maxQueuedBlockAnns), - txBroadcast: make(chan []common.Hash), - txAnnounce: make(chan []common.Hash), - getPooledTx: getPooledTx, - term: make(chan struct{}), - } -} - -// broadcastBlocks is a write loop that multiplexes blocks and block accouncements -// to the remote peer. The goal is to have an async writer that does not lock up -// node internals and at the same time rate limits queued data. -func (p *peer) broadcastBlocks(removePeer func(string)) { - for { - select { - case prop := <-p.queuedBlocks: - if err := p.SendNewBlock(prop.block, prop.td); err != nil { - removePeer(p.id) - return - } - p.Log().Trace("Propagated block", "number", prop.block.Number(), "hash", prop.block.Hash(), "td", prop.td) - - case block := <-p.queuedBlockAnns: - if err := p.SendNewBlockHashes([]common.Hash{block.Hash()}, []uint64{block.NumberU64()}); err != nil { - removePeer(p.id) - return - } - p.Log().Trace("Announced block", "number", block.Number(), "hash", block.Hash()) - - case <-p.term: - return - } - } -} - -// broadcastTransactions is a write loop that schedules transaction broadcasts -// to the remote peer. The goal is to have an async writer that does not lock up -// node internals and at the same time rate limits queued data. -func (p *peer) broadcastTransactions(removePeer func(string)) { - var ( - queue []common.Hash // Queue of hashes to broadcast as full transactions - done chan struct{} // Non-nil if background broadcaster is running - fail = make(chan error, 1) // Channel used to receive network error - ) - for { - // If there's no in-flight broadcast running, check if a new one is needed - if done == nil && len(queue) > 0 { - // Pile transaction until we reach our allowed network limit - var ( - hashes []common.Hash - txs []*types.Transaction - size common.StorageSize - ) - for i := 0; i < len(queue) && size < txsyncPackSize; i++ { - if tx := p.getPooledTx(queue[i]); tx != nil { - txs = append(txs, tx) - size += tx.Size() - } - hashes = append(hashes, queue[i]) - } - queue = queue[:copy(queue, queue[len(hashes):])] - - // If there's anything available to transfer, fire up an async writer - if len(txs) > 0 { - done = make(chan struct{}) - go func() { - if err := p.sendTransactions(txs); err != nil { - fail <- err - return - } - close(done) - p.Log().Trace("Sent transactions", "count", len(txs)) - }() - } - } - // Transfer goroutine may or may not have been started, listen for events - select { - case hashes := <-p.txBroadcast: - // New batch of transactions to be broadcast, queue them (with cap) - queue = append(queue, hashes...) - if len(queue) > maxQueuedTxs { - // Fancy copy and resize to ensure buffer doesn't grow indefinitely - queue = queue[:copy(queue, queue[len(queue)-maxQueuedTxs:])] - } - - case <-done: - done = nil - - case <-fail: - removePeer(p.id) - return - - case <-p.term: - return - } - } -} - -// announceTransactions is a write loop that schedules transaction broadcasts -// to the remote peer. The goal is to have an async writer that does not lock up -// node internals and at the same time rate limits queued data. -func (p *peer) announceTransactions(removePeer func(string)) { - var ( - queue []common.Hash // Queue of hashes to announce as transaction stubs - done chan struct{} // Non-nil if background announcer is running - fail = make(chan error, 1) // Channel used to receive network error - ) - for { - // If there's no in-flight announce running, check if a new one is needed - if done == nil && len(queue) > 0 { - // Pile transaction hashes until we reach our allowed network limit - var ( - hashes []common.Hash - pending []common.Hash - size common.StorageSize - ) - for i := 0; i < len(queue) && size < txsyncPackSize; i++ { - if p.getPooledTx(queue[i]) != nil { - pending = append(pending, queue[i]) - size += common.HashLength - } - hashes = append(hashes, queue[i]) - } - queue = queue[:copy(queue, queue[len(hashes):])] - - // If there's anything available to transfer, fire up an async writer - if len(pending) > 0 { - done = make(chan struct{}) - go func() { - if err := p.sendPooledTransactionHashes(pending); err != nil { - fail <- err - return - } - close(done) - p.Log().Trace("Sent transaction announcements", "count", len(pending)) - }() - } - } - // Transfer goroutine may or may not have been started, listen for events - select { - case hashes := <-p.txAnnounce: - // New batch of transactions to be broadcast, queue them (with cap) - queue = append(queue, hashes...) - if len(queue) > maxQueuedTxAnns { - // Fancy copy and resize to ensure buffer doesn't grow indefinitely - queue = queue[:copy(queue, queue[len(queue)-maxQueuedTxAnns:])] - } - - case <-done: - done = nil - - case <-fail: - removePeer(p.id) - return - - case <-p.term: - return - } - } -} - -// close signals the broadcast goroutine to terminate. -func (p *peer) close() { - close(p.term) -} - -// Info gathers and returns a collection of metadata known about a peer. -func (p *peer) Info() *PeerInfo { +// info gathers and returns some `eth` protocol metadata known about a peer. +func (p *ethPeer) info() *ethPeerInfo { hash, td := p.Head() - return &PeerInfo{ - Version: p.version, + return ðPeerInfo{ + Version: p.Version(), Difficulty: td, Head: hash.Hex(), } } -// Head retrieves a copy of the current head hash and total difficulty of the -// peer. -func (p *peer) Head() (hash common.Hash, td *big.Int) { - p.lock.RLock() - defer p.lock.RUnlock() - - copy(hash[:], p.head[:]) - return hash, new(big.Int).Set(p.td) -} - -// SetHead updates the head hash and total difficulty of the peer. -func (p *peer) SetHead(hash common.Hash, td *big.Int) { - p.lock.Lock() - defer p.lock.Unlock() - - copy(p.head[:], hash[:]) - p.td.Set(td) -} - -// MarkBlock marks a block as known for the peer, ensuring that the block will -// never be propagated to this particular peer. -func (p *peer) MarkBlock(hash common.Hash) { - // If we reached the memory allowance, drop a previously known block hash - for p.knownBlocks.Cardinality() >= maxKnownBlocks { - p.knownBlocks.Pop() - } - p.knownBlocks.Add(hash) -} - -// MarkTransaction marks a transaction as known for the peer, ensuring that it -// will never be propagated to this particular peer. -func (p *peer) MarkTransaction(hash common.Hash) { - // If we reached the memory allowance, drop a previously known transaction hash - for p.knownTxs.Cardinality() >= maxKnownTxs { - p.knownTxs.Pop() - } - p.knownTxs.Add(hash) -} - -// SendTransactions64 sends transactions to the peer and includes the hashes -// in its transaction hash set for future reference. -// -// This method is legacy support for initial transaction exchange in eth/64 and -// prior. For eth/65 and higher use SendPooledTransactionHashes. -func (p *peer) SendTransactions64(txs types.Transactions) error { - return p.sendTransactions(txs) -} - -// sendTransactions sends transactions to the peer and includes the hashes -// in its transaction hash set for future reference. -// -// This method is a helper used by the async transaction sender. Don't call it -// directly as the queueing (memory) and transmission (bandwidth) costs should -// not be managed directly. -func (p *peer) sendTransactions(txs types.Transactions) error { - // Mark all the transactions as known, but ensure we don't overflow our limits - for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(txs)) { - p.knownTxs.Pop() - } - for _, tx := range txs { - p.knownTxs.Add(tx.Hash()) - } - return p2p.Send(p.rw, TransactionMsg, txs) -} - -// AsyncSendTransactions queues a list of transactions (by hash) to eventually -// propagate to a remote peer. The number of pending sends are capped (new ones -// will force old sends to be dropped) -func (p *peer) AsyncSendTransactions(hashes []common.Hash) { - select { - case p.txBroadcast <- hashes: - // Mark all the transactions as known, but ensure we don't overflow our limits - for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { - p.knownTxs.Pop() - } - for _, hash := range hashes { - p.knownTxs.Add(hash) - } - case <-p.term: - p.Log().Debug("Dropping transaction propagation", "count", len(hashes)) - } -} - -// sendPooledTransactionHashes sends transaction hashes to the peer and includes -// them in its transaction hash set for future reference. -// -// This method is a helper used by the async transaction announcer. Don't call it -// directly as the queueing (memory) and transmission (bandwidth) costs should -// not be managed directly. -func (p *peer) sendPooledTransactionHashes(hashes []common.Hash) error { - // Mark all the transactions as known, but ensure we don't overflow our limits - for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { - p.knownTxs.Pop() - } - for _, hash := range hashes { - p.knownTxs.Add(hash) - } - return p2p.Send(p.rw, NewPooledTransactionHashesMsg, hashes) -} - -// AsyncSendPooledTransactionHashes queues a list of transactions hashes to eventually -// announce to a remote peer. The number of pending sends are capped (new ones -// will force old sends to be dropped) -func (p *peer) AsyncSendPooledTransactionHashes(hashes []common.Hash) { - select { - case p.txAnnounce <- hashes: - // Mark all the transactions as known, but ensure we don't overflow our limits - for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { - p.knownTxs.Pop() - } - for _, hash := range hashes { - p.knownTxs.Add(hash) - } - case <-p.term: - p.Log().Debug("Dropping transaction announcement", "count", len(hashes)) - } -} - -// SendPooledTransactionsRLP sends requested transactions to the peer and adds the -// hashes in its transaction hash set for future reference. -// -// Note, the method assumes the hashes are correct and correspond to the list of -// transactions being sent. -func (p *peer) SendPooledTransactionsRLP(hashes []common.Hash, txs []rlp.RawValue) error { - // Mark all the transactions as known, but ensure we don't overflow our limits - for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { - p.knownTxs.Pop() - } - for _, hash := range hashes { - p.knownTxs.Add(hash) - } - return p2p.Send(p.rw, PooledTransactionsMsg, txs) -} - -// SendNewBlockHashes announces the availability of a number of blocks through -// a hash notification. -func (p *peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error { - // Mark all the block hashes as known, but ensure we don't overflow our limits - for p.knownBlocks.Cardinality() > max(0, maxKnownBlocks-len(hashes)) { - p.knownBlocks.Pop() - } - for _, hash := range hashes { - p.knownBlocks.Add(hash) - } - request := make(newBlockHashesData, len(hashes)) - for i := 0; i < len(hashes); i++ { - request[i].Hash = hashes[i] - request[i].Number = numbers[i] - } - return p2p.Send(p.rw, NewBlockHashesMsg, request) -} - -// AsyncSendNewBlockHash queues the availability of a block for propagation to a -// remote peer. If the peer's broadcast queue is full, the event is silently -// dropped. -func (p *peer) AsyncSendNewBlockHash(block *types.Block) { - select { - case p.queuedBlockAnns <- block: - // Mark all the block hash as known, but ensure we don't overflow our limits - for p.knownBlocks.Cardinality() >= maxKnownBlocks { - p.knownBlocks.Pop() - } - p.knownBlocks.Add(block.Hash()) - default: - p.Log().Debug("Dropping block announcement", "number", block.NumberU64(), "hash", block.Hash()) - } -} - -// SendNewBlock propagates an entire block to a remote peer. -func (p *peer) SendNewBlock(block *types.Block, td *big.Int) error { - // Mark all the block hash as known, but ensure we don't overflow our limits - for p.knownBlocks.Cardinality() >= maxKnownBlocks { - p.knownBlocks.Pop() - } - p.knownBlocks.Add(block.Hash()) - return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, td}) -} - -// AsyncSendNewBlock queues an entire block for propagation to a remote peer. If -// the peer's broadcast queue is full, the event is silently dropped. -func (p *peer) AsyncSendNewBlock(block *types.Block, td *big.Int) { - select { - case p.queuedBlocks <- &propEvent{block: block, td: td}: - // Mark all the block hash as known, but ensure we don't overflow our limits - for p.knownBlocks.Cardinality() >= maxKnownBlocks { - p.knownBlocks.Pop() - } - p.knownBlocks.Add(block.Hash()) - default: - p.Log().Debug("Dropping block propagation", "number", block.NumberU64(), "hash", block.Hash()) - } -} - -// SendBlockHeaders sends a batch of block headers to the remote peer. -func (p *peer) SendBlockHeaders(headers []*types.Header) error { - return p2p.Send(p.rw, BlockHeadersMsg, headers) -} - -// SendBlockBodies sends a batch of block contents to the remote peer. -func (p *peer) SendBlockBodies(bodies []*blockBody) error { - return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies)) -} - -// SendBlockBodiesRLP sends a batch of block contents to the remote peer from -// an already RLP encoded format. -func (p *peer) SendBlockBodiesRLP(bodies []rlp.RawValue) error { - return p2p.Send(p.rw, BlockBodiesMsg, bodies) -} - -// SendNodeDataRLP sends a batch of arbitrary internal data, corresponding to the -// hashes requested. -func (p *peer) SendNodeData(data [][]byte) error { - return p2p.Send(p.rw, NodeDataMsg, data) -} - -// SendReceiptsRLP sends a batch of transaction receipts, corresponding to the -// ones requested from an already RLP encoded format. -func (p *peer) SendReceiptsRLP(receipts []rlp.RawValue) error { - return p2p.Send(p.rw, ReceiptsMsg, receipts) -} - -// RequestOneHeader is a wrapper around the header query functions to fetch a -// single header. It is used solely by the fetcher. -func (p *peer) RequestOneHeader(hash common.Hash) error { - p.Log().Debug("Fetching single header", "hash", hash) - return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: uint64(1), Skip: uint64(0), Reverse: false}) -} - -// RequestHeadersByHash fetches a batch of blocks' headers corresponding to the -// specified header query, based on the hash of an origin block. -func (p *peer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error { - p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse) - return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) -} - -// RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the -// specified header query, based on the number of an origin block. -func (p *peer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error { - p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse) - return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) -} - -// RequestBodies fetches a batch of blocks' bodies corresponding to the hashes -// specified. -func (p *peer) RequestBodies(hashes []common.Hash) error { - p.Log().Debug("Fetching batch of block bodies", "count", len(hashes)) - return p2p.Send(p.rw, GetBlockBodiesMsg, hashes) -} - -// RequestNodeData fetches a batch of arbitrary data from a node's known state -// data, corresponding to the specified hashes. -func (p *peer) RequestNodeData(hashes []common.Hash) error { - p.Log().Debug("Fetching batch of state data", "count", len(hashes)) - return p2p.Send(p.rw, GetNodeDataMsg, hashes) -} - -// RequestReceipts fetches a batch of transaction receipts from a remote node. -func (p *peer) RequestReceipts(hashes []common.Hash) error { - p.Log().Debug("Fetching batch of receipts", "count", len(hashes)) - return p2p.Send(p.rw, GetReceiptsMsg, hashes) -} - -// RequestTxs fetches a batch of transactions from a remote node. -func (p *peer) RequestTxs(hashes []common.Hash) error { - p.Log().Debug("Fetching batch of transactions", "count", len(hashes)) - return p2p.Send(p.rw, GetPooledTransactionsMsg, hashes) -} - -// Handshake executes the eth protocol handshake, negotiating version number, -// network IDs, difficulties, head and genesis blocks. -func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) error { - // Send out own handshake in a new thread - errc := make(chan error, 2) - - var ( - status63 statusData63 // safe to read after two values have been received from errc - status statusData // safe to read after two values have been received from errc - ) - go func() { - switch { - case p.version == eth63: - errc <- p2p.Send(p.rw, StatusMsg, &statusData63{ - ProtocolVersion: uint32(p.version), - NetworkId: network, - TD: td, - CurrentBlock: head, - GenesisBlock: genesis, - }) - case p.version >= eth64: - errc <- p2p.Send(p.rw, StatusMsg, &statusData{ - ProtocolVersion: uint32(p.version), - NetworkID: network, - TD: td, - Head: head, - Genesis: genesis, - ForkID: forkID, - }) - default: - panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) - } - }() - go func() { - switch { - case p.version == eth63: - errc <- p.readStatusLegacy(network, &status63, genesis) - case p.version >= eth64: - errc <- p.readStatus(network, &status, genesis, forkFilter) - default: - panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) - } - }() - timeout := time.NewTimer(handshakeTimeout) - defer timeout.Stop() - for i := 0; i < 2; i++ { - select { - case err := <-errc: - if err != nil { - return err - } - case <-timeout.C: - return p2p.DiscReadTimeout - } - } - switch { - case p.version == eth63: - p.td, p.head = status63.TD, status63.CurrentBlock - case p.version >= eth64: - p.td, p.head = status.TD, status.Head - default: - panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) - } - return nil -} - -func (p *peer) readStatusLegacy(network uint64, status *statusData63, genesis common.Hash) error { - msg, err := p.rw.ReadMsg() - if err != nil { - return err - } - if msg.Code != StatusMsg { - return errResp(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg) - } - if msg.Size > protocolMaxMsgSize { - return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, protocolMaxMsgSize) - } - // Decode the handshake and make sure everything matches - if err := msg.Decode(&status); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - if status.GenesisBlock != genesis { - return errResp(ErrGenesisMismatch, "%x (!= %x)", status.GenesisBlock[:8], genesis[:8]) - } - if status.NetworkId != network { - return errResp(ErrNetworkIDMismatch, "%d (!= %d)", status.NetworkId, network) - } - if int(status.ProtocolVersion) != p.version { - return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version) - } - return nil -} - -func (p *peer) readStatus(network uint64, status *statusData, genesis common.Hash, forkFilter forkid.Filter) error { - msg, err := p.rw.ReadMsg() - if err != nil { - return err - } - if msg.Code != StatusMsg { - return errResp(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg) - } - if msg.Size > protocolMaxMsgSize { - return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, protocolMaxMsgSize) - } - // Decode the handshake and make sure everything matches - if err := msg.Decode(&status); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - if status.NetworkID != network { - return errResp(ErrNetworkIDMismatch, "%d (!= %d)", status.NetworkID, network) - } - if int(status.ProtocolVersion) != p.version { - return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version) - } - if status.Genesis != genesis { - return errResp(ErrGenesisMismatch, "%x (!= %x)", status.Genesis, genesis) - } - if err := forkFilter(status.ForkID); err != nil { - return errResp(ErrForkIDRejected, "%v", err) - } - return nil -} - -// String implements fmt.Stringer. -func (p *peer) String() string { - return fmt.Sprintf("Peer %s [%s]", p.id, - fmt.Sprintf("eth/%2d", p.version), - ) -} - -// peerSet represents the collection of active peers currently participating in -// the Ethereum sub-protocol. -type peerSet struct { - peers map[string]*peer - lock sync.RWMutex - closed bool -} - -// newPeerSet creates a new peer set to track the active participants. -func newPeerSet() *peerSet { - return &peerSet{ - peers: make(map[string]*peer), - } -} - -// Register injects a new peer into the working set, or returns an error if the -// peer is already known. If a new peer it registered, its broadcast loop is also -// started. -func (ps *peerSet) Register(p *peer, removePeer func(string)) error { - ps.lock.Lock() - defer ps.lock.Unlock() - - if ps.closed { - return errClosed - } - if _, ok := ps.peers[p.id]; ok { - return errAlreadyRegistered - } - ps.peers[p.id] = p - - go p.broadcastBlocks(removePeer) - go p.broadcastTransactions(removePeer) - if p.version >= eth65 { - go p.announceTransactions(removePeer) - } - return nil -} - -// Unregister removes a remote peer from the active set, disabling any further -// actions to/from that particular entity. -func (ps *peerSet) Unregister(id string) error { - ps.lock.Lock() - defer ps.lock.Unlock() - - p, ok := ps.peers[id] - if !ok { - return errNotRegistered - } - delete(ps.peers, id) - p.close() - - return nil -} - -// Peer retrieves the registered peer with the given id. -func (ps *peerSet) Peer(id string) *peer { - ps.lock.RLock() - defer ps.lock.RUnlock() - - return ps.peers[id] -} - -// Len returns if the current number of peers in the set. -func (ps *peerSet) Len() int { - ps.lock.RLock() - defer ps.lock.RUnlock() - - return len(ps.peers) -} - -// PeersWithoutBlock retrieves a list of peers that do not have a given block in -// their set of known hashes. -func (ps *peerSet) PeersWithoutBlock(hash common.Hash) []*peer { - ps.lock.RLock() - defer ps.lock.RUnlock() - - list := make([]*peer, 0, len(ps.peers)) - for _, p := range ps.peers { - if !p.knownBlocks.Contains(hash) { - list = append(list, p) - } - } - return list -} - -// PeersWithoutTx retrieves a list of peers that do not have a given transaction -// in their set of known hashes. -func (ps *peerSet) PeersWithoutTx(hash common.Hash) []*peer { - ps.lock.RLock() - defer ps.lock.RUnlock() - - list := make([]*peer, 0, len(ps.peers)) - for _, p := range ps.peers { - if !p.knownTxs.Contains(hash) { - list = append(list, p) - } - } - return list -} - -// BestPeer retrieves the known peer with the currently highest total difficulty. -func (ps *peerSet) BestPeer() *peer { - ps.lock.RLock() - defer ps.lock.RUnlock() - - var ( - bestPeer *peer - bestTd *big.Int - ) - for _, p := range ps.peers { - if _, td := p.Head(); bestPeer == nil || td.Cmp(bestTd) > 0 { - bestPeer, bestTd = p, td - } - } - return bestPeer +// snapPeerInfo represents a short summary of the `snap` sub-protocol metadata known +// about a connected peer. +type snapPeerInfo struct { + Version uint `json:"version"` // Snapshot protocol version negotiated } -// WorstPeer retrieves the known peer with the currently lowest total difficulty. -func (ps *peerSet) WorstPeer() *peer { - ps.lock.RLock() - defer ps.lock.RUnlock() - - var ( - worstPeer *peer - worstTD *big.Int - ) - for _, p := range ps.peers { - if _, td := p.Head(); worstPeer == nil || td.Cmp(worstTD) < 0 { - worstPeer, worstTD = p, td - } - } - return worstPeer +// snapPeer is a wrapper around snap.Peer to maintain a few extra metadata. +type snapPeer struct { + *snap.Peer } -// Close disconnects all peers. -// No new peers can be registered after Close has returned. -func (ps *peerSet) Close() { - ps.lock.Lock() - defer ps.lock.Unlock() - - for _, p := range ps.peers { - p.Disconnect(p2p.DiscQuitting) +// info gathers and returns some `snap` protocol metadata known about a peer. +func (p *snapPeer) info() *snapPeerInfo { + return &snapPeerInfo{ + Version: p.Version(), } - ps.closed = true } diff --git a/eth/peerset.go b/eth/peerset.go new file mode 100644 index 0000000000..97d3eed643 --- /dev/null +++ b/eth/peerset.go @@ -0,0 +1,276 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "errors" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/eth/protocols/snap" + "github.com/ethereum/go-ethereum/p2p" +) + +var ( + // errPeerSetClosed is returned if a peer is attempted to be added or removed + // from the peer set after it has been terminated. + errPeerSetClosed = errors.New("peerset closed") + + // errPeerAlreadyRegistered is returned if a peer is attempted to be added + // to the peer set, but one with the same id already exists. + errPeerAlreadyRegistered = errors.New("peer already registered") + + // errPeerNotRegistered is returned if a peer is attempted to be removed from + // a peer set, but no peer with the given id exists. + errPeerNotRegistered = errors.New("peer not registered") + + // errSnapWithoutEth is returned if a peer attempts to connect only on the + // snap protocol without advertizing the eth main protocol. + errSnapWithoutEth = errors.New("peer connected on snap without compatible eth support") +) + +// peerSet represents the collection of active peers currently participating in +// the `eth` protocol, with or without the `snap` extension. +type peerSet struct { + peers map[string]*ethPeer // Peers connected on the `eth` protocol + snapPeers int // Number of `snap` compatible peers for connection prioritization + + snapWait map[string]chan *snap.Peer // Peers connected on `eth` waiting for their snap extension + snapPend map[string]*snap.Peer // Peers connected on the `snap` protocol, but not yet on `eth` + + lock sync.RWMutex + closed bool +} + +// newPeerSet creates a new peer set to track the active participants. +func newPeerSet() *peerSet { + return &peerSet{ + peers: make(map[string]*ethPeer), + snapWait: make(map[string]chan *snap.Peer), + snapPend: make(map[string]*snap.Peer), + } +} + +// registerSnapExtension unblocks an already connected `eth` peer waiting for its +// `snap` extension, or if no such peer exists, tracks the extension for the time +// being until the `eth` main protocol starts looking for it. +func (ps *peerSet) registerSnapExtension(peer *snap.Peer) error { + // Reject the peer if it advertises `snap` without `eth` as `snap` is only a + // satellite protocol meaningful with the chain selection of `eth` + if !peer.RunningCap(eth.ProtocolName, eth.ProtocolVersions) { + return errSnapWithoutEth + } + // Ensure nobody can double connect + ps.lock.Lock() + defer ps.lock.Unlock() + + id := peer.ID() + if _, ok := ps.peers[id]; ok { + return errPeerAlreadyRegistered // avoid connections with the same id as existing ones + } + if _, ok := ps.snapPend[id]; ok { + return errPeerAlreadyRegistered // avoid connections with the same id as pending ones + } + // Inject the peer into an `eth` counterpart is available, otherwise save for later + if wait, ok := ps.snapWait[id]; ok { + delete(ps.snapWait, id) + wait <- peer + return nil + } + ps.snapPend[id] = peer + return nil +} + +// waitExtensions blocks until all satellite protocols are connected and tracked +// by the peerset. +func (ps *peerSet) waitSnapExtension(peer *eth.Peer) (*snap.Peer, error) { + // If the peer does not support a compatible `snap`, don't wait + if !peer.RunningCap(snap.ProtocolName, snap.ProtocolVersions) { + return nil, nil + } + // Ensure nobody can double connect + ps.lock.Lock() + + id := peer.ID() + if _, ok := ps.peers[id]; ok { + ps.lock.Unlock() + return nil, errPeerAlreadyRegistered // avoid connections with the same id as existing ones + } + if _, ok := ps.snapWait[id]; ok { + ps.lock.Unlock() + return nil, errPeerAlreadyRegistered // avoid connections with the same id as pending ones + } + // If `snap` already connected, retrieve the peer from the pending set + if snap, ok := ps.snapPend[id]; ok { + delete(ps.snapPend, id) + + ps.lock.Unlock() + return snap, nil + } + // Otherwise wait for `snap` to connect concurrently + wait := make(chan *snap.Peer) + ps.snapWait[id] = wait + ps.lock.Unlock() + + return <-wait, nil +} + +// registerPeer injects a new `eth` peer into the working set, or returns an error +// if the peer is already known. +func (ps *peerSet) registerPeer(peer *eth.Peer, ext *snap.Peer) error { + // Start tracking the new peer + ps.lock.Lock() + defer ps.lock.Unlock() + + if ps.closed { + return errPeerSetClosed + } + id := peer.ID() + if _, ok := ps.peers[id]; ok { + return errPeerAlreadyRegistered + } + eth := ðPeer{ + Peer: peer, + } + if ext != nil { + eth.snapExt = &snapPeer{ext} + ps.snapPeers++ + } + ps.peers[id] = eth + return nil +} + +// unregisterPeer removes a remote peer from the active set, disabling any further +// actions to/from that particular entity. +func (ps *peerSet) unregisterPeer(id string) error { + ps.lock.Lock() + defer ps.lock.Unlock() + + peer, ok := ps.peers[id] + if !ok { + return errPeerNotRegistered + } + delete(ps.peers, id) + if peer.snapExt != nil { + ps.snapPeers-- + } + return nil +} + +// peer retrieves the registered peer with the given id. +func (ps *peerSet) peer(id string) *ethPeer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + return ps.peers[id] +} + +// peersWithoutBlock retrieves a list of peers that do not have a given block in +// their set of known hashes so it might be propagated to them. +func (ps *peerSet) peersWithoutBlock(hash common.Hash) []*ethPeer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + list := make([]*ethPeer, 0, len(ps.peers)) + for _, p := range ps.peers { + if !p.KnownBlock(hash) { + list = append(list, p) + } + } + return list +} + +// peersWithoutTransaction retrieves a list of peers that do not have a given +// transaction in their set of known hashes. +func (ps *peerSet) peersWithoutTransaction(hash common.Hash) []*ethPeer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + list := make([]*ethPeer, 0, len(ps.peers)) + for _, p := range ps.peers { + if !p.KnownTransaction(hash) { + list = append(list, p) + } + } + return list +} + +// len returns if the current number of `eth` peers in the set. Since the `snap` +// peers are tied to the existence of an `eth` connection, that will always be a +// subset of `eth`. +func (ps *peerSet) len() int { + ps.lock.RLock() + defer ps.lock.RUnlock() + + return len(ps.peers) +} + +// snapLen returns if the current number of `snap` peers in the set. +func (ps *peerSet) snapLen() int { + ps.lock.RLock() + defer ps.lock.RUnlock() + + return ps.snapPeers +} + +// peerWithHighestTD retrieves the known peer with the currently highest total +// difficulty. +func (ps *peerSet) peerWithHighestTD() *eth.Peer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + var ( + bestPeer *eth.Peer + bestTd *big.Int + ) + for _, p := range ps.peers { + if _, td := p.Head(); bestPeer == nil || td.Cmp(bestTd) > 0 { + bestPeer, bestTd = p.Peer, td + } + } + return bestPeer +} + +// close disconnects all peers. +func (ps *peerSet) close() { + ps.lock.Lock() + defer ps.lock.Unlock() + + for _, p := range ps.peers { + p.Disconnect(p2p.DiscQuitting) + } + ps.closed = true +} + +// WorstPeer retrieves the known peer with the currently lowest total difficulty. +func (ps *peerSet) WorstPeer() *ethPeer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + var ( + worstPeer *ethPeer + worstTD *big.Int + ) + for _, p := range ps.peers { + if _, td := p.Head(); worstPeer == nil || td.Cmp(worstTD) < 0 { + worstPeer, worstTD = p, td + } + } + return worstPeer +} diff --git a/eth/protocol.go b/eth/protocol.go deleted file mode 100644 index ff38da3df0..0000000000 --- a/eth/protocol.go +++ /dev/null @@ -1,221 +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 . - -package eth - -import ( - "fmt" - "io" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/forkid" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/rlp" -) - -// Constants to match up protocol versions and messages -const ( - eth63 = 63 - eth64 = 64 - eth65 = 65 -) - -// protocolName is the official short name of the protocol used during capability negotiation. -const protocolName = "eth" - -// DefaultProtocolVersions are the supported versions of the eth protocol (first is primary). -var DefaultProtocolVersions = []uint{eth65, eth64, eth63} - -// protocolLengths are the number of implemented message corresponding to different protocol versions. -var protocolLengths = map[uint]uint64{eth65: 17, eth64: 17, eth63: 17} - -const protocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message - -// eth protocol message codes -const ( - StatusMsg = 0x00 - NewBlockHashesMsg = 0x01 - TransactionMsg = 0x02 - GetBlockHeadersMsg = 0x03 - BlockHeadersMsg = 0x04 - GetBlockBodiesMsg = 0x05 - BlockBodiesMsg = 0x06 - NewBlockMsg = 0x07 - GetNodeDataMsg = 0x0d - NodeDataMsg = 0x0e - GetReceiptsMsg = 0x0f - ReceiptsMsg = 0x10 - - // New protocol message codes introduced in eth65 - // - // Previously these message ids were used by some legacy and unsupported - // eth protocols, reown them here. - NewPooledTransactionHashesMsg = 0x08 - GetPooledTransactionsMsg = 0x09 - PooledTransactionsMsg = 0x0a -) - -type errCode int - -const ( - ErrMsgTooLarge = iota - ErrDecode - ErrInvalidMsgCode - ErrProtocolVersionMismatch - ErrNetworkIDMismatch - ErrGenesisMismatch - ErrForkIDRejected - ErrNoStatusMsg - ErrExtraStatusMsg -) - -func (e errCode) String() string { - return errorToString[int(e)] -} - -// XXX change once legacy code is out -var errorToString = map[int]string{ - ErrMsgTooLarge: "Message too long", - ErrDecode: "Invalid message", - ErrInvalidMsgCode: "Invalid message code", - ErrProtocolVersionMismatch: "Protocol version mismatch", - ErrNetworkIDMismatch: "Network ID mismatch", - ErrGenesisMismatch: "Genesis mismatch", - ErrForkIDRejected: "Fork ID rejected", - ErrNoStatusMsg: "No status message", - ErrExtraStatusMsg: "Extra status message", -} - -type txPool interface { - // Has returns an indicator whether txpool has a transaction - // cached with the given hash. - Has(hash common.Hash) bool - - // Get retrieves the transaction from local txpool with given - // tx hash. - Get(hash common.Hash) *types.Transaction - - // AddRemotes should add the given transactions to the pool. - AddRemotes([]*types.Transaction) []error - - // Pending should return pending transactions. - // The slice should be modifiable by the caller. - Pending() (map[common.Address]types.Transactions, error) - - // SubscribeNewTxsEvent should return an event subscription of - // NewTxsEvent and send events to the given channel. - SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription -} - -// statusData63 is the network packet for the status message for eth/63. -type statusData63 struct { - ProtocolVersion uint32 - NetworkId uint64 - TD *big.Int - CurrentBlock common.Hash - GenesisBlock common.Hash -} - -// statusData is the network packet for the status message for eth/64 and later. -type statusData struct { - ProtocolVersion uint32 - NetworkID uint64 - TD *big.Int - Head common.Hash - Genesis common.Hash - ForkID forkid.ID -} - -// newBlockHashesData is the network packet for the block announcements. -type newBlockHashesData []struct { - Hash common.Hash // Hash of one particular block being announced - Number uint64 // Number of one particular block being announced -} - -// getBlockHeadersData represents a block header query. -type getBlockHeadersData struct { - Origin hashOrNumber // Block from which to retrieve headers - Amount uint64 // Maximum number of headers to retrieve - Skip uint64 // Blocks to skip between consecutive headers - Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis) -} - -// hashOrNumber is a combined field for specifying an origin block. -type hashOrNumber struct { - Hash common.Hash // Block hash from which to retrieve headers (excludes Number) - Number uint64 // Block hash from which to retrieve headers (excludes Hash) -} - -// EncodeRLP is a specialized encoder for hashOrNumber to encode only one of the -// two contained union fields. -func (hn *hashOrNumber) EncodeRLP(w io.Writer) error { - if hn.Hash == (common.Hash{}) { - return rlp.Encode(w, hn.Number) - } - if hn.Number != 0 { - return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number) - } - return rlp.Encode(w, hn.Hash) -} - -// DecodeRLP is a specialized decoder for hashOrNumber to decode the contents -// into either a block hash or a block number. -func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error { - _, size, _ := s.Kind() - origin, err := s.Raw() - if err == nil { - switch { - case size == 32: - err = rlp.DecodeBytes(origin, &hn.Hash) - case size <= 8: - err = rlp.DecodeBytes(origin, &hn.Number) - default: - err = fmt.Errorf("invalid input size %d for origin", size) - } - } - return err -} - -// newBlockData is the network packet for the block propagation message. -type newBlockData struct { - Block *types.Block - TD *big.Int -} - -// sanityCheck verifies that the values are reasonable, as a DoS protection -func (request *newBlockData) sanityCheck() error { - if err := request.Block.SanityCheck(); err != nil { - return err - } - //TD at mainnet block #7753254 is 76 bits. If it becomes 100 million times - // larger, it will still fit within 100 bits - if tdlen := request.TD.BitLen(); tdlen > 100 { - return fmt.Errorf("too large block TD: bitlen %d", tdlen) - } - return nil -} - -// blockBody represents the data content of a single block. -type blockBody struct { - Transactions []*types.Transaction // Transactions contained within a block - Uncles []*types.Header // Uncles contained within a block -} - -// blockBodiesData is the network packet for block content distribution. -type blockBodiesData []*blockBody diff --git a/eth/protocol_test.go b/eth/protocol_test.go deleted file mode 100644 index 09d50701dd..0000000000 --- a/eth/protocol_test.go +++ /dev/null @@ -1,460 +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 . - -package eth - -import ( - "fmt" - "math/big" - "sync" - "sync/atomic" - "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/forkid" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/params/types/genesisT" - "github.com/ethereum/go-ethereum/params/types/goethereum" - "github.com/ethereum/go-ethereum/rlp" -) - -func init() { - // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) -} - -var testAccount, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - -// Tests that handshake failures are detected and reported correctly. -func TestStatusMsgErrors63(t *testing.T) { - pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil) - var ( - genesis = pm.blockchain.Genesis() - head = pm.blockchain.CurrentHeader() - td = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64()) - ) - defer pm.Stop() - - tests := []struct { - code uint64 - data interface{} - wantError error - }{ - { - code: TransactionMsg, data: []interface{}{}, - wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"), - }, - { - code: StatusMsg, data: statusData63{10, DefaultConfig.NetworkId, td, head.Hash(), genesis.Hash()}, - wantError: errResp(ErrProtocolVersionMismatch, "10 (!= %d)", 63), - }, - { - code: StatusMsg, data: statusData63{63, 999, td, head.Hash(), genesis.Hash()}, - wantError: errResp(ErrNetworkIDMismatch, "999 (!= %d)", DefaultConfig.NetworkId), - }, - { - code: StatusMsg, data: statusData63{63, DefaultConfig.NetworkId, td, head.Hash(), common.Hash{3}}, - wantError: errResp(ErrGenesisMismatch, "0300000000000000 (!= %x)", genesis.Hash().Bytes()[:8]), - }, - } - for i, test := range tests { - p, errc := newTestPeer("peer", 63, pm, false) - // The send call might hang until reset because - // the protocol might not read the payload. - go p2p.Send(p.app, test.code, test.data) - - select { - case err := <-errc: - if err == nil { - t.Errorf("test %d: protocol returned nil error, want %q", i, test.wantError) - } else if err.Error() != test.wantError.Error() { - t.Errorf("test %d: wrong error: got %q, want %q", i, err, test.wantError) - } - case <-time.After(2 * time.Second): - t.Errorf("protocol did not shut down within 2 seconds") - } - p.close() - } -} - -func TestStatusMsgErrors64(t *testing.T) { - pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil) - var ( - genesis = pm.blockchain.Genesis() - head = pm.blockchain.CurrentHeader() - td = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64()) - forkID = forkid.NewID(pm.blockchain.Config(), pm.blockchain.Genesis().Hash(), pm.blockchain.CurrentHeader().Number.Uint64()) - ) - defer pm.Stop() - - tests := []struct { - code uint64 - data interface{} - wantError error - }{ - { - code: TransactionMsg, data: []interface{}{}, - wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"), - }, - { - code: StatusMsg, data: statusData{10, DefaultConfig.NetworkId, td, head.Hash(), genesis.Hash(), forkID}, - wantError: errResp(ErrProtocolVersionMismatch, "10 (!= %d)", 64), - }, - { - code: StatusMsg, data: statusData{64, 999, td, head.Hash(), genesis.Hash(), forkID}, - wantError: errResp(ErrNetworkIDMismatch, "999 (!= %d)", DefaultConfig.NetworkId), - }, - { - code: StatusMsg, data: statusData{64, DefaultConfig.NetworkId, td, head.Hash(), common.Hash{3}, forkID}, - wantError: errResp(ErrGenesisMismatch, "0300000000000000000000000000000000000000000000000000000000000000 (!= %x)", genesis.Hash()), - }, - { - code: StatusMsg, data: statusData{64, DefaultConfig.NetworkId, td, head.Hash(), genesis.Hash(), forkid.ID{Hash: [4]byte{0x00, 0x01, 0x02, 0x03}}}, - wantError: errResp(ErrForkIDRejected, forkid.ErrLocalIncompatibleOrStale.Error()), - }, - } - for i, test := range tests { - p, errc := newTestPeer("peer", 64, pm, false) - // The send call might hang until reset because - // the protocol might not read the payload. - go p2p.Send(p.app, test.code, test.data) - - select { - case err := <-errc: - if err == nil { - t.Errorf("test %d: protocol returned nil error, want %q", i, test.wantError) - } else if err.Error() != test.wantError.Error() { - t.Errorf("test %d: wrong error: got %q, want %q", i, err, test.wantError) - } - case <-time.After(2 * time.Second): - t.Errorf("protocol did not shut down within 2 seconds") - } - p.close() - } -} - -func TestForkIDSplit(t *testing.T) { - var ( - engine = ethash.NewFaker() - - configNoFork = &goethereum.ChainConfig{HomesteadBlock: big.NewInt(1)} - configProFork = &goethereum.ChainConfig{ - HomesteadBlock: big.NewInt(1), - EIP150Block: big.NewInt(2), - EIP155Block: big.NewInt(2), - EIP158Block: big.NewInt(2), - ByzantiumBlock: big.NewInt(3), - } - dbNoFork = rawdb.NewMemoryDatabase() - dbProFork = rawdb.NewMemoryDatabase() - - gspecNoFork = &genesisT.Genesis{Config: configNoFork} - gspecProFork = &genesisT.Genesis{Config: configProFork} - - genesisNoFork = core.MustCommitGenesis(dbNoFork, gspecNoFork) - genesisProFork = core.MustCommitGenesis(dbProFork, gspecProFork) - - chainNoFork, _ = core.NewBlockChain(dbNoFork, nil, configNoFork, engine, vm.Config{}, nil, nil) - chainProFork, _ = core.NewBlockChain(dbProFork, nil, configProFork, engine, vm.Config{}, nil, nil) - - blocksNoFork, _ = core.GenerateChain(configNoFork, genesisNoFork, engine, dbNoFork, 2, nil) - blocksProFork, _ = core.GenerateChain(configProFork, genesisProFork, engine, dbProFork, 2, nil) - - ethNoFork, _ = NewProtocolManager(configNoFork, nil, downloader.FullSync, 1, new(event.TypeMux), &testTxPool{pool: make(map[common.Hash]*types.Transaction)}, engine, chainNoFork, dbNoFork, 1, nil) - ethProFork, _ = NewProtocolManager(configProFork, nil, downloader.FullSync, 1, new(event.TypeMux), &testTxPool{pool: make(map[common.Hash]*types.Transaction)}, engine, chainProFork, dbProFork, 1, nil) - ) - ethNoFork.Start(1000) - ethProFork.Start(1000) - - // Both nodes should allow the other to connect (same genesis, next fork is the same) - p2pNoFork, p2pProFork := p2p.MsgPipe() - peerNoFork := newPeer(64, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil) - peerProFork := newPeer(64, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil) - - errc := make(chan error, 2) - go func() { errc <- ethNoFork.handle(peerProFork) }() - go func() { errc <- ethProFork.handle(peerNoFork) }() - - select { - case err := <-errc: - t.Fatalf("frontier nofork <-> profork failed: %v", err) - case <-time.After(250 * time.Millisecond): - p2pNoFork.Close() - p2pProFork.Close() - } - // Progress into Homestead. Fork's match, so we don't care what the future holds - chainNoFork.InsertChain(blocksNoFork[:1]) - chainProFork.InsertChain(blocksProFork[:1]) - - p2pNoFork, p2pProFork = p2p.MsgPipe() - peerNoFork = newPeer(64, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil) - peerProFork = newPeer(64, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil) - - errc = make(chan error, 2) - go func() { errc <- ethNoFork.handle(peerProFork) }() - go func() { errc <- ethProFork.handle(peerNoFork) }() - - select { - case err := <-errc: - t.Fatalf("homestead nofork <-> profork failed: %v", err) - case <-time.After(250 * time.Millisecond): - p2pNoFork.Close() - p2pProFork.Close() - } - // Progress into Spurious. Forks mismatch, signalling differing chains, reject - chainNoFork.InsertChain(blocksNoFork[1:2]) - chainProFork.InsertChain(blocksProFork[1:2]) - - p2pNoFork, p2pProFork = p2p.MsgPipe() - peerNoFork = newPeer(64, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil) - peerProFork = newPeer(64, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil) - - errc = make(chan error, 2) - go func() { errc <- ethNoFork.handle(peerProFork) }() - go func() { errc <- ethProFork.handle(peerNoFork) }() - - select { - case err := <-errc: - if want := errResp(ErrForkIDRejected, forkid.ErrLocalIncompatibleOrStale.Error()); err.Error() != want.Error() { - t.Fatalf("fork ID rejection error mismatch: have %v, want %v", err, want) - } - case <-time.After(250 * time.Millisecond): - t.Fatalf("split peers not rejected") - } -} - -// This test checks that received transactions are added to the local pool. -func TestRecvTransactions63(t *testing.T) { testRecvTransactions(t, 63) } -func TestRecvTransactions64(t *testing.T) { testRecvTransactions(t, 64) } -func TestRecvTransactions65(t *testing.T) { testRecvTransactions(t, 65) } - -func testRecvTransactions(t *testing.T, protocol int) { - txAdded := make(chan []*types.Transaction) - 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() - - tx := newTestTransaction(testAccount, 0, 0) - if err := p2p.Send(p.app, TransactionMsg, []interface{}{tx}); err != nil { - t.Fatalf("send error: %v", err) - } - select { - case added := <-txAdded: - if len(added) != 1 { - t.Errorf("wrong number of added transactions: got %d, want 1", len(added)) - } else if added[0].Hash() != tx.Hash() { - t.Errorf("added wrong tx hash: got %v, want %v", added[0].Hash(), tx.Hash()) - } - case <-time.After(2 * time.Second): - t.Errorf("no NewTxsEvent received within 2 seconds") - } -} - -// This test checks that pending transactions are sent. -func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) } -func TestSendTransactions64(t *testing.T) { testSendTransactions(t, 64) } -func TestSendTransactions65(t *testing.T) { testSendTransactions(t, 65) } - -func testSendTransactions(t *testing.T, protocol int) { - pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil) - defer pm.Stop() - - // Fill the pool with big transactions (use a subscription to wait until all - // the transactions are announced to avoid spurious events causing extra - // broadcasts). - const txsize = txsyncPackSize / 10 - alltxs := make([]*types.Transaction, 100) - for nonce := range alltxs { - alltxs[nonce] = newTestTransaction(testAccount, uint64(nonce), txsize) - } - pm.txpool.AddRemotes(alltxs) - time.Sleep(100 * time.Millisecond) // Wait until new tx even gets out of the system (lame) - - // Connect several peers. They should all receive the pending transactions. - var wg sync.WaitGroup - checktxs := func(p *testPeer) { - defer wg.Done() - defer p.close() - seen := make(map[common.Hash]bool) - for _, tx := range alltxs { - seen[tx.Hash()] = false - } - for n := 0; n < len(alltxs) && !t.Failed(); { - var forAllHashes func(callback func(hash common.Hash)) - switch protocol { - case 63: - fallthrough - case 64: - msg, err := p.app.ReadMsg() - if err != nil { - t.Errorf("%v: read error: %v", p.Peer, err) - continue - } else if msg.Code != TransactionMsg { - t.Errorf("%v: got code %d, want TxMsg", p.Peer, msg.Code) - continue - } - var txs []*types.Transaction - if err := msg.Decode(&txs); err != nil { - t.Errorf("%v: %v", p.Peer, err) - continue - } - forAllHashes = func(callback func(hash common.Hash)) { - for _, tx := range txs { - callback(tx.Hash()) - } - } - case 65: - msg, err := p.app.ReadMsg() - if err != nil { - t.Errorf("%v: read error: %v", p.Peer, err) - continue - } else if msg.Code != NewPooledTransactionHashesMsg { - t.Errorf("%v: got code %d, want NewPooledTransactionHashesMsg", p.Peer, msg.Code) - continue - } - var hashes []common.Hash - if err := msg.Decode(&hashes); err != nil { - t.Errorf("%v: %v", p.Peer, err) - continue - } - forAllHashes = func(callback func(hash common.Hash)) { - for _, h := range hashes { - callback(h) - } - } - } - forAllHashes(func(hash common.Hash) { - seentx, want := seen[hash] - if seentx { - t.Errorf("%v: got tx more than once: %x", p.Peer, hash) - } - if !want { - t.Errorf("%v: got unexpected tx: %x", p.Peer, hash) - } - seen[hash] = true - n++ - }) - } - } - for i := 0; i < 3; i++ { - p, _ := newTestPeer(fmt.Sprintf("peer #%d", i), protocol, pm, true) - wg.Add(1) - go checktxs(p) - } - wg.Wait() -} - -func TestTransactionPropagation(t *testing.T) { testSyncTransaction(t, true) } -func TestTransactionAnnouncement(t *testing.T) { testSyncTransaction(t, false) } - -func testSyncTransaction(t *testing.T, propagtion bool) { - // Create a protocol manager for transaction fetcher and sender - pmFetcher, _ := newTestProtocolManagerMust(t, downloader.FastSync, 0, nil, nil) - defer pmFetcher.Stop() - pmSender, _ := newTestProtocolManagerMust(t, downloader.FastSync, 1024, nil, nil) - pmSender.broadcastTxAnnouncesOnly = !propagtion - defer pmSender.Stop() - - // Sync up the two peers - io1, io2 := p2p.MsgPipe() - - go pmSender.handle(pmSender.newPeer(65, p2p.NewPeer(enode.ID{}, "sender", nil), io2, pmSender.txpool.Get)) - go pmFetcher.handle(pmFetcher.newPeer(65, p2p.NewPeer(enode.ID{}, "fetcher", nil), io1, pmFetcher.txpool.Get)) - - time.Sleep(250 * time.Millisecond) - pmFetcher.doSync(peerToSyncOp(downloader.FullSync, pmFetcher.peers.BestPeer())) - atomic.StoreUint32(&pmFetcher.acceptTxs, 1) - - newTxs := make(chan core.NewTxsEvent, 1024) - sub := pmFetcher.txpool.SubscribeNewTxsEvent(newTxs) - defer sub.Unsubscribe() - - // Fill the pool with new transactions - alltxs := make([]*types.Transaction, 1024) - for nonce := range alltxs { - alltxs[nonce] = newTestTransaction(testAccount, uint64(nonce), 0) - } - pmSender.txpool.AddRemotes(alltxs) - - var got int -loop: - for { - select { - case ev := <-newTxs: - got += len(ev.Txs) - if got == 1024 { - break loop - } - case <-time.NewTimer(time.Second).C: - t.Fatal("Failed to retrieve all transaction") - } - } -} - -// Tests that the custom union field encoder and decoder works correctly. -func TestGetBlockHeadersDataEncodeDecode(t *testing.T) { - // Create a "random" hash for testing - var hash common.Hash - for i := range hash { - hash[i] = byte(i) - } - // Assemble some table driven tests - tests := []struct { - packet *getBlockHeadersData - fail bool - }{ - // Providing the origin as either a hash or a number should both work - {fail: false, packet: &getBlockHeadersData{Origin: hashOrNumber{Number: 314}}}, - {fail: false, packet: &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}}}, - - // Providing arbitrary query field should also work - {fail: false, packet: &getBlockHeadersData{Origin: hashOrNumber{Number: 314}, Amount: 314, Skip: 1, Reverse: true}}, - {fail: false, packet: &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: 314, Skip: 1, Reverse: true}}, - - // Providing both the origin hash and origin number must fail - {fail: true, packet: &getBlockHeadersData{Origin: hashOrNumber{Hash: hash, Number: 314}}}, - } - // Iterate over each of the tests and try to encode and then decode - for i, tt := range tests { - bytes, err := rlp.EncodeToBytes(tt.packet) - if err != nil && !tt.fail { - t.Fatalf("test %d: failed to encode packet: %v", i, err) - } else if err == nil && tt.fail { - t.Fatalf("test %d: encode should have failed", i) - } - if !tt.fail { - packet := new(getBlockHeadersData) - if err := rlp.DecodeBytes(bytes, packet); err != nil { - t.Fatalf("test %d: failed to decode packet: %v", i, err) - } - if packet.Origin.Hash != tt.packet.Origin.Hash || packet.Origin.Number != tt.packet.Origin.Number || packet.Amount != tt.packet.Amount || - packet.Skip != tt.packet.Skip || packet.Reverse != tt.packet.Reverse { - t.Fatalf("test %d: encode decode mismatch: have %+v, want %+v", i, packet, tt.packet) - } - } - } -} diff --git a/eth/protocols/eth/broadcast.go b/eth/protocols/eth/broadcast.go new file mode 100644 index 0000000000..328396d510 --- /dev/null +++ b/eth/protocols/eth/broadcast.go @@ -0,0 +1,195 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +const ( + // This is the target size for the packs of transactions or announcements. A + // pack can get larger than this if a single transactions exceeds this size. + maxTxPacketSize = 100 * 1024 +) + +// blockPropagation is a block propagation event, waiting for its turn in the +// broadcast queue. +type blockPropagation struct { + block *types.Block + td *big.Int +} + +// broadcastBlocks is a write loop that multiplexes blocks and block accouncements +// to the remote peer. The goal is to have an async writer that does not lock up +// node internals and at the same time rate limits queued data. +func (p *Peer) broadcastBlocks() { + for { + select { + case prop := <-p.queuedBlocks: + if err := p.SendNewBlock(prop.block, prop.td); err != nil { + return + } + p.Log().Trace("Propagated block", "number", prop.block.Number(), "hash", prop.block.Hash(), "td", prop.td) + + case block := <-p.queuedBlockAnns: + if err := p.SendNewBlockHashes([]common.Hash{block.Hash()}, []uint64{block.NumberU64()}); err != nil { + return + } + p.Log().Trace("Announced block", "number", block.Number(), "hash", block.Hash()) + + case <-p.term: + return + } + } +} + +// broadcastTransactions is a write loop that schedules transaction broadcasts +// to the remote peer. The goal is to have an async writer that does not lock up +// node internals and at the same time rate limits queued data. +func (p *Peer) broadcastTransactions() { + var ( + queue []common.Hash // Queue of hashes to broadcast as full transactions + done chan struct{} // Non-nil if background broadcaster is running + fail = make(chan error, 1) // Channel used to receive network error + failed bool // Flag whether a send failed, discard everything onward + ) + for { + // If there's no in-flight broadcast running, check if a new one is needed + if done == nil && len(queue) > 0 { + // Pile transaction until we reach our allowed network limit + var ( + hashes []common.Hash + txs []*types.Transaction + size common.StorageSize + ) + for i := 0; i < len(queue) && size < maxTxPacketSize; i++ { + if tx := p.txpool.Get(queue[i]); tx != nil { + txs = append(txs, tx) + size += tx.Size() + } + hashes = append(hashes, queue[i]) + } + queue = queue[:copy(queue, queue[len(hashes):])] + + // If there's anything available to transfer, fire up an async writer + if len(txs) > 0 { + done = make(chan struct{}) + go func() { + if err := p.SendTransactions(txs); err != nil { + fail <- err + return + } + close(done) + p.Log().Trace("Sent transactions", "count", len(txs)) + }() + } + } + // Transfer goroutine may or may not have been started, listen for events + select { + case hashes := <-p.txBroadcast: + // If the connection failed, discard all transaction events + if failed { + continue + } + // New batch of transactions to be broadcast, queue them (with cap) + queue = append(queue, hashes...) + if len(queue) > maxQueuedTxs { + // Fancy copy and resize to ensure buffer doesn't grow indefinitely + queue = queue[:copy(queue, queue[len(queue)-maxQueuedTxs:])] + } + + case <-done: + done = nil + + case <-fail: + failed = true + + case <-p.term: + return + } + } +} + +// announceTransactions is a write loop that schedules transaction broadcasts +// to the remote peer. The goal is to have an async writer that does not lock up +// node internals and at the same time rate limits queued data. +func (p *Peer) announceTransactions() { + var ( + queue []common.Hash // Queue of hashes to announce as transaction stubs + done chan struct{} // Non-nil if background announcer is running + fail = make(chan error, 1) // Channel used to receive network error + failed bool // Flag whether a send failed, discard everything onward + ) + for { + // If there's no in-flight announce running, check if a new one is needed + if done == nil && len(queue) > 0 { + // Pile transaction hashes until we reach our allowed network limit + var ( + count int + pending []common.Hash + size common.StorageSize + ) + for count = 0; count < len(queue) && size < maxTxPacketSize; count++ { + if p.txpool.Get(queue[count]) != nil { + pending = append(pending, queue[count]) + size += common.HashLength + } + } + // Shift and trim queue + queue = queue[:copy(queue, queue[count:])] + + // If there's anything available to transfer, fire up an async writer + if len(pending) > 0 { + done = make(chan struct{}) + go func() { + if err := p.sendPooledTransactionHashes(pending); err != nil { + fail <- err + return + } + close(done) + p.Log().Trace("Sent transaction announcements", "count", len(pending)) + }() + } + } + // Transfer goroutine may or may not have been started, listen for events + select { + case hashes := <-p.txAnnounce: + // If the connection failed, discard all transaction events + if failed { + continue + } + // New batch of transactions to be broadcast, queue them (with cap) + queue = append(queue, hashes...) + if len(queue) > maxQueuedTxAnns { + // Fancy copy and resize to ensure buffer doesn't grow indefinitely + queue = queue[:copy(queue, queue[len(queue)-maxQueuedTxAnns:])] + } + + case <-done: + done = nil + + case <-fail: + failed = true + + case <-p.term: + return + } + } +} diff --git a/eth/protocols/eth/discovery.go b/eth/protocols/eth/discovery.go new file mode 100644 index 0000000000..025479b423 --- /dev/null +++ b/eth/protocols/eth/discovery.go @@ -0,0 +1,65 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/forkid" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/rlp" +) + +// enrEntry is the ENR entry which advertises `eth` protocol on the discovery. +type enrEntry struct { + ForkID forkid.ID // Fork identifier per EIP-2124 + + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` +} + +// ENRKey implements enr.Entry. +func (e enrEntry) ENRKey() string { + return "eth" +} + +// StartENRUpdater starts the `eth` ENR updater loop, which listens for chain +// head events and updates the requested node record whenever a fork is passed. +func StartENRUpdater(chain *core.BlockChain, ln *enode.LocalNode) { + var newHead = make(chan core.ChainHeadEvent, 10) + sub := chain.SubscribeChainHeadEvent(newHead) + + go func() { + defer sub.Unsubscribe() + for { + select { + case <-newHead: + ln.Set(currentENREntry(chain)) + case <-sub.Err(): + // Would be nice to sync with Stop, but there is no + // good way to do that. + return + } + } + }() +} + +// currentENREntry constructs an `eth` ENR entry based on the current state of the chain. +func currentENREntry(chain *core.BlockChain) *enrEntry { + return &enrEntry{ + ForkID: forkid.NewID(chain.Config(), chain.Genesis().Hash(), chain.CurrentHeader().Number.Uint64()), + } +} diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go new file mode 100644 index 0000000000..d7e663c672 --- /dev/null +++ b/eth/protocols/eth/handler.go @@ -0,0 +1,261 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" + "github.com/ethereum/go-ethereum/params/types/ctypes" + "github.com/ethereum/go-ethereum/trie" +) + +const ( + // softResponseLimit is the target maximum size of replies to data retrievals. + softResponseLimit = 2 * 1024 * 1024 + + // estHeaderSize is the approximate size of an RLP encoded block header. + estHeaderSize = 500 + + // maxHeadersServe is the maximum number of block headers to serve. This number + // is there to limit the number of disk lookups. + maxHeadersServe = 1024 + + // maxBodiesServe is the maximum number of block bodies to serve. This number + // is mostly there to limit the number of disk lookups. With 24KB block sizes + // nowadays, the practical limit will always be softResponseLimit. + maxBodiesServe = 1024 + + // maxNodeDataServe is the maximum number of state trie nodes to serve. This + // number is there to limit the number of disk lookups. + maxNodeDataServe = 1024 + + // maxReceiptsServe is the maximum number of block receipts to serve. This + // number is mostly there to limit the number of disk lookups. With block + // containing 200+ transactions nowadays, the practical limit will always + // be softResponseLimit. + maxReceiptsServe = 1024 +) + +// Handler is a callback to invoke from an outside runner after the boilerplate +// exchanges have passed. +type Handler func(peer *Peer) error + +// Backend defines the data retrieval methods to serve remote requests and the +// callback methods to invoke on remote deliveries. +type Backend interface { + // Chain retrieves the blockchain object to serve data. + Chain() *core.BlockChain + + // StateBloom retrieves the bloom filter - if any - for state trie nodes. + StateBloom() *trie.SyncBloom + + // TxPool retrieves the transaction pool object to serve data. + TxPool() TxPool + + // AcceptTxs retrieves whether transaction processing is enabled on the node + // or if inbound transactions should simply be dropped. + AcceptTxs() bool + + // RunPeer is invoked when a peer joins on the `eth` protocol. The handler + // should do any peer maintenance work, handshakes and validations. If all + // is passed, control should be given back to the `handler` to process the + // inbound messages going forward. + RunPeer(peer *Peer, handler Handler) error + + // PeerInfo retrieves all known `eth` information about a peer. + PeerInfo(id enode.ID) interface{} + + // Handle is a callback to be invoked when a data packet is received from + // the remote peer. Only packets not consumed by the protocol handler will + // be forwarded to the backend. + Handle(peer *Peer, packet Packet) error +} + +// TxPool defines the methods needed by the protocol handler to serve transactions. +type TxPool interface { + // Get retrieves the the transaction from the local txpool with the given hash. + Get(hash common.Hash) *types.Transaction +} + +// MakeProtocols constructs the P2P protocol definitions for `eth`. +func MakeProtocols(backend Backend, network uint64, protocolVersions []uint, dnsdisc enode.Iterator) []p2p.Protocol { + protocols := make([]p2p.Protocol, len(protocolVersions)) + for i, version := range protocolVersions { + version := version // Closure + + protocols[i] = p2p.Protocol{ + Name: ProtocolName, + Version: version, + Length: protocolLengths[version], + Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { + peer := NewPeer(version, p, rw, backend.TxPool()) + defer peer.Close() + + return backend.RunPeer(peer, func(peer *Peer) error { + return Handle(backend, peer) + }) + }, + NodeInfo: func() interface{} { + return nodeInfo(backend.Chain(), network) + }, + PeerInfo: func(id enode.ID) interface{} { + return backend.PeerInfo(id) + }, + Attributes: []enr.Entry{currentENREntry(backend.Chain())}, + DialCandidates: dnsdisc, + } + } + return protocols +} + +// NodeInfo represents a short summary of the `eth` sub-protocol metadata +// known about the host peer. +type NodeInfo struct { + Network uint64 `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3, Rinkeby=4) + Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain + Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block + Config ctypes.ChainConfigurator `json:"config"` // Chain configuration for the fork rules + Head common.Hash `json:"head"` // Hex hash of the host's best owned block +} + +// nodeInfo retrieves some `eth` protocol metadata about the running host node. +func nodeInfo(chain *core.BlockChain, network uint64) *NodeInfo { + head := chain.CurrentBlock() + return &NodeInfo{ + Network: network, + Difficulty: chain.GetTd(head.Hash(), head.NumberU64()), + Genesis: chain.Genesis().Hash(), + Config: chain.Config(), + Head: head.Hash(), + } +} + +// Handle is invoked whenever an `eth` connection is made that successfully passes +// the protocol handshake. This method will keep processing messages until the +// connection is torn down. +func Handle(backend Backend, peer *Peer) error { + for { + if err := handleMessage(backend, peer); err != nil { + peer.Log().Debug("Message handling failed in `eth`", "err", err) + return err + } + } +} + +type msgHandler func(backend Backend, msg Decoder, peer *Peer) error +type Decoder interface { + Decode(val interface{}) error + Time() time.Time +} + +var eth64 = map[uint64]msgHandler{ + GetBlockHeadersMsg: handleGetBlockHeaders, + BlockHeadersMsg: handleBlockHeaders, + GetBlockBodiesMsg: handleGetBlockBodies, + BlockBodiesMsg: handleBlockBodies, + GetNodeDataMsg: handleGetNodeData, + NodeDataMsg: handleNodeData, + GetReceiptsMsg: handleGetReceipts, + ReceiptsMsg: handleReceipts, + NewBlockHashesMsg: handleNewBlockhashes, + NewBlockMsg: handleNewBlock, + TransactionsMsg: handleTransactions, +} +var eth65 = map[uint64]msgHandler{ + // old 64 messages + GetBlockHeadersMsg: handleGetBlockHeaders, + BlockHeadersMsg: handleBlockHeaders, + GetBlockBodiesMsg: handleGetBlockBodies, + BlockBodiesMsg: handleBlockBodies, + GetNodeDataMsg: handleGetNodeData, + NodeDataMsg: handleNodeData, + GetReceiptsMsg: handleGetReceipts, + ReceiptsMsg: handleReceipts, + NewBlockHashesMsg: handleNewBlockhashes, + NewBlockMsg: handleNewBlock, + TransactionsMsg: handleTransactions, + // New eth65 messages + NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes, + GetPooledTransactionsMsg: handleGetPooledTransactions, + PooledTransactionsMsg: handlePooledTransactions, +} + +var eth66 = map[uint64]msgHandler{ + // eth64 announcement messages (no id) + NewBlockHashesMsg: handleNewBlockhashes, + NewBlockMsg: handleNewBlock, + TransactionsMsg: handleTransactions, + // eth65 announcement messages (no id) + NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes, + // eth66 messages with request-id + GetBlockHeadersMsg: handleGetBlockHeaders66, + BlockHeadersMsg: handleBlockHeaders66, + GetBlockBodiesMsg: handleGetBlockBodies66, + BlockBodiesMsg: handleBlockBodies66, + GetNodeDataMsg: handleGetNodeData66, + NodeDataMsg: handleNodeData66, + GetReceiptsMsg: handleGetReceipts66, + ReceiptsMsg: handleReceipts66, + GetPooledTransactionsMsg: handleGetPooledTransactions66, + PooledTransactionsMsg: handlePooledTransactions66, +} + +// handleMessage is invoked whenever an inbound message is received from a remote +// peer. The remote connection is torn down upon returning any error. +func handleMessage(backend Backend, peer *Peer) error { + // Read the next message from the remote peer, and ensure it's fully consumed + msg, err := peer.rw.ReadMsg() + if err != nil { + return err + } + if msg.Size > maxMessageSize { + return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize) + } + defer msg.Discard() + + var handlers = eth64 + if peer.Version() == ETH65 { + handlers = eth65 + } else if peer.Version() >= ETH66 { + handlers = eth66 + } + // Track the emount of time it takes to serve the request and run the handler + if metrics.Enabled { + h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code) + defer func(start time.Time) { + sampler := func() metrics.Sample { + return metrics.ResettingSample( + metrics.NewExpDecaySample(1028, 0.015), + ) + } + metrics.GetOrRegisterHistogramLazy(h, nil, sampler).Update(time.Since(start).Microseconds()) + }(time.Now()) + } + if handler := handlers[msg.Code]; handler != nil { + return handler(backend, msg, peer) + } + return fmt.Errorf("%w: %v", errInvalidMsgCode, msg.Code) +} diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go new file mode 100644 index 0000000000..954829e37d --- /dev/null +++ b/eth/protocols/eth/handler_test.go @@ -0,0 +1,522 @@ +// 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 . + +package eth + +import ( + "math" + "math/big" + "math/rand" + "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/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/params/types/genesisT" + "github.com/ethereum/go-ethereum/params/vars" + "github.com/ethereum/go-ethereum/trie" +) + +var ( + // testKey is a private key to use for funding a tester account. + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + + // testAddr is the Ethereum address of the tester account. + testAddr = crypto.PubkeyToAddress(testKey.PublicKey) +) + +// testBackend is a mock implementation of the live Ethereum message handler. Its +// purpose is to allow testing the request/reply workflows and wire serialization +// in the `eth` protocol without actually doing any data processing. +type testBackend struct { + db ethdb.Database + chain *core.BlockChain + txpool *core.TxPool +} + +// newTestBackend creates an empty chain and wraps it into a mock backend. +func newTestBackend(blocks int) *testBackend { + return newTestBackendWithGenerator(blocks, nil) +} + +// newTestBackend creates a chain with a number of explicitly defined blocks and +// wraps it into a mock backend. +func newTestBackendWithGenerator(blocks int, generator func(int, *core.BlockGen)) *testBackend { + // Create a database pre-initialize with a genesis block + db := rawdb.NewMemoryDatabase() + gspec := (&genesisT.Genesis{ + Config: params.TestChainConfig, + Alloc: genesisT.GenesisAlloc{testAddr: {Balance: big.NewInt(1000000)}}, + }) + core.MustCommitGenesis(db, gspec) + + chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + + bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, generator) + if _, err := chain.InsertChain(bs); err != nil { + panic(err) + } + txconfig := core.DefaultTxPoolConfig + txconfig.Journal = "" // Don't litter the disk with test journals + + return &testBackend{ + db: db, + chain: chain, + txpool: core.NewTxPool(txconfig, params.TestChainConfig, chain), + } +} + +// close tears down the transaction pool and chain behind the mock backend. +func (b *testBackend) close() { + b.txpool.Stop() + b.chain.Stop() +} + +func (b *testBackend) Chain() *core.BlockChain { return b.chain } +func (b *testBackend) StateBloom() *trie.SyncBloom { return nil } +func (b *testBackend) TxPool() TxPool { return b.txpool } + +func (b *testBackend) RunPeer(peer *Peer, handler Handler) error { + // Normally the backend would do peer mainentance and handshakes. All that + // is omitted and we will just give control back to the handler. + return handler(peer) +} +func (b *testBackend) PeerInfo(enode.ID) interface{} { panic("not implemented") } + +func (b *testBackend) AcceptTxs() bool { + panic("data processing tests should be done in the handler package") +} +func (b *testBackend) Handle(*Peer, Packet) error { + panic("data processing tests should be done in the handler package") +} + +// Tests that block headers can be retrieved from a remote chain based on user queries. +func TestGetBlockHeaders64(t *testing.T) { testGetBlockHeaders(t, 64) } +func TestGetBlockHeaders65(t *testing.T) { testGetBlockHeaders(t, 65) } + +func testGetBlockHeaders(t *testing.T, protocol uint) { + t.Parallel() + + backend := newTestBackend(maxHeadersServe + 15) + defer backend.close() + + peer, _ := newTestPeer("peer", protocol, backend) + defer peer.close() + + // Create a "random" unknown hash for testing + var unknown common.Hash + for i := range unknown { + unknown[i] = byte(i) + } + // Create a batch of tests for various scenarios + limit := uint64(maxHeadersServe) + tests := []struct { + query *GetBlockHeadersPacket // The query to execute for header retrieval + expect []common.Hash // The hashes of the block whose headers are expected + }{ + // A single random block should be retrievable by hash and number too + { + &GetBlockHeadersPacket{Origin: HashOrNumber{Hash: backend.chain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, + []common.Hash{backend.chain.GetBlockByNumber(limit / 2).Hash()}, + }, { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: limit / 2}, Amount: 1}, + []common.Hash{backend.chain.GetBlockByNumber(limit / 2).Hash()}, + }, + // Multiple headers should be retrievable in both directions + { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: limit / 2}, Amount: 3}, + []common.Hash{ + backend.chain.GetBlockByNumber(limit / 2).Hash(), + backend.chain.GetBlockByNumber(limit/2 + 1).Hash(), + backend.chain.GetBlockByNumber(limit/2 + 2).Hash(), + }, + }, { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, + []common.Hash{ + backend.chain.GetBlockByNumber(limit / 2).Hash(), + backend.chain.GetBlockByNumber(limit/2 - 1).Hash(), + backend.chain.GetBlockByNumber(limit/2 - 2).Hash(), + }, + }, + // Multiple headers with skip lists should be retrievable + { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, + []common.Hash{ + backend.chain.GetBlockByNumber(limit / 2).Hash(), + backend.chain.GetBlockByNumber(limit/2 + 4).Hash(), + backend.chain.GetBlockByNumber(limit/2 + 8).Hash(), + }, + }, { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, + []common.Hash{ + backend.chain.GetBlockByNumber(limit / 2).Hash(), + backend.chain.GetBlockByNumber(limit/2 - 4).Hash(), + backend.chain.GetBlockByNumber(limit/2 - 8).Hash(), + }, + }, + // The chain endpoints should be retrievable + { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: 0}, Amount: 1}, + []common.Hash{backend.chain.GetBlockByNumber(0).Hash()}, + }, { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64()}, Amount: 1}, + []common.Hash{backend.chain.CurrentBlock().Hash()}, + }, + // Ensure protocol limits are honored + { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, + backend.chain.GetBlockHashesFromHash(backend.chain.CurrentBlock().Hash(), limit), + }, + // Check that requesting more than available is handled gracefully + { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, + []common.Hash{ + backend.chain.GetBlockByNumber(backend.chain.CurrentBlock().NumberU64() - 4).Hash(), + backend.chain.GetBlockByNumber(backend.chain.CurrentBlock().NumberU64()).Hash(), + }, + }, { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, + []common.Hash{ + backend.chain.GetBlockByNumber(4).Hash(), + backend.chain.GetBlockByNumber(0).Hash(), + }, + }, + // Check that requesting more than available is handled gracefully, even if mid skip + { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, + []common.Hash{ + backend.chain.GetBlockByNumber(backend.chain.CurrentBlock().NumberU64() - 4).Hash(), + backend.chain.GetBlockByNumber(backend.chain.CurrentBlock().NumberU64() - 1).Hash(), + }, + }, { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, + []common.Hash{ + backend.chain.GetBlockByNumber(4).Hash(), + backend.chain.GetBlockByNumber(1).Hash(), + }, + }, + // Check a corner case where requesting more can iterate past the endpoints + { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: 2}, Amount: 5, Reverse: true}, + []common.Hash{ + backend.chain.GetBlockByNumber(2).Hash(), + backend.chain.GetBlockByNumber(1).Hash(), + backend.chain.GetBlockByNumber(0).Hash(), + }, + }, + // Check a corner case where skipping overflow loops back into the chain start + { + &GetBlockHeadersPacket{Origin: HashOrNumber{Hash: backend.chain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1}, + []common.Hash{ + backend.chain.GetBlockByNumber(3).Hash(), + }, + }, + // Check a corner case where skipping overflow loops back to the same header + { + &GetBlockHeadersPacket{Origin: HashOrNumber{Hash: backend.chain.GetBlockByNumber(1).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64}, + []common.Hash{ + backend.chain.GetBlockByNumber(1).Hash(), + }, + }, + // Check that non existing headers aren't returned + { + &GetBlockHeadersPacket{Origin: HashOrNumber{Hash: unknown}, Amount: 1}, + []common.Hash{}, + }, { + &GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64() + 1}, Amount: 1}, + []common.Hash{}, + }, + } + // Run each of the tests and verify the results against the chain + for i, tt := range tests { + // Collect the headers to expect in the response + var headers []*types.Header + for _, hash := range tt.expect { + headers = append(headers, backend.chain.GetBlockByHash(hash).Header()) + } + // Send the hash request and verify the response + p2p.Send(peer.app, GetBlockHeadersMsg, tt.query) + if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, headers); err != nil { + t.Errorf("test %d: headers mismatch: %v", i, err) + } + // If the test used number origins, repeat with hashes as the too + if tt.query.Origin.Hash == (common.Hash{}) { + if origin := backend.chain.GetBlockByNumber(tt.query.Origin.Number); origin != nil { + tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0 + + p2p.Send(peer.app, GetBlockHeadersMsg, tt.query) + if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, headers); err != nil { + t.Errorf("test %d: headers mismatch: %v", i, err) + } + } + } + } +} + +// Tests that block contents can be retrieved from a remote chain based on their hashes. +func TestGetBlockBodies64(t *testing.T) { testGetBlockBodies(t, 64) } +func TestGetBlockBodies65(t *testing.T) { testGetBlockBodies(t, 65) } + +func testGetBlockBodies(t *testing.T, protocol uint) { + t.Parallel() + + backend := newTestBackend(maxBodiesServe + 15) + defer backend.close() + + peer, _ := newTestPeer("peer", protocol, backend) + defer peer.close() + + // Create a batch of tests for various scenarios + limit := maxBodiesServe + tests := []struct { + random int // Number of blocks to fetch randomly from the chain + explicit []common.Hash // Explicitly requested blocks + available []bool // Availability of explicitly requested blocks + expected int // Total number of existing blocks to expect + }{ + {1, nil, nil, 1}, // A single random block should be retrievable + {10, nil, nil, 10}, // Multiple random blocks should be retrievable + {limit, nil, nil, limit}, // The maximum possible blocks should be retrievable + {limit + 1, nil, nil, limit}, // No more than the possible block count should be returned + {0, []common.Hash{backend.chain.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable + {0, []common.Hash{backend.chain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable + {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned + + // Existing and non-existing blocks interleaved should not cause problems + {0, []common.Hash{ + {}, + backend.chain.GetBlockByNumber(1).Hash(), + {}, + backend.chain.GetBlockByNumber(10).Hash(), + {}, + backend.chain.GetBlockByNumber(100).Hash(), + {}, + }, []bool{false, true, false, true, false, true, false}, 3}, + } + // Run each of the tests and verify the results against the chain + for i, tt := range tests { + // Collect the hashes to request, and the response to expectva + var ( + hashes []common.Hash + bodies []*BlockBody + seen = make(map[int64]bool) + ) + for j := 0; j < tt.random; j++ { + for { + num := rand.Int63n(int64(backend.chain.CurrentBlock().NumberU64())) + if !seen[num] { + seen[num] = true + + block := backend.chain.GetBlockByNumber(uint64(num)) + hashes = append(hashes, block.Hash()) + if len(bodies) < tt.expected { + bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) + } + break + } + } + } + for j, hash := range tt.explicit { + hashes = append(hashes, hash) + if tt.available[j] && len(bodies) < tt.expected { + block := backend.chain.GetBlockByHash(hash) + bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) + } + } + // Send the hash request and verify the response + p2p.Send(peer.app, GetBlockBodiesMsg, hashes) + if err := p2p.ExpectMsg(peer.app, BlockBodiesMsg, bodies); err != nil { + t.Errorf("test %d: bodies mismatch: %v", i, err) + } + } +} + +// Tests that the state trie nodes can be retrieved based on hashes. +func TestGetNodeData64(t *testing.T) { testGetNodeData(t, 64) } +func TestGetNodeData65(t *testing.T) { testGetNodeData(t, 65) } + +func testGetNodeData(t *testing.T, protocol uint) { + t.Parallel() + + // Define three accounts to simulate transactions with + acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) + acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) + + signer := types.HomesteadSigner{} + // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) + generator := func(i int, block *core.BlockGen) { + switch i { + case 0: + // In block 1, the test bank sends account #1 some ether. + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(10000), vars.TxGas, nil, nil), signer, testKey) + 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(testAddr), acc1Addr, big.NewInt(1000), vars.TxGas, nil, nil), signer, testKey) + tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), vars.TxGas, nil, nil), signer, acc1Key) + block.AddTx(tx1) + block.AddTx(tx2) + case 2: + // Block 3 is empty but was mined by account #2. + block.SetCoinbase(acc2Addr) + block.SetExtra([]byte("yeehaw")) + case 3: + // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). + b2 := block.PrevBlock(1).Header() + b2.Extra = []byte("foo") + block.AddUncle(b2) + b3 := block.PrevBlock(2).Header() + b3.Extra = []byte("foo") + block.AddUncle(b3) + } + } + // Assemble the test environment + backend := newTestBackendWithGenerator(4, generator) + defer backend.close() + + peer, _ := newTestPeer("peer", protocol, backend) + defer peer.close() + + // Fetch for now the entire chain db + var hashes []common.Hash + + it := backend.db.NewIterator(nil, nil) + for it.Next() { + if key := it.Key(); len(key) == common.HashLength { + hashes = append(hashes, common.BytesToHash(key)) + } + } + it.Release() + + p2p.Send(peer.app, GetNodeDataMsg, hashes) + msg, err := peer.app.ReadMsg() + if err != nil { + t.Fatalf("failed to read node data response: %v", err) + } + if msg.Code != NodeDataMsg { + t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, NodeDataMsg) + } + var data [][]byte + if err := msg.Decode(&data); err != nil { + t.Fatalf("failed to decode response node data: %v", err) + } + // Verify that all hashes correspond to the requested data, and reconstruct a state tree + for i, want := range hashes { + if hash := crypto.Keccak256Hash(data[i]); hash != want { + t.Errorf("data hash mismatch: have %x, want %x", hash, want) + } + } + statedb := rawdb.NewMemoryDatabase() + for i := 0; i < len(data); i++ { + statedb.Put(hashes[i].Bytes(), data[i]) + } + accounts := []common.Address{testAddr, acc1Addr, acc2Addr} + for i := uint64(0); i <= backend.chain.CurrentBlock().NumberU64(); i++ { + trie, _ := state.New(backend.chain.GetBlockByNumber(i).Root(), state.NewDatabase(statedb), nil) + + for j, acc := range accounts { + state, _ := backend.chain.State() + bw := state.GetBalance(acc) + bh := trie.GetBalance(acc) + + if (bw != nil && bh == nil) || (bw == nil && bh != nil) { + t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) + } + if bw != nil && bh != nil && bw.Cmp(bw) != 0 { + t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) + } + } + } +} + +// Tests that the transaction receipts can be retrieved based on hashes. +func TestGetBlockReceipts64(t *testing.T) { testGetBlockReceipts(t, 64) } +func TestGetBlockReceipts65(t *testing.T) { testGetBlockReceipts(t, 65) } + +func testGetBlockReceipts(t *testing.T, protocol uint) { + t.Parallel() + + // Define three accounts to simulate transactions with + acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) + acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) + + signer := types.HomesteadSigner{} + // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) + generator := func(i int, block *core.BlockGen) { + switch i { + case 0: + // In block 1, the test bank sends account #1 some ether. + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(10000), vars.TxGas, nil, nil), signer, testKey) + 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(testAddr), acc1Addr, big.NewInt(1000), vars.TxGas, nil, nil), signer, testKey) + tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), vars.TxGas, nil, nil), signer, acc1Key) + block.AddTx(tx1) + block.AddTx(tx2) + case 2: + // Block 3 is empty but was mined by account #2. + block.SetCoinbase(acc2Addr) + block.SetExtra([]byte("yeehaw")) + case 3: + // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). + b2 := block.PrevBlock(1).Header() + b2.Extra = []byte("foo") + block.AddUncle(b2) + b3 := block.PrevBlock(2).Header() + b3.Extra = []byte("foo") + block.AddUncle(b3) + } + } + // Assemble the test environment + backend := newTestBackendWithGenerator(4, generator) + defer backend.close() + + peer, _ := newTestPeer("peer", protocol, backend) + defer peer.close() + + // Collect the hashes to request, and the response to expect + var ( + hashes []common.Hash + receipts []types.Receipts + ) + for i := uint64(0); i <= backend.chain.CurrentBlock().NumberU64(); i++ { + block := backend.chain.GetBlockByNumber(i) + + hashes = append(hashes, block.Hash()) + receipts = append(receipts, backend.chain.GetReceiptsByHash(block.Hash())) + } + // Send the hash request and verify the response + p2p.Send(peer.app, GetReceiptsMsg, hashes) + if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, receipts); err != nil { + t.Errorf("receipts mismatch: %v", err) + } +} diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go new file mode 100644 index 0000000000..8433fa343a --- /dev/null +++ b/eth/protocols/eth/handlers.go @@ -0,0 +1,510 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "encoding/json" + "fmt" + + "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" + "github.com/ethereum/go-ethereum/trie" +) + +// handleGetBlockHeaders handles Block header query, collect the requested headers and reply +func handleGetBlockHeaders(backend Backend, msg Decoder, peer *Peer) error { + // Decode the complex header query + var query GetBlockHeadersPacket + if err := msg.Decode(&query); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + response := answerGetBlockHeadersQuery(backend, &query, peer) + return peer.SendBlockHeaders(response) +} + +// handleGetBlockHeaders66 is the eth/66 version of handleGetBlockHeaders +func handleGetBlockHeaders66(backend Backend, msg Decoder, peer *Peer) error { + // Decode the complex header query + var query GetBlockHeadersPacket66 + if err := msg.Decode(&query); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + response := answerGetBlockHeadersQuery(backend, query.GetBlockHeadersPacket, peer) + return peer.ReplyBlockHeaders(query.RequestId, response) +} + +func answerGetBlockHeadersQuery(backend Backend, query *GetBlockHeadersPacket, peer *Peer) []*types.Header { + hashMode := query.Origin.Hash != (common.Hash{}) + first := true + maxNonCanonical := uint64(100) + + // Gather headers until the fetch or network limits is reached + var ( + bytes common.StorageSize + headers []*types.Header + unknown bool + lookups int + ) + for !unknown && len(headers) < int(query.Amount) && bytes < softResponseLimit && + len(headers) < maxHeadersServe && lookups < 2*maxHeadersServe { + lookups++ + // Retrieve the next header satisfying the query + var origin *types.Header + if hashMode { + if first { + first = false + origin = backend.Chain().GetHeaderByHash(query.Origin.Hash) + if origin != nil { + query.Origin.Number = origin.Number.Uint64() + } + } else { + origin = backend.Chain().GetHeader(query.Origin.Hash, query.Origin.Number) + } + } else { + origin = backend.Chain().GetHeaderByNumber(query.Origin.Number) + } + if origin == nil { + break + } + headers = append(headers, origin) + bytes += estHeaderSize + + // Advance to the next header of the query + switch { + case hashMode && query.Reverse: + // Hash based traversal towards the genesis block + ancestor := query.Skip + 1 + if ancestor == 0 { + unknown = true + } else { + query.Origin.Hash, query.Origin.Number = backend.Chain().GetAncestor(query.Origin.Hash, query.Origin.Number, ancestor, &maxNonCanonical) + unknown = (query.Origin.Hash == common.Hash{}) + } + case hashMode && !query.Reverse: + // Hash based traversal towards the leaf block + var ( + current = origin.Number.Uint64() + next = current + query.Skip + 1 + ) + if next <= current { + infos, _ := json.MarshalIndent(peer.Peer.Info(), "", " ") + peer.Log().Warn("GetBlockHeaders skip overflow attack", "current", current, "skip", query.Skip, "next", next, "attacker", infos) + unknown = true + } else { + if header := backend.Chain().GetHeaderByNumber(next); header != nil { + nextHash := header.Hash() + expOldHash, _ := backend.Chain().GetAncestor(nextHash, next, query.Skip+1, &maxNonCanonical) + if expOldHash == query.Origin.Hash { + query.Origin.Hash, query.Origin.Number = nextHash, next + } else { + unknown = true + } + } else { + unknown = true + } + } + case query.Reverse: + // Number based traversal towards the genesis block + if query.Origin.Number >= query.Skip+1 { + query.Origin.Number -= query.Skip + 1 + } else { + unknown = true + } + + case !query.Reverse: + // Number based traversal towards the leaf block + query.Origin.Number += query.Skip + 1 + } + } + return headers +} + +func handleGetBlockBodies(backend Backend, msg Decoder, peer *Peer) error { + // Decode the block body retrieval message + var query GetBlockBodiesPacket + if err := msg.Decode(&query); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + response := answerGetBlockBodiesQuery(backend, query, peer) + return peer.SendBlockBodiesRLP(response) +} + +func handleGetBlockBodies66(backend Backend, msg Decoder, peer *Peer) error { + // Decode the block body retrieval message + var query GetBlockBodiesPacket66 + if err := msg.Decode(&query); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + response := answerGetBlockBodiesQuery(backend, query.GetBlockBodiesPacket, peer) + return peer.ReplyBlockBodiesRLP(query.RequestId, response) +} + +func answerGetBlockBodiesQuery(backend Backend, query GetBlockBodiesPacket, peer *Peer) []rlp.RawValue { + // Gather blocks until the fetch or network limits is reached + var ( + bytes int + bodies []rlp.RawValue + ) + for lookups, hash := range query { + if bytes >= softResponseLimit || len(bodies) >= maxBodiesServe || + lookups >= 2*maxBodiesServe { + break + } + if data := backend.Chain().GetBodyRLP(hash); len(data) != 0 { + bodies = append(bodies, data) + bytes += len(data) + } + } + return bodies +} + +func handleGetNodeData(backend Backend, msg Decoder, peer *Peer) error { + // Decode the trie node data retrieval message + var query GetNodeDataPacket + if err := msg.Decode(&query); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + response := answerGetNodeDataQuery(backend, query, peer) + return peer.SendNodeData(response) +} + +func handleGetNodeData66(backend Backend, msg Decoder, peer *Peer) error { + // Decode the trie node data retrieval message + var query GetNodeDataPacket66 + if err := msg.Decode(&query); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + response := answerGetNodeDataQuery(backend, query.GetNodeDataPacket, peer) + return peer.ReplyNodeData(query.RequestId, response) +} + +func answerGetNodeDataQuery(backend Backend, query GetNodeDataPacket, peer *Peer) [][]byte { + // Gather state data until the fetch or network limits is reached + var ( + bytes int + nodes [][]byte + ) + for lookups, hash := range query { + if bytes >= softResponseLimit || len(nodes) >= maxNodeDataServe || + lookups >= 2*maxNodeDataServe { + break + } + // Retrieve the requested state entry + if bloom := backend.StateBloom(); bloom != nil && !bloom.Contains(hash[:]) { + // Only lookup the trie node if there's chance that we actually have it + continue + } + entry, err := backend.Chain().TrieNode(hash) + if len(entry) == 0 || err != nil { + // Read the contract code with prefix only to save unnecessary lookups. + entry, err = backend.Chain().ContractCodeWithPrefix(hash) + } + if err == nil && len(entry) > 0 { + nodes = append(nodes, entry) + bytes += len(entry) + } + } + return nodes +} + +func handleGetReceipts(backend Backend, msg Decoder, peer *Peer) error { + // Decode the block receipts retrieval message + var query GetReceiptsPacket + if err := msg.Decode(&query); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + response := answerGetReceiptsQuery(backend, query, peer) + return peer.SendReceiptsRLP(response) +} + +func handleGetReceipts66(backend Backend, msg Decoder, peer *Peer) error { + // Decode the block receipts retrieval message + var query GetReceiptsPacket66 + if err := msg.Decode(&query); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + response := answerGetReceiptsQuery(backend, query.GetReceiptsPacket, peer) + return peer.ReplyReceiptsRLP(query.RequestId, response) +} + +func answerGetReceiptsQuery(backend Backend, query GetReceiptsPacket, peer *Peer) []rlp.RawValue { + // Gather state data until the fetch or network limits is reached + var ( + bytes int + receipts []rlp.RawValue + ) + for lookups, hash := range query { + if bytes >= softResponseLimit || len(receipts) >= maxReceiptsServe || + lookups >= 2*maxReceiptsServe { + break + } + // Retrieve the requested block's receipts + results := backend.Chain().GetReceiptsByHash(hash) + if results == nil { + if header := backend.Chain().GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { + continue + } + } + // If known, encode and queue for response packet + if encoded, err := rlp.EncodeToBytes(results); err != nil { + log.Error("Failed to encode receipt", "err", err) + } else { + receipts = append(receipts, encoded) + bytes += len(encoded) + } + } + return receipts +} + +func handleNewBlockhashes(backend Backend, msg Decoder, peer *Peer) error { + // A batch of new block announcements just arrived + ann := new(NewBlockHashesPacket) + if err := msg.Decode(ann); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + // Mark the hashes as present at the remote node + for _, block := range *ann { + peer.markBlock(block.Hash) + } + // Deliver them all to the backend for queuing + return backend.Handle(peer, ann) +} + +func handleNewBlock(backend Backend, msg Decoder, peer *Peer) error { + // Retrieve and decode the propagated block + ann := new(NewBlockPacket) + if err := msg.Decode(ann); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + if hash := types.CalcUncleHash(ann.Block.Uncles()); hash != ann.Block.UncleHash() { + log.Warn("Propagated block has invalid uncles", "have", hash, "exp", ann.Block.UncleHash()) + return nil // TODO(karalabe): return error eventually, but wait a few releases + } + if hash := types.DeriveSha(ann.Block.Transactions(), trie.NewStackTrie(nil)); hash != ann.Block.TxHash() { + log.Warn("Propagated block has invalid body", "have", hash, "exp", ann.Block.TxHash()) + return nil // TODO(karalabe): return error eventually, but wait a few releases + } + if err := ann.sanityCheck(); err != nil { + return err + } + ann.Block.ReceivedAt = msg.Time() + ann.Block.ReceivedFrom = peer + + // Mark the peer as owning the block + peer.markBlock(ann.Block.Hash()) + + return backend.Handle(peer, ann) +} + +func handleBlockHeaders(backend Backend, msg Decoder, peer *Peer) error { + // A batch of headers arrived to one of our previous requests + res := new(BlockHeadersPacket) + if err := msg.Decode(res); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + return backend.Handle(peer, res) +} + +func handleBlockHeaders66(backend Backend, msg Decoder, peer *Peer) error { + // A batch of headers arrived to one of our previous requests + res := new(BlockHeadersPacket66) + if err := msg.Decode(res); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + return backend.Handle(peer, &res.BlockHeadersPacket) +} + +func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error { + // A batch of block bodies arrived to one of our previous requests + res := new(BlockBodiesPacket) + if err := msg.Decode(res); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + return backend.Handle(peer, res) +} + +func handleBlockBodies66(backend Backend, msg Decoder, peer *Peer) error { + // A batch of block bodies arrived to one of our previous requests + res := new(BlockBodiesPacket66) + if err := msg.Decode(res); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + return backend.Handle(peer, &res.BlockBodiesPacket) +} + +func handleNodeData(backend Backend, msg Decoder, peer *Peer) error { + // A batch of node state data arrived to one of our previous requests + res := new(NodeDataPacket) + if err := msg.Decode(res); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + return backend.Handle(peer, res) +} + +func handleNodeData66(backend Backend, msg Decoder, peer *Peer) error { + // A batch of node state data arrived to one of our previous requests + res := new(NodeDataPacket66) + if err := msg.Decode(res); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + return backend.Handle(peer, &res.NodeDataPacket) +} + +func handleReceipts(backend Backend, msg Decoder, peer *Peer) error { + // A batch of receipts arrived to one of our previous requests + res := new(ReceiptsPacket) + if err := msg.Decode(res); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + return backend.Handle(peer, res) +} + +func handleReceipts66(backend Backend, msg Decoder, peer *Peer) error { + // A batch of receipts arrived to one of our previous requests + res := new(ReceiptsPacket66) + if err := msg.Decode(res); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + return backend.Handle(peer, &res.ReceiptsPacket) +} + +func handleNewPooledTransactionHashes(backend Backend, msg Decoder, peer *Peer) error { + // New transaction announcement arrived, make sure we have + // a valid and fresh chain to handle them + if !backend.AcceptTxs() { + return nil + } + ann := new(NewPooledTransactionHashesPacket) + if err := msg.Decode(ann); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + // Schedule all the unknown hashes for retrieval + for _, hash := range *ann { + peer.markTransaction(hash) + } + return backend.Handle(peer, ann) +} + +func handleGetPooledTransactions(backend Backend, msg Decoder, peer *Peer) error { + // Decode the pooled transactions retrieval message + var query GetPooledTransactionsPacket + if err := msg.Decode(&query); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + hashes, txs := answerGetPooledTransactions(backend, query, peer) + return peer.SendPooledTransactionsRLP(hashes, txs) +} + +func handleGetPooledTransactions66(backend Backend, msg Decoder, peer *Peer) error { + // Decode the pooled transactions retrieval message + var query GetPooledTransactionsPacket66 + if err := msg.Decode(&query); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + hashes, txs := answerGetPooledTransactions(backend, query.GetPooledTransactionsPacket, peer) + return peer.ReplyPooledTransactionsRLP(query.RequestId, hashes, txs) +} + +func answerGetPooledTransactions(backend Backend, query GetPooledTransactionsPacket, peer *Peer) ([]common.Hash, []rlp.RawValue) { + // Gather transactions until the fetch or network limits is reached + var ( + bytes int + hashes []common.Hash + txs []rlp.RawValue + ) + for _, hash := range query { + if bytes >= softResponseLimit { + break + } + // Retrieve the requested transaction, skipping if unknown to us + tx := backend.TxPool().Get(hash) + if tx == nil { + continue + } + // If known, encode and queue for response packet + if encoded, err := rlp.EncodeToBytes(tx); err != nil { + log.Error("Failed to encode transaction", "err", err) + } else { + hashes = append(hashes, hash) + txs = append(txs, encoded) + bytes += len(encoded) + } + } + return hashes, txs +} + +func handleTransactions(backend Backend, msg Decoder, peer *Peer) error { + // Transactions arrived, make sure we have a valid and fresh chain to handle them + if !backend.AcceptTxs() { + return nil + } + // Transactions can be processed, parse all of them and deliver to the pool + var txs TransactionsPacket + if err := msg.Decode(&txs); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + for i, tx := range txs { + // Validate and mark the remote transaction + if tx == nil { + return fmt.Errorf("%w: transaction %d is nil", errDecode, i) + } + peer.markTransaction(tx.Hash()) + } + return backend.Handle(peer, &txs) +} + +func handlePooledTransactions(backend Backend, msg Decoder, peer *Peer) error { + // Transactions arrived, make sure we have a valid and fresh chain to handle them + if !backend.AcceptTxs() { + return nil + } + // Transactions can be processed, parse all of them and deliver to the pool + var txs PooledTransactionsPacket + if err := msg.Decode(&txs); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + for i, tx := range txs { + // Validate and mark the remote transaction + if tx == nil { + return fmt.Errorf("%w: transaction %d is nil", errDecode, i) + } + peer.markTransaction(tx.Hash()) + } + return backend.Handle(peer, &txs) +} + +func handlePooledTransactions66(backend Backend, msg Decoder, peer *Peer) error { + // Transactions arrived, make sure we have a valid and fresh chain to handle them + if !backend.AcceptTxs() { + return nil + } + // Transactions can be processed, parse all of them and deliver to the pool + var txs PooledTransactionsPacket66 + if err := msg.Decode(&txs); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + for i, tx := range txs.PooledTransactionsPacket { + // Validate and mark the remote transaction + if tx == nil { + return fmt.Errorf("%w: transaction %d is nil", errDecode, i) + } + peer.markTransaction(tx.Hash()) + } + return backend.Handle(peer, &txs.PooledTransactionsPacket) +} diff --git a/eth/protocols/eth/handshake.go b/eth/protocols/eth/handshake.go new file mode 100644 index 0000000000..57a4e0bc34 --- /dev/null +++ b/eth/protocols/eth/handshake.go @@ -0,0 +1,107 @@ +// 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 . + +package eth + +import ( + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/forkid" + "github.com/ethereum/go-ethereum/p2p" +) + +const ( + // handshakeTimeout is the maximum allowed time for the `eth` handshake to + // complete before dropping the connection.= as malicious. + handshakeTimeout = 5 * time.Second +) + +// Handshake executes the eth protocol handshake, negotiating version number, +// network IDs, difficulties, head and genesis blocks. +func (p *Peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) error { + // Send out own handshake in a new thread + errc := make(chan error, 2) + + var status StatusPacket // safe to read after two values have been received from errc + + go func() { + errc <- p2p.Send(p.rw, StatusMsg, &StatusPacket{ + ProtocolVersion: uint32(p.version), + NetworkID: network, + TD: td, + Head: head, + Genesis: genesis, + ForkID: forkID, + }) + }() + go func() { + errc <- p.readStatus(network, &status, genesis, forkFilter) + }() + timeout := time.NewTimer(handshakeTimeout) + defer timeout.Stop() + for i := 0; i < 2; i++ { + select { + case err := <-errc: + if err != nil { + return err + } + case <-timeout.C: + return p2p.DiscReadTimeout + } + } + p.td, p.head = status.TD, status.Head + + // TD at mainnet block #7753254 is 76 bits. If it becomes 100 million times + // larger, it will still fit within 100 bits + if tdlen := p.td.BitLen(); tdlen > 100 { + return fmt.Errorf("too large total difficulty: bitlen %d", tdlen) + } + return nil +} + +// readStatus reads the remote handshake message. +func (p *Peer) readStatus(network uint64, status *StatusPacket, genesis common.Hash, forkFilter forkid.Filter) error { + msg, err := p.rw.ReadMsg() + if err != nil { + return err + } + if msg.Code != StatusMsg { + return fmt.Errorf("%w: first msg has code %x (!= %x)", errNoStatusMsg, msg.Code, StatusMsg) + } + if msg.Size > maxMessageSize { + return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize) + } + // Decode the handshake and make sure everything matches + if err := msg.Decode(&status); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + if status.NetworkID != network { + return fmt.Errorf("%w: %d (!= %d)", errNetworkIDMismatch, status.NetworkID, network) + } + if uint(status.ProtocolVersion) != p.version { + return fmt.Errorf("%w: %d (!= %d)", errProtocolVersionMismatch, status.ProtocolVersion, p.version) + } + if status.Genesis != genesis { + return fmt.Errorf("%w: %x (!= %x)", errGenesisMismatch, status.Genesis, genesis) + } + if err := forkFilter(status.ForkID); err != nil { + return fmt.Errorf("%w: %v", errForkIDRejected, err) + } + return nil +} diff --git a/eth/protocols/eth/handshake_test.go b/eth/protocols/eth/handshake_test.go new file mode 100644 index 0000000000..65f9a00064 --- /dev/null +++ b/eth/protocols/eth/handshake_test.go @@ -0,0 +1,91 @@ +// 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 . + +package eth + +import ( + "errors" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/forkid" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// Tests that handshake failures are detected and reported correctly. +func TestHandshake64(t *testing.T) { testHandshake(t, 64) } +func TestHandshake65(t *testing.T) { testHandshake(t, 65) } + +func testHandshake(t *testing.T, protocol uint) { + t.Parallel() + + // Create a test backend only to have some valid genesis chain + backend := newTestBackend(3) + defer backend.close() + + var ( + genesis = backend.chain.Genesis() + head = backend.chain.CurrentBlock() + td = backend.chain.GetTd(head.Hash(), head.NumberU64()) + forkID = forkid.NewID(backend.chain.Config(), backend.chain.Genesis().Hash(), backend.chain.CurrentHeader().Number.Uint64()) + ) + tests := []struct { + code uint64 + data interface{} + want error + }{ + { + code: TransactionsMsg, data: []interface{}{}, + want: errNoStatusMsg, + }, + { + code: StatusMsg, data: StatusPacket{10, 1, td, head.Hash(), genesis.Hash(), forkID}, + want: errProtocolVersionMismatch, + }, + { + code: StatusMsg, data: StatusPacket{uint32(protocol), 999, td, head.Hash(), genesis.Hash(), forkID}, + want: errNetworkIDMismatch, + }, + { + code: StatusMsg, data: StatusPacket{uint32(protocol), 1, td, head.Hash(), common.Hash{3}, forkID}, + want: errGenesisMismatch, + }, + { + code: StatusMsg, data: StatusPacket{uint32(protocol), 1, td, head.Hash(), genesis.Hash(), forkid.ID{Hash: [4]byte{0x00, 0x01, 0x02, 0x03}}}, + want: errForkIDRejected, + }, + } + for i, test := range tests { + // Create the two peers to shake with each other + app, net := p2p.MsgPipe() + defer app.Close() + defer net.Close() + + peer := NewPeer(protocol, p2p.NewPeer(enode.ID{}, "peer", nil), net, nil) + defer peer.Close() + + // Send the junk test with one peer, check the handshake failure + go p2p.Send(app, test.code, test.data) + + err := peer.Handshake(1, td, head.Hash(), genesis.Hash(), forkID, forkid.NewFilter(backend.chain)) + if err == nil { + t.Errorf("test %d: protocol returned nil error, want %q", i, test.want) + } else if !errors.Is(err, test.want) { + t.Errorf("test %d: wrong error: got %q, want %q", i, err, test.want) + } + } +} diff --git a/eth/protocols/eth/peer.go b/eth/protocols/eth/peer.go new file mode 100644 index 0000000000..709fca8655 --- /dev/null +++ b/eth/protocols/eth/peer.go @@ -0,0 +1,522 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "math/big" + "math/rand" + "sync" + + mapset "github.com/deckarep/golang-set" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" +) + +const ( + // maxKnownTxs is the maximum transactions hashes to keep in the known list + // before starting to randomly evict them. + maxKnownTxs = 32768 + + // maxKnownBlocks is the maximum block hashes to keep in the known list + // before starting to randomly evict them. + maxKnownBlocks = 1024 + + // maxQueuedTxs is the maximum number of transactions to queue up before dropping + // older broadcasts. + maxQueuedTxs = 4096 + + // maxQueuedTxAnns is the maximum number of transaction announcements to queue up + // before dropping older announcements. + maxQueuedTxAnns = 4096 + + // maxQueuedBlocks is the maximum number of block propagations to queue up before + // dropping broadcasts. There's not much point in queueing stale blocks, so a few + // that might cover uncles should be enough. + maxQueuedBlocks = 4 + + // maxQueuedBlockAnns is the maximum number of block announcements to queue up before + // dropping broadcasts. Similarly to block propagations, there's no point to queue + // above some healthy uncle limit, so use that. + maxQueuedBlockAnns = 4 +) + +// max is a helper function which returns the larger of the two given integers. +func max(a, b int) int { + if a > b { + return a + } + return b +} + +// Peer is a collection of relevant information we have about a `eth` peer. +type Peer struct { + id string // Unique ID for the peer, cached + + *p2p.Peer // The embedded P2P package peer + rw p2p.MsgReadWriter // Input/output streams for snap + version uint // Protocol version negotiated + + head common.Hash // Latest advertised head block hash + td *big.Int // Latest advertised head block total difficulty + + knownBlocks mapset.Set // Set of block hashes known to be known by this peer + queuedBlocks chan *blockPropagation // Queue of blocks to broadcast to the peer + queuedBlockAnns chan *types.Block // Queue of blocks to announce to the peer + + txpool TxPool // Transaction pool used by the broadcasters for liveness checks + knownTxs mapset.Set // Set of transaction hashes known to be known by this peer + txBroadcast chan []common.Hash // Channel used to queue transaction propagation requests + txAnnounce chan []common.Hash // Channel used to queue transaction announcement requests + + term chan struct{} // Termination channel to stop the broadcasters + lock sync.RWMutex // Mutex protecting the internal fields +} + +// NewPeer create a wrapper for a network connection and negotiated protocol +// version. +func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool) *Peer { + peer := &Peer{ + id: p.ID().String(), + Peer: p, + rw: rw, + version: version, + knownTxs: mapset.NewSet(), + knownBlocks: mapset.NewSet(), + queuedBlocks: make(chan *blockPropagation, maxQueuedBlocks), + queuedBlockAnns: make(chan *types.Block, maxQueuedBlockAnns), + txBroadcast: make(chan []common.Hash), + txAnnounce: make(chan []common.Hash), + txpool: txpool, + term: make(chan struct{}), + } + // Start up all the broadcasters + go peer.broadcastBlocks() + go peer.broadcastTransactions() + if version >= ETH65 { + go peer.announceTransactions() + } + return peer +} + +// Close signals the broadcast goroutine to terminate. Only ever call this if +// you created the peer yourself via NewPeer. Otherwise let whoever created it +// clean it up! +func (p *Peer) Close() { + close(p.term) +} + +// ID retrieves the peer's unique identifier. +func (p *Peer) ID() string { + return p.id +} + +// Version retrieves the peer's negoatiated `eth` protocol version. +func (p *Peer) Version() uint { + return p.version +} + +// Head retrieves the current head hash and total difficulty of the peer. +func (p *Peer) Head() (hash common.Hash, td *big.Int) { + p.lock.RLock() + defer p.lock.RUnlock() + + copy(hash[:], p.head[:]) + return hash, new(big.Int).Set(p.td) +} + +// SetHead updates the head hash and total difficulty of the peer. +func (p *Peer) SetHead(hash common.Hash, td *big.Int) { + p.lock.Lock() + defer p.lock.Unlock() + + copy(p.head[:], hash[:]) + p.td.Set(td) +} + +// KnownBlock returns whether peer is known to already have a block. +func (p *Peer) KnownBlock(hash common.Hash) bool { + return p.knownBlocks.Contains(hash) +} + +// KnownTransaction returns whether peer is known to already have a transaction. +func (p *Peer) KnownTransaction(hash common.Hash) bool { + return p.knownTxs.Contains(hash) +} + +// markBlock marks a block as known for the peer, ensuring that the block will +// never be propagated to this particular peer. +func (p *Peer) markBlock(hash common.Hash) { + // If we reached the memory allowance, drop a previously known block hash + for p.knownBlocks.Cardinality() >= maxKnownBlocks { + p.knownBlocks.Pop() + } + p.knownBlocks.Add(hash) +} + +// markTransaction marks a transaction as known for the peer, ensuring that it +// will never be propagated to this particular peer. +func (p *Peer) markTransaction(hash common.Hash) { + // If we reached the memory allowance, drop a previously known transaction hash + for p.knownTxs.Cardinality() >= maxKnownTxs { + p.knownTxs.Pop() + } + p.knownTxs.Add(hash) +} + +// SendTransactions sends transactions to the peer and includes the hashes +// in its transaction hash set for future reference. +// +// This method is a helper used by the async transaction sender. Don't call it +// directly as the queueing (memory) and transmission (bandwidth) costs should +// not be managed directly. +// +// The reasons this is public is to allow packages using this protocol to write +// tests that directly send messages without having to do the asyn queueing. +func (p *Peer) SendTransactions(txs types.Transactions) error { + // Mark all the transactions as known, but ensure we don't overflow our limits + for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(txs)) { + p.knownTxs.Pop() + } + for _, tx := range txs { + p.knownTxs.Add(tx.Hash()) + } + return p2p.Send(p.rw, TransactionsMsg, txs) +} + +// AsyncSendTransactions queues a list of transactions (by hash) to eventually +// propagate to a remote peer. The number of pending sends are capped (new ones +// will force old sends to be dropped) +func (p *Peer) AsyncSendTransactions(hashes []common.Hash) { + select { + case p.txBroadcast <- hashes: + // Mark all the transactions as known, but ensure we don't overflow our limits + for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { + p.knownTxs.Pop() + } + for _, hash := range hashes { + p.knownTxs.Add(hash) + } + case <-p.term: + p.Log().Debug("Dropping transaction propagation", "count", len(hashes)) + } +} + +// sendPooledTransactionHashes sends transaction hashes to the peer and includes +// them in its transaction hash set for future reference. +// +// This method is a helper used by the async transaction announcer. Don't call it +// directly as the queueing (memory) and transmission (bandwidth) costs should +// not be managed directly. +func (p *Peer) sendPooledTransactionHashes(hashes []common.Hash) error { + // Mark all the transactions as known, but ensure we don't overflow our limits + for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { + p.knownTxs.Pop() + } + for _, hash := range hashes { + p.knownTxs.Add(hash) + } + return p2p.Send(p.rw, NewPooledTransactionHashesMsg, NewPooledTransactionHashesPacket(hashes)) +} + +// AsyncSendPooledTransactionHashes queues a list of transactions hashes to eventually +// announce to a remote peer. The number of pending sends are capped (new ones +// will force old sends to be dropped) +func (p *Peer) AsyncSendPooledTransactionHashes(hashes []common.Hash) { + select { + case p.txAnnounce <- hashes: + // Mark all the transactions as known, but ensure we don't overflow our limits + for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { + p.knownTxs.Pop() + } + for _, hash := range hashes { + p.knownTxs.Add(hash) + } + case <-p.term: + p.Log().Debug("Dropping transaction announcement", "count", len(hashes)) + } +} + +// SendPooledTransactionsRLP sends requested transactions to the peer and adds the +// hashes in its transaction hash set for future reference. +// +// Note, the method assumes the hashes are correct and correspond to the list of +// transactions being sent. +func (p *Peer) SendPooledTransactionsRLP(hashes []common.Hash, txs []rlp.RawValue) error { + // Mark all the transactions as known, but ensure we don't overflow our limits + for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { + p.knownTxs.Pop() + } + for _, hash := range hashes { + p.knownTxs.Add(hash) + } + return p2p.Send(p.rw, PooledTransactionsMsg, txs) // Not packed into PooledTransactionsPacket to avoid RLP decoding +} + +// ReplyPooledTransactionsRLP is the eth/66 version of SendPooledTransactionsRLP. +func (p *Peer) ReplyPooledTransactionsRLP(id uint64, hashes []common.Hash, txs []rlp.RawValue) error { + // Mark all the transactions as known, but ensure we don't overflow our limits + for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { + p.knownTxs.Pop() + } + for _, hash := range hashes { + p.knownTxs.Add(hash) + } + // Not packed into PooledTransactionsPacket to avoid RLP decoding + return p2p.Send(p.rw, PooledTransactionsMsg, PooledTransactionsRLPPacket66{ + RequestId: id, + PooledTransactionsRLPPacket: txs, + }) +} + +// SendNewBlockHashes announces the availability of a number of blocks through +// a hash notification. +func (p *Peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error { + // Mark all the block hashes as known, but ensure we don't overflow our limits + for p.knownBlocks.Cardinality() > max(0, maxKnownBlocks-len(hashes)) { + p.knownBlocks.Pop() + } + for _, hash := range hashes { + p.knownBlocks.Add(hash) + } + request := make(NewBlockHashesPacket, len(hashes)) + for i := 0; i < len(hashes); i++ { + request[i].Hash = hashes[i] + request[i].Number = numbers[i] + } + return p2p.Send(p.rw, NewBlockHashesMsg, request) +} + +// AsyncSendNewBlockHash queues the availability of a block for propagation to a +// remote peer. If the peer's broadcast queue is full, the event is silently +// dropped. +func (p *Peer) AsyncSendNewBlockHash(block *types.Block) { + select { + case p.queuedBlockAnns <- block: + // Mark all the block hash as known, but ensure we don't overflow our limits + for p.knownBlocks.Cardinality() >= maxKnownBlocks { + p.knownBlocks.Pop() + } + p.knownBlocks.Add(block.Hash()) + default: + p.Log().Debug("Dropping block announcement", "number", block.NumberU64(), "hash", block.Hash()) + } +} + +// SendNewBlock propagates an entire block to a remote peer. +func (p *Peer) SendNewBlock(block *types.Block, td *big.Int) error { + // Mark all the block hash as known, but ensure we don't overflow our limits + for p.knownBlocks.Cardinality() >= maxKnownBlocks { + p.knownBlocks.Pop() + } + p.knownBlocks.Add(block.Hash()) + return p2p.Send(p.rw, NewBlockMsg, &NewBlockPacket{ + Block: block, + TD: td, + }) +} + +// AsyncSendNewBlock queues an entire block for propagation to a remote peer. If +// the peer's broadcast queue is full, the event is silently dropped. +func (p *Peer) AsyncSendNewBlock(block *types.Block, td *big.Int) { + select { + case p.queuedBlocks <- &blockPropagation{block: block, td: td}: + // Mark all the block hash as known, but ensure we don't overflow our limits + for p.knownBlocks.Cardinality() >= maxKnownBlocks { + p.knownBlocks.Pop() + } + p.knownBlocks.Add(block.Hash()) + default: + p.Log().Debug("Dropping block propagation", "number", block.NumberU64(), "hash", block.Hash()) + } +} + +// SendBlockHeaders sends a batch of block headers to the remote peer. +func (p *Peer) SendBlockHeaders(headers []*types.Header) error { + return p2p.Send(p.rw, BlockHeadersMsg, BlockHeadersPacket(headers)) +} + +// ReplyBlockHeaders is the eth/66 version of SendBlockHeaders. +func (p *Peer) ReplyBlockHeaders(id uint64, headers []*types.Header) error { + return p2p.Send(p.rw, BlockHeadersMsg, BlockHeadersPacket66{ + RequestId: id, + BlockHeadersPacket: headers, + }) +} + +// SendBlockBodiesRLP sends a batch of block contents to the remote peer from +// an already RLP encoded format. +func (p *Peer) SendBlockBodiesRLP(bodies []rlp.RawValue) error { + return p2p.Send(p.rw, BlockBodiesMsg, bodies) // Not packed into BlockBodiesPacket to avoid RLP decoding +} + +// ReplyBlockBodiesRLP is the eth/66 version of SendBlockBodiesRLP. +func (p *Peer) ReplyBlockBodiesRLP(id uint64, bodies []rlp.RawValue) error { + // Not packed into BlockBodiesPacket to avoid RLP decoding + return p2p.Send(p.rw, BlockBodiesMsg, BlockBodiesRLPPacket66{ + RequestId: id, + BlockBodiesRLPPacket: bodies, + }) +} + +// SendNodeDataRLP sends a batch of arbitrary internal data, corresponding to the +// hashes requested. +func (p *Peer) SendNodeData(data [][]byte) error { + return p2p.Send(p.rw, NodeDataMsg, NodeDataPacket(data)) +} + +// ReplyNodeData is the eth/66 response to GetNodeData. +func (p *Peer) ReplyNodeData(id uint64, data [][]byte) error { + return p2p.Send(p.rw, NodeDataMsg, NodeDataPacket66{ + RequestId: id, + NodeDataPacket: data, + }) +} + +// SendReceiptsRLP sends a batch of transaction receipts, corresponding to the +// ones requested from an already RLP encoded format. +func (p *Peer) SendReceiptsRLP(receipts []rlp.RawValue) error { + return p2p.Send(p.rw, ReceiptsMsg, receipts) // Not packed into ReceiptsPacket to avoid RLP decoding +} + +// ReplyReceiptsRLP is the eth/66 response to GetReceipts. +func (p *Peer) ReplyReceiptsRLP(id uint64, receipts []rlp.RawValue) error { + return p2p.Send(p.rw, ReceiptsMsg, ReceiptsRLPPacket66{ + RequestId: id, + ReceiptsRLPPacket: receipts, + }) +} + +// RequestOneHeader is a wrapper around the header query functions to fetch a +// single header. It is used solely by the fetcher. +func (p *Peer) RequestOneHeader(hash common.Hash) error { + p.Log().Debug("Fetching single header", "hash", hash) + query := GetBlockHeadersPacket{ + Origin: HashOrNumber{Hash: hash}, + Amount: uint64(1), + Skip: uint64(0), + Reverse: false, + } + if p.Version() >= ETH66 { + return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{ + RequestId: rand.Uint64(), + GetBlockHeadersPacket: &query, + }) + } + return p2p.Send(p.rw, GetBlockHeadersMsg, &query) +} + +// RequestHeadersByHash fetches a batch of blocks' headers corresponding to the +// specified header query, based on the hash of an origin block. +func (p *Peer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error { + p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse) + query := GetBlockHeadersPacket{ + Origin: HashOrNumber{Hash: origin}, + Amount: uint64(amount), + Skip: uint64(skip), + Reverse: reverse, + } + if p.Version() >= ETH66 { + return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{ + RequestId: rand.Uint64(), + GetBlockHeadersPacket: &query, + }) + } + return p2p.Send(p.rw, GetBlockHeadersMsg, &query) +} + +// RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the +// specified header query, based on the number of an origin block. +func (p *Peer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error { + p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse) + query := GetBlockHeadersPacket{ + Origin: HashOrNumber{Number: origin}, + Amount: uint64(amount), + Skip: uint64(skip), + Reverse: reverse, + } + if p.Version() >= ETH66 { + return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{ + RequestId: rand.Uint64(), + GetBlockHeadersPacket: &query, + }) + } + return p2p.Send(p.rw, GetBlockHeadersMsg, &query) +} + +// ExpectRequestHeadersByNumber is a testing method to mirror the recipient side +// of the RequestHeadersByNumber operation. +func (p *Peer) ExpectRequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error { + req := &GetBlockHeadersPacket{ + Origin: HashOrNumber{Number: origin}, + Amount: uint64(amount), + Skip: uint64(skip), + Reverse: reverse, + } + return p2p.ExpectMsg(p.rw, GetBlockHeadersMsg, req) +} + +// RequestBodies fetches a batch of blocks' bodies corresponding to the hashes +// specified. +func (p *Peer) RequestBodies(hashes []common.Hash) error { + p.Log().Debug("Fetching batch of block bodies", "count", len(hashes)) + if p.Version() >= ETH66 { + return p2p.Send(p.rw, GetBlockBodiesMsg, &GetBlockBodiesPacket66{ + RequestId: rand.Uint64(), + GetBlockBodiesPacket: hashes, + }) + } + return p2p.Send(p.rw, GetBlockBodiesMsg, GetBlockBodiesPacket(hashes)) +} + +// RequestNodeData fetches a batch of arbitrary data from a node's known state +// data, corresponding to the specified hashes. +func (p *Peer) RequestNodeData(hashes []common.Hash) error { + p.Log().Debug("Fetching batch of state data", "count", len(hashes)) + if p.Version() >= ETH66 { + return p2p.Send(p.rw, GetNodeDataMsg, &GetNodeDataPacket66{ + RequestId: rand.Uint64(), + GetNodeDataPacket: hashes, + }) + } + return p2p.Send(p.rw, GetNodeDataMsg, GetNodeDataPacket(hashes)) +} + +// RequestReceipts fetches a batch of transaction receipts from a remote node. +func (p *Peer) RequestReceipts(hashes []common.Hash) error { + p.Log().Debug("Fetching batch of receipts", "count", len(hashes)) + if p.Version() >= ETH66 { + return p2p.Send(p.rw, GetReceiptsMsg, &GetReceiptsPacket66{ + RequestId: rand.Uint64(), + GetReceiptsPacket: hashes, + }) + } + return p2p.Send(p.rw, GetReceiptsMsg, GetReceiptsPacket(hashes)) +} + +// RequestTxs fetches a batch of transactions from a remote node. +func (p *Peer) RequestTxs(hashes []common.Hash) error { + p.Log().Debug("Fetching batch of transactions", "count", len(hashes)) + if p.Version() >= ETH66 { + return p2p.Send(p.rw, GetPooledTransactionsMsg, &GetPooledTransactionsPacket66{ + RequestId: rand.Uint64(), + GetPooledTransactionsPacket: hashes, + }) + } + return p2p.Send(p.rw, GetPooledTransactionsMsg, GetPooledTransactionsPacket(hashes)) +} diff --git a/eth/protocols/eth/peer_test.go b/eth/protocols/eth/peer_test.go new file mode 100644 index 0000000000..70e9959f82 --- /dev/null +++ b/eth/protocols/eth/peer_test.go @@ -0,0 +1,61 @@ +// 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 . + +// This file contains some shares testing functionality, common to multiple +// different files and modules being tested. + +package eth + +import ( + "crypto/rand" + + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// testPeer is a simulated peer to allow testing direct network calls. +type testPeer struct { + *Peer + + net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging + app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side +} + +// newTestPeer creates a new peer registered at the given data backend. +func newTestPeer(name string, version uint, backend Backend) (*testPeer, <-chan error) { + // Create a message pipe to communicate through + app, net := p2p.MsgPipe() + + // Start the peer on a new thread + var id enode.ID + rand.Read(id[:]) + + peer := NewPeer(version, p2p.NewPeer(id, name, nil), net, backend.TxPool()) + errc := make(chan error, 1) + go func() { + errc <- backend.RunPeer(peer, func(peer *Peer) error { + return Handle(backend, peer) + }) + }() + return &testPeer{app: app, net: net, Peer: peer}, errc +} + +// close terminates the local side of the peer, notifying the remote protocol +// manager of termination. +func (p *testPeer) close() { + p.Peer.Close() + p.app.Close() +} diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go new file mode 100644 index 0000000000..7f1832754f --- /dev/null +++ b/eth/protocols/eth/protocol.go @@ -0,0 +1,368 @@ +// 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 . + +package eth + +import ( + "errors" + "fmt" + "io" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/forkid" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" +) + +// Constants to match up protocol versions and messages +const ( + ETH64 = 64 + ETH65 = 65 + ETH66 = 66 +) + +// ProtocolName is the official short name of the `eth` protocol used during +// devp2p capability negotiation. +const ProtocolName = "eth" + +// ProtocolVersions are the supported versions of the `eth` protocol (first +// is primary). +var ProtocolVersions = []uint{ETH66, ETH65, ETH64} + +// protocolLengths are the number of implemented message corresponding to +// different protocol versions. +var protocolLengths = map[uint]uint64{ETH66: 17, ETH65: 17, ETH64: 17} + +// maxMessageSize is the maximum cap on the size of a protocol message. +const maxMessageSize = 10 * 1024 * 1024 + +const ( + // Protocol messages in eth/64 + StatusMsg = 0x00 + NewBlockHashesMsg = 0x01 + TransactionsMsg = 0x02 + GetBlockHeadersMsg = 0x03 + BlockHeadersMsg = 0x04 + GetBlockBodiesMsg = 0x05 + BlockBodiesMsg = 0x06 + NewBlockMsg = 0x07 + GetNodeDataMsg = 0x0d + NodeDataMsg = 0x0e + GetReceiptsMsg = 0x0f + ReceiptsMsg = 0x10 + + // Protocol messages overloaded in eth/65 + NewPooledTransactionHashesMsg = 0x08 + GetPooledTransactionsMsg = 0x09 + PooledTransactionsMsg = 0x0a +) + +var ( + errNoStatusMsg = errors.New("no status message") + errMsgTooLarge = errors.New("message too long") + errDecode = errors.New("invalid message") + errInvalidMsgCode = errors.New("invalid message code") + errProtocolVersionMismatch = errors.New("protocol version mismatch") + errNetworkIDMismatch = errors.New("network ID mismatch") + errGenesisMismatch = errors.New("genesis mismatch") + errForkIDRejected = errors.New("fork ID rejected") +) + +// Packet represents a p2p message in the `eth` protocol. +type Packet interface { + Name() string // Name returns a string corresponding to the message type. + Kind() byte // Kind returns the message type. +} + +// StatusPacket is the network packet for the status message for eth/64 and later. +type StatusPacket struct { + ProtocolVersion uint32 + NetworkID uint64 + TD *big.Int + Head common.Hash + Genesis common.Hash + ForkID forkid.ID +} + +// NewBlockHashesPacket is the network packet for the block announcements. +type NewBlockHashesPacket []struct { + Hash common.Hash // Hash of one particular block being announced + Number uint64 // Number of one particular block being announced +} + +// Unpack retrieves the block hashes and numbers from the announcement packet +// and returns them in a split flat format that's more consistent with the +// internal data structures. +func (p *NewBlockHashesPacket) Unpack() ([]common.Hash, []uint64) { + var ( + hashes = make([]common.Hash, len(*p)) + numbers = make([]uint64, len(*p)) + ) + for i, body := range *p { + hashes[i], numbers[i] = body.Hash, body.Number + } + return hashes, numbers +} + +// TransactionsPacket is the network packet for broadcasting new transactions. +type TransactionsPacket []*types.Transaction + +// GetBlockHeadersPacket represents a block header query. +type GetBlockHeadersPacket struct { + Origin HashOrNumber // Block from which to retrieve headers + Amount uint64 // Maximum number of headers to retrieve + Skip uint64 // Blocks to skip between consecutive headers + Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis) +} + +// GetBlockHeadersPacket represents a block header query over eth/66 +type GetBlockHeadersPacket66 struct { + RequestId uint64 + *GetBlockHeadersPacket +} + +// HashOrNumber is a combined field for specifying an origin block. +type HashOrNumber struct { + Hash common.Hash // Block hash from which to retrieve headers (excludes Number) + Number uint64 // Block hash from which to retrieve headers (excludes Hash) +} + +// EncodeRLP is a specialized encoder for HashOrNumber to encode only one of the +// two contained union fields. +func (hn *HashOrNumber) EncodeRLP(w io.Writer) error { + if hn.Hash == (common.Hash{}) { + return rlp.Encode(w, hn.Number) + } + if hn.Number != 0 { + return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number) + } + return rlp.Encode(w, hn.Hash) +} + +// DecodeRLP is a specialized decoder for HashOrNumber to decode the contents +// into either a block hash or a block number. +func (hn *HashOrNumber) DecodeRLP(s *rlp.Stream) error { + _, size, _ := s.Kind() + origin, err := s.Raw() + if err == nil { + switch { + case size == 32: + err = rlp.DecodeBytes(origin, &hn.Hash) + case size <= 8: + err = rlp.DecodeBytes(origin, &hn.Number) + default: + err = fmt.Errorf("invalid input size %d for origin", size) + } + } + return err +} + +// BlockHeadersPacket represents a block header response. +type BlockHeadersPacket []*types.Header + +// BlockHeadersPacket represents a block header response over eth/66. +type BlockHeadersPacket66 struct { + RequestId uint64 + BlockHeadersPacket +} + +// NewBlockPacket is the network packet for the block propagation message. +type NewBlockPacket struct { + Block *types.Block + TD *big.Int +} + +// sanityCheck verifies that the values are reasonable, as a DoS protection +func (request *NewBlockPacket) sanityCheck() error { + if err := request.Block.SanityCheck(); err != nil { + return err + } + //TD at mainnet block #7753254 is 76 bits. If it becomes 100 million times + // larger, it will still fit within 100 bits + if tdlen := request.TD.BitLen(); tdlen > 100 { + return fmt.Errorf("too large block TD: bitlen %d", tdlen) + } + return nil +} + +// GetBlockBodiesPacket represents a block body query. +type GetBlockBodiesPacket []common.Hash + +// GetBlockBodiesPacket represents a block body query over eth/66. +type GetBlockBodiesPacket66 struct { + RequestId uint64 + GetBlockBodiesPacket +} + +// BlockBodiesPacket is the network packet for block content distribution. +type BlockBodiesPacket []*BlockBody + +// BlockBodiesPacket is the network packet for block content distribution over eth/66. +type BlockBodiesPacket66 struct { + RequestId uint64 + BlockBodiesPacket +} + +// BlockBodiesRLPPacket is used for replying to block body requests, in cases +// where we already have them RLP-encoded, and thus can avoid the decode-encode +// roundtrip. +type BlockBodiesRLPPacket []rlp.RawValue + +// BlockBodiesRLPPacket66 is the BlockBodiesRLPPacket over eth/66 +type BlockBodiesRLPPacket66 struct { + RequestId uint64 + BlockBodiesRLPPacket +} + +// BlockBody represents the data content of a single block. +type BlockBody struct { + Transactions []*types.Transaction // Transactions contained within a block + Uncles []*types.Header // Uncles contained within a block +} + +// Unpack retrieves the transactions and uncles from the range packet and returns +// them in a split flat format that's more consistent with the internal data structures. +func (p *BlockBodiesPacket) Unpack() ([][]*types.Transaction, [][]*types.Header) { + var ( + txset = make([][]*types.Transaction, len(*p)) + uncleset = make([][]*types.Header, len(*p)) + ) + for i, body := range *p { + txset[i], uncleset[i] = body.Transactions, body.Uncles + } + return txset, uncleset +} + +// GetNodeDataPacket represents a trie node data query. +type GetNodeDataPacket []common.Hash + +// GetNodeDataPacket represents a trie node data query over eth/66. +type GetNodeDataPacket66 struct { + RequestId uint64 + GetNodeDataPacket +} + +// NodeDataPacket is the network packet for trie node data distribution. +type NodeDataPacket [][]byte + +// NodeDataPacket is the network packet for trie node data distribution over eth/66. +type NodeDataPacket66 struct { + RequestId uint64 + NodeDataPacket +} + +// GetReceiptsPacket represents a block receipts query. +type GetReceiptsPacket []common.Hash + +// GetReceiptsPacket represents a block receipts query over eth/66. +type GetReceiptsPacket66 struct { + RequestId uint64 + GetReceiptsPacket +} + +// ReceiptsPacket is the network packet for block receipts distribution. +type ReceiptsPacket [][]*types.Receipt + +// ReceiptsPacket is the network packet for block receipts distribution over eth/66. +type ReceiptsPacket66 struct { + RequestId uint64 + ReceiptsPacket +} + +// ReceiptsRLPPacket is used for receipts, when we already have it encoded +type ReceiptsRLPPacket []rlp.RawValue + +// ReceiptsPacket66 is the eth-66 version of ReceiptsRLPPacket +type ReceiptsRLPPacket66 struct { + RequestId uint64 + ReceiptsRLPPacket +} + +// NewPooledTransactionHashesPacket represents a transaction announcement packet. +type NewPooledTransactionHashesPacket []common.Hash + +// GetPooledTransactionsPacket represents a transaction query. +type GetPooledTransactionsPacket []common.Hash + +type GetPooledTransactionsPacket66 struct { + RequestId uint64 + GetPooledTransactionsPacket +} + +// PooledTransactionsPacket is the network packet for transaction distribution. +type PooledTransactionsPacket []*types.Transaction + +// PooledTransactionsPacket is the network packet for transaction distribution over eth/66. +type PooledTransactionsPacket66 struct { + RequestId uint64 + PooledTransactionsPacket +} + +// PooledTransactionsPacket is the network packet for transaction distribution, used +// in the cases we already have them in rlp-encoded form +type PooledTransactionsRLPPacket []rlp.RawValue + +// PooledTransactionsRLPPacket66 is the eth/66 form of PooledTransactionsRLPPacket +type PooledTransactionsRLPPacket66 struct { + RequestId uint64 + PooledTransactionsRLPPacket +} + +func (*StatusPacket) Name() string { return "Status" } +func (*StatusPacket) Kind() byte { return StatusMsg } + +func (*NewBlockHashesPacket) Name() string { return "NewBlockHashes" } +func (*NewBlockHashesPacket) Kind() byte { return NewBlockHashesMsg } + +func (*TransactionsPacket) Name() string { return "Transactions" } +func (*TransactionsPacket) Kind() byte { return TransactionsMsg } + +func (*GetBlockHeadersPacket) Name() string { return "GetBlockHeaders" } +func (*GetBlockHeadersPacket) Kind() byte { return GetBlockHeadersMsg } + +func (*BlockHeadersPacket) Name() string { return "BlockHeaders" } +func (*BlockHeadersPacket) Kind() byte { return BlockHeadersMsg } + +func (*GetBlockBodiesPacket) Name() string { return "GetBlockBodies" } +func (*GetBlockBodiesPacket) Kind() byte { return GetBlockBodiesMsg } + +func (*BlockBodiesPacket) Name() string { return "BlockBodies" } +func (*BlockBodiesPacket) Kind() byte { return BlockBodiesMsg } + +func (*NewBlockPacket) Name() string { return "NewBlock" } +func (*NewBlockPacket) Kind() byte { return NewBlockMsg } + +func (*GetNodeDataPacket) Name() string { return "GetNodeData" } +func (*GetNodeDataPacket) Kind() byte { return GetNodeDataMsg } + +func (*NodeDataPacket) Name() string { return "NodeData" } +func (*NodeDataPacket) Kind() byte { return NodeDataMsg } + +func (*GetReceiptsPacket) Name() string { return "GetReceipts" } +func (*GetReceiptsPacket) Kind() byte { return GetReceiptsMsg } + +func (*ReceiptsPacket) Name() string { return "Receipts" } +func (*ReceiptsPacket) Kind() byte { return ReceiptsMsg } + +func (*NewPooledTransactionHashesPacket) Name() string { return "NewPooledTransactionHashes" } +func (*NewPooledTransactionHashesPacket) Kind() byte { return NewPooledTransactionHashesMsg } + +func (*GetPooledTransactionsPacket) Name() string { return "GetPooledTransactions" } +func (*GetPooledTransactionsPacket) Kind() byte { return GetPooledTransactionsMsg } + +func (*PooledTransactionsPacket) Name() string { return "PooledTransactions" } +func (*PooledTransactionsPacket) Kind() byte { return PooledTransactionsMsg } diff --git a/eth/protocols/eth/protocol_test.go b/eth/protocols/eth/protocol_test.go new file mode 100644 index 0000000000..7910c9b735 --- /dev/null +++ b/eth/protocols/eth/protocol_test.go @@ -0,0 +1,268 @@ +// 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 . + +package eth + +import ( + "bytes" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" +) + +// Tests that the custom union field encoder and decoder works correctly. +func TestGetBlockHeadersDataEncodeDecode(t *testing.T) { + // Create a "random" hash for testing + var hash common.Hash + for i := range hash { + hash[i] = byte(i) + } + // Assemble some table driven tests + tests := []struct { + packet *GetBlockHeadersPacket + fail bool + }{ + // Providing the origin as either a hash or a number should both work + {fail: false, packet: &GetBlockHeadersPacket{Origin: HashOrNumber{Number: 314}}}, + {fail: false, packet: &GetBlockHeadersPacket{Origin: HashOrNumber{Hash: hash}}}, + + // Providing arbitrary query field should also work + {fail: false, packet: &GetBlockHeadersPacket{Origin: HashOrNumber{Number: 314}, Amount: 314, Skip: 1, Reverse: true}}, + {fail: false, packet: &GetBlockHeadersPacket{Origin: HashOrNumber{Hash: hash}, Amount: 314, Skip: 1, Reverse: true}}, + + // Providing both the origin hash and origin number must fail + {fail: true, packet: &GetBlockHeadersPacket{Origin: HashOrNumber{Hash: hash, Number: 314}}}, + } + // Iterate over each of the tests and try to encode and then decode + for i, tt := range tests { + bytes, err := rlp.EncodeToBytes(tt.packet) + if err != nil && !tt.fail { + t.Fatalf("test %d: failed to encode packet: %v", i, err) + } else if err == nil && tt.fail { + t.Fatalf("test %d: encode should have failed", i) + } + if !tt.fail { + packet := new(GetBlockHeadersPacket) + if err := rlp.DecodeBytes(bytes, packet); err != nil { + t.Fatalf("test %d: failed to decode packet: %v", i, err) + } + if packet.Origin.Hash != tt.packet.Origin.Hash || packet.Origin.Number != tt.packet.Origin.Number || packet.Amount != tt.packet.Amount || + packet.Skip != tt.packet.Skip || packet.Reverse != tt.packet.Reverse { + t.Fatalf("test %d: encode decode mismatch: have %+v, want %+v", i, packet, tt.packet) + } + } + } +} + +// TestEth66EmptyMessages tests encoding of empty eth66 messages +func TestEth66EmptyMessages(t *testing.T) { + // All empty messages encodes to the same format + want := common.FromHex("c4820457c0") + + for i, msg := range []interface{}{ + // Headers + GetBlockHeadersPacket66{1111, nil}, + BlockHeadersPacket66{1111, nil}, + // Bodies + GetBlockBodiesPacket66{1111, nil}, + BlockBodiesPacket66{1111, nil}, + BlockBodiesRLPPacket66{1111, nil}, + // Node data + GetNodeDataPacket66{1111, nil}, + NodeDataPacket66{1111, nil}, + // Receipts + GetReceiptsPacket66{1111, nil}, + ReceiptsPacket66{1111, nil}, + // Transactions + GetPooledTransactionsPacket66{1111, nil}, + PooledTransactionsPacket66{1111, nil}, + PooledTransactionsRLPPacket66{1111, nil}, + + // Headers + BlockHeadersPacket66{1111, BlockHeadersPacket([]*types.Header{})}, + // Bodies + GetBlockBodiesPacket66{1111, GetBlockBodiesPacket([]common.Hash{})}, + BlockBodiesPacket66{1111, BlockBodiesPacket([]*BlockBody{})}, + BlockBodiesRLPPacket66{1111, BlockBodiesRLPPacket([]rlp.RawValue{})}, + // Node data + GetNodeDataPacket66{1111, GetNodeDataPacket([]common.Hash{})}, + NodeDataPacket66{1111, NodeDataPacket([][]byte{})}, + // Receipts + GetReceiptsPacket66{1111, GetReceiptsPacket([]common.Hash{})}, + ReceiptsPacket66{1111, ReceiptsPacket([][]*types.Receipt{})}, + // Transactions + GetPooledTransactionsPacket66{1111, GetPooledTransactionsPacket([]common.Hash{})}, + PooledTransactionsPacket66{1111, PooledTransactionsPacket([]*types.Transaction{})}, + PooledTransactionsRLPPacket66{1111, PooledTransactionsRLPPacket([]rlp.RawValue{})}, + } { + if have, _ := rlp.EncodeToBytes(msg); !bytes.Equal(have, want) { + t.Errorf("test %d, type %T, have\n\t%x\nwant\n\t%x", i, msg, have, want) + } + } + +} + +// TestEth66Messages tests the encoding of all redefined eth66 messages +func TestEth66Messages(t *testing.T) { + + // Some basic structs used during testing + var ( + header *types.Header + blockBody *BlockBody + blockBodyRlp rlp.RawValue + txs []*types.Transaction + txRlps []rlp.RawValue + hashes []common.Hash + receipts []*types.Receipt + receiptsRlp rlp.RawValue + + err error + ) + header = &types.Header{ + Difficulty: big.NewInt(2222), + Number: big.NewInt(3333), + GasLimit: 4444, + GasUsed: 5555, + Time: 6666, + Extra: []byte{0x77, 0x88}, + } + // Init the transactions, taken from a different test + { + for _, hexrlp := range []string{ + "f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", + "f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", + } { + var tx *types.Transaction + rlpdata := common.FromHex(hexrlp) + if err := rlp.DecodeBytes(rlpdata, &tx); err != nil { + t.Fatal(err) + } + txs = append(txs, tx) + txRlps = append(txRlps, rlpdata) + } + } + // init the block body data, both object and rlp form + blockBody = &BlockBody{ + Transactions: txs, + Uncles: []*types.Header{header}, + } + blockBodyRlp, err = rlp.EncodeToBytes(blockBody) + if err != nil { + t.Fatal(err) + } + + hashes = []common.Hash{ + common.HexToHash("deadc0de"), + common.HexToHash("feedbeef"), + } + byteSlices := [][]byte{ + common.FromHex("deadc0de"), + common.FromHex("feedbeef"), + } + // init the receipts + { + receipts = []*types.Receipt{ + { + Status: types.ReceiptStatusFailed, + CumulativeGasUsed: 1, + Logs: []*types.Log{ + { + Address: common.BytesToAddress([]byte{0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + }, + TxHash: hashes[0], + ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), + GasUsed: 111111, + }, + } + rlpData, err := rlp.EncodeToBytes(receipts) + if err != nil { + t.Fatal(err) + } + receiptsRlp = rlpData + } + + for i, tc := range []struct { + message interface{} + want []byte + }{ + { + GetBlockHeadersPacket66{1111, &GetBlockHeadersPacket{HashOrNumber{hashes[0], 0}, 5, 5, false}}, + common.FromHex("e8820457e4a000000000000000000000000000000000000000000000000000000000deadc0de050580"), + }, + { + GetBlockHeadersPacket66{1111, &GetBlockHeadersPacket{HashOrNumber{common.Hash{}, 9999}, 5, 5, false}}, + common.FromHex("ca820457c682270f050580"), + }, + { + BlockHeadersPacket66{1111, BlockHeadersPacket{header}}, + common.FromHex("f90202820457f901fcf901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"), + }, + { + GetBlockBodiesPacket66{1111, GetBlockBodiesPacket(hashes)}, + common.FromHex("f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"), + }, + { + BlockBodiesPacket66{1111, BlockBodiesPacket([]*BlockBody{blockBody})}, + common.FromHex("f902dc820457f902d6f902d3f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afbf901fcf901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"), + }, + { // Identical to non-rlp-shortcut version + BlockBodiesRLPPacket66{1111, BlockBodiesRLPPacket([]rlp.RawValue{blockBodyRlp})}, + common.FromHex("f902dc820457f902d6f902d3f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afbf901fcf901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"), + }, + { + GetNodeDataPacket66{1111, GetNodeDataPacket(hashes)}, + common.FromHex("f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"), + }, + { + NodeDataPacket66{1111, NodeDataPacket(byteSlices)}, + common.FromHex("ce820457ca84deadc0de84feedbeef"), + }, + { + GetReceiptsPacket66{1111, GetReceiptsPacket(hashes)}, + common.FromHex("f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"), + }, + { + ReceiptsPacket66{1111, ReceiptsPacket([][]*types.Receipt{receipts})}, + common.FromHex("f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"), + }, + { + ReceiptsRLPPacket66{1111, ReceiptsRLPPacket([]rlp.RawValue{receiptsRlp})}, + common.FromHex("f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"), + }, + { + GetPooledTransactionsPacket66{1111, GetPooledTransactionsPacket(hashes)}, + common.FromHex("f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"), + }, + { + PooledTransactionsPacket66{1111, PooledTransactionsPacket(txs)}, + common.FromHex("f8d7820457f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb"), + }, + { + PooledTransactionsRLPPacket66{1111, PooledTransactionsRLPPacket(txRlps)}, + common.FromHex("f8d7820457f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb"), + }, + } { + if have, _ := rlp.EncodeToBytes(tc.message); !bytes.Equal(have, tc.want) { + t.Errorf("test %d, type %T, have\n\t%x\nwant\n\t%x", i, tc.message, have, tc.want) + } + } +} diff --git a/p2p/discv5/metrics.go b/eth/protocols/snap/discovery.go similarity index 66% rename from p2p/discv5/metrics.go rename to eth/protocols/snap/discovery.go index e68d53c13c..684ec7e632 100644 --- a/p2p/discv5/metrics.go +++ b/eth/protocols/snap/discovery.go @@ -1,4 +1,4 @@ -// Copyright 2018 The go-ethereum Authors +// Copyright 2020 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,11 +14,19 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package discv5 +package snap -import "github.com/ethereum/go-ethereum/metrics" - -var ( - ingressTrafficMeter = metrics.NewRegisteredMeter("discv5/InboundTraffic", nil) - egressTrafficMeter = metrics.NewRegisteredMeter("discv5/OutboundTraffic", nil) +import ( + "github.com/ethereum/go-ethereum/rlp" ) + +// enrEntry is the ENR entry which advertises `snap` protocol on the discovery. +type enrEntry struct { + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` +} + +// ENRKey implements enr.Entry. +func (e enrEntry) ENRKey() string { + return "snap" +} diff --git a/eth/protocols/snap/handler.go b/eth/protocols/snap/handler.go new file mode 100644 index 0000000000..f7939964f3 --- /dev/null +++ b/eth/protocols/snap/handler.go @@ -0,0 +1,514 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package snap + +import ( + "bytes" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +const ( + // softResponseLimit is the target maximum size of replies to data retrievals. + softResponseLimit = 2 * 1024 * 1024 + + // maxCodeLookups is the maximum number of bytecodes to serve. This number is + // there to limit the number of disk lookups. + maxCodeLookups = 1024 + + // stateLookupSlack defines the ratio by how much a state response can exceed + // the requested limit in order to try and avoid breaking up contracts into + // multiple packages and proving them. + stateLookupSlack = 0.1 + + // maxTrieNodeLookups is the maximum number of state trie nodes to serve. This + // number is there to limit the number of disk lookups. + maxTrieNodeLookups = 1024 + + // maxTrieNodeTimeSpent is the maximum time we should spend on looking up trie nodes. + // If we spend too much time, then it's a fairly high chance of timing out + // at the remote side, which means all the work is in vain. + maxTrieNodeTimeSpent = 5 * time.Second +) + +// Handler is a callback to invoke from an outside runner after the boilerplate +// exchanges have passed. +type Handler func(peer *Peer) error + +// Backend defines the data retrieval methods to serve remote requests and the +// callback methods to invoke on remote deliveries. +type Backend interface { + // Chain retrieves the blockchain object to serve data. + Chain() *core.BlockChain + + // RunPeer is invoked when a peer joins on the `eth` protocol. The handler + // should do any peer maintenance work, handshakes and validations. If all + // is passed, control should be given back to the `handler` to process the + // inbound messages going forward. + RunPeer(peer *Peer, handler Handler) error + + // PeerInfo retrieves all known `snap` information about a peer. + PeerInfo(id enode.ID) interface{} + + // Handle is a callback to be invoked when a data packet is received from + // the remote peer. Only packets not consumed by the protocol handler will + // be forwarded to the backend. + Handle(peer *Peer, packet Packet) error +} + +// MakeProtocols constructs the P2P protocol definitions for `snap`. +func MakeProtocols(backend Backend, dnsdisc enode.Iterator) []p2p.Protocol { + protocols := make([]p2p.Protocol, len(ProtocolVersions)) + for i, version := range ProtocolVersions { + version := version // Closure + + protocols[i] = p2p.Protocol{ + Name: ProtocolName, + Version: version, + Length: protocolLengths[version], + Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { + return backend.RunPeer(newPeer(version, p, rw), func(peer *Peer) error { + return handle(backend, peer) + }) + }, + NodeInfo: func() interface{} { + return nodeInfo(backend.Chain()) + }, + PeerInfo: func(id enode.ID) interface{} { + return backend.PeerInfo(id) + }, + Attributes: []enr.Entry{&enrEntry{}}, + DialCandidates: dnsdisc, + } + } + return protocols +} + +// handle is the callback invoked to manage the life cycle of a `snap` peer. +// When this function terminates, the peer is disconnected. +func handle(backend Backend, peer *Peer) error { + for { + if err := handleMessage(backend, peer); err != nil { + peer.Log().Debug("Message handling failed in `snap`", "err", err) + return err + } + } +} + +// handleMessage is invoked whenever an inbound message is received from a +// remote peer on the `snap` protocol. The remote connection is torn down upon +// returning any error. +func handleMessage(backend Backend, peer *Peer) error { + // Read the next message from the remote peer, and ensure it's fully consumed + msg, err := peer.rw.ReadMsg() + if err != nil { + return err + } + if msg.Size > maxMessageSize { + return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize) + } + defer msg.Discard() + start := time.Now() + // Track the emount of time it takes to serve the request and run the handler + if metrics.Enabled { + h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code) + defer func(start time.Time) { + sampler := func() metrics.Sample { + return metrics.ResettingSample( + metrics.NewExpDecaySample(1028, 0.015), + ) + } + metrics.GetOrRegisterHistogramLazy(h, nil, sampler).Update(time.Since(start).Microseconds()) + }(start) + } + // Handle the message depending on its contents + switch { + case msg.Code == GetAccountRangeMsg: + // Decode the account retrieval request + var req GetAccountRangePacket + if err := msg.Decode(&req); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + if req.Bytes > softResponseLimit { + req.Bytes = softResponseLimit + } + // Retrieve the requested state and bail out if non existent + tr, err := trie.New(req.Root, backend.Chain().StateCache().TrieDB()) + if err != nil { + return p2p.Send(peer.rw, AccountRangeMsg, &AccountRangePacket{ID: req.ID}) + } + it, err := backend.Chain().Snapshots().AccountIterator(req.Root, req.Origin) + if err != nil { + return p2p.Send(peer.rw, AccountRangeMsg, &AccountRangePacket{ID: req.ID}) + } + // Iterate over the requested range and pile accounts up + var ( + accounts []*AccountData + size uint64 + last common.Hash + ) + for it.Next() && size < req.Bytes { + hash, account := it.Hash(), common.CopyBytes(it.Account()) + + // Track the returned interval for the Merkle proofs + last = hash + + // Assemble the reply item + size += uint64(common.HashLength + len(account)) + accounts = append(accounts, &AccountData{ + Hash: hash, + Body: account, + }) + // If we've exceeded the request threshold, abort + if bytes.Compare(hash[:], req.Limit[:]) >= 0 { + break + } + } + it.Release() + + // Generate the Merkle proofs for the first and last account + proof := light.NewNodeSet() + if err := tr.Prove(req.Origin[:], 0, proof); err != nil { + log.Warn("Failed to prove account range", "origin", req.Origin, "err", err) + return p2p.Send(peer.rw, AccountRangeMsg, &AccountRangePacket{ID: req.ID}) + } + if last != (common.Hash{}) { + if err := tr.Prove(last[:], 0, proof); err != nil { + log.Warn("Failed to prove account range", "last", last, "err", err) + return p2p.Send(peer.rw, AccountRangeMsg, &AccountRangePacket{ID: req.ID}) + } + } + var proofs [][]byte + for _, blob := range proof.NodeList() { + proofs = append(proofs, blob) + } + // Send back anything accumulated + return p2p.Send(peer.rw, AccountRangeMsg, &AccountRangePacket{ + ID: req.ID, + Accounts: accounts, + Proof: proofs, + }) + + case msg.Code == AccountRangeMsg: + // A range of accounts arrived to one of our previous requests + res := new(AccountRangePacket) + if err := msg.Decode(res); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + // Ensure the range is monotonically increasing + for i := 1; i < len(res.Accounts); i++ { + if bytes.Compare(res.Accounts[i-1].Hash[:], res.Accounts[i].Hash[:]) >= 0 { + return fmt.Errorf("accounts not monotonically increasing: #%d [%x] vs #%d [%x]", i-1, res.Accounts[i-1].Hash[:], i, res.Accounts[i].Hash[:]) + } + } + return backend.Handle(peer, res) + + case msg.Code == GetStorageRangesMsg: + // Decode the storage retrieval request + var req GetStorageRangesPacket + if err := msg.Decode(&req); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + if req.Bytes > softResponseLimit { + req.Bytes = softResponseLimit + } + // TODO(karalabe): Do we want to enforce > 0 accounts and 1 account if origin is set? + // TODO(karalabe): - Logging locally is not ideal as remote faulst annoy the local user + // TODO(karalabe): - Dropping the remote peer is less flexible wrt client bugs (slow is better than non-functional) + + // Calculate the hard limit at which to abort, even if mid storage trie + hardLimit := uint64(float64(req.Bytes) * (1 + stateLookupSlack)) + + // Retrieve storage ranges until the packet limit is reached + var ( + slots [][]*StorageData + proofs [][]byte + size uint64 + ) + for _, account := range req.Accounts { + // If we've exceeded the requested data limit, abort without opening + // a new storage range (that we'd need to prove due to exceeded size) + if size >= req.Bytes { + break + } + // The first account might start from a different origin and end sooner + var origin common.Hash + if len(req.Origin) > 0 { + origin, req.Origin = common.BytesToHash(req.Origin), nil + } + var limit = common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + if len(req.Limit) > 0 { + limit, req.Limit = common.BytesToHash(req.Limit), nil + } + // Retrieve the requested state and bail out if non existent + it, err := backend.Chain().Snapshots().StorageIterator(req.Root, account, origin) + if err != nil { + return p2p.Send(peer.rw, StorageRangesMsg, &StorageRangesPacket{ID: req.ID}) + } + // Iterate over the requested range and pile slots up + var ( + storage []*StorageData + last common.Hash + abort bool + ) + for it.Next() { + if size >= hardLimit { + abort = true + break + } + hash, slot := it.Hash(), common.CopyBytes(it.Slot()) + + // Track the returned interval for the Merkle proofs + last = hash + + // Assemble the reply item + size += uint64(common.HashLength + len(slot)) + storage = append(storage, &StorageData{ + Hash: hash, + Body: slot, + }) + // If we've exceeded the request threshold, abort + if bytes.Compare(hash[:], limit[:]) >= 0 { + break + } + } + slots = append(slots, storage) + it.Release() + + // Generate the Merkle proofs for the first and last storage slot, but + // only if the response was capped. If the entire storage trie included + // in the response, no need for any proofs. + if origin != (common.Hash{}) || abort { + // Request started at a non-zero hash or was capped prematurely, add + // the endpoint Merkle proofs + accTrie, err := trie.New(req.Root, backend.Chain().StateCache().TrieDB()) + if err != nil { + return p2p.Send(peer.rw, StorageRangesMsg, &StorageRangesPacket{ID: req.ID}) + } + var acc state.Account + if err := rlp.DecodeBytes(accTrie.Get(account[:]), &acc); err != nil { + return p2p.Send(peer.rw, StorageRangesMsg, &StorageRangesPacket{ID: req.ID}) + } + stTrie, err := trie.New(acc.Root, backend.Chain().StateCache().TrieDB()) + if err != nil { + return p2p.Send(peer.rw, StorageRangesMsg, &StorageRangesPacket{ID: req.ID}) + } + proof := light.NewNodeSet() + if err := stTrie.Prove(origin[:], 0, proof); err != nil { + log.Warn("Failed to prove storage range", "origin", req.Origin, "err", err) + return p2p.Send(peer.rw, StorageRangesMsg, &StorageRangesPacket{ID: req.ID}) + } + if last != (common.Hash{}) { + if err := stTrie.Prove(last[:], 0, proof); err != nil { + log.Warn("Failed to prove storage range", "last", last, "err", err) + return p2p.Send(peer.rw, StorageRangesMsg, &StorageRangesPacket{ID: req.ID}) + } + } + for _, blob := range proof.NodeList() { + proofs = append(proofs, blob) + } + // Proof terminates the reply as proofs are only added if a node + // refuses to serve more data (exception when a contract fetch is + // finishing, but that's that). + break + } + } + // Send back anything accumulated + return p2p.Send(peer.rw, StorageRangesMsg, &StorageRangesPacket{ + ID: req.ID, + Slots: slots, + Proof: proofs, + }) + + case msg.Code == StorageRangesMsg: + // A range of storage slots arrived to one of our previous requests + res := new(StorageRangesPacket) + if err := msg.Decode(res); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + // Ensure the ranges ae monotonically increasing + for i, slots := range res.Slots { + for j := 1; j < len(slots); j++ { + if bytes.Compare(slots[j-1].Hash[:], slots[j].Hash[:]) >= 0 { + return fmt.Errorf("storage slots not monotonically increasing for account #%d: #%d [%x] vs #%d [%x]", i, j-1, slots[j-1].Hash[:], j, slots[j].Hash[:]) + } + } + } + return backend.Handle(peer, res) + + case msg.Code == GetByteCodesMsg: + // Decode bytecode retrieval request + var req GetByteCodesPacket + if err := msg.Decode(&req); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + if req.Bytes > softResponseLimit { + req.Bytes = softResponseLimit + } + if len(req.Hashes) > maxCodeLookups { + req.Hashes = req.Hashes[:maxCodeLookups] + } + // Retrieve bytecodes until the packet size limit is reached + var ( + codes [][]byte + bytes uint64 + ) + for _, hash := range req.Hashes { + if hash == emptyCode { + // Peers should not request the empty code, but if they do, at + // least sent them back a correct response without db lookups + codes = append(codes, []byte{}) + } else if blob, err := backend.Chain().ContractCode(hash); err == nil { + codes = append(codes, blob) + bytes += uint64(len(blob)) + } + if bytes > req.Bytes { + break + } + } + // Send back anything accumulated + return p2p.Send(peer.rw, ByteCodesMsg, &ByteCodesPacket{ + ID: req.ID, + Codes: codes, + }) + + case msg.Code == ByteCodesMsg: + // A batch of byte codes arrived to one of our previous requests + res := new(ByteCodesPacket) + if err := msg.Decode(res); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + return backend.Handle(peer, res) + + case msg.Code == GetTrieNodesMsg: + // Decode trie node retrieval request + var req GetTrieNodesPacket + if err := msg.Decode(&req); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + if req.Bytes > softResponseLimit { + req.Bytes = softResponseLimit + } + // Make sure we have the state associated with the request + triedb := backend.Chain().StateCache().TrieDB() + + accTrie, err := trie.NewSecure(req.Root, triedb) + if err != nil { + // We don't have the requested state available, bail out + return p2p.Send(peer.rw, TrieNodesMsg, &TrieNodesPacket{ID: req.ID}) + } + snap := backend.Chain().Snapshots().Snapshot(req.Root) + if snap == nil { + // We don't have the requested state snapshotted yet, bail out. + // In reality we could still serve using the account and storage + // tries only, but let's protect the node a bit while it's doing + // snapshot generation. + return p2p.Send(peer.rw, TrieNodesMsg, &TrieNodesPacket{ID: req.ID}) + } + // Retrieve trie nodes until the packet size limit is reached + var ( + nodes [][]byte + bytes uint64 + loads int // Trie hash expansions to cound database reads + ) + for _, pathset := range req.Paths { + switch len(pathset) { + case 0: + // Ensure we penalize invalid requests + return fmt.Errorf("%w: zero-item pathset requested", errBadRequest) + + case 1: + // If we're only retrieving an account trie node, fetch it directly + blob, resolved, err := accTrie.TryGetNode(pathset[0]) + loads += resolved // always account database reads, even for failures + if err != nil { + break + } + nodes = append(nodes, blob) + bytes += uint64(len(blob)) + + default: + // Storage slots requested, open the storage trie and retrieve from there + account, err := snap.Account(common.BytesToHash(pathset[0])) + loads++ // always account database reads, even for failures + if err != nil { + break + } + stTrie, err := trie.NewSecure(common.BytesToHash(account.Root), triedb) + loads++ // always account database reads, even for failures + if err != nil { + break + } + for _, path := range pathset[1:] { + blob, resolved, err := stTrie.TryGetNode(path) + loads += resolved // always account database reads, even for failures + if err != nil { + break + } + nodes = append(nodes, blob) + bytes += uint64(len(blob)) + + // Sanity check limits to avoid DoS on the store trie loads + if bytes > req.Bytes || loads > maxTrieNodeLookups || time.Since(start) > maxTrieNodeTimeSpent { + break + } + } + } + // Abort request processing if we've exceeded our limits + if bytes > req.Bytes || loads > maxTrieNodeLookups || time.Since(start) > maxTrieNodeTimeSpent { + break + } + } + // Send back anything accumulated + return p2p.Send(peer.rw, TrieNodesMsg, &TrieNodesPacket{ + ID: req.ID, + Nodes: nodes, + }) + + case msg.Code == TrieNodesMsg: + // A batch of trie nodes arrived to one of our previous requests + res := new(TrieNodesPacket) + if err := msg.Decode(res); err != nil { + return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + } + return backend.Handle(peer, res) + + default: + return fmt.Errorf("%w: %v", errInvalidMsgCode, msg.Code) + } +} + +// NodeInfo represents a short summary of the `snap` sub-protocol metadata +// known about the host peer. +type NodeInfo struct{} + +// nodeInfo retrieves some `snap` protocol metadata about the running host node. +func nodeInfo(chain *core.BlockChain) *NodeInfo { + return &NodeInfo{} +} diff --git a/eth/protocols/snap/peer.go b/eth/protocols/snap/peer.go new file mode 100644 index 0000000000..4f3d550f1f --- /dev/null +++ b/eth/protocols/snap/peer.go @@ -0,0 +1,116 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package snap + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p" +) + +// Peer is a collection of relevant information we have about a `snap` peer. +type Peer struct { + id string // Unique ID for the peer, cached + + *p2p.Peer // The embedded P2P package peer + rw p2p.MsgReadWriter // Input/output streams for snap + version uint // Protocol version negotiated + + logger log.Logger // Contextual logger with the peer id injected +} + +// newPeer create a wrapper for a network connection and negotiated protocol +// version. +func newPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter) *Peer { + id := p.ID().String() + return &Peer{ + id: id, + Peer: p, + rw: rw, + version: version, + logger: log.New("peer", id[:8]), + } +} + +// ID retrieves the peer's unique identifier. +func (p *Peer) ID() string { + return p.id +} + +// Version retrieves the peer's negoatiated `snap` protocol version. +func (p *Peer) Version() uint { + return p.version +} + +// Log overrides the P2P logget with the higher level one containing only the id. +func (p *Peer) Log() log.Logger { + return p.logger +} + +// RequestAccountRange fetches a batch of accounts rooted in a specific account +// trie, starting with the origin. +func (p *Peer) RequestAccountRange(id uint64, root common.Hash, origin, limit common.Hash, bytes uint64) error { + p.logger.Trace("Fetching range of accounts", "reqid", id, "root", root, "origin", origin, "limit", limit, "bytes", common.StorageSize(bytes)) + return p2p.Send(p.rw, GetAccountRangeMsg, &GetAccountRangePacket{ + ID: id, + Root: root, + Origin: origin, + Limit: limit, + Bytes: bytes, + }) +} + +// RequestStorageRange fetches a batch of storage slots belonging to one or more +// accounts. If slots from only one accout is requested, an origin marker may also +// be used to retrieve from there. +func (p *Peer) RequestStorageRanges(id uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, bytes uint64) error { + if len(accounts) == 1 && origin != nil { + p.logger.Trace("Fetching range of large storage slots", "reqid", id, "root", root, "account", accounts[0], "origin", common.BytesToHash(origin), "limit", common.BytesToHash(limit), "bytes", common.StorageSize(bytes)) + } else { + p.logger.Trace("Fetching ranges of small storage slots", "reqid", id, "root", root, "accounts", len(accounts), "first", accounts[0], "bytes", common.StorageSize(bytes)) + } + return p2p.Send(p.rw, GetStorageRangesMsg, &GetStorageRangesPacket{ + ID: id, + Root: root, + Accounts: accounts, + Origin: origin, + Limit: limit, + Bytes: bytes, + }) +} + +// RequestByteCodes fetches a batch of bytecodes by hash. +func (p *Peer) RequestByteCodes(id uint64, hashes []common.Hash, bytes uint64) error { + p.logger.Trace("Fetching set of byte codes", "reqid", id, "hashes", len(hashes), "bytes", common.StorageSize(bytes)) + return p2p.Send(p.rw, GetByteCodesMsg, &GetByteCodesPacket{ + ID: id, + Hashes: hashes, + Bytes: bytes, + }) +} + +// RequestTrieNodes fetches a batch of account or storage trie nodes rooted in +// a specificstate trie. +func (p *Peer) RequestTrieNodes(id uint64, root common.Hash, paths []TrieNodePathSet, bytes uint64) error { + p.logger.Trace("Fetching set of trie nodes", "reqid", id, "root", root, "pathsets", len(paths), "bytes", common.StorageSize(bytes)) + return p2p.Send(p.rw, GetTrieNodesMsg, &GetTrieNodesPacket{ + ID: id, + Root: root, + Paths: paths, + Bytes: bytes, + }) +} diff --git a/eth/protocols/snap/protocol.go b/eth/protocols/snap/protocol.go new file mode 100644 index 0000000000..5528e9212e --- /dev/null +++ b/eth/protocols/snap/protocol.go @@ -0,0 +1,218 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package snap + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/rlp" +) + +// Constants to match up protocol versions and messages +const ( + snap1 = 1 +) + +// ProtocolName is the official short name of the `snap` protocol used during +// devp2p capability negotiation. +const ProtocolName = "snap" + +// ProtocolVersions are the supported versions of the `snap` protocol (first +// is primary). +var ProtocolVersions = []uint{snap1} + +// protocolLengths are the number of implemented message corresponding to +// different protocol versions. +var protocolLengths = map[uint]uint64{snap1: 8} + +// maxMessageSize is the maximum cap on the size of a protocol message. +const maxMessageSize = 10 * 1024 * 1024 + +const ( + GetAccountRangeMsg = 0x00 + AccountRangeMsg = 0x01 + GetStorageRangesMsg = 0x02 + StorageRangesMsg = 0x03 + GetByteCodesMsg = 0x04 + ByteCodesMsg = 0x05 + GetTrieNodesMsg = 0x06 + TrieNodesMsg = 0x07 +) + +var ( + errMsgTooLarge = errors.New("message too long") + errDecode = errors.New("invalid message") + errInvalidMsgCode = errors.New("invalid message code") + errBadRequest = errors.New("bad request") +) + +// Packet represents a p2p message in the `snap` protocol. +type Packet interface { + Name() string // Name returns a string corresponding to the message type. + Kind() byte // Kind returns the message type. +} + +// GetAccountRangePacket represents an account query. +type GetAccountRangePacket struct { + ID uint64 // Request ID to match up responses with + Root common.Hash // Root hash of the account trie to serve + Origin common.Hash // Hash of the first account to retrieve + Limit common.Hash // Hash of the last account to retrieve + Bytes uint64 // Soft limit at which to stop returning data +} + +// AccountRangePacket represents an account query response. +type AccountRangePacket struct { + ID uint64 // ID of the request this is a response for + Accounts []*AccountData // List of consecutive accounts from the trie + Proof [][]byte // List of trie nodes proving the account range +} + +// AccountData represents a single account in a query response. +type AccountData struct { + Hash common.Hash // Hash of the account + Body rlp.RawValue // Account body in slim format +} + +// Unpack retrieves the accounts from the range packet and converts from slim +// wire representation to consensus format. The returned data is RLP encoded +// since it's expected to be serialized to disk without further interpretation. +// +// Note, this method does a round of RLP decoding and reencoding, so only use it +// once and cache the results if need be. Ideally discard the packet afterwards +// to not double the memory use. +func (p *AccountRangePacket) Unpack() ([]common.Hash, [][]byte, error) { + var ( + hashes = make([]common.Hash, len(p.Accounts)) + accounts = make([][]byte, len(p.Accounts)) + ) + for i, acc := range p.Accounts { + val, err := snapshot.FullAccountRLP(acc.Body) + if err != nil { + return nil, nil, fmt.Errorf("invalid account %x: %v", acc.Body, err) + } + hashes[i], accounts[i] = acc.Hash, val + } + return hashes, accounts, nil +} + +// GetStorageRangesPacket represents an storage slot query. +type GetStorageRangesPacket struct { + ID uint64 // Request ID to match up responses with + Root common.Hash // Root hash of the account trie to serve + Accounts []common.Hash // Account hashes of the storage tries to serve + Origin []byte // Hash of the first storage slot to retrieve (large contract mode) + Limit []byte // Hash of the last storage slot to retrieve (large contract mode) + Bytes uint64 // Soft limit at which to stop returning data +} + +// StorageRangesPacket represents a storage slot query response. +type StorageRangesPacket struct { + ID uint64 // ID of the request this is a response for + Slots [][]*StorageData // Lists of consecutive storage slots for the requested accounts + Proof [][]byte // Merkle proofs for the *last* slot range, if it's incomplete +} + +// StorageData represents a single storage slot in a query response. +type StorageData struct { + Hash common.Hash // Hash of the storage slot + Body []byte // Data content of the slot +} + +// Unpack retrieves the storage slots from the range packet and returns them in +// a split flat format that's more consistent with the internal data structures. +func (p *StorageRangesPacket) Unpack() ([][]common.Hash, [][][]byte) { + var ( + hashset = make([][]common.Hash, len(p.Slots)) + slotset = make([][][]byte, len(p.Slots)) + ) + for i, slots := range p.Slots { + hashset[i] = make([]common.Hash, len(slots)) + slotset[i] = make([][]byte, len(slots)) + for j, slot := range slots { + hashset[i][j] = slot.Hash + slotset[i][j] = slot.Body + } + } + return hashset, slotset +} + +// GetByteCodesPacket represents a contract bytecode query. +type GetByteCodesPacket struct { + ID uint64 // Request ID to match up responses with + Hashes []common.Hash // Code hashes to retrieve the code for + Bytes uint64 // Soft limit at which to stop returning data +} + +// ByteCodesPacket represents a contract bytecode query response. +type ByteCodesPacket struct { + ID uint64 // ID of the request this is a response for + Codes [][]byte // Requested contract bytecodes +} + +// GetTrieNodesPacket represents a state trie node query. +type GetTrieNodesPacket struct { + ID uint64 // Request ID to match up responses with + Root common.Hash // Root hash of the account trie to serve + Paths []TrieNodePathSet // Trie node hashes to retrieve the nodes for + Bytes uint64 // Soft limit at which to stop returning data +} + +// TrieNodePathSet is a list of trie node paths to retrieve. A naive way to +// represent trie nodes would be a simple list of `account || storage` path +// segments concatenated, but that would be very wasteful on the network. +// +// Instead, this array special cases the first element as the path in the +// account trie and the remaining elements as paths in the storage trie. To +// address an account node, the slice should have a length of 1 consisting +// of only the account path. There's no need to be able to address both an +// account node and a storage node in the same request as it cannot happen +// that a slot is accessed before the account path is fully expanded. +type TrieNodePathSet [][]byte + +// TrieNodesPacket represents a state trie node query response. +type TrieNodesPacket struct { + ID uint64 // ID of the request this is a response for + Nodes [][]byte // Requested state trie nodes +} + +func (*GetAccountRangePacket) Name() string { return "GetAccountRange" } +func (*GetAccountRangePacket) Kind() byte { return GetAccountRangeMsg } + +func (*AccountRangePacket) Name() string { return "AccountRange" } +func (*AccountRangePacket) Kind() byte { return AccountRangeMsg } + +func (*GetStorageRangesPacket) Name() string { return "GetStorageRanges" } +func (*GetStorageRangesPacket) Kind() byte { return GetStorageRangesMsg } + +func (*StorageRangesPacket) Name() string { return "StorageRanges" } +func (*StorageRangesPacket) Kind() byte { return StorageRangesMsg } + +func (*GetByteCodesPacket) Name() string { return "GetByteCodes" } +func (*GetByteCodesPacket) Kind() byte { return GetByteCodesMsg } + +func (*ByteCodesPacket) Name() string { return "ByteCodes" } +func (*ByteCodesPacket) Kind() byte { return ByteCodesMsg } + +func (*GetTrieNodesPacket) Name() string { return "GetTrieNodes" } +func (*GetTrieNodesPacket) Kind() byte { return GetTrieNodesMsg } + +func (*TrieNodesPacket) Name() string { return "TrieNodes" } +func (*TrieNodesPacket) Kind() byte { return TrieNodesMsg } diff --git a/eth/protocols/snap/sync.go b/eth/protocols/snap/sync.go new file mode 100644 index 0000000000..2924fa0802 --- /dev/null +++ b/eth/protocols/snap/sync.go @@ -0,0 +1,2638 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package snap + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "math/big" + "math/rand" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" + "golang.org/x/crypto/sha3" +) + +var ( + // emptyRoot is the known root hash of an empty trie. + emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + + // emptyCode is the known hash of the empty EVM bytecode. + emptyCode = crypto.Keccak256Hash(nil) +) + +const ( + // maxRequestSize is the maximum number of bytes to request from a remote peer. + maxRequestSize = 512 * 1024 + + // maxStorageSetRequestCountis th maximum number of contracts to request the + // storage of in a single query. If this number is too low, we're not filling + // responses fully and waste round trip times. If it's too high, we're capping + // responses and waste bandwidth. + maxStorageSetRequestCount = maxRequestSize / 1024 + + // maxCodeRequestCount is the maximum number of bytecode blobs to request in a + // single query. If this number is too low, we're not filling responses fully + // and waste round trip times. If it's too high, we're capping responses and + // waste bandwidth. + // + // Depoyed bytecodes are currently capped at 24KB, so the minimum request + // size should be maxRequestSize / 24K. Assuming that most contracts do not + // come close to that, requesting 4x should be a good approximation. + maxCodeRequestCount = maxRequestSize / (24 * 1024) * 4 + + // maxTrieRequestCount is the maximum number of trie node blobs to request in + // a single query. If this number is too low, we're not filling responses fully + // and waste round trip times. If it's too high, we're capping responses and + // waste bandwidth. + maxTrieRequestCount = 512 + + // accountConcurrency is the number of chunks to split the account trie into + // to allow concurrent retrievals. + accountConcurrency = 16 + + // storageConcurrency is the number of chunks to split the a large contract + // storage trie into to allow concurrent retrievals. + storageConcurrency = 16 +) + +var ( + // requestTimeout is the maximum time a peer is allowed to spend on serving + // a single network request. + requestTimeout = 15 * time.Second // TODO(karalabe): Make it dynamic ala fast-sync? +) + +// ErrCancelled is returned from snap syncing if the operation was prematurely +// terminated. +var ErrCancelled = errors.New("sync cancelled") + +// accountRequest tracks a pending account range request to ensure responses are +// to actual requests and to validate any security constraints. +// +// Concurrency note: account requests and responses are handled concurrently from +// the main runloop to allow Merkle proof verifications on the peer's thread and +// to drop on invalid response. The request struct must contain all the data to +// construct the response without accessing runloop internals (i.e. task). That +// is only included to allow the runloop to match a response to the task being +// synced without having yet another set of maps. +type accountRequest struct { + peer string // Peer to which this request is assigned + id uint64 // Request ID of this request + + cancel chan struct{} // Channel to track sync cancellation + timeout *time.Timer // Timer to track delivery timeout + stale chan struct{} // Channel to signal the request was dropped + + origin common.Hash // First account requested to allow continuation checks + limit common.Hash // Last account requested to allow non-overlapping chunking + + task *accountTask // Task which this request is filling (only access fields through the runloop!!) +} + +// accountResponse is an already Merkle-verified remote response to an account +// range request. It contains the subtrie for the requested account range and +// the database that's going to be filled with the internal nodes on commit. +type accountResponse struct { + task *accountTask // Task which this request is filling + + hashes []common.Hash // Account hashes in the returned range + accounts []*state.Account // Expanded accounts in the returned range + + nodes ethdb.KeyValueStore // Database containing the reconstructed trie nodes + trie *trie.Trie // Reconstructed trie to reject incomplete account paths + + bounds map[common.Hash]struct{} // Boundary nodes to avoid persisting incomplete accounts + overflow *light.NodeSet // Overflow nodes to avoid persisting across chunk boundaries + + cont bool // Whether the account range has a continuation +} + +// bytecodeRequest tracks a pending bytecode request to ensure responses are to +// actual requests and to validate any security constraints. +// +// Concurrency note: bytecode requests and responses are handled concurrently from +// the main runloop to allow Keccak256 hash verifications on the peer's thread and +// to drop on invalid response. The request struct must contain all the data to +// construct the response without accessing runloop internals (i.e. task). That +// is only included to allow the runloop to match a response to the task being +// synced without having yet another set of maps. +type bytecodeRequest struct { + peer string // Peer to which this request is assigned + id uint64 // Request ID of this request + + cancel chan struct{} // Channel to track sync cancellation + timeout *time.Timer // Timer to track delivery timeout + stale chan struct{} // Channel to signal the request was dropped + + hashes []common.Hash // Bytecode hashes to validate responses + task *accountTask // Task which this request is filling (only access fields through the runloop!!) +} + +// bytecodeResponse is an already verified remote response to a bytecode request. +type bytecodeResponse struct { + task *accountTask // Task which this request is filling + + hashes []common.Hash // Hashes of the bytecode to avoid double hashing + codes [][]byte // Actual bytecodes to store into the database (nil = missing) +} + +// storageRequest tracks a pending storage ranges request to ensure responses are +// to actual requests and to validate any security constraints. +// +// Concurrency note: storage requests and responses are handled concurrently from +// the main runloop to allow Merkel proof verifications on the peer's thread and +// to drop on invalid response. The request struct must contain all the data to +// construct the response without accessing runloop internals (i.e. tasks). That +// is only included to allow the runloop to match a response to the task being +// synced without having yet another set of maps. +type storageRequest struct { + peer string // Peer to which this request is assigned + id uint64 // Request ID of this request + + cancel chan struct{} // Channel to track sync cancellation + timeout *time.Timer // Timer to track delivery timeout + stale chan struct{} // Channel to signal the request was dropped + + accounts []common.Hash // Account hashes to validate responses + roots []common.Hash // Storage roots to validate responses + + origin common.Hash // First storage slot requested to allow continuation checks + limit common.Hash // Last storage slot requested to allow non-overlapping chunking + + mainTask *accountTask // Task which this response belongs to (only access fields through the runloop!!) + subTask *storageTask // Task which this response is filling (only access fields through the runloop!!) +} + +// storageResponse is an already Merkle-verified remote response to a storage +// range request. It contains the subtries for the requested storage ranges and +// the databases that's going to be filled with the internal nodes on commit. +type storageResponse struct { + mainTask *accountTask // Task which this response belongs to + subTask *storageTask // Task which this response is filling + + accounts []common.Hash // Account hashes requested, may be only partially filled + roots []common.Hash // Storage roots requested, may be only partially filled + + hashes [][]common.Hash // Storage slot hashes in the returned range + slots [][][]byte // Storage slot values in the returned range + nodes []ethdb.KeyValueStore // Database containing the reconstructed trie nodes + tries []*trie.Trie // Reconstructed tries to reject overflown slots + + // Fields relevant for the last account only + bounds map[common.Hash]struct{} // Boundary nodes to avoid persisting (incomplete) + overflow *light.NodeSet // Overflow nodes to avoid persisting across chunk boundaries + cont bool // Whether the last storage range has a continuation +} + +// trienodeHealRequest tracks a pending state trie request to ensure responses +// are to actual requests and to validate any security constraints. +// +// Concurrency note: trie node requests and responses are handled concurrently from +// the main runloop to allow Keccak256 hash verifications on the peer's thread and +// to drop on invalid response. The request struct must contain all the data to +// construct the response without accessing runloop internals (i.e. task). That +// is only included to allow the runloop to match a response to the task being +// synced without having yet another set of maps. +type trienodeHealRequest struct { + peer string // Peer to which this request is assigned + id uint64 // Request ID of this request + + cancel chan struct{} // Channel to track sync cancellation + timeout *time.Timer // Timer to track delivery timeout + stale chan struct{} // Channel to signal the request was dropped + + hashes []common.Hash // Trie node hashes to validate responses + paths []trie.SyncPath // Trie node paths requested for rescheduling + + task *healTask // Task which this request is filling (only access fields through the runloop!!) +} + +// trienodeHealResponse is an already verified remote response to a trie node request. +type trienodeHealResponse struct { + task *healTask // Task which this request is filling + + hashes []common.Hash // Hashes of the trie nodes to avoid double hashing + paths []trie.SyncPath // Trie node paths requested for rescheduling missing ones + nodes [][]byte // Actual trie nodes to store into the database (nil = missing) +} + +// bytecodeHealRequest tracks a pending bytecode request to ensure responses are to +// actual requests and to validate any security constraints. +// +// Concurrency note: bytecode requests and responses are handled concurrently from +// the main runloop to allow Keccak256 hash verifications on the peer's thread and +// to drop on invalid response. The request struct must contain all the data to +// construct the response without accessing runloop internals (i.e. task). That +// is only included to allow the runloop to match a response to the task being +// synced without having yet another set of maps. +type bytecodeHealRequest struct { + peer string // Peer to which this request is assigned + id uint64 // Request ID of this request + + cancel chan struct{} // Channel to track sync cancellation + timeout *time.Timer // Timer to track delivery timeout + stale chan struct{} // Channel to signal the request was dropped + + hashes []common.Hash // Bytecode hashes to validate responses + task *healTask // Task which this request is filling (only access fields through the runloop!!) +} + +// bytecodeHealResponse is an already verified remote response to a bytecode request. +type bytecodeHealResponse struct { + task *healTask // Task which this request is filling + + hashes []common.Hash // Hashes of the bytecode to avoid double hashing + codes [][]byte // Actual bytecodes to store into the database (nil = missing) +} + +// accountTask represents the sync task for a chunk of the account snapshot. +type accountTask struct { + // These fields get serialized to leveldb on shutdown + Next common.Hash // Next account to sync in this interval + Last common.Hash // Last account to sync in this interval + SubTasks map[common.Hash][]*storageTask // Storage intervals needing fetching for large contracts + + // These fields are internals used during runtime + req *accountRequest // Pending request to fill this task + res *accountResponse // Validate response filling this task + pend int // Number of pending subtasks for this round + + needCode []bool // Flags whether the filling accounts need code retrieval + needState []bool // Flags whether the filling accounts need storage retrieval + needHeal []bool // Flags whether the filling accounts's state was chunked and need healing + + codeTasks map[common.Hash]struct{} // Code hashes that need retrieval + stateTasks map[common.Hash]common.Hash // Account hashes->roots that need full state retrieval + + done bool // Flag whether the task can be removed +} + +// storageTask represents the sync task for a chunk of the storage snapshot. +type storageTask struct { + Next common.Hash // Next account to sync in this interval + Last common.Hash // Last account to sync in this interval + + // These fields are internals used during runtime + root common.Hash // Storage root hash for this instance + req *storageRequest // Pending request to fill this task + done bool // Flag whether the task can be removed +} + +// healTask represents the sync task for healing the snap-synced chunk boundaries. +type healTask struct { + scheduler *trie.Sync // State trie sync scheduler defining the tasks + + trieTasks map[common.Hash]trie.SyncPath // Set of trie node tasks currently queued for retrieval + codeTasks map[common.Hash]struct{} // Set of byte code tasks currently queued for retrieval +} + +// syncProgress is a database entry to allow suspending and resuming a snapshot state +// sync. Opposed to full and fast sync, there is no way to restart a suspended +// snap sync without prior knowledge of the suspension point. +type syncProgress struct { + Tasks []*accountTask // The suspended account tasks (contract tasks within) + + // Status report during syncing phase + AccountSynced uint64 // Number of accounts downloaded + AccountBytes common.StorageSize // Number of account trie bytes persisted to disk + BytecodeSynced uint64 // Number of bytecodes downloaded + BytecodeBytes common.StorageSize // Number of bytecode bytes downloaded + StorageSynced uint64 // Number of storage slots downloaded + StorageBytes common.StorageSize // Number of storage trie bytes persisted to disk + + // Status report during healing phase + TrienodeHealSynced uint64 // Number of state trie nodes downloaded + TrienodeHealBytes common.StorageSize // Number of state trie bytes persisted to disk + TrienodeHealDups uint64 // Number of state trie nodes already processed + TrienodeHealNops uint64 // Number of state trie nodes not requested + BytecodeHealSynced uint64 // Number of bytecodes downloaded + BytecodeHealBytes common.StorageSize // Number of bytecodes persisted to disk + BytecodeHealDups uint64 // Number of bytecodes already processed + BytecodeHealNops uint64 // Number of bytecodes not requested +} + +// SyncPeer abstracts out the methods required for a peer to be synced against +// with the goal of allowing the construction of mock peers without the full +// blown networking. +type SyncPeer interface { + // ID retrieves the peer's unique identifier. + ID() string + + // RequestAccountRange fetches a batch of accounts rooted in a specific account + // trie, starting with the origin. + RequestAccountRange(id uint64, root, origin, limit common.Hash, bytes uint64) error + + // RequestStorageRange fetches a batch of storage slots belonging to one or + // more accounts. If slots from only one accout is requested, an origin marker + // may also be used to retrieve from there. + RequestStorageRanges(id uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, bytes uint64) error + + // RequestByteCodes fetches a batch of bytecodes by hash. + RequestByteCodes(id uint64, hashes []common.Hash, bytes uint64) error + + // RequestTrieNodes fetches a batch of account or storage trie nodes rooted in + // a specificstate trie. + RequestTrieNodes(id uint64, root common.Hash, paths []TrieNodePathSet, bytes uint64) error + + // Log retrieves the peer's own contextual logger. + Log() log.Logger +} + +// Syncer is an Ethereum account and storage trie syncer based on snapshots and +// the snap protocol. It's purpose is to download all the accounts and storage +// slots from remote peers and reassemble chunks of the state trie, on top of +// which a state sync can be run to fix any gaps / overlaps. +// +// Every network request has a variety of failure events: +// - The peer disconnects after task assignment, failing to send the request +// - The peer disconnects after sending the request, before delivering on it +// - The peer remains connected, but does not deliver a response in time +// - The peer delivers a stale response after a previous timeout +// - The peer delivers a refusal to serve the requested state +type Syncer struct { + db ethdb.KeyValueStore // Database to store the trie nodes into (and dedup) + + root common.Hash // Current state trie root being synced + tasks []*accountTask // Current account task set being synced + snapped bool // Flag to signal that snap phase is done + healer *healTask // Current state healing task being executed + update chan struct{} // Notification channel for possible sync progression + + peers map[string]SyncPeer // Currently active peers to download from + peerJoin *event.Feed // Event feed to react to peers joining + peerDrop *event.Feed // Event feed to react to peers dropping + + // Request tracking during syncing phase + statelessPeers map[string]struct{} // Peers that failed to deliver state data + accountIdlers map[string]struct{} // Peers that aren't serving account requests + bytecodeIdlers map[string]struct{} // Peers that aren't serving bytecode requests + storageIdlers map[string]struct{} // Peers that aren't serving storage requests + + accountReqs map[uint64]*accountRequest // Account requests currently running + bytecodeReqs map[uint64]*bytecodeRequest // Bytecode requests currently running + storageReqs map[uint64]*storageRequest // Storage requests currently running + + accountReqFails chan *accountRequest // Failed account range requests to revert + bytecodeReqFails chan *bytecodeRequest // Failed bytecode requests to revert + storageReqFails chan *storageRequest // Failed storage requests to revert + + accountResps chan *accountResponse // Account sub-tries to integrate into the database + bytecodeResps chan *bytecodeResponse // Bytecodes to integrate into the database + storageResps chan *storageResponse // Storage sub-tries to integrate into the database + + accountSynced uint64 // Number of accounts downloaded + accountBytes common.StorageSize // Number of account trie bytes persisted to disk + bytecodeSynced uint64 // Number of bytecodes downloaded + bytecodeBytes common.StorageSize // Number of bytecode bytes downloaded + storageSynced uint64 // Number of storage slots downloaded + storageBytes common.StorageSize // Number of storage trie bytes persisted to disk + + // Request tracking during healing phase + trienodeHealIdlers map[string]struct{} // Peers that aren't serving trie node requests + bytecodeHealIdlers map[string]struct{} // Peers that aren't serving bytecode requests + + trienodeHealReqs map[uint64]*trienodeHealRequest // Trie node requests currently running + bytecodeHealReqs map[uint64]*bytecodeHealRequest // Bytecode requests currently running + + trienodeHealReqFails chan *trienodeHealRequest // Failed trienode requests to revert + bytecodeHealReqFails chan *bytecodeHealRequest // Failed bytecode requests to revert + + trienodeHealResps chan *trienodeHealResponse // Trie nodes to integrate into the database + bytecodeHealResps chan *bytecodeHealResponse // Bytecodes to integrate into the database + + trienodeHealSynced uint64 // Number of state trie nodes downloaded + trienodeHealBytes common.StorageSize // Number of state trie bytes persisted to disk + trienodeHealDups uint64 // Number of state trie nodes already processed + trienodeHealNops uint64 // Number of state trie nodes not requested + bytecodeHealSynced uint64 // Number of bytecodes downloaded + bytecodeHealBytes common.StorageSize // Number of bytecodes persisted to disk + bytecodeHealDups uint64 // Number of bytecodes already processed + bytecodeHealNops uint64 // Number of bytecodes not requested + + startTime time.Time // Time instance when snapshot sync started + startAcc common.Hash // Account hash where sync started from + logTime time.Time // Time instance when status was last reported + + pend sync.WaitGroup // Tracks network request goroutines for graceful shutdown + lock sync.RWMutex // Protects fields that can change outside of sync (peers, reqs, root) +} + +// NewSyncer creates a new snapshot syncer to download the Ethereum state over the +// snap protocol. +func NewSyncer(db ethdb.KeyValueStore) *Syncer { + return &Syncer{ + db: db, + + peers: make(map[string]SyncPeer), + peerJoin: new(event.Feed), + peerDrop: new(event.Feed), + update: make(chan struct{}, 1), + + accountIdlers: make(map[string]struct{}), + storageIdlers: make(map[string]struct{}), + bytecodeIdlers: make(map[string]struct{}), + + accountReqs: make(map[uint64]*accountRequest), + storageReqs: make(map[uint64]*storageRequest), + bytecodeReqs: make(map[uint64]*bytecodeRequest), + accountReqFails: make(chan *accountRequest), + storageReqFails: make(chan *storageRequest), + bytecodeReqFails: make(chan *bytecodeRequest), + accountResps: make(chan *accountResponse), + storageResps: make(chan *storageResponse), + bytecodeResps: make(chan *bytecodeResponse), + + trienodeHealIdlers: make(map[string]struct{}), + bytecodeHealIdlers: make(map[string]struct{}), + + trienodeHealReqs: make(map[uint64]*trienodeHealRequest), + bytecodeHealReqs: make(map[uint64]*bytecodeHealRequest), + trienodeHealReqFails: make(chan *trienodeHealRequest), + bytecodeHealReqFails: make(chan *bytecodeHealRequest), + trienodeHealResps: make(chan *trienodeHealResponse), + bytecodeHealResps: make(chan *bytecodeHealResponse), + } +} + +// Register injects a new data source into the syncer's peerset. +func (s *Syncer) Register(peer SyncPeer) error { + // Make sure the peer is not registered yet + id := peer.ID() + + s.lock.Lock() + if _, ok := s.peers[id]; ok { + log.Error("Snap peer already registered", "id", id) + + s.lock.Unlock() + return errors.New("already registered") + } + s.peers[id] = peer + + // Mark the peer as idle, even if no sync is running + s.accountIdlers[id] = struct{}{} + s.storageIdlers[id] = struct{}{} + s.bytecodeIdlers[id] = struct{}{} + s.trienodeHealIdlers[id] = struct{}{} + s.bytecodeHealIdlers[id] = struct{}{} + s.lock.Unlock() + + // Notify any active syncs that a new peer can be assigned data + s.peerJoin.Send(id) + return nil +} + +// Unregister injects a new data source into the syncer's peerset. +func (s *Syncer) Unregister(id string) error { + // Remove all traces of the peer from the registry + s.lock.Lock() + if _, ok := s.peers[id]; !ok { + log.Error("Snap peer not registered", "id", id) + + s.lock.Unlock() + return errors.New("not registered") + } + delete(s.peers, id) + + // Remove status markers, even if no sync is running + delete(s.statelessPeers, id) + + delete(s.accountIdlers, id) + delete(s.storageIdlers, id) + delete(s.bytecodeIdlers, id) + delete(s.trienodeHealIdlers, id) + delete(s.bytecodeHealIdlers, id) + s.lock.Unlock() + + // Notify any active syncs that pending requests need to be reverted + s.peerDrop.Send(id) + return nil +} + +// Sync starts (or resumes a previous) sync cycle to iterate over an state trie +// with the given root and reconstruct the nodes based on the snapshot leaves. +// Previously downloaded segments will not be redownloaded of fixed, rather any +// errors will be healed after the leaves are fully accumulated. +func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error { + // Move the trie root from any previous value, revert stateless markers for + // any peers and initialize the syncer if it was not yet run + s.lock.Lock() + s.root = root + s.healer = &healTask{ + scheduler: state.NewStateSync(root, s.db, nil), + trieTasks: make(map[common.Hash]trie.SyncPath), + codeTasks: make(map[common.Hash]struct{}), + } + s.statelessPeers = make(map[string]struct{}) + s.lock.Unlock() + + if s.startTime == (time.Time{}) { + s.startTime = time.Now() + } + // Retrieve the previous sync status from LevelDB and abort if already synced + s.loadSyncStatus() + if len(s.tasks) == 0 && s.healer.scheduler.Pending() == 0 { + log.Debug("Snapshot sync already completed") + return nil + } + defer func() { // Persist any progress, independent of failure + for _, task := range s.tasks { + s.forwardAccountTask(task) + } + s.cleanAccountTasks() + s.saveSyncStatus() + }() + + log.Debug("Starting snapshot sync cycle", "root", root) + defer s.report(true) + + // Whether sync completed or not, disregard any future packets + defer func() { + log.Debug("Terminating snapshot sync cycle", "root", root) + s.lock.Lock() + s.accountReqs = make(map[uint64]*accountRequest) + s.storageReqs = make(map[uint64]*storageRequest) + s.bytecodeReqs = make(map[uint64]*bytecodeRequest) + s.trienodeHealReqs = make(map[uint64]*trienodeHealRequest) + s.bytecodeHealReqs = make(map[uint64]*bytecodeHealRequest) + s.lock.Unlock() + }() + // Keep scheduling sync tasks + peerJoin := make(chan string, 16) + peerJoinSub := s.peerJoin.Subscribe(peerJoin) + defer peerJoinSub.Unsubscribe() + + peerDrop := make(chan string, 16) + peerDropSub := s.peerDrop.Subscribe(peerDrop) + defer peerDropSub.Unsubscribe() + + for { + // Remove all completed tasks and terminate sync if everything's done + s.cleanStorageTasks() + s.cleanAccountTasks() + if len(s.tasks) == 0 && s.healer.scheduler.Pending() == 0 { + return nil + } + // Assign all the data retrieval tasks to any free peers + s.assignAccountTasks(cancel) + s.assignBytecodeTasks(cancel) + s.assignStorageTasks(cancel) + + if len(s.tasks) == 0 { + // Sync phase done, run heal phase + s.assignTrienodeHealTasks(cancel) + s.assignBytecodeHealTasks(cancel) + } + // Wait for something to happen + select { + case <-s.update: + // Something happened (new peer, delivery, timeout), recheck tasks + case <-peerJoin: + // A new peer joined, try to schedule it new tasks + case id := <-peerDrop: + s.revertRequests(id) + case <-cancel: + return ErrCancelled + + case req := <-s.accountReqFails: + s.revertAccountRequest(req) + case req := <-s.bytecodeReqFails: + s.revertBytecodeRequest(req) + case req := <-s.storageReqFails: + s.revertStorageRequest(req) + case req := <-s.trienodeHealReqFails: + s.revertTrienodeHealRequest(req) + case req := <-s.bytecodeHealReqFails: + s.revertBytecodeHealRequest(req) + + case res := <-s.accountResps: + s.processAccountResponse(res) + case res := <-s.bytecodeResps: + s.processBytecodeResponse(res) + case res := <-s.storageResps: + s.processStorageResponse(res) + case res := <-s.trienodeHealResps: + s.processTrienodeHealResponse(res) + case res := <-s.bytecodeHealResps: + s.processBytecodeHealResponse(res) + } + // Report stats if something meaningful happened + s.report(false) + } +} + +// loadSyncStatus retrieves a previously aborted sync status from the database, +// or generates a fresh one if none is available. +func (s *Syncer) loadSyncStatus() { + var progress syncProgress + + if status := rawdb.ReadSnapshotSyncStatus(s.db); status != nil { + if err := json.Unmarshal(status, &progress); err != nil { + log.Error("Failed to decode snap sync status", "err", err) + } else { + for _, task := range progress.Tasks { + log.Debug("Scheduled account sync task", "from", task.Next, "last", task.Last) + } + s.tasks = progress.Tasks + s.snapped = len(s.tasks) == 0 + + s.accountSynced = progress.AccountSynced + s.accountBytes = progress.AccountBytes + s.bytecodeSynced = progress.BytecodeSynced + s.bytecodeBytes = progress.BytecodeBytes + s.storageSynced = progress.StorageSynced + s.storageBytes = progress.StorageBytes + + s.trienodeHealSynced = progress.TrienodeHealSynced + s.trienodeHealBytes = progress.TrienodeHealBytes + s.bytecodeHealSynced = progress.BytecodeHealSynced + s.bytecodeHealBytes = progress.BytecodeHealBytes + return + } + } + // Either we've failed to decode the previus state, or there was none. + // Start a fresh sync by chunking up the account range and scheduling + // them for retrieval. + s.tasks = nil + s.accountSynced, s.accountBytes = 0, 0 + s.bytecodeSynced, s.bytecodeBytes = 0, 0 + s.storageSynced, s.storageBytes = 0, 0 + s.trienodeHealSynced, s.trienodeHealBytes = 0, 0 + s.bytecodeHealSynced, s.bytecodeHealBytes = 0, 0 + + var next common.Hash + step := new(big.Int).Sub( + new(big.Int).Div( + new(big.Int).Exp(common.Big2, common.Big256, nil), + big.NewInt(accountConcurrency), + ), common.Big1, + ) + for i := 0; i < accountConcurrency; i++ { + last := common.BigToHash(new(big.Int).Add(next.Big(), step)) + if i == accountConcurrency-1 { + // Make sure we don't overflow if the step is not a proper divisor + last = common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + } + s.tasks = append(s.tasks, &accountTask{ + Next: next, + Last: last, + SubTasks: make(map[common.Hash][]*storageTask), + }) + log.Debug("Created account sync task", "from", next, "last", last) + next = common.BigToHash(new(big.Int).Add(last.Big(), common.Big1)) + } +} + +// saveSyncStatus marshals the remaining sync tasks into leveldb. +func (s *Syncer) saveSyncStatus() { + progress := &syncProgress{ + Tasks: s.tasks, + AccountSynced: s.accountSynced, + AccountBytes: s.accountBytes, + BytecodeSynced: s.bytecodeSynced, + BytecodeBytes: s.bytecodeBytes, + StorageSynced: s.storageSynced, + StorageBytes: s.storageBytes, + TrienodeHealSynced: s.trienodeHealSynced, + TrienodeHealBytes: s.trienodeHealBytes, + BytecodeHealSynced: s.bytecodeHealSynced, + BytecodeHealBytes: s.bytecodeHealBytes, + } + status, err := json.Marshal(progress) + if err != nil { + panic(err) // This can only fail during implementation + } + rawdb.WriteSnapshotSyncStatus(s.db, status) +} + +// cleanAccountTasks removes account range retrieval tasks that have already been +// completed. +func (s *Syncer) cleanAccountTasks() { + for i := 0; i < len(s.tasks); i++ { + if s.tasks[i].done { + s.tasks = append(s.tasks[:i], s.tasks[i+1:]...) + i-- + } + } + if len(s.tasks) == 0 { + s.lock.Lock() + s.snapped = true + s.lock.Unlock() + } +} + +// cleanStorageTasks iterates over all the account tasks and storage sub-tasks +// within, cleaning any that have been completed. +func (s *Syncer) cleanStorageTasks() { + for _, task := range s.tasks { + for account, subtasks := range task.SubTasks { + // Remove storage range retrieval tasks that completed + for j := 0; j < len(subtasks); j++ { + if subtasks[j].done { + subtasks = append(subtasks[:j], subtasks[j+1:]...) + j-- + } + } + if len(subtasks) > 0 { + task.SubTasks[account] = subtasks + continue + } + // If all storage chunks are done, mark the account as done too + for j, hash := range task.res.hashes { + if hash == account { + task.needState[j] = false + } + } + delete(task.SubTasks, account) + task.pend-- + + // If this was the last pending task, forward the account task + if task.pend == 0 { + s.forwardAccountTask(task) + } + } + } +} + +// assignAccountTasks attempts to match idle peers to pending account range +// retrievals. +func (s *Syncer) assignAccountTasks(cancel chan struct{}) { + s.lock.Lock() + defer s.lock.Unlock() + + // If there are no idle peers, short circuit assignment + if len(s.accountIdlers) == 0 { + return + } + // Iterate over all the tasks and try to find a pending one + for _, task := range s.tasks { + // Skip any tasks already filling + if task.req != nil || task.res != nil { + continue + } + // Task pending retrieval, try to find an idle peer. If no such peer + // exists, we probably assigned tasks for all (or they are stateless). + // Abort the entire assignment mechanism. + var idle string + for id := range s.accountIdlers { + // If the peer rejected a query in this sync cycle, don't bother asking + // again for anything, it's either out of sync or already pruned + if _, ok := s.statelessPeers[id]; ok { + continue + } + idle = id + break + } + if idle == "" { + return + } + peer := s.peers[idle] + + // Matched a pending task to an idle peer, allocate a unique request id + var reqid uint64 + for { + reqid = uint64(rand.Int63()) + if reqid == 0 { + continue + } + if _, ok := s.accountReqs[reqid]; ok { + continue + } + break + } + // Generate the network query and send it to the peer + req := &accountRequest{ + peer: idle, + id: reqid, + cancel: cancel, + stale: make(chan struct{}), + origin: task.Next, + limit: task.Last, + task: task, + } + req.timeout = time.AfterFunc(requestTimeout, func() { + peer.Log().Debug("Account range request timed out", "reqid", reqid) + s.scheduleRevertAccountRequest(req) + }) + s.accountReqs[reqid] = req + delete(s.accountIdlers, idle) + + s.pend.Add(1) + go func(root common.Hash) { + defer s.pend.Done() + + // Attempt to send the remote request and revert if it fails + if err := peer.RequestAccountRange(reqid, root, req.origin, req.limit, maxRequestSize); err != nil { + peer.Log().Debug("Failed to request account range", "err", err) + s.scheduleRevertAccountRequest(req) + } + }(s.root) + + // Inject the request into the task to block further assignments + task.req = req + } +} + +// assignBytecodeTasks attempts to match idle peers to pending code retrievals. +func (s *Syncer) assignBytecodeTasks(cancel chan struct{}) { + s.lock.Lock() + defer s.lock.Unlock() + + // If there are no idle peers, short circuit assignment + if len(s.bytecodeIdlers) == 0 { + return + } + // Iterate over all the tasks and try to find a pending one + for _, task := range s.tasks { + // Skip any tasks not in the bytecode retrieval phase + if task.res == nil { + continue + } + // Skip tasks that are already retrieving (or done with) all codes + if len(task.codeTasks) == 0 { + continue + } + // Task pending retrieval, try to find an idle peer. If no such peer + // exists, we probably assigned tasks for all (or they are stateless). + // Abort the entire assignment mechanism. + var idle string + for id := range s.bytecodeIdlers { + // If the peer rejected a query in this sync cycle, don't bother asking + // again for anything, it's either out of sync or already pruned + if _, ok := s.statelessPeers[id]; ok { + continue + } + idle = id + break + } + if idle == "" { + return + } + peer := s.peers[idle] + + // Matched a pending task to an idle peer, allocate a unique request id + var reqid uint64 + for { + reqid = uint64(rand.Int63()) + if reqid == 0 { + continue + } + if _, ok := s.bytecodeReqs[reqid]; ok { + continue + } + break + } + // Generate the network query and send it to the peer + hashes := make([]common.Hash, 0, maxCodeRequestCount) + for hash := range task.codeTasks { + delete(task.codeTasks, hash) + hashes = append(hashes, hash) + if len(hashes) >= maxCodeRequestCount { + break + } + } + req := &bytecodeRequest{ + peer: idle, + id: reqid, + cancel: cancel, + stale: make(chan struct{}), + hashes: hashes, + task: task, + } + req.timeout = time.AfterFunc(requestTimeout, func() { + peer.Log().Debug("Bytecode request timed out", "reqid", reqid) + s.scheduleRevertBytecodeRequest(req) + }) + s.bytecodeReqs[reqid] = req + delete(s.bytecodeIdlers, idle) + + s.pend.Add(1) + go func() { + defer s.pend.Done() + + // Attempt to send the remote request and revert if it fails + if err := peer.RequestByteCodes(reqid, hashes, maxRequestSize); err != nil { + log.Debug("Failed to request bytecodes", "err", err) + s.scheduleRevertBytecodeRequest(req) + } + }() + } +} + +// assignStorageTasks attempts to match idle peers to pending storage range +// retrievals. +func (s *Syncer) assignStorageTasks(cancel chan struct{}) { + s.lock.Lock() + defer s.lock.Unlock() + + // If there are no idle peers, short circuit assignment + if len(s.storageIdlers) == 0 { + return + } + // Iterate over all the tasks and try to find a pending one + for _, task := range s.tasks { + // Skip any tasks not in the storage retrieval phase + if task.res == nil { + continue + } + // Skip tasks that are already retrieving (or done with) all small states + if len(task.SubTasks) == 0 && len(task.stateTasks) == 0 { + continue + } + // Task pending retrieval, try to find an idle peer. If no such peer + // exists, we probably assigned tasks for all (or they are stateless). + // Abort the entire assignment mechanism. + var idle string + for id := range s.storageIdlers { + // If the peer rejected a query in this sync cycle, don't bother asking + // again for anything, it's either out of sync or already pruned + if _, ok := s.statelessPeers[id]; ok { + continue + } + idle = id + break + } + if idle == "" { + return + } + peer := s.peers[idle] + + // Matched a pending task to an idle peer, allocate a unique request id + var reqid uint64 + for { + reqid = uint64(rand.Int63()) + if reqid == 0 { + continue + } + if _, ok := s.storageReqs[reqid]; ok { + continue + } + break + } + // Generate the network query and send it to the peer. If there are + // large contract tasks pending, complete those before diving into + // even more new contracts. + var ( + accounts = make([]common.Hash, 0, maxStorageSetRequestCount) + roots = make([]common.Hash, 0, maxStorageSetRequestCount) + subtask *storageTask + ) + for account, subtasks := range task.SubTasks { + for _, st := range subtasks { + // Skip any subtasks already filling + if st.req != nil { + continue + } + // Found an incomplete storage chunk, schedule it + accounts = append(accounts, account) + roots = append(roots, st.root) + subtask = st + break // Large contract chunks are downloaded individually + } + if subtask != nil { + break // Large contract chunks are downloaded individually + } + } + if subtask == nil { + // No large contract required retrieval, but small ones available + for acccount, root := range task.stateTasks { + delete(task.stateTasks, acccount) + + accounts = append(accounts, acccount) + roots = append(roots, root) + + if len(accounts) >= maxStorageSetRequestCount { + break + } + } + } + // If nothing was found, it means this task is actually already fully + // retrieving, but large contracts are hard to detect. Skip to the next. + if len(accounts) == 0 { + continue + } + req := &storageRequest{ + peer: idle, + id: reqid, + cancel: cancel, + stale: make(chan struct{}), + accounts: accounts, + roots: roots, + mainTask: task, + subTask: subtask, + } + if subtask != nil { + req.origin = subtask.Next + req.limit = subtask.Last + } + req.timeout = time.AfterFunc(requestTimeout, func() { + peer.Log().Debug("Storage request timed out", "reqid", reqid) + s.scheduleRevertStorageRequest(req) + }) + s.storageReqs[reqid] = req + delete(s.storageIdlers, idle) + + s.pend.Add(1) + go func(root common.Hash) { + defer s.pend.Done() + + // Attempt to send the remote request and revert if it fails + var origin, limit []byte + if subtask != nil { + origin, limit = req.origin[:], req.limit[:] + } + if err := peer.RequestStorageRanges(reqid, root, accounts, origin, limit, maxRequestSize); err != nil { + log.Debug("Failed to request storage", "err", err) + s.scheduleRevertStorageRequest(req) + } + }(s.root) + + // Inject the request into the subtask to block further assignments + if subtask != nil { + subtask.req = req + } + } +} + +// assignTrienodeHealTasks attempts to match idle peers to trie node requests to +// heal any trie errors caused by the snap sync's chunked retrieval model. +func (s *Syncer) assignTrienodeHealTasks(cancel chan struct{}) { + s.lock.Lock() + defer s.lock.Unlock() + + // If there are no idle peers, short circuit assignment + if len(s.trienodeHealIdlers) == 0 { + return + } + // Iterate over pending tasks and try to find a peer to retrieve with + for len(s.healer.trieTasks) > 0 || s.healer.scheduler.Pending() > 0 { + // If there are not enough trie tasks queued to fully assign, fill the + // queue from the state sync scheduler. The trie synced schedules these + // together with bytecodes, so we need to queue them combined. + var ( + have = len(s.healer.trieTasks) + len(s.healer.codeTasks) + want = maxTrieRequestCount + maxCodeRequestCount + ) + if have < want { + nodes, paths, codes := s.healer.scheduler.Missing(want - have) + for i, hash := range nodes { + s.healer.trieTasks[hash] = paths[i] + } + for _, hash := range codes { + s.healer.codeTasks[hash] = struct{}{} + } + } + // If all the heal tasks are bytecodes or already downloading, bail + if len(s.healer.trieTasks) == 0 { + return + } + // Task pending retrieval, try to find an idle peer. If no such peer + // exists, we probably assigned tasks for all (or they are stateless). + // Abort the entire assignment mechanism. + var idle string + for id := range s.trienodeHealIdlers { + // If the peer rejected a query in this sync cycle, don't bother asking + // again for anything, it's either out of sync or already pruned + if _, ok := s.statelessPeers[id]; ok { + continue + } + idle = id + break + } + if idle == "" { + return + } + peer := s.peers[idle] + + // Matched a pending task to an idle peer, allocate a unique request id + var reqid uint64 + for { + reqid = uint64(rand.Int63()) + if reqid == 0 { + continue + } + if _, ok := s.trienodeHealReqs[reqid]; ok { + continue + } + break + } + // Generate the network query and send it to the peer + var ( + hashes = make([]common.Hash, 0, maxTrieRequestCount) + paths = make([]trie.SyncPath, 0, maxTrieRequestCount) + pathsets = make([]TrieNodePathSet, 0, maxTrieRequestCount) + ) + for hash, pathset := range s.healer.trieTasks { + delete(s.healer.trieTasks, hash) + + hashes = append(hashes, hash) + paths = append(paths, pathset) + pathsets = append(pathsets, [][]byte(pathset)) // TODO(karalabe): group requests by account hash + + if len(hashes) >= maxTrieRequestCount { + break + } + } + req := &trienodeHealRequest{ + peer: idle, + id: reqid, + cancel: cancel, + stale: make(chan struct{}), + hashes: hashes, + paths: paths, + task: s.healer, + } + req.timeout = time.AfterFunc(requestTimeout, func() { + peer.Log().Debug("Trienode heal request timed out", "reqid", reqid) + s.scheduleRevertTrienodeHealRequest(req) + }) + s.trienodeHealReqs[reqid] = req + delete(s.trienodeHealIdlers, idle) + + s.pend.Add(1) + go func(root common.Hash) { + defer s.pend.Done() + + // Attempt to send the remote request and revert if it fails + if err := peer.RequestTrieNodes(reqid, root, pathsets, maxRequestSize); err != nil { + log.Debug("Failed to request trienode healers", "err", err) + s.scheduleRevertTrienodeHealRequest(req) + } + }(s.root) + } +} + +// assignBytecodeHealTasks attempts to match idle peers to bytecode requests to +// heal any trie errors caused by the snap sync's chunked retrieval model. +func (s *Syncer) assignBytecodeHealTasks(cancel chan struct{}) { + s.lock.Lock() + defer s.lock.Unlock() + + // If there are no idle peers, short circuit assignment + if len(s.bytecodeHealIdlers) == 0 { + return + } + // Iterate over pending tasks and try to find a peer to retrieve with + for len(s.healer.codeTasks) > 0 || s.healer.scheduler.Pending() > 0 { + // If there are not enough trie tasks queued to fully assign, fill the + // queue from the state sync scheduler. The trie synced schedules these + // together with trie nodes, so we need to queue them combined. + var ( + have = len(s.healer.trieTasks) + len(s.healer.codeTasks) + want = maxTrieRequestCount + maxCodeRequestCount + ) + if have < want { + nodes, paths, codes := s.healer.scheduler.Missing(want - have) + for i, hash := range nodes { + s.healer.trieTasks[hash] = paths[i] + } + for _, hash := range codes { + s.healer.codeTasks[hash] = struct{}{} + } + } + // If all the heal tasks are trienodes or already downloading, bail + if len(s.healer.codeTasks) == 0 { + return + } + // Task pending retrieval, try to find an idle peer. If no such peer + // exists, we probably assigned tasks for all (or they are stateless). + // Abort the entire assignment mechanism. + var idle string + for id := range s.bytecodeHealIdlers { + // If the peer rejected a query in this sync cycle, don't bother asking + // again for anything, it's either out of sync or already pruned + if _, ok := s.statelessPeers[id]; ok { + continue + } + idle = id + break + } + if idle == "" { + return + } + peer := s.peers[idle] + + // Matched a pending task to an idle peer, allocate a unique request id + var reqid uint64 + for { + reqid = uint64(rand.Int63()) + if reqid == 0 { + continue + } + if _, ok := s.bytecodeHealReqs[reqid]; ok { + continue + } + break + } + // Generate the network query and send it to the peer + hashes := make([]common.Hash, 0, maxCodeRequestCount) + for hash := range s.healer.codeTasks { + delete(s.healer.codeTasks, hash) + + hashes = append(hashes, hash) + if len(hashes) >= maxCodeRequestCount { + break + } + } + req := &bytecodeHealRequest{ + peer: idle, + id: reqid, + cancel: cancel, + stale: make(chan struct{}), + hashes: hashes, + task: s.healer, + } + req.timeout = time.AfterFunc(requestTimeout, func() { + peer.Log().Debug("Bytecode heal request timed out", "reqid", reqid) + s.scheduleRevertBytecodeHealRequest(req) + }) + s.bytecodeHealReqs[reqid] = req + delete(s.bytecodeHealIdlers, idle) + + s.pend.Add(1) + go func() { + defer s.pend.Done() + + // Attempt to send the remote request and revert if it fails + if err := peer.RequestByteCodes(reqid, hashes, maxRequestSize); err != nil { + log.Debug("Failed to request bytecode healers", "err", err) + s.scheduleRevertBytecodeHealRequest(req) + } + }() + } +} + +// revertRequests locates all the currently pending reuqests from a particular +// peer and reverts them, rescheduling for others to fulfill. +func (s *Syncer) revertRequests(peer string) { + // Gather the requests first, revertals need the lock too + s.lock.Lock() + var accountReqs []*accountRequest + for _, req := range s.accountReqs { + if req.peer == peer { + accountReqs = append(accountReqs, req) + } + } + var bytecodeReqs []*bytecodeRequest + for _, req := range s.bytecodeReqs { + if req.peer == peer { + bytecodeReqs = append(bytecodeReqs, req) + } + } + var storageReqs []*storageRequest + for _, req := range s.storageReqs { + if req.peer == peer { + storageReqs = append(storageReqs, req) + } + } + var trienodeHealReqs []*trienodeHealRequest + for _, req := range s.trienodeHealReqs { + if req.peer == peer { + trienodeHealReqs = append(trienodeHealReqs, req) + } + } + var bytecodeHealReqs []*bytecodeHealRequest + for _, req := range s.bytecodeHealReqs { + if req.peer == peer { + bytecodeHealReqs = append(bytecodeHealReqs, req) + } + } + s.lock.Unlock() + + // Revert all the requests matching the peer + for _, req := range accountReqs { + s.revertAccountRequest(req) + } + for _, req := range bytecodeReqs { + s.revertBytecodeRequest(req) + } + for _, req := range storageReqs { + s.revertStorageRequest(req) + } + for _, req := range trienodeHealReqs { + s.revertTrienodeHealRequest(req) + } + for _, req := range bytecodeHealReqs { + s.revertBytecodeHealRequest(req) + } +} + +// scheduleRevertAccountRequest asks the event loop to clean up an account range +// request and return all failed retrieval tasks to the scheduler for reassignment. +func (s *Syncer) scheduleRevertAccountRequest(req *accountRequest) { + select { + case s.accountReqFails <- req: + // Sync event loop notified + case <-req.cancel: + // Sync cycle got cancelled + case <-req.stale: + // Request already reverted + } +} + +// revertAccountRequest cleans up an account range request and returns all failed +// retrieval tasks to the scheduler for reassignment. +// +// Note, this needs to run on the event runloop thread to reschedule to idle peers. +// On peer threads, use scheduleRevertAccountRequest. +func (s *Syncer) revertAccountRequest(req *accountRequest) { + log.Debug("Reverting account request", "peer", req.peer, "reqid", req.id) + select { + case <-req.stale: + log.Trace("Account request already reverted", "peer", req.peer, "reqid", req.id) + return + default: + } + close(req.stale) + + // Remove the request from the tracked set + s.lock.Lock() + delete(s.accountReqs, req.id) + s.lock.Unlock() + + // If there's a timeout timer still running, abort it and mark the account + // task as not-pending, ready for resheduling + req.timeout.Stop() + if req.task.req == req { + req.task.req = nil + } +} + +// scheduleRevertBytecodeRequest asks the event loop to clean up a bytecode request +// and return all failed retrieval tasks to the scheduler for reassignment. +func (s *Syncer) scheduleRevertBytecodeRequest(req *bytecodeRequest) { + select { + case s.bytecodeReqFails <- req: + // Sync event loop notified + case <-req.cancel: + // Sync cycle got cancelled + case <-req.stale: + // Request already reverted + } +} + +// revertBytecodeRequest cleans up a bytecode request and returns all failed +// retrieval tasks to the scheduler for reassignment. +// +// Note, this needs to run on the event runloop thread to reschedule to idle peers. +// On peer threads, use scheduleRevertBytecodeRequest. +func (s *Syncer) revertBytecodeRequest(req *bytecodeRequest) { + log.Debug("Reverting bytecode request", "peer", req.peer) + select { + case <-req.stale: + log.Trace("Bytecode request already reverted", "peer", req.peer, "reqid", req.id) + return + default: + } + close(req.stale) + + // Remove the request from the tracked set + s.lock.Lock() + delete(s.bytecodeReqs, req.id) + s.lock.Unlock() + + // If there's a timeout timer still running, abort it and mark the code + // retrievals as not-pending, ready for resheduling + req.timeout.Stop() + for _, hash := range req.hashes { + req.task.codeTasks[hash] = struct{}{} + } +} + +// scheduleRevertStorageRequest asks the event loop to clean up a storage range +// request and return all failed retrieval tasks to the scheduler for reassignment. +func (s *Syncer) scheduleRevertStorageRequest(req *storageRequest) { + select { + case s.storageReqFails <- req: + // Sync event loop notified + case <-req.cancel: + // Sync cycle got cancelled + case <-req.stale: + // Request already reverted + } +} + +// revertStorageRequest cleans up a storage range request and returns all failed +// retrieval tasks to the scheduler for reassignment. +// +// Note, this needs to run on the event runloop thread to reschedule to idle peers. +// On peer threads, use scheduleRevertStorageRequest. +func (s *Syncer) revertStorageRequest(req *storageRequest) { + log.Debug("Reverting storage request", "peer", req.peer) + select { + case <-req.stale: + log.Trace("Storage request already reverted", "peer", req.peer, "reqid", req.id) + return + default: + } + close(req.stale) + + // Remove the request from the tracked set + s.lock.Lock() + delete(s.storageReqs, req.id) + s.lock.Unlock() + + // If there's a timeout timer still running, abort it and mark the storage + // task as not-pending, ready for resheduling + req.timeout.Stop() + if req.subTask != nil { + req.subTask.req = nil + } else { + for i, account := range req.accounts { + req.mainTask.stateTasks[account] = req.roots[i] + } + } +} + +// scheduleRevertTrienodeHealRequest asks the event loop to clean up a trienode heal +// request and return all failed retrieval tasks to the scheduler for reassignment. +func (s *Syncer) scheduleRevertTrienodeHealRequest(req *trienodeHealRequest) { + select { + case s.trienodeHealReqFails <- req: + // Sync event loop notified + case <-req.cancel: + // Sync cycle got cancelled + case <-req.stale: + // Request already reverted + } +} + +// revertTrienodeHealRequest cleans up a trienode heal request and returns all +// failed retrieval tasks to the scheduler for reassignment. +// +// Note, this needs to run on the event runloop thread to reschedule to idle peers. +// On peer threads, use scheduleRevertTrienodeHealRequest. +func (s *Syncer) revertTrienodeHealRequest(req *trienodeHealRequest) { + log.Debug("Reverting trienode heal request", "peer", req.peer) + select { + case <-req.stale: + log.Trace("Trienode heal request already reverted", "peer", req.peer, "reqid", req.id) + return + default: + } + close(req.stale) + + // Remove the request from the tracked set + s.lock.Lock() + delete(s.trienodeHealReqs, req.id) + s.lock.Unlock() + + // If there's a timeout timer still running, abort it and mark the trie node + // retrievals as not-pending, ready for resheduling + req.timeout.Stop() + for i, hash := range req.hashes { + req.task.trieTasks[hash] = req.paths[i] + } +} + +// scheduleRevertBytecodeHealRequest asks the event loop to clean up a bytecode heal +// request and return all failed retrieval tasks to the scheduler for reassignment. +func (s *Syncer) scheduleRevertBytecodeHealRequest(req *bytecodeHealRequest) { + select { + case s.bytecodeHealReqFails <- req: + // Sync event loop notified + case <-req.cancel: + // Sync cycle got cancelled + case <-req.stale: + // Request already reverted + } +} + +// revertBytecodeHealRequest cleans up a bytecode heal request and returns all +// failed retrieval tasks to the scheduler for reassignment. +// +// Note, this needs to run on the event runloop thread to reschedule to idle peers. +// On peer threads, use scheduleRevertBytecodeHealRequest. +func (s *Syncer) revertBytecodeHealRequest(req *bytecodeHealRequest) { + log.Debug("Reverting bytecode heal request", "peer", req.peer) + select { + case <-req.stale: + log.Trace("Bytecode heal request already reverted", "peer", req.peer, "reqid", req.id) + return + default: + } + close(req.stale) + + // Remove the request from the tracked set + s.lock.Lock() + delete(s.bytecodeHealReqs, req.id) + s.lock.Unlock() + + // If there's a timeout timer still running, abort it and mark the code + // retrievals as not-pending, ready for resheduling + req.timeout.Stop() + for _, hash := range req.hashes { + req.task.codeTasks[hash] = struct{}{} + } +} + +// processAccountResponse integrates an already validated account range response +// into the account tasks. +func (s *Syncer) processAccountResponse(res *accountResponse) { + // Switch the task from pending to filling + res.task.req = nil + res.task.res = res + + // Ensure that the response doesn't overflow into the subsequent task + last := res.task.Last.Big() + for i, hash := range res.hashes { + // Mark the range complete if the last is already included. + // Keep iteration to delete the extra states if exists. + cmp := hash.Big().Cmp(last) + if cmp == 0 { + res.cont = false + continue + } + if cmp > 0 { + // Chunk overflown, cut off excess, but also update the boundary nodes + for j := i; j < len(res.hashes); j++ { + if err := res.trie.Prove(res.hashes[j][:], 0, res.overflow); err != nil { + panic(err) // Account range was already proven, what happened + } + } + res.hashes = res.hashes[:i] + res.accounts = res.accounts[:i] + res.cont = false // Mark range completed + break + } + } + // Iterate over all the accounts and assemble which ones need further sub- + // filling before the entire account range can be persisted. + res.task.needCode = make([]bool, len(res.accounts)) + res.task.needState = make([]bool, len(res.accounts)) + res.task.needHeal = make([]bool, len(res.accounts)) + + res.task.codeTasks = make(map[common.Hash]struct{}) + res.task.stateTasks = make(map[common.Hash]common.Hash) + + resumed := make(map[common.Hash]struct{}) + + res.task.pend = 0 + for i, account := range res.accounts { + // Check if the account is a contract with an unknown code + if !bytes.Equal(account.CodeHash, emptyCode[:]) { + if code := rawdb.ReadCodeWithPrefix(s.db, common.BytesToHash(account.CodeHash)); code == nil { + res.task.codeTasks[common.BytesToHash(account.CodeHash)] = struct{}{} + res.task.needCode[i] = true + res.task.pend++ + } + } + // Check if the account is a contract with an unknown storage trie + if account.Root != emptyRoot { + if node, err := s.db.Get(account.Root[:]); err != nil || node == nil { + // If there was a previous large state retrieval in progress, + // don't restart it from scratch. This happens if a sync cycle + // is interrupted and resumed later. However, *do* update the + // previous root hash. + if subtasks, ok := res.task.SubTasks[res.hashes[i]]; ok { + log.Debug("Resuming large storage retrieval", "account", res.hashes[i], "root", account.Root) + for _, subtask := range subtasks { + subtask.root = account.Root + } + res.task.needHeal[i] = true + resumed[res.hashes[i]] = struct{}{} + } else { + res.task.stateTasks[res.hashes[i]] = account.Root + } + res.task.needState[i] = true + res.task.pend++ + } + } + } + // Delete any subtasks that have been aborted but not resumed. This may undo + // some progress if a new peer gives us less accounts than an old one, but for + // now we have to live with that. + for hash := range res.task.SubTasks { + if _, ok := resumed[hash]; !ok { + log.Debug("Aborting suspended storage retrieval", "account", hash) + delete(res.task.SubTasks, hash) + } + } + // If the account range contained no contracts, or all have been fully filled + // beforehand, short circuit storage filling and forward to the next task + if res.task.pend == 0 { + s.forwardAccountTask(res.task) + return + } + // Some accounts are incomplete, leave as is for the storage and contract + // task assigners to pick up and fill. +} + +// processBytecodeResponse integrates an already validated bytecode response +// into the account tasks. +func (s *Syncer) processBytecodeResponse(res *bytecodeResponse) { + batch := s.db.NewBatch() + + var ( + codes uint64 + bytes common.StorageSize + ) + for i, hash := range res.hashes { + code := res.codes[i] + + // If the bytecode was not delivered, reschedule it + if code == nil { + res.task.codeTasks[hash] = struct{}{} + continue + } + // Code was delivered, mark it not needed any more + for j, account := range res.task.res.accounts { + if res.task.needCode[j] && hash == common.BytesToHash(account.CodeHash) { + res.task.needCode[j] = false + res.task.pend-- + } + } + // Push the bytecode into a database batch + s.bytecodeSynced++ + s.bytecodeBytes += common.StorageSize(len(code)) + + codes++ + bytes += common.StorageSize(len(code)) + + rawdb.WriteCode(batch, hash, code) + } + if err := batch.Write(); err != nil { + log.Crit("Failed to persist bytecodes", "err", err) + } + log.Debug("Persisted set of bytecodes", "count", codes, "bytes", bytes) + + // If this delivery completed the last pending task, forward the account task + // to the next chunk + if res.task.pend == 0 { + s.forwardAccountTask(res.task) + return + } + // Some accounts are still incomplete, leave as is for the storage and contract + // task assigners to pick up and fill. +} + +// processStorageResponse integrates an already validated storage response +// into the account tasks. +func (s *Syncer) processStorageResponse(res *storageResponse) { + // Switch the suntask from pending to idle + if res.subTask != nil { + res.subTask.req = nil + } + batch := s.db.NewBatch() + + var ( + slots int + nodes int + skipped int + bytes common.StorageSize + ) + // Iterate over all the accounts and reconstruct their storage tries from the + // delivered slots + for i, account := range res.accounts { + // If the account was not delivered, reschedule it + if i >= len(res.hashes) { + res.mainTask.stateTasks[account] = res.roots[i] + continue + } + // State was delivered, if complete mark as not needed any more, otherwise + // mark the account as needing healing + for j, hash := range res.mainTask.res.hashes { + if account != hash { + continue + } + acc := res.mainTask.res.accounts[j] + + // If the packet contains multiple contract storage slots, all + // but the last are surely complete. The last contract may be + // chunked, so check it's continuation flag. + if res.subTask == nil && res.mainTask.needState[j] && (i < len(res.hashes)-1 || !res.cont) { + res.mainTask.needState[j] = false + res.mainTask.pend-- + } + // If the last contract was chunked, mark it as needing healing + // to avoid writing it out to disk prematurely. + if res.subTask == nil && !res.mainTask.needHeal[j] && i == len(res.hashes)-1 && res.cont { + res.mainTask.needHeal[j] = true + } + // If the last contract was chunked, we need to switch to large + // contract handling mode + if res.subTask == nil && i == len(res.hashes)-1 && res.cont { + // If we haven't yet started a large-contract retrieval, create + // the subtasks for it within the main account task + if tasks, ok := res.mainTask.SubTasks[account]; !ok { + var ( + next common.Hash + ) + step := new(big.Int).Sub( + new(big.Int).Div( + new(big.Int).Exp(common.Big2, common.Big256, nil), + big.NewInt(storageConcurrency), + ), common.Big1, + ) + for k := 0; k < storageConcurrency; k++ { + last := common.BigToHash(new(big.Int).Add(next.Big(), step)) + if k == storageConcurrency-1 { + // Make sure we don't overflow if the step is not a proper divisor + last = common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + } + tasks = append(tasks, &storageTask{ + Next: next, + Last: last, + root: acc.Root, + }) + log.Debug("Created storage sync task", "account", account, "root", acc.Root, "from", next, "last", last) + next = common.BigToHash(new(big.Int).Add(last.Big(), common.Big1)) + } + res.mainTask.SubTasks[account] = tasks + + // Since we've just created the sub-tasks, this response + // is surely for the first one (zero origin) + res.subTask = tasks[0] + } + } + // If we're in large contract delivery mode, forward the subtask + if res.subTask != nil { + // Ensure the response doesn't overflow into the subsequent task + last := res.subTask.Last.Big() + for k, hash := range res.hashes[i] { + // Mark the range complete if the last is already included. + // Keep iteration to delete the extra states if exists. + cmp := hash.Big().Cmp(last) + if cmp == 0 { + res.cont = false + continue + } + if cmp > 0 { + // Chunk overflown, cut off excess, but also update the boundary + for l := k; l < len(res.hashes[i]); l++ { + if err := res.tries[i].Prove(res.hashes[i][l][:], 0, res.overflow); err != nil { + panic(err) // Account range was already proven, what happened + } + } + res.hashes[i] = res.hashes[i][:k] + res.slots[i] = res.slots[i][:k] + res.cont = false // Mark range completed + break + } + } + // Forward the relevant storage chunk (even if created just now) + if res.cont { + res.subTask.Next = common.BigToHash(new(big.Int).Add(res.hashes[i][len(res.hashes[i])-1].Big(), big.NewInt(1))) + } else { + res.subTask.done = true + } + } + } + // Iterate over all the reconstructed trie nodes and push them to disk + slots += len(res.hashes[i]) + + it := res.nodes[i].NewIterator(nil, nil) + for it.Next() { + // Boundary nodes are not written for the last result, since they are incomplete + if i == len(res.hashes)-1 && res.subTask != nil { + if _, ok := res.bounds[common.BytesToHash(it.Key())]; ok { + skipped++ + continue + } + if _, err := res.overflow.Get(it.Key()); err == nil { + skipped++ + continue + } + } + // Node is not a boundary, persist to disk + batch.Put(it.Key(), it.Value()) + + bytes += common.StorageSize(common.HashLength + len(it.Value())) + nodes++ + } + it.Release() + } + if err := batch.Write(); err != nil { + log.Crit("Failed to persist storage slots", "err", err) + } + s.storageSynced += uint64(slots) + s.storageBytes += bytes + + log.Debug("Persisted set of storage slots", "accounts", len(res.hashes), "slots", slots, "nodes", nodes, "skipped", skipped, "bytes", bytes) + + // If this delivery completed the last pending task, forward the account task + // to the next chunk + if res.mainTask.pend == 0 { + s.forwardAccountTask(res.mainTask) + return + } + // Some accounts are still incomplete, leave as is for the storage and contract + // task assigners to pick up and fill. +} + +// processTrienodeHealResponse integrates an already validated trienode response +// into the healer tasks. +func (s *Syncer) processTrienodeHealResponse(res *trienodeHealResponse) { + for i, hash := range res.hashes { + node := res.nodes[i] + + // If the trie node was not delivered, reschedule it + if node == nil { + res.task.trieTasks[hash] = res.paths[i] + continue + } + // Push the trie node into the state syncer + s.trienodeHealSynced++ + s.trienodeHealBytes += common.StorageSize(len(node)) + + err := s.healer.scheduler.Process(trie.SyncResult{Hash: hash, Data: node}) + switch err { + case nil: + case trie.ErrAlreadyProcessed: + s.trienodeHealDups++ + case trie.ErrNotRequested: + s.trienodeHealNops++ + default: + log.Error("Invalid trienode processed", "hash", hash, "err", err) + } + } + batch := s.db.NewBatch() + if err := s.healer.scheduler.Commit(batch); err != nil { + log.Error("Failed to commit healing data", "err", err) + } + if err := batch.Write(); err != nil { + log.Crit("Failed to persist healing data", "err", err) + } + log.Debug("Persisted set of healing data", "type", "trienodes", "bytes", common.StorageSize(batch.ValueSize())) +} + +// processBytecodeHealResponse integrates an already validated bytecode response +// into the healer tasks. +func (s *Syncer) processBytecodeHealResponse(res *bytecodeHealResponse) { + for i, hash := range res.hashes { + node := res.codes[i] + + // If the trie node was not delivered, reschedule it + if node == nil { + res.task.codeTasks[hash] = struct{}{} + continue + } + // Push the trie node into the state syncer + s.bytecodeHealSynced++ + s.bytecodeHealBytes += common.StorageSize(len(node)) + + err := s.healer.scheduler.Process(trie.SyncResult{Hash: hash, Data: node}) + switch err { + case nil: + case trie.ErrAlreadyProcessed: + s.bytecodeHealDups++ + case trie.ErrNotRequested: + s.bytecodeHealNops++ + default: + log.Error("Invalid bytecode processed", "hash", hash, "err", err) + } + } + batch := s.db.NewBatch() + if err := s.healer.scheduler.Commit(batch); err != nil { + log.Error("Failed to commit healing data", "err", err) + } + if err := batch.Write(); err != nil { + log.Crit("Failed to persist healing data", "err", err) + } + log.Debug("Persisted set of healing data", "type", "bytecode", "bytes", common.StorageSize(batch.ValueSize())) +} + +// forwardAccountTask takes a filled account task and persists anything available +// into the database, after which it forwards the next account marker so that the +// task's next chunk may be filled. +func (s *Syncer) forwardAccountTask(task *accountTask) { + // Remove any pending delivery + res := task.res + if res == nil { + return // nothing to forward + } + task.res = nil + + // Iterate over all the accounts and gather all the incomplete trie nodes. A + // node is incomplete if we haven't yet filled it (sync was interrupted), or + // if we filled it in multiple chunks (storage trie), in which case the few + // nodes on the chunk boundaries are missing. + incompletes := light.NewNodeSet() + for i := range res.accounts { + // If the filling was interrupted, mark everything after as incomplete + if task.needCode[i] || task.needState[i] { + for j := i; j < len(res.accounts); j++ { + if err := res.trie.Prove(res.hashes[j][:], 0, incompletes); err != nil { + panic(err) // Account range was already proven, what happened + } + } + break + } + // Filling not interrupted until this point, mark incomplete if needs healing + if task.needHeal[i] { + if err := res.trie.Prove(res.hashes[i][:], 0, incompletes); err != nil { + panic(err) // Account range was already proven, what happened + } + } + } + // Persist every finalized trie node that's not on the boundary + batch := s.db.NewBatch() + + var ( + nodes int + skipped int + bytes common.StorageSize + ) + it := res.nodes.NewIterator(nil, nil) + for it.Next() { + // Boundary nodes are not written, since they are incomplete + if _, ok := res.bounds[common.BytesToHash(it.Key())]; ok { + skipped++ + continue + } + // Overflow nodes are not written, since they mess with another task + if _, err := res.overflow.Get(it.Key()); err == nil { + skipped++ + continue + } + // Accounts with split storage requests are incomplete + if _, err := incompletes.Get(it.Key()); err == nil { + skipped++ + continue + } + // Node is neither a boundary, not an incomplete account, persist to disk + batch.Put(it.Key(), it.Value()) + + bytes += common.StorageSize(common.HashLength + len(it.Value())) + nodes++ + } + it.Release() + + if err := batch.Write(); err != nil { + log.Crit("Failed to persist accounts", "err", err) + } + s.accountBytes += bytes + s.accountSynced += uint64(len(res.accounts)) + + log.Debug("Persisted range of accounts", "accounts", len(res.accounts), "nodes", nodes, "skipped", skipped, "bytes", bytes) + + // Task filling persisted, push it the chunk marker forward to the first + // account still missing data. + for i, hash := range res.hashes { + if task.needCode[i] || task.needState[i] { + return + } + task.Next = common.BigToHash(new(big.Int).Add(hash.Big(), big.NewInt(1))) + } + // All accounts marked as complete, track if the entire task is done + task.done = !res.cont +} + +// OnAccounts is a callback method to invoke when a range of accounts are +// received from a remote peer. +func (s *Syncer) OnAccounts(peer SyncPeer, id uint64, hashes []common.Hash, accounts [][]byte, proof [][]byte) error { + size := common.StorageSize(len(hashes) * common.HashLength) + for _, account := range accounts { + size += common.StorageSize(len(account)) + } + for _, node := range proof { + size += common.StorageSize(len(node)) + } + logger := peer.Log().New("reqid", id) + logger.Trace("Delivering range of accounts", "hashes", len(hashes), "accounts", len(accounts), "proofs", len(proof), "bytes", size) + + // Whether or not the response is valid, we can mark the peer as idle and + // notify the scheduler to assign a new task. If the response is invalid, + // we'll drop the peer in a bit. + s.lock.Lock() + if _, ok := s.peers[peer.ID()]; ok { + s.accountIdlers[peer.ID()] = struct{}{} + } + select { + case s.update <- struct{}{}: + default: + } + // Ensure the response is for a valid request + req, ok := s.accountReqs[id] + if !ok { + // Request stale, perhaps the peer timed out but came through in the end + logger.Warn("Unexpected account range packet") + s.lock.Unlock() + return nil + } + delete(s.accountReqs, id) + + // Clean up the request timeout timer, we'll see how to proceed further based + // on the actual delivered content + if !req.timeout.Stop() { + // The timeout is already triggered, and this request will be reverted+rescheduled + s.lock.Unlock() + return nil + } + + // Response is valid, but check if peer is signalling that it does not have + // the requested data. For account range queries that means the state being + // retrieved was either already pruned remotely, or the peer is not yet + // synced to our head. + if len(hashes) == 0 && len(accounts) == 0 && len(proof) == 0 { + logger.Debug("Peer rejected account range request", "root", s.root) + s.statelessPeers[peer.ID()] = struct{}{} + s.lock.Unlock() + + // Signal this request as failed, and ready for rescheduling + s.scheduleRevertAccountRequest(req) + return nil + } + root := s.root + s.lock.Unlock() + + // Reconstruct a partial trie from the response and verify it + keys := make([][]byte, len(hashes)) + for i, key := range hashes { + keys[i] = common.CopyBytes(key[:]) + } + nodes := make(light.NodeList, len(proof)) + for i, node := range proof { + nodes[i] = node + } + proofdb := nodes.NodeSet() + + var end []byte + if len(keys) > 0 { + end = keys[len(keys)-1] + } + db, tr, notary, cont, err := trie.VerifyRangeProof(root, req.origin[:], end, keys, accounts, proofdb) + if err != nil { + logger.Warn("Account range failed proof", "err", err) + // Signal this request as failed, and ready for rescheduling + s.scheduleRevertAccountRequest(req) + return err + } + // Partial trie reconstructed, send it to the scheduler for storage filling + bounds := make(map[common.Hash]struct{}) + + it := notary.Accessed().NewIterator(nil, nil) + for it.Next() { + bounds[common.BytesToHash(it.Key())] = struct{}{} + } + it.Release() + + accs := make([]*state.Account, len(accounts)) + for i, account := range accounts { + acc := new(state.Account) + if err := rlp.DecodeBytes(account, acc); err != nil { + panic(err) // We created these blobs, we must be able to decode them + } + accs[i] = acc + } + response := &accountResponse{ + task: req.task, + hashes: hashes, + accounts: accs, + nodes: db, + trie: tr, + bounds: bounds, + overflow: light.NewNodeSet(), + cont: cont, + } + select { + case s.accountResps <- response: + case <-req.cancel: + case <-req.stale: + } + return nil +} + +// OnByteCodes is a callback method to invoke when a batch of contract +// bytes codes are received from a remote peer. +func (s *Syncer) OnByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) error { + s.lock.RLock() + syncing := !s.snapped + s.lock.RUnlock() + + if syncing { + return s.onByteCodes(peer, id, bytecodes) + } + return s.onHealByteCodes(peer, id, bytecodes) +} + +// onByteCodes is a callback method to invoke when a batch of contract +// bytes codes are received from a remote peer in the syncing phase. +func (s *Syncer) onByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) error { + var size common.StorageSize + for _, code := range bytecodes { + size += common.StorageSize(len(code)) + } + logger := peer.Log().New("reqid", id) + logger.Trace("Delivering set of bytecodes", "bytecodes", len(bytecodes), "bytes", size) + + // Whether or not the response is valid, we can mark the peer as idle and + // notify the scheduler to assign a new task. If the response is invalid, + // we'll drop the peer in a bit. + s.lock.Lock() + if _, ok := s.peers[peer.ID()]; ok { + s.bytecodeIdlers[peer.ID()] = struct{}{} + } + select { + case s.update <- struct{}{}: + default: + } + // Ensure the response is for a valid request + req, ok := s.bytecodeReqs[id] + if !ok { + // Request stale, perhaps the peer timed out but came through in the end + logger.Warn("Unexpected bytecode packet") + s.lock.Unlock() + return nil + } + delete(s.bytecodeReqs, id) + + // Clean up the request timeout timer, we'll see how to proceed further based + // on the actual delivered content + if !req.timeout.Stop() { + // The timeout is already triggered, and this request will be reverted+rescheduled + s.lock.Unlock() + return nil + } + + // Response is valid, but check if peer is signalling that it does not have + // the requested data. For bytecode range queries that means the peer is not + // yet synced. + if len(bytecodes) == 0 { + logger.Debug("Peer rejected bytecode request") + s.statelessPeers[peer.ID()] = struct{}{} + s.lock.Unlock() + + // Signal this request as failed, and ready for rescheduling + s.scheduleRevertBytecodeRequest(req) + return nil + } + s.lock.Unlock() + + // Cross reference the requested bytecodes with the response to find gaps + // that the serving node is missing + hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) + hash := make([]byte, 32) + + codes := make([][]byte, len(req.hashes)) + for i, j := 0, 0; i < len(bytecodes); i++ { + // Find the next hash that we've been served, leaving misses with nils + hasher.Reset() + hasher.Write(bytecodes[i]) + hasher.Read(hash) + + for j < len(req.hashes) && !bytes.Equal(hash, req.hashes[j][:]) { + j++ + } + if j < len(req.hashes) { + codes[j] = bytecodes[i] + j++ + continue + } + // We've either ran out of hashes, or got unrequested data + logger.Warn("Unexpected bytecodes", "count", len(bytecodes)-i) + // Signal this request as failed, and ready for rescheduling + s.scheduleRevertBytecodeRequest(req) + return errors.New("unexpected bytecode") + } + // Response validated, send it to the scheduler for filling + response := &bytecodeResponse{ + task: req.task, + hashes: req.hashes, + codes: codes, + } + select { + case s.bytecodeResps <- response: + case <-req.cancel: + case <-req.stale: + } + return nil +} + +// OnStorage is a callback method to invoke when ranges of storage slots +// are received from a remote peer. +func (s *Syncer) OnStorage(peer SyncPeer, id uint64, hashes [][]common.Hash, slots [][][]byte, proof [][]byte) error { + // Gather some trace stats to aid in debugging issues + var ( + hashCount int + slotCount int + size common.StorageSize + ) + for _, hashset := range hashes { + size += common.StorageSize(common.HashLength * len(hashset)) + hashCount += len(hashset) + } + for _, slotset := range slots { + for _, slot := range slotset { + size += common.StorageSize(len(slot)) + } + slotCount += len(slotset) + } + for _, node := range proof { + size += common.StorageSize(len(node)) + } + logger := peer.Log().New("reqid", id) + logger.Trace("Delivering ranges of storage slots", "accounts", len(hashes), "hashes", hashCount, "slots", slotCount, "proofs", len(proof), "size", size) + + // Whether or not the response is valid, we can mark the peer as idle and + // notify the scheduler to assign a new task. If the response is invalid, + // we'll drop the peer in a bit. + s.lock.Lock() + if _, ok := s.peers[peer.ID()]; ok { + s.storageIdlers[peer.ID()] = struct{}{} + } + select { + case s.update <- struct{}{}: + default: + } + // Ensure the response is for a valid request + req, ok := s.storageReqs[id] + if !ok { + // Request stale, perhaps the peer timed out but came through in the end + logger.Warn("Unexpected storage ranges packet") + s.lock.Unlock() + return nil + } + delete(s.storageReqs, id) + + // Clean up the request timeout timer, we'll see how to proceed further based + // on the actual delivered content + if !req.timeout.Stop() { + // The timeout is already triggered, and this request will be reverted+rescheduled + s.lock.Unlock() + return nil + } + + // Reject the response if the hash sets and slot sets don't match, or if the + // peer sent more data than requested. + if len(hashes) != len(slots) { + s.lock.Unlock() + s.scheduleRevertStorageRequest(req) // reschedule request + logger.Warn("Hash and slot set size mismatch", "hashset", len(hashes), "slotset", len(slots)) + return errors.New("hash and slot set size mismatch") + } + if len(hashes) > len(req.accounts) { + s.lock.Unlock() + s.scheduleRevertStorageRequest(req) // reschedule request + logger.Warn("Hash set larger than requested", "hashset", len(hashes), "requested", len(req.accounts)) + return errors.New("hash set larger than requested") + } + // Response is valid, but check if peer is signalling that it does not have + // the requested data. For storage range queries that means the state being + // retrieved was either already pruned remotely, or the peer is not yet + // synced to our head. + if len(hashes) == 0 { + logger.Debug("Peer rejected storage request") + s.statelessPeers[peer.ID()] = struct{}{} + s.lock.Unlock() + s.scheduleRevertStorageRequest(req) // reschedule request + return nil + } + s.lock.Unlock() + + // Reconstruct the partial tries from the response and verify them + var ( + dbs = make([]ethdb.KeyValueStore, len(hashes)) + tries = make([]*trie.Trie, len(hashes)) + notary *trie.KeyValueNotary + cont bool + ) + for i := 0; i < len(hashes); i++ { + // Convert the keys and proofs into an internal format + keys := make([][]byte, len(hashes[i])) + for j, key := range hashes[i] { + keys[j] = common.CopyBytes(key[:]) + } + nodes := make(light.NodeList, 0, len(proof)) + if i == len(hashes)-1 { + for _, node := range proof { + nodes = append(nodes, node) + } + } + var err error + if len(nodes) == 0 { + // No proof has been attached, the response must cover the entire key + // space and hash to the origin root. + dbs[i], tries[i], _, _, err = trie.VerifyRangeProof(req.roots[i], nil, nil, keys, slots[i], nil) + if err != nil { + s.scheduleRevertStorageRequest(req) // reschedule request + logger.Warn("Storage slots failed proof", "err", err) + return err + } + } else { + // A proof was attached, the response is only partial, check that the + // returned data is indeed part of the storage trie + proofdb := nodes.NodeSet() + + var end []byte + if len(keys) > 0 { + end = keys[len(keys)-1] + } + dbs[i], tries[i], notary, cont, err = trie.VerifyRangeProof(req.roots[i], req.origin[:], end, keys, slots[i], proofdb) + if err != nil { + s.scheduleRevertStorageRequest(req) // reschedule request + logger.Warn("Storage range failed proof", "err", err) + return err + } + } + } + // Partial tries reconstructed, send them to the scheduler for storage filling + bounds := make(map[common.Hash]struct{}) + + if notary != nil { // if all contract storages are delivered in full, no notary will be created + it := notary.Accessed().NewIterator(nil, nil) + for it.Next() { + bounds[common.BytesToHash(it.Key())] = struct{}{} + } + it.Release() + } + response := &storageResponse{ + mainTask: req.mainTask, + subTask: req.subTask, + accounts: req.accounts, + roots: req.roots, + hashes: hashes, + slots: slots, + nodes: dbs, + tries: tries, + bounds: bounds, + overflow: light.NewNodeSet(), + cont: cont, + } + select { + case s.storageResps <- response: + case <-req.cancel: + case <-req.stale: + } + return nil +} + +// OnTrieNodes is a callback method to invoke when a batch of trie nodes +// are received from a remote peer. +func (s *Syncer) OnTrieNodes(peer SyncPeer, id uint64, trienodes [][]byte) error { + var size common.StorageSize + for _, node := range trienodes { + size += common.StorageSize(len(node)) + } + logger := peer.Log().New("reqid", id) + logger.Trace("Delivering set of healing trienodes", "trienodes", len(trienodes), "bytes", size) + + // Whether or not the response is valid, we can mark the peer as idle and + // notify the scheduler to assign a new task. If the response is invalid, + // we'll drop the peer in a bit. + s.lock.Lock() + if _, ok := s.peers[peer.ID()]; ok { + s.trienodeHealIdlers[peer.ID()] = struct{}{} + } + select { + case s.update <- struct{}{}: + default: + } + // Ensure the response is for a valid request + req, ok := s.trienodeHealReqs[id] + if !ok { + // Request stale, perhaps the peer timed out but came through in the end + logger.Warn("Unexpected trienode heal packet") + s.lock.Unlock() + return nil + } + delete(s.trienodeHealReqs, id) + + // Clean up the request timeout timer, we'll see how to proceed further based + // on the actual delivered content + if !req.timeout.Stop() { + // The timeout is already triggered, and this request will be reverted+rescheduled + s.lock.Unlock() + return nil + } + + // Response is valid, but check if peer is signalling that it does not have + // the requested data. For bytecode range queries that means the peer is not + // yet synced. + if len(trienodes) == 0 { + logger.Debug("Peer rejected trienode heal request") + s.statelessPeers[peer.ID()] = struct{}{} + s.lock.Unlock() + + // Signal this request as failed, and ready for rescheduling + s.scheduleRevertTrienodeHealRequest(req) + return nil + } + s.lock.Unlock() + + // Cross reference the requested trienodes with the response to find gaps + // that the serving node is missing + hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) + hash := make([]byte, 32) + + nodes := make([][]byte, len(req.hashes)) + for i, j := 0, 0; i < len(trienodes); i++ { + // Find the next hash that we've been served, leaving misses with nils + hasher.Reset() + hasher.Write(trienodes[i]) + hasher.Read(hash) + + for j < len(req.hashes) && !bytes.Equal(hash, req.hashes[j][:]) { + j++ + } + if j < len(req.hashes) { + nodes[j] = trienodes[i] + j++ + continue + } + // We've either ran out of hashes, or got unrequested data + logger.Warn("Unexpected healing trienodes", "count", len(trienodes)-i) + // Signal this request as failed, and ready for rescheduling + s.scheduleRevertTrienodeHealRequest(req) + return errors.New("unexpected healing trienode") + } + // Response validated, send it to the scheduler for filling + response := &trienodeHealResponse{ + task: req.task, + hashes: req.hashes, + paths: req.paths, + nodes: nodes, + } + select { + case s.trienodeHealResps <- response: + case <-req.cancel: + case <-req.stale: + } + return nil +} + +// onHealByteCodes is a callback method to invoke when a batch of contract +// bytes codes are received from a remote peer in the healing phase. +func (s *Syncer) onHealByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) error { + var size common.StorageSize + for _, code := range bytecodes { + size += common.StorageSize(len(code)) + } + logger := peer.Log().New("reqid", id) + logger.Trace("Delivering set of healing bytecodes", "bytecodes", len(bytecodes), "bytes", size) + + // Whether or not the response is valid, we can mark the peer as idle and + // notify the scheduler to assign a new task. If the response is invalid, + // we'll drop the peer in a bit. + s.lock.Lock() + if _, ok := s.peers[peer.ID()]; ok { + s.bytecodeHealIdlers[peer.ID()] = struct{}{} + } + select { + case s.update <- struct{}{}: + default: + } + // Ensure the response is for a valid request + req, ok := s.bytecodeHealReqs[id] + if !ok { + // Request stale, perhaps the peer timed out but came through in the end + logger.Warn("Unexpected bytecode heal packet") + s.lock.Unlock() + return nil + } + delete(s.bytecodeHealReqs, id) + + // Clean up the request timeout timer, we'll see how to proceed further based + // on the actual delivered content + if !req.timeout.Stop() { + // The timeout is already triggered, and this request will be reverted+rescheduled + s.lock.Unlock() + return nil + } + + // Response is valid, but check if peer is signalling that it does not have + // the requested data. For bytecode range queries that means the peer is not + // yet synced. + if len(bytecodes) == 0 { + logger.Debug("Peer rejected bytecode heal request") + s.statelessPeers[peer.ID()] = struct{}{} + s.lock.Unlock() + + // Signal this request as failed, and ready for rescheduling + s.scheduleRevertBytecodeHealRequest(req) + return nil + } + s.lock.Unlock() + + // Cross reference the requested bytecodes with the response to find gaps + // that the serving node is missing + hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) + hash := make([]byte, 32) + + codes := make([][]byte, len(req.hashes)) + for i, j := 0, 0; i < len(bytecodes); i++ { + // Find the next hash that we've been served, leaving misses with nils + hasher.Reset() + hasher.Write(bytecodes[i]) + hasher.Read(hash) + + for j < len(req.hashes) && !bytes.Equal(hash, req.hashes[j][:]) { + j++ + } + if j < len(req.hashes) { + codes[j] = bytecodes[i] + j++ + continue + } + // We've either ran out of hashes, or got unrequested data + logger.Warn("Unexpected healing bytecodes", "count", len(bytecodes)-i) + // Signal this request as failed, and ready for rescheduling + s.scheduleRevertBytecodeHealRequest(req) + return errors.New("unexpected healing bytecode") + } + // Response validated, send it to the scheduler for filling + response := &bytecodeHealResponse{ + task: req.task, + hashes: req.hashes, + codes: codes, + } + select { + case s.bytecodeHealResps <- response: + case <-req.cancel: + case <-req.stale: + } + return nil +} + +// hashSpace is the total size of the 256 bit hash space for accounts. +var hashSpace = new(big.Int).Exp(common.Big2, common.Big256, nil) + +// report calculates various status reports and provides it to the user. +func (s *Syncer) report(force bool) { + if len(s.tasks) > 0 { + s.reportSyncProgress(force) + return + } + s.reportHealProgress(force) +} + +// reportSyncProgress calculates various status reports and provides it to the user. +func (s *Syncer) reportSyncProgress(force bool) { + // Don't report all the events, just occasionally + if !force && time.Since(s.logTime) < 3*time.Second { + return + } + // Don't report anything until we have a meaningful progress + synced := s.accountBytes + s.bytecodeBytes + s.storageBytes + if synced == 0 { + return + } + accountGaps := new(big.Int) + for _, task := range s.tasks { + accountGaps.Add(accountGaps, new(big.Int).Sub(task.Last.Big(), task.Next.Big())) + } + accountFills := new(big.Int).Sub(hashSpace, accountGaps) + if accountFills.BitLen() == 0 { + return + } + s.logTime = time.Now() + estBytes := float64(new(big.Int).Div( + new(big.Int).Mul(new(big.Int).SetUint64(uint64(synced)), hashSpace), + accountFills, + ).Uint64()) + + elapsed := time.Since(s.startTime) + estTime := elapsed / time.Duration(synced) * time.Duration(estBytes) + + // Create a mega progress report + var ( + progress = fmt.Sprintf("%.2f%%", float64(synced)*100/estBytes) + accounts = fmt.Sprintf("%d@%v", s.accountSynced, s.accountBytes.TerminalString()) + storage = fmt.Sprintf("%d@%v", s.storageSynced, s.storageBytes.TerminalString()) + bytecode = fmt.Sprintf("%d@%v", s.bytecodeSynced, s.bytecodeBytes.TerminalString()) + ) + log.Info("State sync in progress", "synced", progress, "state", synced, + "accounts", accounts, "slots", storage, "codes", bytecode, "eta", common.PrettyDuration(estTime-elapsed)) +} + +// reportHealProgress calculates various status reports and provides it to the user. +func (s *Syncer) reportHealProgress(force bool) { + // Don't report all the events, just occasionally + if !force && time.Since(s.logTime) < 3*time.Second { + return + } + s.logTime = time.Now() + + // Create a mega progress report + var ( + trienode = fmt.Sprintf("%d@%v", s.trienodeHealSynced, s.trienodeHealBytes.TerminalString()) + bytecode = fmt.Sprintf("%d@%v", s.bytecodeHealSynced, s.bytecodeHealBytes.TerminalString()) + ) + log.Info("State heal in progress", "nodes", trienode, "codes", bytecode, + "pending", s.healer.scheduler.Pending()) +} diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go new file mode 100644 index 0000000000..3e9778dbc7 --- /dev/null +++ b/eth/protocols/snap/sync_test.go @@ -0,0 +1,1607 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package snap + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "fmt" + "math/big" + "sort" + "sync" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" + "golang.org/x/crypto/sha3" +) + +func TestHashing(t *testing.T) { + t.Parallel() + + var bytecodes = make([][]byte, 10) + for i := 0; i < len(bytecodes); i++ { + buf := make([]byte, 100) + rand.Read(buf) + bytecodes[i] = buf + } + var want, got string + var old = func() { + hasher := sha3.NewLegacyKeccak256() + for i := 0; i < len(bytecodes); i++ { + hasher.Reset() + hasher.Write(bytecodes[i]) + hash := hasher.Sum(nil) + got = fmt.Sprintf("%v\n%v", got, hash) + } + } + var new = func() { + hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) + var hash = make([]byte, 32) + for i := 0; i < len(bytecodes); i++ { + hasher.Reset() + hasher.Write(bytecodes[i]) + hasher.Read(hash) + want = fmt.Sprintf("%v\n%v", want, hash) + } + } + old() + new() + if want != got { + t.Errorf("want\n%v\ngot\n%v\n", want, got) + } +} + +func BenchmarkHashing(b *testing.B) { + var bytecodes = make([][]byte, 10000) + for i := 0; i < len(bytecodes); i++ { + buf := make([]byte, 100) + rand.Read(buf) + bytecodes[i] = buf + } + var old = func() { + hasher := sha3.NewLegacyKeccak256() + for i := 0; i < len(bytecodes); i++ { + hasher.Reset() + hasher.Write(bytecodes[i]) + hasher.Sum(nil) + } + } + var new = func() { + hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) + var hash = make([]byte, 32) + for i := 0; i < len(bytecodes); i++ { + hasher.Reset() + hasher.Write(bytecodes[i]) + hasher.Read(hash) + } + } + b.Run("old", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + old() + } + }) + b.Run("new", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + new() + } + }) +} + +type ( + accountHandlerFunc func(t *testPeer, requestId uint64, root common.Hash, origin common.Hash, limit common.Hash, cap uint64) error + storageHandlerFunc func(t *testPeer, requestId uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, max uint64) error + trieHandlerFunc func(t *testPeer, requestId uint64, root common.Hash, paths []TrieNodePathSet, cap uint64) error + codeHandlerFunc func(t *testPeer, id uint64, hashes []common.Hash, max uint64) error +) + +type testPeer struct { + id string + test *testing.T + remote *Syncer + logger log.Logger + accountTrie *trie.Trie + accountValues entrySlice + storageTries map[common.Hash]*trie.Trie + storageValues map[common.Hash]entrySlice + + accountRequestHandler accountHandlerFunc + storageRequestHandler storageHandlerFunc + trieRequestHandler trieHandlerFunc + codeRequestHandler codeHandlerFunc + term func() +} + +func newTestPeer(id string, t *testing.T, term func()) *testPeer { + peer := &testPeer{ + id: id, + test: t, + logger: log.New("id", id), + accountRequestHandler: defaultAccountRequestHandler, + trieRequestHandler: defaultTrieRequestHandler, + storageRequestHandler: defaultStorageRequestHandler, + codeRequestHandler: defaultCodeRequestHandler, + term: term, + } + //stderrHandler := log.StreamHandler(os.Stderr, log.TerminalFormat(true)) + //peer.logger.SetHandler(stderrHandler) + return peer +} + +func (t *testPeer) ID() string { return t.id } +func (t *testPeer) Log() log.Logger { return t.logger } + +func (t *testPeer) RequestAccountRange(id uint64, root, origin, limit common.Hash, bytes uint64) error { + t.logger.Trace("Fetching range of accounts", "reqid", id, "root", root, "origin", origin, "limit", limit, "bytes", common.StorageSize(bytes)) + go t.accountRequestHandler(t, id, root, origin, limit, bytes) + return nil +} + +func (t *testPeer) RequestTrieNodes(id uint64, root common.Hash, paths []TrieNodePathSet, bytes uint64) error { + t.logger.Trace("Fetching set of trie nodes", "reqid", id, "root", root, "pathsets", len(paths), "bytes", common.StorageSize(bytes)) + go t.trieRequestHandler(t, id, root, paths, bytes) + return nil +} + +func (t *testPeer) RequestStorageRanges(id uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, bytes uint64) error { + if len(accounts) == 1 && origin != nil { + t.logger.Trace("Fetching range of large storage slots", "reqid", id, "root", root, "account", accounts[0], "origin", common.BytesToHash(origin), "limit", common.BytesToHash(limit), "bytes", common.StorageSize(bytes)) + } else { + t.logger.Trace("Fetching ranges of small storage slots", "reqid", id, "root", root, "accounts", len(accounts), "first", accounts[0], "bytes", common.StorageSize(bytes)) + } + go t.storageRequestHandler(t, id, root, accounts, origin, limit, bytes) + return nil +} + +func (t *testPeer) RequestByteCodes(id uint64, hashes []common.Hash, bytes uint64) error { + t.logger.Trace("Fetching set of byte codes", "reqid", id, "hashes", len(hashes), "bytes", common.StorageSize(bytes)) + go t.codeRequestHandler(t, id, hashes, bytes) + return nil +} + +// defaultTrieRequestHandler is a well-behaving handler for trie healing requests +func defaultTrieRequestHandler(t *testPeer, requestId uint64, root common.Hash, paths []TrieNodePathSet, cap uint64) error { + // Pass the response + var nodes [][]byte + for _, pathset := range paths { + switch len(pathset) { + case 1: + blob, _, err := t.accountTrie.TryGetNode(pathset[0]) + if err != nil { + t.logger.Info("Error handling req", "error", err) + break + } + nodes = append(nodes, blob) + default: + account := t.storageTries[(common.BytesToHash(pathset[0]))] + for _, path := range pathset[1:] { + blob, _, err := account.TryGetNode(path) + if err != nil { + t.logger.Info("Error handling req", "error", err) + break + } + nodes = append(nodes, blob) + } + } + } + t.remote.OnTrieNodes(t, requestId, nodes) + return nil +} + +// defaultAccountRequestHandler is a well-behaving handler for AccountRangeRequests +func defaultAccountRequestHandler(t *testPeer, id uint64, root common.Hash, origin common.Hash, limit common.Hash, cap uint64) error { + keys, vals, proofs := createAccountRequestResponse(t, root, origin, limit, cap) + if err := t.remote.OnAccounts(t, id, keys, vals, proofs); err != nil { + t.test.Errorf("Remote side rejected our delivery: %v", err) + t.term() + return err + } + return nil +} + +func createAccountRequestResponse(t *testPeer, root common.Hash, origin common.Hash, limit common.Hash, cap uint64) (keys []common.Hash, vals [][]byte, proofs [][]byte) { + var size uint64 + if limit == (common.Hash{}) { + limit = common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + } + for _, entry := range t.accountValues { + if size > cap { + break + } + if bytes.Compare(origin[:], entry.k) <= 0 { + keys = append(keys, common.BytesToHash(entry.k)) + vals = append(vals, entry.v) + size += uint64(32 + len(entry.v)) + } + // If we've exceeded the request threshold, abort + if bytes.Compare(entry.k, limit[:]) >= 0 { + break + } + } + // Unless we send the entire trie, we need to supply proofs + // Actually, we need to supply proofs either way! This seems to be an implementation + // quirk in go-ethereum + proof := light.NewNodeSet() + if err := t.accountTrie.Prove(origin[:], 0, proof); err != nil { + t.logger.Error("Could not prove inexistence of origin", "origin", origin, "error", err) + } + if len(keys) > 0 { + lastK := (keys[len(keys)-1])[:] + if err := t.accountTrie.Prove(lastK, 0, proof); err != nil { + t.logger.Error("Could not prove last item", "error", err) + } + } + for _, blob := range proof.NodeList() { + proofs = append(proofs, blob) + } + return keys, vals, proofs +} + +// defaultStorageRequestHandler is a well-behaving storage request handler +func defaultStorageRequestHandler(t *testPeer, requestId uint64, root common.Hash, accounts []common.Hash, bOrigin, bLimit []byte, max uint64) error { + hashes, slots, proofs := createStorageRequestResponse(t, root, accounts, bOrigin, bLimit, max) + if err := t.remote.OnStorage(t, requestId, hashes, slots, proofs); err != nil { + t.test.Errorf("Remote side rejected our delivery: %v", err) + t.term() + } + return nil +} + +func defaultCodeRequestHandler(t *testPeer, id uint64, hashes []common.Hash, max uint64) error { + var bytecodes [][]byte + for _, h := range hashes { + bytecodes = append(bytecodes, getCodeByHash(h)) + } + if err := t.remote.OnByteCodes(t, id, bytecodes); err != nil { + t.test.Errorf("Remote side rejected our delivery: %v", err) + t.term() + } + return nil +} + +func createStorageRequestResponse(t *testPeer, root common.Hash, accounts []common.Hash, origin, limit []byte, max uint64) (hashes [][]common.Hash, slots [][][]byte, proofs [][]byte) { + var size uint64 + for _, account := range accounts { + // The first account might start from a different origin and end sooner + var originHash common.Hash + if len(origin) > 0 { + originHash = common.BytesToHash(origin) + } + var limitHash = common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + if len(limit) > 0 { + limitHash = common.BytesToHash(limit) + } + var ( + keys []common.Hash + vals [][]byte + abort bool + ) + for _, entry := range t.storageValues[account] { + if size >= max { + abort = true + break + } + if bytes.Compare(entry.k, originHash[:]) < 0 { + continue + } + keys = append(keys, common.BytesToHash(entry.k)) + vals = append(vals, entry.v) + size += uint64(32 + len(entry.v)) + if bytes.Compare(entry.k, limitHash[:]) >= 0 { + break + } + } + hashes = append(hashes, keys) + slots = append(slots, vals) + + // Generate the Merkle proofs for the first and last storage slot, but + // only if the response was capped. If the entire storage trie included + // in the response, no need for any proofs. + if originHash != (common.Hash{}) || abort { + // If we're aborting, we need to prove the first and last item + // This terminates the response (and thus the loop) + proof := light.NewNodeSet() + stTrie := t.storageTries[account] + + // Here's a potential gotcha: when constructing the proof, we cannot + // use the 'origin' slice directly, but must use the full 32-byte + // hash form. + if err := stTrie.Prove(originHash[:], 0, proof); err != nil { + t.logger.Error("Could not prove inexistence of origin", "origin", originHash, "error", err) + } + if len(keys) > 0 { + lastK := (keys[len(keys)-1])[:] + if err := stTrie.Prove(lastK, 0, proof); err != nil { + t.logger.Error("Could not prove last item", "error", err) + } + } + for _, blob := range proof.NodeList() { + proofs = append(proofs, blob) + } + break + } + } + return hashes, slots, proofs +} + +// the createStorageRequestResponseAlwaysProve tests a cornercase, where it always +// supplies the proof for the last account, even if it is 'complete'.h +func createStorageRequestResponseAlwaysProve(t *testPeer, root common.Hash, accounts []common.Hash, bOrigin, bLimit []byte, max uint64) (hashes [][]common.Hash, slots [][][]byte, proofs [][]byte) { + var size uint64 + max = max * 3 / 4 + + var origin common.Hash + if len(bOrigin) > 0 { + origin = common.BytesToHash(bOrigin) + } + var exit bool + for i, account := range accounts { + var keys []common.Hash + var vals [][]byte + for _, entry := range t.storageValues[account] { + if bytes.Compare(entry.k, origin[:]) < 0 { + exit = true + } + keys = append(keys, common.BytesToHash(entry.k)) + vals = append(vals, entry.v) + size += uint64(32 + len(entry.v)) + if size > max { + exit = true + } + } + if i == len(accounts)-1 { + exit = true + } + hashes = append(hashes, keys) + slots = append(slots, vals) + + if exit { + // If we're aborting, we need to prove the first and last item + // This terminates the response (and thus the loop) + proof := light.NewNodeSet() + stTrie := t.storageTries[account] + + // Here's a potential gotcha: when constructing the proof, we cannot + // use the 'origin' slice directly, but must use the full 32-byte + // hash form. + if err := stTrie.Prove(origin[:], 0, proof); err != nil { + t.logger.Error("Could not prove inexistence of origin", "origin", origin, + "error", err) + } + if len(keys) > 0 { + lastK := (keys[len(keys)-1])[:] + if err := stTrie.Prove(lastK, 0, proof); err != nil { + t.logger.Error("Could not prove last item", "error", err) + } + } + for _, blob := range proof.NodeList() { + proofs = append(proofs, blob) + } + break + } + } + return hashes, slots, proofs +} + +// emptyRequestAccountRangeFn is a rejects AccountRangeRequests +func emptyRequestAccountRangeFn(t *testPeer, requestId uint64, root common.Hash, origin common.Hash, limit common.Hash, cap uint64) error { + t.remote.OnAccounts(t, requestId, nil, nil, nil) + return nil +} + +func nonResponsiveRequestAccountRangeFn(t *testPeer, requestId uint64, root common.Hash, origin common.Hash, limit common.Hash, cap uint64) error { + return nil +} + +func emptyTrieRequestHandler(t *testPeer, requestId uint64, root common.Hash, paths []TrieNodePathSet, cap uint64) error { + t.remote.OnTrieNodes(t, requestId, nil) + return nil +} + +func nonResponsiveTrieRequestHandler(t *testPeer, requestId uint64, root common.Hash, paths []TrieNodePathSet, cap uint64) error { + return nil +} + +func emptyStorageRequestHandler(t *testPeer, requestId uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, max uint64) error { + t.remote.OnStorage(t, requestId, nil, nil, nil) + return nil +} + +func nonResponsiveStorageRequestHandler(t *testPeer, requestId uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, max uint64) error { + return nil +} + +func proofHappyStorageRequestHandler(t *testPeer, requestId uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, max uint64) error { + hashes, slots, proofs := createStorageRequestResponseAlwaysProve(t, root, accounts, origin, limit, max) + if err := t.remote.OnStorage(t, requestId, hashes, slots, proofs); err != nil { + t.test.Errorf("Remote side rejected our delivery: %v", err) + t.term() + } + return nil +} + +//func emptyCodeRequestHandler(t *testPeer, id uint64, hashes []common.Hash, max uint64) error { +// var bytecodes [][]byte +// t.remote.OnByteCodes(t, id, bytecodes) +// return nil +//} + +func corruptCodeRequestHandler(t *testPeer, id uint64, hashes []common.Hash, max uint64) error { + var bytecodes [][]byte + for _, h := range hashes { + // Send back the hashes + bytecodes = append(bytecodes, h[:]) + } + if err := t.remote.OnByteCodes(t, id, bytecodes); err != nil { + t.logger.Info("remote error on delivery (as expected)", "error", err) + // Mimic the real-life handler, which drops a peer on errors + t.remote.Unregister(t.id) + } + return nil +} + +func cappedCodeRequestHandler(t *testPeer, id uint64, hashes []common.Hash, max uint64) error { + var bytecodes [][]byte + for _, h := range hashes[:1] { + bytecodes = append(bytecodes, getCodeByHash(h)) + } + // Missing bytecode can be retrieved again, no error expected + if err := t.remote.OnByteCodes(t, id, bytecodes); err != nil { + t.test.Errorf("Remote side rejected our delivery: %v", err) + t.term() + } + return nil +} + +// starvingStorageRequestHandler is somewhat well-behaving storage handler, but it caps the returned results to be very small +func starvingStorageRequestHandler(t *testPeer, requestId uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, max uint64) error { + return defaultStorageRequestHandler(t, requestId, root, accounts, origin, limit, 500) +} + +func starvingAccountRequestHandler(t *testPeer, requestId uint64, root common.Hash, origin common.Hash, limit common.Hash, cap uint64) error { + return defaultAccountRequestHandler(t, requestId, root, origin, limit, 500) +} + +//func misdeliveringAccountRequestHandler(t *testPeer, requestId uint64, root common.Hash, origin common.Hash, cap uint64) error { +// return defaultAccountRequestHandler(t, requestId-1, root, origin, 500) +//} + +func corruptAccountRequestHandler(t *testPeer, requestId uint64, root common.Hash, origin common.Hash, limit common.Hash, cap uint64) error { + hashes, accounts, proofs := createAccountRequestResponse(t, root, origin, limit, cap) + if len(proofs) > 0 { + proofs = proofs[1:] + } + if err := t.remote.OnAccounts(t, requestId, hashes, accounts, proofs); err != nil { + t.logger.Info("remote error on delivery (as expected)", "error", err) + // Mimic the real-life handler, which drops a peer on errors + t.remote.Unregister(t.id) + } + return nil +} + +// corruptStorageRequestHandler doesn't provide good proofs +func corruptStorageRequestHandler(t *testPeer, requestId uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, max uint64) error { + hashes, slots, proofs := createStorageRequestResponse(t, root, accounts, origin, limit, max) + if len(proofs) > 0 { + proofs = proofs[1:] + } + if err := t.remote.OnStorage(t, requestId, hashes, slots, proofs); err != nil { + t.logger.Info("remote error on delivery (as expected)", "error", err) + // Mimic the real-life handler, which drops a peer on errors + t.remote.Unregister(t.id) + } + return nil +} + +func noProofStorageRequestHandler(t *testPeer, requestId uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, max uint64) error { + hashes, slots, _ := createStorageRequestResponse(t, root, accounts, origin, limit, max) + if err := t.remote.OnStorage(t, requestId, hashes, slots, nil); err != nil { + t.logger.Info("remote error on delivery (as expected)", "error", err) + // Mimic the real-life handler, which drops a peer on errors + t.remote.Unregister(t.id) + } + return nil +} + +// TestSyncBloatedProof tests a scenario where we provide only _one_ value, but +// also ship the entire trie inside the proof. If the attack is successful, +// the remote side does not do any follow-up requests +func TestSyncBloatedProof(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems := makeAccountTrieNoStorage(100) + source := newTestPeer("source", t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + + source.accountRequestHandler = func(t *testPeer, requestId uint64, root common.Hash, origin common.Hash, limit common.Hash, cap uint64) error { + var ( + proofs [][]byte + keys []common.Hash + vals [][]byte + ) + // The values + for _, entry := range t.accountValues { + if bytes.Compare(entry.k, origin[:]) < 0 { + continue + } + if bytes.Compare(entry.k, limit[:]) > 0 { + continue + } + keys = append(keys, common.BytesToHash(entry.k)) + vals = append(vals, entry.v) + } + // The proofs + proof := light.NewNodeSet() + if err := t.accountTrie.Prove(origin[:], 0, proof); err != nil { + t.logger.Error("Could not prove origin", "origin", origin, "error", err) + } + // The bloat: add proof of every single element + for _, entry := range t.accountValues { + if err := t.accountTrie.Prove(entry.k, 0, proof); err != nil { + t.logger.Error("Could not prove item", "error", err) + } + } + // And remove one item from the elements + if len(keys) > 2 { + keys = append(keys[:1], keys[2:]...) + vals = append(vals[:1], vals[2:]...) + } + for _, blob := range proof.NodeList() { + proofs = append(proofs, blob) + } + if err := t.remote.OnAccounts(t, requestId, keys, vals, proofs); err != nil { + t.logger.Info("remote error on delivery (as expected)", "error", err) + t.term() + // This is actually correct, signal to exit the test successfully + } + return nil + } + syncer := setupSyncer(source) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err == nil { + t.Fatal("No error returned from incomplete/cancelled sync") + } +} + +func setupSyncer(peers ...*testPeer) *Syncer { + stateDb := rawdb.NewMemoryDatabase() + syncer := NewSyncer(stateDb) + for _, peer := range peers { + syncer.Register(peer) + peer.remote = syncer + } + return syncer +} + +// TestSync tests a basic sync with one peer +func TestSync(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems := makeAccountTrieNoStorage(100) + + mkSource := func(name string) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + return source + } + syncer := setupSyncer(mkSource("source")) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +// TestSyncTinyTriePanic tests a basic sync with one peer, and a tiny trie. This caused a +// panic within the prover +func TestSyncTinyTriePanic(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems := makeAccountTrieNoStorage(1) + + mkSource := func(name string) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + return source + } + syncer := setupSyncer(mkSource("source")) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +// TestMultiSync tests a basic sync with multiple peers +func TestMultiSync(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems := makeAccountTrieNoStorage(100) + + mkSource := func(name string) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + return source + } + syncer := setupSyncer(mkSource("sourceA"), mkSource("sourceB")) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +// TestSyncWithStorage tests basic sync using accounts + storage + code +func TestSyncWithStorage(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(3, 3000, true, false) + + mkSource := func(name string) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + source.storageTries = storageTries + source.storageValues = storageElems + return source + } + syncer := setupSyncer(mkSource("sourceA")) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +// TestMultiSyncManyUseless contains one good peer, and many which doesn't return anything valuable at all +func TestMultiSyncManyUseless(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false) + + mkSource := func(name string, noAccount, noStorage, noTrieNode bool) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + source.storageTries = storageTries + source.storageValues = storageElems + + if !noAccount { + source.accountRequestHandler = emptyRequestAccountRangeFn + } + if !noStorage { + source.storageRequestHandler = emptyStorageRequestHandler + } + if !noTrieNode { + source.trieRequestHandler = emptyTrieRequestHandler + } + return source + } + + syncer := setupSyncer( + mkSource("full", true, true, true), + mkSource("noAccounts", false, true, true), + mkSource("noStorage", true, false, true), + mkSource("noTrie", true, true, false), + ) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +// TestMultiSyncManyUseless contains one good peer, and many which doesn't return anything valuable at all +func TestMultiSyncManyUselessWithLowTimeout(t *testing.T) { + // We're setting the timeout to very low, to increase the chance of the timeout + // being triggered. This was previously a cause of panic, when a response + // arrived simultaneously as a timeout was triggered. + defer func(old time.Duration) { requestTimeout = old }(requestTimeout) + requestTimeout = time.Millisecond + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false) + + mkSource := func(name string, noAccount, noStorage, noTrieNode bool) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + source.storageTries = storageTries + source.storageValues = storageElems + + if !noAccount { + source.accountRequestHandler = emptyRequestAccountRangeFn + } + if !noStorage { + source.storageRequestHandler = emptyStorageRequestHandler + } + if !noTrieNode { + source.trieRequestHandler = emptyTrieRequestHandler + } + return source + } + + syncer := setupSyncer( + mkSource("full", true, true, true), + mkSource("noAccounts", false, true, true), + mkSource("noStorage", true, false, true), + mkSource("noTrie", true, true, false), + ) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +// TestMultiSyncManyUnresponsive contains one good peer, and many which doesn't respond at all +func TestMultiSyncManyUnresponsive(t *testing.T) { + // We're setting the timeout to very low, to make the test run a bit faster + defer func(old time.Duration) { requestTimeout = old }(requestTimeout) + requestTimeout = time.Millisecond + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false) + + mkSource := func(name string, noAccount, noStorage, noTrieNode bool) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + source.storageTries = storageTries + source.storageValues = storageElems + + if !noAccount { + source.accountRequestHandler = nonResponsiveRequestAccountRangeFn + } + if !noStorage { + source.storageRequestHandler = nonResponsiveStorageRequestHandler + } + if !noTrieNode { + source.trieRequestHandler = nonResponsiveTrieRequestHandler + } + return source + } + + syncer := setupSyncer( + mkSource("full", true, true, true), + mkSource("noAccounts", false, true, true), + mkSource("noStorage", true, false, true), + mkSource("noTrie", true, true, false), + ) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +func checkStall(t *testing.T, term func()) chan struct{} { + testDone := make(chan struct{}) + go func() { + select { + case <-time.After(time.Minute): // TODO(karalabe): Make tests smaller, this is too much + t.Log("Sync stalled") + term() + case <-testDone: + return + } + }() + return testDone +} + +// TestSyncBoundaryAccountTrie tests sync against a few normal peers, but the +// account trie has a few boundary elements. +func TestSyncBoundaryAccountTrie(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems := makeBoundaryAccountTrie(3000) + + mkSource := func(name string) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + return source + } + syncer := setupSyncer( + mkSource("peer-a"), + mkSource("peer-b"), + ) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +// TestSyncNoStorageAndOneCappedPeer tests sync using accounts and no storage, where one peer is +// consistently returning very small results +func TestSyncNoStorageAndOneCappedPeer(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems := makeAccountTrieNoStorage(3000) + + mkSource := func(name string, slow bool) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + + if slow { + source.accountRequestHandler = starvingAccountRequestHandler + } + return source + } + + syncer := setupSyncer( + mkSource("nice-a", false), + mkSource("nice-b", false), + mkSource("nice-c", false), + mkSource("capped", true), + ) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +// TestSyncNoStorageAndOneCodeCorruptPeer has one peer which doesn't deliver +// code requests properly. +func TestSyncNoStorageAndOneCodeCorruptPeer(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems := makeAccountTrieNoStorage(3000) + + mkSource := func(name string, codeFn codeHandlerFunc) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + source.codeRequestHandler = codeFn + return source + } + // One is capped, one is corrupt. If we don't use a capped one, there's a 50% + // chance that the full set of codes requested are sent only to the + // non-corrupt peer, which delivers everything in one go, and makes the + // test moot + syncer := setupSyncer( + mkSource("capped", cappedCodeRequestHandler), + mkSource("corrupt", corruptCodeRequestHandler), + ) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +func TestSyncNoStorageAndOneAccountCorruptPeer(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems := makeAccountTrieNoStorage(3000) + + mkSource := func(name string, accFn accountHandlerFunc) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + source.accountRequestHandler = accFn + return source + } + // One is capped, one is corrupt. If we don't use a capped one, there's a 50% + // chance that the full set of codes requested are sent only to the + // non-corrupt peer, which delivers everything in one go, and makes the + // test moot + syncer := setupSyncer( + mkSource("capped", defaultAccountRequestHandler), + mkSource("corrupt", corruptAccountRequestHandler), + ) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +// TestSyncNoStorageAndOneCodeCappedPeer has one peer which delivers code hashes +// one by one +func TestSyncNoStorageAndOneCodeCappedPeer(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems := makeAccountTrieNoStorage(3000) + + mkSource := func(name string, codeFn codeHandlerFunc) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + source.codeRequestHandler = codeFn + return source + } + // Count how many times it's invoked. Remember, there are only 8 unique hashes, + // so it shouldn't be more than that + var counter int + syncer := setupSyncer( + mkSource("capped", func(t *testPeer, id uint64, hashes []common.Hash, max uint64) error { + counter++ + return cappedCodeRequestHandler(t, id, hashes, max) + }), + ) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + // There are only 8 unique hashes, and 3K accounts. However, the code + // deduplication is per request batch. If it were a perfect global dedup, + // we would expect only 8 requests. If there were no dedup, there would be + // 3k requests. + // We expect somewhere below 100 requests for these 8 unique hashes. + if threshold := 100; counter > threshold { + t.Fatalf("Error, expected < %d invocations, got %d", threshold, counter) + } + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +// TestSyncBoundaryStorageTrie tests sync against a few normal peers, but the +// storage trie has a few boundary elements. +func TestSyncBoundaryStorageTrie(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(10, 1000, false, true) + + mkSource := func(name string) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + source.storageTries = storageTries + source.storageValues = storageElems + return source + } + syncer := setupSyncer( + mkSource("peer-a"), + mkSource("peer-b"), + ) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +// TestSyncWithStorageAndOneCappedPeer tests sync using accounts + storage, where one peer is +// consistently returning very small results +func TestSyncWithStorageAndOneCappedPeer(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(300, 1000, false, false) + + mkSource := func(name string, slow bool) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + source.storageTries = storageTries + source.storageValues = storageElems + + if slow { + source.storageRequestHandler = starvingStorageRequestHandler + } + return source + } + + syncer := setupSyncer( + mkSource("nice-a", false), + mkSource("slow", true), + ) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +// TestSyncWithStorageAndCorruptPeer tests sync using accounts + storage, where one peer is +// sometimes sending bad proofs +func TestSyncWithStorageAndCorruptPeer(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false) + + mkSource := func(name string, handler storageHandlerFunc) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + source.storageTries = storageTries + source.storageValues = storageElems + source.storageRequestHandler = handler + return source + } + + syncer := setupSyncer( + mkSource("nice-a", defaultStorageRequestHandler), + mkSource("nice-b", defaultStorageRequestHandler), + mkSource("nice-c", defaultStorageRequestHandler), + mkSource("corrupt", corruptStorageRequestHandler), + ) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +func TestSyncWithStorageAndNonProvingPeer(t *testing.T) { + t.Parallel() + + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false) + + mkSource := func(name string, handler storageHandlerFunc) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + source.storageTries = storageTries + source.storageValues = storageElems + source.storageRequestHandler = handler + return source + } + syncer := setupSyncer( + mkSource("nice-a", defaultStorageRequestHandler), + mkSource("nice-b", defaultStorageRequestHandler), + mkSource("nice-c", defaultStorageRequestHandler), + mkSource("corrupt", noProofStorageRequestHandler), + ) + done := checkStall(t, term) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + close(done) + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +// TestSyncWithStorage tests basic sync using accounts + storage + code, against +// a peer who insists on delivering full storage sets _and_ proofs. This triggered +// an error, where the recipient erroneously clipped the boundary nodes, but +// did not mark the account for healing. +func TestSyncWithStorageMisbehavingProve(t *testing.T) { + t.Parallel() + var ( + once sync.Once + cancel = make(chan struct{}) + term = func() { + once.Do(func() { + close(cancel) + }) + } + ) + sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorageWithUniqueStorage(10, 30, false) + + mkSource := func(name string) *testPeer { + source := newTestPeer(name, t, term) + source.accountTrie = sourceAccountTrie + source.accountValues = elems + source.storageTries = storageTries + source.storageValues = storageElems + source.storageRequestHandler = proofHappyStorageRequestHandler + return source + } + syncer := setupSyncer(mkSource("sourceA")) + if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil { + t.Fatalf("sync failed: %v", err) + } + verifyTrie(syncer.db, sourceAccountTrie.Hash(), t) +} + +type kv struct { + k, v []byte +} + +// Some helpers for sorting +type entrySlice []*kv + +func (p entrySlice) Len() int { return len(p) } +func (p entrySlice) Less(i, j int) bool { return bytes.Compare(p[i].k, p[j].k) < 0 } +func (p entrySlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +func key32(i uint64) []byte { + key := make([]byte, 32) + binary.LittleEndian.PutUint64(key, i) + return key +} + +var ( + codehashes = []common.Hash{ + crypto.Keccak256Hash([]byte{0}), + crypto.Keccak256Hash([]byte{1}), + crypto.Keccak256Hash([]byte{2}), + crypto.Keccak256Hash([]byte{3}), + crypto.Keccak256Hash([]byte{4}), + crypto.Keccak256Hash([]byte{5}), + crypto.Keccak256Hash([]byte{6}), + crypto.Keccak256Hash([]byte{7}), + } +) + +// getCodeHash returns a pseudo-random code hash +func getCodeHash(i uint64) []byte { + h := codehashes[int(i)%len(codehashes)] + return common.CopyBytes(h[:]) +} + +// getCodeByHash convenience function to lookup the code from the code hash +func getCodeByHash(hash common.Hash) []byte { + if hash == emptyCode { + return nil + } + for i, h := range codehashes { + if h == hash { + return []byte{byte(i)} + } + } + return nil +} + +// makeAccountTrieNoStorage spits out a trie, along with the leafs +func makeAccountTrieNoStorage(n int) (*trie.Trie, entrySlice) { + db := trie.NewDatabase(rawdb.NewMemoryDatabase()) + accTrie, _ := trie.New(common.Hash{}, db) + var entries entrySlice + for i := uint64(1); i <= uint64(n); i++ { + value, _ := rlp.EncodeToBytes(state.Account{ + Nonce: i, + Balance: big.NewInt(int64(i)), + Root: emptyRoot, + CodeHash: getCodeHash(i), + }) + key := key32(i) + elem := &kv{key, value} + accTrie.Update(elem.k, elem.v) + entries = append(entries, elem) + } + sort.Sort(entries) + accTrie.Commit(nil) + return accTrie, entries +} + +// makeBoundaryAccountTrie constructs an account trie. Instead of filling +// accounts normally, this function will fill a few accounts which have +// boundary hash. +func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) { + var ( + entries entrySlice + boundaries []common.Hash + + db = trie.NewDatabase(rawdb.NewMemoryDatabase()) + trie, _ = trie.New(common.Hash{}, db) + ) + // Initialize boundaries + var next common.Hash + step := new(big.Int).Sub( + new(big.Int).Div( + new(big.Int).Exp(common.Big2, common.Big256, nil), + big.NewInt(accountConcurrency), + ), common.Big1, + ) + for i := 0; i < accountConcurrency; i++ { + last := common.BigToHash(new(big.Int).Add(next.Big(), step)) + if i == accountConcurrency-1 { + last = common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + } + boundaries = append(boundaries, last) + next = common.BigToHash(new(big.Int).Add(last.Big(), common.Big1)) + } + // Fill boundary accounts + for i := 0; i < len(boundaries); i++ { + value, _ := rlp.EncodeToBytes(state.Account{ + Nonce: uint64(0), + Balance: big.NewInt(int64(i)), + Root: emptyRoot, + CodeHash: getCodeHash(uint64(i)), + }) + elem := &kv{boundaries[i].Bytes(), value} + trie.Update(elem.k, elem.v) + entries = append(entries, elem) + } + // Fill other accounts if required + for i := uint64(1); i <= uint64(n); i++ { + value, _ := rlp.EncodeToBytes(state.Account{ + Nonce: i, + Balance: big.NewInt(int64(i)), + Root: emptyRoot, + CodeHash: getCodeHash(i), + }) + elem := &kv{key32(i), value} + trie.Update(elem.k, elem.v) + entries = append(entries, elem) + } + sort.Sort(entries) + trie.Commit(nil) + return trie, entries +} + +// makeAccountTrieWithStorageWithUniqueStorage creates an account trie where each accounts +// has a unique storage set. +func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool) (*trie.Trie, entrySlice, map[common.Hash]*trie.Trie, map[common.Hash]entrySlice) { + var ( + db = trie.NewDatabase(rawdb.NewMemoryDatabase()) + accTrie, _ = trie.New(common.Hash{}, db) + entries entrySlice + storageTries = make(map[common.Hash]*trie.Trie) + storageEntries = make(map[common.Hash]entrySlice) + ) + // Create n accounts in the trie + for i := uint64(1); i <= uint64(accounts); i++ { + key := key32(i) + codehash := emptyCode[:] + if code { + codehash = getCodeHash(i) + } + // Create a storage trie + stTrie, stEntries := makeStorageTrieWithSeed(uint64(slots), i, db) + stRoot := stTrie.Hash() + stTrie.Commit(nil) + value, _ := rlp.EncodeToBytes(state.Account{ + Nonce: i, + Balance: big.NewInt(int64(i)), + Root: stRoot, + CodeHash: codehash, + }) + elem := &kv{key, value} + accTrie.Update(elem.k, elem.v) + entries = append(entries, elem) + + storageTries[common.BytesToHash(key)] = stTrie + storageEntries[common.BytesToHash(key)] = stEntries + } + sort.Sort(entries) + + accTrie.Commit(nil) + return accTrie, entries, storageTries, storageEntries +} + +// makeAccountTrieWithStorage spits out a trie, along with the leafs +func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (*trie.Trie, entrySlice, map[common.Hash]*trie.Trie, map[common.Hash]entrySlice) { + var ( + db = trie.NewDatabase(rawdb.NewMemoryDatabase()) + accTrie, _ = trie.New(common.Hash{}, db) + entries entrySlice + storageTries = make(map[common.Hash]*trie.Trie) + storageEntries = make(map[common.Hash]entrySlice) + ) + // Make a storage trie which we reuse for the whole lot + var ( + stTrie *trie.Trie + stEntries entrySlice + ) + if boundary { + stTrie, stEntries = makeBoundaryStorageTrie(slots, db) + } else { + stTrie, stEntries = makeStorageTrieWithSeed(uint64(slots), 0, db) + } + stRoot := stTrie.Hash() + + // Create n accounts in the trie + for i := uint64(1); i <= uint64(accounts); i++ { + key := key32(i) + codehash := emptyCode[:] + if code { + codehash = getCodeHash(i) + } + value, _ := rlp.EncodeToBytes(state.Account{ + Nonce: i, + Balance: big.NewInt(int64(i)), + Root: stRoot, + CodeHash: codehash, + }) + elem := &kv{key, value} + accTrie.Update(elem.k, elem.v) + entries = append(entries, elem) + // we reuse the same one for all accounts + storageTries[common.BytesToHash(key)] = stTrie + storageEntries[common.BytesToHash(key)] = stEntries + } + sort.Sort(entries) + stTrie.Commit(nil) + accTrie.Commit(nil) + return accTrie, entries, storageTries, storageEntries +} + +// makeStorageTrieWithSeed fills a storage trie with n items, returning the +// not-yet-committed trie and the sorted entries. The seeds can be used to ensure +// that tries are unique. +func makeStorageTrieWithSeed(n, seed uint64, db *trie.Database) (*trie.Trie, entrySlice) { + trie, _ := trie.New(common.Hash{}, db) + var entries entrySlice + for i := uint64(1); i <= n; i++ { + // store 'x' at slot 'x' + slotValue := key32(i + seed) + rlpSlotValue, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(slotValue[:])) + + slotKey := key32(i) + key := crypto.Keccak256Hash(slotKey[:]) + + elem := &kv{key[:], rlpSlotValue} + trie.Update(elem.k, elem.v) + entries = append(entries, elem) + } + sort.Sort(entries) + trie.Commit(nil) + return trie, entries +} + +// makeBoundaryStorageTrie constructs a storage trie. Instead of filling +// storage slots normally, this function will fill a few slots which have +// boundary hash. +func makeBoundaryStorageTrie(n int, db *trie.Database) (*trie.Trie, entrySlice) { + var ( + entries entrySlice + boundaries []common.Hash + trie, _ = trie.New(common.Hash{}, db) + ) + // Initialize boundaries + var next common.Hash + step := new(big.Int).Sub( + new(big.Int).Div( + new(big.Int).Exp(common.Big2, common.Big256, nil), + big.NewInt(accountConcurrency), + ), common.Big1, + ) + for i := 0; i < accountConcurrency; i++ { + last := common.BigToHash(new(big.Int).Add(next.Big(), step)) + if i == accountConcurrency-1 { + last = common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + } + boundaries = append(boundaries, last) + next = common.BigToHash(new(big.Int).Add(last.Big(), common.Big1)) + } + // Fill boundary slots + for i := 0; i < len(boundaries); i++ { + key := boundaries[i] + val := []byte{0xde, 0xad, 0xbe, 0xef} + + elem := &kv{key[:], val} + trie.Update(elem.k, elem.v) + entries = append(entries, elem) + } + // Fill other slots if required + for i := uint64(1); i <= uint64(n); i++ { + slotKey := key32(i) + key := crypto.Keccak256Hash(slotKey[:]) + + slotValue := key32(i) + rlpSlotValue, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(slotValue[:])) + + elem := &kv{key[:], rlpSlotValue} + trie.Update(elem.k, elem.v) + entries = append(entries, elem) + } + sort.Sort(entries) + trie.Commit(nil) + return trie, entries +} + +func verifyTrie(db ethdb.KeyValueStore, root common.Hash, t *testing.T) { + t.Helper() + triedb := trie.NewDatabase(db) + accTrie, err := trie.New(root, triedb) + if err != nil { + t.Fatal(err) + } + accounts, slots := 0, 0 + accIt := trie.NewIterator(accTrie.NodeIterator(nil)) + for accIt.Next() { + var acc struct { + Nonce uint64 + Balance *big.Int + Root common.Hash + CodeHash []byte + } + if err := rlp.DecodeBytes(accIt.Value, &acc); err != nil { + log.Crit("Invalid account encountered during snapshot creation", "err", err) + } + accounts++ + if acc.Root != emptyRoot { + storeTrie, err := trie.NewSecure(acc.Root, triedb) + if err != nil { + t.Fatal(err) + } + storeIt := trie.NewIterator(storeTrie.NodeIterator(nil)) + for storeIt.Next() { + slots++ + } + if err := storeIt.Err; err != nil { + t.Fatal(err) + } + } + } + if err := accIt.Err; err != nil { + t.Fatal(err) + } + t.Logf("accounts: %d, slots: %d", accounts, slots) +} diff --git a/eth/state_accessor.go b/eth/state_accessor.go new file mode 100644 index 0000000000..285c2cccab --- /dev/null +++ b/eth/state_accessor.go @@ -0,0 +1,183 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "errors" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/trie" +) + +// stateAtBlock retrieves the state database associated with a certain block. +// If no state is locally available for the given block, a number of blocks +// are attempted to be reexecuted to generate the desired state. The optional +// base layer statedb can be passed then it's regarded as the statedb of the +// parent block. +func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (statedb *state.StateDB, err error) { + var ( + current *types.Block + database state.Database + report = true + origin = block.NumberU64() + ) + // Check the live database first if we have the state fully available, use that. + if checkLive { + statedb, err = eth.blockchain.StateAt(block.Root()) + if err == nil { + return statedb, nil + } + } + if base != nil { + // The optional base statedb is given, mark the start point as parent block + statedb, database, report = base, base.Database(), false + current = eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) + } else { + // Otherwise try to reexec blocks until we find a state or reach our limit + current = block + + // Create an ephemeral trie.Database for isolating the live one. Otherwise + // the internal junks created by tracing will be persisted into the disk. + database = state.NewDatabaseWithConfig(eth.chainDb, &trie.Config{Cache: 16}) + + // If we didn't check the dirty database, do check the clean one, otherwise + // we would rewind past a persisted block (specific corner case is chain + // tracing from the genesis). + if !checkLive { + statedb, err = state.New(current.Root(), database, nil) + if err == nil { + return statedb, nil + } + } + // Database does not have the state for the given block, try to regenerate + for i := uint64(0); i < reexec; i++ { + if current.NumberU64() == 0 { + return nil, errors.New("genesis state is missing") + } + parent := eth.blockchain.GetBlock(current.ParentHash(), current.NumberU64()-1) + if parent == nil { + return nil, fmt.Errorf("missing block %v %d", current.ParentHash(), current.NumberU64()-1) + } + current = parent + + statedb, err = state.New(current.Root(), database, nil) + if err == nil { + break + } + } + if err != nil { + switch err.(type) { + case *trie.MissingNodeError: + return nil, fmt.Errorf("required historical state unavailable (reexec=%d)", reexec) + default: + return nil, err + } + } + } + // State was available at historical point, regenerate + var ( + start = time.Now() + logged time.Time + parent common.Hash + ) + for current.NumberU64() < origin { + // Print progress logs if long enough time elapsed + if time.Since(logged) > 8*time.Second && report { + log.Info("Regenerating historical state", "block", current.NumberU64()+1, "target", origin, "remaining", origin-current.NumberU64()-1, "elapsed", time.Since(start)) + logged = time.Now() + } + // Retrieve the next block to regenerate and process it + next := current.NumberU64() + 1 + if current = eth.blockchain.GetBlockByNumber(next); current == nil { + return nil, fmt.Errorf("block #%d not found", next) + } + _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}) + if err != nil { + return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err) + } + // Finalize the state so any modifications are written to the trie + root, err := statedb.Commit(eth.blockchain.Config().IsEnabled(eth.blockchain.Config().GetEIP161dTransition, block.Number())) + if err != nil { + return nil, err + } + statedb, err = state.New(root, database, nil) + if err != nil { + return nil, fmt.Errorf("state reset after block %d failed: %v", current.NumberU64(), err) + } + database.TrieDB().Reference(root, common.Hash{}) + if parent != (common.Hash{}) { + database.TrieDB().Dereference(parent) + } + parent = root + } + if report { + nodes, imgs := database.TrieDB().Size() + log.Info("Historical state regenerated", "block", current.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs) + } + return statedb, nil +} + +// stateAtTransaction returns the execution environment of a certain transaction. +func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { + // Short circuit if it's genesis block. + if block.NumberU64() == 0 { + return nil, vm.BlockContext{}, nil, errors.New("no transaction in genesis") + } + // Create the parent state database + parent := eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) + if parent == nil { + return nil, vm.BlockContext{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash()) + } + // Lookup the statedb of parent block from the live database, + // otherwise regenerate it on the flight. + statedb, err := eth.stateAtBlock(parent, reexec, nil, true) + if err != nil { + return nil, vm.BlockContext{}, nil, err + } + if txIndex == 0 && len(block.Transactions()) == 0 { + return nil, vm.BlockContext{}, statedb, nil + } + // Recompute transactions up to the target index. + signer := types.MakeSigner(eth.blockchain.Config(), block.Number()) + for idx, tx := range block.Transactions() { + // Assemble the transaction call message and return if the requested offset + msg, _ := tx.AsMessage(signer) + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) + if idx == txIndex { + return msg, context, statedb, nil + } + // Not yet the searched for transaction, execute on top of the current state + vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{}) + statedb.Prepare(tx.Hash(), block.Hash(), idx) + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { + return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) + } + // Ensure any modifications are committed to the state + // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect + // + statedb.Finalise(vmenv.ChainConfig().IsEnabled(vmenv.ChainConfig().GetEIP161dTransition, block.Number())) + } + return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) +} diff --git a/eth/sync.go b/eth/sync.go index 4bed453996..51952264f3 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params/vars" @@ -53,7 +54,7 @@ var ( ) type txsync struct { - p *peer + p *eth.Peer txs []*types.Transaction } @@ -63,8 +64,8 @@ type txsync struct { // If it doesn't change, it means that we've stalled syncing for some reason, // and should disable the permapoint feature in case that's keeping // us on a dead chain. -func (pm *ProtocolManager) artificialFinalitySafetyLoop() { - defer pm.wg.Done() +func (h *handler) artificialFinalitySafetyLoop() { + defer h.wg.Done() t := time.NewTicker(artificialFinalitySafetyInterval) defer t.Stop() @@ -72,22 +73,22 @@ func (pm *ProtocolManager) artificialFinalitySafetyLoop() { for { select { case <-t.C: - if pm.blockchain.IsArtificialFinalityEnabled() { + if h.chain.IsArtificialFinalityEnabled() { // Check if your chain has grown stale. // If it has, disable artificial finality, we could be on an attacker's // chain getting starved. - if time.Since(time.Unix(int64(pm.blockchain.CurrentHeader().Time), 0)) > artificialFinalitySafetyInterval { - pm.blockchain.EnableArtificialFinality(false, "reason", "stale safety interval", "interval", artificialFinalitySafetyInterval) + if time.Since(time.Unix(int64(h.chain.CurrentHeader().Time), 0)) > artificialFinalitySafetyInterval { + h.chain.EnableArtificialFinality(false, "reason", "stale safety interval", "interval", artificialFinalitySafetyInterval) } } - case <-pm.quitSync: + case <-h.quitSync: return } } } // syncTransactions starts sending all currently pending transactions to the given peer. -func (pm *ProtocolManager) syncTransactions(p *peer) { +func (h *handler) syncTransactions(p *eth.Peer) { // Assemble the set of transaction to broadcast or announce to the remote // peer. Fun fact, this is quite an expensive operation as it needs to sort // the transactions if the sorting is not cached yet. However, with a random @@ -95,7 +96,7 @@ func (pm *ProtocolManager) syncTransactions(p *peer) { // // TODO(karalabe): Figure out if we could get away with random order somehow var txs types.Transactions - pending, _ := pm.txpool.Pending() + pending, _ := h.txpool.Pending() for _, batch := range pending { txs = append(txs, batch...) } @@ -105,7 +106,7 @@ func (pm *ProtocolManager) syncTransactions(p *peer) { // The eth/65 protocol introduces proper transaction announcements, so instead // of dripping transactions across multiple peers, just send the entire list as // an announcement and let the remote side decide what they need (likely nothing). - if p.version >= eth65 { + if p.Version() >= eth.ETH65 { hashes := make([]common.Hash, len(txs)) for i, tx := range txs { hashes[i] = tx.Hash() @@ -115,8 +116,8 @@ func (pm *ProtocolManager) syncTransactions(p *peer) { } // Out of luck, peer is running legacy protocols, drop the txs over select { - case pm.txsyncCh <- &txsync{p: p, txs: txs}: - case <-pm.quitSync: + case h.txsyncCh <- &txsync{p: p, txs: txs}: + case <-h.quitSync: } } @@ -124,8 +125,8 @@ func (pm *ProtocolManager) syncTransactions(p *peer) { // connection. When a new peer appears, we relay all currently pending // transactions. In order to minimise egress bandwidth usage, we send // the transactions in small packs to one peer at a time. -func (pm *ProtocolManager) txsyncLoop64() { - defer pm.wg.Done() +func (h *handler) txsyncLoop64() { + defer h.wg.Done() var ( pending = make(map[enode.ID]*txsync) @@ -136,7 +137,7 @@ func (pm *ProtocolManager) txsyncLoop64() { // send starts a sending a pack of transactions from the sync. send := func(s *txsync) { - if s.p.version >= eth65 { + if s.p.Version() >= eth.ETH65 { panic("initial transaction syncer running on eth/65+") } // Fill pack with transactions up to the target size. @@ -150,14 +151,13 @@ func (pm *ProtocolManager) txsyncLoop64() { // Remove the transactions that will be sent. s.txs = s.txs[:copy(s.txs, s.txs[len(pack.txs):])] if len(s.txs) == 0 { - delete(pending, s.p.ID()) + delete(pending, s.p.Peer.ID()) } // Send the pack in the background. s.p.Log().Trace("Sending batch of transactions", "count", len(pack.txs), "bytes", size) sending = true - go func() { done <- pack.p.SendTransactions64(pack.txs) }() + go func() { done <- pack.p.SendTransactions(pack.txs) }() } - // pick chooses the next pending sync. pick := func() *txsync { if len(pending) == 0 { @@ -174,8 +174,8 @@ func (pm *ProtocolManager) txsyncLoop64() { for { select { - case s := <-pm.txsyncCh: - pending[s.p.ID()] = s + case s := <-h.txsyncCh: + pending[s.p.Peer.ID()] = s if !sending { send(s) } @@ -184,13 +184,13 @@ func (pm *ProtocolManager) txsyncLoop64() { // Stop tracking peers that cause send failures. if err != nil { pack.p.Log().Debug("Transaction send failed", "err", err) - delete(pending, pack.p.ID()) + delete(pending, pack.p.Peer.ID()) } // Schedule the next send. if s := pick(); s != nil { send(s) } - case <-pm.quitSync: + case <-h.quitSync: return } } @@ -198,7 +198,7 @@ func (pm *ProtocolManager) txsyncLoop64() { // chainSyncer coordinates blockchain sync components. type chainSyncer struct { - pm *ProtocolManager + handler *handler force *time.Timer forced bool // true when force timer fired peerEventCh chan struct{} @@ -208,15 +208,15 @@ type chainSyncer struct { // chainSyncOp is a scheduled sync operation. type chainSyncOp struct { mode downloader.SyncMode - peer *peer + peer *eth.Peer td *big.Int head common.Hash } // newChainSyncer creates a chainSyncer. -func newChainSyncer(pm *ProtocolManager) *chainSyncer { +func newChainSyncer(handler *handler) *chainSyncer { return &chainSyncer{ - pm: pm, + handler: handler, peerEventCh: make(chan struct{}), } } @@ -224,23 +224,24 @@ func newChainSyncer(pm *ProtocolManager) *chainSyncer { // handlePeerEvent notifies the syncer about a change in the peer set. // This is called for new peers and every time a peer announces a new // chain head. -func (cs *chainSyncer) handlePeerEvent(p *peer) bool { +func (cs *chainSyncer) handlePeerEvent(peer *eth.Peer) bool { select { case cs.peerEventCh <- struct{}{}: return true - case <-cs.pm.quitSync: + case <-cs.handler.quitSync: return false } } // loop runs in its own goroutine and launches the sync when necessary. func (cs *chainSyncer) loop() { - defer cs.pm.wg.Done() + defer cs.handler.wg.Done() - cs.pm.blockFetcher.Start() - cs.pm.txFetcher.Start() - defer cs.pm.blockFetcher.Stop() - defer cs.pm.txFetcher.Stop() + cs.handler.blockFetcher.Start() + cs.handler.txFetcher.Start() + defer cs.handler.blockFetcher.Stop() + defer cs.handler.txFetcher.Stop() + defer cs.handler.downloader.Terminate() // The force timer lowers the peer count threshold down to one when it fires. // This ensures we'll always start sync even if there aren't enough peers. @@ -251,7 +252,6 @@ func (cs *chainSyncer) loop() { if op := cs.nextSyncOp(); op != nil { cs.startSync(op) } - select { case <-cs.peerEventCh: // Peer information changed, recheck. @@ -262,14 +262,13 @@ func (cs *chainSyncer) loop() { case <-cs.force.C: cs.forced = true - case <-cs.pm.quitSync: + case <-cs.handler.quitSync: // Disable all insertion on the blockchain. This needs to happen before // terminating the downloader because the downloader waits for blockchain // inserts, and these can take a long time to finish. - cs.pm.blockchain.StopInsert() - cs.pm.downloader.Terminate() + cs.handler.chain.StopInsert() + cs.handler.downloader.Terminate() if cs.doneCh != nil { - // Wait for the current sync to end. <-cs.doneCh } return @@ -287,79 +286,82 @@ func (cs *chainSyncer) nextSyncOp() *chainSyncOp { minPeers := defaultMinSyncPeers if cs.forced { minPeers = 1 - } else if minPeers > cs.pm.maxPeers { - minPeers = cs.pm.maxPeers + } else if minPeers > cs.handler.maxPeers { + minPeers = cs.handler.maxPeers } - if cs.pm.peers.Len() < minArtificialFinalityPeers { - if cs.pm.blockchain.IsArtificialFinalityEnabled() { + if cs.handler.peers.len() < minArtificialFinalityPeers { + if cs.handler.chain.IsArtificialFinalityEnabled() { // If artificial finality state is forcefully set (overridden) this will just be a noop. - cs.pm.blockchain.EnableArtificialFinality(false, "reason", "low peers", "peers", cs.pm.peers.Len()) + cs.handler.chain.EnableArtificialFinality(false, "reason", "low peers", "peers", cs.handler.peers.len()) } } - if cs.pm.peers.Len() < minPeers { + if cs.handler.peers.len() < minPeers { return nil } - - // We have enough peers, check TD. - peer := cs.pm.peers.BestPeer() + // We have enough peers, check TD + peer := cs.handler.peers.peerWithHighestTD() if peer == nil { return nil } mode, ourTD := cs.modeAndLocalHead() + if mode == downloader.FastSync && atomic.LoadUint32(&cs.handler.snapSync) == 1 { + // Fast sync via the snap protocol + mode = downloader.SnapSync + } op := peerToSyncOp(mode, peer) if op.td.Cmp(ourTD) <= 0 { // Enable artificial finality if parameters if should. // - In full sync mode. if op.mode == downloader.FullSync && // - Have enough peers. - cs.pm.peers.Len() >= minArtificialFinalityPeers && + cs.handler.peers.len() >= minArtificialFinalityPeers && // - Head is not stale. - !(time.Since(time.Unix(int64(cs.pm.blockchain.CurrentHeader().Time), 0)) > artificialFinalitySafetyInterval) && + !(time.Since(time.Unix(int64(cs.handler.chain.CurrentHeader().Time), 0)) > artificialFinalitySafetyInterval) && // - AF is disabled (so we should reenable). - !cs.pm.blockchain.IsArtificialFinalityEnabled() { - cs.pm.blockchain.EnableArtificialFinality(true, "reason", "synced", "peers", cs.pm.peers.Len()) + !cs.handler.chain.IsArtificialFinalityEnabled() { + cs.handler.chain.EnableArtificialFinality(true, "reason", "synced", "peers", cs.handler.peers.len()) } return nil // We're in sync. } return op } -func peerToSyncOp(mode downloader.SyncMode, p *peer) *chainSyncOp { +func peerToSyncOp(mode downloader.SyncMode, p *eth.Peer) *chainSyncOp { peerHead, peerTD := p.Head() return &chainSyncOp{mode: mode, peer: p, td: peerTD, head: peerHead} } func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) { // If we're in fast sync mode, return that directly - if atomic.LoadUint32(&cs.pm.fastSync) == 1 { - block := cs.pm.blockchain.CurrentFastBlock() - td := cs.pm.blockchain.GetTdByHash(block.Hash()) + if atomic.LoadUint32(&cs.handler.fastSync) == 1 { + block := cs.handler.chain.CurrentFastBlock() + td := cs.handler.chain.GetTdByHash(block.Hash()) return downloader.FastSync, td } // We are probably in full sync, but we might have rewound to before the // fast sync pivot, check if we should reenable - if pivot := rawdb.ReadLastPivotNumber(cs.pm.chaindb); pivot != nil { - if head := cs.pm.blockchain.CurrentBlock(); head.NumberU64() < *pivot { - block := cs.pm.blockchain.CurrentFastBlock() - td := cs.pm.blockchain.GetTdByHash(block.Hash()) + if pivot := rawdb.ReadLastPivotNumber(cs.handler.database); pivot != nil { + if head := cs.handler.chain.CurrentBlock(); head.NumberU64() < *pivot { + block := cs.handler.chain.CurrentFastBlock() + td := cs.handler.chain.GetTdByHash(block.Hash()) return downloader.FastSync, td } } // Nope, we're really full syncing - head := cs.pm.blockchain.CurrentHeader() - td := cs.pm.blockchain.GetTd(head.Hash(), head.Number.Uint64()) + head := cs.handler.chain.CurrentBlock() + td := cs.handler.chain.GetTd(head.Hash(), head.NumberU64()) return downloader.FullSync, td } // startSync launches doSync in a new goroutine. func (cs *chainSyncer) startSync(op *chainSyncOp) { cs.doneCh = make(chan error, 1) - go func() { cs.doneCh <- cs.pm.doSync(op) }() + go func() { cs.doneCh <- cs.handler.doSync(op) }() } // doSync synchronizes the local blockchain with a remote peer. -func (pm *ProtocolManager) doSync(op *chainSyncOp) error { - if op.mode == downloader.FastSync { +func (h *handler) doSync(op *chainSyncOp) error { + if op.mode == downloader.FastSync || op.mode == downloader.SnapSync { // Before launch the fast sync, we have to ensure user uses the same // txlookup limit. // The main concern here is: during the fast sync Geth won't index the @@ -369,35 +371,37 @@ func (pm *ProtocolManager) doSync(op *chainSyncOp) error { // has been indexed. So here for the user-experience wise, it's non-optimal // that user can't change limit during the fast sync. If changed, Geth // will just blindly use the original one. - limit := pm.blockchain.TxLookupLimit() - if stored := rawdb.ReadFastTxLookupLimit(pm.chaindb); stored == nil { - rawdb.WriteFastTxLookupLimit(pm.chaindb, limit) + limit := h.chain.TxLookupLimit() + if stored := rawdb.ReadFastTxLookupLimit(h.database); stored == nil { + rawdb.WriteFastTxLookupLimit(h.database, limit) } else if *stored != limit { - pm.blockchain.SetTxLookupLimit(*stored) + h.chain.SetTxLookupLimit(*stored) log.Warn("Update txLookup limit", "provided", limit, "updated", *stored) } } // Run the sync cycle, and disable fast sync if we're past the pivot block - err := pm.downloader.Synchronise(op.peer.id, op.head, op.td, op.mode) + err := h.downloader.Synchronise(op.peer.ID(), op.head, op.td, op.mode) if err != nil { return err } - if atomic.LoadUint32(&pm.fastSync) == 1 { + if atomic.LoadUint32(&h.fastSync) == 1 { log.Info("Fast sync complete, auto disabling") - atomic.StoreUint32(&pm.fastSync, 0) + atomic.StoreUint32(&h.fastSync, 0) + } + if atomic.LoadUint32(&h.snapSync) == 1 { + log.Info("Snap sync complete, auto disabling") + atomic.StoreUint32(&h.snapSync, 0) } - // If we've successfully finished a sync cycle and passed any required checkpoint, // enable accepting transactions from the network. - head := pm.blockchain.CurrentBlock() - if head.NumberU64() >= pm.checkpointNumber { + head := h.chain.CurrentBlock() + if head.NumberU64() >= h.checkpointNumber { // Checkpoint passed, sanity check the timestamp to have a fallback mechanism // for non-checkpointed (number = 0) private networks. if head.Time() >= uint64(time.Now().AddDate(0, -1, 0).Unix()) { - atomic.StoreUint32(&pm.acceptTxs, 1) + atomic.StoreUint32(&h.acceptTxs, 1) } } - if 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 @@ -405,8 +409,7 @@ func (pm *ProtocolManager) doSync(op *chainSyncOp) error { // 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. - pm.BroadcastBlock(head, false) + h.BroadcastBlock(head, false) } - return nil } diff --git a/eth/sync_test.go b/eth/sync_test.go index 7e9b42e372..f4a93daeb9 100644 --- a/eth/sync_test.go +++ b/eth/sync_test.go @@ -17,15 +17,21 @@ package eth import ( + "math/big" "sync/atomic" "testing" "time" + "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/params/types/genesisT" ) // blockGenContemporaryTime creates a block gen function that will bump the block times to within throwing @@ -44,42 +50,91 @@ func blockGenContemporaryTime(numBlocks int64) func(i int, gen *core.BlockGen) { } } -func TestFastSyncDisabling63(t *testing.T) { testFastSyncDisabling(t, 63) } +// newTestHandlerWithBlocks creates a new handler for testing purposes, with a +// given number of initial blocks. +func newTestHandlerWithBlocksWithOpts(blocks int, mode downloader.SyncMode, gen func(int, *core.BlockGen)) *testHandler { + // Create a database pre-initialize with a genesis block + db := rawdb.NewMemoryDatabase() + gspec := &genesisT.Genesis{ + Config: params.TestChainConfig, + Alloc: genesisT.GenesisAlloc{testAddr: {Balance: big.NewInt(1000000)}}, + } + core.MustCommitGenesis(db, gspec) + + chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + + bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, gen) + if _, err := chain.InsertChain(bs); err != nil { + panic(err) + } + txpool := newTestTxPool() + + handler, _ := newHandler(&handlerConfig{ + Database: db, + Chain: chain, + TxPool: txpool, + Network: 1, + Sync: mode, + BloomCache: 1, + }) + handler.Start(1000) + + return &testHandler{ + db: db, + chain: chain, + txpool: txpool, + handler: handler, + } +} + +// Tests that fast sync is disabled after a successful sync cycle. func TestFastSyncDisabling64(t *testing.T) { testFastSyncDisabling(t, 64) } func TestFastSyncDisabling65(t *testing.T) { testFastSyncDisabling(t, 65) } // Tests that fast sync gets disabled as soon as a real block is successfully // imported into the blockchain. -func testFastSyncDisabling(t *testing.T, protocol int) { +func testFastSyncDisabling(t *testing.T, protocol uint) { t.Parallel() - maxBlocksCreated := 1024 - genFunc := blockGenContemporaryTime(int64(maxBlocksCreated)) - - // Create a pristine protocol manager, check that fast sync is left enabled - pmEmpty, _ := newTestProtocolManagerMust(t, downloader.FastSync, 0, genFunc, nil) - if atomic.LoadUint32(&pmEmpty.fastSync) == 0 { + // Create an empty handler and ensure it's in fast sync mode + empty := newTestHandler() + if atomic.LoadUint32(&empty.handler.fastSync) == 0 { t.Fatalf("fast sync disabled on pristine blockchain") } - // Create a full protocol manager, check that fast sync gets disabled - pmFull, _ := newTestProtocolManagerMust(t, downloader.FastSync, maxBlocksCreated, genFunc, nil) - if atomic.LoadUint32(&pmFull.fastSync) == 1 { + defer empty.close() + + // Create a full handler and ensure fast sync ends up disabled + full := newTestHandlerWithBlocks(1024) + if atomic.LoadUint32(&full.handler.fastSync) == 1 { t.Fatalf("fast sync not disabled on non-empty blockchain") } - - // Sync up the two peers - io1, io2 := p2p.MsgPipe() - go pmFull.handle(pmFull.newPeer(protocol, p2p.NewPeer(enode.ID{}, "empty", nil), io2, pmFull.txpool.Get)) - go pmEmpty.handle(pmEmpty.newPeer(protocol, p2p.NewPeer(enode.ID{}, "full", nil), io1, pmEmpty.txpool.Get)) - + defer full.close() + + // Sync up the two handlers + emptyPipe, fullPipe := p2p.MsgPipe() + defer emptyPipe.Close() + defer fullPipe.Close() + + emptyPeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), emptyPipe, empty.txpool) + fullPeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), fullPipe, full.txpool) + defer emptyPeer.Close() + defer fullPeer.Close() + + go empty.handler.runEthPeer(emptyPeer, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(empty.handler), peer) + }) + go full.handler.runEthPeer(fullPeer, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(full.handler), peer) + }) + // Wait a bit for the above handlers to start time.Sleep(250 * time.Millisecond) - op := peerToSyncOp(downloader.FastSync, pmEmpty.peers.BestPeer()) - if err := pmEmpty.doSync(op); err != nil { - t.Fatal("sync failed:", err) - } // Check that fast sync was disabled - if atomic.LoadUint32(&pmEmpty.fastSync) == 1 { + op := peerToSyncOp(downloader.FastSync, empty.handler.peers.peerWithHighestTD()) + if err := empty.handler.doSync(op); err != nil { + t.Fatal("sync failed:", err) + } + if atomic.LoadUint32(&empty.handler.fastSync) == 1 { t.Fatalf("fast sync not disabled after successful synchronisation") } } @@ -89,48 +144,64 @@ func TestArtificialFinalityFeatureEnablingDisabling(t *testing.T) { genFunc := blockGenContemporaryTime(int64(maxBlocksCreated)) // Create a full protocol manager, check that fast sync gets disabled - a, _ := newTestProtocolManagerMust(t, downloader.FastSync, maxBlocksCreated, genFunc, nil) - if atomic.LoadUint32(&a.fastSync) == 1 { + a := newTestHandlerWithBlocksWithOpts(1024, downloader.FullSync, genFunc) + if atomic.LoadUint32(&a.handler.fastSync) == 1 { t.Fatalf("fast sync not disabled on non-empty blockchain") } + defer a.close() one := uint64(1) - a.blockchain.Config().SetECBP1100Transition(&one) + a.chain.Config().SetECBP1100Transition(&one) oMinAFPeers := minArtificialFinalityPeers defer func() { - // Clean up after, resetting global default to original valu. + // Clean up after, resetting global default to original value. minArtificialFinalityPeers = oMinAFPeers }() minArtificialFinalityPeers = 1 // Create a full protocol manager, check that fast sync gets disabled - b, _ := newTestProtocolManagerMust(t, downloader.FastSync, 0, genFunc, nil) - if atomic.LoadUint32(&b.fastSync) == 0 { - t.Fatalf("fast sync disabled on pristine blockchain") + b := newTestHandlerWithBlocksWithOpts(0, downloader.FullSync, genFunc) + if atomic.LoadUint32(&b.handler.fastSync) == 1 { + t.Fatalf("fast sync not disabled on non-empty blockchain") } - b.blockchain.Config().SetECBP1100Transition(&one) + defer b.close() + b.chain.Config().SetECBP1100Transition(&one) // b.chainSync.forced = true - io1, io2 := p2p.MsgPipe() - go a.handle(a.newPeer(65, p2p.NewPeer(enode.ID{}, "peer-b", nil), io2, a.txpool.Get)) - go b.handle(b.newPeer(65, p2p.NewPeer(enode.ID{}, "peer-a", nil), io1, b.txpool.Get)) + // Sync up the two handlers + emptyPipe, fullPipe := p2p.MsgPipe() + defer emptyPipe.Close() + defer fullPipe.Close() + + fullPeer := eth.NewPeer(65, p2p.NewPeer(enode.ID{2}, "", nil), fullPipe, a.txpool) + emptyPeer := eth.NewPeer(65, p2p.NewPeer(enode.ID{1}, "", nil), emptyPipe, b.txpool) + defer emptyPeer.Close() + defer fullPeer.Close() + + go b.handler.runEthPeer(emptyPeer, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(b.handler), peer) + }) + go a.handler.runEthPeer(fullPeer, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(a.handler), peer) + }) + // Wait a bit for the above handlers to start time.Sleep(250 * time.Millisecond) - op := peerToSyncOp(downloader.FullSync, b.peers.BestPeer()) - if err := b.doSync(op); err != nil { + op := peerToSyncOp(downloader.FullSync, b.handler.peers.peerWithHighestTD()) + if err := b.handler.doSync(op); err != nil { t.Fatalf("sync failed: %v", err) } - b.chainSync.forced = true - next := b.chainSync.nextSyncOp() + b.handler.chainSync.forced = true + next := b.handler.chainSync.nextSyncOp() if next != nil { t.Fatal("non-nil next sync op") } - if !b.blockchain.Config().IsEnabled(b.blockchain.Config().GetECBP1100Transition, b.blockchain.CurrentBlock().Number()) { + if !b.chain.Config().IsEnabled(b.chain.Config().GetECBP1100Transition, b.chain.CurrentBlock().Number()) { t.Error("AF feature not configured") } - if !b.blockchain.IsArtificialFinalityEnabled() { + if !b.chain.IsArtificialFinalityEnabled() { t.Error("AF not enabled") } @@ -138,12 +209,12 @@ func TestArtificialFinalityFeatureEnablingDisabling(t *testing.T) { minArtificialFinalityPeers = oMinAFPeers // Next sync op will unset AF because manager only has 1 peer. - b.chainSync.forced = true - next = b.chainSync.nextSyncOp() + b.handler.chainSync.forced = true + next = b.handler.chainSync.nextSyncOp() if next != nil { t.Fatal("non-nil next sync op") } - if b.blockchain.IsArtificialFinalityEnabled() { + if b.chain.IsArtificialFinalityEnabled() { t.Error("AF not disabled") } } @@ -155,13 +226,11 @@ func TestArtificialFinalityFeatureEnablingDisabling_NoDisable(t *testing.T) { genFunc := blockGenContemporaryTime(int64(maxBlocksCreated)) // Create a full protocol manager, check that fast sync gets disabled - a, _ := newTestProtocolManagerMust(t, downloader.FastSync, maxBlocksCreated, genFunc, nil) - if atomic.LoadUint32(&a.fastSync) == 1 { - t.Fatalf("fast sync not disabled on non-empty blockchain") - } + a := newTestHandlerWithBlocksWithOpts(1024, downloader.FullSync, genFunc) + defer a.close() one := uint64(1) - a.blockchain.Config().SetECBP1100Transition(&one) + a.chain.Config().SetECBP1100Transition(&one) oMinAFPeers := minArtificialFinalityPeers defer func() { @@ -171,29 +240,41 @@ func TestArtificialFinalityFeatureEnablingDisabling_NoDisable(t *testing.T) { minArtificialFinalityPeers = 1 // Create a full protocol manager, check that fast sync gets disabled - b, _ := newTestProtocolManagerMust(t, downloader.FastSync, 0, genFunc, nil) - if atomic.LoadUint32(&b.fastSync) == 0 { - t.Fatalf("fast sync disabled on pristine blockchain") - } - b.blockchain.Config().SetECBP1100Transition(&one) + b := newTestHandlerWithBlocksWithOpts(0, downloader.FullSync, genFunc) + defer b.close() + b.chain.Config().SetECBP1100Transition(&one) // NOTE1: Set the nodisable switch to the on position. // This prevents the de-activation of AF features once they have been enabled. // In geth code, this is set in the backend during blockchain construction. - b.blockchain.ArtificialFinalityNoDisable(1) - - io1, io2 := p2p.MsgPipe() - go a.handle(a.newPeer(65, p2p.NewPeer(enode.ID{}, "peer-b", nil), io2, a.txpool.Get)) - go b.handle(b.newPeer(65, p2p.NewPeer(enode.ID{}, "peer-a", nil), io1, b.txpool.Get)) + b.chain.ArtificialFinalityNoDisable(1) + + // Sync up the two handlers + emptyPipe, fullPipe := p2p.MsgPipe() + defer emptyPipe.Close() + defer fullPipe.Close() + + fullPeer := eth.NewPeer(65, p2p.NewPeer(enode.ID{2}, "", nil), fullPipe, a.txpool) + emptyPeer := eth.NewPeer(65, p2p.NewPeer(enode.ID{1}, "", nil), emptyPipe, b.txpool) + defer emptyPeer.Close() + defer fullPeer.Close() + + go b.handler.runEthPeer(emptyPeer, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(b.handler), peer) + }) + go a.handler.runEthPeer(fullPeer, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(a.handler), peer) + }) + // Wait a bit for the above handlers to start time.Sleep(250 * time.Millisecond) - op := peerToSyncOp(downloader.FullSync, b.peers.BestPeer()) - if err := b.doSync(op); err != nil { + op := peerToSyncOp(downloader.FullSync, b.handler.peers.peerWithHighestTD()) + if err := b.handler.doSync(op); err != nil { t.Fatalf("sync failed: %v", err) } - b.chainSync.forced = true - next := b.chainSync.nextSyncOp() + b.handler.chainSync.forced = true + next := b.handler.chainSync.nextSyncOp() // Revert safety condition overrides to default values. // Set the value back to default (more than 1). @@ -202,20 +283,20 @@ func TestArtificialFinalityFeatureEnablingDisabling_NoDisable(t *testing.T) { if next != nil { t.Fatal("non-nil next sync op") } - if !b.blockchain.Config().IsEnabled(b.blockchain.Config().GetECBP1100Transition, b.blockchain.CurrentBlock().Number()) { + if !b.chain.Config().IsEnabled(b.chain.Config().GetECBP1100Transition, b.chain.CurrentBlock().Number()) { t.Error("AF feature not configured") } - if !b.blockchain.IsArtificialFinalityEnabled() { + if !b.chain.IsArtificialFinalityEnabled() { t.Error("AF not enabled") } // Next sync op will unset AF because manager only has 1 peer. - b.chainSync.forced = true - next = b.chainSync.nextSyncOp() + b.handler.chainSync.forced = true + next = b.handler.chainSync.nextSyncOp() if next != nil { t.Fatal("non-nil next sync op") } - if !b.blockchain.IsArtificialFinalityEnabled() { + if !b.chain.IsArtificialFinalityEnabled() { t.Error(`AF not enabled; The AF disable mechanism triggered by the minimum peers floor should have been short-circuited, preventing AF disablement on this sync op (with the minArtificialFinalityPeers value set to > 1 (defaultMinSyncPeers = 5)), @@ -231,13 +312,10 @@ func TestArtificialFinalityFeatureEnablingDisabling_StaleHead(t *testing.T) { maxBlocksCreated := 1024 // Create a full protocol manager, check that fast sync gets disabled - a, _ := newTestProtocolManagerMust(t, downloader.FastSync, maxBlocksCreated, nil, nil) - if atomic.LoadUint32(&a.fastSync) == 1 { - t.Fatalf("fast sync not disabled on non-empty blockchain") - } - + a := newTestHandlerWithBlocksWithOpts(maxBlocksCreated, downloader.FullSync, nil) + defer a.close() one := uint64(1) - a.blockchain.Config().SetECBP1100Transition(&one) + a.chain.Config().SetECBP1100Transition(&one) oMinAFPeers := minArtificialFinalityPeers defer func() { @@ -247,24 +325,36 @@ func TestArtificialFinalityFeatureEnablingDisabling_StaleHead(t *testing.T) { minArtificialFinalityPeers = 1 // Create a full protocol manager, check that fast sync gets disabled - b, _ := newTestProtocolManagerMust(t, downloader.FastSync, 0, nil, nil) - if atomic.LoadUint32(&b.fastSync) == 0 { - t.Fatalf("fast sync disabled on pristine blockchain") - } - b.blockchain.Config().SetECBP1100Transition(&one) - - io1, io2 := p2p.MsgPipe() - go a.handle(a.newPeer(65, p2p.NewPeer(enode.ID{}, "peer-b", nil), io2, a.txpool.Get)) - go b.handle(b.newPeer(65, p2p.NewPeer(enode.ID{}, "peer-a", nil), io1, b.txpool.Get)) + b := newTestHandlerWithBlocksWithOpts(0, downloader.FullSync, nil) + defer b.close() + b.chain.Config().SetECBP1100Transition(&one) + + // Sync up the two handlers + emptyPipe, fullPipe := p2p.MsgPipe() + defer emptyPipe.Close() + defer fullPipe.Close() + + fullPeer := eth.NewPeer(65, p2p.NewPeer(enode.ID{2}, "", nil), fullPipe, a.txpool) + emptyPeer := eth.NewPeer(65, p2p.NewPeer(enode.ID{1}, "", nil), emptyPipe, b.txpool) + defer emptyPeer.Close() + defer fullPeer.Close() + + go b.handler.runEthPeer(emptyPeer, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(b.handler), peer) + }) + go a.handler.runEthPeer(fullPeer, func(peer *eth.Peer) error { + return eth.Handle((*ethHandler)(a.handler), peer) + }) + // Wait a bit for the above handlers to start time.Sleep(250 * time.Millisecond) - op := peerToSyncOp(downloader.FullSync, b.peers.BestPeer()) - if err := b.doSync(op); err != nil { + op := peerToSyncOp(downloader.FullSync, b.handler.peers.peerWithHighestTD()) + if err := b.handler.doSync(op); err != nil { t.Fatalf("sync failed: %v", err) } - b.chainSync.forced = true - next := b.chainSync.nextSyncOp() + b.handler.chainSync.forced = true + next := b.handler.chainSync.nextSyncOp() // Revert safety condition overrides to default values. // Set the value back to default (more than 1). @@ -273,16 +363,16 @@ func TestArtificialFinalityFeatureEnablingDisabling_StaleHead(t *testing.T) { if next != nil { t.Fatal("non-nil next sync op") } - if !b.blockchain.Config().IsEnabled(b.blockchain.Config().GetECBP1100Transition, b.blockchain.CurrentBlock().Number()) { + if !b.chain.Config().IsEnabled(b.chain.Config().GetECBP1100Transition, b.chain.CurrentBlock().Number()) { t.Error("AF feature not configured") } // Unit test the timestamp. We want to be sure that the blockchain's current header is actually very old // (since we expect its age to act as a condition preventing the enabling of AF features). - d := uint64(time.Now().Unix()) - b.blockchain.CurrentHeader().Time + d := uint64(time.Now().Unix()) - b.chain.CurrentHeader().Time if time.Second*time.Duration(d) < artificialFinalitySafetyInterval { t.Errorf("Expected blockchain current head to be very old, it is not.") } - if b.blockchain.IsArtificialFinalityEnabled() { + if b.chain.IsArtificialFinalityEnabled() { t.Error("AF is enabled despite a very stale head") } } diff --git a/eth/api_tracer.go b/eth/tracers/api.go similarity index 51% rename from eth/api_tracer.go rename to eth/tracers/api.go index ae43e1a162..3e7c8d93ed 100644 --- a/eth/api_tracer.go +++ b/eth/tracers/api.go @@ -1,4 +1,4 @@ -// Copyright 2017 The go-ethereum Authors +// Copyright 2021 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package eth +package tracers import ( "bufio" @@ -32,18 +32,18 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params/types/ctypes" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/trie" ) const ( @@ -57,6 +57,104 @@ const ( defaultTraceReexec = uint64(128) ) +// Backend interface provides the common API services (that are provided by +// both full and light clients) with access to necessary functions. +type Backend interface { + HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) + HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) + BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) + BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) + GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) + RPCGasCap() uint64 + ChainConfig() ctypes.ChainConfigurator + Engine() consensus.Engine + ChainDb() ethdb.Database + StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) + StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) +} + +// API is the collection of tracing APIs exposed over the private debugging endpoint. +type API struct { + backend Backend +} + +// NewAPI creates a new API definition for the tracing methods of the Ethereum service. +func NewAPI(backend Backend) *API { + return &API{backend: backend} +} + +type chainContext struct { + api *API + ctx context.Context +} + +func (context *chainContext) Engine() consensus.Engine { + return context.api.backend.Engine() +} + +func (context *chainContext) GetHeader(hash common.Hash, number uint64) *types.Header { + header, err := context.api.backend.HeaderByNumber(context.ctx, rpc.BlockNumber(number)) + if err != nil { + return nil + } + if header.Hash() == hash { + return header + } + header, err = context.api.backend.HeaderByHash(context.ctx, hash) + if err != nil { + return nil + } + return header +} + +// chainContext construts the context reader which is used by the evm for reading +// the necessary chain context. +func (api *API) chainContext(ctx context.Context) core.ChainContext { + return &chainContext{api: api, ctx: ctx} +} + +// blockByNumber is the wrapper of the chain access function offered by the backend. +// It will return an error if the block is not found. +func (api *API) blockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { + block, err := api.backend.BlockByNumber(ctx, number) + if err != nil { + return nil, err + } + if block == nil { + return nil, fmt.Errorf("block #%d not found", number) + } + return block, nil +} + +// blockByHash is the wrapper of the chain access function offered by the backend. +// It will return an error if the block is not found. +func (api *API) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { + block, err := api.backend.BlockByHash(ctx, hash) + if err != nil { + return nil, err + } + if block == nil { + return nil, fmt.Errorf("block %s not found", hash.Hex()) + } + return block, nil +} + +// blockByNumberAndHash is the wrapper of the chain access function offered by +// the backend. It will return an error if the block is not found. +// +// Note this function is friendly for the light client which can only retrieve the +// historical(before the CHT) header/block by number. +func (api *API) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber, hash common.Hash) (*types.Block, error) { + block, err := api.blockByNumber(ctx, number) + if err != nil { + return nil, err + } + if block.Hash() == hash { + return block, nil + } + return api.blockByHash(ctx, hash) +} + // TraceConfig holds extra parameters to trace functions. type TraceConfig struct { *vm.LogConfig @@ -68,11 +166,18 @@ type TraceConfig struct { // StdTraceConfig holds extra parameters to standard-json trace functions. type StdTraceConfig struct { - *vm.LogConfig + vm.LogConfig Reexec *uint64 TxHash common.Hash } +// txTraceContext is the contextual infos about a transaction before it gets run. +type txTraceContext struct { + index int // Index of the transaction within the block + hash common.Hash // Hash of the transaction + block common.Hash // Hash of the block containing the transaction +} + // txTraceResult is the result of a single transaction trace. type txTraceResult struct { Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer @@ -99,39 +204,20 @@ type blockTraceResult struct { // txTraceTask represents a single transaction trace task when an entire block // is being traced. type txTraceTask struct { - statedb *state.StateDB // Intermediate state prepped for tracing - index int // Transaction offset in the block - taskExtraContext map[string]interface{} + statedb *state.StateDB // Intermediate state prepped for tracing + index int // Transaction offset in the block } // TraceChain returns the structured logs created during the execution of EVM // between two blocks (excluding start) and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) { - // Fetch the block interval that we want to trace - var from, to *types.Block - - switch start { - case rpc.PendingBlockNumber: - from = api.eth.miner.PendingBlock() - case rpc.LatestBlockNumber: - from = api.eth.blockchain.CurrentBlock() - default: - from = api.eth.blockchain.GetBlockByNumber(uint64(start)) - } - switch end { - case rpc.PendingBlockNumber: - to = api.eth.miner.PendingBlock() - case rpc.LatestBlockNumber: - to = api.eth.blockchain.CurrentBlock() - default: - to = api.eth.blockchain.GetBlockByNumber(uint64(end)) - } - // Trace the chain if we've found all our blocks - if from == nil { - return nil, fmt.Errorf("starting block #%d not found", start) +func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) { // Fetch the block interval that we want to trace + from, err := api.blockByNumber(ctx, start) + if err != nil { + return nil, err } - if to == nil { - return nil, fmt.Errorf("end block #%d not found", end) + to, err := api.blockByNumber(ctx, end) + if err != nil { + return nil, err } if from.Number().Cmp(to.Number()) >= 0 { return nil, fmt.Errorf("end block (#%d) needs to come after start block (#%d)", end, start) @@ -142,7 +228,7 @@ func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.Block // traceChain configures a new tracer according to the provided configuration, and // executes all the transactions contained within. The return value will be one item // per transaction, dependent on the requested tracer. -func traceChain(ctx context.Context, eth *Ethereum, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) { +func (api *API) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) { // Tracing a chain is a **long** operation, only do with subscriptions notifier, supported := rpc.NotifierFromContext(ctx) if !supported { @@ -150,54 +236,22 @@ func traceChain(ctx context.Context, eth *Ethereum, start, end *types.Block, con } sub := notifier.CreateSubscription() - // Ensure we have a valid starting state before doing any work - origin := start.NumberU64() - database := state.NewDatabaseWithCache(eth.ChainDb(), 16, "") // Chain tracing will probably start at genesis - - if number := start.NumberU64(); number > 0 { - start = eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1) - if start == nil { - return nil, fmt.Errorf("parent block #%d not found", number-1) - } - } - statedb, err := state.New(start.Root(), database, nil) - if err != nil { - // If the starting state is missing, allow some number of blocks to be reexecuted - reexec := defaultTraceReexec - if config != nil && config.Reexec != nil { - reexec = *config.Reexec - } - // Find the most recent block that has the state available - for i := uint64(0); i < reexec; i++ { - start = eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1) - if start == nil { - break - } - if statedb, err = state.New(start.Root(), database, nil); err == nil { - break - } - } - // If we still don't have the state available, bail out - if err != nil { - switch err.(type) { - case *trie.MissingNodeError: - return nil, errors.New("required historical state unavailable") - default: - return nil, err - } - } + // Prepare all the states for tracing. Note this procedure can take very + // long time. Timeout mechanism is necessary. + reexec := defaultTraceReexec + if config != nil && config.Reexec != nil { + reexec = *config.Reexec } - // Execute all the transaction contained within the chain concurrently for each block - blocks := int(end.NumberU64() - origin) - + blocks := int(end.NumberU64() - start.NumberU64()) threads := runtime.NumCPU() if threads > blocks { threads = blocks } var ( - pend = new(sync.WaitGroup) - tasks = make(chan *blockTraceTask, threads) - results = make(chan *blockTraceTask, threads) + pend = new(sync.WaitGroup) + tasks = make(chan *blockTraceTask, threads) + results = make(chan *blockTraceTask, threads) + localctx = context.Background() ) for th := 0; th < threads; th++ { pend.Add(1) @@ -206,21 +260,24 @@ func traceChain(ctx context.Context, eth *Ethereum, start, end *types.Block, con // Fetch and execute the next block trace tasks for task := range tasks { - signer := types.MakeSigner(eth.blockchain.Config(), task.block.Number()) - + signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number()) + blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), nil) // Trace all the transactions contained within for i, tx := range task.block.Transactions() { msg, _ := tx.AsMessage(signer) - vmctx := core.NewEVMContext(msg, task.block.Header(), eth.blockchain, nil) - - res, err := traceTx(ctx, eth, msg, vmctx, task.statedb, nil, config) + txctx := &txTraceContext{ + index: i, + hash: tx.Hash(), + block: task.block.Hash(), + } + res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, nil, config) if err != nil { task.results[i] = &txTraceResult{Error: err.Error()} log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) break } // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - task.statedb.Finalise(eth.blockchain.Config().IsEnabled(eth.blockchain.Config().GetEIP161dTransition, task.block.Number())) + task.statedb.Finalise(api.backend.ChainConfig().IsEnabled(api.backend.ChainConfig().GetEIP161dTransition, task.block.Number())) task.results[i] = &txTraceResult{Result: res} } // Stream the result back to the user or abort on teardown @@ -237,11 +294,12 @@ func traceChain(ctx context.Context, eth *Ethereum, start, end *types.Block, con go func() { var ( - logged time.Time - number uint64 - traced uint64 - failed error - proot common.Hash + logged time.Time + number uint64 + traced uint64 + failed error + parent common.Hash + statedb *state.StateDB ) // Ensure everything is properly cleaned up on any exit path defer func() { @@ -259,7 +317,7 @@ func traceChain(ctx context.Context, eth *Ethereum, start, end *types.Block, con close(results) }() // Feed all the blocks both into the tracer, as well as fast process concurrently - for number = start.NumberU64() + 1; number <= end.NumberU64(); number++ { + for number = start.NumberU64(); number < end.NumberU64(); number++ { // Stop tracing if interruption was requested select { case <-notifier.Closed(): @@ -268,59 +326,46 @@ func traceChain(ctx context.Context, eth *Ethereum, start, end *types.Block, con } // Print progress logs if long enough time elapsed if time.Since(logged) > 8*time.Second { - if number > origin { - nodes, imgs := database.TrieDB().Size() - log.Info("Tracing chain segment", "start", origin, "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin), "memory", nodes+imgs) - } else { - log.Info("Preparing state for chain trace", "block", number, "start", origin, "elapsed", time.Since(begin)) - } logged = time.Now() + log.Info("Tracing chain segment", "start", start.NumberU64(), "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin)) } - // Retrieve the next block to trace - block := eth.blockchain.GetBlockByNumber(number) - if block == nil { - failed = fmt.Errorf("block #%d not found", number) - break - } - // Send the block over to the concurrent tracers (if not in the fast-forward phase) - if number > origin { - txs := block.Transactions() - - select { - case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: block, rootref: proot, results: make([]*txTraceResult, len(txs))}: - case <-notifier.Closed(): - return - } - traced += uint64(len(txs)) - } - // Generate the next state snapshot fast without tracing - _, _, _, err := eth.blockchain.Processor().Process(block, statedb, vm.Config{}) + // Retrieve the parent state to trace on top + block, err := api.blockByNumber(localctx, rpc.BlockNumber(number)) if err != nil { failed = err break } - // Finalize the state so any modifications are written to the trie - root, err := statedb.Commit(eth.blockchain.Config().IsEnabled(eth.blockchain.Config().GetEIP161dTransition, block.Number())) + // Prepare the statedb for tracing. Don't use the live database for + // tracing to avoid persisting state junks into the database. + statedb, err = api.backend.StateAtBlock(localctx, block, reexec, statedb, false) if err != nil { failed = err break } - if err := statedb.Reset(root); err != nil { + if statedb.Database().TrieDB() != nil { + // Hold the reference for tracer, will be released at the final stage + statedb.Database().TrieDB().Reference(block.Root(), common.Hash{}) + + // Release the parent state because it's already held by the tracer + if parent != (common.Hash{}) { + statedb.Database().TrieDB().Dereference(parent) + } + } + parent = block.Root() + + next, err := api.blockByNumber(localctx, rpc.BlockNumber(number+1)) + if err != nil { failed = err break } - // Reference the trie twice, once for us, once for the tracer - database.TrieDB().Reference(root, common.Hash{}) - if number >= origin { - database.TrieDB().Reference(root, common.Hash{}) - } - // Dereference all past tries we ourselves are done working with - if proot != (common.Hash{}) { - database.TrieDB().Dereference(proot) + // Send the block over to the concurrent tracers (if not in the fast-forward phase) + txs := next.Transactions() + select { + case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: next, rootref: block.Root(), results: make([]*txTraceResult, len(txs))}: + case <-notifier.Closed(): + return } - proot = root - - // TODO(karalabe): Do we need the preimages? Won't they accumulate too much? + traced += uint64(len(txs)) } }() @@ -328,7 +373,7 @@ func traceChain(ctx context.Context, eth *Ethereum, start, end *types.Block, con go func() { var ( done = make(map[uint64]*blockTraceResult) - next = origin + 1 + next = start.NumberU64() + 1 ) for res := range results { // Queue up next received result @@ -339,9 +384,10 @@ func traceChain(ctx context.Context, eth *Ethereum, start, end *types.Block, con } done[uint64(result.Block)] = result - // Dereference any paret tries held in memory by this task - database.TrieDB().Dereference(res.rootref) - + // Dereference any parent tries held in memory by this task + if res.statedb.Database().TrieDB() != nil { + res.statedb.Database().TrieDB().Dereference(res.rootref) + } // Stream completed traces to the user, aborting on the first error for result, ok := done[next]; ok; result, ok = done[next] { if len(result.Traces) > 0 || next == end.NumberU64() { @@ -355,69 +401,39 @@ func traceChain(ctx context.Context, eth *Ethereum, start, end *types.Block, con return sub, nil } -// traceChain configures a new tracer according to the provided configuration, and -// executes all the transactions contained within. The return value will be one item -// per transaction, dependent on the requested tracer. -func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) { - return traceChain(ctx, api.eth, start, end, config) -} - // TraceBlockByNumber returns the structured logs created during the execution of // EVM and returns them as a JSON object. -func traceBlockByNumber(ctx context.Context, eth *Ethereum, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { - // Fetch the block that we want to trace - var block *types.Block - - switch number { - case rpc.PendingBlockNumber: - block = eth.miner.PendingBlock() - case rpc.LatestBlockNumber: - block = eth.blockchain.CurrentBlock() - default: - block = eth.blockchain.GetBlockByNumber(uint64(number)) - } - // Trace the block if it was found - if block == nil { - return nil, fmt.Errorf("block #%d not found", number) +func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { + block, err := api.blockByNumber(ctx, number) + if err != nil { + return nil, err } - return traceBlock(ctx, eth, block, config) -} - -// TraceBlockByNumber returns the structured logs created during the execution of -// EVM and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { - return traceBlockByNumber(ctx, api.eth, number, config) + return api.traceBlock(ctx, block, config) } // TraceBlockByHash returns the structured logs created during the execution of // EVM and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { - block := api.eth.blockchain.GetBlockByHash(hash) - if block == nil { - return nil, fmt.Errorf("block %#x not found", hash) +func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { + block, err := api.blockByHash(ctx, hash) + if err != nil { + return nil, err } return api.traceBlock(ctx, block, config) } -// traceBlockRLP returns the structured logs created during the execution of EVM +// TraceBlock returns the structured logs created during the execution of EVM // and returns them as a JSON object. -func traceBlockRLP(ctx context.Context, eth *Ethereum, blob []byte, config *TraceConfig) ([]*txTraceResult, error) { +func (api *API) TraceBlock(ctx context.Context, blob []byte, config *TraceConfig) ([]*txTraceResult, error) { block := new(types.Block) if err := rlp.Decode(bytes.NewReader(blob), block); err != nil { return nil, fmt.Errorf("could not decode block: %v", err) } - return traceBlock(ctx, eth, block, config) -} - -// TraceBlock returns the structured logs created during the execution of EVM -// and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceBlock(ctx context.Context, blob []byte, config *TraceConfig) ([]*txTraceResult, error) { - return traceBlockRLP(ctx, api.eth, blob, config) + return api.traceBlock(ctx, block, config) } // TraceBlockFromFile returns the structured logs created during the execution of // EVM and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) { +func (api *API) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) { blob, err := ioutil.ReadFile(file) if err != nil { return nil, fmt.Errorf("could not read file: %v", err) @@ -428,9 +444,8 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string, // TraceBadBlock returns the structured logs created during the execution of // EVM against a block pulled from the pool of bad ones and returns them as a JSON // object. -func (api *PrivateDebugAPI) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { - blocks := api.eth.blockchain.BadBlocks() - for _, block := range blocks { +func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { + for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) { if block.Hash() == hash { return api.traceBlock(ctx, block, config) } @@ -441,10 +456,10 @@ func (api *PrivateDebugAPI) TraceBadBlock(ctx context.Context, hash common.Hash, // StandardTraceBlockToFile dumps the structured logs created during the // execution of EVM to the local file system and returns a list of files // to the caller. -func (api *PrivateDebugAPI) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { - block := api.eth.blockchain.GetBlockByHash(hash) - if block == nil { - return nil, fmt.Errorf("block %#x not found", hash) +func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { + block, err := api.blockByHash(ctx, hash) + if err != nil { + return nil, err } return api.standardTraceBlockToFile(ctx, block, config) } @@ -452,9 +467,8 @@ func (api *PrivateDebugAPI) StandardTraceBlockToFile(ctx context.Context, hash c // StandardTraceBadBlockToFile dumps the structured logs created during the // execution of EVM against a block pulled from the pool of bad ones to the // local file system and returns a list of files to the caller. -func (api *PrivateDebugAPI) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { - blocks := api.eth.blockchain.BadBlocks() - for _, block := range blocks { +func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { + for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) { if block.Hash() == hash { return api.standardTraceBlockToFile(ctx, block, config) } @@ -465,27 +479,25 @@ func (api *PrivateDebugAPI) StandardTraceBadBlockToFile(ctx context.Context, has // traceBlock configures a new tracer according to the provided configuration, and // executes all the transactions contained within. The return value will be one item // per transaction, dependent on the requestd tracer. -func traceBlock(ctx context.Context, eth *Ethereum, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { - // Create the parent state database - if err := eth.engine.VerifyHeader(eth.blockchain, block.Header(), true); err != nil { - return nil, err +func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { + if block.NumberU64() == 0 { + return nil, errors.New("genesis is not traceable") } - parent := eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) - if parent == nil { - return nil, fmt.Errorf("parent %#x not found", block.ParentHash()) + parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) + if err != nil { + return nil, err } reexec := defaultTraceReexec if config != nil && config.Reexec != nil { reexec = *config.Reexec } - statedb, err := computeStateDB(eth, parent, reexec) + statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true) if err != nil { return nil, err } // Execute all the transaction contained within the block concurrently var ( - signer = types.MakeSigner(eth.blockchain.Config(), block.Number()) - + signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) txs = block.Transactions() results = make([]*txTraceResult, len(txs)) @@ -496,17 +508,21 @@ func traceBlock(ctx context.Context, eth *Ethereum, block *types.Block, config * if threads > len(txs) { threads = len(txs) } + blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + blockHash := block.Hash() for th := 0; th < threads; th++ { pend.Add(1) go func() { defer pend.Done() - // Fetch and execute the next transaction trace tasks for task := range jobs { msg, _ := txs[task.index].AsMessage(signer) - vmctx := core.NewEVMContext(msg, block.Header(), eth.blockchain, nil) - - res, err := traceTx(ctx, eth, msg, vmctx, task.statedb, task.taskExtraContext, config) + txctx := &txTraceContext{ + index: task.index, + hash: txs[task.index].Hash(), + block: blockHash, + } + res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, nil, config) if err != nil { results[task.index] = &txTraceResult{Error: err.Error()} continue @@ -518,28 +534,20 @@ func traceBlock(ctx context.Context, eth *Ethereum, block *types.Block, config * // Feed the transactions into the tracers and return var failed error for i, tx := range txs { - taskExtraContext := map[string]interface{}{ - "blockNumber": block.NumberU64(), - "blockHash": block.Hash().Hex(), - "transactionHash": tx.Hash().Hex(), - "transactionPosition": uint64(i), - } - // Send the trace task over for execution - jobs <- &txTraceTask{statedb: statedb.Copy(), index: i, taskExtraContext: taskExtraContext} + jobs <- &txTraceTask{statedb: statedb.Copy(), index: i} // Generate the next state snapshot fast without tracing msg, _ := tx.AsMessage(signer) - vmctx := core.NewEVMContext(msg, block.Header(), eth.blockchain, nil) - - vmenv := vm.NewEVM(vmctx, statedb, eth.blockchain.Config(), vm.Config{}) + statedb.Prepare(tx.Hash(), block.Hash(), i) + vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { failed = err break } // Finalize the state so any modifications are written to the trie // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - statedb.Finalise(vmenv.ChainConfig().IsEnabled(vmenv.ChainConfig().GetEIP161dTransition, block.Number())) + statedb.Finalise(vmenv.ChainConfig().IsEnabled(api.backend.ChainConfig().GetEIP161dTransition, block.Number())) } close(jobs) pend.Wait() @@ -551,33 +559,28 @@ func traceBlock(ctx context.Context, eth *Ethereum, block *types.Block, config * return results, nil } -func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { - return traceBlock(ctx, api.eth, block, config) -} - // standardTraceBlockToFile configures a new tracer which uses standard JSON output, // and traces either a full block or an individual transaction. The return value will // be one filename per transaction traced. -func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) { +func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) { // If we're tracing a single transaction, make sure it's present if config != nil && config.TxHash != (common.Hash{}) { if !containsTx(block, config.TxHash) { return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash) } } - // Create the parent state database - if err := api.eth.engine.VerifyHeader(api.eth.blockchain, block.Header(), true); err != nil { - return nil, err + if block.NumberU64() == 0 { + return nil, errors.New("genesis is not traceable") } - parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) - if parent == nil { - return nil, fmt.Errorf("parent %#x not found", block.ParentHash()) + parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) + if err != nil { + return nil, err } reexec := defaultTraceReexec if config != nil && config.Reexec != nil { reexec = *config.Reexec } - statedb, err := computeStateDB(api.eth, parent, reexec) + statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true) if err != nil { return nil, err } @@ -587,18 +590,17 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block txHash common.Hash ) if config != nil { - if config.LogConfig != nil { - logConfig = *config.LogConfig - } + logConfig = config.LogConfig txHash = config.TxHash } logConfig.Debug = true // Execute transaction, either tracing all or just the requested one var ( - signer = types.MakeSigner(api.eth.blockchain.Config(), block.Number()) dumps []string - chainConfig = api.eth.blockchain.Config() + signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) + chainConfig = api.backend.ChainConfig() + vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) canon = true ) // Check if there are any overrides: the caller may wish to enable a future @@ -608,7 +610,6 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block // Therefore, it's perfectly valid to specify `"futureForkBlock": 0`, to enable `futureFork` if config != nil && config.Overrides != nil { - overrideEIP2929 := config.Overrides.GetEIP2929Transition() existingEIP2929 := chainConfig.GetEIP2929Transition() @@ -634,13 +635,12 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block for i, tx := range block.Transactions() { // Prepare the trasaction for un-traced execution var ( - msg, _ = tx.AsMessage(signer) - vmctx = core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) - - vmConf vm.Config - dump *os.File - writer *bufio.Writer - err error + msg, _ = tx.AsMessage(signer) + txContext = core.NewEVMTxContext(msg) + vmConf vm.Config + dump *os.File + writer *bufio.Writer + err error ) // If the transaction needs tracing, swap out the configs if tx.Hash() == txHash || txHash == (common.Hash{}) { @@ -664,7 +664,8 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block } } // Execute the transaction and flush any traces to disk - vmenv := vm.NewEVM(vmctx, statedb, chainConfig, vmConf) + vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) + statedb.Prepare(tx.Hash(), block.Hash(), i) _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) if writer != nil { writer.Flush() @@ -678,7 +679,7 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block } // Finalize the state so any modifications are written to the trie // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - statedb.Finalise(vmenv.ChainConfig().IsEnabled(vmenv.ChainConfig().GetEIP161dTransition, block.Number())) + statedb.Finalise(vmenv.ChainConfig().IsEnabled(api.backend.ChainConfig().GetEIP161dTransition, block.Number())) // If we've traced the transaction we were looking for, abort if tx.Hash() == txHash { @@ -699,150 +700,67 @@ func containsTx(block *types.Block, hash common.Hash) bool { return false } -// computeStateDB retrieves the state database associated with a certain block. -// If no state is locally available for the given block, a number of blocks are -// attempted to be reexecuted to generate the desired state. -func computeStateDB(eth *Ethereum, block *types.Block, reexec uint64) (*state.StateDB, error) { - // If we have the state fully available, use that - statedb, err := eth.blockchain.StateAt(block.Root()) - if err == nil { - return statedb, nil - } - // Otherwise try to reexec blocks until we find a state or reach our limit - origin := block.NumberU64() - database := state.NewDatabaseWithCache(eth.ChainDb(), 16, "") - - for i := uint64(0); i < reexec; i++ { - block = eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) - if block == nil { - break - } - if statedb, err = state.New(block.Root(), database, nil); err == nil { - break - } - } +// TraceTransaction returns the structured logs created during the execution of EVM +// and returns them as a JSON object. +func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { + _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) if err != nil { - switch err.(type) { - case *trie.MissingNodeError: - return nil, fmt.Errorf("required historical state unavailable (reexec=%d)", reexec) - default: - return nil, err - } + return nil, err } - // State was available at historical point, regenerate - var ( - start = time.Now() - logged time.Time - proot common.Hash - ) - for block.NumberU64() < origin { - // Print progress logs if long enough time elapsed - if time.Since(logged) > 8*time.Second { - log.Info("Regenerating historical state", "block", block.NumberU64()+1, "target", origin, "remaining", origin-block.NumberU64()-1, "elapsed", time.Since(start)) - logged = time.Now() - } - // Retrieve the next block to regenerate and process it - if block = eth.blockchain.GetBlockByNumber(block.NumberU64() + 1); block == nil { - return nil, fmt.Errorf("block #%d not found", block.NumberU64()+1) - } - _, _, _, err := eth.blockchain.Processor().Process(block, statedb, vm.Config{}) - if err != nil { - return nil, fmt.Errorf("processing block %d failed: %v", block.NumberU64(), err) - } - // Finalize the state so any modifications are written to the trie - root, err := statedb.Commit(eth.blockchain.Config().IsEnabled(eth.blockchain.Config().GetEIP161dTransition, block.Number())) - if err != nil { - return nil, err - } - if err := statedb.Reset(root); err != nil { - return nil, fmt.Errorf("state reset after block %d failed: %v", block.NumberU64(), err) - } - database.TrieDB().Reference(root, common.Hash{}) - if proot != (common.Hash{}) { - database.TrieDB().Dereference(proot) - } - proot = root - } - nodes, imgs := database.TrieDB().Size() - log.Info("Historical state regenerated", "block", block.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs) - return statedb, nil -} - -func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*state.StateDB, error) { - return computeStateDB(api.eth, block, reexec) -} - -// traceTransaction returns the structured logs created during the execution of EVM -// and returns them as a JSON object. -func traceTransaction(ctx context.Context, eth *Ethereum, hash common.Hash, config *TraceConfig) (interface{}, error) { - // Retrieve the transaction and assemble its EVM context - tx, blockHash, _, index := rawdb.ReadTransaction(eth.ChainDb(), hash) - if tx == nil { - return nil, fmt.Errorf("transaction %#x not found", hash) + // It shouldn't happen in practice. + if blockNumber == 0 { + return nil, errors.New("genesis is not traceable") } reexec := defaultTraceReexec if config != nil && config.Reexec != nil { reexec = *config.Reexec } - // Retrieve the block - block := eth.blockchain.GetBlockByHash(blockHash) - if block == nil { - return nil, fmt.Errorf("block %#x not found", blockHash) + block, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNumber), blockHash) + if err != nil { + return nil, err } - msg, vmctx, statedb, err := computeTxEnv(eth, block, int(index), reexec) + msg, vmctx, statedb, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec) if err != nil { return nil, err } - - taskExtraContext := map[string]interface{}{ - "blockNumber": block.NumberU64(), - "blockHash": blockHash.Hex(), - "transactionHash": tx.Hash().Hex(), - "transactionPosition": index, + txctx := &txTraceContext{ + index: int(index), + hash: hash, + block: blockHash, } - - // Trace the transaction and return - return traceTx(ctx, eth, msg, vmctx, statedb, taskExtraContext, config) + return api.traceTx(ctx, msg, txctx, vmctx, statedb, nil, config) } -// TraceTransaction returns the structured logs created during the execution of EVM -// and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { - return traceTransaction(ctx, api.eth, hash, config) -} - -// traceCall lets you trace a given eth_call. It collects the structured logs created during the execution of EVM -// if the given transaction was added on top of the provided block and returns them as a JSON object. +// TraceCall lets you trace a given eth_call. It collects the structured logs +// created during the execution of EVM if the given transaction was added on +// top of the provided block and returns them as a JSON object. // You can provide -2 as a block number to trace on top of the pending block. -func traceCall(ctx context.Context, eth *Ethereum, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { - // First try to retrieve the state - blockNrOrHash.RequireCanonical = true - statedb, header, err := eth.APIBackend.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) +func (api *API) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { + // Try to retrieve the specified block + var ( + err error + block *types.Block + ) + if hash, ok := blockNrOrHash.Hash(); ok { + block, err = api.blockByHash(ctx, hash) + } else if number, ok := blockNrOrHash.Number(); ok { + block, err = api.blockByNumber(ctx, number) + } if err != nil { - // Try to retrieve the specified block - var block *types.Block - if hash, ok := blockNrOrHash.Hash(); ok { - block = eth.blockchain.GetBlockByHash(hash) - } else if number, ok := blockNrOrHash.Number(); ok { - block = eth.blockchain.GetBlockByNumber(uint64(number)) - } - if block == nil { - return nil, fmt.Errorf("block %v not found: %v", blockNrOrHash, err) - } - // try to recompute the state - reexec := defaultTraceReexec - if config != nil && config.Reexec != nil { - reexec = *config.Reexec - } - _, _, statedb, err = computeTxEnv(eth, block, len(block.Transactions())-1, reexec) - if err != nil { - return nil, err - } + return nil, err + } + // try to recompute the state + reexec := defaultTraceReexec + if config != nil && config.Reexec != nil { + reexec = *config.Reexec + } + statedb, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true) + if err != nil { + return nil, err } - // Execute the trace - msg := args.ToMessage(eth.APIBackend.RPCGasCap()) - vmctx := core.NewEVMContext(msg, header, eth.blockchain, nil) + msg := args.ToMessage(api.backend.RPCGasCap()) + vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) originalCanTransfer := vmctx.CanTransfer originalTransfer := vmctx.Transfer @@ -882,49 +800,41 @@ func traceCall(ctx context.Context, eth *Ethereum, args ethapi.CallArgs, blockNr "hasFromSufficientBalanceForGasCost": hasFromSufficientBalanceForGasCost, } - return traceTx(ctx, eth, msg, vmctx, statedb, taskExtraContext, config) -} - -// TraceCall lets you trace a given eth_call. It collects the structured logs created during the execution of EVM -// if the given transaction was added on top of the provided block and returns them as a JSON object. -// You can provide -2 as a block number to trace on top of the pending block. -func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { - return traceCall(ctx, api.eth, args, blockNrOrHash, config) + return api.traceTx(ctx, msg, new(txTraceContext), vmctx, statedb, taskExtraContext, config) } // TraceCallMany lets you trace a given eth_call. It collects the structured logs created during the execution of EVM // if the given transaction was added on top of the provided block and returns them as a JSON object. // You can provide -2 as a block number to trace on top of the pending block. -func traceCallMany(ctx context.Context, eth *Ethereum, txs []ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { - // First try to retrieve the state - statedb, header, err := eth.APIBackend.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) +func (api *API) TraceCallMany(ctx context.Context, txs []ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { + // Try to retrieve the specified block + var ( + err error + block *types.Block + ) + if hash, ok := blockNrOrHash.Hash(); ok { + block, err = api.blockByHash(ctx, hash) + } else if number, ok := blockNrOrHash.Number(); ok { + block, err = api.blockByNumber(ctx, number) + } if err != nil { - // Try to retrieve the specified block - var block *types.Block - if hash, ok := blockNrOrHash.Hash(); ok { - block = eth.blockchain.GetBlockByHash(hash) - } else if number, ok := blockNrOrHash.Number(); ok { - block = eth.blockchain.GetBlockByNumber(uint64(number)) - } - if block == nil { - return nil, fmt.Errorf("block %v not found: %v", blockNrOrHash, err) - } - // try to recompute the state - reexec := defaultTraceReexec - if config != nil && config.Reexec != nil { - reexec = *config.Reexec - } - _, _, statedb, err = computeTxEnv(eth, block, len(block.Transactions())-1, reexec) - if err != nil { - return nil, err - } + return nil, err + } + // try to recompute the state + reexec := defaultTraceReexec + if config != nil && config.Reexec != nil { + reexec = *config.Reexec + } + statedb, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true) + if err != nil { + return nil, err } - // Execute the trace var results = make([]interface{}, len(txs)) for idx, args := range txs { - msg := args.ToMessage(eth.APIBackend.RPCGasCap()) - vmctx := core.NewEVMContext(msg, header, eth.blockchain, nil) + // Execute the trace + msg := args.ToMessage(api.backend.RPCGasCap()) + vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) originalCanTransfer := vmctx.CanTransfer originalTransfer := vmctx.Transfer @@ -964,7 +874,7 @@ func traceCallMany(ctx context.Context, eth *Ethereum, txs []ethapi.CallArgs, bl "hasFromSufficientBalanceForGasCost": hasFromSufficientBalanceForGasCost, } - res, err := traceTx(ctx, eth, msg, vmctx, statedb, taskExtraContext, config) + res, err := api.traceTx(ctx, msg, new(txTraceContext), vmctx, statedb, taskExtraContext, config) if err != nil { results[idx] = &txTraceResult{Error: err.Error()} continue @@ -983,11 +893,12 @@ func traceCallMany(ctx context.Context, eth *Ethereum, txs []ethapi.CallArgs, bl // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func traceTx(ctx context.Context, eth *Ethereum, message core.Message, vmctx vm.Context, statedb *state.StateDB, extraContext map[string]interface{}, config *TraceConfig) (interface{}, error) { +func (api *API) traceTx(ctx context.Context, message core.Message, txctx *txTraceContext, vmctx vm.BlockContext, statedb *state.StateDB, extraContext map[string]interface{}, config *TraceConfig) (interface{}, error) { // Assemble the structured logger or the JavaScript tracer var ( - tracer vm.Tracer - err error + tracer vm.Tracer + err error + txContext = core.NewEVMTxContext(message) ) switch { case config != nil && config.Tracer != nil: @@ -999,14 +910,16 @@ func traceTx(ctx context.Context, eth *Ethereum, message core.Message, vmctx vm. } } // Constuct the JavaScript tracer to execute with - if tracer, err = tracers.New(*config.Tracer); err != nil { + if tracer, err = New(*config.Tracer, txContext); err != nil { return nil, err } // Handle timeouts and RPC cancellations deadlineCtx, cancel := context.WithTimeout(ctx, timeout) go func() { <-deadlineCtx.Done() - tracer.(*tracers.Tracer).Stop(errors.New("execution timeout")) + if deadlineCtx.Err() == context.DeadlineExceeded { + tracer.(*Tracer).Stop(errors.New("execution timeout")) + } }() defer cancel() @@ -1017,14 +930,25 @@ func traceTx(ctx context.Context, eth *Ethereum, message core.Message, vmctx vm. tracer = vm.NewStructLogger(config.LogConfig) } // Run the transaction with tracing enabled. - vmenv := vm.NewEVM(vmctx, statedb, eth.blockchain.Config(), vm.Config{Debug: true, Tracer: tracer}) + vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer}) + + // Call Prepare to clear out the statedb access list + statedb.Prepare(txctx.hash, txctx.block, txctx.index) switch tracer := tracer.(type) { - case *tracers.Tracer: + case *Tracer: if extraContext == nil { extraContext = map[string]interface{}{} } + if txctx.hash != (common.Hash{}) { + extraContext["transactionHash"] = txctx.hash.Hex() + } + if txctx.block != (common.Hash{}) { + extraContext["blockHash"] = txctx.block.Hex() + } + extraContext["transactionPosition"] = uint64(txctx.index) + // Add useful context for all tracers extraContext["from"] = message.From() if message.To() != nil { @@ -1042,7 +966,8 @@ func traceTx(ctx context.Context, eth *Ethereum, message core.Message, vmctx vm. if err != nil { return nil, fmt.Errorf("tracing failed: %v", err) } - // Depending on the tracer type, format and return the output + + // Depending on the tracer type, format and return the output. switch tracer := tracer.(type) { case *vm.StructLogger: // If the result contains a revert reason, return it. @@ -1057,7 +982,7 @@ func traceTx(ctx context.Context, eth *Ethereum, message core.Message, vmctx vm. StructLogs: ethapi.FormatLogs(tracer.StructLogs()), }, nil - case *tracers.Tracer: + case *Tracer: return tracer.GetResult() default: @@ -1065,51 +990,23 @@ func traceTx(ctx context.Context, eth *Ethereum, message core.Message, vmctx vm. } } -// traceTx configures a new tracer according to the provided configuration, and -// executes the given message in the provided environment. The return value will -// be tracer dependent. -func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, vmctx vm.Context, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { - return traceTx(ctx, api.eth, message, vmctx, statedb, nil, config) -} - -// computeTxEnv returns the execution environment of a certain transaction. -func computeTxEnv(eth *Ethereum, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, error) { - // Create the parent state database - parent := eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) - if parent == nil { - return nil, vm.Context{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash()) - } - statedb, err := computeStateDB(eth, parent, reexec) - if err != nil { - return nil, vm.Context{}, nil, err - } +// APIs return the collection of RPC services the tracer package offers. +func APIs(backend Backend) []rpc.API { + debugAPI := NewAPI(backend) - if txIndex == 0 && len(block.Transactions()) == 0 { - return nil, vm.Context{}, statedb, nil + // Append all the local APIs and return + return []rpc.API{ + { + Namespace: "debug", + Version: "1.0", + Service: debugAPI, + Public: false, + }, + { + Namespace: "trace", + Version: "1.0", + Service: NewTraceAPI(debugAPI), + Public: false, + }, } - - // Recompute transactions up to the target index. - signer := types.MakeSigner(eth.blockchain.Config(), block.Number()) - - for idx, tx := range block.Transactions() { - // Assemble the transaction call message and return if the requested offset - msg, _ := tx.AsMessage(signer) - context := core.NewEVMContext(msg, block.Header(), eth.blockchain, nil) - if idx == txIndex { - return msg, context, statedb, nil - } - // Not yet the searched for transaction, execute on top of the current state - vmenv := vm.NewEVM(context, statedb, eth.blockchain.Config(), vm.Config{}) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { - return nil, vm.Context{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) - } - // Ensure any modifications are committed to the state - // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - statedb.Finalise(vmenv.ChainConfig().IsEnabled(vmenv.ChainConfig().GetEIP161dTransition, block.Number())) - } - return nil, vm.Context{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) -} - -func (api *PrivateDebugAPI) computeTxEnv(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, error) { - return computeTxEnv(api.eth, block, txIndex, reexec) } diff --git a/eth/api_tracer_parity.go b/eth/tracers/api_parity.go similarity index 74% rename from eth/api_tracer_parity.go rename to eth/tracers/api_parity.go index 80a2f4a6cf..535b93fc46 100644 --- a/eth/api_tracer_parity.go +++ b/eth/tracers/api_parity.go @@ -14,12 +14,11 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package eth +package tracers import ( "context" "encoding/json" - "fmt" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -74,6 +73,18 @@ func setTraceConfigDefaultTracer(config *TraceConfig) *TraceConfig { return config } +// TraceAPI is the collection of Ethereum full node APIs exposed over +// the private debugging endpoint. +type TraceAPI struct { + debugAPI *API +} + +// NewTraceAPI creates a new API definition for the full node-related +// private debug methods of the Ethereum service. +func NewTraceAPI(debugAPI *API) *TraceAPI { + return &TraceAPI{debugAPI: debugAPI} +} + // decorateResponse applies formatting to trace results if needed. func decorateResponse(res interface{}, config *TraceConfig) (interface{}, error) { if config != nil && config.NestedTraceOutput && config.Tracer != nil { @@ -109,8 +120,9 @@ func decorateNestedTraceResponse(res interface{}, tracer string) interface{} { return out } -func traceBlockReward(ctx context.Context, eth *Ethereum, block *types.Block, config *TraceConfig) (*ParityTrace, error) { - chainConfig := eth.blockchain.Config() +// traceBlockReward retrieve the block reward for the coinbase address +func (api *TraceAPI) traceBlockReward(ctx context.Context, block *types.Block, config *TraceConfig) (*ParityTrace, error) { + chainConfig := api.debugAPI.backend.ChainConfig() minerReward, _ := ethash.GetRewards(chainConfig, block.Header(), block.Uncles()) coinbase := block.Coinbase() @@ -130,8 +142,9 @@ func traceBlockReward(ctx context.Context, eth *Ethereum, block *types.Block, co return tr, nil } -func traceBlockUncleRewards(ctx context.Context, eth *Ethereum, block *types.Block, config *TraceConfig) ([]*ParityTrace, error) { - chainConfig := eth.blockchain.Config() +// traceBlockUncleRewards retrieve the block rewards for uncle addresses +func (api *TraceAPI) traceBlockUncleRewards(ctx context.Context, block *types.Block, config *TraceConfig) ([]*ParityTrace, error) { + chainConfig := api.debugAPI.backend.ChainConfig() _, uncleRewards := ethash.GetRewards(chainConfig, block.Header(), block.Uncles()) results := make([]*ParityTrace, len(uncleRewards)) @@ -159,36 +172,25 @@ func traceBlockUncleRewards(ctx context.Context, eth *Ethereum, block *types.Blo // Block returns the structured logs created during the execution of // EVM and returns them as a JSON object. // The correct name will be TraceBlockByNumber, though we want to be compatible with Parity trace module. -func (api *PrivateTraceAPI) Block(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]interface{}, error) { - // Fetch the block that we want to trace - var block *types.Block - - switch number { - case rpc.PendingBlockNumber: - block = api.eth.miner.PendingBlock() - case rpc.LatestBlockNumber: - block = api.eth.blockchain.CurrentBlock() - default: - block = api.eth.blockchain.GetBlockByNumber(uint64(number)) - } - // Trace the block if it was found - if block == nil { - return nil, fmt.Errorf("block #%d not found", number) - } - +func (api *TraceAPI) Block(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]interface{}, error) { config = setTraceConfigDefaultTracer(config) - traceResults, err := traceBlockByNumber(ctx, api.eth, number, config) + block, err := api.debugAPI.blockByNumber(ctx, number) if err != nil { return nil, err } - traceReward, err := traceBlockReward(ctx, api.eth, block, config) + traceResults, err := api.debugAPI.traceBlock(ctx, block, config) if err != nil { return nil, err } - traceUncleRewards, err := traceBlockUncleRewards(ctx, api.eth, block, config) + traceReward, err := api.traceBlockReward(ctx, block, config) + if err != nil { + return nil, err + } + + traceUncleRewards, err := api.traceBlockUncleRewards(ctx, block, config) if err != nil { return nil, err } @@ -214,43 +216,30 @@ func (api *PrivateTraceAPI) Block(ctx context.Context, number rpc.BlockNumber, c // Transaction returns the structured logs created during the execution of EVM // and returns them as a JSON object. -func (api *PrivateTraceAPI) Transaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { +func (api *TraceAPI) Transaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { config = setTraceConfigDefaultTracer(config) - return traceTransaction(ctx, api.eth, hash, config) + return api.debugAPI.TraceTransaction(ctx, hash, config) } // Filter configures a new tracer according to the provided configuration, and // executes all the transactions contained within. The return value will be one item // per transaction, dependent on the requested tracer. -func (api *PrivateTraceAPI) Filter(ctx context.Context, args TraceFilterArgs, config *TraceConfig) (*rpc.Subscription, error) { +func (api *TraceAPI) Filter(ctx context.Context, args TraceFilterArgs, config *TraceConfig) (*rpc.Subscription, error) { config = setTraceConfigDefaultTracer(config) // Fetch the block interval that we want to trace - start := uint64(args.FromBlock) - end := uint64(args.ToBlock) - - from := api.eth.blockchain.GetBlockByNumber(start) - to := api.eth.blockchain.GetBlockByNumber(end) + start := rpc.BlockNumber(args.FromBlock) + end := rpc.BlockNumber(args.ToBlock) - // Trace the chain if we've found all our blocks - if from == nil { - return nil, fmt.Errorf("starting block #%d not found", start) - } - if to == nil { - return nil, fmt.Errorf("end block #%d not found", end) - } - if from.Number().Cmp(to.Number()) >= 0 { - return nil, fmt.Errorf("end block (#%d) needs to come after start block (#%d)", end, start) - } - return traceChain(ctx, api.eth, from, to, config) + return api.debugAPI.TraceChain(ctx, start, end, config) } // Call lets you trace a given eth_call. It collects the structured logs created during the execution of EVM // if the given transaction was added on top of the provided block and returns them as a JSON object. // You can provide -2 as a block number to trace on top of the pending block. -func (api *PrivateTraceAPI) Call(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { +func (api *TraceAPI) Call(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { config = setTraceConfigDefaultTracer(config) - res, err := traceCall(ctx, api.eth, args, blockNrOrHash, config) + res, err := api.debugAPI.TraceCall(ctx, args, blockNrOrHash, config) if err != nil { return nil, err } @@ -260,11 +249,7 @@ func (api *PrivateTraceAPI) Call(ctx context.Context, args ethapi.CallArgs, bloc // CallMany lets you trace a given eth_call. It collects the structured logs created during the execution of EVM // if the given transaction was added on top of the provided block and returns them as a JSON object. // You can provide -2 as a block number to trace on top of the pending block. -func (api *PrivateTraceAPI) CallMany(ctx context.Context, txs []ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { +func (api *TraceAPI) CallMany(ctx context.Context, txs []ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { config = setTraceConfigDefaultTracer(config) - res, err := traceCallMany(ctx, api.eth, txs, blockNrOrHash, config) - if err != nil { - return nil, err - } - return res, nil + return api.debugAPI.TraceCallMany(ctx, txs, blockNrOrHash, config) } diff --git a/eth/api_tracer_parity_test.go b/eth/tracers/api_parity_test.go similarity index 98% rename from eth/api_tracer_parity_test.go rename to eth/tracers/api_parity_test.go index b5543f1e50..f630ab1a8d 100644 --- a/eth/api_tracer_parity_test.go +++ b/eth/tracers/api_parity_test.go @@ -1,4 +1,4 @@ -package eth +package tracers import ( "testing" diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go new file mode 100644 index 0000000000..1704b1fa37 --- /dev/null +++ b/eth/tracers/api_test.go @@ -0,0 +1,474 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package tracers + +import ( + "bytes" + "context" + "crypto/ecdsa" + "errors" + "fmt" + "math/big" + "reflect" + "sort" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/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/internal/ethapi" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/params/types/ctypes" + "github.com/ethereum/go-ethereum/params/types/genesisT" + "github.com/ethereum/go-ethereum/params/vars" + "github.com/ethereum/go-ethereum/rpc" +) + +var ( + errStateNotFound = errors.New("state not found") + errBlockNotFound = errors.New("block not found") + errTransactionNotFound = errors.New("transaction not found") +) + +type testBackend struct { + chainConfig ctypes.ChainConfigurator + engine consensus.Engine + chaindb ethdb.Database + chain *core.BlockChain +} + +func newTestBackend(t *testing.T, n int, gspec *genesisT.Genesis, generator func(i int, b *core.BlockGen)) *testBackend { + backend := &testBackend{ + chainConfig: params.TestChainConfig, + engine: ethash.NewFaker(), + chaindb: rawdb.NewMemoryDatabase(), + } + // Generate blocks for testing + gspec.Config = backend.chainConfig + var ( + gendb = rawdb.NewMemoryDatabase() + genesis = core.MustCommitGenesis(gendb, gspec) + ) + blocks, _ := core.GenerateChain(backend.chainConfig, genesis, backend.engine, gendb, n, generator) + + // Import the canonical chain + core.MustCommitGenesis(backend.chaindb, gspec) + cacheConfig := &core.CacheConfig{ + TrieCleanLimit: 256, + TrieDirtyLimit: 256, + TrieTimeLimit: 5 * time.Minute, + SnapshotLimit: 0, + TrieDirtyDisabled: true, // Archive mode + } + chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, backend.chainConfig, backend.engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + if n, err := chain.InsertChain(blocks); err != nil { + t.Fatalf("block %d: failed to insert into chain: %v", n, err) + } + backend.chain = chain + return backend +} + +func (b *testBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { + return b.chain.GetHeaderByHash(hash), nil +} + +func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { + if number == rpc.PendingBlockNumber || number == rpc.LatestBlockNumber { + return b.chain.CurrentHeader(), nil + } + return b.chain.GetHeaderByNumber(uint64(number)), nil +} + +func (b *testBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { + return b.chain.GetBlockByHash(hash), nil +} + +func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { + if number == rpc.PendingBlockNumber || number == rpc.LatestBlockNumber { + return b.chain.CurrentBlock(), nil + } + return b.chain.GetBlockByNumber(uint64(number)), nil +} + +func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { + tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash) + if tx == nil { + return nil, common.Hash{}, 0, 0, errTransactionNotFound + } + return tx, hash, blockNumber, index, nil +} + +func (b *testBackend) RPCGasCap() uint64 { + return 25000000 +} + +func (b *testBackend) ChainConfig() ctypes.ChainConfigurator { + return b.chainConfig +} + +func (b *testBackend) Engine() consensus.Engine { + return b.engine +} + +func (b *testBackend) ChainDb() ethdb.Database { + return b.chaindb +} + +func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) { + statedb, err := b.chain.StateAt(block.Root()) + if err != nil { + return nil, errStateNotFound + } + return statedb, nil +} + +func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { + parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) + if parent == nil { + return nil, vm.BlockContext{}, nil, errBlockNotFound + } + statedb, err := b.chain.StateAt(parent.Root()) + if err != nil { + return nil, vm.BlockContext{}, nil, errStateNotFound + } + if txIndex == 0 && len(block.Transactions()) == 0 { + return nil, vm.BlockContext{}, statedb, nil + } + // Recompute transactions up to the target index. + signer := types.MakeSigner(b.chainConfig, block.Number()) + for idx, tx := range block.Transactions() { + msg, _ := tx.AsMessage(signer) + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(block.Header(), b.chain, nil) + if idx == txIndex { + return msg, context, statedb, nil + } + vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{}) + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { + return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) + } + statedb.Finalise(vmenv.ChainConfig().IsEnabled(vmenv.ChainConfig().GetEIP161dTransition, block.Number())) + } + return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) +} + +func TestTraceCall(t *testing.T) { + t.Parallel() + + // Initialize test accounts + accounts := newAccounts(3) + genesis := &genesisT.Genesis{Alloc: genesisT.GenesisAlloc{ + accounts[0].addr: {Balance: big.NewInt(vars.Ether)}, + accounts[1].addr: {Balance: big.NewInt(vars.Ether)}, + accounts[2].addr: {Balance: big.NewInt(vars.Ether)}, + }} + genBlocks := 10 + signer := types.HomesteadSigner{} + api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { + // Transfer from account[0] to account[1] + // value: 1000 wei + // fee: 0 wei + tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), vars.TxGas, big.NewInt(0), nil), signer, accounts[0].key) + b.AddTx(tx) + })) + + var testSuite = []struct { + blockNumber rpc.BlockNumber + call ethapi.CallArgs + config *TraceConfig + expectErr error + expect interface{} + }{ + // Standard JSON trace upon the genesis, plain transfer. + { + blockNumber: rpc.BlockNumber(0), + call: ethapi.CallArgs{ + From: &accounts[0].addr, + To: &accounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1000)), + }, + config: nil, + expectErr: nil, + expect: ðapi.ExecutionResult{ + Gas: vars.TxGas, + Failed: false, + ReturnValue: "", + StructLogs: []ethapi.StructLogRes{}, + }, + }, + // Standard JSON trace upon the head, plain transfer. + { + blockNumber: rpc.BlockNumber(genBlocks), + call: ethapi.CallArgs{ + From: &accounts[0].addr, + To: &accounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1000)), + }, + config: nil, + expectErr: nil, + expect: ðapi.ExecutionResult{ + Gas: vars.TxGas, + Failed: false, + ReturnValue: "", + StructLogs: []ethapi.StructLogRes{}, + }, + }, + // Standard JSON trace upon the non-existent block, error expects + { + blockNumber: rpc.BlockNumber(genBlocks + 1), + call: ethapi.CallArgs{ + From: &accounts[0].addr, + To: &accounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1000)), + }, + config: nil, + expectErr: fmt.Errorf("block #%d not found", genBlocks+1), + expect: nil, + }, + // Standard JSON trace upon the latest block + { + blockNumber: rpc.LatestBlockNumber, + call: ethapi.CallArgs{ + From: &accounts[0].addr, + To: &accounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1000)), + }, + config: nil, + expectErr: nil, + expect: ðapi.ExecutionResult{ + Gas: vars.TxGas, + Failed: false, + ReturnValue: "", + StructLogs: []ethapi.StructLogRes{}, + }, + }, + // Standard JSON trace upon the pending block + { + blockNumber: rpc.PendingBlockNumber, + call: ethapi.CallArgs{ + From: &accounts[0].addr, + To: &accounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1000)), + }, + config: nil, + expectErr: nil, + expect: ðapi.ExecutionResult{ + Gas: vars.TxGas, + Failed: false, + ReturnValue: "", + StructLogs: []ethapi.StructLogRes{}, + }, + }, + } + for _, testspec := range testSuite { + result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config) + if testspec.expectErr != nil { + if err == nil { + t.Errorf("Expect error %v, get nothing", testspec.expectErr) + continue + } + if !reflect.DeepEqual(err, testspec.expectErr) { + t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err) + } + } else { + if err != nil { + t.Errorf("Expect no error, get %v", err) + continue + } + if !reflect.DeepEqual(result, testspec.expect) { + t.Errorf("Result mismatch, want %v, get %v", testspec.expect, result) + } + } + } +} + +func TestTraceTransaction(t *testing.T) { + t.Parallel() + + // Initialize test accounts + accounts := newAccounts(2) + genesis := &genesisT.Genesis{Alloc: genesisT.GenesisAlloc{ + accounts[0].addr: {Balance: big.NewInt(vars.Ether)}, + accounts[1].addr: {Balance: big.NewInt(vars.Ether)}, + }} + target := common.Hash{} + signer := types.HomesteadSigner{} + api := NewAPI(newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { + // Transfer from account[0] to account[1] + // value: 1000 wei + // fee: 0 wei + tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), vars.TxGas, big.NewInt(0), nil), signer, accounts[0].key) + b.AddTx(tx) + target = tx.Hash() + })) + result, err := api.TraceTransaction(context.Background(), target, nil) + if err != nil { + t.Errorf("Failed to trace transaction %v", err) + } + if !reflect.DeepEqual(result, ðapi.ExecutionResult{ + Gas: vars.TxGas, + Failed: false, + ReturnValue: "", + StructLogs: []ethapi.StructLogRes{}, + }) { + t.Error("Transaction tracing result is different") + } +} + +func TestTraceBlock(t *testing.T) { + t.Parallel() + + // Initialize test accounts + accounts := newAccounts(3) + genesis := &genesisT.Genesis{Alloc: genesisT.GenesisAlloc{ + accounts[0].addr: {Balance: big.NewInt(vars.Ether)}, + accounts[1].addr: {Balance: big.NewInt(vars.Ether)}, + accounts[2].addr: {Balance: big.NewInt(vars.Ether)}, + }} + genBlocks := 10 + signer := types.HomesteadSigner{} + api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { + // Transfer from account[0] to account[1] + // value: 1000 wei + // fee: 0 wei + tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), vars.TxGas, big.NewInt(0), nil), signer, accounts[0].key) + b.AddTx(tx) + })) + + var testSuite = []struct { + blockNumber rpc.BlockNumber + config *TraceConfig + expect interface{} + expectErr error + }{ + // Trace genesis block, expect error + { + blockNumber: rpc.BlockNumber(0), + config: nil, + expect: nil, + expectErr: errors.New("genesis is not traceable"), + }, + // Trace head block + { + blockNumber: rpc.BlockNumber(genBlocks), + config: nil, + expectErr: nil, + expect: []*txTraceResult{ + { + Result: ðapi.ExecutionResult{ + Gas: vars.TxGas, + Failed: false, + ReturnValue: "", + StructLogs: []ethapi.StructLogRes{}, + }, + }, + }, + }, + // Trace non-existent block + { + blockNumber: rpc.BlockNumber(genBlocks + 1), + config: nil, + expectErr: fmt.Errorf("block #%d not found", genBlocks+1), + expect: nil, + }, + // Trace latest block + { + blockNumber: rpc.LatestBlockNumber, + config: nil, + expectErr: nil, + expect: []*txTraceResult{ + { + Result: ðapi.ExecutionResult{ + Gas: vars.TxGas, + Failed: false, + ReturnValue: "", + StructLogs: []ethapi.StructLogRes{}, + }, + }, + }, + }, + // Trace pending block + { + blockNumber: rpc.PendingBlockNumber, + config: nil, + expectErr: nil, + expect: []*txTraceResult{ + { + Result: ðapi.ExecutionResult{ + Gas: vars.TxGas, + Failed: false, + ReturnValue: "", + StructLogs: []ethapi.StructLogRes{}, + }, + }, + }, + }, + } + for _, testspec := range testSuite { + result, err := api.TraceBlockByNumber(context.Background(), testspec.blockNumber, testspec.config) + if testspec.expectErr != nil { + if err == nil { + t.Errorf("Expect error %v, get nothing", testspec.expectErr) + continue + } + if !reflect.DeepEqual(err, testspec.expectErr) { + t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err) + } + } else { + if err != nil { + t.Errorf("Expect no error, get %v", err) + continue + } + if !reflect.DeepEqual(result, testspec.expect) { + t.Errorf("Result mismatch, want %v, get %v", testspec.expect, result) + } + } + } +} + +type Account struct { + key *ecdsa.PrivateKey + addr common.Address +} + +type Accounts []Account + +func (a Accounts) Len() int { return len(a) } +func (a Accounts) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a Accounts) Less(i, j int) bool { return bytes.Compare(a[i].addr.Bytes(), a[j].addr.Bytes()) < 0 } + +func newAccounts(n int) (accounts Accounts) { + for i := 0; i < n; i++ { + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + accounts = append(accounts, Account{key: key, addr: addr}) + } + sort.Sort(accounts) + return accounts +} diff --git a/eth/tracers/internal/tracers/assets.go b/eth/tracers/internal/tracers/assets.go index d77959ac4b..322f837412 100644 --- a/eth/tracers/internal/tracers/assets.go +++ b/eth/tracers/internal/tracers/assets.go @@ -147,7 +147,7 @@ func call_tracerJs() (*asset, error) { return a, nil } -var _call_tracer_parityJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xdc\x3b\xef\x6f\x1b\x37\xb2\x9f\xad\xbf\x62\xce\x1f\xae\x12\xa2\x48\x72\xd2\xcb\x03\xe4\x73\x0e\xae\xe3\xb4\xc6\xb9\x71\x60\x3b\x2d\x8a\xc0\x78\x47\xed\xce\x4a\x8c\x57\xe4\x1e\xc9\xb5\xac\xa6\xfe\xdf\x1f\x66\x48\xee\x0f\x69\xe5\xb8\xbd\xe0\xbd\xe2\xe5\x4b\xb4\x24\x67\x38\x33\x9c\xdf\xa4\xc7\x63\x38\xd1\xc5\xda\xc8\xf9\xc2\xc1\x8b\xc9\xc1\x7f\xc1\xf5\x02\x61\xae\x9f\xa3\x5b\xa0\xc1\x72\x09\xc7\xa5\x5b\x68\x63\x7b\xe3\x31\x5c\x2f\xa4\x85\x4c\xe6\x08\xd2\x42\x21\x8c\x03\x9d\x81\xdb\x58\x9f\xcb\x99\x11\x66\x3d\xea\x8d\xc7\x1e\xa6\x73\x9a\x30\x64\x06\x11\xac\xce\xdc\x4a\x18\x9c\xc2\x5a\x97\x90\x08\x05\x06\x53\x69\x9d\x91\xb3\xd2\x21\x48\x07\x42\xa5\x63\x6d\x60\xa9\x53\x99\xad\x09\xa5\x74\x50\xaa\x14\x0d\x6f\xed\xd0\x2c\x6d\xa4\xe3\xfb\x77\x1f\xe0\x1c\xad\x45\x03\xdf\xa3\x42\x23\x72\x78\x5f\xce\x72\x99\xc0\xb9\x4c\x50\x59\x04\x61\xa1\xa0\x11\xbb\xc0\x14\x66\x8c\x8e\x00\xdf\x12\x29\x57\x81\x14\x78\xab\x4b\x95\x0a\x27\xb5\x1a\x02\x4a\xa2\x1c\xee\xd0\x58\xa9\x15\xbc\x8c\x5b\x05\x84\x43\xd0\x86\x90\xf4\x85\x23\x06\x0c\xe8\x82\xe0\x06\x20\xd4\x1a\x72\xe1\x6a\xd0\x27\x08\xa4\xe6\x3b\x05\xa9\x78\x9b\x85\x2e\x10\xdc\x42\x38\xe2\x7a\x25\xf3\x1c\x66\x08\xa5\xc5\xac\xcc\x87\x84\x6d\x56\x3a\xf8\xf9\xec\xfa\x87\x8b\x0f\xd7\x70\xfc\xee\x17\xf8\xf9\xf8\xf2\xf2\xf8\xdd\xf5\x2f\x87\xb0\x92\x6e\xa1\x4b\x07\x78\x87\x1e\x95\x5c\x16\xb9\xc4\x14\x56\xc2\x18\xa1\xdc\x1a\x74\x46\x18\x7e\x3c\xbd\x3c\xf9\xe1\xf8\xdd\xf5\xf1\x77\x67\xe7\x67\xd7\xbf\x80\x36\xf0\xf6\xec\xfa\xdd\xe9\xd5\x15\xbc\xbd\xb8\x84\x63\x78\x7f\x7c\x79\x7d\x76\xf2\xe1\xfc\xf8\x12\xde\x7f\xb8\x7c\x7f\x71\x75\x3a\x82\x2b\x24\xaa\x90\xe0\xbf\x2c\xf3\x8c\x4f\xcf\x20\xa4\xe8\x84\xcc\x6d\x94\xc4\x2f\xba\x04\xbb\xd0\x65\x9e\xc2\x42\xdc\x21\x18\x4c\x50\xde\x61\x0a\x02\x12\x5d\xac\x9f\x7c\xa8\x84\x4b\xe4\x5a\xcd\x99\xe7\x9d\x0a\x09\x67\x19\x28\xed\x86\x60\x11\xe1\xef\x0b\xe7\x8a\xe9\x78\xbc\x5a\xad\x46\x73\x55\x8e\xb4\x99\x8f\x73\x8f\xce\x8e\x5f\x8f\x7a\x84\x33\x11\x79\x7e\x6d\x44\x82\x86\x0e\x47\x40\x56\x92\xf8\x73\xbd\x52\xe0\x8c\x50\x56\x24\x74\xd4\xf4\x3b\x61\x65\x14\x0e\xf0\x9e\xbe\x9c\x25\xa5\x05\x83\x85\x36\xf4\x3b\xcf\xa3\x9e\x49\xe5\xd0\x28\x91\x33\x6e\x0b\x4b\x91\x22\xcc\xd6\x20\x9a\x08\x87\x4d\x66\x48\x8d\xfc\x71\x83\x54\x99\x36\x4b\x56\xcb\x51\xef\x73\x6f\x2f\x50\x68\x9d\x48\x6e\x89\x40\xc2\x9f\x94\xc6\xa0\x72\x24\xca\xd2\x58\x79\x87\xbc\x04\xfc\x9a\x20\xcf\xd3\x9f\x7e\x04\xbc\xc7\xa4\xf4\x98\xf6\x2a\x24\x53\xf8\xf8\xf9\xe1\x66\xd8\x63\xd4\x73\x74\x27\x71\xe2\x1c\xd5\xdc\x2d\xa0\xef\x75\x5b\xe4\x03\xda\xae\xb4\x98\xf2\xd1\xd2\xe8\x52\x5a\x26\x0c\x0c\x0a\xab\x95\x1d\x42\xb2\xc0\xe4\x56\xaa\x39\x64\x46\x2f\x99\x17\xa9\x60\xae\x19\xb7\xf4\x84\xfc\xcb\x3a\x2c\xfe\x05\x4b\x74\x0b\x4d\x2a\x60\xc1\x69\x52\x6f\x22\x28\xe0\x16\xf0\xd3\x8f\xa0\x8b\x44\xa7\x38\xea\xed\x6d\xd3\x34\x85\xac\x54\x2c\xb5\xfe\x00\x3e\x1b\x74\xa5\x21\x65\x97\x76\x54\x71\x35\xca\x79\xe5\xe1\x43\x60\x2c\x45\x9b\xa0\x4a\x31\xe5\x83\xbb\xb5\xb0\x5a\xb0\xaa\xc0\x0a\xbf\xb9\x43\xf8\x54\x5a\xd7\x58\xc3\xd4\x0b\x05\xba\x24\x53\x6e\x1e\xbb\x54\xce\x73\x23\xe8\xb7\x42\xc3\x74\x8f\x7a\x7b\x15\xf0\x14\x32\x91\x5b\xa4\x7d\x0b\x61\xa4\x5b\x9f\x1a\xa3\xcd\x8f\xa2\x28\xa4\x9a\x4f\xe1\x73\x6f\x6f\x6f\x3f\xd1\x8a\x35\x06\x12\x83\x5e\x82\xc4\x2b\x58\xa7\x8d\x98\x23\x6d\x4b\xc7\x36\x17\x76\x7f\x0a\xfb\x17\xf5\xd7\x90\x80\x1f\x9f\x9d\x0b\x0b\xa5\x54\xee\xd5\xb7\xa0\xef\xd0\x64\xb9\x5e\x75\x2d\x5b\x8a\xfb\xb0\xa7\xfc\x15\x01\xef\x13\xc4\x14\xd3\xae\x95\x52\xdd\x89\x5c\xa6\xf0\xa9\x5c\x16\x24\x22\x27\x15\x93\x4c\x6b\xbf\x13\x1d\xe3\x0c\x55\xa9\x1a\x18\xbc\x43\xe3\x3c\xee\xcb\xf8\x9b\xd7\x84\x83\x4b\x85\x13\x91\xe5\x19\xf9\xe0\x26\x5f\x61\x80\xd7\x7b\x7d\xce\xe5\x52\x92\xaa\x8b\x84\x9c\xf9\xc1\xe4\xc5\xb7\xd0\x3f\x98\xbc\x78\x39\x68\x40\xf1\x4a\x0f\x54\x18\x4c\xf4\xb2\x90\xac\x5b\x82\xfe\x63\xc2\x4b\x99\xbb\xe7\x52\xc5\xa1\x61\x6f\xef\xa1\xfb\xc4\xae\x9c\x30\x4e\xaa\xf9\xcf\x92\xf4\xee\x73\x53\x22\x5e\x43\xa7\x51\x10\x52\x59\x67\xca\xa4\x96\x81\xa7\x97\x83\x56\x3c\x86\xab\x8d\xa1\xf6\xbe\x57\xb7\xb2\x60\xd7\x63\xdf\x6a\xc3\x44\xd8\x29\x7c\xf4\x5b\xda\x32\xcb\x64\x22\xc9\xcc\x67\x22\x17\x2a\xf1\x1e\x96\x75\x33\x43\xb3\xdf\xdb\x63\x1b\x96\xf6\x62\xf6\x09\x13\x77\xba\x2c\xdc\xba\x61\x27\x7a\xf6\x69\xc0\xd4\x13\x50\xff\x4e\x18\xb8\xa7\x60\xe3\x87\x21\x9c\x04\x2b\xee\x21\x3c\xf4\xf6\xf6\xa2\x51\x99\x12\x0f\x03\x8d\xe3\x31\x90\xe9\x92\x17\x90\xea\x4e\xdf\x06\x63\xa5\x23\x5d\x07\x59\x78\x77\x48\x56\x5e\x79\x1b\xb4\xa3\xde\x1e\xc1\x35\x88\xc9\xf5\x7c\x08\xe9\xcc\x13\x44\xb9\x88\x28\x5c\x69\x90\xdd\x1e\x32\xdb\x20\x97\x4b\x4c\xa5\x70\x98\xaf\x7b\x7b\x7b\x44\x2f\x4f\xc0\x11\xe4\x7a\x3e\x9a\xa3\x63\xf1\xf4\x07\x87\xbd\xbd\x3d\x99\x41\xdf\xcf\xfe\xe5\xe8\x88\x85\x9b\x49\x85\xa9\x47\xbf\xc7\x7e\x21\x13\x65\xee\xaa\x7d\x09\x28\x70\x48\x3f\x1f\x3c\x15\x3f\x23\x68\x95\xaf\x21\xa1\x64\x40\xcc\x48\x23\xed\xda\x3a\x5c\x06\xe6\xec\x10\x32\x61\xc9\x21\xc8\x0c\x56\x08\x85\xc1\xe7\xec\xef\x40\xab\x04\x03\x95\x76\x6d\xd9\xf7\x1e\x01\xed\x36\xd2\xc5\xc8\xe9\x77\xe5\x72\x86\xa6\x3f\x80\xbf\xc2\xe4\x3e\x9b\x0c\xe0\xe8\x88\x7f\x44\xda\x03\x4c\xa0\x97\xb0\xe8\x22\x30\xca\xf0\x57\xce\x48\x35\xf7\xbc\x06\x5a\xcf\x32\x10\xa0\x70\x05\x95\x27\x91\x16\x66\x48\x9e\x97\x5d\x0a\xa6\x43\x10\x69\x4a\xae\x95\x03\x44\x15\x0e\xda\x5b\xc2\x5f\xff\x4a\xfe\x9d\x08\xda\x3f\xb9\x3c\x3d\xbe\x3e\xdd\x87\xdf\x7e\x83\xd6\xc8\x8b\xfd\x41\x83\x32\xa9\x2e\xb2\x2c\x10\xe7\x1d\x6d\x81\x78\xdb\x3f\x18\x8c\xee\x44\x5e\xe2\x45\xe6\xc9\x0c\x6b\x4f\x55\x0a\x47\x01\xe6\xd9\x26\xcc\x8b\x16\x0c\x01\x8d\xc7\x70\x6c\x2d\x2e\x67\x39\x6e\xc7\xcd\x10\x58\x39\xc6\x92\xa3\xf4\xfa\x4f\xb6\x9d\x23\x69\x55\xdc\x35\x88\x9f\x29\xde\x73\xeb\x02\xa7\x00\x00\xba\x18\xf2\x00\x79\x76\x1e\x70\xfa\x07\xbc\xe7\x33\x8a\x22\x24\xad\x3a\x4e\x53\x83\xd6\xf6\x07\x03\xbf\x5c\xaa\xa2\x74\xd3\xd6\xf2\x25\x2e\xb5\x59\x8f\x2c\xe5\x0d\x7d\x66\x6d\xe8\x39\x8d\x30\x73\x61\x79\x87\xa8\xa9\xc7\x77\x42\xe6\x62\x96\xe3\xf7\xc2\xf6\xeb\x35\x67\x6a\x5a\xaf\x69\x4f\x9d\x68\xeb\xa6\x71\x8a\x3e\xe2\x1c\xcb\x8b\xc0\xf6\x27\xf7\xfb\xdb\x12\x9d\x0c\x6a\x6d\x39\x78\x35\x20\x90\x87\xc3\xca\x06\xea\xd8\x58\x94\x76\xd1\x67\x95\xab\x67\xeb\xe0\x77\x14\xad\xbe\xc3\x46\x58\xef\xb6\x75\xce\x62\x9e\x71\x0c\x20\xff\x47\xba\x37\x17\x1c\x5c\xd9\x1d\x08\xca\xa2\x6c\x39\xe3\x83\x71\x5a\x7b\x4c\xef\x2e\xae\x4f\xa7\xf0\x4f\x24\x87\xe2\xc8\xdc\xee\xfc\x99\x6f\x10\x23\x33\x9f\x53\x6c\xeb\x6d\x50\xd2\xab\xd3\xf3\xb7\x6f\x4e\xaf\xae\x2f\x3f\x9c\x5c\xef\x37\x14\x35\xc7\xcc\x11\x2b\x9d\x59\x01\x2d\x22\x74\xed\xd9\x8f\x04\xf3\xfc\xe0\xc6\x8f\xc0\x51\x87\x33\xd9\x7b\x1c\x02\x3e\xde\x30\xee\x87\x6d\xa1\xb7\x97\xfa\x23\xf8\x3a\x3a\xea\xb4\x57\xb7\xb0\xdc\xe9\xb8\xe0\x71\xed\x18\x7c\x5d\x55\x4c\x67\xb4\xe2\x3b\x1f\x9a\x1e\xa1\x79\x5b\x43\x77\xb8\xe3\xca\xc5\x85\x4c\x91\x62\x4e\xe2\x93\xa5\x4a\xef\x52\xad\xf0\xf7\x3b\xba\xe3\xf3\xf3\x96\x9b\x3b\x3e\x3f\x3f\xb9\x78\xd3\x72\x7d\x6f\x4e\xcf\x4f\xbf\x3f\xbe\x3e\xdd\x5c\x7b\x75\x7d\x7c\x7d\x76\xc2\xa3\x4d\xaf\xe8\x34\xa9\xda\x2e\xc1\x1f\x6c\x08\xbe\x72\x76\x14\xef\x39\xe8\x71\x28\xf1\x59\x4a\x83\x4f\x3b\x04\xb7\xd0\x54\xbe\x9a\x90\xa1\x66\x42\x25\x31\xd6\xda\xa8\xc4\xd2\xbe\xaf\x73\x9c\xbe\xd3\x83\xc7\x98\xed\x60\xa0\x21\x7a\xaf\xb8\x1c\x81\xd8\xcb\xf7\x9f\x2e\x0e\xf8\x07\x4c\x60\x0a\x07\x81\xbb\x47\x62\xc5\x0b\x78\x46\xe8\xff\x40\xc4\x78\xd9\x01\xf9\xe7\x8c\x1b\x5b\x36\xf9\xe7\x8c\x27\xba\x74\x17\x59\x36\x85\x4d\x41\x7f\xbb\x25\xe8\x6a\xfd\x39\xaa\xed\xf5\x7f\xdb\x5a\x1f\x62\x4f\xd4\xd1\x2f\x99\x5e\x54\x45\x3a\x08\xc6\xd1\xa1\x36\x5e\x4d\xb8\x70\x1d\xc5\x35\xc1\xf9\xf0\x67\xcb\xc8\xfc\xd6\xac\x19\x29\x9d\xbb\x2c\x50\xa5\xd0\xe7\x04\x8f\x76\xfd\x2d\x6e\x4d\x85\xa0\x0a\x7b\xbe\x86\xc9\x20\x82\x5d\x5f\xbc\xb9\x98\x52\x99\x91\x92\xa3\xa1\xaa\x8a\x8b\x42\x85\xf7\x2e\x26\xbb\xd2\x82\x15\x99\xcf\x07\xe3\x0e\x1e\x51\xb2\x10\x6a\xee\x2d\x94\xd9\xaf\xd1\x07\x3e\x3d\x17\x84\xf5\x08\x66\x72\x7e\xa6\x5c\xbf\x1a\x79\x06\x2f\x5e\x4e\x26\x81\x5b\x36\xc8\x07\xc0\xdc\x22\x34\x04\xd9\x34\xe3\x80\x72\x53\x2e\x93\xfd\x60\xd1\x5f\x3b\x01\xe8\xac\x98\xa9\x2e\x6e\xd7\xc4\x43\xaa\x2a\x8c\xc4\x3b\x04\xe9\xbe\xb1\x8c\x13\x44\x9e\xeb\x15\x45\x88\x11\xfc\x8c\x1e\xa3\x42\x64\xf7\x1d\x9a\x28\xc4\x65\xb3\x79\x50\x79\x75\xc1\xbd\x0e\x83\xb0\x14\x6b\x98\x21\x55\x13\xb7\x6b\x3e\x98\x74\xad\xc4\x52\x26\xd6\xe3\xe3\x06\x8a\xc1\xb9\x30\x8c\xd6\xe0\xbf\x4b\xb4\x0e\x53\x76\x00\x22\x71\xa5\xc8\xf3\x35\xcc\xe5\x1d\x2a\x86\xee\x93\xb4\xe3\xf9\x0d\xe1\xd5\xcb\xf1\xab\x6f\xc1\x94\x39\x0e\x46\xbd\x46\x96\x50\xb1\x1a\xe4\x4d\x13\xc1\xa2\xde\x60\xe1\x16\xfd\x01\xbc\xde\x91\x6e\x34\x95\x3b\x78\x99\x8d\xdc\xa0\x13\x0c\x9e\xc3\x81\x4f\x27\x78\xb3\x5a\x63\xba\xf2\x92\xa6\x42\x35\x7d\xc0\xb6\x16\x7d\x6e\x6a\x78\xff\x56\x18\x91\x8b\x19\x0e\xa6\xdc\xa2\x64\xf2\x56\x22\xf4\xa8\xe8\x48\xa1\xc8\x85\x54\x20\x92\x44\x97\xca\xd1\xb1\xc5\x76\x53\xbe\xa6\xf8\xfb\x8d\x8b\xf8\xb8\x9b\x27\x92\x04\xad\x8d\xe1\x98\xcf\x9c\x88\x12\x4b\x82\xa6\x12\x59\xa6\xd8\x38\x53\xf2\xc9\x9a\x43\x60\x58\xb1\x92\x79\x1e\x11\x2e\xb5\xa5\x4d\x66\x08\x2b\xa3\x29\xcb\x94\x54\xf5\x4a\x52\x3b\x3a\x2b\x0b\x5a\x81\x80\x5c\x73\xc9\xcf\x9e\x15\x84\x99\xdb\x91\x8f\xab\x6c\xb2\xda\x80\xd2\xab\x51\x3b\x27\x6b\x6a\xba\x2f\x79\x83\x7e\x77\x67\x98\x97\xa7\x3f\x9d\x5e\x56\xb9\xe5\x93\x4f\x6e\x14\x0b\xd6\xae\x5e\x48\x15\xb7\xf8\x10\x7e\x95\x7a\x2e\x6c\xb2\x30\x03\xef\x71\x58\x40\xba\x74\xc4\x11\xdb\x82\xef\x24\x08\x4b\xcc\x53\xd8\x11\x52\xf9\xce\x9f\xdf\xa3\x10\xd6\xc6\xae\x15\xcb\x36\x26\xe8\x29\xde\x61\xae\x0b\x34\xdb\xb6\xbc\x8b\xd7\xeb\x0f\x97\xef\xf6\x77\xeb\xf8\xd1\x13\x74\xdc\x47\x95\x6d\x0f\x3e\xd9\x0c\xf9\x71\xf5\x39\xaa\x27\x94\x94\xbf\x43\xf4\x41\x76\x47\xbb\xc2\xac\xa7\x70\x18\x29\x7d\x16\x88\x18\x0c\xea\x24\x68\x5b\x5a\x4f\x94\x04\x51\x10\xa4\x31\x1e\xc3\x7b\x5d\x70\x2e\x45\xc7\x92\x0b\xeb\x6a\xbd\x9f\xa3\xef\x94\x34\xb5\xc3\x96\xb9\xb3\xbd\xc7\x5c\xc5\xa8\xd0\x45\x4c\x7b\x2a\xaf\x40\xd9\xca\x66\x0d\xdf\x35\xf1\xa2\x0a\x16\xde\x93\xbb\xa6\xc5\x0b\xf0\x8b\x1a\x7e\xbb\xa5\x4b\xc2\xa7\x38\x4c\x7b\x90\x2f\x45\xc1\x5e\xd3\xf9\x7c\xb0\x6c\x54\x21\x2a\x6f\x06\xb6\xe7\x95\x0c\xd9\x35\xd1\x77\x9c\x3b\x53\xf0\x1c\xe2\x07\x65\x28\x83\x8d\x4a\xc1\x6b\x40\x8a\x39\x3a\x84\x1a\xea\x10\x36\x86\x08\x36\xc4\x7e\x92\xa1\x41\xd7\xa5\x87\xb5\x57\xfd\x8b\x41\x37\xc2\x7f\x97\x22\xb7\xfd\xc9\xa0\xed\x4d\x9d\xe6\xb4\xeb\x68\xab\xb0\x22\x98\x76\x29\x75\xd8\x00\xdb\x50\x3e\x5f\x18\x9d\xe8\x14\x1f\xc5\x10\x3d\x75\x1d\xea\x19\x59\x70\x22\x9d\x2e\xdf\x77\x8a\x4e\xdb\x7d\xb1\x13\x91\xe7\x8d\xde\x58\x95\x7f\x9d\xee\x6c\x90\x35\x6b\xe1\x9d\x7d\xc8\x91\x54\x29\xde\x5f\x64\x11\xd3\x00\x5e\xc3\xf3\x83\x1a\x43\xb3\x88\x88\x26\x14\x05\x12\x1d\x61\x00\x0d\x6b\x5a\xe1\xa8\x6a\x09\x78\x5f\xe8\x5d\xe1\x0a\xe3\x45\x11\x77\x7b\xd9\x12\x3c\x90\x50\xeb\xa5\x36\xd8\xb5\xc9\x7e\x95\xfc\x67\x42\xe6\xa5\xc1\xfd\x43\xe8\x08\x76\xb6\x34\x99\x48\x58\xc5\x2d\x02\xb7\x07\x2d\x58\xbd\xc4\x85\x5e\xf5\x3a\x38\x7a\xd8\x1d\x47\xb7\x0d\xa9\x6e\xef\xb7\xf3\x20\xbe\xa3\x12\x16\x4a\x2b\xe6\xd8\x30\xa4\xed\x18\xdf\x7d\x4e\x4f\x32\xb3\x2d\x53\x82\x67\xd0\x30\xc1\xa6\x05\x76\x99\xd8\xc3\xff\xae\xa1\x55\x5c\x47\xab\x69\x32\x5e\xf9\xb1\xc6\x24\x31\x5d\xab\x5d\x97\xc1\x05\x0e\x2f\xf9\xf8\xde\x08\x27\xfa\x95\x7d\x3e\xfc\x7f\xb4\xb1\xae\x1e\x40\xf4\x33\xc1\x8f\x0d\x06\x3e\xc6\xd7\x04\x36\x6f\x91\x1a\x3b\xb4\x4d\xa9\xe3\xee\x84\x8d\xe9\x3d\x73\xc0\x35\xb4\x70\x72\x96\x47\x43\xdc\x30\xe9\xc7\xac\x3f\x52\xff\x67\xf7\x02\xd1\xee\xb7\xac\xc2\xa7\x0e\x6d\xb3\xf0\x59\x44\x9d\x43\x7c\xd9\xa4\x1b\x59\xfb\x37\x93\xfb\x6f\xb6\xad\xb9\xc3\x44\x1f\x62\xee\x78\xa6\x3e\x61\xe2\x6a\xe7\xc3\x35\x18\x7d\x15\x06\xef\xa4\x2e\x29\x41\xc6\x27\xf7\x43\xc3\x02\xfe\xef\x35\x4c\xe0\x1f\xe0\x3b\x96\x30\xe5\x1f\x8f\xf5\x4c\x7f\x6f\xc7\xf4\xc9\xfd\xd2\x56\xb7\xb4\xaa\x57\x1f\xea\x0b\x29\x3e\xb2\xe6\x8d\x14\x57\xf3\x24\x03\x5f\xe9\x35\xb2\x2b\x9d\xf1\x8d\xae\x2f\xdd\x33\xff\x22\x61\x8f\xe1\x1f\xb9\x99\x0a\xbe\xdd\xe9\x82\x8a\x91\x90\xbc\xe5\x94\xa3\xaf\xab\x64\x7e\xe8\xcb\x20\x58\x08\x95\x86\x06\x94\x48\x53\xe9\x2f\xcd\x03\x85\x62\x2e\xa4\xea\x75\x0a\xf0\x8b\x15\x44\x97\xe2\x6c\xd5\xe5\xcd\x3c\x33\xb4\x0a\xd9\x6c\x09\x71\xef\x09\xf9\xe4\x86\xfd\x6c\x5e\xb2\xf5\x9e\xe6\x09\xbf\xe4\x06\xff\x53\x1f\xb8\xd9\xa9\xdc\xe5\x60\xd8\x44\xf8\x91\x93\xb2\xe5\x92\xdb\x0e\x20\x62\xdb\xcc\x17\xa4\x2a\x85\x24\x47\xa1\xfc\x93\x1d\xcc\x9c\xbe\x43\x63\x7b\x4f\x30\xda\x3f\x62\xb3\x1b\x91\x3b\x7e\xf6\xda\x0e\x70\x3c\x86\xcb\x98\x2b\xd0\x06\xed\x96\x49\x5d\xdf\xb5\x1e\x1e\xc4\xbb\xcb\xaf\xda\x47\xf9\xfa\x8d\x94\xdd\x62\x7b\x3c\x23\xe1\xa3\xec\x08\xd5\xcd\x00\x46\xc1\xed\xd1\xf6\x48\x63\xef\xaa\x33\x46\x0a\xf4\xd4\x34\xe7\xe9\xae\xdf\xeb\xdd\xdb\x5c\x38\x17\x1c\x51\xc3\x10\xbd\x87\x96\x8e\x9f\xd1\xa1\x72\xbd\xa7\xb9\x66\x2e\x3e\x83\x5b\xde\x34\xa4\xff\xbb\xcb\xab\xba\x7d\xb8\xe5\x8c\xce\xab\x42\x37\x30\xef\xb4\x1e\x42\x8e\x82\x7b\x81\xf1\x91\x5b\xbc\xa6\x79\xac\x37\x19\xfd\xbc\x2f\x8d\xb7\x1c\x3d\xdf\x28\x2e\x30\xde\x88\xf8\x16\xd4\x0c\x51\x81\x74\x68\x04\x29\x2b\x99\x75\x78\x97\x45\x54\x5a\x46\xc7\xe7\x22\xc9\x3d\x07\xc4\xe1\x91\x14\x59\x8e\x54\xf3\x51\x6f\xcf\x8f\x37\x22\x43\xe2\xee\xeb\xc8\xe0\x33\x5e\x86\x0c\x37\x06\xb3\x5c\x27\xb7\x53\x00\x48\xdc\xfd\x88\x3f\xb8\x63\x5e\xdd\x23\xd0\x30\x7d\xf0\xe8\xc6\x65\x02\xcd\xd1\x90\x6f\xb2\x6f\x5c\x1d\x30\x60\xb8\x3e\xa8\xee\xdc\x82\x01\xd1\xdc\x76\xeb\x9b\x97\x56\x97\x06\x1b\x2e\xca\xdd\x6f\x7b\xa8\x08\x40\xce\x69\xda\x0d\x40\x53\x1d\x40\x1b\xd7\x19\xb4\x98\x87\xfc\xac\xcf\xcb\xa7\xcd\x59\x3f\x14\x18\x95\xcb\x86\x6c\xe4\x92\x65\xc3\xf7\xd5\xfc\xdc\x83\xdc\xd8\x89\xbb\x6f\x09\xf8\x07\x61\x17\xd3\x5a\xc4\xf4\x39\xac\x26\xfd\x33\x8b\xc6\xb4\x1f\xf0\x7b\xd5\x4f\xb8\x6a\x1c\x1b\x83\x9b\x0b\xdf\x6b\xcb\x41\x7c\x6b\x71\x9c\xa8\xe8\x25\x67\xe9\xf3\x8e\x56\x77\xd1\xe0\xd2\x37\xea\xd8\x8d\xab\x14\x32\x69\xac\x23\xd5\x5c\x92\x0d\x24\xcd\x67\x7c\x42\x01\x2e\x0b\xb7\x06\xcd\xcf\x77\x3c\xd2\xd4\xe8\x22\xe8\x6a\x04\x1c\xf2\xab\x1d\xc3\xef\x60\x75\x4c\x39\x30\x9d\x93\x1b\xb2\x68\x6b\xdb\xc2\xa2\x3f\x80\x5c\xeb\x62\xe4\x71\xe1\xbd\x58\x16\xcd\xb5\xd3\xc6\x3d\xbe\x92\xbe\x99\x43\xde\xf1\xd5\xe4\x6f\xe2\xd5\x64\x32\xf9\xdb\xcb\x57\x93\xc9\x01\xfd\xa2\xff\xb3\x49\x96\x4d\x26\xfb\x43\xb0\x28\x4c\xb2\xe0\x7d\xd0\xba\x54\x38\xd1\xd9\x25\x27\x97\xdc\x9d\xd9\xbc\x86\x83\x6a\xb2\xf5\x5a\x69\xd3\xa1\x4d\x6e\x06\x9d\x3d\xd7\x91\x5d\xc8\xcc\xd5\xcf\x61\x3a\x7c\xe1\x24\x3a\xb5\xee\xdc\x89\x0c\xb7\xf2\x7a\x3b\x40\x1f\xc7\xfe\x58\x66\xc6\xd8\x63\x52\xb2\x03\xf4\xb0\xd7\x2e\x43\xdd\xfd\xd3\x51\x56\x8b\x9b\x24\xb6\xd6\xb4\x90\xf0\x6d\xf0\xd6\x74\x57\x53\x9a\xca\xed\xb0\xb0\x2e\xb8\xb9\xde\x0e\x84\x84\x80\xd7\x5a\x13\x89\x68\xbe\xc2\x64\xd7\x2a\x7f\xc5\xb0\xed\xb0\x32\xe6\xa6\x4b\x8f\x8b\xc0\xa0\x7f\xb2\xc4\x9d\x13\xf2\xe8\xde\x04\xa0\xb4\x52\xcd\x1b\xae\x3a\x45\x2b\x0d\x55\xa2\x12\xf3\x34\xd8\x40\xa6\x0d\x7c\xb2\x5a\xf9\xd7\x69\x68\x24\xa1\xf4\x8f\x65\xfd\xbb\x75\x7e\xc2\xab\x64\x82\x6e\x0d\x19\x0a\x7e\x66\xe6\x34\x77\xcc\x61\x89\x42\x49\x35\xcf\x4a\xca\x63\x18\x1f\x1b\x6d\x68\xc2\x52\x98\xd0\x50\x5a\x34\x16\x56\x0b\x1d\x92\x7c\xae\x2b\x0b\x83\x64\x8d\xc3\x70\x09\x26\x6d\x91\x8b\x35\x48\x47\x05\x45\xe0\xaa\x19\x39\xb8\x09\x14\x45\x30\xf4\x2f\x87\x43\x81\x5e\x87\x13\xb2\xa4\xc3\xde\x7f\xd2\xd4\xe5\x77\x94\x51\xe3\x58\xa2\x97\xcc\x4b\x0c\xa6\x21\xad\x2b\x8b\x54\x38\x04\x91\xb9\x90\x43\xfa\x55\x7c\x0f\xc3\x17\x0c\x22\xcb\x30\x71\xd6\x3f\x86\x23\xf1\x1b\xad\x1d\xd0\xae\x55\x2a\xe5\x49\xa8\x48\xdb\xd4\xe6\x16\x95\x5d\x0f\x74\x5a\x48\xae\x3e\x9c\x9d\x9c\xbd\xf1\x58\x5a\x4c\xd8\x52\x26\x32\xdd\xe0\xa2\x9d\x31\xb7\x78\xae\x78\xf9\xba\x1c\x77\x9c\x48\xf3\xc5\x48\x7b\x6a\xeb\xa5\xc4\x86\x30\x76\xdc\xdb\x56\x02\xa5\x99\x2a\x0b\xe3\xdc\xb7\xa9\x2e\x7c\x35\xdb\xf8\xfc\xed\xb7\x90\xb4\xf1\xd3\x43\x4d\x56\x1c\xe3\xa5\xcf\x3c\x2a\xe4\x23\xa7\xcf\xf5\x0a\xcd\x89\xb0\x18\x6e\xf3\x7d\x30\x9b\xb2\xe6\x8d\xc2\x23\xf4\xda\xdd\x84\xf1\x60\xc1\x34\xce\xce\x23\xa0\xe4\xdf\x31\x60\x56\xf4\x4c\x5b\xd4\xf1\xb4\x2d\x67\x3c\x66\xa7\x30\xd9\x1d\x60\xa3\x71\xec\x8a\xb2\xdb\xf1\xbb\x0b\x62\x47\x3a\x40\xf4\xf2\x08\x89\xab\x82\xdb\xcc\x10\x1a\xf9\x45\x7b\x4d\x9d\x1a\x70\xbe\xe2\x25\x1a\xb3\x95\x58\x9c\x78\xd9\x3f\xe2\xc3\x37\x2a\xdf\xe6\x0b\xe3\xd1\x42\xd8\x8b\x95\x7a\x6f\x74\x81\xc6\xad\x5b\xb8\xaa\x46\x69\x6b\x83\xa0\xf0\xdb\xa8\x3e\x36\x97\xdd\xb4\xee\x4c\xc2\x8c\x3f\xcf\xc3\xad\x3e\x76\xf5\x2c\xd8\xc7\xf7\x7f\xe2\xda\xa7\x12\x9d\xdb\x34\xdf\x44\xb7\x3a\xb9\x4f\x58\xbf\xc5\x6d\xdc\x8f\x5b\x94\x4d\xfa\xab\x56\x40\x63\x49\xbb\x21\xfa\x34\xa1\x34\x77\xff\x58\xe1\xba\x89\x8d\xc8\x9d\xf2\x69\x37\xfd\xe3\x69\x57\x82\xba\xf5\x22\xf2\x70\xcd\x53\x5e\x17\xa8\xb3\x30\xfe\xf1\x16\xd7\x37\xa1\x5e\xe5\xa8\x56\x99\x7d\x85\x47\x71\x51\xfd\xdf\x2d\x74\x0c\xd6\x92\x6c\x63\xfc\x63\x0d\x71\xb3\xa3\x6b\xdd\x66\x6a\x0b\x6a\xe7\x9d\xc6\xc6\x4e\xdd\xd8\xb7\x71\xb7\x1d\x56\xec\x35\xd9\xd8\xe8\xa8\xf2\xa9\xe8\x49\xbb\xf3\xb2\x80\x70\xbf\x72\x19\xfb\x37\x01\x83\x6d\x14\xc3\xd5\x16\x21\x50\x53\xdd\xea\x21\x6f\x0e\x7b\x5f\xdc\xa3\x92\xba\x3c\x9a\x1c\x82\xfc\x7b\x0b\x3b\xc8\x67\xcf\x5a\x6f\x35\x16\x32\x4f\x4f\x7c\xd7\x8c\x17\x7e\x94\x37\xf5\xbb\xa2\x37\x98\xe3\x5c\x38\xe4\x94\xa5\xa4\xdc\x9b\x22\x87\x7f\x80\xc3\x8d\x97\xaa\xc6\xf7\x44\xf5\x2b\x74\x8f\x45\x8b\xed\x35\xad\x90\x41\x89\xb3\xd7\xaf\x7a\x65\x78\xf3\x43\x6b\x2b\x76\xeb\x9e\xfe\xd6\x3a\x7f\x26\xfc\x71\xd8\x6c\x6e\xd7\xf2\x0c\xbf\x46\x89\x56\x89\x70\xfd\x76\x4a\x57\xe1\xdb\x95\xd2\x44\xb0\x8f\xf2\x66\xd0\xb8\x50\x6f\xe4\x88\x01\x7f\xcc\x05\x9b\xb9\xca\x46\xda\xe4\xb9\x08\x60\x9f\x9b\x41\x2b\x18\x51\x2c\xa4\xc3\x3f\x66\x8d\x06\x87\xb0\xf1\x6f\x3c\x86\x2b\x2a\x4c\x4c\x38\xdd\x50\x48\x37\xe1\x78\x70\x13\x70\x3c\x86\x9f\x68\x9c\xc1\xaa\xa2\xba\x09\x36\x17\x76\x6b\x37\x02\xfb\x5e\x84\xf7\x5f\x4a\xba\x2d\x1a\xb9\x4a\xee\xd8\xeb\x4c\x49\x57\xe7\xb0\x8d\x0b\xf6\xf0\x77\x42\x3f\xf2\x5b\xd8\xdd\x71\x9d\x91\x9c\xb0\x40\xe1\x3a\xe4\x2f\x0f\xad\xa8\xfe\x39\x72\xe2\xab\x7d\x68\x76\x26\x87\x91\x70\xfe\x33\x2f\xbf\xb1\x4e\x83\xa0\x1a\x57\x6d\x43\x4f\xec\x49\xa4\x4e\xc4\xf0\x1f\xe8\xd2\xc3\x9a\xa3\x63\x6b\xe5\x9c\xca\x90\xb0\xa8\xa1\x0f\xfe\xf4\xab\x7c\xec\x8f\x9f\xfd\xce\x63\x6f\x9f\x7a\xd5\x59\xd9\xa2\xb3\x05\x71\x89\x89\x2c\x64\x34\xdd\x86\xaa\xec\xd4\x92\xf1\x18\xae\xc3\xdf\xe1\x60\xda\xad\x2f\x3b\x55\xa5\xa5\x29\xa1\x9b\xf2\x88\x92\xb0\x8e\x50\x69\x16\x2a\x6f\x9f\x3f\x5e\xfb\x36\xd3\x4e\xad\x08\x7f\x80\xca\x3e\x45\xd7\xd7\xd5\x5f\xd4\x8e\x2f\x28\x47\xd5\xe0\xd9\xd6\x8d\x0b\x5f\x40\xce\xd6\x0e\xb7\x8e\xbc\x95\xd7\xff\xce\x53\xaf\x55\xad\xe3\xe8\xfd\x6b\xcf\xa8\x65\x7b\x06\xb3\x52\xa5\xc7\xdd\xca\xc9\x07\x4d\xf3\x4d\xc5\xdc\x0b\x7f\x5a\xd5\x46\x1f\x8f\x7c\x3c\x86\xf0\xbe\x7d\x5b\x70\xaa\x64\x71\x12\x8b\xbd\x87\xde\xff\x04\x00\x00\xff\xff\x21\xf8\xaf\x37\x60\x3d\x00\x00") +var _call_tracer_parityJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xdc\x3b\xef\x6f\x1b\x37\xb2\x9f\xad\xbf\x62\xce\x1f\xae\x12\xa2\x48\x72\xd2\xcb\x03\xe4\x73\x0e\xae\xe3\xb4\xc6\xb9\x71\x60\x3b\x2d\x8a\xc0\x78\x47\xed\xce\x4a\x8c\x57\xe4\x1e\xc9\xb5\xac\xa6\xfe\xdf\x1f\x66\x48\xee\x0f\x69\xe5\xb8\xbd\xe0\xbd\xe2\xe5\x4b\xb4\x24\x67\x38\x33\x9c\xdf\xa4\xc7\x63\x38\xd1\xc5\xda\xc8\xf9\xc2\xc1\x8b\xc9\xc1\x7f\xc1\xf5\x02\x61\xae\x9f\xa3\x5b\xa0\xc1\x72\x09\xc7\xa5\x5b\x68\x63\x7b\xe3\x31\x5c\x2f\xa4\x85\x4c\xe6\x08\xd2\x42\x21\x8c\x03\x9d\x81\xdb\x58\x9f\xcb\x99\x11\x66\x3d\xea\x8d\xc7\x1e\xa6\x73\x9a\x30\x64\x06\x11\xac\xce\xdc\x4a\x18\x9c\xc2\x5a\x97\x90\x08\x05\x06\x53\x69\x9d\x91\xb3\xd2\x21\x48\x07\x42\xa5\x63\x6d\x60\xa9\x53\x99\xad\x09\xa5\x74\x50\xaa\x14\x0d\x6f\xed\xd0\x2c\x6d\xa4\xe3\xfb\x77\x1f\xe0\x1c\xad\x45\x03\xdf\xa3\x42\x23\x72\x78\x5f\xce\x72\x99\xc0\xb9\x4c\x50\x59\x04\x61\xa1\xa0\x11\xbb\xc0\x14\x66\x8c\x8e\x00\xdf\x12\x29\x57\x81\x14\x78\xab\x4b\x95\x0a\x27\xb5\x1a\x02\x4a\xa2\x1c\xee\xd0\x58\xa9\x15\xbc\x8c\x5b\x05\x84\x43\xd0\x86\x90\xf4\x85\x23\x06\x0c\xe8\x82\xe0\x06\x20\xd4\x1a\x72\xe1\x6a\xd0\x27\x08\xa4\xe6\x3b\x05\xa9\x78\x9b\x85\x2e\x10\xdc\x42\x38\xe2\x7a\x25\xf3\x1c\x66\x08\xa5\xc5\xac\xcc\x87\x84\x6d\x56\x3a\xf8\xf9\xec\xfa\x87\x8b\x0f\xd7\x70\xfc\xee\x17\xf8\xf9\xf8\xf2\xf2\xf8\xdd\xf5\x2f\x87\xb0\x92\x6e\xa1\x4b\x07\x78\x87\x1e\x95\x5c\x16\xb9\xc4\x14\x56\xc2\x18\xa1\xdc\x1a\x74\x46\x18\x7e\x3c\xbd\x3c\xf9\xe1\xf8\xdd\xf5\xf1\x77\x67\xe7\x67\xd7\xbf\x80\x36\xf0\xf6\xec\xfa\xdd\xe9\xd5\x15\xbc\xbd\xb8\x84\x63\x78\x7f\x7c\x79\x7d\x76\xf2\xe1\xfc\xf8\x12\xde\x7f\xb8\x7c\x7f\x71\x75\x3a\x82\x2b\x24\xaa\x90\xe0\xbf\x2c\xf3\x8c\x4f\xcf\x20\xa4\xe8\x84\xcc\x6d\x94\xc4\x2f\xba\x04\xbb\xd0\x65\x9e\xc2\x42\xdc\x21\x18\x4c\x50\xde\x61\x0a\x02\x12\x5d\xac\x9f\x7c\xa8\x84\x4b\xe4\x5a\xcd\x99\xe7\x9d\x0a\x09\x67\x19\x28\xed\x86\x60\x11\xe1\xef\x0b\xe7\x8a\xe9\x78\xbc\x5a\xad\x46\x73\x55\x8e\xb4\x99\x8f\x73\x8f\xce\x8e\x5f\x8f\x7a\x84\x33\x11\x79\x7e\x6d\x44\x82\x86\x0e\x47\x40\x56\x92\xf8\x73\xbd\x52\xe0\x8c\x50\x56\x24\x74\xd4\xf4\x3b\x61\x65\x14\x0e\xf0\x9e\xbe\x9c\x25\xa5\x05\x83\x85\x36\xf4\x3b\xcf\xa3\x9e\x49\xe5\xd0\x28\x91\x33\x6e\x0b\x4b\x91\x22\xcc\xd6\x20\x9a\x08\x87\x4d\x66\x48\x8d\xfc\x71\x83\x54\x99\x36\x4b\x56\xcb\x51\xef\x73\x6f\x2f\x50\x68\x9d\x48\x6e\x89\x40\xc2\x9f\x94\xc6\xa0\x72\x24\xca\xd2\x58\x79\x87\xbc\x04\xfc\x9a\x20\xcf\xd3\x9f\x7e\x04\xbc\xc7\xa4\xf4\x98\xf6\x2a\x24\x53\xf8\xf8\xf9\xe1\x66\xd8\x63\xd4\x73\x74\x27\x71\xe2\x1c\xd5\xdc\x2d\xa0\xef\x75\x5b\xe4\x03\xda\xae\xb4\x98\xf2\xd1\xd2\xe8\x52\x5a\x26\x0c\x0c\x0a\xab\x95\x1d\x42\xb2\xc0\xe4\x56\xaa\x39\x64\x46\x2f\x99\x17\xa9\x60\xae\x19\xb7\xf4\x84\xfc\xcb\x3a\x2c\xfe\x05\x4b\x74\x0b\x4d\x2a\x60\xc1\x69\x52\x6f\x22\x28\xe0\x16\xf0\xd3\x8f\xa0\x8b\x44\xa7\x38\xea\xed\x6d\xd3\x34\x85\xac\x54\x2c\xb5\xfe\x00\x3e\x1b\x74\xa5\x21\x65\x97\x76\x54\x71\x35\xca\x79\xe5\xe1\x43\x60\x2c\x45\x9b\xa0\x4a\x31\xe5\x83\xbb\xb5\xb0\x5a\xb0\xaa\xc0\x0a\xbf\xb9\x43\xf8\x54\x5a\xd7\x58\xc3\xd4\x0b\x05\xba\x24\x53\x6e\x1e\xbb\x54\xce\x73\x23\xe8\xb7\x42\xc3\x74\x8f\x7a\x7b\x15\xf0\x14\x32\x91\x5b\xa4\x7d\x0b\x61\xa4\x5b\x9f\x1a\xa3\xcd\x8f\xa2\x28\xa4\x9a\x4f\xe1\x73\x6f\x6f\x6f\x3f\xd1\x8a\x35\x06\x12\x83\x5e\x82\xc4\x2b\x58\xa7\x8d\x98\x23\x6d\x4b\xc7\x36\x17\x76\x7f\x0a\xfb\x17\xf5\xd7\x90\x80\x1f\x9f\x9d\x0b\x0b\xa5\x54\xee\xd5\xb7\xa0\xef\xd0\x64\xb9\x5e\x75\x2d\x5b\x8a\xfb\xb0\xa7\xfc\x15\x01\xef\x13\xc4\x14\xd3\xae\x95\x52\xdd\x89\x5c\xa6\xf0\xa9\x5c\x16\x24\x22\x27\x15\x93\x4c\x6b\xbf\x13\x1d\xe3\x0c\x55\xa9\x1a\x18\xbc\x43\xe3\x3c\xee\xcb\xf8\x9b\xd7\x84\x83\x4b\x85\x13\x91\xe5\x19\xf9\xe0\x26\x5f\x61\x80\xd7\x7b\x7d\xce\xe5\x52\x92\xaa\x8b\x84\x9c\xf9\xc1\xe4\xc5\xb7\xd0\x3f\x98\xbc\x78\x39\x68\x40\xf1\x4a\x0f\x54\x18\x4c\xf4\xb2\x90\xac\x5b\x82\xfe\x63\xc2\x4b\x99\xbb\xe7\x52\xc5\xa1\x61\x6f\xef\xa1\xfb\xc4\xae\x9c\x30\x4e\xaa\xf9\xcf\x92\xf4\xee\x73\x53\x22\x5e\x43\xa7\x51\x10\x52\x59\x67\xca\xa4\x96\x81\xa7\x97\x83\x56\x3c\x86\xab\x8d\xa1\xf6\xbe\x57\xb7\xb2\x60\xd7\x63\xdf\x6a\xc3\x44\xd8\x29\x7c\xf4\x5b\xda\x32\xcb\x64\x22\xc9\xcc\x67\x22\x17\x2a\xf1\x1e\x96\x75\x33\x43\xb3\xdf\xdb\x63\x1b\x96\xf6\x62\xf6\x09\x13\x77\xba\x2c\xdc\xba\x61\x27\x7a\xf6\x69\xc0\xd4\x13\x50\xff\x4e\x18\xb8\xa7\x60\xe3\x87\x21\x9c\x04\x2b\xee\x21\x3c\xf4\xf6\xf6\xa2\x51\x99\x12\x0f\x03\x8d\xe3\x31\x90\xe9\x92\x17\x90\xea\x4e\xdf\x06\x63\xa5\x23\x5d\x07\x59\x78\x77\x48\x56\x5e\x79\x1b\xb4\xa3\xde\x1e\xc1\x35\x88\xc9\xf5\x7c\x08\xe9\xcc\x13\x44\xb9\x88\x28\x5c\x69\x90\xdd\x1e\x32\xdb\x20\x97\x4b\x4c\xa5\x70\x98\xaf\x7b\x7b\x7b\x44\x2f\x4f\xc0\x11\xe4\x7a\x3e\x9a\xa3\x63\xf1\xf4\x07\x87\xbd\xbd\x3d\x99\x41\xdf\xcf\xfe\xe5\xe8\x88\x85\x9b\x49\x85\xa9\x47\xbf\xc7\x7e\x21\x13\x65\xee\xaa\x7d\x09\x28\x70\x48\x3f\x1f\x3c\x15\x3f\x23\x68\x95\xaf\x21\xa1\x64\x40\xcc\x48\x23\xed\xda\x3a\x5c\x06\xe6\xec\x10\x32\x61\xc9\x21\xc8\x0c\x56\x08\x85\xc1\xe7\xec\xef\x40\xab\x04\x03\x95\x76\x6d\xd9\xf7\x1e\x01\xed\x36\xd2\xc5\xc8\xe9\x77\xe5\x72\x86\xa6\x3f\x80\xbf\xc2\xe4\x3e\x9b\x0c\xe0\xe8\x88\x7f\x44\xda\x03\x4c\xa0\x97\xb0\xe8\x22\x30\xca\xf0\x57\xce\x48\x35\xf7\xbc\x06\x5a\xcf\x32\x10\xa0\x70\x05\x95\x27\x91\x16\x66\x48\x9e\x97\x5d\x0a\xa6\x43\x10\x69\x4a\xae\x95\x03\x44\x15\x0e\xda\x5b\xc2\x5f\xff\x4a\xfe\x9d\x08\xda\x3f\xb9\x3c\x3d\xbe\x3e\xdd\x87\xdf\x7e\x83\xd6\xc8\x8b\xfd\x41\x83\x32\xa9\x2e\xb2\x2c\x10\xe7\x1d\x6d\x81\x78\xdb\x3f\x18\x8c\xee\x44\x5e\xe2\x45\xe6\xc9\x0c\x6b\x4f\x55\x0a\x47\x01\xe6\xd9\x26\xcc\x8b\x16\x0c\x01\x8d\xc7\x70\x6c\x2d\x2e\x67\x39\x6e\xc7\xcd\x10\x58\x39\xc6\x92\xa3\xf4\xfa\x4f\xb6\x9d\x23\x69\x55\xdc\x35\x88\x9f\x29\xde\x73\xeb\x02\xa7\x00\x00\xba\x18\xf2\x00\x79\x76\x1e\x70\xfa\x07\xbc\xe7\x33\x8a\x22\x24\xad\x3a\x4e\x53\x83\xd6\xf6\x07\x03\xbf\x5c\xaa\xa2\x74\xd3\xd6\xf2\x25\x2e\xb5\x59\x8f\x2c\xe5\x0d\x7d\x66\x6d\xe8\x39\x8d\x30\x73\x61\x79\x87\xa8\xa9\xc7\x77\x42\xe6\x62\x96\xe3\xf7\xc2\xf6\xeb\x35\x67\x6a\x5a\xaf\x69\x4f\x9d\x68\xeb\xa6\x71\x8a\x3e\xe2\x1c\xcb\x8b\xc0\xf6\x27\xf7\xfb\xdb\x12\x9d\x0c\x6a\x6d\x39\x78\x35\x20\x90\x87\xc3\xca\x06\xea\xd8\x58\x94\x76\xd1\x67\x95\xab\x67\xeb\xe0\x77\x14\xad\xbe\xc3\x46\x58\xef\xb6\x75\xce\x62\x9e\x71\x0c\x20\xff\x47\xba\x37\x17\x1c\x5c\xd9\x1d\x08\xca\xa2\x6c\x39\xe3\x83\x71\x5a\x7b\x4c\xef\x2e\xae\x4f\xa7\xf0\x4f\x24\x87\xe2\xc8\xdc\xee\xfc\x99\x6f\x10\x23\x33\x9f\x53\x6c\xeb\x6d\x50\xd2\xab\xd3\xf3\xb7\x6f\x4e\xaf\xae\x2f\x3f\x9c\x5c\xef\x37\x14\x35\xc7\xcc\x11\x2b\x9d\x59\x01\x2d\x22\x74\xed\xd9\x8f\x04\xf3\xfc\xe0\xc6\x8f\xc0\x51\x87\x33\xd9\x7b\x1c\x02\x3e\xde\x30\xee\x87\x6d\xa1\xb7\x97\xfa\x23\xf8\x3a\x3a\xea\xb4\x57\xb7\xb0\xdc\xe9\xb8\xe0\x71\xed\x18\x7c\x5d\x55\x4c\x67\xb4\xe2\x3b\x1f\x9a\x1e\xa1\x79\x5b\x43\x77\xb8\xe3\xca\xc5\x85\x4c\x91\x62\x4e\xe2\x93\xa5\x4a\xef\x52\xad\xf0\xf7\x3b\xba\xe3\xf3\xf3\x96\x9b\x3b\x3e\x3f\x3f\xb9\x78\xd3\x72\x7d\x6f\x4e\xcf\x4f\xbf\x3f\xbe\x3e\xdd\x5c\x7b\x75\x7d\x7c\x7d\x76\xc2\xa3\x4d\xaf\xe8\x34\xa9\xda\x2e\xc1\x1f\x6c\x08\xbe\x72\x76\x14\xef\x39\xe8\x71\x28\xf1\x59\x4a\x83\x4f\x3b\x04\xb7\xd0\x54\xbe\x9a\x90\xa1\x66\x42\x25\x31\xd6\xda\xa8\xc4\xd2\xbe\xaf\x73\x9c\xbe\xd3\x83\xc7\x98\xed\x60\xa0\x21\x7a\xaf\xb8\x1c\x81\xd8\xcb\xf7\x9f\x2e\x0e\xf8\x07\x4c\x60\x0a\x07\x81\xbb\x47\x62\xc5\x0b\x78\x46\xe8\xff\x40\xc4\x78\xd9\x01\xf9\xe7\x8c\x1b\x5b\x36\xf9\xe7\x8c\x27\xba\x74\x17\x59\x36\x85\x4d\x41\x7f\xbb\x25\xe8\x6a\xfd\x39\xaa\xed\xf5\x7f\xdb\x5a\x1f\x62\x4f\xd4\xd1\x2f\x99\x5e\x54\x45\x3a\x08\xc6\xd1\xa1\x36\x5e\x4d\xb8\x70\x1d\xc5\x35\xc1\xf9\xf0\x67\xcb\xc8\xfc\xd6\xac\x19\x29\x9d\xbb\x2c\x50\xa5\xd0\xe7\x04\x8f\x76\xfd\x2d\x6e\x4d\x85\xa0\x0a\x7b\xbe\x86\xc9\x20\x82\x5d\x5f\xbc\xb9\x98\x52\x99\x91\x92\xa3\xa1\xaa\x8a\x8b\x42\x85\xf7\x2e\x26\xbb\xd2\x82\x15\x99\xcf\x07\xe3\x0e\x1e\x51\xb2\x10\x6a\xee\x2d\x94\xd9\xaf\xd1\x07\x3e\x3d\x17\x84\xf5\x08\x66\x72\x7e\xa6\x5c\xbf\x1a\x79\x06\x2f\x5e\x4e\x26\x81\x5b\x36\xc8\x07\xc0\xdc\x22\x34\x04\xd9\x34\xe3\x80\x72\x53\x2e\x93\xfd\x60\xd1\x5f\x3b\x01\xe8\xac\x98\xa9\x2e\x6e\xd7\xc4\x43\xaa\x2a\x8c\xc4\x3b\x04\xe9\xbe\xb1\x8c\x13\x44\x9e\xeb\x15\x45\x88\x11\xfc\x8c\x1e\xa3\x42\x64\xf7\x1d\x9a\x28\xc4\x65\xb3\x79\x50\x79\x75\xc1\xbd\x0e\x83\xb0\x14\x6b\x98\x21\x55\x13\xb7\x6b\x3e\x98\x74\xad\xc4\x52\x26\xd6\xe3\xe3\x06\x8a\xc1\xb9\x30\x8c\xd6\xe0\xbf\x4b\xb4\x0e\x53\x76\x00\x22\x71\xa5\xc8\xf3\x35\xcc\xe5\x1d\x2a\x86\xee\x93\xb4\xe3\xf9\x0d\xe1\xd5\xcb\xf1\xab\x6f\xc1\x94\x39\x0e\x46\xbd\x46\x96\x50\xb1\x1a\xe4\x4d\x13\xc1\xa2\xde\x60\xe1\x16\xfd\x01\xbc\xde\x91\x6e\x34\x95\x3b\x78\x99\x8d\xdc\xa0\x13\x0c\x9e\xc3\x81\x4f\x27\x78\xb3\x5a\x63\xba\xf2\x92\xa6\x42\x35\x7d\xc0\xb6\x16\x7d\x6e\x6a\x78\xff\x56\x18\x91\x8b\x19\x0e\xa6\xdc\xa2\x64\xf2\x56\x22\xf4\xa8\xe8\x48\xa1\xc8\x85\x54\x20\x92\x44\x97\xca\xd1\xb1\xc5\x76\x53\xbe\xa6\xf8\xfb\x8d\x8b\xf8\xb8\x9b\x27\x92\x04\xad\x8d\xe1\x98\xcf\x9c\x88\x12\x4b\x82\xa6\x12\x59\xa6\xd8\x38\x53\xf2\xc9\x9a\x43\x60\x58\xb1\x92\x79\x1e\x11\x2e\xb5\xa5\x4d\x66\x08\x2b\xa3\x29\xcb\x94\x54\xf5\x4a\x52\x3b\x3a\x2b\x0b\x5a\x81\x80\x5c\x73\xc9\xcf\x9e\x15\x84\x99\xdb\x91\x8f\xab\x6c\xb2\xda\x80\xd2\xab\x51\x3b\x27\x6b\x6a\xba\x2f\x79\x83\x7e\x77\x67\x98\x97\xa7\x3f\x9d\x5e\x56\xb9\xe5\x93\x4f\x6e\x14\x0b\xd6\xae\x5e\x48\x15\xb7\xf8\x10\x7e\x95\x7a\x2e\x6c\xb2\x30\x03\xef\x71\x58\x40\xba\x74\xc4\x11\xdb\x82\xef\x24\x08\x4b\xcc\x53\xd8\x11\x52\xf9\xce\x9f\xdf\xa3\x10\xd6\xc6\xae\x15\xcb\x36\x26\xe8\x29\xde\x61\xae\x0b\x34\xdb\xb6\xbc\x8b\xd7\xeb\x0f\x97\xef\xf6\x77\xeb\xf8\xd1\x13\x74\xdc\x47\x95\x6d\x0f\x3e\xd9\x0c\xf9\x71\xf5\x39\xaa\x27\x94\x94\xbf\x43\xf4\x41\x76\x47\xbb\xc2\xac\xa7\x70\x18\x29\x7d\x16\x88\x18\x0c\xea\x24\x68\x5b\x5a\x4f\x94\x04\x51\x10\xa4\x31\x1e\xc3\x7b\x5d\x70\x2e\x45\xc7\x92\x0b\xeb\x6a\xbd\x9f\xa3\xef\x94\x34\xb5\xc3\x96\xb9\xb3\xbd\xc7\x5c\xc5\xa8\xd0\x45\x4c\x7b\x2a\xaf\x40\xd9\xca\x66\x0d\xdf\x35\xf1\xa2\x0a\x16\xde\x93\xbb\xa6\xc5\x0b\xf0\x8b\x1a\x7e\xbb\xa5\x4b\xc2\xa7\x38\x4c\x7b\x90\x2f\x45\xc1\x5e\xd3\xf9\x7c\xb0\x6c\x54\x21\x2a\x6f\x06\xb6\xe7\x95\x0c\xd9\x35\xd1\x77\x9c\x3b\x53\xf0\x1c\xe2\x07\x65\x28\x83\x8d\x4a\xc1\x6b\x40\x8a\x39\x3a\x84\x1a\xea\x10\x36\x86\x08\x36\xc4\x7e\x92\xa1\x41\xd7\xa5\x87\xb5\x57\xfd\x8b\x41\x37\xc2\x7f\x97\x22\xb7\xfd\xc9\xa0\xed\x4d\x9d\xe6\xb4\xeb\x68\xab\xb0\x22\x98\x76\x29\x75\xd8\x00\xdb\x50\x3e\x5f\x18\x9d\xe8\x14\x1f\xc5\x10\x3d\x75\x1d\xea\x19\x59\x70\x22\x9d\x2e\xdf\x77\x8a\x4e\xdb\x7d\xb1\x13\x91\xe7\x8d\xde\x58\x95\x7f\x9d\xee\x6c\x90\x35\x6b\xe1\x9d\x7d\xc8\x91\x54\x29\xde\x5f\x64\x11\xd3\x00\x5e\xc3\xf3\x83\x1a\x43\xb3\x88\x88\x26\x14\x05\x12\x1d\x61\x00\x0d\x6b\x5a\xe1\xa8\x6a\x09\x78\x5f\xe8\x5d\xe1\x0a\xe3\x45\x11\x77\x7b\xd9\x12\x3c\x90\x50\xeb\xa5\x36\xd8\xb5\xc9\x7e\x95\xfc\x67\x42\xe6\xa5\xc1\xfd\x43\xe8\x08\x76\xb6\x34\x99\x48\x58\xc5\x2d\x02\xb7\x07\x2d\x58\xbd\xc4\x85\x5e\xf5\x3a\x38\x7a\xd8\x1d\x47\xb7\x0d\xa9\x6e\xef\xb7\xf3\x20\xbe\xa3\x12\x16\x4a\x2b\xe6\xd8\x30\xa4\xed\x18\xdf\x7d\x4e\x4f\x32\xb3\x2d\x53\x82\x67\xd0\x30\xc1\xa6\x05\x76\x99\xd8\xc3\xff\xae\xa1\x55\x5c\x47\xab\x69\x32\x5e\xf9\xb1\xc6\x24\x31\x5d\xab\x5d\x97\xc1\x05\x0e\x2f\xf9\xf8\xde\x08\x27\xfa\x95\x7d\x3e\xfc\x7f\xb4\xb1\xae\x1e\x40\xf4\x33\xc1\x8f\x0d\x06\x3e\xc6\xd7\x04\x36\x6f\x91\x1a\x3b\xb4\x4d\xa9\xe3\xee\x84\x8d\xe9\x3d\x73\xc0\x35\xb4\x70\x72\x96\x47\x43\xdc\x30\xe9\xc7\xac\x3f\x52\xff\x67\xf7\x02\xd1\xee\xb7\xac\xc2\xa7\x0e\x6d\xb3\xf0\x59\x44\x9d\x43\x7c\xd9\xa4\x1b\x59\xfb\x37\x93\xfb\x6f\xb6\xad\xb9\xc3\x44\x1f\x62\xee\x78\xa6\x3e\x61\xe2\x6a\xe7\xc3\x35\x18\x7d\x15\x06\xef\xa4\x2e\x29\x41\xc6\x27\xf7\x43\xc3\x02\xfe\xef\x35\x4c\xe0\x1f\xe0\x3b\x96\x30\xe5\x1f\x8f\xf5\x4c\x7f\x6f\xc7\xf4\xc9\xfd\xd2\x56\xb7\xb4\xaa\x57\x1f\xea\x0b\x29\x3e\xb2\xe6\x8d\x14\x57\xf3\x24\x03\x5f\xe9\x35\xb2\x2b\x9d\xf1\x8d\xae\x2f\xdd\x33\xff\x22\x61\x8f\xe1\x1f\xb9\x99\x0a\xbe\xdd\xe9\x82\x8a\x91\x90\xbc\xe5\x94\xa3\xaf\xab\x64\x7e\xe8\xcb\x20\x58\x08\x95\x86\x06\x94\x48\x53\xe9\x2f\xcd\x03\x85\x62\x2e\xa4\xea\x75\x0a\xf0\x8b\x15\x44\x97\xe2\x6c\xd5\xe5\xcd\x3c\x33\xb4\x0a\xd9\x6c\x09\x71\xef\x09\xf9\xe4\x86\xfd\x6c\x5e\xb2\xf5\x9e\xe6\x09\xbf\xe4\x06\xff\x53\x1f\xb8\xd9\xa9\xdc\xe5\x60\xd8\x44\xf8\x91\x93\xb2\xe5\x92\xdb\x0e\x20\x62\xdb\xcc\x17\xa4\x2a\x85\x24\x47\xa1\xfc\x93\x1d\xcc\x9c\xbe\x43\x63\x7b\x4f\x30\xda\x3f\x62\xb3\x1b\x91\x3b\x7e\xf6\xda\x0e\x70\x3c\x86\xcb\x98\x2b\xd0\x06\xed\x96\x49\x5d\xdf\xb5\x1e\x1e\xc4\xbb\xcb\xaf\xda\x47\xf9\xfa\x8d\x94\xdd\x62\x7b\x3c\x23\xe1\xa3\xec\x08\xd5\xcd\x00\x46\xc1\xed\xd1\xf6\x48\x63\xef\xaa\x33\x46\x0a\xf4\xd4\x34\xe7\xe9\xae\xdf\xeb\xdd\xdb\x5c\x38\x17\x1c\x51\xc3\x10\xbd\x87\x96\x8e\x9f\xd1\xa1\x72\xbd\xa7\xb9\x66\x2e\x3e\x83\x5b\xde\x34\xa4\xff\xbb\xcb\xab\xba\x7d\xb8\xe5\x8c\xce\xab\x42\x37\x30\xef\xb4\x1e\x42\x8e\x82\x7b\x81\xf1\x91\x5b\xbc\xa6\x79\xac\x37\x19\xfd\xbc\x2f\x8d\xb7\x1c\x3d\xdf\x28\x2e\x30\xde\x88\xf8\x16\xd4\x0c\x51\x81\x74\x68\x04\x29\x2b\x99\x75\x78\x97\x45\x54\x5a\x46\xc7\xe7\x22\xc9\x3d\x07\xc4\xe1\x91\x14\x59\x8e\x54\xf3\x51\x6f\xcf\x8f\x37\x22\x43\xe2\xee\xeb\xc8\xe0\x33\x5e\x86\x0c\x37\x06\xb3\x5c\x27\xb7\x53\x00\x48\xdc\xfd\x88\x3f\xb8\x63\x5e\xdd\x23\xd0\x30\x7d\xf0\xe8\xc6\x65\x02\xcd\xd1\x90\x6f\xb2\x6f\x5c\x1d\x30\x60\xb8\x3e\xa8\xee\xdc\x82\x01\xd1\xdc\x76\xeb\x9b\x97\x56\x97\x06\x1b\x2e\xca\xdd\x6f\x7b\xa8\x08\x40\xce\x69\xda\x0d\x40\x53\x1d\x40\x1b\xd7\x19\xb4\x98\x87\xfc\xac\xcf\xcb\xa7\xcd\x59\x3f\x14\x18\x95\xcb\x86\x6c\xe4\x92\x65\xc3\xf7\xd5\xfc\xdc\x83\xdc\xd8\x89\xbb\x6f\x09\xf8\x07\x61\x17\xd3\x5a\xc4\xf4\x39\xac\x26\xfd\x33\x8b\xe9\xe6\x09\xd4\x8f\xb7\x6a\xe8\x8d\xc1\xcd\x85\xef\xb5\xe5\xf0\xbd\xb5\x38\x4e\x54\x94\x92\x9b\xf4\x19\x47\xab\xaf\x68\x70\xe9\x5b\x74\xec\xc0\x55\x0a\x99\x34\xd6\x91\x52\x2e\x49\xfb\x93\xe6\x03\x3e\xa1\x00\x97\x85\x5b\x83\xe6\x87\x3b\x1e\x69\x6a\x74\x11\xb4\x34\x02\x0e\xf9\xbd\x8e\xe1\x17\xb0\x3a\x26\x1b\x98\xce\xc9\x01\x59\xb4\xb5\x55\x61\xd1\x1f\x40\xae\x75\x31\xf2\xb8\xf0\x5e\x2c\x8b\xe6\xda\x69\xe3\x06\x5f\x49\xdf\xc6\x21\xbf\xf8\x6a\xf2\x37\xf1\x6a\x32\x99\xfc\xed\xe5\xab\xc9\xe4\x80\x7e\xd1\xff\xd9\x24\xcb\x26\x93\xfd\x21\x58\x14\x26\x59\xf0\x3e\x68\x5d\x2a\x9c\xe8\xec\x8f\x93\x33\xee\xce\x69\x5e\xc3\x41\x35\xd9\x7a\xa7\xb4\xe9\xca\x26\x37\x83\xce\x6e\xeb\xc8\x2e\x64\xe6\xea\x87\x30\x1d\x5e\x70\x12\xdd\x59\x77\xd6\x44\x26\x5b\xf9\xbb\x1d\xa0\x8f\x63\x7f\x2c\x27\x63\xec\x31\x1d\xd9\x01\x7a\xd8\x6b\x17\xa0\xee\xfe\xe9\x28\xab\xc5\x4d\x12\x5b\x6b\x5a\x48\xf8\x1e\x78\x6b\xba\xab\x1d\x4d\x85\x76\x58\x58\x97\xda\x5c\x69\x07\x42\x42\xa8\x6b\xad\x89\x44\x34\xdf\x5f\xb2\x53\x95\xbf\x62\xd8\x76\x58\x99\x71\xd3\x99\xc7\x45\x60\xd0\x3f\x56\xe2\x9e\x09\xf9\x72\x6f\x02\x50\x5a\xa9\xe6\x0d\x27\x9d\xa2\x95\x86\x6a\x50\x89\x79\x1a\x6c\x20\xd3\x06\x3e\x59\xad\xfc\xbb\x34\x34\x92\x50\xfa\x67\xb2\xfe\xc5\x3a\x3f\xde\x55\x32\x41\xb7\x86\x0c\x05\x3f\x30\x73\x9a\x7b\xe5\xb0\x44\xa1\xa4\x9a\x67\x25\x65\x30\x8c\x8f\x8d\x36\xb4\x5f\x29\x40\x68\x28\x2d\x1a\x0b\xab\x85\x0e\xe9\x3d\x57\x94\x85\x41\xb2\xc6\x61\xb8\xfe\x92\xb6\xc8\xc5\x1a\xa4\xa3\x52\x22\x70\xd5\x8c\x19\xdc\xfe\x89\x22\x18\xfa\x37\xc3\xa1\x34\xaf\x03\x09\x59\xd2\x61\xef\x3f\x69\xe7\xf2\x0b\xca\xa8\x71\x2c\xd1\x4b\xe6\x25\x86\xd1\x90\xd0\x95\x45\x2a\x1c\x82\xc8\x5c\xc8\x1e\xfd\x2a\xbe\x81\xe1\xab\x05\x91\x65\x98\x38\xeb\x9f\xc1\x91\xf8\x8d\xd6\x0e\x68\xd7\x2a\x89\xf2\x24\x54\xa4\x6d\x6a\x73\x8b\xca\xae\xa7\x39\x2d\x24\x57\x1f\xce\x4e\xce\xde\x78\x2c\x2d\x26\x6c\x29\x13\x99\x6e\x70\xd1\xce\x95\x5b\x3c\x57\xbc\x7c\x5d\x8e\x3b\x4e\xa4\xf9\x56\xa4\x3d\xb5\xf5\x46\x62\x43\x18\x3b\x6e\x6c\x2b\x81\xd2\x4c\x95\x7f\x71\xd6\xdb\x54\x17\xbe\x94\x6d\x7c\xfe\xf6\x5b\x48\xd7\xf8\xd1\xa1\x26\x2b\x8e\x91\xd2\xe7\x1c\x15\xf2\x91\xd3\xe7\x7a\x85\xe6\x44\x58\x0c\xf7\xf8\x3e\x98\x4d\x59\xf3\x46\xe1\xf9\x79\xed\x6e\xc2\x78\xb0\x60\x1a\x67\xe7\x11\x50\xf2\xef\x18\x30\x2b\x7a\xa6\x2d\xea\x78\xda\x96\x33\x1e\xb3\x53\x98\xec\x0e\xb0\xd1\x38\x76\x45\xd9\xed\xf8\xdd\x05\xd1\x9d\x08\x54\x2b\x1b\xa3\xc3\x8d\x5c\xa2\xbd\xa6\x4e\x06\x38\x37\xf1\x32\x8c\x99\x49\x2c\x44\xbc\xb4\x1f\xf1\xda\x1b\x55\x6e\xf3\x35\xf1\x68\x21\xec\xc5\x4a\xbd\x37\xba\x40\xe3\xd6\x2d\x5c\x55\x53\xb4\xb5\x41\x50\xf1\x6d\x54\x1f\x9b\xcb\x6e\x5a\xf7\x23\x61\xc6\x9f\xe0\xe1\x56\xcf\xba\x7a\x02\xec\x23\xfa\x3f\x71\xed\x93\x87\xce\x6d\x9a\xef\x9f\x5b\x5d\xdb\x27\xac\xdf\xe2\x36\xee\xc7\xed\xc8\x26\xfd\x55\xd9\xdf\x58\xd2\x6e\x7e\x3e\x4d\x28\xcd\xdd\x3f\x56\xb8\x6e\x62\xd3\x71\xa7\x7c\xda\x0d\xfe\x78\xda\x95\xa0\x6e\xbd\x88\x3c\x5c\xf3\x94\xd7\x05\xea\x2c\x8c\x7f\xbc\xc5\xf5\x4d\xa8\x4d\x39\x8e\x55\x86\x5e\xe1\x51\x5c\x40\xff\x77\x0b\x1d\x83\xb5\x24\xdb\x18\xff\x58\x43\xdc\xec\xe8\x50\xb7\x99\xda\x82\xda\x79\x7f\xb1\xb1\x53\x37\xf6\x6d\xdc\x6d\x17\x15\xfb\x4a\x36\x36\x35\xaa\x0c\x2a\xfa\xce\xee\x4c\x2c\x20\xdc\xaf\x9c\xc4\xfe\x4d\xc0\x60\x1b\x85\x6f\xb5\x45\x08\xcd\x54\xa3\x7a\xc8\x9b\xc3\xde\x17\xf7\xa8\xa4\x2e\x8f\x26\x87\x20\xff\xde\xc2\x0e\xf2\xd9\xb3\xd6\xbb\x8c\x85\xcc\xd3\x13\xdf\x21\xe3\x85\x1f\xe5\x4d\xfd\x86\xe8\x0d\xe6\x38\x17\x0e\x39\x49\x29\x29\xdb\xa6\x58\xe1\x1f\xdb\x70\x93\xa5\xaa\xe7\x3d\x51\xfd\x0a\xdd\x63\xf1\x61\x7b\x4d\x2b\x48\x50\xaa\xec\xf5\xab\x5e\x19\xde\xf7\xd0\xda\x8a\xdd\xba\x7f\xbf\xb5\xce\x9f\x09\x7f\x1c\x36\x1b\xd9\xb5\x3c\xc3\xaf\x51\xa2\x55\x22\x5c\xbf\x9d\xc4\x55\xf8\x76\x25\x31\x11\xec\xa3\xbc\x19\x34\x2e\xcf\x1b\x59\x61\xc0\x1f\xb3\xbf\x66\x76\xb2\x91\x28\x79\x2e\x02\xd8\xe7\x66\x98\x0a\x46\x14\x8b\xe6\xf0\x8f\x59\xa3\xc1\x21\x6c\xfc\x1b\x8f\xe1\x8a\x4a\x11\x13\x4e\x37\x14\xcd\x4d\x38\x1e\xdc\x04\x1c\x8f\xe1\x27\x1a\x67\xb0\xaa\x80\x6e\x82\xcd\x85\xdd\xda\x8d\xc0\xbe\x17\xe1\xad\x97\x92\x6e\x8b\x46\xae\x88\x3b\xf6\x3a\x53\xd2\xd5\x59\x6b\xe3\x32\x3d\xfc\x4d\xd0\x8f\xfc\xee\x75\x77\x24\x67\x24\x27\x2c\x50\xb8\x0e\x19\xcb\x43\x2b\x8e\x7f\x8e\x9c\xf8\xca\x1e\x9a\x5d\xc8\x61\x24\x9c\xff\xa4\xcb\x6f\xac\xd3\x20\xa8\xc6\xb5\xda\xd0\x13\x7b\x12\xa9\x13\x31\xe0\x07\xba\xf4\xb0\xe6\xe8\xd8\x5a\x39\xa7\xc2\x23\x2c\x6a\xe8\x83\x3f\xfd\x2a\x03\xfb\xe3\x67\xbf\xf3\xd8\xdb\xa7\x5e\x75\x51\xb6\xe8\x6c\x41\x5c\x62\x22\x0b\x19\x4d\xb7\xa1\x2a\x3b\xb5\x64\x3c\x86\xeb\xf0\x37\x37\x98\x76\xeb\xcb\x4e\x55\x69\x69\x4a\xe8\x9c\x3c\xa2\x24\xac\x23\x54\x8c\x85\x5a\xdb\x67\x8c\xd7\xbe\xa5\xb4\x53\x2b\xc2\x1f\x9b\xb2\x4f\xd1\xf5\xd5\xf4\x17\xb5\xe3\x0b\xca\x51\x35\x73\xb6\x75\xe3\xc2\x97\x8c\xb3\xb5\xc3\xad\x23\x6f\x65\xf2\xbf\xf3\xd4\x6b\x55\xeb\x38\x7a\xff\xb2\x33\x6a\xd9\x9e\xc1\xac\x54\xe9\x71\xb7\x72\xf2\x41\xd3\x7c\x53\x31\xf7\xc2\x9f\x51\xb5\xd1\xc7\x23\x1f\x8f\x21\xbc\x65\xdf\x16\x9c\x2a\x59\x9c\xc4\x62\xef\xa1\xf7\x3f\x01\x00\x00\xff\xff\x16\xa0\x42\x98\x4c\x3d\x00\x00") func call_tracer_parityJsBytes() ([]byte, error) { return bindataRead( @@ -227,7 +227,7 @@ func opcount_tracerJs() (*asset, error) { return a, nil } -var _prestate_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x57\xdd\x6f\xdb\x38\x12\x7f\x96\xfe\x8a\xd9\xbc\xc4\xc6\xfa\xe4\xb6\x0b\xdc\x01\xce\xe5\x00\xd5\x75\xdb\x00\x6e\x12\xd8\xea\xe6\x72\x8b\x7d\xa0\xa8\x91\xc5\x33\x4d\x0a\x24\xe5\x8f\x6b\xf3\xbf\x1f\x86\x94\xfc\x91\x26\x6d\xba\x6f\x36\x67\xf8\x9b\x99\xdf\x7c\x51\xc3\x21\x8c\x75\xbd\x33\x62\x51\x39\x78\xf3\xea\xf5\x3f\x20\xab\x10\x16\xfa\x6f\xe8\x2a\x34\xd8\xac\x20\x6d\x5c\xa5\x8d\x8d\x87\x43\xc8\x2a\x61\xa1\x14\x12\x41\x58\xa8\x99\x71\xa0\x4b\x70\x8f\xf4\xa5\xc8\x0d\x33\xbb\x24\x1e\x0e\xc3\x9d\x27\xc5\x84\x50\x1a\x44\xb0\xba\x74\x1b\x66\x70\x04\x3b\xdd\x00\x67\x0a\x0c\x16\xc2\x3a\x23\xf2\xc6\x21\x08\x07\x4c\x15\x43\x6d\x60\xa5\x0b\x51\xee\x08\x52\x38\x68\x54\x81\xc6\x9b\x76\x68\x56\xb6\xf3\xe3\xc3\xf5\x67\x98\xa2\xb5\x68\xe0\x03\x2a\x34\x4c\xc2\x6d\x93\x4b\xc1\x61\x2a\x38\x2a\x8b\xc0\x2c\xd4\x74\x62\x2b\x2c\x20\xf7\x70\x74\xf1\x3d\xb9\x32\x6f\x5d\x81\xf7\xba\x51\x05\x73\x42\xab\x01\xa0\x20\xcf\x61\x8d\xc6\x0a\xad\xe0\xb7\xce\x54\x0b\x38\x00\x6d\x08\xa4\xc7\x1c\x05\x60\x40\xd7\x74\xaf\x0f\x4c\xed\x40\x32\x77\xb8\xfa\x02\x42\x0e\x71\x17\x20\x94\x37\x53\xe9\x1a\xc1\x55\xcc\x51\xd4\x1b\x21\x25\xe4\x08\x8d\xc5\xb2\x91\x03\x42\xcb\x1b\x07\x77\x57\xd9\xc7\x9b\xcf\x19\xa4\xd7\xf7\x70\x97\xce\x66\xe9\x75\x76\x7f\x01\x1b\xe1\x2a\xdd\x38\xc0\x35\x06\x28\xb1\xaa\xa5\xc0\x02\x36\xcc\x18\xa6\xdc\x0e\x74\x49\x08\x9f\x26\xb3\xf1\xc7\xf4\x3a\x4b\xdf\x5e\x4d\xaf\xb2\x7b\xd0\x06\xde\x5f\x65\xd7\x93\xf9\x1c\xde\xdf\xcc\x20\x85\xdb\x74\x96\x5d\x8d\x3f\x4f\xd3\x19\xdc\x7e\x9e\xdd\xde\xcc\x27\x09\xcc\x91\xbc\x42\xba\xff\x63\xce\x4b\x9f\x3d\x83\x50\xa0\x63\x42\xda\x8e\x89\x7b\xdd\x80\xad\x74\x23\x0b\xa8\xd8\x1a\xc1\x20\x47\xb1\xc6\x02\x18\x70\x5d\xef\x5e\x9c\x54\xc2\x62\x52\xab\x85\x8f\xf9\xd9\x82\x84\xab\x12\x94\x76\x03\xb0\x88\xf0\xcf\xca\xb9\x7a\x34\x1c\x6e\x36\x9b\x64\xa1\x9a\x44\x9b\xc5\x50\x06\x38\x3b\xfc\x57\x12\x13\x66\x6d\xd0\x3a\xe6\x30\x33\x8c\xa3\x01\xdd\xb8\xba\x71\x16\x6c\x53\x96\x82\x0b\x54\x0e\x84\x2a\xb5\x59\xf9\x4a\x01\xa7\x81\x1b\x64\x0e\x81\x81\xd4\x9c\x49\xc0\x2d\xf2\xc6\xcb\x02\xd3\xbe\x5c\x0d\x53\x96\x71\x7f\x5a\x1a\xbd\xa2\x58\x1b\xeb\xe8\x87\xb5\xb8\xca\x25\x16\xb0\x40\x85\x56\xd8\x0c\x72\xa9\xf9\x32\x89\xbf\xc4\xd1\x91\x37\x54\x28\x3e\xc4\x4e\xcb\x57\xc7\x06\xcf\x0d\x42\xde\x08\x59\x08\xb5\x48\xe2\xa8\x53\x1f\xc1\x97\x87\x41\x1c\x55\xcc\xce\x1d\xd6\x57\x4a\x38\x2c\x46\x50\x32\x69\x71\x10\x7b\x60\xa9\xf5\xb2\xa9\x53\xce\x75\xe3\x43\xfa\x2f\x72\x17\x4c\xd8\x1a\xb9\x28\xa9\x66\xd8\x5e\xea\xb4\x17\xed\xbd\xd1\x39\xe9\x27\x71\x74\x02\x33\x82\xb2\x51\x3e\xca\x1e\x2b\x0a\x33\x80\x22\xef\x7f\x89\xa3\x68\xcd\x0c\x61\xc1\x25\x38\xfd\x11\xb7\x5e\xd8\xbf\x88\xa3\x48\x94\xd0\x73\x95\xb0\x49\x07\xfc\x07\xe3\xfc\x4f\xb8\xbc\xbc\xf4\xbd\x5e\x0a\x85\x45\x1f\x08\x22\x7a\x4a\x2d\x48\xa2\x9c\x49\xa6\x38\x8e\xe0\xfc\xd5\xf6\x1c\x7e\x85\x22\x4f\x16\xe8\xde\x86\xd3\x60\x2c\x71\x7a\xee\x8c\x50\x8b\xde\xeb\xbf\xf7\x07\xfe\x96\xd2\xfe\x0e\xb4\xea\xd7\x7a\xaf\x1c\xe4\x5c\x17\x5e\xdc\xfa\x1c\xb4\xc6\xba\x68\x95\x5a\x2d\xeb\xb4\x61\x0b\xcf\x37\xfd\x7f\xa0\xa8\x1e\xe2\xe8\xe1\x84\xe5\x79\x50\x7a\x86\xe5\x16\x02\x50\x39\xb3\x2f\xff\x85\xa0\x06\x3e\x4e\x80\xc7\xfb\x5e\x12\xe6\x9d\x2b\x8f\x92\xb0\xc4\xdd\x8f\x33\x41\x02\x51\x6c\xf7\x82\x25\xee\xfa\x17\xf1\xb3\x29\x4a\x5a\xa7\xff\x10\xc5\xf6\xa5\xf9\x7a\x74\xe7\x84\xd7\x39\x69\x1d\xfc\xed\xf7\x1f\xf1\x28\x94\x70\xd4\x02\x42\xad\xf5\x92\x66\x38\x96\x34\x5a\x68\xdc\xfe\xfe\xe9\xd0\x72\x89\x57\xe6\x6e\x0b\x15\xb3\xe0\xf4\xd7\x95\x5d\x64\xfa\x2b\xd7\x42\xe5\xcc\x22\x58\xf4\x8b\x05\x58\x51\x08\xd2\x67\x12\xb8\x56\x0e\xb7\x0e\x48\x5e\x80\x56\x80\x8c\x57\xd4\xb0\x1c\x61\x85\xae\xd2\x45\x12\x47\x64\xfe\x88\x57\xee\xb6\x9e\x51\x1f\xe9\x70\x08\x0b\x82\xe5\xae\x61\x12\xce\xa8\xbd\xcf\x60\xcd\x64\x83\xd6\xcf\x40\x3a\xf8\xea\x8e\x9c\x68\x93\x6a\x93\xb8\x65\xe9\xa4\x87\x08\x3c\xa1\x3b\xde\xc2\xc5\xf3\x3a\x1d\x5e\xab\x17\x3c\xf1\xf1\x12\x51\x14\x29\x59\xf7\x2b\x80\xc8\x0b\xb1\xf8\xe8\x03\x95\x24\xe9\x9c\x76\xba\x75\xb9\x2b\xbf\x6c\x9b\x04\x40\xb2\xe4\x34\xf1\x42\xc7\xda\x6f\xc5\xca\x83\x58\x60\x72\xc3\x76\xc1\x54\xbe\xf3\xf2\xc9\xef\x9f\xbc\x05\x52\xdf\xd5\x78\x39\x9e\x4d\xd2\x6c\x12\x90\x0e\x56\x15\x6e\xe4\xae\x1d\x9b\x85\x4f\x80\x61\xdc\x51\x52\x0c\x5a\xdb\xd6\x1c\x19\x0e\xd1\xfc\xf2\x5c\x75\x7d\xcb\x89\xbf\xb0\x27\xee\xa8\x7e\x0c\xda\x46\x9e\x54\xd0\xa6\xa2\xfe\x92\x32\xc4\x55\x53\xb7\xdb\xb0\x90\x72\x44\x05\xc2\xa1\xf1\xee\xe9\x35\x1a\x1f\x93\x41\xd7\x18\x65\xf7\x6d\x58\x0a\x2a\x9f\x16\xb8\xa5\x8d\x02\x09\x83\x38\x9c\x3f\x57\x33\x5d\x80\x44\x12\x5c\x5e\xc2\x79\x20\xea\xbc\x8d\x6e\x38\x84\x3b\xf4\xaf\xa2\x5c\x8a\xbc\x90\x3b\x28\x50\xa2\xf3\xcb\xf7\xc0\x57\xd7\x5c\x03\x7a\xde\x50\x27\xe0\x56\x58\x27\xd4\x02\xc2\x78\xd8\xd0\x8e\x6d\xe1\x7c\x60\x9c\x35\x54\xe3\x8f\x17\x92\xd3\xf4\xba\x30\x48\xc3\x84\x86\xbe\xe7\x88\x49\xb1\x7f\x8d\x94\xc2\x58\x07\xb5\x64\x1c\xa9\x2a\xa2\xbd\x33\xc7\x1d\x1e\xba\x39\x94\x4b\xff\xcf\x96\x7e\x32\x3d\xf3\xbc\x85\x72\xdb\x2f\x3b\x26\x69\x59\x92\x79\x0b\xbd\x0e\xa3\x1f\x47\x91\xe9\xb4\x8f\xb0\x2f\x0e\x79\xb4\x0e\xeb\xe3\x2c\x52\x89\xe3\x1a\x69\x6e\xfa\x14\x86\xb5\x48\xb6\xf6\x63\x01\xa9\xd1\xe8\xde\x51\x32\xa4\x5e\x9c\x26\xe3\x17\x6f\xf0\x64\x5d\x1e\x57\xda\x89\x80\x46\x97\x69\xd0\xb7\xdc\x53\x85\x28\xf5\x22\xe9\x92\x44\xb3\x2d\x0d\x75\xdd\xeb\x1f\x15\x66\xa0\xe6\xae\x42\x45\xde\x53\x47\xc0\x7e\xcd\x33\xce\xe9\xdd\x53\x0c\xa8\x23\xa8\x6d\x1e\x2d\xdf\x38\x8a\xec\x46\x38\x5e\x81\x37\xa5\xeb\xc3\x72\xeb\xb7\x4e\x73\x9a\x33\x67\x93\x7f\x67\xe3\x9b\x77\x93\xf1\xcd\xed\xfd\xd9\x08\x4e\xce\xe6\x57\xff\x99\xec\xcf\xde\xa6\xd3\xf4\x7a\x3c\x39\x1b\xf9\x8d\xf6\x44\x44\x4e\x77\x31\x90\x41\xeb\x18\x5f\x26\x35\xe2\xb2\xf7\xea\x74\xb1\x1e\x22\x8c\xa2\xdc\x20\x5b\x5e\x1c\x9c\x09\x15\xde\xda\xa0\x6d\xe3\xdf\x41\x97\xf0\x2c\x5b\x17\xcf\x7b\x33\x6e\xf5\x7b\xdd\x9c\x3c\x2c\x70\x3a\x79\x81\x1f\x6f\x7e\xda\x11\x5f\x7c\x8c\x2f\x47\x60\x99\xa4\xe7\xa4\xf8\x1f\x7d\x06\x94\xa5\x45\x37\x00\x54\x85\xde\xac\x50\xb9\x3d\x6a\x90\xb4\xb8\x47\x94\xbd\xee\x27\x7e\xd2\xde\x94\xbd\xfe\x5e\x99\xc0\xbe\x55\x7d\xf3\x94\x2a\x2a\x2a\xc0\x16\xfd\x57\x7f\xf3\xc7\x44\xbd\x69\x99\x7a\x64\xe0\xb7\x47\xef\x22\x2f\x5f\xe1\x4a\x9b\x5d\x62\xe9\x5d\xdc\x3b\x8a\xef\xfb\xac\xa6\xd3\xe9\xbe\x9e\xe8\x0f\x15\xd9\xfe\xe0\xdd\x64\x3a\xf9\x90\x66\x93\x13\xad\x79\x96\x66\x57\xe3\x70\xf4\xd3\x85\xf7\xfa\xc5\x85\x77\x3e\x9f\x67\x37\xb3\xc9\xf9\xa8\xfd\x37\xbd\x49\xdf\x9d\x7f\x63\xb0\x7d\x3b\x7d\xaf\x77\x9d\xbe\xd3\xa6\xf8\x2b\x1d\x70\xb4\x87\x4a\xf6\xd4\x1a\x3a\x5a\xc5\xc7\x5f\x0f\xc0\x54\x37\xd6\xca\xf0\x05\x15\xf9\xfb\x4f\x0e\xb2\x87\xf8\x21\xfe\x7f\x00\x00\x00\xff\xff\x45\xe2\xdb\xb4\xd7\x0f\x00\x00") +var _prestate_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x57\x5f\x6f\xdb\x38\x12\x7f\x96\x3e\xc5\x6c\x5e\x62\x63\x7d\x72\xdb\x05\xee\x00\xe7\x72\x80\xea\xba\x6d\x00\x37\x09\x6c\x75\x73\xb9\xc5\x3e\x50\xd4\xc8\xe2\x99\x26\x05\x92\xf2\x9f\x6b\xf3\xdd\x0f\x43\xca\xb2\x9d\x26\x6d\xba\x6f\x36\x67\xf8\x9b\x99\xdf\xfc\xa3\x86\x43\x18\xeb\x7a\x67\xc4\xa2\x72\xf0\xe6\xd5\xeb\x7f\x40\x56\x21\x2c\xf4\xdf\xd0\x55\x68\xb0\x59\x41\xda\xb8\x4a\x1b\x1b\x0f\x87\x90\x55\xc2\x42\x29\x24\x82\xb0\x50\x33\xe3\x40\x97\xe0\x1e\xe9\x4b\x91\x1b\x66\x76\x49\x3c\x1c\x86\x3b\x4f\x8a\x09\xa1\x34\x88\x60\x75\xe9\x36\xcc\xe0\x08\x76\xba\x01\xce\x14\x18\x2c\x84\x75\x46\xe4\x8d\x43\x10\x0e\x98\x2a\x86\xda\xc0\x4a\x17\xa2\xdc\x11\xa4\x70\xd0\xa8\x02\x8d\x37\xed\xd0\xac\xec\xde\x8f\x0f\xd7\x9f\x61\x8a\xd6\xa2\x81\x0f\xa8\xd0\x30\x09\xb7\x4d\x2e\x05\x87\xa9\xe0\xa8\x2c\x02\xb3\x50\xd3\x89\xad\xb0\x80\xdc\xc3\xd1\xc5\xf7\xe4\xca\xbc\x75\x05\xde\xeb\x46\x15\xcc\x09\xad\x06\x80\x82\x3c\x87\x35\x1a\x2b\xb4\x82\xdf\xf6\xa6\x5a\xc0\x01\x68\x43\x20\x3d\xe6\x28\x00\x03\xba\xa6\x7b\x7d\x60\x6a\x07\x92\xb9\xc3\xd5\x17\x10\x72\x88\xbb\x00\xa1\xbc\x99\x4a\xd7\x08\xae\x62\x8e\xa2\xde\x08\x29\x21\x47\x68\x2c\x96\x8d\x1c\x10\x5a\xde\x38\xb8\xbb\xca\x3e\xde\x7c\xce\x20\xbd\xbe\x87\xbb\x74\x36\x4b\xaf\xb3\xfb\x0b\xd8\x08\x57\xe9\xc6\x01\xae\x31\x40\x89\x55\x2d\x05\x16\xb0\x61\xc6\x30\xe5\x76\xa0\x4b\x42\xf8\x34\x99\x8d\x3f\xa6\xd7\x59\xfa\xf6\x6a\x7a\x95\xdd\x83\x36\xf0\xfe\x2a\xbb\x9e\xcc\xe7\xf0\xfe\x66\x06\x29\xdc\xa6\xb3\xec\x6a\xfc\x79\x9a\xce\xe0\xf6\xf3\xec\xf6\x66\x3e\x49\x60\x8e\xe4\x15\xd2\xfd\x1f\x73\x5e\xfa\xec\x19\x84\x02\x1d\x13\xd2\xee\x99\xb8\xd7\x0d\xd8\x4a\x37\xb2\x80\x8a\xad\x11\x0c\x72\x14\x6b\x2c\x80\x01\xd7\xf5\xee\xc5\x49\x25\x2c\x26\xb5\x5a\xf8\x98\x9f\x2d\x48\xb8\x2a\x41\x69\x37\x00\x8b\x08\xff\xac\x9c\xab\x47\xc3\xe1\x66\xb3\x49\x16\xaa\x49\xb4\x59\x0c\x65\x80\xb3\xc3\x7f\x25\x31\x61\xd6\x06\xad\x63\x0e\x33\xc3\x38\x1a\xd0\x8d\xab\x1b\x67\xc1\x36\x65\x29\xb8\x40\xe5\x40\xa8\x52\x9b\x95\xaf\x14\x70\x1a\xb8\x41\xe6\x10\x18\x48\xcd\x99\x04\xdc\x22\x6f\xbc\x2c\x30\xed\xcb\xd5\x30\x65\x19\xf7\xa7\xa5\xd1\x2b\x8a\xb5\xb1\x8e\x7e\x58\x8b\xab\x5c\x62\x01\x0b\x54\x68\x85\x85\x5c\x6a\xbe\x4c\xe2\x2f\x71\x74\xe4\x0c\xd5\x89\x8f\x30\x28\x65\xa1\x38\x36\x78\x6e\x10\xf2\x46\xc8\x42\xa8\x45\x12\x47\x7b\xf5\x11\x7c\x79\x18\xc4\x51\xc5\xec\xdc\x61\x7d\xa5\x84\xc3\x62\x04\x25\x93\x16\x07\xb1\x07\x96\x5a\x2f\x9b\x3a\xe5\x5c\x37\x3e\xa2\xff\x22\x77\xc1\x84\xad\x91\x8b\x92\x4a\x86\x75\x52\xa7\xbd\xa8\xf3\x46\xe7\xa4\x9f\xc4\xd1\x09\xcc\x08\xca\x46\xf9\x20\x7b\xac\x28\xcc\x00\x8a\xbc\xff\x25\x8e\xa2\x35\x33\x84\x05\x97\xe0\xf4\x47\xdc\x7a\x61\xff\x22\x8e\x22\x51\x42\xcf\x55\xc2\x26\x7b\xe0\x3f\x18\xe7\x7f\xc2\xe5\xe5\xa5\x6f\xf5\x52\x28\x2c\xfa\x40\x10\xd1\x53\x6a\x41\x12\xe5\x4c\x32\xc5\x71\x04\xe7\xaf\xb6\xe7\xf0\x2b\x14\x79\xb2\x40\xf7\x36\x9c\x06\x63\x89\xd3\x73\x67\x84\x5a\xf4\x5e\xff\xbd\x3f\xf0\xb7\x94\xf6\x77\xa0\x55\xbf\xd6\x9d\x72\x90\x73\x5d\x78\x71\xeb\x73\xd0\x1a\xeb\xa2\x55\x6a\xb5\xac\xd3\x86\x2d\x3c\xdf\xf4\xff\x81\xa2\x7a\x88\xa3\x87\x13\x96\xe7\x41\xe9\x19\x96\x5b\x08\x40\xe5\x4c\x57\xfd\x0b\x41\xfd\x7b\x9c\x00\x8f\xf7\xbd\x24\xcc\xf7\xae\x3c\x4a\xc2\x12\x77\x3f\xce\x04\x09\x44\xb1\xed\x04\x4b\xdc\xf5\x2f\xe2\x67\x53\x94\xb4\x4e\xff\x21\x8a\xed\x4b\xf3\xf5\xe8\xce\x09\xaf\x73\xd2\x3a\xf8\xdb\xef\x3f\xe2\x51\x28\xe1\xa8\x05\x84\x5a\xeb\x25\x8d\x70\x2c\x69\xb2\xd0\xb4\xfd\xfd\xd3\xa1\xe3\x12\xaf\xcc\xdd\x16\x2a\x66\xc1\xe9\xaf\x2b\xbb\xc8\xf4\x57\xae\x85\xca\x99\x45\xb0\xe8\xf7\x0a\xb0\xa2\x10\xa4\xcf\x24\x70\xad\x1c\x6e\x1d\x90\xbc\x00\xad\x00\x19\xaf\xa8\x5f\x39\xc2\x0a\x5d\xa5\x8b\x24\x8e\xc8\xfc\x11\xaf\xdc\x6d\x3d\xa3\x3e\xd2\xe1\x10\x16\x04\xcb\x5d\xc3\x24\x9c\x51\x77\x9f\xc1\x9a\xc9\x06\xad\x1f\x81\x74\xf0\xd5\x1d\x39\xd1\x26\xd5\x26\x71\xcb\xd2\x49\x0f\x11\x78\x42\x77\xbc\x85\x8b\xe7\x75\xf6\x78\xad\x5e\xf0\xc4\xc7\x4b\x44\x51\xa4\x64\xdd\x6f\x00\x22\x2f\xc4\xe2\xa3\x0f\x54\x92\x64\xef\xb4\xd3\xad\xcb\xfb\xf2\xcb\xb6\x49\x00\x24\x4b\x4e\x13\x2f\x74\xac\xfd\x52\xac\x3c\x88\x05\x26\x37\x6c\x17\x4c\xe5\x3b\x2f\x9f\xfc\xfe\xc9\x5b\x20\xf5\x5d\x8d\x97\xe3\xd9\x24\xcd\x26\x01\xe9\x60\x55\xe1\x46\xee\xda\xa9\x59\xf8\x04\x18\xc6\x1d\x25\xc5\xa0\xb5\x6d\xcd\x91\xe1\x10\xcd\x2f\xcf\x55\xd7\xb7\x9c\xf8\x0b\x1d\x71\x47\xf5\x63\xd0\x36\xf2\xa4\x82\x36\x15\xf5\x97\x94\x21\xae\x9a\xba\xdd\x86\x7d\x94\x23\x2a\x10\x0e\x8d\x77\x4f\xaf\xd1\xf8\x98\x0c\xba\xc6\x28\xdb\xb5\x61\x29\xa8\x7c\x5a\xe0\x96\x36\x0a\x24\x0c\xe2\x70\xfe\x5c\xcd\xec\x03\x24\x92\xe0\xf2\x12\xce\x03\x51\xe7\x6d\x74\xc3\x21\xdc\xa1\x7f\x14\xe5\x52\xe4\x85\xdc\x41\x81\x12\x9d\xdf\xbd\x07\xbe\xf6\xcd\x35\xa0\xd7\x0d\x75\x02\x6e\x85\x75\x42\x2d\x20\x8c\x87\x0d\xad\xd8\x16\xce\x07\xc6\x59\x43\x35\xfe\x78\x1f\x39\x4d\x8f\x0b\x83\x34\x4c\x68\xe8\x7b\x8e\x98\x14\xdd\x63\xa4\x14\xc6\x3a\xa8\x25\xe3\x48\x55\x11\x75\xce\x1c\x77\x78\xe8\xe6\x50\x2e\xfd\x3f\x5b\xfa\xc9\xf4\xcc\xf3\x16\xca\xad\xdb\x75\x4c\xd2\xae\x24\xf3\x16\x7a\x7b\x8c\x7e\x1c\x45\x66\xaf\x7d\x84\x7d\x71\xc8\xa3\x75\x58\x1f\x67\x91\x4a\x1c\xd7\x48\x73\xd3\xa7\x30\xac\x45\xb2\xd5\x8d\x05\xa4\x46\xa3\x7b\x47\xc9\x90\x7a\x71\x9a\x8c\x5f\xbc\xc1\x93\x75\x79\x5c\x69\x27\x02\x1a\x5d\xa6\x41\xdf\x72\x4f\x15\xa2\xd4\x8b\x64\x9f\x24\x9a\x6d\x69\xa8\xeb\x5e\xff\xa8\x30\x03\x35\x77\x15\x2a\xf2\x9e\x3a\x02\xba\x35\xcf\x38\xa7\x67\x4f\x31\xa0\x8e\xa0\xb6\x79\xb4\x7c\xe3\x28\xb2\x1b\xe1\x78\x05\xde\x94\xae\x0f\xcb\xad\xdf\x3a\xcd\x69\xce\x9c\x4d\xfe\x9d\x8d\x6f\xde\x4d\xc6\x37\xb7\xf7\x67\x23\x38\x39\x9b\x5f\xfd\x67\xd2\x9d\xbd\x4d\xa7\xe9\xf5\x78\x72\x36\xf2\x1b\xed\x89\x88\x9c\xde\xc7\x40\x06\xad\x63\x7c\x99\xd4\x88\xcb\xde\xab\xd3\xc5\x7a\x88\x30\x8a\x72\x83\x6c\x79\x71\x70\x26\x54\x78\x6b\x83\xb6\x8d\x7f\x06\x5d\xc2\xb3\x6c\x5d\x3c\xef\xcd\xb8\xd5\xef\xed\xe7\xe4\x61\x81\xd3\xc9\x0b\xfc\x78\xf3\xd3\x8e\xf8\xe2\x63\x7c\x39\x02\xcb\x24\xbd\x26\xc5\xff\xe8\x2b\xa0\x2c\x2d\xba\x01\xa0\x2a\xf4\x66\x85\xca\x75\xa8\x41\xd2\xe2\x1e\x51\xf6\xba\x9f\xf8\x49\x7b\x53\xf6\xfa\x9d\x32\x81\x7d\xab\xfa\xe6\x29\x55\x54\x54\x80\x2d\xfa\xaf\xfe\xe6\x8f\x89\x7a\xd3\x32\xf5\xc8\xc0\x6f\x8f\xde\x45\x5e\xbe\xc2\x95\x36\xbb\xc4\xd2\xb3\xb8\x77\x14\xdf\xf7\x59\x4d\xa7\xd3\xae\x9e\xe8\x0f\x15\x59\x77\xf0\x6e\x32\x9d\x7c\x48\xb3\xc9\x89\xd6\x3c\x4b\xb3\xab\x71\x38\xfa\xe9\xc2\x7b\xfd\xe2\xc2\x3b\x9f\xcf\xb3\x9b\xd9\xe4\x7c\xd4\xfe\x9b\xde\xa4\xef\xce\xbf\x31\xd8\xbe\x9d\xbe\xd7\xbb\x4e\xdf\x69\x53\xfc\x95\x0e\x38\xda\x43\x25\x7b\x6a\x0d\x1d\xad\xe2\xe3\x8f\x07\x60\x6a\x3f\xd6\xca\xf0\x01\x15\xf9\xfb\x4f\x0e\xb2\x87\xf8\x21\xfe\x7f\x00\x00\x00\xff\xff\x4c\xd8\x95\x0a\xd6\x0f\x00\x00") func prestate_tracerJsBytes() ([]byte, error) { return bindataRead( @@ -247,7 +247,7 @@ func prestate_tracerJs() (*asset, error) { return a, nil } -var _state_diff_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x3c\x6b\x73\x1b\x37\x92\x9f\xc9\x5f\x01\xab\xae\x64\x72\x97\xe6\x43\xa2\x24\x8a\x5a\xed\x96\x22\xcb\x89\xee\x6c\xcb\x25\xd1\xce\x65\x5d\xbe\x14\x66\xa6\x87\xc4\x6a\x08\xb0\x00\x8c\x28\x26\xf2\x7f\xbf\xea\x06\xe6\x45\x0e\x25\x79\x93\xdc\xd5\x3d\x3e\x59\xc4\x34\x1a\x8d\x46\xbf\xd1\x70\xaf\xc7\xce\xd5\x62\xa5\xc5\x74\x66\xd9\x5e\x7f\x70\xc4\x26\x33\x60\x53\xf5\x0a\xec\x0c\x34\xa4\x73\x76\x96\xda\x99\xd2\xa6\xd9\xeb\xb1\xc9\x4c\x18\x16\x8b\x04\x98\x30\x6c\xc1\xb5\x65\x2a\x66\x76\x0d\x3e\x11\x81\xe6\x7a\xd5\x6d\xf6\x7a\x6e\x4e\xed\x67\xc4\x10\x6b\x00\x66\x54\x6c\x97\x5c\xc3\x98\xad\x54\xca\x42\x2e\x99\x86\x48\x18\xab\x45\x90\x5a\x60\xc2\x32\x2e\xa3\x9e\xd2\x6c\xae\x22\x11\xaf\x10\xa5\xb0\x2c\x95\x11\x68\x5a\xda\x82\x9e\x9b\x8c\x8e\xef\xdf\x7f\x64\x6f\xc1\x18\xd0\xec\x7b\x90\xa0\x79\xc2\x3e\xa4\x41\x22\x42\xf6\x56\x84\x20\x0d\x30\x6e\xd8\x02\x47\xcc\x0c\x22\x16\x10\x3a\x9c\xf8\x06\x49\xb9\xf1\xa4\xb0\x37\x2a\x95\x11\xb7\x42\xc9\x0e\x03\x81\x94\xb3\x3b\xd0\x46\x28\xc9\xf6\xb3\xa5\x3c\xc2\x0e\x53\x1a\x91\xb4\xb8\xc5\x0d\x68\xa6\x16\x38\xaf\xcd\xb8\x5c\xb1\x84\xdb\x62\xea\x33\x18\x52\xec\x3b\x62\x42\xd2\x32\x33\xb5\x00\x66\x67\xdc\xe2\xae\x97\x22\x49\x58\x00\x2c\x35\x10\xa7\x49\x07\xb1\x05\xa9\x65\x3f\x5e\x4e\x7e\xb8\xfa\x38\x61\x67\xef\x7f\x62\x3f\x9e\x5d\x5f\x9f\xbd\x9f\xfc\x74\xc2\x96\xc2\xce\x54\x6a\x19\xdc\x81\x43\x25\xe6\x8b\x44\x40\xc4\x96\x5c\x6b\x2e\xed\x8a\xa9\x18\x31\xbc\xbb\xb8\x3e\xff\xe1\xec\xfd\xe4\xec\xbb\xcb\xb7\x97\x93\x9f\x98\xd2\xec\xcd\xe5\xe4\xfd\xc5\xcd\x0d\x7b\x73\x75\xcd\xce\xd8\x87\xb3\xeb\xc9\xe5\xf9\xc7\xb7\x67\xd7\xec\xc3\xc7\xeb\x0f\x57\x37\x17\x5d\x76\x03\x48\x15\xe0\xfc\xa7\x79\x1e\xd3\xe9\x69\x60\x11\x58\x2e\x12\x93\x71\xe2\x27\x95\x32\x33\x53\x69\x12\xb1\x19\xbf\x03\xa6\x21\x04\x71\x07\x11\xe3\x2c\x54\x8b\xd5\xb3\x0f\x15\x71\xf1\x44\xc9\x29\xed\x79\xab\x40\xb2\xcb\x98\x49\x65\x3b\xcc\x00\xb0\xbf\xcc\xac\x5d\x8c\x7b\xbd\xe5\x72\xd9\x9d\xca\xb4\xab\xf4\xb4\x97\x38\x74\xa6\xf7\xd7\x6e\x13\x71\x1a\xcb\x2d\xbc\x16\x71\x3c\xd1\x3c\x04\xcd\x54\x6a\x17\xa9\x35\xcc\xa4\x71\x2c\x42\x01\xd2\x32\x21\x63\xa5\xe7\x24\x2a\xcc\x2a\x16\x6a\xe0\x16\x18\x67\x89\x0a\x79\xc2\xe0\x1e\xc2\x94\xbe\x39\x56\x93\xbc\x6a\x2e\x0d\x0f\x69\x34\xd6\x6a\x8e\x9b\x4d\x8d\xc5\x3f\x8c\x81\x79\x90\x40\xc4\xa6\x20\xc1\x08\x33\x61\x41\xa2\xc2\xdb\x6e\xf3\xd7\x66\xa3\x4c\x0e\x8a\x0a\x6d\x32\x03\x23\xf9\x58\xc2\x4b\x0d\x2c\x48\x45\x12\x09\x39\xed\x36\x1b\x39\xfc\x98\xfd\xfa\xb5\xd3\x6c\x36\x66\xdc\x5c\x4a\x61\xcf\x79\x92\x40\x34\x66\x31\x4f\x0c\x74\x9a\x8d\x84\x1b\x7b\x16\x86\xc8\xe1\xe8\x2c\x0c\x55\x2a\xed\x98\xc9\x34\x49\xfc\xb7\x6b\x88\x53\x19\x8d\x59\xbf\x43\x28\x2e\xb4\x56\x3a\x9f\xdd\x6c\x44\x22\x8e\xdf\x71\x7d\x0b\xda\x8c\xd9\xaf\xcd\x46\xe3\x1d\xcc\x95\x5e\x8d\xd9\xce\xcf\x3b\x1d\xa4\xdb\xc2\x7c\xe1\x88\x47\xc1\x8d\xd8\x72\x86\x16\x44\xa7\x52\x0a\x39\xcd\x78\x12\x82\xee\x38\xf9\x96\x70\x07\x1a\xa5\x5c\x83\x4d\xb5\x84\x08\x19\x8b\x50\xa9\x01\xdd\x6c\x34\xbe\x53\x5a\x8e\xd9\xce\x9f\x77\x3a\xcd\x46\xe3\xb5\xc0\x9d\xec\xbc\xa2\x1f\xe7\x33\x2e\xa7\xf4\xfb\x4f\xf4\xfb\x86\xcf\x61\xcc\x76\x4e\xf1\x07\x31\x40\x98\xab\xe0\x1f\x10\xda\x8b\xf9\xc2\xae\xc6\x2c\x4e\x25\x9d\x43\x4b\x05\xff\x68\x13\xe9\x28\xa9\xad\x3b\xae\xd9\x3d\xea\x9f\x1b\xf6\x74\xb8\x0d\x9f\xb0\xaf\xcd\x46\xc3\x8f\x58\x9d\xc2\x89\x47\x6d\xd5\x0f\x70\xff\xaf\xa6\x84\xf4\x8e\x27\x0e\x69\x06\xbd\x5a\x80\x8a\xd9\x1d\x4f\xd8\x8b\xd3\x53\xb6\x83\xaa\x2e\xa7\x3b\xec\xe1\x01\xc7\xba\x42\x46\x70\x7f\x15\xb7\x76\xfa\xf7\x3b\x6d\x82\xe8\xb3\xbf\x31\xfc\xc5\xfe\x4c\x00\x56\xdd\xd0\x8c\xd6\xe0\xb0\xcd\xc6\x38\x94\xad\xcd\xdd\xa1\xe1\xd9\x96\xd6\xe7\x61\xd8\xa1\x45\x1d\x15\x22\x66\x2d\x3b\x13\xa6\x9b\x8b\xc5\x67\x1e\x86\x5f\xd8\xe9\xe9\x29\x59\xd3\x58\x48\x88\x1c\x68\x03\x39\x30\xa7\x53\x74\x07\xcb\x4e\x19\x4d\x2d\x1d\x75\xd7\x9d\xf2\x49\x13\xe1\x6b\xf1\x3a\x54\x8d\x9f\x91\x84\x31\x11\x82\x5b\xdd\xc0\xe3\x0f\xad\xc3\x0a\x49\x51\x9a\x4f\x01\xb5\xdf\x6f\xec\xa5\x61\x42\x0a\x2b\x78\x42\x68\x1c\x5a\x0d\x73\x75\x57\x88\x31\x4e\xf7\x43\x4e\xad\x88\x1c\x07\x0a\x65\x89\x45\x40\x1a\x28\xe4\x2b\x58\x91\x7c\x7d\x7a\xe7\xc0\x63\x21\x79\x52\x06\x37\x56\x2d\x58\xba\x40\x8f\x20\xa7\x5e\x92\x45\x99\x3a\x37\x34\xc7\x1d\x45\xe8\x61\x08\x03\x21\x0b\x78\xc2\x65\x08\x63\xcf\x8c\xc6\xe7\x32\x5b\xbf\xa0\x62\xd2\xf8\xd7\x0e\xfd\x23\xd5\xf3\x61\x43\x15\x3d\x17\xd4\x73\x34\x1b\xfe\x7a\xd2\x6c\x34\xbe\x7a\xd9\xe9\xf5\x58\xa2\xd4\x6d\xba\xf0\x9a\xcf\x84\x44\x0d\x71\x06\xc6\x2c\x20\x14\x31\xfa\x0c\x9e\x7f\xf5\xca\x58\x18\x23\x45\x2a\xd5\x6c\x54\xd0\x94\xe5\x30\x8a\x74\x87\x45\x41\x59\x18\x51\xc0\x78\x18\xa2\x5c\xa1\xde\x10\x4c\x1b\xc9\x22\xf1\xa8\x31\x48\xec\x14\xe1\x49\xda\x7a\x3d\x26\x15\x93\xe0\x0c\x43\x0c\x36\x9c\xb9\xc3\x01\xd3\x41\xe6\x23\x75\x39\xb9\xa6\xe6\x54\x7a\x3d\x34\x41\xe1\x8c\xcd\x81\x4b\xe3\x8c\xa7\x51\x73\x3c\x40\x99\xf2\x84\x85\x3c\x09\xd3\x84\x6c\xba\x71\x8e\x29\x00\x90\x6c\x01\x1a\x6d\x3d\x8a\x6a\x6a\x52\x9e\x24\x2b\xb4\x11\x1a\x4c\x9a\x58\xd3\x6a\x3b\xc4\xef\xaf\x26\x17\x63\x74\xd5\x4e\x12\xb9\x75\xf6\x4d\x2d\x50\x9c\xe3\xd4\x99\x7d\x34\x2d\x1a\xe3\x97\x25\xb0\x48\xc9\x97\x96\x69\xe0\x65\xb1\x8d\x02\xb6\x9c\x81\x44\x6f\x45\x1b\x85\xe8\x11\x05\x7e\x51\x56\x60\xb6\xbb\xcb\x6a\x80\xba\x4e\xa8\xbd\x7a\x3b\xc1\x77\x52\xe0\x0f\xc3\xcb\x29\x3b\x65\x51\xd0\x9d\x82\xfd\xce\xfd\x2e\x0e\x06\x81\x50\xe4\xf2\x23\x73\x70\xe7\x2a\xf2\x40\x39\x14\x09\x71\x8e\xe8\xbd\x2a\xa1\x69\x7e\xb3\x69\xa1\x6f\x25\xfb\x56\xb2\x6a\x39\x3a\xff\xf9\x35\xb7\x3c\xc3\x56\xdd\x7d\x26\x36\xc8\x41\xbf\xd1\x07\xa2\xf2\x01\x77\xd4\xee\x12\xe3\x85\x21\x76\xc7\x02\x9d\x63\x07\x8f\x4d\x12\xae\xcc\xd9\xc6\x42\x1b\xcb\xac\x98\xc3\xba\xc2\x18\x94\x98\xe4\xb1\x33\xea\xfa\x55\xab\x6a\xea\xd6\xad\xb3\xbf\xa5\x1d\x3d\x3a\x35\x3b\xb6\x93\xf5\x49\xb4\xbb\xfa\x29\xf4\x69\x63\x02\x32\xa2\x1e\x1e\xbf\xe4\xa2\xd2\xeb\x61\x30\x17\x82\x33\xe7\x21\x99\xee\x6c\xdb\x38\xf2\xa2\x66\x33\x8e\xf1\x5c\xe6\x4a\x39\xe3\xc6\x69\x14\x7a\x72\x0a\xd9\x84\x67\xb5\x4e\x25\x46\xfb\x8c\x27\x46\x31\x74\xed\x1d\x8f\x80\xbe\x3a\x75\xcd\xc2\xe0\x3c\x4c\x58\x60\x78\x6d\x6c\x11\x27\xe4\x96\x1f\xc9\x2a\x6f\x92\x5c\x11\x31\x7c\x43\xe4\x88\x14\xd4\x9d\xad\x10\x48\x8e\xdf\x51\x85\x73\x99\x23\x42\xd1\x73\xf1\x80\xe7\x55\xa3\x66\x69\xc2\x9f\x73\xb3\x24\x8f\xcf\x3e\x78\xab\xaa\xc7\xfe\xb5\x90\x6d\xe7\x85\x22\x05\x06\x4d\x0a\x19\xae\x92\x2d\xec\xb0\xb9\x32\x96\x2d\xb4\x0a\x78\x90\xac\x58\x00\x21\x4f\x0d\xb9\xd9\x8b\xcb\x0f\xaf\x06\x87\x03\x2f\xf4\x6e\x3f\x4c\xd8\xd2\xc1\xaa\x38\x53\x6b\x8c\x5c\x64\x3a\x0f\x40\xef\x20\xbf\xdc\xe8\x5f\xd8\x73\x84\xcf\xef\x31\x82\x04\x2c\xd4\xeb\x69\xd5\x38\x31\x48\x0c\x79\xda\x16\xa1\xac\xe1\x51\xdd\x52\xc4\xa1\x5c\xca\x73\x46\x93\xaa\x6f\x62\xa8\x11\x7c\x42\x50\x88\xfd\x9a\x9f\xbc\xf1\xe1\x49\xbd\x9f\xcc\x82\x17\x90\x56\xe7\x09\xcc\x54\x60\x0a\x56\x76\xa1\xcd\x86\xcf\x06\xb6\xb9\xd1\x9b\xcc\x65\xaf\xb9\xd1\x5b\x58\x75\x30\xf4\x43\x87\xda\xfe\xad\x9e\xb4\xce\xb8\x7e\xab\x59\xfd\x16\x7b\xee\xa0\x45\x74\x9f\x53\x7b\x0b\x2b\xb7\xe0\xba\xaa\x7a\x3e\x7e\x16\xd1\xfd\xf6\x10\x55\x63\x2c\x5d\xf5\x47\x37\x48\x61\xc1\x2b\xe7\x95\x1a\xdb\x31\x7b\x85\xde\x88\x9f\x5c\x58\xd5\xeb\xb9\xf0\x41\xb3\x5b\x80\x05\x06\x7f\xa4\x66\x5e\x00\xa2\x7b\x36\xe3\x11\x8b\x90\x41\xdc\xb2\x04\xb8\xb1\x0c\xe5\xce\x4d\xfe\x39\xcc\xd2\x91\x17\xad\xde\x7f\xb4\xfa\xf7\xed\xbf\xf5\xff\xf4\x2f\xbd\xae\x05\x63\x5b\x48\x7a\xbb\xed\xc2\xb3\x06\x2a\xc7\x98\x76\xe3\xa2\xb6\x52\x90\xe6\x59\x93\xe7\x12\xb4\x6f\x53\xde\x37\x7e\x7a\x74\x93\x75\xd2\x6d\x28\x7b\xc8\x6c\xe4\x8b\x67\x4e\xcd\x76\x84\x8a\x5f\xb3\x27\x43\x7b\xaa\xb3\x91\xcf\xc1\x59\x31\x9f\xb9\xf2\xff\x7e\x67\x5d\xc7\x06\xfd\x47\xb0\x41\xff\xae\x6c\x28\xcc\x8f\x2b\x34\xdc\x08\x39\x4d\x7c\x22\x8d\xe1\x2f\x0d\xfa\xca\x04\x15\x5a\xc8\x93\xa1\x13\x45\x87\xe5\xc5\xd7\x34\x1b\xe5\xd9\x25\xab\x82\xc2\x5b\x0e\xcc\x4b\x6e\xea\xb1\x6c\xed\x9f\x54\x7d\x77\x86\xb8\xe6\xe6\x69\x3c\x3c\xd4\x7d\x40\xd5\x38\x29\x22\x8f\xca\xa6\x3a\x18\x3f\x2f\xb9\xb4\xc8\x08\xcf\x01\xb4\xa9\x3b\x38\x69\x07\x17\x4b\x2b\xc1\xc9\x53\x6e\x7d\x2b\x75\x88\xaf\xe4\x6a\xb3\xa8\x10\xfd\x69\xb2\x22\xb2\xbc\x43\x36\xc4\x78\xf2\x3d\x48\xdd\x0c\x34\x20\x8d\x21\xcf\x1d\xb2\x84\x29\xb7\xe2\x0e\x1c\x75\xc6\xfb\x6e\xcb\x96\x45\x15\x8c\x42\xa3\x50\x49\x23\x22\xd0\xae\x10\x88\xbf\x40\x9a\x14\x23\xa4\x04\x51\xdb\x99\x4a\xa7\x33\x5a\x9a\x6a\x27\x3f\x63\x04\xea\x53\x1b\x60\x81\x06\x7e\x4b\xac\x48\xd4\x54\x84\x4c\x49\xf6\xe9\xdd\x4b\xc3\xce\xb9\x9c\x68\x2e\x4d\x0c\x9a\xed\xb2\xec\xcf\x2c\x10\x11\xd3\x4b\x69\xbb\xc2\x5c\x4a\x63\x29\xf6\x27\xa3\xb3\xbb\xeb\xaa\x13\xe6\xbd\xa7\xbc\xd5\xae\xb0\xcb\x4f\xfb\x05\xb4\xca\x2d\x96\x2f\x78\x10\xd4\x67\x64\xfd\x97\xb1\x63\xbc\x2f\x92\x10\xe6\x4e\x9d\x74\x7b\xf1\x7a\x44\xbc\x33\x08\x27\x03\x9d\x66\x91\xcd\xcd\xb8\x8c\x12\x70\x0e\xf9\x86\xcf\x7d\x3e\x4e\xfe\x16\xb4\xc4\x7c\x2d\xd3\x82\xbc\x4e\xf4\xdf\xa4\x06\x3e\xb2\xde\x2a\xee\x0e\x8a\x2c\x54\xad\xae\x9c\xfc\x1f\x13\x44\x17\x3c\xee\xee\x12\xe3\xea\x44\x31\xcb\x86\xaa\xb2\xd8\xd8\xc2\x60\x76\x4a\x88\x4e\x58\xaf\xe7\x04\x0d\xa9\x0f\x67\x10\xde\x5e\xc6\x28\x38\x95\xf0\x7c\x83\x18\xab\x88\x14\xab\xea\x08\x71\xd1\xf9\xd3\x64\x10\x9c\x55\xcf\x22\x81\x84\xa9\xf4\xad\x2c\xac\x95\x84\x7e\x53\xec\x10\x7c\x8b\x56\x6e\x91\xe8\x3c\xf6\x71\x31\x49\x45\x6b\xe9\x18\x5c\xc8\x62\xd5\xda\x37\xab\xdc\x97\x35\x9f\x55\x22\xdb\xfd\x8d\x01\x54\xa0\xec\x2c\x33\xd2\x28\xa6\x3b\x56\xed\x14\xe9\x8a\x41\xd8\x4c\x28\x4b\xf3\xff\x67\x6b\xab\x67\x7e\x0b\xc1\x2a\xf5\x9a\x87\x07\x64\xdb\x93\x42\x1f\xaa\xf9\x82\x6b\x27\x7d\x88\xa0\xdf\xf6\x33\xf3\x1a\x82\x55\xed\x93\x82\xf1\x33\x6e\x7c\xbc\xef\x36\x6f\x1c\xfb\x0d\xf2\xbf\x5c\x20\xc3\x5c\x9c\xcb\x95\x4f\xe5\x0d\xcd\xc5\x33\xc1\x71\xab\x30\xbd\xe6\x51\xe4\x6c\x71\x1e\xfc\xbf\x34\xde\x20\xd3\x75\x40\x75\x99\xb5\x53\x2a\xaa\x7d\x78\xac\xdb\x39\xee\x04\xb5\xe0\xd3\x0b\x9a\xdd\xcd\xab\x52\xa7\xa7\x25\x04\x24\x81\xbb\xbb\xc4\xed\x6e\x91\x96\x6e\x01\x70\x45\xab\xda\xef\x44\x46\xe5\x4e\xc0\x2d\xeb\xe3\xb4\x76\xce\x50\xe7\x36\xca\x7b\x0b\xd6\xee\x0b\x30\x05\xf3\x55\x8c\x22\x4b\x2a\x85\xec\x9e\xdf\x67\x51\xa4\xc1\x18\x52\x7e\xff\xb7\x4f\xba\x5c\xb6\xe1\xaa\x99\x09\xc7\x40\x32\xb3\xe5\x79\x81\xbd\x52\x76\x6a\x55\x31\x52\x3e\xe8\x42\xd9\x6f\x93\x70\x57\xd8\x73\x0a\xb2\x1d\xbe\xac\x41\xdf\x90\x1c\x36\xf2\xa2\x7c\x36\xc1\xb8\x2b\x3a\x2a\xc0\x1b\xa6\x74\x56\x9e\x8d\xa9\x1e\x3f\x57\x18\x6b\xb0\xd7\xca\xb9\x36\x67\x31\x30\x87\x65\x2d\xa9\xd0\x93\x33\x2e\x15\x5d\xc7\xce\xc1\xce\x54\xd4\xce\xea\x43\x34\xb8\x14\xa6\x10\xec\x39\x5d\x68\xd3\x2d\xd2\x2b\x27\xc2\x64\x62\xed\x7d\xf7\x33\xaa\xcc\x83\x55\x0f\xa1\x12\x32\xe0\x06\xbe\x38\x15\x2f\x15\xa2\xba\xae\xaa\x4b\x45\xdd\xcd\x2a\x92\xbb\x3d\x78\x78\x60\x75\x65\xa0\x2c\xee\x7f\xa2\xca\xd1\x08\x95\xb4\x42\x56\xea\x45\x99\xbd\x44\x1d\x15\x54\x57\xe4\x4c\xc2\x92\x05\x8a\x6e\x29\xfc\x6a\x1e\x72\xc6\x17\x0b\x90\x06\x5d\xea\x5c\xe9\x48\xe9\x97\x86\xd9\xfb\x31\xeb\xdf\x87\xc3\xc3\xd1\xd1\x41\x3f\x88\x8e\xa2\xa3\xfd\xf8\x60\x3f\x8e\xf7\xe3\x28\x3c\x1a\xee\xf5\x07\x7b\xc3\x83\xe3\x41\x3f\x1a\x0d\xa3\xd1\x30\x88\xc3\x83\x70\xd8\x8f\x8e\x8f\x60\x18\x1e\xf0\xd1\xf1\xde\x28\x3c\xde\x1b\x8c\x8e\x1e\xaf\x9c\x55\x44\xc5\xa5\xaf\xbb\xbb\xec\xb9\x05\xcb\x53\xd6\xaf\x9d\xb3\xb5\xfc\x78\x7a\x4a\x57\x60\xb5\x93\xb6\x17\x39\xc9\x3e\xd6\x94\xea\x32\x47\x51\x57\xfd\x2b\x1d\x04\xd9\x73\x07\xba\x31\xfd\xa4\x54\xc2\x7a\xfe\x47\x2f\x1c\x5b\xbf\x93\x48\x6d\xfd\x4a\xb5\xfb\x22\x4f\xad\x3f\x8b\xba\x0d\x17\x55\x7d\xda\x72\x25\xfa\x6d\xd5\x00\xe6\x95\xf5\x75\x44\x59\x4d\xff\x09\x34\x04\xb6\x15\x49\x76\x7d\xf0\x38\x0e\x84\x2a\xa3\x28\x17\x02\x9e\xde\x9c\x4b\x70\x7f\xeb\xde\x6a\xb0\x7c\xeb\xd6\x6a\x50\x6c\xec\x2c\x3f\xd1\x28\xe8\x02\xb9\x9f\xaa\x57\x68\xff\x36\x63\xa2\x16\x56\xcc\x85\xa1\x8b\xac\x31\x5b\x68\x78\x95\x9b\x97\x25\xb8\x80\xcb\xbb\x7d\x16\x40\xac\x34\xb0\x05\xd7\xc6\x5d\x71\xba\x12\x57\xa5\x92\xfe\x82\x96\xdf\xf0\xf8\xe5\x2d\xfe\x46\x82\x5d\x06\x57\xa9\xa1\x0a\x4a\x4d\x0a\x3f\x2b\x22\xba\x99\xaf\xa9\xaa\x64\x4b\xaf\x7b\x51\x0a\x25\x71\x8e\x87\x73\xf1\x6b\xe1\x4f\x7d\xa5\x75\xc3\x9f\x5a\xf5\xa3\xd2\x51\x4b\x44\xf7\xed\x8e\xeb\x86\x28\x5c\xac\x8b\x68\xac\x58\x33\x10\xe5\xfa\x8e\xdb\x27\xf2\xcd\x58\x51\x35\x52\xd5\x08\xd0\x99\xb6\xc6\xc3\x03\xdb\x00\xec\xae\x47\x8b\x05\xec\x8b\x4d\xe0\xac\x84\x94\x31\xe2\x91\x43\xa8\xa1\xb4\x7a\x24\xfe\x4c\x9e\x61\x6e\x5c\x71\x0e\x4c\xbd\x62\x1b\x2b\x2a\x3a\x43\x08\x09\xba\xae\x16\x83\x51\x60\x8e\xf7\x1b\xc9\xaf\x98\x89\x47\x2b\xbe\x1a\x4c\x36\xa7\xb9\x39\x93\xca\x09\x20\x5d\xa1\x09\x23\x61\x1f\x2d\xa0\x3b\xc6\x08\xb2\x7a\x49\xe2\x4a\xf8\xe5\x92\x3f\x39\xa0\x2c\x3c\x6e\x3c\xe5\xab\xfb\xe1\xc1\x71\x14\xc5\x23\x08\x02\xce\x0f\x87\x83\x61\x3f\x0a\x0e\xf7\x06\xc3\x20\xe0\xd1\xe1\x70\x10\xc7\xf1\x61\x10\xf4\x0f\x0f\x47\xc3\xa3\x28\x80\x78\x7f\xb8\xbf\x1f\x0d\xf7\x87\x41\x34\x88\x83\xc3\xbd\xa3\x7e\xc1\xd4\xc7\x2b\x5c\xf9\xb5\x70\x39\x8f\x2c\x0e\xe7\x8f\x67\xfa\xa6\x65\xdc\x10\x8d\xaf\xa5\xe2\xf7\x5a\xf0\x98\xca\xac\x3c\x9a\x85\x91\xff\x05\x46\xa9\x92\xc8\x0a\x19\x26\x69\x04\x57\x0b\xea\x7e\xca\x92\x29\x34\x4b\xfe\xee\x27\x64\x9f\xde\xb1\xab\x0f\x3e\xb2\x6d\x36\xaa\x13\x4a\xa9\x03\x68\x5d\x69\x0b\x02\x4d\x61\xd4\xee\x2e\xfe\x95\x77\x01\xbd\x44\x62\x34\x0f\x2d\xe6\x5f\x94\x32\x84\x2a\x49\x84\x11\x4a\xbe\x6c\xb3\xbf\xb2\x57\x83\x2c\x65\xd8\xdd\x65\xad\xca\x4c\x21\x4b\xfd\x69\x99\x93\xf4\x05\x1a\xaa\xb3\x78\x04\xe5\xcd\xb9\x70\x53\xc8\x3b\x85\xc1\xb8\xf7\x06\x98\x17\x7e\x7a\x57\x34\xb1\x75\x5d\x4a\x6f\xef\x7d\x76\xf8\x30\x37\xd3\x49\x11\x46\x33\x03\xd6\xdd\xde\x46\x91\x40\x78\x9e\x30\xdc\x05\xdc\x23\x19\x06\x22\x94\x7f\xe0\xe1\xcc\x55\x8a\x7c\x08\xdf\x45\x56\x55\xfa\x96\x42\x7b\x4f\xa6\xd6\xa5\xf6\xfe\x78\x8b\xbe\xb5\xbc\x42\xee\xaa\x4c\x53\x5c\x34\xb4\x29\x4f\x2a\x75\x5f\x77\x36\xeb\x91\x7e\x2e\x3e\xdd\x66\x6d\x4e\x85\xe9\x01\xce\xf1\xa6\x7e\x2b\x4c\x86\xaf\x70\x09\xbd\x1e\x23\x6e\x90\x99\x00\x57\xaa\xa4\x9e\x4b\x64\xad\xdb\x29\xf1\xc6\x31\xda\x65\xe0\x8e\x68\xab\x3c\xc9\xd9\x6d\xe1\xe4\xbe\xeb\x10\xe2\x4a\x56\x21\xd7\x70\xd8\xe5\x3d\x33\x42\x62\x18\x4f\x96\x7c\xe5\x96\xf2\xdd\x4b\x17\x9f\xde\xd1\x0a\xca\x75\x9a\x9d\x9e\x5f\x5f\x9c\x4d\x2e\xf2\xba\x9e\x5f\x55\xc2\x32\x59\xf9\x36\xc5\x88\xad\x0b\x59\x76\x55\x6a\xef\xbb\x6e\x37\x75\xf7\xf9\x5b\x78\x42\x13\x72\xc6\x95\x54\xc7\x58\x58\x94\xa5\x0b\x59\x03\x77\xa0\x57\x4c\x2d\x28\x6e\xa2\x06\x1c\xd7\x80\xe5\x85\x0d\x9d\x3e\x4e\x2b\x49\x45\xa2\xa6\x85\x54\xf4\x7a\xec\x9c\x2f\x6c\xea\x85\xd4\x67\x93\x62\x3e\x87\x48\x70\x0b\xc9\xca\x17\x1d\x5c\xa6\x76\xca\x12\x35\xed\x4e\xc1\x92\x2a\xb6\xf2\x46\x15\xe5\x95\x39\xff\x8e\x02\x56\x82\xa9\xd8\x17\x07\xea\x95\x4d\xe9\xb5\xae\x9b\x87\x87\x1c\x5b\x95\x65\x65\x9e\xe5\x58\x8a\x3b\x9e\xbc\xc8\xb7\x0d\xab\x2b\x51\x54\x6c\x49\xcb\xaf\xd4\x6e\x57\xea\x18\x75\x57\xbd\x88\x0e\x43\x97\x0a\x60\x61\xfa\xb6\xcd\xfb\xb2\xfd\xdc\x9f\x31\x39\x4b\x90\xfd\x2e\x19\xf3\xb7\xa8\x79\x6a\x4e\xe7\x3d\xe3\xa8\x10\xee\x84\x0a\xa9\xaa\xbd\xae\xc6\x1d\xac\xb7\x06\x34\xcb\x37\xdc\xae\x41\xb5\x38\x47\xf7\xbb\x95\x6b\x26\x3a\x74\xea\x16\xc1\x6c\xda\xb7\xef\x19\x24\x87\x16\xea\xa0\xf4\xa3\x8a\xac\x77\xb6\x35\x1b\x0d\xb3\x14\x18\x4a\xa2\xf4\x75\xd5\xa2\x68\xc1\xcc\xce\x35\x44\xa3\xb2\x73\xf1\xef\x93\xf3\xab\xd7\x17\xe7\x57\x1f\x7e\xda\x19\xb3\xca\xd8\xcd\xe5\xdf\x2f\xf2\xb1\xef\xce\xde\x9e\xbd\x3f\xbf\xd8\x19\xaf\xc7\xa0\x99\x1e\x15\xe1\x3f\x2e\x68\x2c\x0f\x6f\xbb\x0b\x80\xdb\x56\xbf\x5d\x69\xff\x6c\xe7\x7a\xd6\x68\x50\x8d\xfd\xa4\x20\xc6\xe9\xbd\x5f\x83\x4a\x35\x79\xcd\x09\x91\x66\x2a\x8f\x7c\xca\x16\xf3\x98\x6a\x09\x3a\xf7\xf0\x2d\x5e\x14\x9a\xaa\x7d\x5d\x60\x8c\x23\xa8\x53\x9f\x58\x3f\x4a\xe7\x9e\x27\xd4\xb5\x37\x87\xb7\x63\x66\x78\x62\x3b\xcc\x88\x5f\xa0\xc3\x54\x1c\x1b\xb0\x1d\x06\x32\x52\xcb\x39\xb8\xe2\x87\xd3\x5d\xfa\xe2\xf7\x54\x62\xd4\xa0\xdd\x25\x63\x7a\x15\xbb\xe2\x8d\x8b\xd4\xc5\x2f\xb0\x09\xba\x57\x07\x0a\x24\x47\x1e\xfb\x9f\x69\xe6\xd3\xbc\xd9\x6b\x6d\xe5\x6c\x67\x7d\xd5\xfd\xea\x49\xba\xef\x2e\x90\xef\x9a\x44\x84\xd0\x2a\x6d\xfa\x9f\xe2\xeb\xd9\xdb\xb7\xb9\xc4\xe1\x0f\x14\xc3\x7c\xe0\xf5\xc5\xdb\x8b\xef\xcf\x26\x17\x15\xa8\x9b\xc9\xd9\xe4\xf2\xdc\x0d\xd5\x88\xcd\x56\xa9\x1c\xac\x49\xa5\x4f\x8e\xaa\x0d\x9a\x3e\xa9\xe3\x72\x65\x67\xd4\x41\x21\x5d\x2a\xaa\xe6\x0b\x91\xb8\xec\xce\xdf\xe1\x21\x54\x62\x14\x9b\x41\xb2\x10\x72\x8a\x29\x18\x9b\x73\x21\x2d\xf7\x6f\x21\xea\xcc\x03\x5d\x2c\xe5\xb9\x8a\x30\x1f\x34\x78\xd4\x51\x21\x9d\x59\x84\x5a\xf0\x2a\x4b\x70\xea\x0a\xab\x95\x8a\x6a\x1d\x8b\x6f\xde\x5e\x9d\xbd\xde\xd4\xe2\x2c\x93\x7c\x44\x18\x7c\x52\xf9\xa4\x76\x97\xf2\xcd\x5a\x02\x6e\x26\x57\xd7\x35\x76\xe4\x77\xa4\xa0\x1e\x6e\xfd\xc4\x1f\xa3\xf1\xe2\xed\x9b\xd7\x17\x37\x93\xeb\x8f\xe7\x93\xed\x16\xef\x11\x4a\x6b\x05\x9f\xae\xeb\xd7\x16\x2c\xdf\x22\xf3\x34\xa9\x84\xb2\xcb\x99\x7f\xfc\xe2\xc3\xad\xf2\x8b\x0c\xf4\x3f\x3e\x04\x89\xb9\x48\x4c\xb3\x41\xd3\xb7\x05\x1d\xff\x1f\x50\xfc\x6f\x0b\x28\x4a\x82\xe3\x0a\xfe\x1b\x92\xc3\x93\xc4\x05\xdf\x24\x26\xe5\x6e\x70\x61\x41\x53\x0c\xad\x30\xb0\xc0\xc0\xdb\x45\x27\x26\x6f\xed\xa3\xaa\x6d\x86\xd8\xc7\xf6\x28\xe7\x42\x4e\x9b\x0d\x37\xbc\x2d\xeb\xf9\xc6\x26\xe9\xb5\x34\x28\xcf\x28\x9e\x48\x82\x7e\x5b\x0e\x64\xd5\xb7\x67\x49\xd9\x75\xaa\x87\xfa\x01\x8a\x4e\xc0\x6c\xd9\x76\x71\xa1\xba\x05\xca\x5d\x74\xfa\x86\x74\xb7\xc2\x3a\x48\x36\x5e\xac\x1a\x2a\x79\x07\xda\x9e\xdb\xfb\x7f\x83\x95\x99\xa8\xef\xe8\xbe\x95\x9d\xb2\xcf\x2f\xa7\xdc\xbc\x15\x73\x61\x5f\x76\x18\xfe\xed\xff\xf9\xa0\x45\x08\xfe\xef\x8f\x06\x22\xfc\x93\x98\xfa\x92\xaa\x05\x45\x41\xd2\xb7\x45\xd4\x60\x2f\x5d\xff\xdd\xc2\x8a\x7a\x4a\xeb\xc0\x3e\x0b\x57\x7f\x08\xed\xfd\xe7\x5b\x58\x7d\xc9\x3b\x08\x5a\xd9\x48\xbb\xd2\xaa\xaf\xb3\x68\x77\xd3\x94\xac\x47\xc4\x7f\xa5\x37\x44\xeb\xa3\x63\xd6\xcf\xf9\xe2\x77\x87\xc4\xd9\xfb\x6e\xc6\x8a\xae\x49\x03\x17\xf5\xf9\xd1\x76\x97\x47\x51\xf6\x03\x27\xb8\x01\x47\x4a\x7e\x1c\x38\x1d\x62\xbb\x15\x59\x36\xf7\xa4\xba\x95\x4f\x94\xfd\x9e\x66\xd3\xbb\xf3\x34\xb1\x62\x91\xac\xb2\xf5\xe8\x28\xf2\x45\x62\x00\x53\x9a\x81\x08\x9f\x9a\x91\x26\xc9\xf7\xdc\x9c\x2b\xb3\x41\xda\xb6\x89\x3e\x6d\x96\x2e\x40\xa2\x9a\x5e\x56\x49\x11\xa6\xe8\x9d\xc9\x7a\xa8\x8b\x37\x6c\x54\x1a\x89\x84\xe1\xf4\x8e\x0f\xc7\xcb\xbd\x2e\x54\x36\xea\x38\xe4\x68\x32\x28\xdd\xcf\x3e\xba\x2a\x61\x51\x7e\x71\x93\x29\xcb\xbd\xb1\x5c\xdb\x7c\x06\x35\x34\x2d\x34\x58\xd0\x85\x3f\xf3\x7b\x9d\x71\xf3\x46\xab\xf9\x4d\x5e\x01\xf2\x0f\x39\xde\x28\x4d\x4c\x3b\x93\x51\x95\x15\xdf\x30\xe1\xe1\xc1\xbf\x81\x7b\x7a\xad\x67\xaf\xb1\x89\x3b\xeb\x12\x36\xe7\x54\xa4\x98\xb8\x7b\x38\x52\x7c\x57\xe2\xcc\x53\x1c\x9c\x54\x33\xbe\xb7\x73\x52\xe0\xe0\x49\x82\x18\xae\xe4\x7b\x25\x2f\xee\x85\xb1\x42\x4e\x0b\x47\x50\x9d\x8d\xf1\x2f\x6a\x11\x8e\x92\xaa\x77\x85\xf9\x3b\x68\xd5\xa2\x36\x8c\x17\x51\xd0\x05\xc4\x60\x32\x2b\x44\xa3\xd5\x88\xd3\x7f\xf1\xf2\xc3\x2b\x8f\x3d\xe9\xe2\x7b\x87\x10\xef\x30\x03\xb6\xb7\x50\x46\xa0\x18\x75\x9a\xa5\x08\x38\xeb\x8b\x29\xf5\x67\x48\x65\x19\x48\xea\xb3\xca\x84\xd0\x2a\xb6\xe0\x2b\xf7\xb4\x37\x2b\x35\x4d\xb9\x61\xa1\x32\x36\x43\x47\x8f\x24\x94\xbb\x03\x4f\x25\xe3\x96\xbc\x19\x0a\x51\xfe\xc9\x55\x55\x2b\x8f\x26\x28\x46\x79\xc6\x81\xe1\xde\xbf\x41\x76\xd6\xd8\xfa\xc1\x6f\xbd\xb5\xd6\xc1\xf4\x6b\xb5\xed\x79\x2d\x42\xa8\xba\x8e\xfa\xa0\xa2\xe4\x62\x36\x1b\x13\xd6\x10\x6c\xf4\x28\x14\xb5\x24\xdf\x96\xe0\x6b\x6f\xee\x9d\x6e\xf6\x8e\x82\xd3\x23\x94\xa2\x43\xc6\x47\x53\x04\x5d\x54\xfe\xeb\xea\xfe\xbe\xea\x3f\x8a\xf7\x0e\xc3\x01\x0f\xe3\x10\xfa\x83\xa3\x11\xdf\x0b\xfa\xfb\x47\xd1\xe8\x20\x06\x00\x7e\x7c\x1c\x04\xc7\x87\x83\x20\x18\x1e\xc6\x07\xc3\x01\x8f\x46\x61\x7f\x10\x1e\x1e\x1e\x8e\x86\x07\x07\xc7\x07\x7b\x7b\x7b\x83\xa6\xef\xfb\x39\x0b\xc3\x22\x36\x2a\xd4\x07\x97\x76\xcf\xca\xdc\xb3\xb8\xd2\xc3\x32\xdc\xe2\x5c\x69\xa0\x4d\x2c\x81\xf1\x85\x7b\x12\xae\x52\xed\x5f\x21\xd3\x63\xa6\xd0\x54\x96\x70\x91\x4c\x4d\x90\xb9\x1e\xc1\xa9\xe7\x9c\x0e\x41\x6d\x9e\x4d\x65\x72\xbe\x8f\xc9\xd5\xeb\xab\x71\xa5\x2d\x02\xf3\xca\x6a\x6f\x52\xc0\xc3\xdb\x66\xfe\xd0\xe8\x51\xe9\x5d\x13\xcb\xe2\x85\xd2\x86\xb9\xcf\x14\xb0\xf2\x9c\xa6\xaa\x85\x9d\xe2\x3d\x37\xda\xef\xa5\x72\x70\x78\xe6\x19\x56\xda\xd4\x63\x2f\x79\x1e\x03\xc8\xda\xcb\x10\x53\x71\x57\x5c\x36\x8c\x59\x62\x4b\x67\x41\x98\x7e\xce\x9f\x61\x3d\xfa\x80\x29\x7b\xd9\x40\x15\xb1\xf5\x6a\xb0\x29\x95\x83\xa9\xd7\xaf\x40\xfc\x58\xef\xc4\xe6\x45\x9a\xef\x35\xa3\x35\xb2\x4a\x73\x66\xd0\xd6\x1f\x6e\x39\xf1\xac\x3e\x40\x2a\x09\xae\x7b\x22\x53\xba\x56\xf3\x54\xd5\x3c\xbe\xf2\x57\x48\x65\x62\x9e\xa1\xe2\x56\xe5\x47\xde\x12\xf4\xff\x04\x64\x2d\x20\xce\xab\xfc\xa9\xfd\x5c\xc5\x7f\x54\xf3\x8f\xe3\xe8\xf8\x20\x3a\x3e\xda\x3f\x1a\x86\x61\x70\x1c\x46\x3c\xdc\x1b\x1e\x87\x47\x43\x88\xa3\xf0\xe0\xe8\xb8\x7f\x14\x1e\xec\x07\x00\x61\xb0\x0f\x87\x10\x84\xd0\xdf\x0f\x86\x21\xdf\x1f\x04\xfd\x28\xde\x8b\x9b\xa5\xad\xaf\x29\x7e\xf9\xda\xec\xf7\xd0\x7e\xbf\xca\x73\x75\xbf\x14\x8a\x6f\x57\xfd\x0c\xa8\xd6\x00\x94\x31\x3c\xdb\x32\xaf\x27\x35\xbf\x83\x75\x0e\xe2\x90\x0f\x07\xd1\x68\xef\x68\x34\x08\xf8\x60\x34\x1a\x85\x83\x7e\x74\x7c\x18\xc1\x68\x18\xc7\x87\xa3\xa3\xe3\x63\xd8\xdf\x1b\x85\x87\x07\xa3\x60\x7f\x78\x7c\x38\x8c\xf6\x47\x7b\x10\xf0\xfe\xe0\x38\xd8\x3f\x3a\xd8\xa3\x58\xbe\xd8\xe8\x1f\x69\xa1\x2b\xcb\xd4\x9e\x54\xe9\xde\xb5\x55\xba\xab\xba\xce\x1a\x86\xa1\xf4\xbf\x4e\xf0\x24\x51\xa1\x7f\xf6\xdc\x2a\x9a\x18\x9b\xd5\xfe\xe2\xfc\xc3\x49\xb3\xf1\xb5\xf9\xb5\xf9\x9f\x01\x00\x00\xff\xff\x46\xc1\xb6\xdb\x41\x46\x00\x00") +var _state_diff_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x3c\x6b\x73\x1b\x37\x92\x9f\xc9\x5f\xd1\x56\x5d\xc9\xe4\x2e\xc3\x87\x44\xbd\x57\xbb\xa5\xc8\x72\xa2\x3b\xdb\x72\x49\x4c\xf6\xb2\x2e\x5f\x0a\x33\xd3\x43\x62\x35\x1c\xb0\x00\x8c\x24\x6e\xe4\xff\x7e\xd5\x0d\xcc\x8b\x1c\x4a\xf2\x26\xb9\xab\x7b\x7c\xb2\x88\x69\x34\xba\x1b\xfd\x06\xe0\xc1\x00\xce\xd5\x62\xa9\xe5\x74\x66\x61\x67\x38\x3a\x80\xc9\x0c\x61\xaa\xbe\x41\x3b\x43\x8d\xd9\x1c\xce\x32\x3b\x53\xda\xb4\x07\x03\x98\xcc\xa4\x81\x58\x26\x08\xd2\xc0\x42\x68\x0b\x2a\x06\xbb\x02\x9f\xc8\x40\x0b\xbd\xec\xb7\x07\x03\x37\xa7\xf1\x33\x61\x88\x35\x22\x18\x15\xdb\x7b\xa1\xf1\x18\x96\x2a\x83\x50\xa4\xa0\x31\x92\xc6\x6a\x19\x64\x16\x41\x5a\x10\x69\x34\x50\x1a\xe6\x2a\x92\xf1\x92\x50\x4a\x0b\x59\x1a\xa1\xe6\xa5\x2d\xea\xb9\xc9\xe9\xf8\xee\xc3\x0f\xf0\x0e\x8d\x41\x0d\xdf\x61\x8a\x5a\x24\xf0\x31\x0b\x12\x19\xc2\x3b\x19\x62\x6a\x10\x84\x81\x05\x8d\x98\x19\x46\x10\x30\x3a\x9a\xf8\x96\x48\xb9\xf1\xa4\xc0\x5b\x95\xa5\x91\xb0\x52\xa5\x3d\x40\x49\x94\xc3\x1d\x6a\x23\x55\x0a\xbb\xf9\x52\x1e\x61\x0f\x94\x26\x24\x1d\x61\x89\x01\x0d\x6a\x41\xf3\xba\x20\xd2\x25\x24\xc2\x96\x53\x5f\x20\x90\x92\xef\x08\x64\xca\xcb\xcc\xd4\x02\xc1\xce\x84\x25\xae\xef\x65\x92\x40\x80\x90\x19\x8c\xb3\xa4\x47\xd8\x82\xcc\xc2\x5f\x2f\x27\xdf\x5f\xfd\x30\x81\xb3\x0f\x3f\xc1\x5f\xcf\xae\xaf\xcf\x3e\x4c\x7e\x3a\x81\x7b\x69\x67\x2a\xb3\x80\x77\xe8\x50\xc9\xf9\x22\x91\x18\xc1\xbd\xd0\x5a\xa4\x76\x09\x2a\x26\x0c\xef\x2f\xae\xcf\xbf\x3f\xfb\x30\x39\xfb\xf6\xf2\xdd\xe5\xe4\x27\x50\x1a\xde\x5e\x4e\x3e\x5c\xdc\xdc\xc0\xdb\xab\x6b\x38\x83\x8f\x67\xd7\x93\xcb\xf3\x1f\xde\x9d\x5d\xc3\xc7\x1f\xae\x3f\x5e\xdd\x5c\xf4\xe1\x06\x89\x2a\xa4\xf9\xcf\xcb\x3c\xe6\xdd\xd3\x08\x11\x5a\x21\x13\x93\x4b\xe2\x27\x95\x81\x99\xa9\x2c\x89\x60\x26\xee\x10\x34\x86\x28\xef\x30\x02\x01\xa1\x5a\x2c\x5f\xbc\xa9\x84\x4b\x24\x2a\x9d\x32\xcf\x1b\x15\x12\x2e\x63\x48\x95\xed\x81\x41\x84\x3f\xcd\xac\x5d\x1c\x0f\x06\xf7\xf7\xf7\xfd\x69\x9a\xf5\x95\x9e\x0e\x12\x87\xce\x0c\xfe\xdc\x6f\x13\x4e\x63\x85\xc5\x37\x32\x8e\x27\x5a\x84\xa8\x41\x65\x76\x91\x59\x03\x26\x8b\x63\x19\x4a\x4c\x2d\xc8\x34\x56\x7a\xce\xaa\x02\x56\x41\xa8\x51\x58\x04\x01\x89\x0a\x45\x02\xf8\x80\x61\xc6\xdf\x9c\xa8\x59\x5f\xb5\x48\x8d\x08\x79\x34\xd6\x6a\x4e\xcc\x66\xc6\xd2\x1f\xc6\xe0\x3c\x48\x30\x82\x29\xa6\x68\xa4\x99\x40\x90\xa8\xf0\xb6\xdf\xfe\xa5\xdd\xaa\x92\x43\xaa\xc2\x4c\xe6\x60\xac\x1f\xf7\xf8\x5a\x23\x04\x99\x4c\x22\x99\x4e\xfb\xed\x56\x01\x7f\x0c\xbf\x7c\xe9\xb5\xdb\xad\x99\x30\x97\xa9\xb4\xe7\x22\x49\x30\x3a\x86\x58\x24\x06\x7b\xed\x56\x22\x8c\x3d\x0b\x43\x92\x70\x74\x16\x86\x2a\x4b\xed\x31\xa4\x59\x92\xf8\x6f\xd7\x18\x67\x69\x74\x0c\xc3\x1e\xa3\xb8\xd0\x5a\xe9\x62\x76\xbb\x15\xc9\x38\x7e\x2f\xf4\x2d\x6a\x73\x0c\xbf\xb4\x5b\xad\xf7\x38\x57\x7a\x79\x0c\x5b\x3f\x6f\xf5\x88\x6e\x8b\xf3\x85\x23\x9e\x14\x37\x82\xfb\x19\x79\x10\x9d\xa5\xa9\x4c\xa7\xb9\x4c\x42\xd4\x3d\xa7\xdf\x29\xde\xa1\x26\x2d\xd7\x68\x33\x9d\x62\x44\x82\x25\xa8\xcc\xa0\x6e\xb7\x5a\xdf\x2a\x9d\x1e\xc3\xd6\x1f\xb7\x7a\xed\x56\xeb\x8d\x24\x4e\xb6\xbe\xe1\x1f\xe7\x33\x91\x4e\xf9\xf7\x1f\xf8\xf7\x8d\x98\xe3\x31\x6c\x9d\xd2\x0f\x16\x80\x34\x57\xc1\xdf\x31\xb4\x17\xf3\x85\x5d\x1e\x43\x9c\xa5\xbc\x0f\x1d\x15\xfc\xbd\xcb\xa4\x93\xa6\x76\xee\x84\x86\x07\xb2\x3f\x37\xec\xe9\x70\x0c\x9f\xc0\x97\x76\xab\xe5\x47\xac\xce\xf0\xc4\xa3\xb6\xea\x7b\x7c\xf8\x57\x53\x41\x7a\x27\x12\x87\x34\x87\x5e\x2e\x50\xc5\x70\x27\x12\x78\x75\x7a\x0a\x5b\x64\xea\xe9\x74\x0b\x1e\x1f\x69\xac\x2f\xd3\x08\x1f\xae\xe2\xce\xd6\xf0\x61\xab\xcb\x10\x43\xf8\x0b\xd0\x2f\xf8\x23\x03\x58\x75\xc3\x33\x3a\xa3\xfd\x2e\x1c\xd3\x50\xbe\xb6\x70\x9b\x46\x7b\x5b\x59\x5f\x84\x61\x8f\x17\x75\x54\xc8\x18\x3a\x76\x26\x4d\xbf\x50\x8b\x4f\x22\x0c\x3f\xc3\xe9\xe9\x29\x7b\xd3\x58\xa6\x18\x39\xd0\x16\x49\x60\xce\xbb\xe8\x36\x16\x4e\x81\xa7\x56\xb6\xba\xef\x76\xf9\xa4\x4d\xf0\x8d\x78\x1d\xaa\xd6\xcf\x44\xc2\x31\x13\x42\xac\xae\xe1\xf1\x9b\xd6\x83\x52\x53\x94\x16\x53\x24\xeb\xf7\x8c\xbd\x36\x20\x53\x69\xa5\x48\x18\x8d\x43\xab\x71\xae\xee\x4a\x35\xa6\xe9\x7e\xc8\x99\x15\x93\xe3\x40\xb1\xaa\xb1\x04\xc8\x03\xa5\x7e\x05\x4b\xd6\xaf\x1f\xdf\x3b\xf0\x58\xa6\x22\xa9\x82\x1b\xab\x16\x90\x2d\x28\x22\xa4\x53\xaf\xc9\xb2\x4a\x9d\x1b\x9a\x13\x47\x11\x45\x18\xc6\xc0\xc8\x02\x91\x88\x34\xc4\x63\x2f\x8c\xd6\xa7\xaa\x58\x3f\x93\x61\xf2\xf8\x97\x1e\xff\x93\xaa\x97\xc3\x86\x2a\x7a\x29\xa8\x97\x68\x3e\xfc\xe5\xa4\xdd\x6a\x7d\xf1\xba\x33\x18\x40\xa2\xd4\x6d\xb6\xf0\x96\x0f\x32\x25\x0b\x71\x0e\xc6\x2c\x30\x94\x31\xc5\x0c\x51\x7c\xf5\xc6\x58\x3a\x23\xc5\x26\xd5\x6e\xd5\xd0\x54\xf5\x30\x8a\x74\x0f\xa2\xa0\xaa\x8c\xa4\x60\x22\x0c\x49\xaf\xc8\x6e\x18\xa6\x4b\x64\xb1\x7a\x34\x38\x24\x38\x25\x78\xd6\xb6\xc1\x00\x52\x05\x29\x3a\xc7\x10\xa3\x0d\x67\x6e\x73\xd0\xf4\x48\xf8\x44\x5d\x41\xae\x69\xd8\x95\xc1\x80\x5c\x50\x38\x83\x39\x8a\xd4\x38\xe7\x69\xd4\x9c\x36\x30\xcd\x44\x02\xa1\x48\xc2\x2c\x61\x9f\x6e\x5c\x60\x0a\x10\x53\x58\xa0\x26\x5f\x4f\xaa\x9a\x99\x4c\x24\xc9\x92\x7c\x84\x46\x93\x25\xd6\x74\xba\x0e\xf1\x87\xab\xc9\xc5\x31\x85\x6a\xa7\x89\xc2\x3a\xff\xa6\x16\xa4\xce\x71\xe6\xdc\x3e\xb9\x16\x4d\xf9\xcb\x3d\x42\xa4\xd2\xd7\x16\x34\x8a\xaa\xda\x46\x01\xdc\xcf\x30\xa5\x68\xc5\x8c\x62\xf4\x84\x01\xbf\xaa\x1a\x30\x6c\x6f\x43\x03\x50\xdf\x29\xb5\x37\x6f\xa7\xf8\x4e\x0b\xfc\x66\x78\x3d\x85\x53\x88\x82\xfe\x14\xed\xb7\xee\x77\xb9\x31\x04\x44\x2a\x57\x6c\x99\x83\x3b\x57\x91\x07\x2a\xa0\x58\x89\x0b\x44\x1f\x54\x05\x4d\xfb\xab\x5d\x0b\x7f\xab\xf8\xb7\x8a\x57\x2b\xd0\xf9\xcf\x6f\x84\x15\x39\xb6\x3a\xf7\xb9\xda\x90\x04\x3d\xa3\x8f\x4c\xe5\x23\x71\xd4\xed\xb3\xe0\xa5\x61\x71\xc7\x92\x82\x63\x8f\xb6\x2d\x65\x5c\x79\xb0\x8d\xa5\x36\x16\xac\x9c\xe3\xaa\xc1\x18\xd2\x98\xe4\xa9\x3d\xea\xfb\x55\xeb\x66\xea\xd6\x6d\xf2\xbf\x15\x8e\x9e\x9c\x9a\x6f\xdb\xc9\xea\x24\xe6\xae\x79\x0a\x7f\x5a\x9b\x40\x82\x68\x86\xa7\x2f\x85\xaa\x0c\x06\x94\xcc\x85\xe8\xdc\x79\xc8\xae\x3b\x67\x9b\x46\x5e\x35\x30\xe3\x04\x2f\xd2\xc2\x28\x67\xc2\x38\x8b\xa2\x48\xce\x29\x9b\xf4\xa2\xd6\x59\x4a\xd9\x3e\x88\xc4\x28\xa0\xd0\xde\xf3\x08\xf8\xab\x33\xd7\x3c\x0d\x2e\xd2\x84\x05\xa5\xd7\xc6\x96\x79\x42\xe1\xf9\x89\xac\x2a\x93\x1c\x8a\x58\xe0\x6b\x2a\xc7\xa4\x90\xed\x6c\x84\x20\x72\x3c\x47\x35\xc9\xe5\x81\x88\x54\xcf\xe5\x03\x5e\x56\xad\x86\xa5\x19\x7f\x21\xcd\x8a\x3e\xbe\x78\xe3\xad\xaa\x6f\xfb\x97\x52\xb7\x5d\x14\x8a\x14\x1a\x72\x29\xec\xb8\x2a\xbe\xb0\x07\x73\x65\x2c\x2c\xb4\x0a\x44\x90\x2c\x21\xc0\x50\x64\x86\xc3\xec\xc5\xe5\xc7\x6f\x46\xfb\x23\xaf\xf4\x8e\x1f\x90\xb6\xb2\xb1\x2a\xce\xcd\x9a\x32\x97\x34\x9b\x07\xa8\xb7\x48\x5e\x6e\xf4\x4f\xf0\x12\xe5\xf3\x3c\x46\x98\xa0\xc5\x66\x3b\xad\x3b\x27\xc0\xc4\x70\xa4\xed\x30\xca\x06\x19\x35\x2d\xc5\x12\x2a\xb4\xbc\x10\x34\x9b\xfa\x3a\x86\x06\xc5\x67\x04\xa5\xda\xaf\xc4\xc9\x1b\x9f\x9e\x34\xc7\xc9\x3c\x79\xc1\xd4\xea\xa2\x80\x99\x4a\x2a\xc1\xaa\x21\xb4\xdd\xf2\xd5\xc0\xa6\x30\x7a\x93\x87\xec\x95\x30\x7a\x8b\xcb\x1e\xa5\x7e\x14\x50\xbb\xbf\x36\x92\x36\x39\xd7\xaf\x75\xab\x5f\xe3\xcf\x1d\xb4\x8c\x1e\x0a\x6a\x6f\x71\xe9\x16\x5c\x35\x55\x2f\xc7\x4f\x32\x7a\xd8\x9c\xa2\x6a\xca\xa5\xeb\xf1\xe8\x86\x28\x2c\x65\xe5\xa2\x52\x6b\x33\x66\x6f\xd0\x6b\xf9\x93\x4b\xab\x06\x03\x97\x3e\x68\xb8\x45\x5c\x50\xf2\xc7\x66\xe6\x15\x20\x7a\x80\x99\x88\x20\x22\x01\x09\x0b\x09\x0a\x63\x81\xf4\xce\x4d\xfe\x39\xcc\xcb\x91\x57\x9d\xc1\x7f\x74\x86\x0f\xdd\xbf\x0c\xff\xf0\x2f\x83\xbe\x45\x63\x3b\x44\x7a\xb7\xeb\xd2\xb3\x16\x19\xc7\x31\x73\xe3\xb2\xb6\x4a\x92\xe6\x45\x53\xd4\x12\xcc\xb7\xa9\xf2\x4d\x9f\x9e\x64\xb2\x49\xbb\x0d\x57\x0f\xb9\x8f\x7c\xf5\xc2\xa9\x39\x47\x64\xf8\x0d\x3c\x19\xe6\xa9\xc9\x47\xbe\x04\x67\xcd\x7d\x16\xc6\xff\xdb\xed\x75\x93\x18\xf4\xef\x21\x06\xfd\x9b\x8a\xa1\x74\x3f\xae\xd1\x70\x23\xd3\x69\xe2\x0b\x69\x4a\x7f\x79\xd0\x77\x26\xb8\xd1\xc2\x91\x8c\x82\x28\x05\x2c\xaf\xbe\xa6\xdd\xaa\xce\xae\x78\x15\x52\xde\x6a\x62\x5e\x09\x53\x4f\x55\x6b\xff\xa4\xe9\xbb\x3d\xa4\x35\xd7\x77\xe3\xf1\xb1\xe9\x03\x99\xc6\x49\x99\x79\xd4\x98\xea\x51\xfe\x7c\x2f\x52\x4b\x82\xf0\x12\x20\x9f\xba\x45\x93\xb6\x68\xb1\xac\x96\x9c\x3c\x17\xd6\x37\x52\x47\xf8\x2a\xa1\x36\xcf\x0a\x29\x9e\x26\x4b\x26\xcb\x07\x64\xc3\x82\xe7\xd8\x43\xd4\xcd\x50\x23\xd1\x18\x8a\x22\x20\xa7\x38\x15\x56\xde\xa1\xa3\xce\xf8\xd8\x6d\xe1\xbe\xec\x82\x71\x6a\x14\xaa\xd4\xc8\x08\xb5\x6b\x04\xd2\x2f\x4c\x4d\x46\x19\x52\x42\xa8\xed\x4c\x65\xd3\x19\x2f\xcd\xbd\x93\x9f\x29\x03\xf5\xa5\x0d\x42\xa0\x51\xdc\xb2\x28\x12\x35\x95\x21\xa8\x14\x7e\x7c\xff\xda\xc0\xb9\x48\x27\x5a\xa4\x26\x46\x0d\xdb\x90\xff\x99\x27\x22\x72\x7a\x99\xda\xbe\x34\x97\xa9\xb1\x9c\xfb\xb3\xd3\xd9\xde\x76\xdd\x09\xf3\xc1\x53\xde\xe9\xd6\xc4\xe5\xa7\xfd\x03\xb5\x2a\x3c\x96\x6f\x78\x30\xd4\x27\x12\xfd\xe7\x63\x27\x78\xdf\x24\x61\xcc\xbd\x26\xed\xf6\xea\xf5\x84\x7a\xe7\x10\x4e\x07\x7a\xed\xb2\x9a\x9b\x89\x34\x4a\xd0\x05\xe4\x1b\x31\xf7\xf5\x38\xc7\x5b\xd4\x29\xd5\x6b\xb9\x15\x14\x7d\xa2\xff\x26\x33\xf0\x99\xf5\x46\x75\x77\x50\xec\xa1\x1a\x6d\xe5\xe4\xff\x98\x22\xba\xe4\x71\x7b\x9b\x05\xd7\xa4\x8a\x79\x35\x54\xd7\xc5\xd6\x06\x01\xc3\x29\x23\x3a\x81\xc1\xc0\x29\x1a\x51\x1f\xce\x30\xbc\xbd\x8c\x49\x71\x6a\xe9\xf9\x1a\x31\x56\x31\x29\x56\x35\x11\xe2\xb2\xf3\xe7\xc9\x60\x38\xab\x5e\x44\x02\x2b\x53\xe5\x5b\x55\x59\x6b\x05\xfd\xba\xda\x11\xf8\x06\xab\xdc\xa0\xd1\x45\xee\xe3\x72\x92\x9a\xd5\xf2\x36\xb8\x94\xc5\xaa\x95\x6f\x56\xb9\x2f\x2b\x31\xab\x42\xb6\xfb\x9b\x12\xa8\x40\xd9\x59\xee\xa4\x49\x4d\xb7\xac\xda\x2a\xcb\x15\x43\xb0\xb9\x52\x56\xe6\xff\xcf\xb6\x56\x2f\xfc\x0e\x81\xd5\xfa\x35\x8f\x8f\x24\xb6\x67\x95\x3e\x54\xf3\x85\xd0\x4e\xfb\x08\xc1\xb0\xeb\x67\x16\x3d\x04\xab\xba\x27\xa5\xe0\x67\xc2\xf8\x7c\xdf\x31\x6f\x9c\xf8\x0d\xc9\xbf\xda\x20\xa3\x5a\x5c\xa4\x4b\x5f\xca\x1b\x9e\x4b\x7b\x42\xe3\x56\x51\x79\x2d\xa2\xc8\xf9\xe2\x22\xf9\x7f\x6d\xbc\x43\xe6\xe3\x80\xfa\x32\x2b\xbb\x54\x76\xfb\x68\x5b\x37\x4b\xdc\x29\x6a\x29\xa7\x57\x3c\xbb\x5f\x74\xa5\x4e\x4f\x2b\x08\x58\x03\xb7\xb7\x59\xda\xfd\xb2\x2c\xdd\x00\xe0\x9a\x56\x8d\xdf\x99\x8c\xda\x99\x80\x5b\xd6\xe7\x69\xdd\x42\xa0\x2e\x6c\x54\x79\x0b\x56\xce\x0b\xa8\x04\xf3\x5d\x8c\xb2\x4a\xaa\xa4\xec\x5e\xde\x67\x51\xa4\xd1\x18\x36\x7e\xff\xb7\x2f\xba\x5c\xb5\xe1\xba\x99\x89\xa0\x44\x32\xf7\xe5\x45\x83\xbd\xd6\x76\xea\xd4\x31\x72\x3d\xe8\x52\xd9\xaf\xd3\x70\xd7\xd8\x73\x06\xb2\x19\xbe\x6a\x41\x5f\x51\x1c\xb6\x8a\xa6\x7c\x3e\xc1\xb8\x23\x3a\x6e\xc0\x1b\x50\x3a\x6f\xcf\xc6\xdc\x8f\x9f\x2b\xca\x35\xe0\x8d\x72\xa1\xcd\x79\x0c\xaa\x61\xa1\x93\x2a\x8a\xe4\x20\x52\xc5\xc7\xb1\x73\xb4\x33\x15\x75\xf3\xfe\x10\x0f\xde\x4b\x53\x2a\xf6\x9c\x0f\xb4\xf9\x14\xe9\x1b\xa7\xc2\xec\x62\xed\x43\xff\x13\x99\xcc\xa3\x55\x8f\xa1\x92\x69\x20\x0c\x7e\x76\x26\x5e\x69\x44\xf5\x5d\x57\x97\x9b\xba\xeb\x5d\x24\x77\x7a\xf0\xf8\x08\x4d\x6d\xa0\x3c\xef\x7f\xa6\xcb\xd1\x0a\x55\x6a\x65\x5a\xeb\x17\xe5\xfe\x92\x6c\x54\x72\x5f\x51\x40\x8a\xf7\x10\x28\x3e\xa5\xf0\xab\x79\xc8\x99\x58\x2c\x30\x35\x14\x52\xe7\x4a\x47\x4a\xbf\x36\x60\x1f\x8e\x61\xf8\x10\x8e\xf7\x0f\x0f\xf6\x86\x41\x74\x10\x1d\xec\xc6\x7b\xbb\x71\xbc\x1b\x47\xe1\xc1\x78\x67\x38\xda\x19\xef\x1d\x8d\x86\xd1\xe1\x38\x3a\x1c\x07\x71\xb8\x17\x8e\x87\xd1\xd1\x01\x8e\xc3\x3d\x71\x78\xb4\x73\x18\x1e\xed\x8c\x0e\x0f\x9e\xee\x9c\xd5\x54\xc5\x95\xaf\xdb\xdb\xf0\xd2\x86\xe5\x29\x0c\x1b\xe7\x6c\x6c\x3f\x9e\x9e\xf2\x11\x58\xe3\xa4\xcd\x4d\x4e\xf6\x8f\x0d\xad\xba\x3c\x50\x34\x75\xff\x2a\x1b\xc1\xfe\xdc\x81\xae\x4d\x3f\xa9\xb4\xb0\x5e\xfe\xd1\x2b\xc7\xc6\xef\xac\x52\x1b\xbf\x72\xef\xbe\xac\x53\x9b\xf7\xa2\x89\xe1\xb2\xab\xcf\x2c\xd7\xb2\xdf\x4e\x03\x60\xd1\x59\x5f\x45\x94\xf7\xf4\x9f\x41\xc3\x60\x1b\x91\xe4\xc7\x07\x4f\xe3\x20\xa8\x2a\x8a\x6a\x23\xe0\x79\xe6\x5c\x81\xfb\x6b\x79\x6b\xc0\xf2\xb5\xac\x35\xa0\x58\xe3\xac\xd8\xd1\x28\xe8\x23\x87\x9f\x7a\x54\xe8\xfe\x3a\x67\xa2\x16\x56\xce\xa5\xe1\x83\xac\x63\x58\x68\xfc\xa6\x70\x2f\xf7\xe8\x12\x2e\x1f\xf6\x21\xc0\x58\x69\x84\x85\xd0\xc6\x1d\x71\xba\x16\x57\xad\x93\xfe\x8a\x97\x5f\x8b\xf8\x55\x16\x7f\x25\xc1\xae\x82\xab\xf5\x50\x25\x97\x26\x65\x9c\x95\x11\x9f\xcc\x37\x74\x55\xf2\xa5\x57\xa3\x28\xa7\x92\x34\xc7\xc3\xb9\xfc\xb5\x8c\xa7\xbe\xd3\xba\x16\x4f\xad\xfa\xab\xd2\x51\x47\x46\x0f\xdd\x9e\xbb\x0d\x51\x86\x58\x97\xd1\x58\xb9\xe2\x20\xaa\xfd\x1d\xc7\x27\xc9\xcd\x58\x59\x77\x52\xf5\x0c\xd0\xb9\xb6\xd6\xe3\x23\xac\x01\xf6\x57\xb3\xc5\x12\xf6\xd5\x3a\x70\xde\x42\xca\x05\xf1\xc4\x26\x34\x50\x5a\xdf\x12\xbf\x27\x2f\x70\x37\xae\x39\x87\xa6\xd9\xb0\x8d\x95\x35\x9b\x61\x84\x0c\xdd\xd4\x8b\xa1\x2c\xb0\xc0\xfb\x95\xe4\xd7\xdc\xc4\x93\x1d\x5f\x8d\x26\x9f\xd3\x5e\x9f\xc9\xed\x04\x4c\x5d\xa3\x89\x32\x61\x9f\x2d\x50\x38\xa6\x0c\xb2\x7e\x48\xe2\x5a\xf8\xd5\x96\x3f\x07\xa0\x3c\x3d\x6e\x3d\x17\xab\x87\xe1\xde\x51\x14\xc5\x87\x18\x04\x42\xec\x8f\x47\xe3\x61\x14\xec\xef\x8c\xc6\x41\x20\xa2\xfd\xf1\x28\x8e\xe3\xfd\x20\x18\xee\xef\x1f\x8e\x0f\xa2\x00\xe3\xdd\xf1\xee\x6e\x34\xde\x1d\x07\xd1\x28\x0e\xf6\x77\x0e\x86\xa5\x50\x9f\xee\x70\x15\xc7\xc2\xd5\x3a\xb2\xdc\x9c\xdf\x5f\xe8\xeb\x9e\x71\x4d\x35\xbe\x54\x9a\xdf\x2b\xc9\x63\x96\xe6\xed\xd1\x3c\x8d\xfc\x2f\x70\x4a\xb5\x42\x56\xa6\x61\x92\x45\x78\xb5\xe0\xdb\x4f\x79\x31\x45\x6e\xc9\x9f\xfd\x84\xf0\xe3\x7b\xb8\xfa\xe8\x33\xdb\x76\xab\x3e\xa1\x52\x3a\xa0\xd6\xb5\x6b\x41\xa8\x39\x8d\xda\xde\xa6\xbf\x8a\x5b\x40\xaf\x89\x18\x2d\x42\x4b\xf5\x17\x97\x0c\xa1\x4a\x12\x69\xa4\x4a\x5f\x77\xe1\xcf\xf0\xcd\xe8\xa4\x4a\x9c\x4b\x17\x65\x7a\xa7\x28\x99\xf6\xde\x9c\xea\xba\x1f\xdf\x97\x97\xd0\xfa\xae\x24\xb7\x0f\xbe\xba\x7b\x9c\x9b\xe9\xa4\x4c\x83\xc1\xa0\x75\xa7\xaf\x51\x24\x09\x5e\x24\x40\x54\xe0\x03\x95\x22\x06\x23\xd2\x5f\x14\xe1\xcc\x75\x7a\x7c\x0a\xde\x27\x56\x6b\xf7\x8e\x42\xfb\xc0\xae\xd2\x95\xe6\x7e\x7b\xca\x7b\x67\x45\x87\xdb\x75\x89\xa6\xb4\x68\x68\x33\x91\xd4\xfa\xb6\x4e\xb6\xab\x99\x7a\xb1\xfd\xfd\x76\x63\x4d\x44\xe9\x3d\xcd\xf1\xae\x7a\x23\x4c\x8e\xaf\x74\xe9\x83\x01\xb0\x34\xd8\xcc\xd1\xb5\x1a\xf9\xce\x24\x89\xd6\x71\xca\xb2\x71\x82\x76\x15\xb4\x23\xda\x2a\x4f\x72\x7e\xda\x37\x79\xe8\x3b\x84\xb4\x92\x55\x24\x35\x1a\x76\x75\xcb\x8c\x91\x18\x10\xc9\xbd\x58\xba\xa5\xfc\xed\xa3\x8b\x1f\xdf\xf3\x0a\xca\xdd\x14\x3b\x3d\xbf\xbe\x38\x9b\x5c\x14\x7d\x39\xbf\x6a\x8a\xf7\xc9\xd2\x5f\x33\x8c\x60\x55\x49\xf2\xa3\x4e\xfb\xd0\x77\xdc\x34\x9d\xc7\x6f\x90\x09\x4f\x28\x04\x57\x51\x7d\x63\x71\x51\xd5\x2e\x12\x0d\xde\xa1\x5e\x82\x5a\x70\xde\xc3\x17\x68\xdc\x05\x2a\xaf\x6c\x14\xb4\x69\x5a\x45\x2b\x12\x35\x2d\xb5\x62\x30\x80\x73\xb1\xb0\x99\x57\x52\x5f\x0d\xca\xf9\x1c\x23\x29\x2c\x26\x4b\xdf\x34\x70\x95\xd6\x29\x24\x6a\xda\x9f\xa2\x65\x53\xea\x14\x17\x4d\x94\x37\xc6\xe2\x3b\x29\x58\x05\xa6\xe6\x1f\x1c\xe8\xf6\x36\x74\x1c\xd2\x57\x2b\x5d\x98\x02\x5b\x5d\x64\x55\x99\x15\x58\xca\x33\x9a\xa2\x49\xb7\x09\xab\x6b\x31\xd4\x7c\x41\xc7\xaf\xd4\xed\xd6\xfa\x10\x4d\x47\xb5\x84\x8e\x52\x8f\x1a\x60\xe9\xba\x36\xcd\xfb\xbc\x79\xdf\x5f\x30\x39\x2f\x70\x3d\x97\x00\xfe\x14\xb4\x28\xad\x79\xbf\x67\x82\x0c\xc2\xed\x50\xa9\x55\x8d\xc7\xcd\xc4\xc1\xea\xd1\x7e\xbb\x7a\x42\xed\x2e\x98\x96\xfb\xe8\x7e\x77\x0a\xcb\xa4\x80\xcc\xb7\x3d\xa8\x1a\xf6\xd7\xef\x0c\x91\xc3\x0b\xf5\x48\xfb\xc9\x44\x56\x6f\xa6\xb5\x5b\x2d\x73\x2f\x29\x15\x24\xed\xeb\xab\x45\x79\x85\x32\xdf\xd7\x90\x9c\xca\xd6\xc5\xbf\x4f\xce\xaf\xde\x5c\x9c\x5f\x7d\xfc\x69\xeb\x18\x6a\x63\x37\x97\x7f\xbb\x28\xc6\xbe\x3d\x7b\x77\xf6\xe1\xfc\x62\xeb\x78\x35\x87\xcc\xed\xa8\x4c\xdf\x69\x41\x63\x45\x78\xdb\x5f\x20\xde\x76\x86\xdd\xda\xf5\xcd\x6e\x61\x67\xad\x16\xf7\xc8\x4f\x4a\x62\x9c\xdd\xfb\x35\xb8\xd5\x52\xf4\x8c\x08\x69\x6e\xf2\x24\xa7\x7c\x31\x8f\xa9\x91\xa0\x73\x0f\xdf\x11\x65\xa3\xa8\x7e\x2f\x0b\x8d\x71\x04\xf5\x9a\x0b\xe3\x27\xe9\xdc\xf1\x84\xba\xeb\xc9\xe1\xed\x31\x18\x91\xd8\x1e\x18\xf9\x0f\xec\x81\x8a\x63\x83\xb6\x07\x98\x46\xea\x7e\x8e\xae\x79\xe1\x6c\x97\xbf\x78\x9e\x2a\x82\x1a\x75\xfb\xec\x4c\xaf\x62\xd7\x7c\x71\x99\xb6\xfc\x07\xae\x83\xee\x34\x81\x22\xeb\x91\xc7\xfe\x47\x9e\xf9\xbc\x6c\x76\x3a\x1b\x25\xdb\x5b\x5d\x75\xb7\xbe\x93\xee\xbb\x4b\xc4\xfb\x26\x91\x21\x76\x2a\x4c\xff\x53\x72\x3d\x7b\xf7\xae\xd0\x38\xfa\x41\x6a\x58\x0c\xbc\xb9\x78\x77\xf1\xdd\xd9\xe4\xa2\x06\x75\x33\x39\x9b\x5c\x9e\xbb\xa1\x06\xb5\xd9\xa8\x95\xa3\x15\xad\xf4\xc5\x4d\xfd\x82\xa5\x2f\xca\x44\xba\xb4\x33\xbe\x01\x91\xba\x52\x52\xcd\x17\x32\x71\xd5\x99\x3f\x83\x23\xa8\xc4\x28\x98\x61\xb2\x90\xe9\x94\x4a\x28\x98\x0b\x99\x5a\xe1\xdf\x32\x34\xb9\x07\x3e\x18\x2a\x6a\x0d\x69\x3e\x6a\xf4\xa8\xa3\x52\x3b\xf3\x0c\xb3\x94\x55\x5e\xa0\x34\x35\x46\x6b\x1d\xd1\x26\x11\xdf\xbc\xbb\x3a\x7b\xb3\x6e\xc5\x79\x25\xf8\x84\x32\xf8\xa2\xf0\x59\xeb\xae\xd4\x8b\x8d\x04\xdc\x4c\xae\xae\x1b\xfc\xc8\x6f\x48\x41\x33\xdc\xea\x8e\x3f\x45\xe3\xc5\xbb\xb7\x6f\x2e\x6e\x26\xd7\x3f\x9c\x4f\x36\x7b\xbc\x27\x28\x6d\x54\x7c\x3e\x6e\x5f\x59\xb0\x7a\x0a\x2c\xb2\xa4\x96\xca\xde\xcf\xfc\xe3\x15\x9f\x6e\x55\x5f\x54\x50\xfc\xf1\x29\x48\x2c\x64\x62\xda\x2d\x9e\xbe\x29\xe9\xf8\xff\x84\xe2\x7f\x5b\x42\x51\x51\x1c\xd7\xb0\x5f\xd3\x1c\x91\x24\x2e\xf9\x66\x35\xa9\xde\xe6\x96\x16\x35\xe7\xd0\x8a\x12\x0b\x4a\xbc\x5d\x76\x62\x8a\xab\x79\xdc\x75\xcd\x11\xfb\xdc\x9e\xf4\x5c\xa6\xd3\x76\xcb\x0d\x6f\xaa\x7a\xbe\xf2\x92\xf3\x4a\x19\x54\x54\x14\xcf\x14\x41\xbf\xae\x06\xb2\xea\xeb\xab\xa4\xfc\x38\xd4\x43\x7d\x8f\xe5\x4d\xbe\x7c\xd9\x6e\x79\x20\xba\x01\xca\x1d\x54\xfa\x0b\xe5\x6e\x85\x55\x90\x7c\xbc\x5c\x35\x54\xe9\x1d\x6a\x7b\x6e\x1f\xfe\x0d\x97\x66\xa2\xbe\xe5\xf3\x52\x38\x85\x4f\xaf\xa7\xc2\xbc\x93\x73\x69\x5f\xf7\x80\xfe\xf6\xff\x7c\xd4\x32\x44\xff\xf7\x0f\x06\x23\xfa\x93\x85\xfa\x9a\xab\xfd\xb2\xa1\xe8\xaf\x35\x34\x60\xaf\x1c\xdf\xdd\xe2\x92\xef\x84\x36\x81\x7d\x92\xae\x7f\x10\xda\x87\x4f\xb7\xb8\xfc\x5c\xdc\x00\xe8\xe4\x23\xdd\xda\x55\x7b\x9d\x67\xbb\xeb\xae\x64\x35\x23\xfe\x33\xbf\x01\x5a\x1d\x3d\x86\x61\x21\x17\xcf\x1d\x11\x67\x1f\xfa\xb9\x28\xfa\x26\x0b\x5c\xd6\xe7\x47\xbb\x7d\x11\x45\xf9\x0f\x9a\xe0\x06\x1c\x29\xc5\x76\xd0\x74\x8c\xed\x46\x64\xf9\xdc\x93\x3a\x2b\x3f\x72\xf5\x7b\x9a\x4f\xef\xcf\xb3\xc4\xca\x45\xb2\xcc\xd7\xe3\xad\x28\x16\x89\x11\x4d\x65\x06\x21\x7c\x6e\x46\x96\x24\xdf\x09\x73\xae\xcc\x1a\x69\x9b\x26\xfa\xb2\x39\x75\x09\x12\xf7\xe4\xf2\xe3\x02\x69\xca\xbb\x2f\xf9\x1d\xe8\xf2\x0d\x1a\xb7\x46\x22\x69\x04\xbf\xc3\xa3\xf1\xea\x5d\x15\x6e\xfb\xf4\x1c\x72\x72\x19\x5c\xee\xe7\x1f\x5d\x97\xaf\x6c\xbf\xb8\xc9\x5c\xe5\xde\x58\xa1\x6d\x31\x83\x2f\x24\x2d\x34\x5a\xd4\x65\x3c\xf3\xbc\xce\x84\x79\xab\xd5\xfc\xa6\x78\x61\xe8\x1f\x62\xbc\x55\x9a\x85\x76\x96\x46\x75\x51\x7c\xc5\x84\xc7\x47\xff\x86\xed\xf9\xb5\x5e\xbc\xc6\x3a\xee\xfc\x96\xaf\x39\xe7\x26\xc5\xc4\x9d\xa3\xb1\xe1\xbb\x16\x65\x51\xe2\xd0\xa4\x86\xf1\x9d\xad\x93\x12\x87\x48\x12\xc2\x70\x95\x7e\x50\xe9\xc5\x83\x34\x56\xa6\xd3\x32\x10\xd4\x67\x53\xfe\x4b\x56\x44\xa3\x6c\xea\x7d\x69\xfe\x86\x5a\x75\xf8\x1a\xc5\xab\x28\xe8\x23\x61\x30\xb9\x17\xe2\xd1\x7a\xc6\xe9\xbf\x78\xfd\x11\xb5\xc7\x9a\x7c\x70\xbd\xc5\x88\xb7\xc0\xa0\x1d\x2c\x94\x91\xa4\x46\xbd\x76\x25\x03\xce\xef\xb5\x54\xee\x57\xa4\xca\x02\xa6\x7c\x4f\x2a\x57\x42\xab\x60\x21\x96\xee\x69\x6e\xde\x6a\x9a\x0a\x03\xa1\x32\x36\x47\xc7\x8f\x1c\x94\x3b\xc3\xce\x52\x10\x96\xa3\x19\x29\x51\xf1\xc9\x75\x45\x6b\x8f\x1e\x38\x47\x79\xc1\x86\x11\xef\x5f\xa1\x3b\x2b\x62\xfd\xe8\x59\xef\xac\xdc\x40\xfa\xa5\x7e\x6d\x79\x25\x43\xa8\x87\x8e\xe6\xa4\xa2\x12\x62\xd6\x2f\x16\xac\x20\x58\xbb\x63\x50\xf6\x92\xfc\xb5\x02\xdf\x7b\x73\xef\x6c\xf3\x77\x10\x82\x1f\x91\x94\x37\x5c\x7c\x36\xc5\xd0\x65\xe7\xbe\xa9\x6f\xef\xbb\xf6\x87\xf1\xce\x7e\x38\x12\x61\x1c\xe2\x70\x74\x70\x28\x76\x82\xe1\xee\x41\x74\xb8\x17\x23\xa2\x38\x3a\x0a\x82\xa3\xfd\x51\x10\x8c\xf7\xe3\xbd\xf1\x48\x44\x87\xe1\x70\x14\xee\xef\xef\x1f\x8e\xf7\xf6\x8e\xf6\x76\x76\x76\x46\x6d\x7f\x6f\xe7\x2c\x0c\xcb\xdc\xa8\x34\x1f\x5a\xda\x3d\x0b\x73\xcf\xda\x2a\x0f\xc3\x88\xc5\xb9\xd2\xc8\x4c\xdc\x23\x88\x85\x7b\xd2\xad\x32\xed\x5f\x11\xf3\x63\xa4\xd0\xd4\x96\x70\x99\x4c\x43\x92\xb9\x9a\xc1\xa9\x97\xec\x0e\x43\xad\xef\x4d\x6d\x72\x79\x58\x5d\xf5\x03\x79\x1d\xc7\x4b\x2b\x26\xad\x78\x35\xf4\xe4\x7b\x9b\xfc\x22\x3e\x37\x80\x56\x9b\x9f\xa6\xd2\xfd\xe4\xab\x69\x25\xe2\xa7\x8e\xfa\xd7\xcf\x7d\xfc\xd5\x28\x5e\x23\x6f\xac\xe6\xf6\xbb\xfa\xce\xc8\xed\x46\xfd\xbd\x4c\x65\x9f\xdc\x8b\x8e\xca\x29\x90\xa7\xaa\xe1\xad\x90\x3f\xf1\xa8\x12\xf3\x02\x8d\xb6\xaa\x70\x31\x1d\xc9\xcf\xda\xf3\x1b\x0b\xce\x89\xfe\xa1\xfb\x52\x3d\x7f\x52\xd1\x8f\xe2\xe8\x68\x2f\x3a\x3a\xd8\x3d\x18\x87\x61\x70\x14\x46\x22\xdc\x19\x1f\x85\x07\x63\x8c\xa3\x70\xef\xe0\x68\x78\x10\xee\xed\x06\x88\x61\xb0\x8b\xfb\x18\x84\x38\xdc\x0d\xc6\xa1\xd8\x1d\x05\xc3\x28\xde\x89\xdb\x15\xd6\x57\xf4\xbc\x7a\xca\xf3\x5b\x28\xbb\x5f\xe5\xa5\xaa\x5e\xc9\x3c\x37\x6b\x7a\x0e\xd4\xa8\xef\x55\x0c\x2f\x76\x44\xab\x39\xfc\x6f\xe0\x8c\x82\x38\x14\xe3\x51\x74\xb8\x73\x70\x38\x0a\xc4\xe8\xf0\xf0\x30\x1c\x0d\xa3\xa3\xfd\x08\x0f\xc7\x71\xbc\x7f\x78\x70\x74\x84\xbb\x3b\x87\xe1\xfe\xde\x61\xb0\x3b\x3e\xda\x1f\x47\xbb\x87\x3b\x18\x88\xe1\xe8\x28\xd8\x3d\xd8\xdb\xe1\xd4\xb5\x64\xf4\xf7\x74\x48\xb5\x65\x1a\x77\xaa\x72\x4c\xd8\xa9\x1c\xcd\x5c\xe7\xf7\x5b\xb1\xf2\x9f\x24\x88\x24\x51\xa1\x7f\xa5\xdb\x29\xef\xdc\xb5\xeb\xd7\x61\x8b\x0f\x27\xed\xd6\x97\xf6\x97\xf6\x7f\x06\x00\x00\xff\xff\x90\x08\xd6\x89\xf0\x44\x00\x00") func state_diff_tracerJsBytes() ([]byte, error) { return bindataRead( @@ -287,7 +287,7 @@ func trigram_tracerJs() (*asset, error) { return a, nil } -var _unigram_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x54\x4d\x6f\xdb\x46\x10\xbd\xeb\x57\xbc\xa3\x8c\xa8\xa4\xd3\x5e\x0a\xa5\x09\xc0\x1a\x76\x22\xc0\x91\x0d\x89\x6e\x60\x14\x3d\x2c\xc9\x21\xb9\xe8\x6a\x87\xd8\x9d\x95\x42\x04\xfa\xef\xc5\x92\xa2\xe5\x1a\x6e\x13\x9e\x04\xcd\xbc\x8f\x79\x33\x64\x9a\xe2\x8a\xbb\xde\xe9\xa6\x15\xfc\x7c\xf9\xf6\x57\xe4\x2d\xa1\xe1\x9f\x48\x5a\x72\x14\x76\xc8\x82\xb4\xec\xfc\x2c\x4d\x91\xb7\xda\xa3\xd6\x86\xa0\x3d\x3a\xe5\x04\x5c\x43\x5e\xf4\x1b\x5d\x38\xe5\xfa\x64\x96\xa6\x23\xe6\xd5\x72\x64\xa8\x1d\x11\x3c\xd7\x72\x50\x8e\x96\xe8\x39\xa0\x54\x16\x8e\x2a\xed\xc5\xe9\x22\x08\x41\x0b\x94\xad\x52\x76\xd8\x71\xa5\xeb\x3e\x52\x6a\x41\xb0\x15\xb9\x41\x5a\xc8\xed\xfc\xe4\xe3\xe3\xfa\x01\xb7\xe4\x3d\x39\x7c\x24\x4b\x4e\x19\xdc\x87\xc2\xe8\x12\xb7\xba\x24\xeb\x09\xca\xa3\x8b\xff\xf8\x96\x2a\x14\x03\x5d\x04\xde\x44\x2b\xdb\x93\x15\xdc\x70\xb0\x95\x12\xcd\x76\x01\xd2\xd1\x39\xf6\xe4\xbc\x66\x8b\x5f\x26\xa9\x13\xe1\x02\xec\x22\xc9\x5c\x49\x1c\xc0\x81\xbb\x88\xbb\x80\xb2\x3d\x8c\x92\x33\xf4\x07\x02\x39\xcf\x5d\x41\xdb\x41\xa6\xe5\x8e\x20\xad\x92\x38\xf5\x41\x1b\x83\x82\x10\x3c\xd5\xc1\x2c\x22\x5b\x11\x04\x5f\x56\xf9\xa7\xbb\x87\x1c\xd9\xfa\x11\x5f\xb2\xcd\x26\x5b\xe7\x8f\xef\x70\xd0\xd2\x72\x10\xd0\x9e\x46\x2a\xbd\xeb\x8c\xa6\x0a\x07\xe5\x9c\xb2\xd2\x83\xeb\xc8\xf0\xf9\x7a\x73\xf5\x29\x5b\xe7\xd9\xef\xab\xdb\x55\xfe\x08\x76\xb8\x59\xe5\xeb\xeb\xed\x16\x37\x77\x1b\x64\xb8\xcf\x36\xf9\xea\xea\xe1\x36\xdb\xe0\xfe\x61\x73\x7f\xb7\xbd\x4e\xb0\xa5\xe8\x8a\x22\xfe\xfb\x99\xd7\xc3\xf6\x1c\xa1\x22\x51\xda\xf8\x29\x89\x47\x0e\xf0\x2d\x07\x53\xa1\x55\x7b\x82\xa3\x92\xf4\x9e\x2a\x28\x94\xdc\xf5\x3f\xbc\xd4\xc8\xa5\x0c\xdb\x66\x98\xf9\x3f\x0f\x12\xab\x1a\x96\x65\x01\x4f\x84\xdf\x5a\x91\x6e\x99\xa6\x87\xc3\x21\x69\x6c\x48\xd8\x35\xa9\x19\xe9\x7c\xfa\x21\x99\xcd\xbe\xcd\x00\x20\x4d\xd1\x6a\x2f\x71\x39\x91\x76\xa7\xba\xe8\x8a\xbb\x92\x2b\xf2\x10\x46\xc9\xc1\x0a\x39\x3f\x74\xc7\xd6\x25\xbe\x1d\x17\x13\xd6\x72\xe7\xc7\x16\x0f\x1b\x76\x05\xb9\x11\x3e\xb6\xc7\xea\x12\x97\x4f\xdd\x5e\xa8\x8b\x4a\xda\xee\xf9\x6f\xaa\x86\xdc\x68\x4f\xae\x3f\x09\x8e\x77\x10\x7d\xfc\xf1\x19\xf4\x95\xca\x20\xe4\x93\x01\x1d\xa1\x4b\xd4\xc1\x96\xf1\xfa\xe6\x86\x9b\x05\xaa\xe2\x02\xe3\x14\xf1\xd9\xab\x78\x9b\x78\x0f\xc3\x4d\xc2\x5d\x22\xbc\x15\xa7\x6d\x33\xbf\x78\xf7\xd4\xa3\x6b\xcc\xa5\xd5\x3e\x89\x83\xfc\xc9\xdd\x5f\x17\x67\x7c\x7c\xfe\x55\x7b\xf3\xe6\x0c\x3c\x3e\xfd\x22\xe3\x09\xff\x83\xc2\x7b\xbc\x7d\x0d\x37\x34\xc5\x40\x26\xda\x73\x88\xb5\x0a\x46\x9e\xe7\x72\x68\x4f\x17\xad\x4a\x09\xca\x9c\xa2\x88\x6f\x27\xd7\x50\x76\x4a\xab\x1e\x6f\x2d\xb2\x0c\x14\xaf\xe6\x73\x5c\xcc\x26\x1d\x47\xfe\x35\x21\x65\xcc\x20\x36\x2d\x7d\x38\xd5\x82\xc8\x42\x0b\x39\x15\xdf\x55\xde\x93\x8b\x9f\x29\x38\x92\xe0\xac\x9f\x18\x23\xac\xd6\x56\x99\x89\xfb\x74\xd1\xe2\x54\xa9\x6d\x33\x7a\x1b\x4b\xcf\xcc\x95\xf2\xf5\xf9\xe2\x74\x3d\x7f\x0a\x07\x1f\x70\xf9\x62\x27\xa3\xe4\x39\xe4\x97\xe1\x1e\x17\xb3\xe3\xec\x9f\x00\x00\x00\xff\xff\x8d\xba\x8d\xa8\xe6\x05\x00\x00") +var _unigram_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x94\x41\x6f\xdb\xc6\x13\xc5\xef\xfa\x14\xef\x68\x23\xfa\x8b\xc9\xbf\x97\x42\x69\x0a\xb0\x86\x9d\x08\x70\x64\x43\xa2\x1b\x18\x45\x0f\x4b\x72\x48\x2e\xba\xda\x21\x76\x67\xa5\x08\x81\xbf\x7b\x31\xa4\x68\xb9\x85\xdb\x86\x27\x41\x3b\xef\x37\x6f\xde\x0e\x99\x65\xb8\xe2\xfe\x18\x6c\xdb\x09\xfe\xff\xf6\xdd\x8f\x28\x3a\x42\xcb\xff\x23\xe9\x28\x50\xda\x21\x4f\xd2\x71\x88\xb3\x2c\x43\xd1\xd9\x88\xc6\x3a\x82\x8d\xe8\x4d\x10\x70\x03\xf9\x5b\xbd\xb3\x65\x30\xe1\xb8\x98\x65\xd9\xa8\x79\xf5\x58\x09\x4d\x20\x42\xe4\x46\x0e\x26\xd0\x12\x47\x4e\xa8\x8c\x47\xa0\xda\x46\x09\xb6\x4c\x42\xb0\x02\xe3\xeb\x8c\x03\x76\x5c\xdb\xe6\xa8\x48\x2b\x48\xbe\xa6\x30\xb4\x16\x0a\xbb\x38\xf9\xf8\xb8\x7e\xc0\x2d\xc5\x48\x01\x1f\xc9\x53\x30\x0e\xf7\xa9\x74\xb6\xc2\xad\xad\xc8\x47\x82\x89\xe8\xf5\x9f\xd8\x51\x8d\x72\xc0\xa9\xf0\x46\xad\x6c\x4f\x56\x70\xc3\xc9\xd7\x46\x2c\xfb\x39\xc8\xaa\x73\xec\x29\x44\xcb\x1e\x3f\x4c\xad\x4e\xc0\x39\x38\x28\xe4\xc2\x88\x0e\x10\xc0\xbd\xea\x2e\x61\xfc\x11\xce\xc8\x59\xfa\x1d\x81\x9c\xe7\xae\x61\xfd\xd0\xa6\xe3\x9e\x20\x9d\x11\x9d\xfa\x60\x9d\x43\x49\x48\x91\x9a\xe4\xe6\x4a\x2b\x93\xe0\xcb\xaa\xf8\x74\xf7\x50\x20\x5f\x3f\xe2\x4b\xbe\xd9\xe4\xeb\xe2\xf1\x3d\x0e\x56\x3a\x4e\x02\xda\xd3\x88\xb2\xbb\xde\x59\xaa\x71\x30\x21\x18\x2f\x47\x70\xa3\x84\xcf\xd7\x9b\xab\x4f\xf9\xba\xc8\x7f\x59\xdd\xae\x8a\x47\x70\xc0\xcd\xaa\x58\x5f\x6f\xb7\xb8\xb9\xdb\x20\xc7\x7d\xbe\x29\x56\x57\x0f\xb7\xf9\x06\xf7\x0f\x9b\xfb\xbb\xed\xf5\x02\x5b\x52\x57\xa4\xfa\xff\xce\xbc\x19\x6e\x2f\x10\x6a\x12\x63\x5d\x9c\x92\x78\xe4\x84\xd8\x71\x72\x35\x3a\xb3\x27\x04\xaa\xc8\xee\xa9\x86\x41\xc5\xfd\xf1\xbb\x2f\x55\x59\xc6\xb1\x6f\x87\x99\xff\x71\x21\xb1\x6a\xe0\x59\xe6\x88\x44\xf8\xa9\x13\xe9\x97\x59\x76\x38\x1c\x16\xad\x4f\x0b\x0e\x6d\xe6\x46\x5c\xcc\x7e\x5e\xcc\x66\xdf\x66\x00\x90\x65\xe8\x6c\x14\xbd\x1c\xc5\xee\x4c\xaf\xae\xb8\xaf\xb8\xa6\x08\x61\x54\x9c\xbc\x50\x88\x43\xb5\x96\x2e\xf1\xed\x69\x3e\x69\x3d\xf7\x71\x2c\x89\xf0\x69\x57\x52\x18\xe5\x63\xb9\x9e\x2e\xf1\xf6\xb9\x3a\x0a\xf5\xda\xc9\xfa\x3d\xff\x41\xf5\x90\x1b\xed\x29\x1c\x4f\x0d\xc7\x3d\x50\x1f\xbf\x7e\x06\x7d\xa5\x2a\x09\xc5\xc5\xa0\x56\xe9\x12\x4d\xf2\x95\x6e\xdf\x85\xe3\x76\x8e\xba\xbc\xc4\x38\x85\x3e\x7b\xa3\xbb\x89\x0f\x70\xdc\x2e\xb8\x5f\x08\x6f\x25\x58\xdf\x5e\x5c\xbe\x7f\xae\xb1\x0d\x2e\xa4\xb3\x71\xa1\x83\xfc\xc6\xfd\xef\x97\x67\xbd\x3e\x7f\x39\x7b\xf3\xe6\x2c\x7c\x7a\xfe\x45\x2e\x12\xfe\x45\x85\x0f\x78\xf7\x9a\x6e\x28\xd2\x40\x26\xec\x39\xc4\xc6\x24\x27\x2f\x73\x39\x74\xa7\x8d\x36\x95\x24\xe3\x4e\x51\xe8\xdb\xc9\x0d\x8c\x9f\xd2\x6a\xc6\x5d\x53\xca\x80\x78\x35\x9f\xa7\xf9\x6c\xea\x13\x28\xbe\xd6\xc8\x38\x37\x34\x9b\x2e\x7d\x58\xd5\x92\xc8\xc3\x0a\x05\xa3\xef\x2a\xef\x29\xe8\x67\x0a\x81\x24\x05\x1f\x27\xa2\xca\x1a\xeb\x8d\x9b\xd8\xa7\x8d\x96\x60\x2a\xeb\xdb\xd1\xdb\x78\xf4\xc2\x5c\x25\x5f\x5f\x5e\xdc\xc8\x3c\xa7\xf8\x1c\xcf\xd3\xec\xcf\x00\x00\x00\xff\xff\xf1\x91\x30\xae\xbd\x05\x00\x00") func unigram_tracerJsBytes() ([]byte, error) { return bindataRead( diff --git a/eth/tracers/internal/tracers/call_tracer_parity.js b/eth/tracers/internal/tracers/call_tracer_parity.js index c32a11e33c..3a81228aaf 100644 --- a/eth/tracers/internal/tracers/call_tracer_parity.js +++ b/eth/tracers/internal/tracers/call_tracer_parity.js @@ -312,7 +312,7 @@ }; var extraCtx = { blockHash: ctx.blockHash, - blockNumber: ctx.blockNumber, + blockNumber: ctx.block, transactionHash: ctx.transactionHash, transactionPosition: ctx.transactionPosition, }; @@ -368,7 +368,7 @@ subtraces: 0, transactionPosition: extraCtx.transactionPosition, transactionHash: extraCtx.transactionHash, - blockNumber: call.block || extraCtx.blockNumber, + blockNumber: extraCtx.blockNumber, blockHash: extraCtx.blockHash, time: call.time, } diff --git a/eth/tracers/internal/tracers/prestate_tracer.js b/eth/tracers/internal/tracers/prestate_tracer.js index 8079fd46f9..711f184929 100644 --- a/eth/tracers/internal/tracers/prestate_tracer.js +++ b/eth/tracers/internal/tracers/prestate_tracer.js @@ -15,7 +15,7 @@ // along with the go-ethereum library. If not, see . // prestateTracer outputs sufficient information to create a local execution of -// the transaction from a custom assembled genesisT block. +// the transaction from a custom assembled genesis block. { // prestate is the genesisT that we're building. prestate: {}, diff --git a/eth/tracers/internal/tracers/unigram_tracer.js b/eth/tracers/internal/tracers/unigram_tracer.js index 000fb13b1e..51107d8f3d 100644 --- a/eth/tracers/internal/tracers/unigram_tracer.js +++ b/eth/tracers/internal/tracers/unigram_tracer.js @@ -36,8 +36,6 @@ // result is invoked when all the opcodes have been iterated over and returns // the final result of the tracing. result: function(ctx) { - if(this.nops > 0){ - return this.hist; - } + return this.hist; }, } diff --git a/eth/tracers/testdata/call_tracer_big_slow.json b/eth/tracers/testdata/call_tracer_big_slow.json index 63376144d0..d02599e311 100644 --- a/eth/tracers/testdata/call_tracer_big_slow.json +++ b/eth/tracers/testdata/call_tracer_big_slow.json @@ -20,44 +20,18 @@ } }, "config": { - "networkId": 1, - "chainId": 61, - "eip2FBlock": 1150000, - "eip7FBlock": 1150000, - "eip150Block": 2500000, + "chainId": 1, + "daoForkSupport": true, + "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 3000000, - "eip160Block": 3000000, - "eip161FBlock": 8772000, - "eip170FBlock": 8772000, - "eip100FBlock": 8772000, - "eip140FBlock": 8772000, - "eip198FBlock": 8772000, - "eip211FBlock": 8772000, - "eip212FBlock": 8772000, - "eip213FBlock": 8772000, - "eip214FBlock": 8772000, - "eip658FBlock": 8772000, - "eip145FBlock": 9573000, - "eip1014FBlock": 9573000, - "eip1052FBlock": 9573000, - "eip152FBlock": 10500839, - "eip1108FBlock": 10500839, - "eip1344FBlock": 10500839, - "eip1884FBlock": 10500839, - "eip2028FBlock": 10500839, - "eip2200FBlock": 10500839, - "ecip1010PauseBlock": 3000000, - "ecip1010Length": 2000000, - "ecip1017FBlock": 5000000, - "ecip1017EraRounds": 5000000, - "ecip1099FBlock": 11700000, - "ecbp1100FBlock": 11380000, - "disposalBlock": 5900000, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "1920000": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f", - "2500000": "0xca12c63534f565899681965528d536c52cb05b7c48e269c2a6cb77ad864d878a" - } + "homesteadBlock": 1150000, + "byzantiumBlock": 8772000, + "constantinopleBlock": 9573000, + "petersburgBlock": 10500839, + "istanbulBlock": 10500839 } }, "context": { diff --git a/eth/tracers/testdata/parity_call_tracer_big_slow.json b/eth/tracers/testdata/parity_call_tracer_big_slow.json index d5638ce46c..7ac393a3c3 100644 --- a/eth/tracers/testdata/parity_call_tracer_big_slow.json +++ b/eth/tracers/testdata/parity_call_tracer_big_slow.json @@ -20,44 +20,18 @@ } }, "config": { - "networkId": 1, - "chainId": 61, - "eip2FBlock": 1150000, - "eip7FBlock": 1150000, - "eip150Block": 2500000, + "chainId": 1, + "daoForkSupport": true, + "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 3000000, - "eip160Block": 3000000, - "eip161FBlock": 8772000, - "eip170FBlock": 8772000, - "eip100FBlock": 8772000, - "eip140FBlock": 8772000, - "eip198FBlock": 8772000, - "eip211FBlock": 8772000, - "eip212FBlock": 8772000, - "eip213FBlock": 8772000, - "eip214FBlock": 8772000, - "eip658FBlock": 8772000, - "eip145FBlock": 9573000, - "eip1014FBlock": 9573000, - "eip1052FBlock": 9573000, - "eip152FBlock": 10500839, - "eip1108FBlock": 10500839, - "eip1344FBlock": 10500839, - "eip1884FBlock": 10500839, - "eip2028FBlock": 10500839, - "eip2200FBlock": 10500839, - "ecip1010PauseBlock": 3000000, - "ecip1010Length": 2000000, - "ecip1017FBlock": 5000000, - "ecip1017EraRounds": 5000000, - "ecip1099FBlock": 11700000, - "ecbp1100FBlock": 11380000, - "disposalBlock": 5900000, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "1920000": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f", - "2500000": "0xca12c63534f565899681965528d536c52cb05b7c48e269c2a6cb77ad864d878a" - } + "homesteadBlock": 1150000, + "byzantiumBlock": 8772000, + "constantinopleBlock": 9573000, + "petersburgBlock": 10500839, + "istanbulBlock": 10500839 } }, "context": { diff --git a/eth/tracers/testdata/parity_call_tracer_callcode_precompiled_fail_hide.json b/eth/tracers/testdata/parity_call_tracer_callcode_precompiled_fail_hide.json index dd3d70b0e0..cb37073a25 100644 --- a/eth/tracers/testdata/parity_call_tracer_callcode_precompiled_fail_hide.json +++ b/eth/tracers/testdata/parity_call_tracer_callcode_precompiled_fail_hide.json @@ -26,42 +26,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/parity_call_tracer_callcode_precompiled_oog.json b/eth/tracers/testdata/parity_call_tracer_callcode_precompiled_oog.json index 62667a78de..30bb52bef9 100644 --- a/eth/tracers/testdata/parity_call_tracer_callcode_precompiled_oog.json +++ b/eth/tracers/testdata/parity_call_tracer_callcode_precompiled_oog.json @@ -26,42 +26,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/parity_call_tracer_callcode_precompiled_throw.json b/eth/tracers/testdata/parity_call_tracer_callcode_precompiled_throw.json index 0343e60010..382dae3189 100644 --- a/eth/tracers/testdata/parity_call_tracer_callcode_precompiled_throw.json +++ b/eth/tracers/testdata/parity_call_tracer_callcode_precompiled_throw.json @@ -26,42 +26,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/parity_call_tracer_gas.json b/eth/tracers/testdata/parity_call_tracer_gas.json index d574c1a991..70ca26810b 100644 --- a/eth/tracers/testdata/parity_call_tracer_gas.json +++ b/eth/tracers/testdata/parity_call_tracer_gas.json @@ -26,42 +26,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/parity_call_tracer_nested_create.json b/eth/tracers/testdata/parity_call_tracer_nested_create.json index 1b92d42095..61ca0bc265 100644 --- a/eth/tracers/testdata/parity_call_tracer_nested_create.json +++ b/eth/tracers/testdata/parity_call_tracer_nested_create.json @@ -26,42 +26,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/parity_call_tracer_nested_create2_action_gas.json b/eth/tracers/testdata/parity_call_tracer_nested_create2_action_gas.json index 6266178318..317251a091 100644 --- a/eth/tracers/testdata/parity_call_tracer_nested_create2_action_gas.json +++ b/eth/tracers/testdata/parity_call_tracer_nested_create2_action_gas.json @@ -26,42 +26,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/parity_call_tracer_nested_create_action_gas.json b/eth/tracers/testdata/parity_call_tracer_nested_create_action_gas.json index 33fd59b2ed..938e7fa88b 100644 --- a/eth/tracers/testdata/parity_call_tracer_nested_create_action_gas.json +++ b/eth/tracers/testdata/parity_call_tracer_nested_create_action_gas.json @@ -26,42 +26,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/parity_call_tracer_nested_create_inerror.json b/eth/tracers/testdata/parity_call_tracer_nested_create_inerror.json index f6c8df15d7..d47213576a 100644 --- a/eth/tracers/testdata/parity_call_tracer_nested_create_inerror.json +++ b/eth/tracers/testdata/parity_call_tracer_nested_create_inerror.json @@ -32,42 +32,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/parity_call_tracer_result_output.json b/eth/tracers/testdata/parity_call_tracer_result_output.json index b78465573a..03b8fed44f 100644 --- a/eth/tracers/testdata/parity_call_tracer_result_output.json +++ b/eth/tracers/testdata/parity_call_tracer_result_output.json @@ -39,42 +39,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/parity_call_tracer_selfdestruct.json b/eth/tracers/testdata/parity_call_tracer_selfdestruct.json index 53a5ef31ae..b97818931a 100644 --- a/eth/tracers/testdata/parity_call_tracer_selfdestruct.json +++ b/eth/tracers/testdata/parity_call_tracer_selfdestruct.json @@ -26,42 +26,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/parity_call_tracer_skip_no_balance_error.json b/eth/tracers/testdata/parity_call_tracer_skip_no_balance_error.json index 3db990fe8e..abf9818675 100644 --- a/eth/tracers/testdata/parity_call_tracer_skip_no_balance_error.json +++ b/eth/tracers/testdata/parity_call_tracer_skip_no_balance_error.json @@ -26,42 +26,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/parity_call_tracer_staticcall_precompiled.json b/eth/tracers/testdata/parity_call_tracer_staticcall_precompiled.json index ebb8094ea8..92f40019b4 100644 --- a/eth/tracers/testdata/parity_call_tracer_staticcall_precompiled.json +++ b/eth/tracers/testdata/parity_call_tracer_staticcall_precompiled.json @@ -35,42 +35,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/state_diff_tracer_big_slow.json b/eth/tracers/testdata/state_diff_tracer_big_slow.json index e597ee24b0..56061ef701 100644 --- a/eth/tracers/testdata/state_diff_tracer_big_slow.json +++ b/eth/tracers/testdata/state_diff_tracer_big_slow.json @@ -20,42 +20,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/state_diff_tracer_block_gas_limit_pseudo_randomness.json b/eth/tracers/testdata/state_diff_tracer_block_gas_limit_pseudo_randomness.json index 71c787579b..bd4fc50e63 100644 --- a/eth/tracers/testdata/state_diff_tracer_block_gas_limit_pseudo_randomness.json +++ b/eth/tracers/testdata/state_diff_tracer_block_gas_limit_pseudo_randomness.json @@ -30,42 +30,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/state_diff_tracer_died_keep_existed.json b/eth/tracers/testdata/state_diff_tracer_died_keep_existed.json index 3f3d555213..c24e7b5246 100644 --- a/eth/tracers/testdata/state_diff_tracer_died_keep_existed.json +++ b/eth/tracers/testdata/state_diff_tracer_died_keep_existed.json @@ -28,42 +28,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/state_diff_tracer_died_remove_non_existed.json b/eth/tracers/testdata/state_diff_tracer_died_remove_non_existed.json index 7893a0910d..9c1ce57ef1 100644 --- a/eth/tracers/testdata/state_diff_tracer_died_remove_non_existed.json +++ b/eth/tracers/testdata/state_diff_tracer_died_remove_non_existed.json @@ -26,42 +26,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/state_diff_tracer_errInsufficientFundsForTransfer.json b/eth/tracers/testdata/state_diff_tracer_errInsufficientFundsForTransfer.json index 0ad064f283..dfcdffca6f 100644 --- a/eth/tracers/testdata/state_diff_tracer_errInsufficientFundsForTransfer.json +++ b/eth/tracers/testdata/state_diff_tracer_errInsufficientFundsForTransfer.json @@ -32,42 +32,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/state_diff_tracer_errInsufficientFundsForTransfer_and_gas_cost.json b/eth/tracers/testdata/state_diff_tracer_errInsufficientFundsForTransfer_and_gas_cost.json index b20cbb59b2..d53212beaf 100644 --- a/eth/tracers/testdata/state_diff_tracer_errInsufficientFundsForTransfer_and_gas_cost.json +++ b/eth/tracers/testdata/state_diff_tracer_errInsufficientFundsForTransfer_and_gas_cost.json @@ -32,42 +32,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/state_diff_tracer_internal_oog.json b/eth/tracers/testdata/state_diff_tracer_internal_oog.json index b6201f7954..758982ebe3 100644 --- a/eth/tracers/testdata/state_diff_tracer_internal_oog.json +++ b/eth/tracers/testdata/state_diff_tracer_internal_oog.json @@ -38,42 +38,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/testdata/state_diff_tracer_sstore_persistance.json b/eth/tracers/testdata/state_diff_tracer_sstore_persistance.json index d77818b210..809c949c11 100644 --- a/eth/tracers/testdata/state_diff_tracer_sstore_persistance.json +++ b/eth/tracers/testdata/state_diff_tracer_sstore_persistance.json @@ -53,42 +53,18 @@ } }, "config": { - "networkId": 7, "chainId": 63, - "eip2FBlock": 0, - "eip7FBlock": 0, + "daoForkSupport": true, "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 0, - "eip160Block": 0, - "eip161FBlock": 0, - "eip170FBlock": 0, - "eip100FBlock": 0, - "eip140FBlock": 0, - "eip198FBlock": 0, - "eip211FBlock": 0, - "eip212FBlock": 0, - "eip213FBlock": 0, - "eip214FBlock": 0, - "eip658FBlock": 0, - "eip145FBlock": 301243, - "eip1014FBlock": 301243, - "eip1052FBlock": 301243, - "eip152FBlock": 999983, - "eip1108FBlock": 999983, - "eip1344FBlock": 999983, - "eip1884FBlock": 999983, - "eip2028FBlock": 999983, - "eip2200FBlock": 999983, - "ecip1017FBlock": 0, - "ecip1017EraRounds": 2000000, - "ecip1099FBlock": 2520000, - "ecbp1100FBlock": 2380000, - "disposalBlock": 0, + "eip158Block": 0, "ethash": {}, - "requireBlockHashes": { - "840013": "0x2ceada2b191879b71a5bcf2241dd9bc50d6d953f1640e62f9c2cee941dc61c9d", - "840014": "0x8ec29dd692c8985b82410817bac232fc82805b746538d17bc924624fe74a0fcf" - } + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 } }, "context": { diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go index 995d910378..84e0ee8297 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/tracer.go @@ -27,11 +27,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - duktape "gopkg.in/olebedev/go-duktape.v3" + "gopkg.in/olebedev/go-duktape.v3" ) // bigIntegerJS is the minified version of https://github.com/peterolson/BigInteger.js. @@ -294,8 +295,6 @@ func (cw *contractWrapper) pushObject(vm *duktape.Context) { // Tracer provides an implementation of Tracer that evaluates a Javascript // function for each VM execution step. type Tracer struct { - inited bool // Flag whether the context was already inited from the EVM - vm *duktape.Context // Javascript VM instance tracerObject int // Stack index of the tracer JavaScript object @@ -331,7 +330,7 @@ type Tracer struct { // New instantiates a new tracer instance. code specifies a Javascript snippet, // which must evaluate to an expression returning an object with 'step', 'fault' // and 'result' functions. -func New(code string) (*Tracer, error) { +func New(code string, txCtx vm.TxContext) (*Tracer, error) { // Resolve any tracers by name and assemble the tracer object if tracer, ok := tracer(code); ok { code = tracer @@ -353,6 +352,8 @@ func New(code string) (*Tracer, error) { refundValue: new(uint), callTracerCallstackLength: new(uint), } + tracer.ctx["gasPrice"] = txCtx.GasPrice + // Set up builtins for this environment tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int { ctx.PushString(hexutil.Encode(popSlice(ctx))) @@ -570,7 +571,7 @@ func wrapError(context string, err error) error { // CapturePreEVM implements the Tracer interface to bootstrap the tracing context, // before EVM init. This is useful for reading initial balance, state, etc. -func (jst *Tracer) CapturePreEVM(env *vm.EVM, inputs map[string]interface{}) error { +func (jst *Tracer) CapturePreEVM(env *vm.EVM, inputs map[string]interface{}) { jst.dbWrapper.db = env.StateDB for key, val := range inputs { @@ -582,15 +583,13 @@ func (jst *Tracer) CapturePreEVM(env *vm.EVM, inputs map[string]interface{}) err _, err := jst.call("init", "ctx", "db") if err != nil { jst.err = wrapError("init", err) - return nil + return } } - - return nil } // CaptureStart implements the Tracer interface to initialize the tracing operation. -func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { +func (jst *Tracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { jst.ctx["type"] = "CALL" if create { jst.ctx["type"] = "CREATE" @@ -601,127 +600,126 @@ func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create b jst.ctx["gas"] = gas jst.ctx["value"] = value - return nil + // Initialize the context + jst.ctx["block"] = env.Context.BlockNumber.Uint64() + jst.dbWrapper.db = env.StateDB + // Compute intrinsic gas + hasEIP2 := env.ChainConfig().IsEnabled(env.ChainConfig().GetEIP2Transition, env.Context.BlockNumber) + hasEIP2028 := env.ChainConfig().IsEnabled(env.ChainConfig().GetEIP2028Transition, env.Context.BlockNumber) + intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", hasEIP2, hasEIP2028) + if err != nil { + return + } + jst.ctx["intrinsicGas"] = intrinsicGas } // CaptureState implements the Tracer interface to trace a single step of VM execution. -func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rdata []byte, contract *vm.Contract, depth int, err error) error { - if jst.err == nil { - // Initialize the context if it wasn't done yet - if !jst.inited { - jst.ctx["block"] = env.BlockNumber.Uint64() - jst.inited = true - } - // If tracing was interrupted, set the error and stop - if atomic.LoadUint32(&jst.interrupt) > 0 { - jst.err = jst.reason - return nil - } +func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { + if jst.err != nil { + return + } + // If tracing was interrupted, set the error and stop + if atomic.LoadUint32(&jst.interrupt) > 0 { + jst.err = jst.reason + return + } + jst.opWrapper.op = op + jst.stackWrapper.stack = scope.Stack + jst.memoryWrapper.memory = scope.Memory + jst.contractWrapper.contract = scope.Contract + + *jst.pcValue = uint(pc) + *jst.gasValue = uint(gas) + *jst.availableGasValue = uint(env.CallGasTemp) + *jst.costValue = uint(cost) + *jst.depthValue = uint(depth) + *jst.returnData = rData + *jst.refundValue = uint(env.StateDB.GetRefund()) + + jst.opErrorValue = nil + if env.CallErrorTemp != nil { + jst.opErrorValue = new(string) + *jst.opErrorValue = env.CallErrorTemp.Error() - jst.opWrapper.op = op - jst.stackWrapper.stack = stack - jst.memoryWrapper.memory = memory - jst.contractWrapper.contract = contract - jst.dbWrapper.db = env.StateDB - - *jst.pcValue = uint(pc) - *jst.gasValue = uint(gas) - *jst.availableGasValue = uint(env.CallGasTemp) - *jst.costValue = uint(cost) - *jst.depthValue = uint(depth) - *jst.returnData = rdata - *jst.refundValue = uint(env.StateDB.GetRefund()) - - jst.opErrorValue = nil - if env.CallErrorTemp != nil { - jst.opErrorValue = new(string) - *jst.opErrorValue = env.CallErrorTemp.Error() - - env.CallErrorTemp = nil // clean temp error storage, for debug tracing - } - jst.errorValue = nil - if err != nil { - jst.errorValue = new(string) - *jst.errorValue = err.Error() - } + env.CallErrorTemp = nil // clean temp error storage, for debug tracing + } + jst.errorValue = nil + if err != nil { + jst.errorValue = new(string) + *jst.errorValue = err.Error() + } + + // Checks wether tracer supports `getCallstackLength` method in order to achieve optimal performance for call_tracer* + // in which case it checks if the call to `step` method has to be made, as the duktape prop call is an expensive operation + if jst.supportsStepPerfOptimisations { + run := false - // Checks wether tracer supports `getCallstackLength` method in order to achieve optimal performance for call_tracer* - // in which case it checks if the call to `step` method has to be made, as the duktape prop call is an expensive operation - if jst.supportsStepPerfOptimisations { - run := false - - if jst.callTracerCallstackLength == nil { - jst.vm.PushString("getCallstackLength") - code := jst.vm.PcallProp(jst.tracerObject, 0) - if code != 0 { - jst.vm.Pop() - err := jst.vm.SafeToString(-1) - jst.err = wrapError("step", errors.New(err)) - return nil - } - - jst.callTracerCallstackLength = new(uint) - *jst.callTracerCallstackLength = jst.vm.GetUint(-1) + if jst.callTracerCallstackLength == nil { + jst.vm.PushString("getCallstackLength") + code := jst.vm.PcallProp(jst.tracerObject, 0) + if code != 0 { jst.vm.Pop() + err := jst.vm.SafeToString(-1) + jst.err = wrapError("step", errors.New(err)) + return } - if *jst.callTracerCallstackLength-1 == uint(depth) { - run = true - } else if jst.handleNextOpCode { - jst.handleNextOpCode = false - run = true - } else if op&0xf0 == 0xf0 { - jst.handleNextOpCode = true - run = true - } + jst.callTracerCallstackLength = new(uint) + *jst.callTracerCallstackLength = jst.vm.GetUint(-1) + jst.vm.Pop() + } - if !run { - return nil - } else { - jst.callTracerCallstackLength = nil - } + if *jst.callTracerCallstackLength-1 == uint(depth) { + run = true + } else if jst.handleNextOpCode { + jst.handleNextOpCode = false + run = true + } else if op&0xf0 == 0xf0 { + jst.handleNextOpCode = true + run = true } - _, err := jst.call("step", "log", "db") - if err != nil { - jst.err = wrapError("step", err) + if !run { + return + } else { + jst.callTracerCallstackLength = nil } } - return nil + + if _, err := jst.call("step", "log", "db"); err != nil { + jst.err = wrapError("step", err) + } } // CaptureFault implements the Tracer interface to trace an execution fault -// while running an opcode. -func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error { - if jst.err == nil { - // clean up op error when CaptureFault is being called directly - jst.opErrorValue = nil - env.CallErrorTemp = nil // clean temp error storage, for debug tracing +func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { + if jst.err != nil { + return + } + // clean up op error when CaptureFault is being called directly + jst.opErrorValue = nil + env.CallErrorTemp = nil // clean temp error storage, for debug tracing - // Apart from the error, everything matches the previous invocation - jst.errorValue = new(string) - *jst.errorValue = err.Error() + // Apart from the error, everything matches the previous invocation + jst.errorValue = new(string) + *jst.errorValue = err.Error() - _, err := jst.call("fault", "log", "db") - if err != nil { - jst.err = wrapError("fault", err) - } + if _, err := jst.call("fault", "log", "db"); err != nil { + jst.err = wrapError("fault", err) } - return nil } // CaptureEnd is called after the call finishes to finalize the tracing. -func (jst *Tracer) CaptureEnd(env *vm.EVM, output []byte, gasUsed uint64, t time.Duration, err error) error { +func (jst *Tracer) CaptureEnd(env *vm.EVM, output []byte, gasUsed uint64, t time.Duration, err error) { jst.dbWrapper.db = env.StateDB jst.ctx["output"] = output - jst.ctx["gasUsed"] = gasUsed jst.ctx["time"] = t.String() + jst.ctx["gasUsed"] = gasUsed if err != nil { jst.ctx["error"] = err.Error() } - return nil } // addCtxIntoState adds/updates the ctx vars in the duktape context diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go index b4de998651..ac449fadbc 100644 --- a/eth/tracers/tracer_test.go +++ b/eth/tracers/tracer_test.go @@ -17,7 +17,6 @@ package tracers import ( - "bytes" "encoding/json" "errors" "math/big" @@ -48,96 +47,80 @@ type dummyStatedb struct { state.StateDB } -func (*dummyStatedb) GetRefund() uint64 { return 1337 } +func (*dummyStatedb) GetRefund() uint64 { return 1337 } +func (*dummyStatedb) GetBalance(addr common.Address) *big.Int { return new(big.Int) } -func runTrace(tracer *Tracer) (json.RawMessage, error) { - env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) - - contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000) - contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} - - _, err := env.Interpreter().Run(contract, []byte{}, false) - if err != nil { - return nil, err - } - return tracer.GetResult() -} - -// TestRegressionPanicSlice tests that we don't panic on bad arguments to memory access -func TestRegressionPanicSlice(t *testing.T) { - tracer, err := New("{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}") - if err != nil { - t.Fatal(err) - } - if _, err = runTrace(tracer); err != nil { - t.Fatal(err) - } -} - -// TestRegressionPanicSlice tests that we don't panic on bad arguments to stack peeks -func TestRegressionPanicPeek(t *testing.T) { - tracer, err := New("{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}") - if err != nil { - t.Fatal(err) - } - if _, err = runTrace(tracer); err != nil { - t.Fatal(err) - } -} - -// TestRegressionPanicSlice tests that we don't panic on bad arguments to memory getUint -func TestRegressionPanicGetUint(t *testing.T) { - tracer, err := New("{ depths: [], step: function(log, db) { this.depths.push(log.memory.getUint(-64));}, fault: function() {}, result: function() { return this.depths; }}") - if err != nil { - t.Fatal(err) - } - if _, err = runTrace(tracer); err != nil { - t.Fatal(err) - } +type vmContext struct { + blockCtx vm.BlockContext + txCtx vm.TxContext } -func TestTracing(t *testing.T) { - tracer, err := New("{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}") - if err != nil { - t.Fatal(err) - } - - ret, err := runTrace(tracer) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(ret, []byte("3")) { - t.Errorf("Expected return value to be 3, got %s", string(ret)) - } +func testCtx() *vmContext { + return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} } -func TestStack(t *testing.T) { - tracer, err := New("{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, fault: function() {}, result: function() { return this.depths; }}") - if err != nil { - t.Fatal(err) - } +func runTrace(tracer *Tracer, vmctx *vmContext) (json.RawMessage, error) { + env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) + var ( + startGas uint64 = 10000 + value = big.NewInt(0) + ) + contract := vm.NewContract(account{}, account{}, value, startGas) + contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} - ret, err := runTrace(tracer) + tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value) + ret, err := env.Interpreter().Run(contract, []byte{}, false) + tracer.CaptureEnd(env, ret, startGas-contract.Gas, 1, err) if err != nil { - t.Fatal(err) - } - if !bytes.Equal(ret, []byte("[0,1,2]")) { - t.Errorf("Expected return value to be [0,1,2], got %s", string(ret)) + return nil, err } + return tracer.GetResult() } -func TestOpcodes(t *testing.T) { - tracer, err := New("{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}") - if err != nil { - t.Fatal(err) - } - - ret, err := runTrace(tracer) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(ret, []byte("[\"PUSH1\",\"PUSH1\",\"STOP\"]")) { - t.Errorf("Expected return value to be [\"PUSH1\",\"PUSH1\",\"STOP\"], got %s", string(ret)) +func TestTracer(t *testing.T) { + execTracer := func(code string) []byte { + t.Helper() + ctx := &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} + tracer, err := New(code, ctx.txCtx) + if err != nil { + t.Fatal(err) + } + ret, err := runTrace(tracer, ctx) + if err != nil { + t.Fatal(err) + } + return ret + } + for i, tt := range []struct { + code string + want string + }{ + { // tests that we don't panic on bad arguments to memory access + code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}", + want: `[{},{},{}]`, + }, { // tests that we don't panic on bad arguments to stack peeks + code: "{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}", + want: `["0","0","0"]`, + }, { // tests that we don't panic on bad arguments to memory getUint + code: "{ depths: [], step: function(log, db) { this.depths.push(log.memory.getUint(-64));}, fault: function() {}, result: function() { return this.depths; }}", + want: `["0","0","0"]`, + }, { // tests some general counting + code: "{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}", + want: `3`, + }, { // tests that depth is reported correctly + code: "{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, fault: function() {}, result: function() { return this.depths; }}", + want: `[0,1,2]`, + }, { // tests to-string of opcodes + code: "{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}", + want: `["PUSH1","PUSH1","STOP"]`, + }, { // tests intrinsic gas + code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasPrice+'.'+ctx.gasUsed+'.'+ctx.intrinsicGas; }}", + want: `"100000.6.21000"`, + }, + } { + if have := execTracer(tt.code); tt.want != string(have) { + t.Errorf("testcase %d: expected return value to be %s got %s\n\tcode: %v", i, tt.want, string(have), tt.code) + } } } @@ -145,7 +128,8 @@ func TestHalt(t *testing.T) { t.Skip("duktape doesn't support abortion") timeout := errors.New("stahp") - tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}") + vmctx := testCtx() + tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}", vmctx.txCtx) if err != nil { t.Fatal(err) } @@ -155,26 +139,67 @@ func TestHalt(t *testing.T) { tracer.Stop(timeout) }() - if _, err = runTrace(tracer); err.Error() != "stahp in server-side tracer function 'step'" { + if _, err = runTrace(tracer, vmctx); err.Error() != "stahp in server-side tracer function 'step'" { t.Errorf("Expected timeout error, got %v", err) } } func TestHaltBetweenSteps(t *testing.T) { - tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}") + vmctx := testCtx() + tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}", vmctx.txCtx) if err != nil { t.Fatal(err) } + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) + scope := &vm.ScopeContext{ + Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), + } - env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) - contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0) - - tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil) + tracer.CaptureState(env, 0, 0, 0, 0, scope, nil, 0, nil) timeout := errors.New("stahp") tracer.Stop(timeout) - tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil) + tracer.CaptureState(env, 0, 0, 0, 0, scope, nil, 0, nil) if _, err := tracer.GetResult(); err.Error() != timeout.Error() { t.Errorf("Expected timeout error, got %v", err) } } + +// TestNoStepExec tests a regular value transfer (no exec), and accessing the statedb +// in 'result' +func TestNoStepExec(t *testing.T) { + runEmptyTrace := func(tracer *Tracer, vmctx *vmContext) (json.RawMessage, error) { + env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) + startGas := uint64(10000) + contract := vm.NewContract(account{}, account{}, big.NewInt(0), startGas) + tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, big.NewInt(0)) + tracer.CaptureEnd(env, nil, startGas-contract.Gas, 1, nil) + return tracer.GetResult() + } + execTracer := func(code string) []byte { + t.Helper() + ctx := &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} + tracer, err := New(code, ctx.txCtx) + if err != nil { + t.Fatal(err) + } + ret, err := runEmptyTrace(tracer, ctx) + if err != nil { + t.Fatal(err) + } + return ret + } + for i, tt := range []struct { + code string + want string + }{ + { // tests that we don't panic on accessing the db methods + code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx, db){ return db.getBalance(ctx.to)} }", + want: `"0"`, + }, + } { + if have := execTracer(tt.code); tt.want != string(have) { + t.Errorf("testcase %d: expected return value to be %s got %s\n\tcode: %v", i, tt.want, string(have), tt.code) + } + } +} diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index d28357ea5d..bd8e54bed0 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -192,16 +192,18 @@ func TestPrestateTracerCreate2(t *testing.T) { result: 0x60f3f640a8508fC6a86d45DF051962668E1e8AC7 */ origin, _ := signer.Sender(tx) - context := vm.Context{ + txContext := vm.TxContext{ + Origin: origin, + GasPrice: big.NewInt(1), + } + context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, - Origin: origin, Coinbase: common.Address{}, BlockNumber: new(big.Int).SetUint64(8000000), Time: new(big.Int).SetUint64(5), Difficulty: big.NewInt(0x30000), GasLimit: uint64(6000000), - GasPrice: big.NewInt(1), } alloc := genesisT.GenesisAlloc{} @@ -220,11 +222,11 @@ func TestPrestateTracerCreate2(t *testing.T) { _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false) // Create the tracer, the EVM environment and run it - tracer, err := New("prestateTracer") + tracer, err := New("prestateTracer", txContext) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) msg, err := tx.AsMessage(signer) if err != nil { @@ -279,26 +281,27 @@ func TestCallTracer(t *testing.T) { } signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) origin, _ := signer.Sender(tx) - - context := vm.Context{ + txContext := vm.TxContext{ + Origin: origin, + GasPrice: tx.GasPrice(), + } + context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, - Origin: origin, Coinbase: test.Context.Miner, BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), Time: new(big.Int).SetUint64(uint64(test.Context.Time)), Difficulty: (*big.Int)(test.Context.Difficulty), GasLimit: uint64(test.Context.GasLimit), - GasPrice: tx.GasPrice(), } _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false) // Create the tracer, the EVM environment and run it - tracer, err := New("callTracer") + tracer, err := New("callTracer", txContext) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) msg, err := tx.AsMessage(signer) if err != nil { @@ -364,26 +367,27 @@ func callTracerParityTestRunner(filename string) error { } signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) origin, _ := signer.Sender(tx) - - context := vm.Context{ + txContext := vm.TxContext{ + Origin: origin, + GasPrice: tx.GasPrice(), + } + context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, - Origin: origin, Coinbase: test.Context.Miner, BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), Time: new(big.Int).SetUint64(uint64(test.Context.Time)), Difficulty: (*big.Int)(test.Context.Difficulty), GasLimit: uint64(test.Context.GasLimit), - GasPrice: tx.GasPrice(), } _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false) // Create the tracer, the EVM environment and run it - tracer, err := New("callTracerParity") + tracer, err := New("callTracerParity", txContext) if err != nil { return fmt.Errorf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) msg, err := tx.AsMessage(signer) if err != nil { @@ -523,16 +527,18 @@ func stateDiffTracerTestRunner(filename string) error { core.Transfer(db, sender, recipient, toAmount) } - context := vm.Context{ + txContext := vm.TxContext{ + Origin: msg.From(), + GasPrice: msg.GasPrice(), + } + context := vm.BlockContext{ CanTransfer: canTransfer, Transfer: transfer, - Origin: msg.From(), Coinbase: test.Context.Miner, BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), Time: new(big.Int).SetUint64(uint64(test.Context.Time)), Difficulty: (*big.Int)(test.Context.Difficulty), GasLimit: uint64(test.Context.GasLimit), - GasPrice: msg.GasPrice(), } _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false) @@ -559,11 +565,11 @@ func stateDiffTracerTestRunner(filename string) error { } // Create the tracer, the EVM environment and run it - tracer, err := New("stateDiffTracer") + tracer, err := New("stateDiffTracer", txContext) if err != nil { return fmt.Errorf("failed to create state diff tracer: %v", err) } - evm := vm.NewEVM(context, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) tracer.CapturePreEVM(evm, taskExtraContext) diff --git a/ethclient/docsgen_test.go b/ethclient/docsgen_test.go index e419506856..8449feadcd 100644 --- a/ethclient/docsgen_test.go +++ b/ethclient/docsgen_test.go @@ -110,6 +110,19 @@ func TestRPCDiscover_BuildStatic(t *testing.T) { "trimNameSpecialChars": trimNameSpecialChars, // contentDescriptorTitle returns the name or description, in that order. "contentDescriptorGenericName": contentDescriptorGenericName, + // isSubscribeMethod checks whether the method is a `*_subscribe` method + "isSubscribeMethod": func(m *meta_schema.MethodObject) bool { + return *m.Result.ContentDescriptorObject.Name == "subscriptionID" + }, + // isSubscriptionableMethod checks whether the method returns a subscription + "isSubscriptionableMethod": func(m *meta_schema.MethodObject) bool { + return *m.Result.ContentDescriptorObject.Description == "*rpc.Subscription" + }, + // isFilterMethod checks whether the method is being used as a filter. + "isFilterMethod": func(m *meta_schema.MethodObject) bool { + // TODO: later, check for `*filters.PublicFilterAPI` only + return *m.Result.ContentDescriptorObject.Name == "rpcID" + }, // methodFormatJSConsole is a pretty-printer that returns the JS console use example for a method. "methodFormatJSConsole": func(m *meta_schema.MethodObject) string { name := string(*m.Name) @@ -145,7 +158,41 @@ func TestRPCDiscover_BuildStatic(t *testing.T) { paramNames = strings.Join(out, ", ") } - return fmt.Sprintf(`curl -X POST http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "%s", "params": [%s]}'`, *m.Name, paramNames) + return fmt.Sprintf(`curl -X POST -H "Content-Type: application/json" http://localhost:8545 --data '{"jsonrpc": "2.0", "id": 42, "method": "%s", "params": [%s]}'`, *m.Name, paramNames) + }, + // methodFormatWS is a pretty printer that returns the websocket's method invocation example string. + "methodFormatWS": func(m *meta_schema.MethodObject) string { + paramNames := "" + if m.Params != nil { + out := []string{} + for _, p := range *m.Params { + out = append(out, contentDescriptorGenericName(p.ContentDescriptorObject)) + } + paramNames = strings.Join(out, ", ") + } + + return fmt.Sprintf(`wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "%s", "params": [%s]}'`, *m.Name, paramNames) + }, + // methodFormatWSSubscribe is a pretty printer that returns the websocket's method invocation example string using the package `*_subscribe` method. + "methodFormatWSSubscribe": func(m *meta_schema.MethodObject) string { + methodName := string(*m.Name) + outParams := []string{} + + methodParts := strings.Split(methodName, "_") + if len(methodParts) == 2 { + methodName = methodParts[0] + "_subscribe" + outParams = append(outParams, fmt.Sprintf("\"%s\"", methodParts[1])) + } + + paramNames := "" + if m.Params != nil { + for _, p := range *m.Params { + outParams = append(outParams, contentDescriptorGenericName(p.ContentDescriptorObject)) + } + paramNames = strings.Join(outParams, ", ") + } + + return fmt.Sprintf(`wscat -c ws://localhost:8546 -x '{"jsonrpc": "2.0", "id": 1, "method": "%s", "params": [%s]}'`, methodName, paramNames) }, }) @@ -172,7 +219,7 @@ func TestRPCDiscover_BuildStatic(t *testing.T) { + Required: {{ if .required }}✓ Yes{{ else }}No{{- end}} {{ if .deprecated }} + Deprecated: :warning: Yes{{- end}} -{{ if (or (gt (len .schema) 1) .schema.properties) }} +{{ if (or (gt (len .schema) 1) .schema.properties) }} === "Schema" ` + "```" + ` Schema @@ -195,7 +242,7 @@ func TestRPCDiscover_BuildStatic(t *testing.T) { #### Params ({{ .Params | len }}) {{ if gt (.Params | len) 0 }} -{{ if eq $methodmap.paramStructure "by-position" }}Parameters must be given _by position_.{{ else if eq $methodmap.paramStructure "by-name" }}Parameters must be given _by name_.{{ end }} +{{ if eq $methodmap.paramStructure "by-position" }}Parameters must be given _by position_.{{ else if eq $methodmap.paramStructure "by-name" }}Parameters must be given _by name_.{{ end }} {{ range $index, $param := .Params }} {{ $parammap := . | tomap }} __{{ sum $index 1 }}:__ {{ template "contentDescTpl" $parammap }} @@ -217,17 +264,32 @@ _None_ #### Client Method Invocation Examples -=== "Shell" +{{ if and (not (isSubscribeMethod .)) (and (not (isSubscriptionableMethod .)) (not (isFilterMethod .))) }} +=== "Shell HTTP" ` + "```" + ` shell {{ methodFormatCURL . }} ` + "```" + ` +{{ end }} +{{ $shellWSExample := methodFormatWS . }} +{{ if isSubscriptionableMethod . }} +{{ $shellWSExample = methodFormatWSSubscribe . }} +{{ end }} + +=== "Shell WebSocket" + + ` + "```" + ` shell + {{ $shellWSExample }} + ` + "```" + ` + +{{ if and (not (isSubscribeMethod .)) (and (not (isSubscriptionableMethod .)) (not (isFilterMethod .))) }} === "Javascript Console" ` + "```" + ` js {{ methodFormatJSConsole . }} ` + "```" + ` +{{ end }} {{ $docs := .ExternalDocs | tomap }}
Source code diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index b6d49c2466..ec8ae6f1ad 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" ) @@ -527,7 +526,7 @@ func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64 // If the transaction was a contract creation use the TransactionReceipt method to get the // contract address after the transaction has been mined. func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error { - data, err := rlp.EncodeToBytes(tx) + data, err := tx.MarshalBinary() if err != nil { return err } diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 0da6dd60e6..9a4b781a55 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -17,9 +17,11 @@ package ethclient import ( + "bytes" "context" "errors" "fmt" + "log" "math/big" "reflect" "testing" @@ -35,9 +37,12 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/types/genesisT" + "github.com/ethereum/go-ethereum/rpc" meta_schema "github.com/open-rpc/meta-schema" ) @@ -197,12 +202,13 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { t.Fatalf("can't create new node: %v", err) } // Create Ethereum Service - config := ð.Config{Genesis: genesis} + config := ðconfig.Config{Genesis: genesis} config.Ethash.PowMode = ethash.ModeFake ethservice, err := eth.New(n, config) if err != nil { t.Fatalf("can't create new ethereum service: %v", err) } + n.RegisterAPIs(tracers.APIs(ethservice.APIBackend)) // Import the test chain. if err := n.Start(); err != nil { t.Fatalf("can't start test node: %v", err) @@ -233,12 +239,48 @@ func generateTestChain() (*genesisT.Genesis, []*types.Block) { return genesis, blocks } -func TestHeader(t *testing.T) { +func TestEthClient(t *testing.T) { backend, chain := newTestBackend(t) client, _ := backend.Attach() defer backend.Close() defer client.Close() + tests := map[string]struct { + test func(t *testing.T) + }{ + "TestHeader": { + func(t *testing.T) { testHeader(t, chain, client) }, + }, + "TestBalanceAt": { + func(t *testing.T) { testBalanceAt(t, client) }, + }, + "TestTxInBlockInterrupted": { + func(t *testing.T) { testTransactionInBlockInterrupted(t, client) }, + }, + "TestChainID": { + func(t *testing.T) { testChainID(t, client) }, + }, + "TestGetBlock": { + func(t *testing.T) { testGetBlock(t, client) }, + }, + "TestStatusFunctions": { + func(t *testing.T) { testStatusFunctions(t, client) }, + }, + "TestCallContract": { + func(t *testing.T) { testCallContract(t, client) }, + }, + "TestAtFunctions": { + func(t *testing.T) { testAtFunctions(t, client) }, + }, + } + + t.Parallel() + for name, tt := range tests { + t.Run(name, tt.test) + } +} + +func testHeader(t *testing.T, chain []*types.Block, client *rpc.Client) { tests := map[string]struct { block *big.Int want *types.Header @@ -253,8 +295,9 @@ func TestHeader(t *testing.T) { want: chain[1].Header(), }, "future_block": { - block: big.NewInt(1000000000), - want: nil, + block: big.NewInt(1000000000), + want: nil, + wantErr: ethereum.NotFound, }, } for name, tt := range tests { @@ -264,10 +307,10 @@ func TestHeader(t *testing.T) { defer cancel() got, err := ec.HeaderByNumber(ctx, tt.block) - if tt.wantErr != nil && (err == nil || err.Error() != tt.wantErr.Error()) { + if !errors.Is(err, tt.wantErr) { t.Fatalf("HeaderByNumber(%v) error = %q, want %q", tt.block, err, tt.wantErr) } - if got != nil && got.Number.Sign() == 0 { + if got != nil && got.Number != nil && got.Number.Sign() == 0 { got.Number = big.NewInt(0) // hack to make DeepEqual work } if !reflect.DeepEqual(got, tt.want) { @@ -277,12 +320,7 @@ func TestHeader(t *testing.T) { } } -func TestBalanceAt(t *testing.T) { - backend, _ := newTestBackend(t) - client, _ := backend.Attach() - defer backend.Close() - defer client.Close() - +func testBalanceAt(t *testing.T, client *rpc.Client) { tests := map[string]struct { account common.Address block *big.Int @@ -323,31 +361,69 @@ func TestBalanceAt(t *testing.T) { } } -func TestTransactionInBlockInterrupted(t *testing.T) { +func TestHeader_TxesUnclesNotEmpty(t *testing.T) { backend, _ := newTestBackend(t) client, _ := backend.Attach() defer backend.Close() defer client.Close() + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancel() + + res := make(map[string]interface{}) + err := client.CallContext(ctx, &res, "eth_getBlockByNumber", "latest", false) + if err != nil { + log.Fatalln(err) + } + + // Sanity check response + if v, ok := res["number"]; !ok { + t.Fatal("missing 'number' field") + } else if n, err := hexutil.DecodeBig(v.(string)); err != nil || n == nil { + t.Fatal(err) + } else if n.Cmp(big.NewInt(1)) != 0 { + t.Fatalf("unexpected 'latest' block number: %v", n) + } + // 'transactions' key should exist as [] + if v, ok := res["transactions"]; !ok { + t.Fatal("missing transactions field") + } else if len(v.([]interface{})) != 0 { + t.Fatal("'transactions' value not []") + } + // 'uncles' key should exist as [] + if v, ok := res["uncles"]; !ok { + t.Fatal("missing uncles field") + } else if len(v.([]interface{})) != 0 { + t.Fatal("'uncles' value not []'") + } +} + +func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) { ec := NewClient(client) + + // Get current block by number + block, err := ec.BlockByNumber(context.Background(), nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + // Test tx in block interupted ctx, cancel := context.WithCancel(context.Background()) cancel() - tx, err := ec.TransactionInBlock(ctx, common.Hash{1}, 1) + tx, err := ec.TransactionInBlock(ctx, block.Hash(), 1) if tx != nil { t.Fatal("transaction should be nil") } - if err == nil { - t.Fatal("error should not be nil") + if err == nil || err == ethereum.NotFound { + t.Fatal("error should not be nil/notfound") + } + // Test tx in block not found + if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 1); err != ethereum.NotFound { + t.Fatal("error should be ethereum.NotFound") } } -func TestChainID(t *testing.T) { - backend, _ := newTestBackend(t) - client, _ := backend.Attach() - defer backend.Close() - defer client.Close() +func testChainID(t *testing.T, client *rpc.Client) { ec := NewClient(client) - id, err := ec.ChainID(context.Background()) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -357,13 +433,9 @@ func TestChainID(t *testing.T) { } } -func TestBlockNumber(t *testing.T) { - backend, _ := newTestBackend(t) - client, _ := backend.Attach() - defer backend.Close() - defer client.Close() +func testGetBlock(t *testing.T, client *rpc.Client) { ec := NewClient(client) - + // Get current block number blockNumber, err := ec.BlockNumber(context.Background()) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -371,6 +443,179 @@ func TestBlockNumber(t *testing.T) { if blockNumber != 1 { t.Fatalf("BlockNumber returned wrong number: %d", blockNumber) } + // Get current block by number + block, err := ec.BlockByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if block.NumberU64() != blockNumber { + t.Fatalf("BlockByNumber returned wrong block: want %d got %d", blockNumber, block.NumberU64()) + } + // Get current block by hash + blockH, err := ec.BlockByHash(context.Background(), block.Hash()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if block.Hash() != blockH.Hash() { + t.Fatalf("BlockByHash returned wrong block: want %v got %v", block.Hash().Hex(), blockH.Hash().Hex()) + } + // Get header by number + header, err := ec.HeaderByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if block.Header().Hash() != header.Hash() { + t.Fatalf("HeaderByNumber returned wrong header: want %v got %v", block.Header().Hash().Hex(), header.Hash().Hex()) + } + // Get header by hash + headerH, err := ec.HeaderByHash(context.Background(), block.Hash()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if block.Header().Hash() != headerH.Hash() { + t.Fatalf("HeaderByHash returned wrong header: want %v got %v", block.Header().Hash().Hex(), headerH.Hash().Hex()) + } +} + +func testStatusFunctions(t *testing.T, client *rpc.Client) { + ec := NewClient(client) + + // Sync progress + progress, err := ec.SyncProgress(context.Background()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if progress != nil { + t.Fatalf("unexpected progress: %v", progress) + } + // NetworkID + networkID, err := ec.NetworkID(context.Background()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if networkID.Cmp(big.NewInt(0)) != 0 { + t.Fatalf("unexpected networkID: %v", networkID) + } + // SuggestGasPrice (should suggest 1 Gwei) + gasPrice, err := ec.SuggestGasPrice(context.Background()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if gasPrice.Cmp(big.NewInt(1000000000)) != 0 { + t.Fatalf("unexpected gas price: %v", gasPrice) + } +} + +func testCallContract(t *testing.T, client *rpc.Client) { + ec := NewClient(client) + + // EstimateGas + msg := ethereum.CallMsg{ + From: testAddr, + To: &common.Address{}, + Gas: 21000, + GasPrice: big.NewInt(1), + Value: big.NewInt(1), + } + gas, err := ec.EstimateGas(context.Background(), msg) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if gas != 21000 { + t.Fatalf("unexpected gas price: %v", gas) + } + // CallContract + if _, err := ec.CallContract(context.Background(), msg, big.NewInt(1)); err != nil { + t.Fatalf("unexpected error: %v", err) + } + // PendingCallCOntract + if _, err := ec.PendingCallContract(context.Background(), msg); err != nil { + t.Fatalf("unexpected error: %v", err) + } +} + +func testAtFunctions(t *testing.T, client *rpc.Client) { + ec := NewClient(client) + // send a transaction for some interesting pending status + sendTransaction(ec) + time.Sleep(100 * time.Millisecond) + // Check pending transaction count + pending, err := ec.PendingTransactionCount(context.Background()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if pending != 1 { + t.Fatalf("unexpected pending, wanted 1 got: %v", pending) + } + // Query balance + balance, err := ec.BalanceAt(context.Background(), testAddr, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + penBalance, err := ec.PendingBalanceAt(context.Background(), testAddr) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if balance.Cmp(penBalance) == 0 { + t.Fatalf("unexpected balance: %v %v", balance, penBalance) + } + // NonceAt + nonce, err := ec.NonceAt(context.Background(), testAddr, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + penNonce, err := ec.PendingNonceAt(context.Background(), testAddr) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if penNonce != nonce+1 { + t.Fatalf("unexpected nonce: %v %v", nonce, penNonce) + } + // StorageAt + storage, err := ec.StorageAt(context.Background(), testAddr, common.Hash{}, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + penStorage, err := ec.PendingStorageAt(context.Background(), testAddr, common.Hash{}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !bytes.Equal(storage, penStorage) { + t.Fatalf("unexpected storage: %v %v", storage, penStorage) + } + // CodeAt + code, err := ec.CodeAt(context.Background(), testAddr, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + penCode, err := ec.PendingCodeAt(context.Background(), testAddr) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !bytes.Equal(code, penCode) { + t.Fatalf("unexpected code: %v %v", code, penCode) + } +} + +func sendTransaction(ec *Client) error { + // Retrieve chainID + chainID, err := ec.ChainID(context.Background()) + if err != nil { + return err + } + // Create transaction + tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, big.NewInt(1), nil) + signer := types.LatestSignerForChainID(chainID) + signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey) + if err != nil { + return err + } + signedTx, err := tx.WithSignature(signer, signature) + if err != nil { + return err + } + // Send transaction + return ec.SendTransaction(context.Background(), signedTx) } func TestRPCDiscover(t *testing.T) { @@ -394,7 +639,7 @@ func TestRPCDiscover(t *testing.T) { return false } - methodNamesSlice := func() (names []string) { + responseMethods := func() (names []string) { for _, m := range *res.Methods { names = append(names, string(*m.Name)) } @@ -403,13 +648,18 @@ func TestRPCDiscover(t *testing.T) { over, under := []string{}, []string{} - for _, name := range methodNamesSlice { + // under: methods which exist in the response document, + // but are not contained in the canonical hardcoded list below + for _, name := range responseMethods { if !sliceContains(allRPCMethods, name) { under = append(under, name) } } + + // over: methods which DO NOT exist in the response document, + // but ARE contained in the canonical hardcoded list below for _, name := range allRPCMethods { - if !sliceContains(methodNamesSlice, name) { + if !sliceContains(responseMethods, name) { over = append(over, name) } } @@ -634,10 +884,13 @@ var allRPCMethods = []string{ "admin_maxPeers", "admin_nodeInfo", "admin_peers", + "admin_peerEvents", "admin_removePeer", "admin_removeTrustedPeer", + "admin_startHTTP", "admin_startRPC", "admin_startWS", + "admin_stopHTTP", "admin_stopRPC", "admin_stopWS", "debug_accountRange", @@ -672,6 +925,7 @@ var allRPCMethods = []string{ "debug_stopCPUProfile", "debug_stopGoTrace", "debug_storageRangeAt", + "debug_subscribe", "debug_testSignCliqueBlock", "debug_traceBadBlock", "debug_traceBlock", @@ -679,7 +933,10 @@ var allRPCMethods = []string{ "debug_traceBlockByNumber", "debug_traceBlockFromFile", "debug_traceCall", + "debug_traceCallMany", + "debug_traceChain", "debug_traceTransaction", + "debug_unsubscribe", "debug_verbosity", "debug_vmodule", "debug_writeBlockProfile", @@ -689,8 +946,8 @@ var allRPCMethods = []string{ "eth_blockNumber", "eth_call", "eth_chainId", - "eth_chainId", "eth_coinbase", + "eth_createAccessList", "eth_estimateGas", "eth_etherbase", "eth_fillTransaction", @@ -722,20 +979,22 @@ var allRPCMethods = []string{ "eth_getUncleCountByBlockHash", "eth_getUncleCountByBlockNumber", "eth_getWork", - "eth_hashrate", + "eth_logs", "eth_mining", "eth_newBlockFilter", + "eth_newHeads", "eth_newSideBlockFilter", + "eth_newSideHeads", "eth_newFilter", "eth_newPendingTransactionFilter", + "eth_newPendingTransactions", "eth_pendingTransactions", - "eth_protocolVersion", "eth_resend", "eth_sendRawTransaction", "eth_sendTransaction", "eth_sign", "eth_signTransaction", - "eth_submitHashRate", + "eth_submitHashrate", "eth_submitWork", "eth_subscribe", "eth_syncing", @@ -743,9 +1002,8 @@ var allRPCMethods = []string{ "eth_unsubscribe", "ethash_getHashrate", "ethash_getWork", - "ethash_submitHashRate", + "ethash_submitHashrate", "ethash_submitWork", - "miner_getHashrate", "miner_setEtherbase", "miner_setExtra", "miner_setGasPrice", @@ -773,7 +1031,10 @@ var allRPCMethods = []string{ "trace_block", "trace_call", "trace_callMany", + "trace_filter", + "trace_subscribe", "trace_transaction", + "trace_unsubscribe", "txpool_content", "txpool_inspect", "txpool_status", diff --git a/ethclient/signer.go b/ethclient/signer.go index 74a93f1e2f..9de020b352 100644 --- a/ethclient/signer.go +++ b/ethclient/signer.go @@ -51,6 +51,9 @@ func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) return s.addr, nil } +func (s *senderFromServer) ChainID() *big.Int { + panic("can't sign with senderFromServer") +} func (s *senderFromServer) Hash(tx *types.Transaction) common.Hash { panic("can't sign with senderFromServer") } diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go index 80380db325..5d19cc3577 100644 --- a/ethdb/leveldb/leveldb.go +++ b/ethdb/leveldb/leveldb.go @@ -83,25 +83,40 @@ type Database struct { // New returns a wrapped LevelDB object. The namespace is the prefix that the // metrics reporting should use for surfacing internal stats. -func New(file string, cache int, handles int, namespace string) (*Database, error) { - // Ensure we have some minimal caching and file guarantees - if cache < minCache { - cache = minCache - } - if handles < minHandles { - handles = minHandles - } +func New(file string, cache int, handles int, namespace string, readonly bool) (*Database, error) { + return NewCustom(file, namespace, func(options *opt.Options) { + // Ensure we have some minimal caching and file guarantees + if cache < minCache { + cache = minCache + } + if handles < minHandles { + handles = minHandles + } + // Set default options + options.OpenFilesCacheCapacity = handles + options.BlockCacheCapacity = cache / 2 * opt.MiB + options.WriteBuffer = cache / 4 * opt.MiB // Two of these are used internally + if readonly { + options.ReadOnly = true + } + }) +} + +// NewCustom returns a wrapped LevelDB object. The namespace is the prefix that the +// metrics reporting should use for surfacing internal stats. +// The customize function allows the caller to modify the leveldb options. +func NewCustom(file string, namespace string, customize func(options *opt.Options)) (*Database, error) { + options := configureOptions(customize) logger := log.New("database", file) - logger.Info("Allocated cache and file handles", "cache", common.StorageSize(cache*1024*1024), "handles", handles) + usedCache := options.GetBlockCacheCapacity() + options.GetWriteBuffer()*2 + logCtx := []interface{}{"cache", common.StorageSize(usedCache), "handles", options.GetOpenFilesCacheCapacity()} + if options.ReadOnly { + logCtx = append(logCtx, "readonly", "true") + } + logger.Info("Allocated cache and file handles", logCtx...) // Open the db and recover any potential corruptions - db, err := leveldb.OpenFile(file, &opt.Options{ - OpenFilesCacheCapacity: handles, - BlockCacheCapacity: cache / 2 * opt.MiB, - WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internally - Filter: filter.NewBloomFilter(10), - DisableSeeksCompaction: true, - }) + db, err := leveldb.OpenFile(file, options) if _, corrupted := err.(*errors.ErrCorrupted); corrupted { db, err = leveldb.RecoverFile(file, nil) } @@ -133,6 +148,20 @@ func New(file string, cache int, handles int, namespace string) (*Database, erro return ldb, nil } +// configureOptions sets some default options, then runs the provided setter. +func configureOptions(customizeFn func(*opt.Options)) *opt.Options { + // Set default options + options := &opt.Options{ + Filter: filter.NewBloomFilter(10), + DisableSeeksCompaction: true, + } + // Allow caller to make custom modifications to the options + if customizeFn != nil { + customizeFn(options) + } + return options +} + // Close stops the metrics collection, flushes any pending data to disk and closes // all io accesses to the underlying key-value store. func (db *Database) Close() error { @@ -432,7 +461,7 @@ func (b *batch) Put(key, value []byte) error { // Delete inserts the a key removal into the batch for later committing. func (b *batch) Delete(key []byte) error { b.b.Delete(key) - b.size++ + b.size += len(key) return nil } diff --git a/ethdb/memorydb/memorydb.go b/ethdb/memorydb/memorydb.go index 4c5e1a84de..fedc9e326c 100644 --- a/ethdb/memorydb/memorydb.go +++ b/ethdb/memorydb/memorydb.go @@ -211,7 +211,7 @@ func (b *batch) Put(key, value []byte) error { // Delete inserts the a key removal into the batch for later committing. func (b *batch) Delete(key []byte) error { b.writes = append(b.writes, keyvalue{common.CopyBytes(key), nil, true}) - b.size += 1 + b.size += len(key) return nil } diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index f82cffab1f..ef83e5a4eb 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -35,8 +35,8 @@ import ( "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" "github.com/ethereum/go-ethereum/eth/downloader" + ethproto "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/log" @@ -61,7 +61,6 @@ const ( // backend encompasses the bare-minimum functionality needed for ethstats reporting type backend interface { - ProtocolVersion() int SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription CurrentHeader() *types.Header @@ -95,6 +94,8 @@ type Service struct { pongCh chan struct{} // Pong notifications are fed into this channel histCh chan []uint64 // History request block numbers are fed into this channel + headSub event.Subscription + txSub event.Subscription } // connWrapper is a wrapper to prevent concurrent-write or concurrent-read on the @@ -189,7 +190,12 @@ func New(node *node.Node, backend backend, engine consensus.Engine, url string) // Start implements node.Lifecycle, starting up the monitoring and reporting daemon. func (s *Service) Start() error { - go s.loop() + // Subscribe to chain events to execute updates on + chainHeadCh := make(chan core.ChainHeadEvent, chainHeadChanSize) + s.headSub = s.backend.SubscribeChainHeadEvent(chainHeadCh) + txEventCh := make(chan core.NewTxsEvent, txChanSize) + s.txSub = s.backend.SubscribeNewTxsEvent(txEventCh) + go s.loop(chainHeadCh, txEventCh) log.Info("Stats daemon started") return nil @@ -197,22 +203,15 @@ func (s *Service) Start() error { // Stop implements node.Lifecycle, terminating the monitoring and reporting daemon. func (s *Service) Stop() error { + s.headSub.Unsubscribe() + s.txSub.Unsubscribe() log.Info("Stats daemon stopped") return nil } // loop keeps trying to connect to the netstats server, reporting chain events // until termination. -func (s *Service) loop() { - // Subscribe to chain events to execute updates on - chainHeadCh := make(chan core.ChainHeadEvent, chainHeadChanSize) - headSub := s.backend.SubscribeChainHeadEvent(chainHeadCh) - defer headSub.Unsubscribe() - - txEventCh := make(chan core.NewTxsEvent, txChanSize) - txSub := s.backend.SubscribeNewTxsEvent(txEventCh) - defer txSub.Unsubscribe() - +func (s *Service) loop(chainHeadCh chan core.ChainHeadEvent, txEventCh chan core.NewTxsEvent) { // Start a goroutine that exhausts the subscriptions to avoid events piling up var ( quitCh = make(chan struct{}) @@ -245,9 +244,9 @@ func (s *Service) loop() { } // node stopped - case <-txSub.Err(): + case <-s.txSub.Err(): break HandleLoop - case <-headSub.Err(): + case <-s.headSub.Err(): break HandleLoop } } @@ -466,13 +465,15 @@ func (s *Service) login(conn *connWrapper) error { // Construct and send the login authentication infos := s.server.NodeInfo() - var network, protocol string + var protocols []string + for _, proto := range s.server.Protocols { + protocols = append(protocols, fmt.Sprintf("%s/%d", proto.Name, proto.Version)) + } + var network string if info := infos.Protocols["eth"]; info != nil { - network = fmt.Sprintf("%d", info.(*eth.NodeInfo).Network) - protocol = fmt.Sprintf("eth/%d", s.backend.ProtocolVersion()) + network = fmt.Sprintf("%d", info.(*ethproto.NodeInfo).Network) } else { network = fmt.Sprintf("%d", infos.Protocols["les"].(*les.NodeInfo).Network) - protocol = fmt.Sprintf("les/%d", les.ClientProtocolVersions[0]) } auth := &authMsg{ ID: s.node, @@ -481,7 +482,7 @@ func (s *Service) login(conn *connWrapper) error { Node: infos.Name, Port: infos.Ports.Listener, Network: network, - Protocol: protocol, + Protocol: strings.Join(protocols, ", "), API: "No", Os: runtime.GOOS, OsVer: runtime.GOARCH, @@ -774,7 +775,7 @@ func (s *Service) reportStats(conn *connWrapper) error { fullBackend, ok := s.backend.(fullNodeBackend) if ok { mining = fullBackend.Miner().Mining() - hashrate = int(fullBackend.Miner().HashRate()) + hashrate = int(fullBackend.Miner().Hashrate()) sync := fullBackend.Downloader().Progress() syncing = fullBackend.CurrentHeader().Number.Uint64() >= sync.HighestBlock diff --git a/event/event_test.go b/event/event_test.go index cc9fa5d7c8..bdad11f13d 100644 --- a/event/event_test.go +++ b/event/event_test.go @@ -29,7 +29,7 @@ func TestSubCloseUnsub(t *testing.T) { // the point of this test is **not** to panic var mux TypeMux mux.Stop() - sub := mux.Subscribe(int(0)) + sub := mux.Subscribe(0) sub.Unsubscribe() } diff --git a/event/feed_test.go b/event/feed_test.go index 93badaaee6..cdf29fdd73 100644 --- a/event/feed_test.go +++ b/event/feed_test.go @@ -27,8 +27,8 @@ import ( func TestFeedPanics(t *testing.T) { { var f Feed - f.Send(int(2)) - want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(int(0))} + f.Send(2) + want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(0)} if err := checkPanic(want, func() { f.Send(uint64(2)) }); err != nil { t.Error(err) } @@ -37,14 +37,14 @@ func TestFeedPanics(t *testing.T) { var f Feed ch := make(chan int) f.Subscribe(ch) - want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(int(0))} + want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(0)} if err := checkPanic(want, func() { f.Send(uint64(2)) }); err != nil { t.Error(err) } } { var f Feed - f.Send(int(2)) + f.Send(2) want := feedTypeError{op: "Subscribe", got: reflect.TypeOf(make(chan uint64)), want: reflect.TypeOf(make(chan<- int))} if err := checkPanic(want, func() { f.Subscribe(make(chan uint64)) }); err != nil { t.Error(err) @@ -58,7 +58,7 @@ func TestFeedPanics(t *testing.T) { } { var f Feed - if err := checkPanic(errBadChannel, func() { f.Subscribe(int(0)) }); err != nil { + if err := checkPanic(errBadChannel, func() { f.Subscribe(0) }); err != nil { t.Error(err) } } diff --git a/event/subscription.go b/event/subscription.go index c80d171f3a..6c62874719 100644 --- a/event/subscription.go +++ b/event/subscription.go @@ -95,6 +95,26 @@ func (s *funcSub) Err() <-chan error { // Resubscribe applies backoff between calls to fn. The time between calls is adapted // based on the error rate, but will never exceed backoffMax. func Resubscribe(backoffMax time.Duration, fn ResubscribeFunc) Subscription { + return ResubscribeErr(backoffMax, func(ctx context.Context, _ error) (Subscription, error) { + return fn(ctx) + }) +} + +// A ResubscribeFunc attempts to establish a subscription. +type ResubscribeFunc func(context.Context) (Subscription, error) + +// ResubscribeErr calls fn repeatedly to keep a subscription established. When the +// subscription is established, ResubscribeErr waits for it to fail and calls fn again. This +// process repeats until Unsubscribe is called or the active subscription ends +// successfully. +// +// The difference between Resubscribe and ResubscribeErr is that with ResubscribeErr, +// the error of the failing subscription is available to the callback for logging +// purposes. +// +// ResubscribeErr applies backoff between calls to fn. The time between calls is adapted +// based on the error rate, but will never exceed backoffMax. +func ResubscribeErr(backoffMax time.Duration, fn ResubscribeErrFunc) Subscription { s := &resubscribeSub{ waitTime: backoffMax / 10, backoffMax: backoffMax, @@ -106,15 +126,18 @@ func Resubscribe(backoffMax time.Duration, fn ResubscribeFunc) Subscription { return s } -// A ResubscribeFunc attempts to establish a subscription. -type ResubscribeFunc func(context.Context) (Subscription, error) +// A ResubscribeErrFunc attempts to establish a subscription. +// For every call but the first, the second argument to this function is +// the error that occurred with the previous subscription. +type ResubscribeErrFunc func(context.Context, error) (Subscription, error) type resubscribeSub struct { - fn ResubscribeFunc + fn ResubscribeErrFunc err chan error unsub chan struct{} unsubOnce sync.Once lastTry mclock.AbsTime + lastSubErr error waitTime, backoffMax time.Duration } @@ -149,7 +172,7 @@ func (s *resubscribeSub) subscribe() Subscription { s.lastTry = mclock.Now() ctx, cancel := context.WithCancel(context.Background()) go func() { - rsub, err := s.fn(ctx) + rsub, err := s.fn(ctx, s.lastSubErr) sub = rsub subscribed <- err }() @@ -178,6 +201,7 @@ func (s *resubscribeSub) waitForError(sub Subscription) bool { defer sub.Unsubscribe() select { case err := <-sub.Err(): + s.lastSubErr = err return err == nil case <-s.unsub: return true diff --git a/event/subscription_test.go b/event/subscription_test.go index c48be3aa30..ba081705c4 100644 --- a/event/subscription_test.go +++ b/event/subscription_test.go @@ -19,6 +19,8 @@ package event import ( "context" "errors" + "fmt" + "reflect" "testing" "time" ) @@ -118,3 +120,37 @@ func TestResubscribeAbort(t *testing.T) { t.Fatal(err) } } + +func TestResubscribeWithErrorHandler(t *testing.T) { + t.Parallel() + + var i int + nfails := 6 + subErrs := make([]string, 0) + sub := ResubscribeErr(100*time.Millisecond, func(ctx context.Context, lastErr error) (Subscription, error) { + i++ + var lastErrVal string + if lastErr != nil { + lastErrVal = lastErr.Error() + } + subErrs = append(subErrs, lastErrVal) + sub := NewSubscription(func(unsubscribed <-chan struct{}) error { + if i < nfails { + return fmt.Errorf("err-%v", i) + } else { + return nil + } + }) + return sub, nil + }) + + <-sub.Err() + if i != nfails { + t.Fatalf("resubscribe function called %d times, want %d times", i, nfails) + } + + expectedSubErrs := []string{"", "err-1", "err-2", "err-3", "err-4", "err-5"} + if !reflect.DeepEqual(subErrs, expectedSubErrs) { + t.Fatalf("unexpected subscription errors %v, want %v", subErrs, expectedSubErrs) + } +} diff --git a/git.diff b/git.diff new file mode 100644 index 0000000000..b79104caf7 --- /dev/null +++ b/git.diff @@ -0,0 +1,11533 @@ +diff --cc .travis.yml +index bf36aac971,7406f31fe7..0000000000 +--- a/.travis.yml ++++ b/.travis.yml +@@@ -2,156 -2,228 +2,384 @@@ language: g + go_import_path: github.com/ethereum/go-ethereum + sudo: false + jobs: +++<<<<<<< HEAD + + include: + + + + # Run linter check. Only test code linters on latest version of Go. + + - stage: lint + + os: linux + + dist: xenial + + go: 1.16.x + + env: + + - lint + + git: + + submodules: false + + script: + + - make lint + + + + # Run core-geth -specific tests, proving regression-safety and config interoperability. + + - stage: build + + name: "Go1.16.x: make test-coregeth" + + os: linux + + dist: xenial + + go: 1.16.x + + script: + + - make test-coregeth + + + + # Run build and tests against latest-1 Go version. + + - stage: build + + name: "Go1.15.x: make test" + + os: linux + + dist: xenial + + go: 1.15.x + + env: + + - GO111MODULE=on + + script: + + - make all + + - travis_wait 60 make test + + + + # Run build and tests on ARM64 on Pull Requests. + + # These tests are divided in half because their aggregate typical runtime + + # exceeds Travis' time limit (~60m) and were consistently causing timeouts. + + - stage: build + + name: "ARM64/Go1.15.x: make test [A-G]" + + if: type = pull_request + + os: linux + + arch: arm64 + + dist: xenial + + go: 1.16.x + + env: + + - GO111MODULE=on + + script: + + - go run build/ci.go test -coverage $(printf './%s/... ' $(go list ./... | sed 's|github.com/ethereum/go-ethereum||g' | cut -d'/' -f2 | uniq | grep -E '^[a-g]')) + + - stage: build + + name: "ARM64/Go1.15.x: make test [H-Z]" + + if: type = pull_request + + os: linux + + arch: arm64 + + dist: xenial + + go: 1.15.x + + env: + + - GO111MODULE=on + + script: + + - go run build/ci.go test -coverage $(printf './%s/... ' $(go list ./... | sed 's|github.com/ethereum/go-ethereum||g' | cut -d'/' -f2 | uniq | grep -E '^[h-z]')) + + + + # Run build and tests with environment-aware possible artifact deployment. + + - stage: build + + if: type = push + + name: "Go1.16.x: make test && deploy" + + os: linux + + dist: xenial + + go: 1.16.x + + script: + + - go run build/ci.go install -dlgo + + - travis_wait 60 make test + + before_deploy: + + - sh build/deploy.sh + + deploy: + + provider: releases + + api_key: + + secure: f5ewX8iaY8E63wEGtLPZ8JF0/KUssJgAE+lqS7Nx5XxAM/GuE5X7mb9FmOiSzYyOexiftG8pwY0HVSXAaNk6HEl1GqlVsqMfuZgrVOAs8e0ltWgseI2T9qDp/oxxwWQ8cnPS3GrLwYUQTE+XfphL9aKkvt2RhoMmd1FPy6Dp0AYxzfLiZWCucBrR7HyHlIUjuM/DdN5U5te2loGhCyrDaKNeiIHQYp7h99cXFhKgC2N1mvXdIE7jxfiJGyEbmNksO0OY4RqBMNl7unOr8DvFf9dQBUc9cWfSRcAxt2DK4+zamX/0HJYfYRJR2Y2+z1oHrbJQnZm+9ZnOaXJt7bdWvON8jLJS+PLZT0kcSklSkKy3WEulijaGn1SIzEesadhnyHZxeDFgMnjVkSOCS0pDNtIv0SxfayqXvbYHzJhRDC4uVTRb+/PxMtZxIPCYBxLHJF6bkwmEWELl5I7UavN+jxuhkMDatqfN4V/j7JfqrLYFLHT9ByE5IBznizvJdxwXg8eo/RDJIJUwHjmKwsX9XBYmEX/UGe1O8ujJNTmg3acX9ECbwM2zgs1Br5WZtjRRDHT+YY6CbP2kPOCkpGqxwM0HPOJN/wALNVeeyCUAw3p/9mNbzJhqA8wCPmgnkzi+4m1HQoF3ADy15gUFEiq914o4dv2eQjUbgs0MaHskhEc= + + file: core-geth*-$TRAVIS_OS_NAME* + + file_glob: true + + draft: true + + on: + + repo: etclabscore/core-geth + + tags: true + + skip_cleanup: true + + tag_name: "$TRAVIS_TAG" + + + + # Run build on ARM5 with environment-aware possible artifact deployment. + + - stage: build + + if: type = push + + name: "ARM5/Go1.16.x: go run build/ci.go install && deploy" + + os: linux + + dist: xenial + + sudo: required + + go: 1.16.x + + env: + + - ARMv5 + + git: + + submodules: false # avoid cloning ethereum/tests + + addons: + + apt: + + packages: + + - gcc-multilib + + script: + + # Switch over GCC to cross compilation (breaks 386, hence why do it here only) + + - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross + + - sudo ln -s /usr/include/asm-generic /usr/include/asm + + - GOPATH=~/go_arm GOARM=5 go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc + + before_deploy: + + - sh build/deploy_arm.sh + + deploy: + + provider: releases + + api_key: + + secure: f5ewX8iaY8E63wEGtLPZ8JF0/KUssJgAE+lqS7Nx5XxAM/GuE5X7mb9FmOiSzYyOexiftG8pwY0HVSXAaNk6HEl1GqlVsqMfuZgrVOAs8e0ltWgseI2T9qDp/oxxwWQ8cnPS3GrLwYUQTE+XfphL9aKkvt2RhoMmd1FPy6Dp0AYxzfLiZWCucBrR7HyHlIUjuM/DdN5U5te2loGhCyrDaKNeiIHQYp7h99cXFhKgC2N1mvXdIE7jxfiJGyEbmNksO0OY4RqBMNl7unOr8DvFf9dQBUc9cWfSRcAxt2DK4+zamX/0HJYfYRJR2Y2+z1oHrbJQnZm+9ZnOaXJt7bdWvON8jLJS+PLZT0kcSklSkKy3WEulijaGn1SIzEesadhnyHZxeDFgMnjVkSOCS0pDNtIv0SxfayqXvbYHzJhRDC4uVTRb+/PxMtZxIPCYBxLHJF6bkwmEWELl5I7UavN+jxuhkMDatqfN4V/j7JfqrLYFLHT9ByE5IBznizvJdxwXg8eo/RDJIJUwHjmKwsX9XBYmEX/UGe1O8ujJNTmg3acX9ECbwM2zgs1Br5WZtjRRDHT+YY6CbP2kPOCkpGqxwM0HPOJN/wALNVeeyCUAw3p/9mNbzJhqA8wCPmgnkzi+4m1HQoF3ADy15gUFEiq914o4dv2eQjUbgs0MaHskhEc= + + file: core-geth*-arm* + + file_glob: true + + draft: true + + on: + + repo: etclabscore/core-geth + + tags: true + + skip_cleanup: true + + tag_name: "$TRAVIS_TAG" + + + + #Run build on OSX with environment-aware possible artifact deployment. + + - stage: build + + if: type = push + + name: "OSX/Go1.16.x: make all && deploy" + + os: osx + + go: 1.16.x + + script: + + - echo "Increase the maximum number of open file descriptors on macOS" + + - NOFILE=20480 + + - sudo sysctl -w kern.maxfiles=$NOFILE + + - sudo sysctl -w kern.maxfilesperproc=$NOFILE + + - sudo launchctl limit maxfiles $NOFILE $NOFILE + + - sudo launchctl limit maxfiles + + - ulimit -S -n $NOFILE + + - ulimit -n + + - unset -f cd + + - go run build/ci.go install -dlgo + + before_deploy: + + - sh build/deploy.sh + + deploy: + + provider: releases + + api_key: + + secure: f5ewX8iaY8E63wEGtLPZ8JF0/KUssJgAE+lqS7Nx5XxAM/GuE5X7mb9FmOiSzYyOexiftG8pwY0HVSXAaNk6HEl1GqlVsqMfuZgrVOAs8e0ltWgseI2T9qDp/oxxwWQ8cnPS3GrLwYUQTE+XfphL9aKkvt2RhoMmd1FPy6Dp0AYxzfLiZWCucBrR7HyHlIUjuM/DdN5U5te2loGhCyrDaKNeiIHQYp7h99cXFhKgC2N1mvXdIE7jxfiJGyEbmNksO0OY4RqBMNl7unOr8DvFf9dQBUc9cWfSRcAxt2DK4+zamX/0HJYfYRJR2Y2+z1oHrbJQnZm+9ZnOaXJt7bdWvON8jLJS+PLZT0kcSklSkKy3WEulijaGn1SIzEesadhnyHZxeDFgMnjVkSOCS0pDNtIv0SxfayqXvbYHzJhRDC4uVTRb+/PxMtZxIPCYBxLHJF6bkwmEWELl5I7UavN+jxuhkMDatqfN4V/j7JfqrLYFLHT9ByE5IBznizvJdxwXg8eo/RDJIJUwHjmKwsX9XBYmEX/UGe1O8ujJNTmg3acX9ECbwM2zgs1Br5WZtjRRDHT+YY6CbP2kPOCkpGqxwM0HPOJN/wALNVeeyCUAw3p/9mNbzJhqA8wCPmgnkzi+4m1HQoF3ADy15gUFEiq914o4dv2eQjUbgs0MaHskhEc= + + file: core-geth*-$TRAVIS_OS_NAME* + + file_glob: true + + draft: true + + on: + + repo: etclabscore/core-geth + + tags: true + + skip_cleanup: true + + tag_name: "$TRAVIS_TAG" +++======= ++ allow_failures: ++ - stage: build ++ os: osx ++ go: 1.15.x ++ env: ++ - azure-osx ++ - azure-ios ++ - cocoapods-ios ++ ++ include: ++ # This builder only tests code linters on latest version of Go ++ - stage: lint ++ os: linux ++ dist: bionic ++ go: 1.16.x ++ env: ++ - lint ++ git: ++ submodules: false # avoid cloning ethereum/tests ++ script: ++ - go run build/ci.go lint ++ ++ # This builder does the Ubuntu PPA upload ++ - stage: build ++ if: type = push ++ os: linux ++ dist: bionic ++ go: 1.16.x ++ env: ++ - ubuntu-ppa ++ - GO111MODULE=on ++ git: ++ submodules: false # avoid cloning ethereum/tests ++ addons: ++ apt: ++ packages: ++ - devscripts ++ - debhelper ++ - dput ++ - fakeroot ++ - python-bzrlib ++ - python-paramiko ++ script: ++ - echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts ++ - go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder " ++ ++ # This builder does the Linux Azure uploads ++ - stage: build ++ if: type = push ++ os: linux ++ dist: bionic ++ sudo: required ++ go: 1.16.x ++ env: ++ - azure-linux ++ - GO111MODULE=on ++ git: ++ submodules: false # avoid cloning ethereum/tests ++ addons: ++ apt: ++ packages: ++ - gcc-multilib ++ script: ++ # Build for the primary platforms that Trusty can manage ++ - go run build/ci.go install -dlgo ++ - go run build/ci.go archive -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds ++ - go run build/ci.go install -dlgo -arch 386 ++ - go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds ++ ++ # Switch over GCC to cross compilation (breaks 386, hence why do it here only) ++ - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross ++ - sudo ln -s /usr/include/asm-generic /usr/include/asm ++ ++ - GOARM=5 go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc ++ - GOARM=5 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds ++ - GOARM=6 go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc ++ - GOARM=6 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds ++ - GOARM=7 go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabihf-gcc ++ - GOARM=7 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds ++ - go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc ++ - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds ++ ++ # This builder does the Linux Azure MIPS xgo uploads ++ - stage: build ++ if: type = push ++ os: linux ++ dist: bionic ++ services: ++ - docker ++ go: 1.16.x ++ env: ++ - azure-linux-mips ++ - GO111MODULE=on ++ git: ++ submodules: false # avoid cloning ethereum/tests ++ script: ++ - go run build/ci.go xgo --alltools -- --targets=linux/mips --ldflags '-extldflags "-static"' -v ++ - for bin in build/bin/*-linux-mips; do mv -f "${bin}" "${bin/-linux-mips/}"; done ++ - go run build/ci.go archive -arch mips -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds ++ ++ - go run build/ci.go xgo --alltools -- --targets=linux/mipsle --ldflags '-extldflags "-static"' -v ++ - for bin in build/bin/*-linux-mipsle; do mv -f "${bin}" "${bin/-linux-mipsle/}"; done ++ - go run build/ci.go archive -arch mipsle -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds ++ ++ - go run build/ci.go xgo --alltools -- --targets=linux/mips64 --ldflags '-extldflags "-static"' -v ++ - for bin in build/bin/*-linux-mips64; do mv -f "${bin}" "${bin/-linux-mips64/}"; done ++ - go run build/ci.go archive -arch mips64 -type tar -signer LINUX_SIGNING_KEY signify SIGNIFY_KEY -upload gethstore/builds ++ ++ - go run build/ci.go xgo --alltools -- --targets=linux/mips64le --ldflags '-extldflags "-static"' -v ++ - 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 -signify SIGNIFY_KEY -upload gethstore/builds ++ ++ # This builder does the Android Maven and Azure uploads ++ - stage: build ++ if: type = push ++ os: linux ++ dist: bionic ++ addons: ++ apt: ++ packages: ++ - openjdk-8-jdk ++ env: ++ - azure-android ++ - maven-android ++ - GO111MODULE=on ++ git: ++ submodules: false # avoid cloning ethereum/tests ++ before_install: ++ # Install Android and it's dependencies manually, Travis is stale ++ - export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 ++ - curl https://dl.google.com/android/repository/commandlinetools-linux-6858069_latest.zip -o android.zip ++ - unzip -q android.zip -d $HOME/sdk && rm android.zip ++ - mv $HOME/sdk/cmdline-tools $HOME/sdk/latest && mkdir $HOME/sdk/cmdline-tools && mv $HOME/sdk/latest $HOME/sdk/cmdline-tools ++ - export PATH=$PATH:$HOME/sdk/cmdline-tools/latest/bin ++ - export ANDROID_HOME=$HOME/sdk ++ ++ - yes | sdkmanager --licenses >/dev/null ++ - sdkmanager "platform-tools" "platforms;android-15" "platforms;android-19" "platforms;android-24" "ndk-bundle" ++ ++ # Install Go to allow building with ++ - curl https://dl.google.com/go/go1.16.linux-amd64.tar.gz | tar -xz ++ - export PATH=`pwd`/go/bin:$PATH ++ - export GOROOT=`pwd`/go ++ - export GOPATH=$HOME/go ++ script: ++ # Build the Android archive and upload it to Maven Central and Azure ++ - mkdir -p $GOPATH/src/github.com/ethereum ++ - ln -s `pwd` $GOPATH/src/github.com/ethereum/go-ethereum ++ - go run build/ci.go aar -signer ANDROID_SIGNING_KEY -signify SIGNIFY_KEY -deploy https://oss.sonatype.org -upload gethstore/builds ++ ++ # This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads ++ - stage: build ++ if: type = push ++ os: osx ++ go: 1.16.x ++ env: ++ - azure-osx ++ - azure-ios ++ - cocoapods-ios ++ - GO111MODULE=on ++ git: ++ submodules: false # avoid cloning ethereum/tests ++ script: ++ - go run build/ci.go install -dlgo ++ - go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds ++ ++ # Build the iOS framework and upload it to CocoaPods and Azure ++ - gem uninstall cocoapods -a -x ++ - 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 ++ - if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then git clone --depth=1 https://github.com/CocoaPods/Specs.git ~/.cocoapods/repos/master && pod setup --verbose; fi ++ ++ - xctool -version ++ - xcrun simctl list ++ ++ # Workaround for https://github.com/golang/go/issues/23749 ++ - export CGO_CFLAGS_ALLOW='-fmodules|-fblocks|-fobjc-arc' ++ - go run build/ci.go xcode -signer IOS_SIGNING_KEY -signify SIGNIFY_KEY -deploy trunk -upload gethstore/builds ++ ++ # These builders run the tests ++ - stage: build ++ os: linux ++ arch: amd64 ++ dist: bionic ++ go: 1.16.x ++ env: ++ - GO111MODULE=on ++ script: ++ - go run build/ci.go test -coverage $TEST_PACKAGES ++ ++ - stage: build ++ if: type = pull_request ++ os: linux ++ arch: arm64 ++ dist: bionic ++ go: 1.16.x ++ env: ++ - GO111MODULE=on ++ script: ++ - go run build/ci.go test -coverage $TEST_PACKAGES ++ ++ - stage: build ++ os: linux ++ dist: bionic ++ go: 1.15.x ++ env: ++ - GO111MODULE=on ++ script: ++ - go run build/ci.go test -coverage $TEST_PACKAGES ++ ++ # This builder does the Azure archive purges to avoid accumulating junk ++ - stage: build ++ if: type = cron ++ os: linux ++ dist: bionic ++ go: 1.16.x ++ env: ++ - azure-purge ++ - GO111MODULE=on ++ git: ++ submodules: false # avoid cloning ethereum/tests ++ script: ++ - go run build/ci.go purge -store gethstore/builds -days 14 +++>>>>>>> c2d2f4ed8f +diff --cc README.md +index aa30bcd28e,4a083d117a..0000000000 +--- a/README.md ++++ b/README.md +@@@ -1,59 -1,326 +1,385 @@@ +++<<<<<<< HEAD + +## CoreGeth: An Ethereum Protocol Provider + + + +> An [ethereum/go-ethereum](https://github.com/ethereum/go-ethereum) downstream effort to make the Ethereum Protocol accessible and extensible for a diverse ecosystem. + + + +Priority is given to reducing opinions around chain configuration, IP-based feature implementations, and API predictability. + +Upstream development from [ethereum/go-ethereum](https://github.com/ethereum/go-ethereum) is merged to this repository regularly, + + usually at every upstream tagged release. Every effort is made to maintain seamless compatibility with upstream source, including compatible RPC, JS, and CLI + + APIs, data storage locations and schemas, and, of course, interoperable node protocols. Applicable bug reports, bug fixes, features, and proposals should be + + made upstream whenever possible. + + + +[![OpenRPC](https://img.shields.io/static/v1.svg?label=OpenRPC&message=1.14.0&color=blue)](#openrpc-discovery) + +[![API Reference](https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667)](https://godoc.org/github.com/etclabscore/core-geth) + +[![Go Report Card](https://goreportcard.com/badge/github.com/etclabscore/core-geth)](https://goreportcard.com/report/github.com/etclabscore/core-geth) + +[![Travis](https://travis-ci.org/etclabscore/core-geth.svg?branch=master)](https://travis-ci.org/etclabscore/core-geth) + +[![Gitter](https://badges.gitter.im/core-geth/community.svg)](https://gitter.im/core-geth/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + + + +## Network/provider comparison + + + +Networks supported by the respective go-ethereum packaged `geth` program. + + + +| Ticker | Consensus | Network | core-geth | ethereum/go-ethereum | + +| --- | --- | --- | --- | --- | + +| ETC | :zap: | Ethereum Classic | :heavy_check_mark: | | + +| ETH | :zap: | Ethereum (Foundation) | :heavy_check_mark: | :heavy_check_mark: | + +| - | :zap: :handshake: | Private chains | :heavy_check_mark: | :heavy_check_mark: | + +| | :zap: | Mordor (Geth+Parity ETH PoW Testnet) | :heavy_check_mark: | | + +| | :zap: | Morden (Geth+Parity ETH PoW Testnet) | | | + +| | :zap: | Ropsten (Geth+Parity ETH PoW Testnet) | :heavy_check_mark: | :heavy_check_mark: | + +| | :handshake: | Rinkeby (Geth-only ETH PoA Testnet) | :heavy_check_mark: | :heavy_check_mark: | + +| | :handshake: | Goerli (Geth+Parity ETH PoA Testnet) | :heavy_check_mark: | :heavy_check_mark: | + +| | :handshake: | Kotti (Geth+Parity ETC PoA Testnet) | :heavy_check_mark: | | + +| | :handshake: | Kovan (Parity-only ETH PoA Testnet) | | | + +| | | Tobalaba (EWF Testnet) | | | + +| | | Ephemeral development PoA network | :heavy_check_mark: | :heavy_check_mark: | + + + +- :zap: = __Proof of Work__ + +- :handshake: = __Proof of Authority__ + + + +1: This is originally an [Ellaism + +Project](https://github.com/ellaism). However, A [recent hard + +fork](https://github.com/ellaism/specs/blob/master/specs/2018-0003-wasm-hardfork.md) + +makes Ellaism not feasible to support with go-ethereum any more. Existing + +Ellaism users are asked to switch to + +[Parity](https://github.com/paritytech/parity). + + + +2: Network not supported by default, but network configuration is possible. Make a PR! + + + +## Documentation + + + +- CoreGeth documentation is available [here](https://etclabscore.github.io/core-geth). + + + Getting Started [Installation](https://etclabscore.github.io/core-geth/getting-started/installation) and [CLI](https://etclabscore.github.io/core-geth/getting-started/run-cli) + + + [JSONRPC API](https://etclabscore.github.io/core-geth/apis/jsonrpc-apis) + + + [Developers](https://etclabscore.github.io/core-geth/developers/build-from-source) + + + [Tutorials](https://etclabscore.github.io/core-geth/tutorials/private-network) + +- Further [ethereum/go-ethereum](https://github.com/ethereum/go-ethereum) documentation about can be found [here](https://geth.ethereum.org/docs/). + +- Documentation about documentation lives [here](./docs/developers/documentation.md). +++======= ++ ## Go Ethereum ++ ++ Official Golang implementation of the Ethereum protocol. ++ ++ [![API Reference]( ++ https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667 ++ )](https://pkg.go.dev/github.com/ethereum/go-ethereum?tab=doc) ++ [![Go Report Card](https://goreportcard.com/badge/github.com/ethereum/go-ethereum)](https://goreportcard.com/report/github.com/ethereum/go-ethereum) ++ [![Travis](https://travis-ci.com/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.com/ethereum/go-ethereum) ++ [![Discord](https://img.shields.io/badge/discord-join%20chat-blue.svg)](https://discord.gg/nthXNEv) ++ ++ Automated builds are available for stable releases and the unstable master branch. Binary ++ archives are published at https://geth.ethereum.org/downloads/. ++ ++ ## Building the source ++ ++ For prerequisites and detailed build instructions please read the [Installation Instructions](https://geth.ethereum.org/docs/install-and-build/installing-geth). ++ ++ Building `geth` requires both a Go (version 1.13 or later) and a C compiler. You can install ++ them using your favourite package manager. Once the dependencies are installed, run ++ ++ ```shell ++ make geth ++ ``` ++ ++ or, to build the full suite of utilities: ++ ++ ```shell ++ make all ++ ``` ++ ++ ## Executables ++ ++ The go-ethereum project comes with several wrappers/executables found in the `cmd` ++ directory. ++ ++ | Command | Description | ++ | :-----------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ++ | **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default), archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI page](https://geth.ethereum.org/docs/interface/command-line-options) for command line options. | ++ | `abigen` | Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://docs.soliditylang.org/en/develop/abi-spec.html) with expanded functionality if the contract bytecode is also available. However, it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://geth.ethereum.org/docs/dapp/native-bindings) page for details. | ++ | `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. | ++ | `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug run`). | ++ | `gethrpctest` | Developer utility tool to support our [ethereum/rpc-test](https://github.com/ethereum/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://eth.wiki/json-rpc/API) specs. Please see the [test suite's readme](https://github.com/ethereum/rpc-tests/blob/master/README.md) for details. | ++ | `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://eth.wiki/en/fundamentals/rlp)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user-friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). | ++ | `puppeth` | a CLI wizard that aids in creating a new Ethereum network. | ++ ++ ## Running `geth` ++ ++ Going through all the possible command line flags is out of scope here (please consult our ++ [CLI Wiki page](https://geth.ethereum.org/docs/interface/command-line-options)), ++ but we've enumerated a few common parameter combos to get you up to speed quickly ++ on how you can run your own `geth` instance. ++ ++ ### Full node on the main Ethereum network ++ ++ By far the most common scenario is people wanting to simply interact with the Ethereum ++ network: create accounts; transfer funds; deploy and interact with contracts. For this ++ particular use-case the user doesn't care about years-old historical data, so we can ++ fast-sync quickly to the current state of the network. To do so: ++ ++ ```shell ++ $ geth console ++ ``` ++ ++ This command will: ++ * Start `geth` in fast sync mode (default, can be changed with the `--syncmode` flag), ++ causing it to download more data in exchange for avoiding processing the entire history ++ of the Ethereum network, which is very CPU intensive. ++ * Start up `geth`'s built-in interactive [JavaScript console](https://geth.ethereum.org/docs/interface/javascript-console), ++ (via the trailing `console` subcommand) through which you can invoke all official [`web3` methods](https://web3js.readthedocs.io/en/) ++ as well as `geth`'s own [management APIs](https://geth.ethereum.org/docs/rpc/server). ++ This tool is optional and if you leave it out you can always attach to an already running ++ `geth` instance with `geth attach`. ++ ++ ### A Full node on the Görli test network ++ ++ Transitioning towards developers, if you'd like to play around with creating Ethereum ++ contracts, you almost certainly would like to do that without any real money involved until ++ you get the hang of the entire system. In other words, instead of attaching to the main ++ network, you want to join the **test** network with your node, which is fully equivalent to ++ the main network, but with play-Ether only. ++ ++ ```shell ++ $ geth --goerli console ++ ``` ++ ++ The `console` subcommand has the exact same meaning as above and they are equally ++ useful on the testnet too. Please, see above for their explanations if you've skipped here. ++ ++ Specifying the `--goerli` flag, however, will reconfigure your `geth` instance a bit: ++ ++ * Instead of connecting the main Ethereum network, the client will connect to the Görli ++ test network, which uses different P2P bootnodes, different network IDs and genesis ++ states. ++ * Instead of using the default data directory (`~/.ethereum` on Linux for example), `geth` ++ will nest itself one level deeper into a `goerli` subfolder (`~/.ethereum/goerli` on ++ Linux). Note, on OSX and Linux this also means that attaching to a running testnet node ++ requires the use of a custom endpoint since `geth attach` will try to attach to a ++ production node endpoint by default, e.g., ++ `geth attach /goerli/geth.ipc`. Windows users are not affected by ++ this. ++ ++ *Note: Although there are some internal protective measures to prevent transactions from ++ crossing over between the main network and test network, you should make sure to always ++ use separate accounts for play-money and real-money. Unless you manually move ++ accounts, `geth` will by default correctly separate the two networks and will not make any ++ accounts available between them.* ++ ++ ### Full node on the Rinkeby test network ++ ++ Go Ethereum also supports connecting to the older proof-of-authority based test network ++ called [*Rinkeby*](https://www.rinkeby.io) which is operated by members of the community. ++ ++ ```shell ++ $ geth --rinkeby console ++ ``` ++ ++ ### Full node on the Ropsten test network ++ ++ In addition to Görli and Rinkeby, Geth also supports the ancient Ropsten testnet. The ++ Ropsten test network is based on the Ethash proof-of-work consensus algorithm. As such, ++ it has certain extra overhead and is more susceptible to reorganization attacks due to the ++ network's low difficulty/security. ++ ++ ```shell ++ $ geth --ropsten console ++ ``` ++ ++ *Note: Older Geth configurations store the Ropsten database in the `testnet` subdirectory.* ++ ++ ### Configuration ++ ++ As an alternative to passing the numerous flags to the `geth` binary, you can also pass a ++ configuration file via: ++ ++ ```shell ++ $ geth --config /path/to/your_config.toml ++ ``` ++ ++ To get an idea how the file should look like you can use the `dumpconfig` subcommand to ++ export your existing configuration: ++ ++ ```shell ++ $ geth --your-favourite-flags dumpconfig ++ ``` ++ ++ *Note: This works only with `geth` v1.6.0 and above.* ++ ++ #### Docker quick start ++ ++ One of the quickest ways to get Ethereum up and running on your machine is by using ++ Docker: ++ ++ ```shell ++ docker run -d --name ethereum-node -v /Users/alice/ethereum:/root \ ++ -p 8545:8545 -p 30303:30303 \ ++ ethereum/client-go ++ ``` ++ ++ This will start `geth` in fast-sync mode with a DB memory allowance of 1GB just as the ++ above command does. It will also create a persistent volume in your home directory for ++ saving your blockchain as well as map the default ports. There is also an `alpine` tag ++ available for a slim version of the image. ++ ++ Do not forget `--http.addr 0.0.0.0`, if you want to access RPC from other containers ++ and/or hosts. By default, `geth` binds to the local interface and RPC endpoints is not ++ accessible from the outside. ++ ++ ### Programmatically interfacing `geth` nodes ++ ++ As a developer, sooner rather than later you'll want to start interacting with `geth` and the ++ Ethereum network via your own programs and not manually through the console. To aid ++ this, `geth` has built-in support for a JSON-RPC based APIs ([standard APIs](https://eth.wiki/json-rpc/API) ++ and [`geth` specific APIs](https://geth.ethereum.org/docs/rpc/server)). ++ These can be exposed via HTTP, WebSockets and IPC (UNIX sockets on UNIX based ++ platforms, and named pipes on Windows). ++ ++ The IPC interface is enabled by default and exposes all the APIs supported by `geth`, ++ whereas the HTTP and WS interfaces need to manually be enabled and only expose a ++ subset of APIs due to security reasons. These can be turned on/off and configured as ++ you'd expect. ++ ++ HTTP based JSON-RPC API options: ++ ++ * `--http` Enable the HTTP-RPC server ++ * `--http.addr` HTTP-RPC server listening interface (default: `localhost`) ++ * `--http.port` HTTP-RPC server listening port (default: `8545`) ++ * `--http.api` API's offered over the HTTP-RPC interface (default: `eth,net,web3`) ++ * `--http.corsdomain` Comma separated list of domains from which to accept cross origin requests (browser enforced) ++ * `--ws` Enable the WS-RPC server ++ * `--ws.addr` WS-RPC server listening interface (default: `localhost`) ++ * `--ws.port` WS-RPC server listening port (default: `8546`) ++ * `--ws.api` API's offered over the WS-RPC interface (default: `eth,net,web3`) ++ * `--ws.origins` Origins from which to accept websockets requests ++ * `--ipcdisable` Disable the IPC-RPC server ++ * `--ipcapi` API's offered over the IPC-RPC interface (default: `admin,debug,eth,miner,net,personal,shh,txpool,web3`) ++ * `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it) ++ ++ You'll need to use your own programming environments' capabilities (libraries, tools, etc) to ++ connect via HTTP, WS or IPC to a `geth` node configured with the above flags and you'll ++ need to speak [JSON-RPC](https://www.jsonrpc.org/specification) on all transports. You ++ can reuse the same connection for multiple requests! ++ ++ **Note: Please understand the security implications of opening up an HTTP/WS based ++ transport before doing so! Hackers on the internet are actively trying to subvert ++ Ethereum nodes with exposed APIs! Further, all browser tabs can access locally ++ running web servers, so malicious web pages could try to subvert locally available ++ APIs!** ++ ++ ### Operating a private network ++ ++ Maintaining your own private network is more involved as a lot of configurations taken for ++ granted in the official networks need to be manually set up. ++ ++ #### Defining the private genesis state ++ ++ First, you'll need to create the genesis state of your networks, which all nodes need to be ++ aware of and agree upon. This consists of a small JSON file (e.g. call it `genesis.json`): ++ ++ ```json ++ { ++ "config": { ++ "chainId": , ++ "homesteadBlock": 0, ++ "eip150Block": 0, ++ "eip155Block": 0, ++ "eip158Block": 0, ++ "byzantiumBlock": 0, ++ "constantinopleBlock": 0, ++ "petersburgBlock": 0, ++ "istanbulBlock": 0 ++ }, ++ "alloc": {}, ++ "coinbase": "0x0000000000000000000000000000000000000000", ++ "difficulty": "0x20000", ++ "extraData": "", ++ "gasLimit": "0x2fefd8", ++ "nonce": "0x0000000000000042", ++ "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", ++ "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", ++ "timestamp": "0x00" ++ } ++ ``` ++ ++ The above fields should be fine for most purposes, although we'd recommend changing ++ the `nonce` to some random value so you prevent unknown remote nodes from being able ++ to connect to you. If you'd like to pre-fund some accounts for easier testing, create ++ the accounts and populate the `alloc` field with their addresses. ++ ++ ```json ++ "alloc": { ++ "0x0000000000000000000000000000000000000001": { ++ "balance": "111111111" ++ }, ++ "0x0000000000000000000000000000000000000002": { ++ "balance": "222222222" ++ } ++ } ++ ``` ++ ++ With the genesis state defined in the above JSON file, you'll need to initialize **every** ++ `geth` node with it prior to starting it up to ensure all blockchain parameters are correctly ++ set: ++ ++ ```shell ++ $ geth init path/to/genesis.json ++ ``` ++ ++ #### Creating the rendezvous point ++ ++ With all nodes that you want to run initialized to the desired genesis state, you'll need to ++ start a bootstrap node that others can use to find each other in your network and/or over ++ the internet. The clean way is to configure and run a dedicated bootnode: ++ ++ ```shell ++ $ bootnode --genkey=boot.key ++ $ bootnode --nodekey=boot.key ++ ``` ++ ++ With the bootnode online, it will display an [`enode` URL](https://eth.wiki/en/fundamentals/enode-url-format) ++ that other nodes can use to connect to it and exchange peer information. Make sure to ++ replace the displayed IP address information (most probably `[::]`) with your externally ++ accessible IP to get the actual `enode` URL. ++ ++ *Note: You could also use a full-fledged `geth` node as a bootnode, but it's the less ++ recommended way.* ++ ++ #### Starting up your member nodes ++ ++ With the bootnode operational and externally reachable (you can try ++ `telnet ` to ensure it's indeed reachable), start every subsequent `geth` ++ node pointed to the bootnode for peer discovery via the `--bootnodes` flag. It will ++ probably also be desirable to keep the data directory of your private network separated, so ++ do also specify a custom `--datadir` flag. ++ ++ ```shell ++ $ geth --datadir=path/to/custom/data/folder --bootnodes= ++ ``` ++ ++ *Note: Since your network will be completely cut off from the main and test networks, you'll ++ also need to configure a miner to process transactions and create new blocks for you.* ++ ++ #### Running a private miner ++ ++ Mining on the public Ethereum network is a complex task as it's only feasible using GPUs, ++ requiring an OpenCL or CUDA enabled `ethminer` instance. For information on such a ++ setup, please consult the [EtherMining subreddit](https://www.reddit.com/r/EtherMining/) ++ and the [ethminer](https://github.com/ethereum-mining/ethminer) repository. ++ ++ In a private network setting, however a single CPU miner instance is more than enough for ++ practical purposes as it can produce a stable stream of blocks at the correct intervals ++ without needing heavy resources (consider running on a single thread, no need for multiple ++ ones either). To start a `geth` instance for mining, run it with all your usual flags, extended ++ by: ++ ++ ```shell ++ $ geth --mine --miner.threads=1 --miner.etherbase=0x0000000000000000000000000000000000000000 ++ ``` ++ ++ Which will start mining blocks and transactions on a single CPU thread, crediting all ++ proceedings to the account specified by `--miner.etherbase`. You can further tune the mining ++ by changing the default gas limit blocks converge to (`--miner.targetgaslimit`) and the price ++ transactions are accepted at (`--miner.gasprice`). +++>>>>>>> c2d2f4ed8f + + ## Contribution + +diff --cc accounts/abi/bind/backends/simulated.go +index 0174f8c77c,d6d525eae1..0000000000 +--- a/accounts/abi/bind/backends/simulated.go ++++ b/accounts/abi/bind/backends/simulated.go +@@@ -77,9 -74,10 +77,16 @@@ type SimulatedBackend struct + + // NewSimulatedBackendWithDatabase creates a new binding backend based on the given database + // and uses a simulated blockchain for testing purposes. +++<<<<<<< HEAD + +func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc genesisT.GenesisAlloc, gasLimit uint64) *SimulatedBackend { + + genesis := genesisT.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc} + + core.MustCommitGenesis(database, &genesis) +++======= ++ // A simulated backend always uses chainID 1337. ++ func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend { ++ genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc} ++ genesis.MustCommit(database) +++>>>>>>> c2d2f4ed8f + blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + + backend := &SimulatedBackend{ +@@@ -94,7 -92,8 +101,12 @@@ + + // NewSimulatedBackend creates a new binding backend using a simulated blockchain + // for testing purposes. +++<<<<<<< HEAD + +func NewSimulatedBackend(alloc genesisT.GenesisAlloc, gasLimit uint64) *SimulatedBackend { +++======= ++ // A simulated backend always uses chainID 1337. ++ func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend { +++>>>>>>> c2d2f4ed8f + return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit) + } + +@@@ -560,7 -559,10 +572,14 @@@ func (b *SimulatedBackend) SendTransact + b.mu.Lock() + defer b.mu.Unlock() + +++<<<<<<< HEAD + + sender, err := types.Sender(types.NewEIP155Signer(b.config.GetChainID()), tx) +++======= ++ // Check transaction validity. ++ block := b.blockchain.CurrentBlock() ++ signer := types.MakeSigner(b.blockchain.Config(), block.Number()) ++ sender, err := types.Sender(signer, tx) +++>>>>>>> c2d2f4ed8f + if err != nil { + panic(fmt.Errorf("invalid transaction: %v", err)) + } +diff --cc accounts/abi/bind/backends/simulated_test.go +index 673d3ad2cc,64ddf8bb2c..0000000000 +--- a/accounts/abi/bind/backends/simulated_test.go ++++ b/accounts/abi/bind/backends/simulated_test.go +@@@ -40,9 -39,9 +40,15 @@@ import + func TestSimulatedBackend(t *testing.T) { + var gasLimit uint64 = 8000029 + key, _ := crypto.GenerateKey() // nolint: gosec +++<<<<<<< HEAD + + auth := bind.NewKeyedTransactor(key) + + genAlloc := make(genesisT.GenesisAlloc) + + genAlloc[auth.From] = genesisT.GenesisAccount{Balance: big.NewInt(9223372036854775807)} +++======= ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) ++ genAlloc := make(core.GenesisAlloc) ++ genAlloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(9223372036854775807)} +++>>>>>>> c2d2f4ed8f + + sim := NewSimulatedBackend(genAlloc, gasLimit) + defer sim.Close() +@@@ -412,9 -411,9 +418,9 @@@ func TestSimulatedBackend_EstimateGas( + + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) +- opts := bind.NewKeyedTransactor(key) ++ opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether)}}, 10000000) + + sim := NewSimulatedBackend(genesisT.GenesisAlloc{addr: {Balance: big.NewInt(vars.Ether)}}, 10000000) + defer sim.Close() + + parsed, _ := abi.JSON(strings.NewReader(contractAbi)) +diff --cc accounts/abi/bind/bind_test.go +index dfeb5b9e50,d0958cb62f..0000000000 +--- a/accounts/abi/bind/bind_test.go ++++ b/accounts/abi/bind/bind_test.go +@@@ -296,9 -296,9 +296,9 @@@ var bindTests = []struct + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() +- auth := bind.NewKeyedTransactor(key) ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + // Deploy an interaction tester contract and call a transaction on it +@@@ -351,9 -351,9 +351,9 @@@ + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() +- auth := bind.NewKeyedTransactor(key) ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + // Deploy a tuple tester contract and execute a structured call on it +@@@ -397,9 -397,9 +397,9 @@@ + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() +- auth := bind.NewKeyedTransactor(key) ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + // Deploy a tuple tester contract and execute a structured call on it +@@@ -455,9 -455,9 +455,9 @@@ + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() +- auth := bind.NewKeyedTransactor(key) ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + // Deploy a slice tester contract and execute a n array call on it +@@@ -503,9 -503,9 +503,9 @@@ + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() +- auth := bind.NewKeyedTransactor(key) ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + // Deploy a default method invoker contract and execute its default method +@@@ -598,9 -701,9 +701,9 @@@ + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() +- auth := bind.NewKeyedTransactor(key) ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + // Deploy a funky gas pattern contract +@@@ -648,9 -751,9 +751,9 @@@ + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() +- auth := bind.NewKeyedTransactor(key) ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + // Deploy a sender tester contract and execute a structured call on it +@@@ -723,9 -826,9 +826,9 @@@ + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() +- auth := bind.NewKeyedTransactor(key) ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + // Deploy a underscorer tester contract and execute a structured call on it +@@@ -817,9 -920,9 +920,9 @@@ + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() +- auth := bind.NewKeyedTransactor(key) ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + // Deploy an eventer contract +@@@ -1007,9 -1110,9 +1110,9 @@@ + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() +- auth := bind.NewKeyedTransactor(key) ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + //deploy the test contract +@@@ -1142,9 -1245,9 +1245,9 @@@ + + ` + key, _ := crypto.GenerateKey() +- auth := bind.NewKeyedTransactor(key) ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + _, _, contract, err := DeployTuple(auth, sim) +@@@ -1284,9 -1387,9 +1387,9 @@@ + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() +- auth := bind.NewKeyedTransactor(key) ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + //deploy the test contract +@@@ -1350,8 -1453,8 +1453,13 @@@ + ` + // Initialize test accounts + key, _ := crypto.GenerateKey() +++<<<<<<< HEAD + + auth := bind.NewKeyedTransactor(key) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) +++======= ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) ++ sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) +++>>>>>>> c2d2f4ed8f + defer sim.Close() + + // deploy the test contract +@@@ -1441,10 -1544,10 +1549,10 @@@ + addr := crypto.PubkeyToAddress(key.PublicKey) + + // Deploy registrar contract + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000) + defer sim.Close() + +- transactOpts := bind.NewKeyedTransactor(key) ++ transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + _, _, _, err := DeployIdentifierCollision(transactOpts, sim) + if err != nil { + t.Fatalf("failed to deploy contract: %v", err) +@@@ -1503,10 -1606,10 +1611,10 @@@ + addr := crypto.PubkeyToAddress(key.PublicKey) + + // Deploy registrar contract + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000) + defer sim.Close() + +- transactOpts := bind.NewKeyedTransactor(key) ++ transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + _, _, c1, err := DeployContractOne(transactOpts, sim) + if err != nil { + t.Fatal("Failed to deploy contract") +@@@ -1563,9 -1666,9 +1671,9 @@@ + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() +- auth := bind.NewKeyedTransactor(key) ++ auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000) + defer sim.Close() + + // Deploy a tester contract and execute a structured call on it +@@@ -1629,10 -1728,10 +1733,10 @@@ + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + + - sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 1000000) + + sim := backends.NewSimulatedBackend(genesisT.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 1000000) + defer sim.Close() + +- opts := bind.NewKeyedTransactor(key) ++ opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + _, _, c, err := DeployNewFallbacks(opts, sim) + if err != nil { + t.Fatalf("Failed to deploy contract: %v", err) +@@@ -1746,20 -1846,16 +1851,32 @@@ func TestGolangBindings(t *testing.T) + t.Fatalf("failed to convert binding test to modules: %v\n%s", err, out) + } + pwd, _ := os.Getwd() +++<<<<<<< HEAD + + repoRoot := filepath.Join(pwd, "..", "..", "..") + + + + replacer := exec.Command(gocmd, "mod", "edit", "-replace", "github.com/ethereum/go-ethereum="+repoRoot) // Repo root +++======= ++ replacer := exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/ethereum/go-ethereum@v0.0.0", "-replace", "github.com/ethereum/go-ethereum="+filepath.Join(pwd, "..", "..", "..")) // Repo root +++>>>>>>> c2d2f4ed8f + replacer.Dir = pkg + if out, err := replacer.CombinedOutput(); err != nil { + - t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out) + + t.Fatalf("failed to replace binding test 'replace' dependency to current source tree: %v\n%s", err, out) + + } + + + + tidyer := exec.Command(gocmd, "mod", "tidy") + + tidyer.Dir = pkg + + if out, err := tidyer.CombinedOutput(); err != nil { + + t.Fatal(err, string(out)) + } +++<<<<<<< HEAD + + +++======= ++ tidier := exec.Command(gocmd, "mod", "tidy") ++ tidier.Dir = pkg ++ if out, err := tidier.CombinedOutput(); err != nil { ++ t.Fatalf("failed to tidy Go module file: %v\n%s", err, out) ++ } +++>>>>>>> c2d2f4ed8f + // Test the entire package and report any failures + cmd := exec.Command(gocmd, "test", "-v", "-count", "1") + cmd.Dir = pkg +diff --cc cmd/devp2p/internal/ethtest/types.go +index 4a0a45a1f4,734adff366..0000000000 +--- a/cmd/devp2p/internal/ethtest/types.go ++++ b/cmd/devp2p/internal/ethtest/types.go +@@@ -338,15 -297,18 +297,29 @@@ loop + if c.ethProtocolVersion == 0 { + t.Fatalf("eth protocol version must be set in Conn") + } +++<<<<<<< HEAD + + // write status message to client + + status := Status{ + + ProtocolVersion: uint32(c.ethProtocolVersion), + + NetworkID: chain.chainConfig.GetChainID().Uint64(), + + TD: chain.TD(chain.Len()), + + Head: chain.blocks[chain.Len()-1].Hash(), + + Genesis: chain.blocks[0].Hash(), + + ForkID: chain.ForkID(), +++======= ++ if status == nil { ++ // write status message to client ++ status = &Status{ ++ ProtocolVersion: uint32(c.ethProtocolVersion), ++ NetworkID: chain.chainConfig.ChainID.Uint64(), ++ TD: chain.TD(chain.Len()), ++ Head: chain.blocks[chain.Len()-1].Hash(), ++ Genesis: chain.blocks[0].Hash(), ++ ForkID: chain.ForkID(), ++ } +++>>>>>>> c2d2f4ed8f + } ++ + if err := c.Write(status); err != nil { + t.Fatalf("could not write to connection: %v", err) + } +diff --cc cmd/evm/internal/t8ntool/execution.go +index 80c54f5b15,c3f1b16efc..0000000000 +--- a/cmd/evm/internal/t8ntool/execution.go ++++ b/cmd/evm/internal/t8ntool/execution.go +@@@ -145,22 -142,10 +144,28 @@@ func (pre *Prestate) Apply(vmConfig vm. + vmConfig.Tracer = tracer + vmConfig.Debug = (tracer != nil) + statedb.Prepare(tx.Hash(), blockHash, txIndex) +++<<<<<<< HEAD + + vmContext.GasPrice = msg.GasPrice() + + vmContext.Origin = msg.From() + + + + evm := vm.NewEVM(vmContext, statedb, chainConfig, vmConfig) + + if chainConfig.IsEnabled(chainConfig.GetEIP2929Transition, vmContext.BlockNumber) { + + statedb.AddAddressToAccessList(msg.From()) + + if dst := msg.To(); dst != nil { + + statedb.AddAddressToAccessList(*dst) + + // If it's a create-tx, the destination will be added inside evm.create + + } + + for addr := range vm.PrecompiledContractsForConfig(chainConfig, vmContext.BlockNumber) { + + statedb.AddAddressToAccessList(addr) + + } + + } + + +++======= ++ txContext := core.NewEVMTxContext(msg) +++>>>>>>> c2d2f4ed8f + snapshot := statedb.Snapshot() ++ evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig) ++ + // (ret []byte, usedGas uint64, failed bool, err error) + msgResult, err := core.ApplyMessage(evm, msg, gaspool) + if err != nil { +@@@ -174,35 -159,45 +179,46 @@@ + return nil, nil, NewError(ErrorMissingBlockhash, hashError) + } + gasUsed += msgResult.UsedGas +- // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx ++ ++ // Receipt: + { + var root []byte + - if chainConfig.IsByzantium(vmContext.BlockNumber) { + - statedb.Finalise(true) + + eip161d := chainConfig.IsEnabled(chainConfig.GetEIP161dTransition, vmContext.BlockNumber) + + if chainConfig.IsEnabled(chainConfig.GetEIP658Transition, vmContext.BlockNumber) { + + statedb.Finalise(eip161d) + } else { + - root = statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)).Bytes() + + root = statedb.IntermediateRoot(eip161d).Bytes() + } + +- receipt := types.NewReceipt(root, msgResult.Failed(), gasUsed) ++ // Create a new receipt for the transaction, storing the intermediate root and ++ // gas used by the tx. ++ receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: gasUsed} ++ if msgResult.Failed() { ++ receipt.Status = types.ReceiptStatusFailed ++ } else { ++ receipt.Status = types.ReceiptStatusSuccessful ++ } + receipt.TxHash = tx.Hash() + receipt.GasUsed = msgResult.UsedGas +- // if the transaction created a contract, store the creation address in the receipt. ++ ++ // If the transaction created a contract, store the creation address in the receipt. + if msg.To() == nil { +- receipt.ContractAddress = crypto.CreateAddress(evm.Context.Origin, tx.Nonce()) ++ receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) + } +- // Set the receipt logs and create a bloom for filtering ++ ++ // Set the receipt logs and create the bloom filter. + receipt.Logs = statedb.GetLogs(tx.Hash()) + receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) +- // These three are non-consensus fields ++ // These three are non-consensus fields: + //receipt.BlockHash +- //receipt.BlockNumber = ++ //receipt.BlockNumber + receipt.TransactionIndex = uint(txIndex) + receipts = append(receipts, receipt) + } ++ + txIndex++ + } + - statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) + + statedb.IntermediateRoot(chainConfig.IsEnabled(chainConfig.GetEIP161dTransition, vmContext.BlockNumber)) + // Add mining reward? + if miningReward > 0 { + // Add mining reward. The mining reward may be `0`, which only makes a difference in the cases +diff --cc cmd/evm/internal/t8ntool/transition.go +index 419e117ec3,fedcd12435..0000000000 +--- a/cmd/evm/internal/t8ntool/transition.go ++++ b/cmd/evm/internal/t8ntool/transition.go +@@@ -25,12 -26,15 +26,23 @@@ import + "path" + + "github.com/ethereum/go-ethereum/common" +++<<<<<<< HEAD +++======= ++ "github.com/ethereum/go-ethereum/common/hexutil" ++ "github.com/ethereum/go-ethereum/core" +++>>>>>>> c2d2f4ed8f + "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/crypto" + "github.com/ethereum/go-ethereum/log" +++<<<<<<< HEAD + + "github.com/ethereum/go-ethereum/params/types/ctypes" + + "github.com/ethereum/go-ethereum/params/types/genesisT" +++======= ++ "github.com/ethereum/go-ethereum/params" ++ "github.com/ethereum/go-ethereum/rlp" +++>>>>>>> c2d2f4ed8f + "github.com/ethereum/go-ethereum/tests" + "gopkg.in/urfave/cli.v1" + ) +@@@ -64,9 -68,9 +76,15 @@@ func (n *NumberedError) Code() int + } + + type input struct { +++<<<<<<< HEAD + + Alloc genesisT.GenesisAlloc `json:"alloc,omitempty"` + + Env *stEnv `json:"env,omitempty"` + + Txs types.Transactions `json:"txs,omitempty"` +++======= ++ Alloc core.GenesisAlloc `json:"alloc,omitempty"` ++ Env *stEnv `json:"env,omitempty"` ++ Txs []*txWithKey `json:"txs,omitempty"` +++>>>>>>> c2d2f4ed8f + } + + func Main(ctx *cli.Context) error { +@@@ -198,24 -186,110 +200,112 @@@ + vmConfig.ExtraEips = extraEips + } + // Set the chain id + - chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name)) + + if err := chainConfig.SetChainID(big.NewInt(ctx.Int64(ChainIDFlag.Name))); err != nil { + + return err + + } + ++ var txsWithKeys []*txWithKey ++ if txStr != stdinSelector { ++ inFile, err := os.Open(txStr) ++ if err != nil { ++ return NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err)) ++ } ++ defer inFile.Close() ++ decoder := json.NewDecoder(inFile) ++ if err := decoder.Decode(&txsWithKeys); err != nil { ++ return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling txs-file: %v", err)) ++ } ++ } else { ++ txsWithKeys = inputData.Txs ++ } ++ // We may have to sign the transactions. ++ signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number))) ++ ++ if txs, err = signUnsignedTransactions(txsWithKeys, signer); err != nil { ++ return NewError(ErrorJson, fmt.Errorf("Failed signing transactions: %v", err)) ++ } ++ ++ // Iterate over all the tests, run them and aggregate the results ++ + // Run the test and aggregate the result + state, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer) + if err != nil { + return err + } ++ body, _ := rlp.EncodeToBytes(txs) + // Dump the excution result +- //postAlloc := state.DumpGenesisFormat(false, false, false) + collector := make(Alloc) + state.DumpToCollector(collector, false, false, false, nil, -1) +- return dispatchOutput(ctx, baseDir, result, collector) ++ return dispatchOutput(ctx, baseDir, result, collector, body) ++ ++ } + ++ // txWithKey is a helper-struct, to allow us to use the types.Transaction along with ++ // a `secretKey`-field, for input ++ type txWithKey struct { ++ key *ecdsa.PrivateKey ++ tx *types.Transaction ++ } ++ ++ func (t *txWithKey) UnmarshalJSON(input []byte) error { ++ // Read the secretKey, if present ++ type sKey struct { ++ Key *common.Hash `json:"secretKey"` ++ } ++ var key sKey ++ if err := json.Unmarshal(input, &key); err != nil { ++ return err ++ } ++ if key.Key != nil { ++ k := key.Key.Hex()[2:] ++ if ecdsaKey, err := crypto.HexToECDSA(k); err != nil { ++ return err ++ } else { ++ t.key = ecdsaKey ++ } ++ } ++ // Now, read the transaction itself ++ var tx types.Transaction ++ if err := json.Unmarshal(input, &tx); err != nil { ++ return err ++ } ++ t.tx = &tx ++ return nil ++ } ++ ++ // signUnsignedTransactions converts the input txs to canonical transactions. ++ // ++ // The transactions can have two forms, either ++ // 1. unsigned or ++ // 2. signed ++ // For (1), r, s, v, need so be zero, and the `secretKey` needs to be set. ++ // If so, we sign it here and now, with the given `secretKey` ++ // If the condition above is not met, then it's considered a signed transaction. ++ // ++ // To manage this, we read the transactions twice, first trying to read the secretKeys, ++ // and secondly to read them with the standard tx json format ++ func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Transactions, error) { ++ var signedTxs []*types.Transaction ++ for i, txWithKey := range txs { ++ tx := txWithKey.tx ++ key := txWithKey.key ++ v, r, s := tx.RawSignatureValues() ++ if key != nil && v.BitLen()+r.BitLen()+s.BitLen() == 0 { ++ // This transaction needs to be signed ++ signed, err := types.SignTx(tx, signer, key) ++ if err != nil { ++ return nil, NewError(ErrorJson, fmt.Errorf("Tx %d: failed to sign tx: %v", i, err)) ++ } ++ signedTxs = append(signedTxs, signed) ++ } else { ++ // Already signed ++ signedTxs = append(signedTxs, tx) ++ } ++ } ++ return signedTxs, nil + } + + -type Alloc map[common.Address]core.GenesisAccount + +type Alloc map[common.Address]genesisT.GenesisAccount + + func (g Alloc) OnRoot(common.Hash) {} + +diff --cc cmd/evm/runner.go +index 8bdb2429cb,4063767cb8..0000000000 +--- a/cmd/evm/runner.go ++++ b/cmd/evm/runner.go +@@@ -38,9 -38,7 +38,13 @@@ import + "github.com/ethereum/go-ethereum/core/vm/runtime" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" +++<<<<<<< HEAD + + "github.com/ethereum/go-ethereum/params/types/ctypes" + + "github.com/ethereum/go-ethereum/params/types/genesisT" + + cli "gopkg.in/urfave/cli.v1" +++======= ++ "gopkg.in/urfave/cli.v1" +++>>>>>>> c2d2f4ed8f + ) + + var runCommand = cli.Command{ +diff --cc cmd/faucet/faucet.go +index db51f60afb,e839f1c886..0000000000 +--- a/cmd/faucet/faucet.go ++++ b/cmd/faucet/faucet.go +@@@ -342,26 -143,24 +341,45 @@@ func main() + if err != nil { + log.Crit("Failed to render the faucet template", "err", err) + } +++<<<<<<< HEAD + + + + if genesis != nil { + + log.Info("Using chain/net config", "network id", *netFlag, "bootnodes", *bootFlag, "chain config", fmt.Sprintf("%v", genesis.Config)) + + + + // Convert the bootnodes to internal enode representations + + for _, boot := range utils.SplitAndTrim(*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) + + } + + } + + } else { + + log.Info("Attaching faucet to running client") + + client, err = ethclient.DialContext(context.Background(), *attachFlag) + + if err != nil { + + log.Crit("Failed to connect to client", "error", err) +++======= ++ // 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 []*enode.Node ++ for _, boot := range strings.Split(*bootFlag, ",") { ++ if url, err := enode.Parse(enode.ValidSchemes, boot); err == nil { ++ enodes = append(enodes, url) ++ } else { ++ log.Error("Failed to parse bootnode URL", "url", boot, "err", err) +++>>>>>>> c2d2f4ed8f + } + } + + + // 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) +@@@ -472,62 -220,22 +490,73 @@@ type faucet struct + lock sync.RWMutex // Lock protecting the faucet's internals + } + +++<<<<<<< HEAD + +const ( + + multiFaucetNodeName = "MultiFaucet" + + coreFaucetNodeName = "CoreFaucet" + +) + + + +// migrateFaucetDirectory moves an existing "MultiFaucet" directory to the new "CoreFaucet". + +// It only returns an error if the migration is attempted and fails. + +// If no 'old' directory is found to migrate (ie DNE), it returns nil (no error). + +func migrateFaucetDirectory(faucetDataDir string) error { + + oldFaucetNodePath := filepath.Join(faucetDataDir, multiFaucetNodeName) + + targetFaucetNodePath := filepath.Join(faucetDataDir, coreFaucetNodeName) + + + + log.Info("Checking datadir migration", "datadir", faucetDataDir) + + + + d, err := os.Stat(oldFaucetNodePath) + + if err != nil && os.IsNotExist(err) { + + // This is an expected positive error. + + return nil + + } + + if err == nil && d.IsDir() { + + + + // Path exists and is a directory. + + log.Warn("Found existing 'MultiFaucet' directory, migrating", "old", oldFaucetNodePath, "new", targetFaucetNodePath) + + if err := os.Rename(oldFaucetNodePath, targetFaucetNodePath); err != nil { + + return err + + } + + return nil + + } + + if err == nil && !d.IsDir() { + + return errors.New("expected a directory at faucet node datadir path, found non-directory") + + } + + // Return any error which is not expected, eg. bad permissions. + + return err + +} + + + +// startStack starts the node stack, ensures peering, and assigns the respective ethclient to the faucet. + +func (f *faucet) startStack(genesis *genesisT.Genesis, port int, enodes []*discv5.Node, network uint64) error { + + + + genesisHash := core.GenesisToBlock(genesis, nil).Hash() + + + + faucetDataDir := faucetDirFromChainIndicators(genesis.Config.GetChainID().Uint64(), genesisHash) + + + + // Handle renaming of existing directory from old name to new name. + + if err := migrateFaucetDirectory(faucetDataDir); err != nil { + + log.Crit("Migration handling of faucet datadir failed", "error", err) + + } + + +++======= ++ // wsConn wraps a websocket connection with a write mutex as the underlying ++ // websocket library does not synchronize access to the stream. ++ type wsConn struct { ++ conn *websocket.Conn ++ wlock sync.Mutex ++ } ++ ++ func newFaucet(genesis *core.Genesis, port int, enodes []*enode.Node, network uint64, stats string, ks *keystore.KeyStore, index []byte) (*faucet, error) { +++>>>>>>> c2d2f4ed8f + // Assemble the raw devp2p protocol stack + stack, err := node.New(&node.Config{ + - Name: "geth", + + Name: coreFaucetNodeName, + Version: params.VersionWithCommit(gitCommit, gitDate), + - DataDir: filepath.Join(os.Getenv("HOME"), ".faucet"), + + DataDir: faucetDataDir, + P2P: p2p.Config{ + - NAT: nat.Any(), + - NoDiscovery: true, + + NAT: nat.Any(), + + // NoDiscovery is DISABLED to allow the node the find peers without relying on manually configured bootnodes. + + // NoDiscovery: true, + DiscoveryV5: true, + ListenAddr: fmt.Sprintf(":%d", port), + MaxPeers: 25, +@@@ -539,70 -247,26 +568,82 @@@ + } + + // Assemble the Ethereum light client protocol +++<<<<<<< HEAD + + cfg := eth.DefaultConfig + + cfg.NetworkId = network + + cfg.Genesis = genesis + + + + switch *syncmodeFlag { + + case "light": + + cfg.SyncMode = downloader.LightSync + + case "fast": + + cfg.SyncMode = downloader.FastSync + + cfg.ProtocolVersions = eth.DefaultProtocolVersions + + case "full": + + cfg.SyncMode = downloader.FullSync + + cfg.ProtocolVersions = eth.DefaultProtocolVersions + + default: + + panic("impossible to reach, this should be handled in the auditFlagUse function") +++======= ++ cfg := ethconfig.Defaults ++ cfg.SyncMode = downloader.LightSync ++ cfg.NetworkId = network ++ cfg.Genesis = genesis ++ utils.SetDNSDiscoveryDefaults(&cfg, genesis.ToBlock(nil).Hash()) ++ ++ lesBackend, err := les.New(stack, &cfg) ++ if err != nil { ++ return nil, fmt.Errorf("Failed to register the Ethereum service: %w", err) +++>>>>>>> c2d2f4ed8f + + } + + + + // Note that we have to set the discovery configs AFTER establishing the configuration + + // sync mode because discovery setting depend on light vs. fast/full. + + switch genesisHash { + + case params.MainnetGenesisHash: + + if genesis.GetChainID().Uint64() == params.DefaultClassicGenesisBlock().GetChainID().Uint64() { + + utils.SetDNSDiscoveryDefaults2(&cfg, params.ClassicDNSNetwork1) + + } else { + + utils.SetDNSDiscoveryDefaults(&cfg, core.GenesisToBlock(genesis, nil).Hash()) + + } + + case params.KottiGenesisHash: + + utils.SetDNSDiscoveryDefaults2(&cfg, params.KottiDNSNetwork1) + + case params.MordorGenesisHash: + + utils.SetDNSDiscoveryDefaults2(&cfg, params.MordorDNSNetwork1) + + default: + + utils.SetDNSDiscoveryDefaults(&cfg, core.GenesisToBlock(genesis, nil).Hash()) + } + + log.Info("Config discovery", "urls", cfg.DiscoveryURLs) + + - // Assemble the ethstats monitoring and reporting service' + - if stats != "" { + - if err := ethstats.New(stack, lesBackend.ApiBackend, lesBackend.Engine(), stats); err != nil { + - return nil, err + + // Establish the backend and enable stats reporting if configured to do so. + + switch *syncmodeFlag { + + case "light": + + lesBackend, err := les.New(stack, &cfg) + + if err != nil { + + return fmt.Errorf("Failed to register the Ethereum service: %w", err) + + } + + if *statsFlag != "" { + + if err := ethstats.New(stack, lesBackend.ApiBackend, lesBackend.Engine(), *statsFlag); err != nil { + + return err + + } + } + + case "fast", "full": + + ethBackend, err := eth.New(stack, &cfg) + + if err != nil { + + return fmt.Errorf("Failed to register the Ethereum service: %w", err) + + } + + if *statsFlag != "" { + + if err := ethstats.New(stack, ethBackend.APIBackend, ethBackend.Engine(), *statsFlag); err != nil { + + return err + + } + + } + + default: + + panic("impossible to reach, this should be handled in the auditFlagUse function") + } + + + // Boot up the client and ensure it connects to bootnodes + if err := stack.Start(); err != nil { + - return nil, err + + return err + } + for _, boot := range enodes { + old, err := enode.Parse(enode.ValidSchemes, boot.String()) +@@@ -718,15 -373,10 +760,19 @@@ func (f *faucet) apiHandler(w http.Resp + f.lock.RLock() + reqs := f.reqs + f.lock.RUnlock() +++<<<<<<< HEAD + + peerCount, err := f.client.PeerCount(context.Background()) + + if err != nil { + + log.Warn("Failed to get peer count", "error", err) + + return + + } + + if err = send(conn, map[string]interface{}{ +++======= ++ if err = send(wsconn, map[string]interface{}{ +++>>>>>>> c2d2f4ed8f + "funds": new(big.Int).Div(balance, ether), + "funded": nonce, + - "peers": f.stack.Server().PeerCount(), + + "peers": peerCount, + "requests": reqs, + }, 3*time.Second); err != nil { + log.Warn("Failed to send initial stats to client", "err", err) +@@@ -748,7 -398,7 +794,11 @@@ + return + } + if !*noauthFlag && !strings.HasPrefix(msg.URL, "https://twitter.com/") && !strings.HasPrefix(msg.URL, "https://www.facebook.com/") { +++<<<<<<< HEAD + + if err = sendError(conn, errors.New("URL doesn't link to supported services")); err != nil { +++======= ++ if err = sendError(wsconn, errors.New("URL doesn't link to supported services")); err != nil { +++>>>>>>> c2d2f4ed8f + log.Warn("Failed to send URL error to client", "err", err) + return + } +@@@ -843,18 -493,10 +893,18 @@@ + amount = new(big.Int).Div(amount, new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(msg.Tier)), nil)) + + tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, amount, 21000, f.price, nil) + - signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainID) + + + + // FIXME(meowsbits): Getting the chain id more than once is redundant and can be optimized. + + chainId, err := f.client.ChainID(context.Background()) + + if err != nil { + + log.Warn("Failed to get chain id", "error", err) + + return + + } + + + + signed, err := f.keystore.SignTx(f.account, tx, chainId) + if err != nil { + f.lock.Unlock() +- if err = sendError(conn, err); err != nil { ++ if err = sendError(wsconn, err); err != nil { + log.Warn("Failed to send transaction creation error to client", "err", err) + return + } +diff --cc cmd/faucet/faucet_test.go +index c991d899fd,4f3e47084e..0000000000 +--- a/cmd/faucet/faucet_test.go ++++ b/cmd/faucet/faucet_test.go +@@@ -1,46 -1,43 +1,91 @@@ +++<<<<<<< HEAD + +package main + + + +import ( + + "fmt" + + "os" + + "path/filepath" + + "testing" + + "time" + +) + + + +func TestMigrateFaucetDirectory(t *testing.T) { + + hardToCollideName := fmt.Sprintf("faucet-migration-test-datadir-%d", time.Now().UnixNano()) + + tempDir := filepath.Join(os.TempDir(), hardToCollideName) + + defer func() { + + os.RemoveAll(tempDir) + + }() + + + + faucetDataDir := filepath.Join(tempDir, "mychain") + + oldFaucetNodeDataDir := filepath.Join(faucetDataDir, multiFaucetNodeName) + + + + if err := os.MkdirAll(oldFaucetNodeDataDir, os.ModePerm); err != nil { + + t.Fatal(err) + + } + + + + filepath.Walk(faucetDataDir, func(path string, info os.FileInfo, err error) error { + + t.Logf("%s", path) + + return nil + + }) + + + + if err := migrateFaucetDirectory(faucetDataDir); err != nil { + + t.Fatal(err) + + } + + + + expected := filepath.Join(faucetDataDir, coreFaucetNodeName) + + d, err := os.Stat(expected) + + if err != nil { + + t.Fatal(err) + + } + + if !d.IsDir() { + + t.Fatal("non-directory") + + } + + filepath.Walk(faucetDataDir, func(path string, info os.FileInfo, err error) error { + + t.Logf("%s", path) + + return nil + + }) +++======= ++ // Copyright 2021 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 . ++ ++ package main ++ ++ import ( ++ "testing" ++ ++ "github.com/ethereum/go-ethereum/common" ++ ) ++ ++ func TestFacebook(t *testing.T) { ++ for _, tt := range []struct { ++ url string ++ want common.Address ++ }{ ++ { ++ "https://www.facebook.com/fooz.gazonk/posts/2837228539847129", ++ common.HexToAddress("0xDeadDeaDDeaDbEefbEeFbEEfBeeFBeefBeeFbEEF"), ++ }, ++ } { ++ _, _, gotAddress, err := authFacebook(tt.url) ++ if err != nil { ++ t.Fatal(err) ++ } ++ if gotAddress != tt.want { ++ t.Fatalf("address wrong, have %v want %v", gotAddress, tt.want) ++ } ++ } +++>>>>>>> c2d2f4ed8f + } +diff --cc cmd/geth/chaincmd.go +index 1d7688b5a4,ff9581fd88..0000000000 +--- a/cmd/geth/chaincmd.go ++++ b/cmd/geth/chaincmd.go +@@@ -19,9 -19,7 +19,8 @@@ package mai + import ( + "encoding/json" + "fmt" + + "io/ioutil" + "os" +- "path/filepath" + "runtime" + "strconv" + "sync/atomic" +@@@ -167,31 -158,15 +165,27 @@@ The export-preimages command export has + utils.SyncModeFlag, + utils.FakePoWFlag, + utils.MainnetFlag, +++<<<<<<< HEAD + + utils.ClassicFlag, + + utils.MordorFlag, + + utils.KottiFlag, +++======= ++ utils.RopstenFlag, +++>>>>>>> c2d2f4ed8f + utils.RinkebyFlag, + - utils.TxLookupLimitFlag, + utils.GoerliFlag, +++<<<<<<< HEAD + + utils.TxLookupLimitFlag, + + utils.YoloV2Flag, + + utils.LegacyTestnetFlag, +++======= ++ utils.YoloV3Flag, +++>>>>>>> c2d2f4ed8f + }, + Category: "BLOCKCHAIN COMMANDS", + Description: ` + -The first argument must be the directory containing the blockchain to download from`, + +The first argument must be the chaindata directory containing the blockchain to download from. + +The second argument can be the ancient chain directory path (default = inside chaindata).`, +- } +- removedbCommand = cli.Command{ +- Action: utils.MigrateFlags(removeDB), +- Name: "removedb", +- Usage: "Remove blockchain and state databases", +- ArgsUsage: " ", +- Flags: []cli.Flag{ +- utils.DataDirFlag, +- }, +- Category: "BLOCKCHAIN COMMANDS", +- Description: ` +- Remove blockchain and state databases`, + } + dumpCommand = cli.Command{ + Action: utils.MigrateFlags(dump), +@@@ -212,29 -187,6 +206,32 @@@ + The arguments are interpreted as block numbers or hashes. + Use "ethereum dump 0" to dump the genesis block.`, + } +++<<<<<<< HEAD + + inspectCommand = cli.Command{ + + Action: utils.MigrateFlags(inspect), + + Name: "inspect", + + Usage: "Inspect the storage size for each type of data in the database", + + ArgsUsage: " ", + + Flags: []cli.Flag{ + + utils.DataDirFlag, + + utils.AncientFlag, + + utils.CacheFlag, + + utils.MainnetFlag, + + utils.ClassicFlag, + + utils.MordorFlag, + + utils.KottiFlag, + + utils.LegacyTestnetFlag, + + utils.RopstenFlag, + + utils.RinkebyFlag, + + utils.GoerliFlag, + + utils.YoloV2Flag, + + utils.LegacyTestnetFlag, + + utils.SyncModeFlag, + + }, + + Category: "BLOCKCHAIN COMMANDS", + + } +++======= +++>>>>>>> c2d2f4ed8f + ) + + // initGenesis will initialise the given JSON format genesis file and writes it as +diff --cc cmd/geth/config.go +index 80aea54443,6fc75363c6..0000000000 +--- a/cmd/geth/config.go ++++ b/cmd/geth/config.go +@@@ -25,13 -25,12 +25,17 @@@ import + "reflect" + "unicode" + +++<<<<<<< HEAD + + "github.com/ethereum/go-ethereum/common/math" + + cli "gopkg.in/urfave/cli.v1" +++======= ++ "gopkg.in/urfave/cli.v1" +++>>>>>>> c2d2f4ed8f + + "github.com/ethereum/go-ethereum/cmd/utils" +- "github.com/ethereum/go-ethereum/eth" ++ "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/internal/ethapi" +- "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/naoina/toml" +@@@ -154,31 -140,11 +145,27 @@@ func makeConfigNode(ctx *cli.Context) ( + // makeFullNode loads geth configuration and creates the Ethereum backend. + func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { + stack, cfg := makeConfigNode(ctx) +++<<<<<<< HEAD + + + + // Handle cross-chain configuration override cases. + + if ctx.GlobalIsSet(utils.ECBP1100Flag.Name) { + + if n := ctx.GlobalUint64(utils.ECBP1100Flag.Name); n != math.MaxUint64 { + + cfg.Eth.ECBP1100 = new(big.Int).SetUint64(n) + + } + + } + + if ctx.GlobalIsSet(utils.ECBP1100NoDisableFlag.Name) { + + if enable := ctx.GlobalBool(utils.ECBP1100NoDisableFlag.Name); enable { + + cfg.Eth.ECBP1100NoDisable = &enable + + } + + } + + +++======= ++ if ctx.GlobalIsSet(utils.OverrideBerlinFlag.Name) { ++ cfg.Eth.OverrideBerlin = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideBerlinFlag.Name)) ++ } +++>>>>>>> c2d2f4ed8f + backend := utils.RegisterEthService(stack, &cfg.Eth) + +- checkWhisper(ctx) + // Configure GraphQL if requested + if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) { + utils.RegisterGraphQLService(stack, backend, cfg.Node) +diff --cc cmd/geth/consolecmd_test.go +index 59cb6d6ec9,c3f41b187c..0000000000 +--- a/cmd/geth/consolecmd_test.go ++++ b/cmd/geth/consolecmd_test.go +@@@ -37,172 -35,16 +37,185 @@@ const + httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0" + ) + +++<<<<<<< HEAD + +// TestConsoleCmdNetworkIdentities tests network identity variables at runtime for a geth instance. + +// This provides a "production equivalent" integration test for consensus-relevant chain identity values which + +// cannot be adequately unit tested because of reliance on cli context variables. + +// These tests should cover expected default values and possible flag-interacting values, like -- with --networkid=n. + +func TestConsoleCmdNetworkIdentities(t *testing.T) { + + chainIdentityCases := []struct { + + flags []string + + networkId int + + chainId int + + genesisHash string + + }{ + + // Default chain value, without and with --networkid flag set. + + {[]string{}, 1, 1, params.MainnetGenesisHash.Hex()}, + + {[]string{"--networkid", "42"}, 42, 1, params.MainnetGenesisHash.Hex()}, + + + + // Non-default chain value, without and with --networkid flag set. + + {[]string{"--classic"}, 1, 61, params.MainnetGenesisHash.Hex()}, + + {[]string{"--classic", "--networkid", "42"}, 42, 61, params.MainnetGenesisHash.Hex()}, + + + + // All other possible -- values. + + {[]string{"--mainnet"}, 1, 1, params.MainnetGenesisHash.Hex()}, + + {[]string{"--testnet"}, 3, 3, params.RopstenGenesisHash.Hex()}, + + {[]string{"--ropsten"}, 3, 3, params.RopstenGenesisHash.Hex()}, + + {[]string{"--rinkeby"}, 4, 4, params.RinkebyGenesisHash.Hex()}, + + {[]string{"--goerli"}, 5, 5, params.GoerliGenesisHash.Hex()}, + + {[]string{"--kotti"}, 6, 6, params.KottiGenesisHash.Hex()}, + + {[]string{"--mordor"}, 7, 63, params.MordorGenesisHash.Hex()}, + + {[]string{"--yolov2"}, 133519467574834, 133519467574834, params.YoloV2GenesisHash.Hex()}, + + } + + for i, p := range chainIdentityCases { + + + + // Disable networking, preventing false-negatives if in an environment without networking service + + // or collisions with an existing geth service. + + p.flags = append(p.flags, "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none") + + + + t.Run(fmt.Sprintf("%d/%v/networkid", i, p.flags), + + consoleCmdStdoutTest(p.flags, "admin.nodeInfo.protocols.eth.network", p.networkId)) + + t.Run(fmt.Sprintf("%d/%v/chainid", i, p.flags), + + consoleCmdStdoutTest(p.flags, "admin.nodeInfo.protocols.eth.config.chainId", p.chainId)) + + t.Run(fmt.Sprintf("%d/%v/genesis_hash", i, p.flags), + + consoleCmdStdoutTest(p.flags, "eth.getBlock(0, false).hash", strconv.Quote(p.genesisHash))) + + } + +} + + + +func consoleCmdStdoutTest(flags []string, execCmd string, want interface{}) func(t *testing.T) { + + return func(t *testing.T) { + + flags = append(flags, "--exec", execCmd, "console") + + geth := runGeth(t, flags...) + + geth.Expect(fmt.Sprintf(`%v + +`, want)) + + geth.ExpectExit() + + if status := geth.ExitStatus(); status != 0 { + + t.Errorf("expected exit status 0, got: %d", status) + + } + + } + +} + + + +// TestGethFailureToLaunch tests that geth fail immediately when given invalid run parameters (ie CLI args). + +func TestGethFailureToLaunch(t *testing.T) { + + cases := []struct { + + flags []string + + expectErrorReStr string + + }{ + + { + + flags: []string{"--badnet"}, + + expectErrorReStr: "(?ism)incorrect usage.*", + + }, + + } + + for _, c := range cases { + + t.Run(fmt.Sprintf("TestIncorrectUsage: %v", c.flags), func(t *testing.T) { + + geth := runGeth(t, c.flags...) + + geth.ExpectRegexp(c.expectErrorReStr) + + geth.ExpectExit() + + if status := geth.ExitStatus(); status == 0 { + + t.Errorf("expected exit status != 0, got: %d", status) + + } + + }) + + } + +} + + + +// TestGethStartupLogs tests that geth logs certain things (given some set of flags). + +// In these cases, geth is run with a console command to print its name (and tests that it does). + +func TestGethStartupLogs(t *testing.T) { + + // semiPersistentDatadir is used to house an adhoc datadir for co-dependent geth test cases. + + semiPersistentDatadir := filepath.Join(os.TempDir(), fmt.Sprintf("geth-startup-logs-test-%d", time.Now().Unix())) + + defer os.RemoveAll(semiPersistentDatadir) + + + + type matching struct { + + pattern string // pattern is the pattern to match against geth's stderr log. + + matches bool // matches defines if the pattern should succeed or fail, ie. if the pattern should exist or should not exist. + + } + + cases := []struct { + + flags []string + + matchers []matching + + + + // callback is run after the geth run case completes. + + // It can be used to reset any persistent state to provide a clean slate for the subsequent cases. + + callback func() error + + }{ + + { + + // -- flag is NOT given and datadir does not exist, representing a first tabula-rasa run. + + // Use without a -- flag is deprecated. User will be warned. + + flags: []string{}, + + matchers: []matching{ + + {pattern: "(?ism).+WARN.+Not specifying a chain flag is deprecated.*", matches: true}, + + }, + + }, + + { + + // Network flag is given. + + // -- flag is NOT given. This is deprecated. User will be warned. + + // Same same but different as above. + + flags: []string{"--networkid=42"}, + + matchers: []matching{ + + {pattern: "(?ism).+WARN.+Not specifying a chain flag is deprecated.*", matches: true}, + + }, + + }, + + // Little bit of a HACK. + + // This is a co-dependent sequence of two test cases. + + // First, startup a geth instance that will create a database, storing the genesis block. + + // This is a basic use case and has no errors. + + // The subsequent case then run geth re-using that datadir which has an existing chain database + + // and contains a stored genesis block. + + // Since the database contains a genesis block, the chain identity and config can (and will) be deduced from it; + + // this causes no need for a -- CLI flag to be passed again. The user will not be warned of a missing -- flag. + + { + + // -- flag is given. All is well. Database (storing genesis) is initialized. + + flags: []string{"--datadir", semiPersistentDatadir, "--mainnet"}, + + matchers: []matching{ + + {pattern: "(?ism).*", matches: true}, + + }, + + }, + + { + + // -- flag is NOT given, BUT geth is being run on top of an existing + + // datadir. Geth will use the existing (stored) genesis found in it. + + // User should NOT be warned. + + flags: []string{"--datadir", semiPersistentDatadir}, + + matchers: []matching{ + + {pattern: "(?ism).+WARN.+Not specifying a chain flag is deprecated.*", matches: false}, + + {pattern: "(?ism).+INFO.+Found stored genesis block.*", matches: true}, + + }, + + callback: func() error { + + // Clean up this mini-suite. + + return os.RemoveAll(semiPersistentDatadir) + + }, + + }, + + } + + for i, c := range cases { + + t.Run(fmt.Sprintf("TestGethStartupLogs/%d: %v", i, c.flags), func(t *testing.T) { + + geth := runGeth(t, append(c.flags, "--exec", "admin.nodeInfo.name", "console")...) + + geth.ExpectRegexp("(?ism).*CoreGeth.*") + + geth.ExpectExit() + + if status := geth.ExitStatus(); status != 0 { + + t.Errorf("expected exit status == 0, got: %d", status) + + } + + for _, match := range c.matchers { + + if matched := regexp.MustCompile(match.pattern).MatchString(geth.StderrText()); matched != match.matches { + + t.Errorf("unexpected stderr output; want: %s (matching?=%v) got: %s", match.pattern, match.matches, geth.StderrText()) + + } + + } + + if c.callback != nil { + + if err := c.callback(); err != nil { + + t.Fatal(err) + + } + + } + + }) + + } +++======= ++ // spawns geth with the given command line args, using a set of flags to minimise ++ // memory and disk IO. If the args don't set --datadir, the ++ // child g gets a temporary data directory. ++ func runMinimalGeth(t *testing.T, args ...string) *testgeth { ++ // --ropsten to make the 'writing genesis to disk' faster (no accounts) ++ // --networkid=1337 to avoid cache bump ++ // --syncmode=full to avoid allocating fast sync bloom ++ allArgs := []string{"--ropsten", "--networkid", "1337", "--syncmode=full", "--port", "0", ++ "--nat", "none", "--nodiscover", "--maxpeers", "0", "--cache", "64"} ++ return runGeth(t, append(allArgs, args...)...) +++>>>>>>> c2d2f4ed8f + } + + // Tests that a node embedded within a console can be started up properly and +@@@ -211,21 -53,9 +224,18 @@@ func TestConsoleWelcome(t *testing.T) + coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" + + // Start a geth console, make sure it's cleaned up and terminate the console +- geth := runGeth(t, +- "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", +- "--etherbase", coinbase, +- "console") ++ geth := runMinimalGeth(t, "--miner.etherbase", coinbase, "console") + + // Gather all the infos the welcome message needs to contain + + geth.SetTemplateFunc("clientname", func() string { + + if params.VersionName != "" { + + return params.VersionName + + } + + if geth.Name() != "" { + + return geth.Name() + + } + + return strings.Title(clientIdentifier) + + }) + geth.SetTemplateFunc("goos", func() string { return runtime.GOOS }) + geth.SetTemplateFunc("goarch", func() string { return runtime.GOARCH }) + geth.SetTemplateFunc("gover", runtime.Version) +diff --cc cmd/geth/main.go +index 219d91a56a,207c93c0d8..0000000000 +--- a/cmd/geth/main.go ++++ b/cmd/geth/main.go +@@@ -62,11 -61,9 +62,13 @@@ var + utils.UnlockedAccountFlag, + utils.PasswordFileFlag, + utils.BootnodesFlag, +- utils.LegacyBootnodesV4Flag, +- utils.LegacyBootnodesV5Flag, + utils.DataDirFlag, + utils.AncientFlag, +++<<<<<<< HEAD + + utils.AncientRPCFlag, +++======= ++ utils.MinFreeDiskSpaceFlag, +++>>>>>>> c2d2f4ed8f + utils.KeyStoreDirFlag, + utils.ExternalSignerFlag, + utils.NoUSBFlag, +@@@ -140,29 -135,20 +140,33 @@@ + utils.NodeKeyFileFlag, + utils.NodeKeyHexFlag, + utils.DNSDiscoveryFlag, +++<<<<<<< HEAD + + utils.EthProtocolsFlag, + utils.MainnetFlag, + utils.DeveloperFlag, + utils.DeveloperPeriodFlag, + + utils.DeveloperPoWFlag, + + utils.ClassicFlag, + + utils.MordorFlag, + + utils.LegacyTestnetFlag, +++======= +++ utils.MainnetFlag, +++ utils.DeveloperFlag, +++ utils.DeveloperPeriodFlag, +++>>>>>>> c2d2f4ed8f + utils.RopstenFlag, + utils.RinkebyFlag, + + utils.KottiFlag, + utils.GoerliFlag, +- utils.YoloV2Flag, ++ utils.YoloV3Flag, + utils.VMEnableDebugFlag, + utils.NetworkIdFlag, + utils.EthStatsURLFlag, + utils.FakePoWFlag, + + utils.FakePoWPoissonFlag, + utils.NoCompactionFlag, + utils.GpoBlocksFlag, +- utils.LegacyGpoBlocksFlag, + utils.GpoPercentileFlag, +- utils.LegacyGpoPercentileFlag, + utils.GpoMaxGasPriceFlag, + utils.EWASMInterpreterFlag, + utils.EVMInterpreterFlag, +@@@ -228,7 -204,7 +224,11 @@@ func init() + // Initialize the CLI app and start Geth + app.Action = geth + app.HideVersion = true // we have a command to print the version +++<<<<<<< HEAD + + app.Copyright = "Copyright 2013-2020 The core-geth and go-ethereum Authors" +++======= ++ app.Copyright = "Copyright 2013-2021 The go-ethereum Authors" +++>>>>>>> c2d2f4ed8f + app.Commands = []cli.Command{ + // See chaincmd.go: + initCommand, +@@@ -287,15 -263,11 +287,10 @@@ func main() + } + } + + -// prepare manipulates memory cache allowance and setups metric system. + -// This function should be called before launching devp2p stack. + -func prepare(ctx *cli.Context) { + - // If we're running a known preset, log it for convenience. + +func checkMainnet(ctx *cli.Context) bool { + + isMainnet := false + + + switch { +- case ctx.GlobalIsSet(utils.LegacyTestnetFlag.Name): +- log.Info("Starting Geth on Ropsten testnet...") +- log.Warn("The --testnet flag is ambiguous! Please specify one of --goerli, --rinkeby, or --ropsten.") +- log.Warn("The generic --testnet flag is deprecated and will be removed in the future!") +- + case ctx.GlobalIsSet(utils.RopstenFlag.Name): + log.Info("Starting Geth on Ropsten testnet...") + +@@@ -305,23 -277,11 +300,26 @@@ + case ctx.GlobalIsSet(utils.GoerliFlag.Name): + log.Info("Starting Geth on Görli testnet...") + ++ case ctx.GlobalIsSet(utils.YoloV3Flag.Name): ++ log.Info("Starting Geth on YOLOv3 testnet...") ++ + case ctx.GlobalIsSet(utils.DeveloperFlag.Name): + - log.Info("Starting Geth in ephemeral dev mode...") + + log.Info("Starting Geth in ephemeral proof-of-authority network dev mode...") + + + + case ctx.GlobalIsSet(utils.DeveloperPoWFlag.Name): + + log.Info("Starting Geth in ephemeral proof-of-work network dev mode...") + + + + case ctx.GlobalIsSet(utils.ClassicFlag.Name): + + log.Info("Starting Geth on Ethereum Classic...") + + + + case ctx.GlobalIsSet(utils.MordorFlag.Name): + + log.Info("Starting Geth on Mordor testnet...") + + + + case ctx.GlobalIsSet(utils.KottiFlag.Name): + + log.Info("Starting Geth on Kotti testnet...") + + + + case ctx.GlobalIsSet(utils.YoloV2Flag.Name): + + log.Info("Starting Geth on YoloV2 testnet...") + + case !ctx.GlobalIsSet(utils.NetworkIdFlag.Name): + log.Info("Starting Geth on Ethereum mainnet...") +@@@ -341,7 -289,7 +339,11 @@@ func prepare(ctx *cli.Context) + // If we're a full node on mainnet without --cache specified, bump default cache allowance + if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) { + // Make sure we're not on any supported preconfigured testnet either +++<<<<<<< HEAD + + if isMainnet { +++======= ++ if !ctx.GlobalIsSet(utils.RopstenFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) && !ctx.GlobalIsSet(utils.DeveloperFlag.Name) { +++>>>>>>> c2d2f4ed8f + // Nope, we're really on mainnet. Bump that cache up! + log.Info("Bumping default cache on mainnet", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 4096) + ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(4096)) +@@@ -493,16 -439,6 +494,16 @@@ func startNode(ctx *cli.Context, stack + ethBackend.TxPool().SetGasPrice(gasprice) + // start mining + threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name) +++<<<<<<< HEAD + + if ctx.GlobalIsSet(utils.LegacyMinerThreadsFlag.Name) && !ctx.GlobalIsSet(utils.MinerThreadsFlag.Name) { + + threads = ctx.GlobalInt(utils.LegacyMinerThreadsFlag.Name) + + log.Warn("The flag --minerthreads is deprecated and will be removed in the future, please use --miner.threads") + + } + + if isDeveloperMode && !ctx.GlobalIsSet(utils.MinerThreadsFlag.Name) && !ctx.GlobalIsSet(utils.LegacyMinerThreadsFlag.Name) { + + threads = 1 + + } +++======= +++>>>>>>> c2d2f4ed8f + if err := ethBackend.StartMining(threads); err != nil { + utils.Fatalf("Failed to start mining: %v", err) + } +diff --cc cmd/geth/misccmd.go +index 97e4fe806a,b347d31d97..0000000000 +--- a/cmd/geth/misccmd.go ++++ b/cmd/geth/misccmd.go +@@@ -132,7 -142,6 +158,10 @@@ func version(ctx *cli.Context) error + fmt.Println("Git Commit Date:", gitDate) + } + fmt.Println("Architecture:", runtime.GOARCH) +++<<<<<<< HEAD + + fmt.Println("Protocol Versions:", eth.DefaultProtocolVersions) +++======= +++>>>>>>> c2d2f4ed8f + fmt.Println("Go Version:", runtime.Version()) + fmt.Println("Operating System:", runtime.GOOS) + fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH")) +diff --cc cmd/geth/usage.go +index 5d168cab14,daea0afc4e..0000000000 +--- a/cmd/geth/usage.go ++++ b/cmd/geth/usage.go +@@@ -36,19 -36,16 +36,29 @@@ var AppHelpFlagGroups = []flags.FlagGro + configFileFlag, + utils.DataDirFlag, + utils.AncientFlag, +++<<<<<<< HEAD + + utils.AncientRPCFlag, +++======= ++ utils.MinFreeDiskSpaceFlag, +++>>>>>>> c2d2f4ed8f + utils.KeyStoreDirFlag, +- utils.NoUSBFlag, ++ utils.USBFlag, + utils.SmartCardDaemonPathFlag, + utils.NetworkIdFlag, + utils.MainnetFlag, +++<<<<<<< HEAD + + utils.ClassicFlag, + + utils.MordorFlag, + + utils.KottiFlag, + + utils.GoerliFlag, + + utils.RinkebyFlag, +++======= ++ utils.GoerliFlag, ++ utils.RinkebyFlag, ++ utils.YoloV3Flag, +++>>>>>>> c2d2f4ed8f + utils.RopstenFlag, + + utils.YoloV2Flag, + utils.SyncModeFlag, + utils.ExitWhenSyncedFlag, + utils.GCModeFlag, +@@@ -163,10 -162,7 +178,8 @@@ + Name: "NETWORKING", + Flags: []cli.Flag{ + utils.BootnodesFlag, +- utils.LegacyBootnodesV4Flag, +- utils.LegacyBootnodesV5Flag, + utils.DNSDiscoveryFlag, + + utils.EthProtocolsFlag, + utils.ListenPortFlag, + utils.MaxPeersFlag, + utils.MaxPendingPeersFlag, +diff --cc cmd/utils/flags.go +index f49010f9a5,fc479f3987..0000000000 +--- a/cmd/utils/flags.go ++++ b/cmd/utils/flags.go +@@@ -65,11 -64,8 +66,11 @@@ import + "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/params/types/ctypes" + + "github.com/ethereum/go-ethereum/params/types/genesisT" + + "github.com/ethereum/go-ethereum/params/vars" + pcsclite "github.com/gballet/go-libpcsclite" +- cli "gopkg.in/urfave/cli.v1" ++ "gopkg.in/urfave/cli.v1" + ) + + func init() { +@@@ -118,10 -114,9 +119,16 @@@ var + Name: "datadir.ancient", + Usage: "Data directory for ancient chain segments (default = inside chaindata)", + } +++<<<<<<< HEAD + + AncientRPCFlag = cli.StringFlag{ + + Name: "ancient.rpc", + + Usage: "Connect to a remote freezer via RPC. Value must an HTTP(S), WS(S), unix socket, or 'stdio' URL. Incompatible with --datadir.ancient", + + Value: "", +++======= ++ MinFreeDiskSpaceFlag = DirectoryFlag{ ++ Name: "datadir.minfreedisk", ++ Usage: "Minimum free disk space in MB, once reached triggers auto shut down (default = --cache.gc converted to MB, 0 = disabled)", +++>>>>>>> c2d2f4ed8f + } + KeyStoreDirFlag = DirectoryFlag{ + Name: "keystore", +@@@ -138,25 -137,20 +149,34 @@@ + } + NetworkIdFlag = cli.Uint64Flag{ + Name: "networkid", +++<<<<<<< HEAD + + Usage: "Explicitly set network id (integer)(For testnets: use --ropsten, --rinkeby, --goerli, --kotti, --mordor, --yolov2 instead)", + + Value: eth.DefaultConfig.NetworkId, +++======= ++ Usage: "Explicitly set network id (integer)(For testnets: use --ropsten, --rinkeby, --goerli instead)", ++ Value: ethconfig.Defaults.NetworkId, ++ } ++ MainnetFlag = cli.BoolFlag{ ++ Name: "mainnet", ++ Usage: "Ethereum mainnet", +++>>>>>>> c2d2f4ed8f + } + - GoerliFlag = cli.BoolFlag{ + - Name: "goerli", + - Usage: "Görli network: pre-configured proof-of-authority test network", + + EthProtocolsFlag = cli.StringFlag{ + + Name: "eth.protocols", + + Usage: "Sets the Ethereum Protocol versions (65|64|63) (default = 65,64,63 first is primary)", + + Value: "", + + } + + ClassicFlag = cli.BoolFlag{ + + Name: "classic", + + Usage: "Ethereum Classic network: pre-configured Ethereum Classic mainnet", + + } + + MainnetFlag = cli.BoolFlag{ + + Name: "mainnet", + + Usage: "Ethereum mainnet: pre-configured Ethereum mainnet", + } +- YoloV2Flag = cli.BoolFlag{ +- Name: "yolov2", +- Usage: "YOLOv2 network: pre-configured proof-of-authority shortlived test network.", ++ YoloV3Flag = cli.BoolFlag{ ++ Name: "yolov3", ++ Usage: "YOLOv3 network: pre-configured proof-of-authority shortlived test network.", + } + RinkebyFlag = cli.BoolFlag{ + Name: "rinkeby", +@@@ -307,8 -298,8 +340,13 @@@ + } + EthashDatasetDirFlag = DirectoryFlag{ + Name: "ethash.dagdir", +++<<<<<<< HEAD + + Usage: "Directory to store the [ethash|etchash] mining DAGs based on supported network", + + Value: DirectoryString(eth.DefaultConfig.Ethash.DatasetDir), +++======= ++ Usage: "Directory to store the ethash mining DAGs", ++ Value: DirectoryString(ethconfig.Defaults.Ethash.DatasetDir), +++>>>>>>> c2d2f4ed8f + } + EthashDatasetsInMemoryFlag = cli.IntFlag{ + Name: "ethash.dagsinmem", +@@@ -781,22 -754,24 +819,38 @@@ + // if none (or the empty string) is specified. If the node is starting a testnet, + // then a subdirectory of the specified datadir will be used. + func MakeDataDir(ctx *cli.Context) string { + + + if path := ctx.GlobalString(DataDirFlag.Name); path != "" { +++<<<<<<< HEAD + + + + // Maintain compatibility with older Geth configurations storing the + + // Ropsten database in `testnet` instead of `ropsten`. + + if ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name) { + + legacyPath := filepath.Join(path, "testnet") + + if _, err := os.Stat(legacyPath); !os.IsNotExist(err) { + + return legacyPath + + } else { + + return filepath.Join(path, "ropsten") + + } +++======= ++ if ctx.GlobalBool(RopstenFlag.Name) { ++ // Maintain compatibility with older Geth configurations storing the ++ // Ropsten database in `testnet` instead of `ropsten`. ++ return filepath.Join(path, "ropsten") ++ } ++ if ctx.GlobalBool(RinkebyFlag.Name) { ++ return filepath.Join(path, "rinkeby") ++ } ++ if ctx.GlobalBool(GoerliFlag.Name) { ++ return filepath.Join(path, "goerli") ++ } ++ if ctx.GlobalBool(YoloV3Flag.Name) { ++ return filepath.Join(path, "yolo-v3") +++>>>>>>> c2d2f4ed8f + } + - return path + + return dataDirPathForCtxChainConfig(ctx, path) + } + - Fatalf("Cannot determine default data directory, please set manually (--datadir)") + + Fatalf("Cannot determine default data directory, please set manually (--%s)", DataDirFlag.Name) + return "" + } + +@@@ -838,26 -813,16 +892,32 @@@ func setNodeUserIdent(ctx *cli.Context + func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { + urls := params.MainnetBootnodes + switch { +++<<<<<<< HEAD + + case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(LegacyBootnodesV4Flag.Name): + + if ctx.GlobalIsSet(LegacyBootnodesV4Flag.Name) { + + urls = SplitAndTrim(ctx.GlobalString(LegacyBootnodesV4Flag.Name)) + + } else { + + urls = SplitAndTrim(ctx.GlobalString(BootnodesFlag.Name)) + + } + + case ctx.GlobalBool(ClassicFlag.Name): + + urls = params.ClassicBootnodes + + case ctx.GlobalBool(MordorFlag.Name): + + urls = params.MordorBootnodes + + case ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name): +++======= ++ case ctx.GlobalIsSet(BootnodesFlag.Name): ++ urls = SplitAndTrim(ctx.GlobalString(BootnodesFlag.Name)) ++ case ctx.GlobalBool(RopstenFlag.Name): +++>>>>>>> c2d2f4ed8f + urls = params.RopstenBootnodes + case ctx.GlobalBool(RinkebyFlag.Name): + urls = params.RinkebyBootnodes + + case ctx.GlobalBool(KottiFlag.Name): + + urls = params.KottiBootnodes + case ctx.GlobalBool(GoerliFlag.Name): + urls = params.GoerliBootnodes +- case ctx.GlobalBool(YoloV2Flag.Name): +- urls = params.YoloV2Bootnodes ++ case ctx.GlobalBool(YoloV3Flag.Name): ++ urls = params.YoloV3Bootnodes + case cfg.BootstrapNodes != nil: + return // already set, don't apply defaults. + } +@@@ -878,28 -843,10 +938,33 @@@ + // setBootstrapNodesV5 creates a list of bootstrap nodes from the command line + // flags, reverting to pre-configured ones if none have been specified. + func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) { +- urls := params.MainnetBootnodes ++ urls := params.V5Bootnodes + switch { +++<<<<<<< HEAD + + case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(LegacyBootnodesV5Flag.Name): + + if ctx.GlobalIsSet(LegacyBootnodesV5Flag.Name) { + + urls = SplitAndTrim(ctx.GlobalString(LegacyBootnodesV5Flag.Name)) + + } else { + + urls = SplitAndTrim(ctx.GlobalString(BootnodesFlag.Name)) + + } + + case ctx.GlobalIsSet(ClassicFlag.Name): + + urls = params.ClassicBootnodes + + case ctx.GlobalIsSet(MordorFlag.Name): + + urls = params.MordorBootnodes + + case ctx.GlobalBool(RopstenFlag.Name): + + urls = params.RopstenBootnodes + + case ctx.GlobalBool(RinkebyFlag.Name): + + urls = params.RinkebyBootnodes + + case ctx.GlobalBool(KottiFlag.Name): + + urls = params.KottiBootnodes + + case ctx.GlobalBool(GoerliFlag.Name): + + urls = params.GoerliBootnodes + + case ctx.GlobalBool(YoloV2Flag.Name): + + urls = params.YoloV2Bootnodes +++======= ++ case ctx.GlobalIsSet(BootnodesFlag.Name): ++ urls = SplitAndTrim(ctx.GlobalString(BootnodesFlag.Name)) +++>>>>>>> c2d2f4ed8f + case cfg.BootstrapNodesV5 != nil: + return // already set, don't apply defaults. + } +@@@ -1332,28 -1244,26 +1382,42 @@@ func setDataDir(ctx *cli.Context, cfg * + switch { + case ctx.GlobalIsSet(DataDirFlag.Name): + cfg.DataDir = ctx.GlobalString(DataDirFlag.Name) + - case ctx.GlobalBool(DeveloperFlag.Name): + + + + case ctx.GlobalBool(DeveloperFlag.Name) || ctx.GlobalBool(DeveloperPoWFlag.Name): + cfg.DataDir = "" // unless explicitly requested, use memory databases +++<<<<<<< HEAD + + + + case (ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name)) && cfg.DataDir == vars.DefaultDataDir(): + + +++======= ++ case ctx.GlobalBool(RopstenFlag.Name) && cfg.DataDir == node.DefaultDataDir(): +++>>>>>>> c2d2f4ed8f + // Maintain compatibility with older Geth configurations storing the + // Ropsten database in `testnet` instead of `ropsten`. + - legacyPath := filepath.Join(node.DefaultDataDir(), "testnet") + + legacyPath := filepath.Join(vars.DefaultDataDir(), "testnet") + + + if _, err := os.Stat(legacyPath); !os.IsNotExist(err) { + + + log.Warn("Using the deprecated `testnet` datadir. Future versions will store the Ropsten chain in `ropsten`.") + cfg.DataDir = legacyPath + } else { + - cfg.DataDir = filepath.Join(node.DefaultDataDir(), "ropsten") + + + + cfg.DataDir = filepath.Join(vars.DefaultDataDir(), "ropsten") + + + } + +++<<<<<<< HEAD + + case cfg.DataDir == vars.DefaultDataDir(): + + cfg.DataDir = dataDirPathForCtxChainConfig(ctx, vars.DefaultDataDir()) +++======= ++ cfg.DataDir = filepath.Join(node.DefaultDataDir(), "ropsten") ++ case ctx.GlobalBool(RinkebyFlag.Name) && cfg.DataDir == node.DefaultDataDir(): ++ cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby") ++ case ctx.GlobalBool(GoerliFlag.Name) && cfg.DataDir == node.DefaultDataDir(): ++ cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli") ++ case ctx.GlobalBool(YoloV3Flag.Name) && cfg.DataDir == node.DefaultDataDir(): ++ cfg.DataDir = filepath.Join(node.DefaultDataDir(), "yolo-v3") +++>>>>>>> c2d2f4ed8f + } + } + +@@@ -1426,47 -1328,12 +1482,52 @@@ func setTxPool(ctx *cli.Context, cfg *c + } + } + +++<<<<<<< HEAD + +func setEthashDatasetDir(ctx *cli.Context, cfg *eth.Config) { + + switch { + + case ctx.GlobalIsSet(EthashDatasetDirFlag.Name): + + cfg.Ethash.DatasetDir = ctx.GlobalString(EthashDatasetDirFlag.Name) + + + + case (ctx.GlobalBool(ClassicFlag.Name) || ctx.GlobalBool(MordorFlag.Name)) && cfg.Ethash.DatasetDir == eth.DefaultConfig.Ethash.DatasetDir: + + // ECIP-1099 is set, use etchash dir for DAGs instead + + home := HomeDir() + + if runtime.GOOS == "darwin" { + + cfg.Ethash.DatasetDir = filepath.Join(home, "Library", "Etchash") + + } else if runtime.GOOS == "windows" { + + localappdata := os.Getenv("LOCALAPPDATA") + + if localappdata != "" { + + cfg.Ethash.DatasetDir = filepath.Join(localappdata, "Etchash") + + } else { + + cfg.Ethash.DatasetDir = filepath.Join(home, "AppData", "Local", "Etchash") + + } + + } else { + + cfg.Ethash.DatasetDir = filepath.Join(home, ".etchash") + + } + + } + +} + + + +func setEthashCacheDir(ctx *cli.Context, cfg *eth.Config) { + + switch { + + case ctx.GlobalIsSet(EthashCacheDirFlag.Name): +++======= ++ func setEthash(ctx *cli.Context, cfg *ethconfig.Config) { ++ if ctx.GlobalIsSet(EthashCacheDirFlag.Name) { +++>>>>>>> c2d2f4ed8f + cfg.Ethash.CacheDir = ctx.GlobalString(EthashCacheDirFlag.Name) + + + + case (ctx.GlobalBool(ClassicFlag.Name) || ctx.GlobalBool(MordorFlag.Name)) && cfg.Ethash.CacheDir == eth.DefaultConfig.Ethash.CacheDir: + + // ECIP-1099 is set, use etchash dir for caches instead + + cfg.Ethash.CacheDir = "etchash" + } + - if ctx.GlobalIsSet(EthashDatasetDirFlag.Name) { + - cfg.Ethash.DatasetDir = ctx.GlobalString(EthashDatasetDirFlag.Name) + +} + + + +func setEthash(ctx *cli.Context, cfg *eth.Config) { + + // ECIP-1099 + + setEthashCacheDir(ctx, cfg) + + setEthashDatasetDir(ctx, cfg) + + + + if ctx.GlobalBool(FakePoWPoissonFlag.Name) { + + cfg.Ethash.PowMode = ethash.ModePoissonFake + } + if ctx.GlobalIsSet(EthashCachesInMemoryFlag.Name) { + cfg.Ethash.CachesInMem = ctx.GlobalInt(EthashCachesInMemoryFlag.Name) +@@@ -1588,32 -1443,19 +1637,35 @@@ func CheckExclusive(ctx *cli.Context, a + } + } + +- // SetShhConfig applies shh-related command line flags to the config. +- func SetShhConfig(ctx *cli.Context, stack *node.Node) { +- if ctx.GlobalIsSet(WhisperEnabledFlag.Name) || +- ctx.GlobalIsSet(WhisperMaxMessageSizeFlag.Name) || +- ctx.GlobalIsSet(WhisperMinPOWFlag.Name) || +- ctx.GlobalIsSet(WhisperRestrictConnectionBetweenLightClientsFlag.Name) { +- log.Warn("Whisper support has been deprecated and the code has been moved to github.com/ethereum/whisper") +- } +- } +- + // SetEthConfig applies eth-related command line flags to the config. +- func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { ++ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { + // Avoid conflicting network flags +++<<<<<<< HEAD + + CheckExclusive(ctx, DeveloperFlag, DeveloperPoWFlag, LegacyTestnetFlag, MainnetFlag, RopstenFlag, RinkebyFlag, GoerliFlag, YoloV2Flag, ClassicFlag, KottiFlag, MordorFlag) + + CheckExclusive(ctx, LegacyLightServFlag, LightServeFlag, SyncModeFlag, "light") + + CheckExclusive(ctx, DeveloperFlag, DeveloperPoWFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer + + CheckExclusive(ctx, GCModeFlag, "archive", TxLookupLimitFlag) + + // todo(rjl493456442) make it available for les server + + // Ancient tx indices pruning is not available for les server now + + // since light client relies on the server for transaction status query. + + CheckExclusive(ctx, LegacyLightServFlag, LightServeFlag, TxLookupLimitFlag) + + + + CheckExclusive(ctx, AncientFlag, AncientRPCFlag) + + CheckExclusive(ctx, DeveloperPoWFlag, DeveloperPeriodFlag, FakePoWFlag) + + CheckExclusive(ctx, FakePoWFlag, FakePoWPoissonFlag) + + +++======= ++ CheckExclusive(ctx, MainnetFlag, DeveloperFlag, RopstenFlag, RinkebyFlag, GoerliFlag, YoloV3Flag) ++ CheckExclusive(ctx, LightServeFlag, SyncModeFlag, "light") ++ CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer ++ if ctx.GlobalString(GCModeFlag.Name) == "archive" && ctx.GlobalUint64(TxLookupLimitFlag.Name) != 0 { ++ ctx.GlobalSet(TxLookupLimitFlag.Name, "0") ++ log.Warn("Disable transaction unindexing for archive node") ++ } ++ if ctx.GlobalIsSet(LightServeFlag.Name) && ctx.GlobalUint64(TxLookupLimitFlag.Name) != 0 { ++ log.Warn("LES server cannot serve old transaction status and cannot connect below les/4 protocol version if transaction lookup index is limited") ++ } +++>>>>>>> c2d2f4ed8f + var ks *keystore.KeyStore + if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 { + ks = keystores[0].(*keystore.KeyStore) +@@@ -1699,92 -1550,48 +1762,111 @@@ + if ctx.GlobalIsSet(RPCGlobalTxFeeCapFlag.Name) { + cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCapFlag.Name) + } +- if ctx.GlobalIsSet(DNSDiscoveryFlag.Name) { ++ if ctx.GlobalIsSet(NoDiscoverFlag.Name) { ++ cfg.EthDiscoveryURLs, cfg.SnapDiscoveryURLs = []string{}, []string{} ++ } else if ctx.GlobalIsSet(DNSDiscoveryFlag.Name) { + urls := ctx.GlobalString(DNSDiscoveryFlag.Name) + if urls == "" { +- cfg.DiscoveryURLs = []string{} ++ cfg.EthDiscoveryURLs = []string{} + } else { +- cfg.DiscoveryURLs = SplitAndTrim(urls) ++ cfg.EthDiscoveryURLs = SplitAndTrim(urls) + } + } +- + // Override any default configs for hard coded networks. + + + + // Override genesis configuration if a -- flag. + + if gen := genesisForCtxChainConfig(ctx); gen != nil { + + cfg.Genesis = gen + + } + + + + // Establish NetworkID. + + // If dev-mode is used, then NetworkID will be overridden. + + if ctx.GlobalIsSet(NetworkIdFlag.Name) { + + cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name) + + } else if cfg.Genesis != nil { + + cfg.NetworkId = *cfg.Genesis.GetNetworkID() + + } + + + + if ctx.GlobalIsSet(EthProtocolsFlag.Name) { + + protocolVersions := SplitAndTrim(ctx.GlobalString(EthProtocolsFlag.Name)) + + if len(protocolVersions) == 0 { + + Fatalf("--%s must be comma separated list of %s", EthProtocolsFlag.Name, strings.Join(strings.Fields(fmt.Sprint(eth.DefaultProtocolVersions)), ",")) + + } + + + + seenVersions := map[uint]interface{}{} + + for _, versionString := range protocolVersions { + + version, err := strconv.ParseUint(versionString, 10, 0) + + if err != nil { + + Fatalf("--%s has invalid value \"%v\" with error: %v", EthProtocolsFlag.Name, versionString, err) + + } + + + + if _, duplicate := seenVersions[uint(version)]; duplicate { + + Fatalf("--%s has duplicate version of %v", EthProtocolsFlag.Name, versionString) + + } + + + + isValid := false + + for _, proto := range eth.DefaultProtocolVersions { + + if proto == uint(version) { + + isValid = true + + seenVersions[uint(version)] = nil + + break + + } + + } + + + + if !isValid { + + Fatalf("--%s must be comma separated list of %s", EthProtocolsFlag.Name, strings.Join(strings.Fields(fmt.Sprint(eth.DefaultProtocolVersions)), ",")) + + } + + cfg.ProtocolVersions = append(cfg.ProtocolVersions, uint(version)) + + } + + } + + + + // set default protocol versions + + if len(cfg.ProtocolVersions) == 0 { + + cfg.ProtocolVersions = eth.DefaultProtocolVersions + + } + + + + // Set DNS discovery defaults for hard coded networks with DNS defaults. + switch { + case ctx.GlobalBool(MainnetFlag.Name): +++<<<<<<< HEAD +++======= ++ if !ctx.GlobalIsSet(NetworkIdFlag.Name) { ++ cfg.NetworkId = 1 ++ } ++ cfg.Genesis = core.DefaultGenesisBlock() ++ SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash) ++ case ctx.GlobalBool(RopstenFlag.Name): +++>>>>>>> c2d2f4ed8f + if !ctx.GlobalIsSet(NetworkIdFlag.Name) { + - cfg.NetworkId = 3 + + cfg.NetworkId = 1 + } + - cfg.Genesis = core.DefaultRopstenGenesisBlock() + + SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash) + + case ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name): + SetDNSDiscoveryDefaults(cfg, params.RopstenGenesisHash) + case ctx.GlobalBool(RinkebyFlag.Name): + - if !ctx.GlobalIsSet(NetworkIdFlag.Name) { + - cfg.NetworkId = 4 + - } + - cfg.Genesis = core.DefaultRinkebyGenesisBlock() + SetDNSDiscoveryDefaults(cfg, params.RinkebyGenesisHash) + case ctx.GlobalBool(GoerliFlag.Name): + - if !ctx.GlobalIsSet(NetworkIdFlag.Name) { + - cfg.NetworkId = 5 + - } + - cfg.Genesis = core.DefaultGoerliGenesisBlock() + SetDNSDiscoveryDefaults(cfg, params.GoerliGenesisHash) +++<<<<<<< HEAD + + case ctx.GlobalBool(ClassicFlag.Name): + + SetDNSDiscoveryDefaults2(cfg, params.ClassicDNSNetwork1) + + case ctx.GlobalBool(KottiFlag.Name): + + SetDNSDiscoveryDefaults2(cfg, params.KottiDNSNetwork1) + + case ctx.GlobalBool(MordorFlag.Name): + + SetDNSDiscoveryDefaults2(cfg, params.MordorDNSNetwork1) + + default: + + // No -- flag was given. + + } + + + + if ctx.GlobalBool(DeveloperFlag.Name) || ctx.GlobalBool(DeveloperPoWFlag.Name) { +++======= ++ case ctx.GlobalBool(YoloV3Flag.Name): ++ if !ctx.GlobalIsSet(NetworkIdFlag.Name) { ++ cfg.NetworkId = new(big.Int).SetBytes([]byte("yolov3x")).Uint64() // "yolov3x" ++ } ++ cfg.Genesis = core.DefaultYoloV3GenesisBlock() ++ case ctx.GlobalBool(DeveloperFlag.Name): +++>>>>>>> c2d2f4ed8f + if !ctx.GlobalIsSet(NetworkIdFlag.Name) { + cfg.NetworkId = 1337 + } +@@@ -1828,30 -1634,22 +1910,29 @@@ + } + chaindb.Close() + } +- if !ctx.GlobalIsSet(MinerGasPriceFlag.Name) && !ctx.GlobalIsSet(LegacyMinerGasPriceFlag.Name) { ++ if !ctx.GlobalIsSet(MinerGasPriceFlag.Name) { + cfg.Miner.GasPrice = big.NewInt(1) + } + - default: + - if cfg.NetworkId == 1 { + - SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash) + - } + } + } + + +// SetDNSDiscoveryDefaults2 configures DNS discovery with the given URL if no URLs are set. + +func SetDNSDiscoveryDefaults2(cfg *eth.Config, url string) { + + if cfg.DiscoveryURLs != nil { + + return + + } + + if cfg.SyncMode == downloader.LightSync { + + url = strings.Replace(url, "@all.", "@les.", 1) + + } + + cfg.DiscoveryURLs = []string{url} + +} + + + // SetDNSDiscoveryDefaults configures DNS discovery with the given URL if + // no URLs are set. +- func SetDNSDiscoveryDefaults(cfg *eth.Config, genesis common.Hash) { +- if cfg.DiscoveryURLs != nil { ++ func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) { ++ if cfg.EthDiscoveryURLs != nil { + return // already set through flags/config + } +- + protocol := "all" + if cfg.SyncMode == downloader.LightSync { + protocol = "les" +@@@ -1969,29 -1770,19 +2056,42 @@@ func MakeChainDatabase(ctx *cli.Context + return chainDb + } + + -func MakeGenesis(ctx *cli.Context) *core.Genesis { + - var genesis *core.Genesis + +// genesisForCtxChainConfig returns the corresponding Genesis for a non-default flag chain value. + +// If no -- flag is set in the global context, a nil value is returned. + +// It does not handle genesis for --dev mode, since that mode includes but also exceeds + +// chain configuration. + +func genesisForCtxChainConfig(ctx *cli.Context) *genesisT.Genesis { + + var genesis *genesisT.Genesis + switch { +++<<<<<<< HEAD + + case ctx.GlobalBool(MainnetFlag.Name): + + genesis = params.DefaultGenesisBlock() + + case ctx.GlobalBool(ClassicFlag.Name): + + genesis = params.DefaultClassicGenesisBlock() + + case ctx.GlobalBool(MordorFlag.Name): + + genesis = params.DefaultMordorGenesisBlock() + + case ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name): + + genesis = params.DefaultRopstenGenesisBlock() +++======= ++ case ctx.GlobalBool(RopstenFlag.Name): ++ genesis = core.DefaultRopstenGenesisBlock() +++>>>>>>> c2d2f4ed8f + case ctx.GlobalBool(RinkebyFlag.Name): + - genesis = core.DefaultRinkebyGenesisBlock() + + genesis = params.DefaultRinkebyGenesisBlock() + + case ctx.GlobalBool(KottiFlag.Name): + + genesis = params.DefaultKottiGenesisBlock() + case ctx.GlobalBool(GoerliFlag.Name): +++<<<<<<< HEAD + + genesis = params.DefaultGoerliGenesisBlock() + + case ctx.GlobalBool(YoloV2Flag.Name): + + genesis = params.DefaultYoloV2GenesisBlock() +++======= ++ genesis = core.DefaultGoerliGenesisBlock() ++ case ctx.GlobalBool(YoloV3Flag.Name): ++ genesis = core.DefaultYoloV3GenesisBlock() ++ case ctx.GlobalBool(DeveloperFlag.Name): ++ Fatalf("Developer chains are ephemeral") +++>>>>>>> c2d2f4ed8f + } + return genesis + } +@@@ -2012,26 -1796,20 +2112,37 @@@ func MakeChain(ctx *cli.Context, stack + Fatalf("%v", err) + } + var engine consensus.Engine + - if config.Clique != nil { + - engine = clique.New(config.Clique, chainDb) + + if config.GetConsensusEngineType().IsClique() { + + engine = clique.New(&ctypes.CliqueConfig{ + + Period: config.GetCliquePeriod(), + + Epoch: config.GetCliqueEpoch(), + + }, chainDb) + } else { + engine = ethash.NewFaker() + - if !ctx.GlobalBool(FakePoWFlag.Name) { + + if ctx.GlobalBool(FakePoWPoissonFlag.Name) { + + engine = ethash.NewPoissonFaker() + + } else if !ctx.GlobalBool(FakePoWFlag.Name) { + engine = ethash.New(ethash.Config{ +++<<<<<<< HEAD + + CacheDir: stack.ResolvePath(eth.DefaultConfig.Ethash.CacheDir), + + CachesInMem: eth.DefaultConfig.Ethash.CachesInMem, + + CachesOnDisk: eth.DefaultConfig.Ethash.CachesOnDisk, + + CachesLockMmap: eth.DefaultConfig.Ethash.CachesLockMmap, + + DatasetDir: stack.ResolvePath(eth.DefaultConfig.Ethash.DatasetDir), + + DatasetsInMem: eth.DefaultConfig.Ethash.DatasetsInMem, + + DatasetsOnDisk: eth.DefaultConfig.Ethash.DatasetsOnDisk, + + DatasetsLockMmap: eth.DefaultConfig.Ethash.DatasetsLockMmap, + + ECIP1099Block: config.GetEthashECIP1099Transition(), +++======= ++ CacheDir: stack.ResolvePath(ethconfig.Defaults.Ethash.CacheDir), ++ CachesInMem: ethconfig.Defaults.Ethash.CachesInMem, ++ CachesOnDisk: ethconfig.Defaults.Ethash.CachesOnDisk, ++ CachesLockMmap: ethconfig.Defaults.Ethash.CachesLockMmap, ++ DatasetDir: stack.ResolvePath(ethconfig.Defaults.Ethash.DatasetDir), ++ DatasetsInMem: ethconfig.Defaults.Ethash.DatasetsInMem, ++ DatasetsOnDisk: ethconfig.Defaults.Ethash.DatasetsOnDisk, ++ DatasetsLockMmap: ethconfig.Defaults.Ethash.DatasetsLockMmap, +++>>>>>>> c2d2f4ed8f + }, nil, false) + } + } +diff --cc cmd/utils/flags_legacy.go +index 6dc99a968a,fb5fde6576..0000000000 +--- a/cmd/utils/flags_legacy.go ++++ b/cmd/utils/flags_legacy.go +@@@ -34,62 -33,9 +33,56 @@@ var ShowDeprecated = cli.Command + Description: "Show flags that have been deprecated and will soon be removed", + } + +- var DeprecatedFlags = []cli.Flag{ +- LegacyTestnetFlag, +- LegacyLightServFlag, +- LegacyLightPeersFlag, +- LegacyMinerThreadsFlag, +- LegacyMinerGasTargetFlag, +- LegacyMinerGasPriceFlag, +- LegacyMinerEtherbaseFlag, +- LegacyMinerExtraDataFlag, +- } ++ var DeprecatedFlags = []cli.Flag{} + + var ( +++<<<<<<< HEAD + + // (Deprecated April 2018) + + LegacyMinerThreadsFlag = cli.IntFlag{ + + Name: "minerthreads", + + Usage: "Number of CPU threads to use for mining (deprecated, use --miner.threads)", + + Value: 0, + + } + + LegacyMinerGasTargetFlag = cli.Uint64Flag{ + + Name: "targetgaslimit", + + Usage: "Target gas floor for mined blocks (deprecated, use --miner.gastarget)", + + Value: eth.DefaultConfig.Miner.GasFloor, + + } + + LegacyMinerGasPriceFlag = BigFlag{ + + Name: "gasprice", + + Usage: "Minimum gas price for mining a transaction (deprecated, use --miner.gasprice)", + + Value: eth.DefaultConfig.Miner.GasPrice, + + } + + LegacyMinerEtherbaseFlag = cli.StringFlag{ + + Name: "etherbase", + + Usage: "Public address for block mining rewards (default = first account, deprecated, use --miner.etherbase)", + + Value: "0", + + } + + LegacyMinerExtraDataFlag = cli.StringFlag{ + + Name: "extradata", + + Usage: "Block extra data set by the miner (default = client version, deprecated, use --miner.extradata)", + + } + + + + // (Deprecated June 2019) + + LegacyLightServFlag = cli.IntFlag{ + + Name: "lightserv", + + Usage: "Maximum percentage of time allowed for serving LES requests (deprecated, use --light.serve)", + + Value: eth.DefaultConfig.LightServ, + + } + + LegacyLightPeersFlag = cli.IntFlag{ + + Name: "lightpeers", + + Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated, use --light.maxpeers)", + + Value: eth.DefaultConfig.LightPeers, + + } + + + + // (Deprecated April 2020) + + LegacyTestnetFlag = cli.BoolFlag{ // TODO(q9f): Remove after Ropsten is discontinued. + + Name: "testnet", + + Usage: "Pre-configured test network (Deprecated: Please choose one of --kotti, --mordor, --goerli, --rinkeby, or --ropsten.)", + + } + + +++======= +++>>>>>>> c2d2f4ed8f + // (Deprecated May 2020, shown in aliased flags section) + LegacyRPCEnabledFlag = cli.BoolFlag{ + Name: "rpc", +diff --cc consensus/clique/clique.go +index 287eba8a76,c62e180faa..0000000000 +--- a/consensus/clique/clique.go ++++ b/consensus/clique/clique.go +@@@ -562,12 -555,11 +556,17 @@@ func (c *Clique) Finalize(chain consens + // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, + // nor block rewards given, and returns the final block. + func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { +++<<<<<<< HEAD + + // No block rewards in PoA, so the state remains as is and uncles are dropped + + header.Root = state.IntermediateRoot(chain.Config().IsEnabled(chain.Config().GetEIP161dTransition, header.Number)) + + header.UncleHash = types.CalcUncleHash(nil) +++======= ++ // Finalize block ++ c.Finalize(chain, header, state, txs, uncles) +++>>>>>>> c2d2f4ed8f + + // Assemble and return the final block for sealing +- return types.NewBlock(header, txs, nil, receipts, new(trie.Trie)), nil ++ return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil + } + + // Authorize injects a private key into the consensus engine to mint new blocks +diff --cc consensus/ethash/consensus.go +index 9a3e42c3dd,011a5688ef..0000000000 +--- a/consensus/ethash/consensus.go ++++ b/consensus/ethash/consensus.go +@@@ -41,8 -39,29 +41,34 @@@ import + + // Ethash proof-of-work protocol constants. + var ( +++<<<<<<< HEAD + + maxUncles = 2 // Maximum number of uncles allowed in a single block + + allowedFutureBlockTime = 15 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks +++======= ++ FrontierBlockReward = big.NewInt(5e+18) // Block reward in wei for successfully mining a block ++ ByzantiumBlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium ++ ConstantinopleBlockReward = big.NewInt(2e+18) // Block reward in wei for successfully mining a block upward from Constantinople ++ maxUncles = 2 // Maximum number of uncles allowed in a single block ++ allowedFutureBlockTimeSeconds = int64(15) // Max seconds from current time allowed for blocks, before they're considered future blocks ++ ++ // calcDifficultyEip2384 is the difficulty adjustment algorithm as specified by EIP 2384. ++ // It offsets the bomb 4M blocks from Constantinople, so in total 9M blocks. ++ // Specification EIP-2384: https://eips.ethereum.org/EIPS/eip-2384 ++ calcDifficultyEip2384 = makeDifficultyCalculator(big.NewInt(9000000)) ++ ++ // calcDifficultyConstantinople is the difficulty adjustment algorithm for Constantinople. ++ // 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 Byzantium rules, but with ++ // bomb offset 5M. ++ // Specification EIP-1234: https://eips.ethereum.org/EIPS/eip-1234 ++ calcDifficultyConstantinople = makeDifficultyCalculator(big.NewInt(5000000)) ++ ++ // calcDifficultyByzantium 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 Byzantium rules. ++ // Specification EIP-649: https://eips.ethereum.org/EIPS/eip-649 ++ calcDifficultyByzantium = makeDifficultyCalculator(big.NewInt(3000000)) +++>>>>>>> c2d2f4ed8f + ) + + // Various error messages to mark blocks invalid. These should be private to +@@@ -225,10 -242,10 +249,10 @@@ func (ethash *Ethash) VerifyUncles(chai + // 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.ChainHeaderReader, header, parent *types.Header, uncle bool, seal bool) error { ++ func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header, uncle bool, seal bool, unixNow int64) 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) + + if uint64(len(header.Extra)) > vars.MaximumExtraDataSize { + + return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), vars.MaximumExtraDataSize) + } + // Verify the header's timestamp + if !uncle { +@@@ -404,59 -383,110 +428,66 @@@ func CalcDifficulty(config ctypes.Chain + // calculate a fake block number for the ice-age delay + // Specification: https://eips.ethereum.org/EIPS/eip-1234 + fakeBlockNumber := new(big.Int) + - if parent.Number.Cmp(bombDelayFromParent) >= 0 { + - fakeBlockNumber = fakeBlockNumber.Sub(parent.Number, bombDelayFromParent) + - } + - // for the exponential factor + - periodCount := fakeBlockNumber + - periodCount.Div(periodCount, expDiffPeriod) + - + - // the exponential factor, commonly referred to as "the bomb" + - // diff = diff + 2^(periodCount - 2) + - if periodCount.Cmp(big1) > 0 { + - y.Sub(periodCount, big2) + - y.Exp(big2, y, nil) + - x.Add(x, y) + + delayWithOffset := new(big.Int).Sub(vars.EIP1234DifficultyBombDelay, common.Big1) + + if parent.Number.Cmp(delayWithOffset) >= 0 { + + fakeBlockNumber = fakeBlockNumber.Sub(parent.Number, delayWithOffset) + } + - return x + - } + -} + - + -// 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 uint64, parent *types.Header) *big.Int { + - // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md + - // algorithm: + - // diff = (parent_diff + + - // (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)) + - // ) + 2^(periodCount - 2) + + exPeriodRef.Set(fakeBlockNumber) + + - bigTime := new(big.Int).SetUint64(time) + - bigParentTime := new(big.Int).SetUint64(parent.Time) + + } else if config.IsEnabled(config.GetEthashEIP649Transition, next) { + + // The calculation uses the Byzantium rules, with bomb offset of 3M. + + // Specification EIP-649: https://eips.ethereum.org/EIPS/eip-649 + + // Related meta-ish EIP-669: https://github.com/ethereum/EIPs/pull/669 + + // Note, the calculations below looks at the parent number, which is 1 below + + // the block number. Thus we remove one from the delay given + + - // 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(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(parent.Difficulty, params.DifficultyBoundDivisor) + - x.Mul(y, x) + - x.Add(parent.Difficulty, 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(parent.Number, big1) + - periodCount.Div(periodCount, expDiffPeriod) + - + - // the exponential factor, commonly referred to as "the bomb" + - // diff = diff + 2^(periodCount - 2) + - if periodCount.Cmp(big1) > 0 { + - y.Sub(periodCount, big2) + - y.Exp(big2, y, nil) + - x.Add(x, y) + - } + - return x + -} + + fakeBlockNumber := new(big.Int) + + delayWithOffset := new(big.Int).Sub(vars.EIP649DifficultyBombDelay, common.Big1) + + if parent.Number.Cmp(delayWithOffset) >= 0 { + + fakeBlockNumber = fakeBlockNumber.Sub(parent.Number, delayWithOffset) + + } + + exPeriodRef.Set(fakeBlockNumber) + + -// 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 uint64, parent *types.Header) *big.Int { + - diff := new(big.Int) + - adjust := new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisor) + - bigTime := new(big.Int) + - bigParentTime := new(big.Int) + + } + + - bigTime.SetUint64(time) + - bigParentTime.SetUint64(parent.Time) + + // EXPLOSION + + - if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 { + - diff.Add(parent.Difficulty, adjust) + + // the 'periodRef' (from above) represents the many ways of hackishly modifying the reference number + + // (ie the 'currentBlock') in order to lie to the function about what time it really is + + // + + // 2^(( periodRef // EDP) - 2) + + // + + x := new(big.Int) + + x.Div(exPeriodRef, params.ExpDiffPeriod) // (periodRef // EDP) + + if x.Cmp(big1) > 0 { // if result large enough (not in algo explicitly) + + x.Sub(x, big2) // - 2 + + x.Exp(big2, x, nil) // 2^ + } else { + - diff.Sub(parent.Difficulty, adjust) + - } + - if diff.Cmp(params.MinimumDifficulty) < 0 { + - diff.Set(params.MinimumDifficulty) + - } + - + - periodCount := new(big.Int).Add(parent.Number, big1) + - periodCount.Div(periodCount, expDiffPeriod) + - if periodCount.Cmp(big1) > 0 { + - // diff = diff + 2^(periodCount - 2) + - expDiff := periodCount.Sub(periodCount, big2) + - expDiff.Exp(big2, expDiff, nil) + - diff.Add(diff, expDiff) + - diff = math.BigMax(diff, params.MinimumDifficulty) + + x.SetUint64(0) + } + - return diff + + out.Add(out, x) + + return out + } + +++<<<<<<< HEAD + +// Some weird constants to avoid constant memory allocs for them. + +var ( + + big1 = big.NewInt(1) + + big2 = big.NewInt(2) + + bigMinus99 = big.NewInt(-99) + +) + + + +// VerifySeal implements consensus.Engine, checking whether the given block satisfies + +// the PoW difficulty requirements. + +func (ethash *Ethash) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error { + + return ethash.verifySeal(chain, header, false) + +} +++======= ++ // Exported for fuzzing ++ var FrontierDifficultyCalulator = calcDifficultyFrontier ++ var HomesteadDifficultyCalulator = calcDifficultyHomestead ++ var DynamicDifficultyCalculator = makeDifficultyCalculator +++>>>>>>> c2d2f4ed8f + + // verifySeal checks whether a block satisfies the PoW difficulty requirements, + // either using the usual ethash cache for it, or alternatively using a full DAG +@@@ -547,12 -576,11 +578,17 @@@ func (ethash *Ethash) Finalize(chain co + // FinalizeAndAssemble implements consensus.Engine, accumulating the block and + // uncle rewards, setting the final state and assembling the block. + func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { +++<<<<<<< HEAD + + // Accumulate any block and uncle rewards and commit the final state root + + accumulateRewards(chain.Config(), state, header, uncles) + + header.Root = state.IntermediateRoot(chain.Config().IsEnabled(chain.Config().GetEIP161dTransition, header.Number)) +++======= ++ // Finalize block ++ ethash.Finalize(chain, header, state, txs, uncles) +++>>>>>>> c2d2f4ed8f + + // Header seems complete, assemble into a block and return +- return types.NewBlock(header, txs, uncles, receipts, new(trie.Trie)), nil ++ return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil + } + + // SealHash returns the hash of a block prior to it being sealed. +diff --cc consensus/ethash/ethash.go +index f5e6778237,550d99893d..0000000000 +--- a/consensus/ethash/ethash.go ++++ b/consensus/ethash/ethash.go +@@@ -34,10 -33,8 +34,14 @@@ import + "time" + "unsafe" + +++<<<<<<< HEAD + + mmap "github.com/edsrzf/mmap-go" + + "github.com/ethereum/go-ethereum/common/hexutil" +++======= ++ "github.com/edsrzf/mmap-go" +++>>>>>>> c2d2f4ed8f + "github.com/ethereum/go-ethereum/consensus" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/rpc" +diff --cc console/console_test.go +index c0515929c2,f6ab781410..0000000000 +--- a/console/console_test.go ++++ b/console/console_test.go +@@@ -29,7 -29,9 +29,8 @@@ import + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/console/prompt" + - "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/eth" ++ "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/internal/jsre" + "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/node" +@@@ -97,8 -98,8 +98,13 @@@ func newTester(t *testing.T, confOverri + if err != nil { + t.Fatalf("failed to create node: %v", err) + } +++<<<<<<< HEAD + + ethConf := ð.Config{ + + Genesis: params.DeveloperGenesisBlock(15, common.Address{}, false), +++======= ++ ethConf := ðconfig.Config{ ++ Genesis: core.DeveloperGenesisBlock(15, common.Address{}), +++>>>>>>> c2d2f4ed8f + Miner: miner.Config{ + Etherbase: common.HexToAddress(testAddress), + }, +diff --cc core/blockchain_snapshot_test.go +index 5b7d7823e0,96a5c7a8d4..0000000000 +--- a/core/blockchain_snapshot_test.go ++++ b/core/blockchain_snapshot_test.go +@@@ -28,28 -28,18 +28,19 @@@ import + "testing" + "time" + ++ "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/rawdb" ++ "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" ++ "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" + + "github.com/ethereum/go-ethereum/params/types/genesisT" + ) + +- // snapshotTest is a test case for snapshot recovery. It can be used for +- // simulating these scenarios: +- // (i) Geth restarts normally with valid legacy snapshot +- // (ii) Geth restarts normally with valid new-format snapshot +- // (iii) Geth restarts after the crash, with broken legacy snapshot +- // (iv) Geth restarts after the crash, with broken new-format snapshot +- // (v) Geth restarts normally, but it's requested to be rewound to a lower point via SetHead +- // (vi) Geth restarts normally with a stale snapshot +- type snapshotTest struct { +- legacy bool // Flag whether the loaded snapshot is in legacy format +- crash bool // Flag whether the Geth restarts from the previous crash +- restartCrash int // Number of blocks to insert after the normal stop, then the crash happens +- gapped int // Number of blocks to insert without enabling snapshot +- setHead uint64 // Block number to set head back to +- ++ // snapshotTestBasic wraps the common testing fields in the snapshot tests. ++ type snapshotTestBasic struct { ++ legacy bool // Wether write the snapshot journal in legacy format + chainBlocks int // Number of blocks to generate for the canonical chain + snapshotBlock uint64 // Block number of the relevant snapshot disk layer + commitBlock uint64 // Block number for which to commit the state to disk +@@@ -590,209 -964,60 +965,224 @@@ func TestRecoverSnapshotFromCrashWithLe + // Expected head fast block: C10 + // Expected head block : C8 + // Expected snapshot disk : C10 +- testSnapshot(t, &snapshotTest{ +- legacy: true, +- crash: false, +- restartCrash: 2, +- gapped: 0, +- setHead: 0, +- chainBlocks: 8, +- snapshotBlock: 0, +- commitBlock: 0, +- expCanonicalBlocks: 10, +- expHeadHeader: 10, +- expHeadFastBlock: 10, +- expHeadBlock: 8, // The persisted state in the first running +- expSnapshotBottom: 10, // The persisted disk layer in the second running +- }) +- } +- +- func testSnapshot(t *testing.T, tt *snapshotTest) { +- // It's hard to follow the test case, visualize the input +- // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) +- // fmt.Println(tt.dump()) +- +- // Create a temporary persistent database +- datadir, err := ioutil.TempDir("", "") +- if err != nil { +- t.Fatalf("Failed to create temporary datadir: %v", err) ++ t.Skip("Legacy format testing is not supported") ++ test := &restartCrashSnapshotTest{ ++ snapshotTestBasic: snapshotTestBasic{ ++ legacy: true, ++ chainBlocks: 8, ++ snapshotBlock: 0, ++ commitBlock: 0, ++ expCanonicalBlocks: 10, ++ expHeadHeader: 10, ++ expHeadFastBlock: 10, ++ expHeadBlock: 8, // The persisted state in the first running ++ expSnapshotBottom: 10, // The persisted disk layer in the second running ++ }, ++ newBlocks: 2, + } +++<<<<<<< HEAD + + os.RemoveAll(datadir) + + + + db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "") + + if err != nil { + + t.Fatalf("Failed to create persistent database: %v", err) + + } + + defer db.Close() // Might double close, should be fine + + + + // Initialize a fresh chain + + var ( + + genesis = MustCommitGenesis(db, new(genesisT.Genesis)) + + engine = ethash.NewFullFaker() + + gendb = rawdb.NewMemoryDatabase() + + + + // Snapshot is enabled, the first snapshot is created from the Genesis. + + // The snapshot memory allowance is 256MB, it means no snapshot flush + + // will happen during the block insertion. + + cacheConfig = defaultCacheConfig + + ) + + chain, err := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + + if err != nil { + + t.Fatalf("Failed to create chain: %v", err) + + } + + blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, gendb, tt.chainBlocks, func(i int, b *BlockGen) {}) + + + + // Insert the blocks with configured settings. + + var breakpoints []uint64 + + if tt.commitBlock > tt.snapshotBlock { + + breakpoints = append(breakpoints, tt.snapshotBlock, tt.commitBlock) + + } else { + + breakpoints = append(breakpoints, tt.commitBlock, tt.snapshotBlock) + + } + + var startPoint uint64 + + for _, point := range breakpoints { + + if _, err := chain.InsertChain(blocks[startPoint:point]); err != nil { + + t.Fatalf("Failed to import canonical chain start: %v", err) + + } + + startPoint = point + + + + if tt.commitBlock > 0 && tt.commitBlock == point { + + chain.stateCache.TrieDB().Commit(blocks[point-1].Root(), true, nil) + + } + + if tt.snapshotBlock > 0 && tt.snapshotBlock == point { + + if tt.legacy { + + // Here we commit the snapshot disk root to simulate + + // committing the legacy snapshot. + + rawdb.WriteSnapshotRoot(db, blocks[point-1].Root()) + + } else { + + chain.snaps.Cap(blocks[point-1].Root(), 0) + + diskRoot, blockRoot := chain.snaps.DiskRoot(), blocks[point-1].Root() + + if !bytes.Equal(diskRoot.Bytes(), blockRoot.Bytes()) { + + t.Fatalf("Failed to flush disk layer change, want %x, got %x", blockRoot, diskRoot) + + } + + } + + } + + } + + if _, err := chain.InsertChain(blocks[startPoint:]); err != nil { + + t.Fatalf("Failed to import canonical chain tail: %v", err) + + } + + // Set the flag for writing legacy journal if necessary + + if tt.legacy { + + chain.writeLegacyJournal = true + + } + + // Pull the plug on the database, simulating a hard crash + + if tt.crash { + + db.Close() + + + + // Start a new blockchain back up and see where the repair leads us + + db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "") + + if err != nil { + + t.Fatalf("Failed to reopen persistent database: %v", err) + + } + + defer db.Close() + + + + // The interesting thing is: instead of start the blockchain after + + // the crash, we do restart twice here: one after the crash and one + + // after the normal stop. It's used to ensure the broken snapshot + + // can be detected all the time. + + chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + + if err != nil { + + t.Fatalf("Failed to recreate chain: %v", err) + + } + + chain.Stop() + + + + chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + + if err != nil { + + t.Fatalf("Failed to recreate chain: %v", err) + + } + + defer chain.Stop() + + } else if tt.gapped > 0 { + + // Insert blocks without enabling snapshot if gapping is required. + + chain.Stop() + + gappedBlocks, _ := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, gendb, tt.gapped, func(i int, b *BlockGen) {}) + + + + // Insert a few more blocks without enabling snapshot + + var cacheConfig = &CacheConfig{ + + TrieCleanLimit: 256, + + TrieDirtyLimit: 256, + + TrieTimeLimit: 5 * time.Minute, + + SnapshotLimit: 0, + + } + + chain, err = NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + + if err != nil { + + t.Fatalf("Failed to recreate chain: %v", err) + + } + + chain.InsertChain(gappedBlocks) + + chain.Stop() + + + + chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + + if err != nil { + + t.Fatalf("Failed to recreate chain: %v", err) + + } + + defer chain.Stop() + + } else if tt.setHead != 0 { + + // Rewind the chain if setHead operation is required. + + chain.SetHead(tt.setHead) + + chain.Stop() + + + + chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + + if err != nil { + + t.Fatalf("Failed to recreate chain: %v", err) + + } + + defer chain.Stop() + + } else if tt.restartCrash != 0 { + + // Firstly, stop the chain properly, with all snapshot journal + + // and state committed. + + chain.Stop() + + + + // Restart chain, forcibly flush the disk layer journal with new format + + newBlocks, _ := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, gendb, tt.restartCrash, func(i int, b *BlockGen) {}) + + chain, err = NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + + if err != nil { + + t.Fatalf("Failed to recreate chain: %v", err) + + } + + chain.InsertChain(newBlocks) + + chain.Snapshot().Cap(newBlocks[len(newBlocks)-1].Root(), 0) + + + + // Simulate the blockchain crash + + // Don't call chain.Stop here, so that no snapshot + + // journal and latest state will be committed + + + + // Restart the chain after the crash + + chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + + if err != nil { + + t.Fatalf("Failed to recreate chain: %v", err) + + } + + defer chain.Stop() + + } else { + + chain.Stop() + + + + // Restart the chain normally + + chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + + if err != nil { + + t.Fatalf("Failed to recreate chain: %v", err) + + } + + defer chain.Stop() + + } + + + + // Iterate over all the remaining blocks and ensure there are no gaps + + verifyNoGaps(t, chain, true, blocks) + + verifyCutoff(t, chain, true, blocks, tt.expCanonicalBlocks) +++======= ++ test.test(t) ++ test.teardown() ++ } +++>>>>>>> c2d2f4ed8f + +- if head := chain.CurrentHeader(); head.Number.Uint64() != tt.expHeadHeader { +- t.Errorf("Head header mismatch: have %d, want %d", head.Number, tt.expHeadHeader) +- } +- if head := chain.CurrentFastBlock(); head.NumberU64() != tt.expHeadFastBlock { +- t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadFastBlock) +- } +- if head := chain.CurrentBlock(); head.NumberU64() != tt.expHeadBlock { +- t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadBlock) +- } +- // Check the disk layer, ensure they are matched +- block := chain.GetBlockByNumber(tt.expSnapshotBottom) +- if block == nil { +- t.Errorf("The correspnding block[%d] of snapshot disk layer is missing", tt.expSnapshotBottom) +- } else if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) { +- t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot()) ++ // Tests the Geth was running with a complete snapshot and then imports a few ++ // more new blocks on top without enabling the snapshot. After the restart, ++ // crash happens. Check everything is ok after the restart. ++ func TestRecoverSnapshotFromWipingCrash(t *testing.T) { ++ // Chain: ++ // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) ++ // ++ // Commit: G ++ // Snapshot: G ++ // ++ // SetHead(0) ++ // ++ // ------------------------------ ++ // ++ // Expected in leveldb: ++ // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10 ++ // ++ // Expected head header : C10 ++ // Expected head fast block: C10 ++ // Expected head block : C8 ++ // Expected snapshot disk : C10 ++ test := &wipeCrashSnapshotTest{ ++ snapshotTestBasic: snapshotTestBasic{ ++ legacy: false, ++ chainBlocks: 8, ++ snapshotBlock: 4, ++ commitBlock: 0, ++ expCanonicalBlocks: 10, ++ expHeadHeader: 10, ++ expHeadFastBlock: 10, ++ expHeadBlock: 10, ++ expSnapshotBottom: 10, ++ }, ++ newBlocks: 2, + } ++ test.test(t) ++ test.teardown() + } +diff --cc core/blockchain_test.go +index 26ee56cab7,3e4757f8b6..0000000000 +--- a/core/blockchain_test.go ++++ b/core/blockchain_test.go +@@@ -597,12 -595,12 +598,17 @@@ func TestFastVsFullChains(t *testing.T + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000) + - gspec = &Genesis{ + + gspec = &genesisT.Genesis{ + Config: params.TestChainConfig, + - Alloc: GenesisAlloc{address: {Balance: funds}}, + + Alloc: genesisT.GenesisAlloc{address: {Balance: funds}}, + } +++<<<<<<< HEAD + + genesis = MustCommitGenesis(gendb, gspec) + + signer = types.NewEIP155Signer(gspec.Config.GetChainID()) +++======= ++ genesis = gspec.MustCommit(gendb) ++ signer = types.LatestSigner(gspec.Config) +++>>>>>>> c2d2f4ed8f + ) + blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, 1024, func(i int, block *BlockGen) { + block.SetCoinbase(common.Address{0x00}) +@@@ -840,8 -838,8 +846,13 @@@ func TestChainTxReorgs(t *testing.T) + addr3: {Balance: big.NewInt(1000000)}, + }, + } +++<<<<<<< HEAD + + genesis = MustCommitGenesis(db, gspec) + + signer = types.NewEIP155Signer(gspec.Config.GetChainID()) +++======= ++ genesis = gspec.MustCommit(db) ++ signer = types.LatestSigner(gspec.Config) +++>>>>>>> c2d2f4ed8f + ) + + // Create two transactions shared between the chains: +@@@ -944,9 -942,9 +955,15 @@@ func TestLogReorgs(t *testing.T) + db = rawdb.NewMemoryDatabase() + // this code generates a log + code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") +++<<<<<<< HEAD + + gspec = &genesisT.Genesis{Config: params.TestChainConfig, Alloc: genesisT.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} + + genesis = MustCommitGenesis(db, gspec) + + signer = types.NewEIP155Signer(gspec.Config.GetChainID()) +++======= ++ gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} ++ genesis = gspec.MustCommit(db) ++ signer = types.LatestSigner(gspec.Config) +++>>>>>>> c2d2f4ed8f + ) + + blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) +@@@ -998,9 -996,9 +1015,15 @@@ func TestLogRebirth(t *testing.T) + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + db = rawdb.NewMemoryDatabase() +++<<<<<<< HEAD + + gspec = &genesisT.Genesis{Config: params.TestChainConfig, Alloc: genesisT.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} + + genesis = MustCommitGenesis(db, gspec) + + signer = types.NewEIP155Signer(gspec.Config.GetChainID()) +++======= ++ gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} ++ genesis = gspec.MustCommit(db) ++ signer = types.LatestSigner(gspec.Config) +++>>>>>>> c2d2f4ed8f + engine = ethash.NewFaker() + blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil) + ) +@@@ -1062,9 -1060,9 +1085,15 @@@ func TestSideLogRebirth(t *testing.T) + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + db = rawdb.NewMemoryDatabase() +++<<<<<<< HEAD + + gspec = &genesisT.Genesis{Config: params.TestChainConfig, Alloc: genesisT.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} + + genesis = MustCommitGenesis(db, gspec) + + signer = types.NewEIP155Signer(gspec.Config.GetChainID()) +++======= ++ gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} ++ genesis = gspec.MustCommit(db) ++ signer = types.LatestSigner(gspec.Config) +++>>>>>>> c2d2f4ed8f + blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + ) + +@@@ -1132,12 -1130,12 +1161,17 @@@ func TestReorgSideEvent(t *testing.T) + db = rawdb.NewMemoryDatabase() + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + - gspec = &Genesis{ + + gspec = &genesisT.Genesis{ + Config: params.TestChainConfig, + - Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}, + + Alloc: genesisT.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}, + } +++<<<<<<< HEAD + + genesis = MustCommitGenesis(db, gspec) + + signer = types.NewEIP155Signer(gspec.Config.GetChainID()) +++======= ++ genesis = gspec.MustCommit(db) ++ signer = types.LatestSigner(gspec.Config) +++>>>>>>> c2d2f4ed8f + ) + + blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) +@@@ -1301,7 -1295,7 +1335,11 @@@ func TestEIP155Transition(t *testing.T + } + block.AddTx(tx) + +++<<<<<<< HEAD + + tx, err = basicTx(types.NewEIP155Signer(gspec.Config.GetChainID())) +++======= ++ tx, err = basicTx(types.LatestSigner(gspec.Config)) +++>>>>>>> c2d2f4ed8f + if err != nil { + t.Fatal(err) + } +@@@ -1313,7 -1307,7 +1351,11 @@@ + } + block.AddTx(tx) + +++<<<<<<< HEAD + + tx, err = basicTx(types.NewEIP155Signer(gspec.Config.GetChainID())) +++======= ++ tx, err = basicTx(types.LatestSigner(gspec.Config)) +++>>>>>>> c2d2f4ed8f + if err != nil { + t.Fatal(err) + } +@@@ -1391,7 -1385,7 +1433,11 @@@ func TestEIP161AccountRemoval(t *testin + var ( + tx *types.Transaction + err error +++<<<<<<< HEAD + + signer = types.NewEIP155Signer(gspec.Config.GetChainID()) +++======= ++ signer = types.LatestSigner(gspec.Config) +++>>>>>>> c2d2f4ed8f + ) + switch i { + case 0: +@@@ -2082,9 -2076,9 +2128,15 @@@ func TestTransactionIndices(t *testing. + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000) +++<<<<<<< HEAD + + gspec = &genesisT.Genesis{Config: params.TestChainConfig, Alloc: genesisT.GenesisAlloc{address: {Balance: funds}}} + + genesis = MustCommitGenesis(gendb, gspec) + + signer = types.NewEIP155Signer(gspec.Config.GetChainID()) +++======= ++ gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}} ++ genesis = gspec.MustCommit(gendb) ++ signer = types.LatestSigner(gspec.Config) +++>>>>>>> c2d2f4ed8f + ) + height := uint64(128) + blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), func(i int, block *BlockGen) { +@@@ -2209,9 -2203,9 +2261,15 @@@ func TestSkipStaleTxIndicesInFastSync( + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000) +++<<<<<<< HEAD + + gspec = &genesisT.Genesis{Config: params.TestChainConfig, Alloc: genesisT.GenesisAlloc{address: {Balance: funds}}} + + genesis = MustCommitGenesis(gendb, gspec) + + signer = types.NewEIP155Signer(gspec.Config.GetChainID()) +++======= ++ gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}} ++ genesis = gspec.MustCommit(gendb) ++ signer = types.LatestSigner(gspec.Config) +++>>>>>>> c2d2f4ed8f + ) + height := uint64(128) + blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), func(i int, block *BlockGen) { +diff --cc core/genesis.go +index 1f6a7319b4,e05e27fe17..0000000000 +--- a/core/genesis.go ++++ b/core/genesis.go +@@@ -51,9 -152,13 +51,19 @@@ func SetupGenesisBlock(db ethdb.Databas + // error is a *params.ConfigCompatError and the new, unwritten config is returned. + // + // The returned chain configuration is never nil. +++<<<<<<< HEAD + +func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *genesisT.Genesis, ecbp1100 *big.Int) (ctypes.ChainConfigurator, common.Hash, error) { + + if genesis != nil && confp.IsEmpty(genesis.Config) { + + return params.AllEthashProtocolChanges, common.Hash{}, genesisT.ErrGenesisNoConfig +++======= ++ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) { ++ return SetupGenesisBlockWithOverride(db, genesis, nil) ++ } ++ ++ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideBerlin *big.Int) (*params.ChainConfig, common.Hash, error) { ++ if genesis != nil && genesis.Config == nil { ++ return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig +++>>>>>>> c2d2f4ed8f + } + // Just commit the new block if there is no stored genesis block. + stored := rawdb.ReadCanonicalHash(db, 0) +@@@ -77,47 -173,41 +87,53 @@@ + if err != nil { + return genesis.Config, common.Hash{}, err + } + + log.Info("Wrote custom genesis block OK", "config", genesis.Config) + return genesis.Config, block.Hash(), nil + } +- + // We have the genesis block in database(perhaps in ancient database) + // but the corresponding state is missing. + header := rawdb.ReadHeader(db, stored, 0) +- if _, err := state.New(header.Root, state.NewDatabaseWithCache(db, 0, ""), nil); err != nil { ++ if _, err := state.New(header.Root, state.NewDatabaseWithConfig(db, nil), nil); err != nil { + if genesis == nil { + - genesis = DefaultGenesisBlock() + + genesis = params.DefaultGenesisBlock() + } + // Ensure the stored genesis matches with the given one. + - hash := genesis.ToBlock(nil).Hash() + + hash := GenesisToBlock(genesis, nil).Hash() + if hash != stored { + - return genesis.Config, hash, &GenesisMismatchError{stored, hash} + + return genesis.Config, hash, &genesisT.GenesisMismatchError{Stored: stored, New: hash} + } + - block, err := genesis.Commit(db) + + block, err := CommitGenesis(genesis, db) + if err != nil { + return genesis.Config, hash, err + } + return genesis.Config, block.Hash(), nil + } +- + // Check whether the genesis block is already written. + if genesis != nil { + - hash := genesis.ToBlock(nil).Hash() + + hash := GenesisToBlock(genesis, nil).Hash() + if hash != stored { + - return genesis.Config, hash, &GenesisMismatchError{stored, hash} + + return genesis.Config, hash, &genesisT.GenesisMismatchError{Stored: stored, New: hash} + } + } +- + // Get the existing chain configuration. +++<<<<<<< HEAD + + newcfg := configOrDefault(genesis, stored) + + + + if ecbp1100 != nil { + + n := ecbp1100.Uint64() + + if err := newcfg.SetECBP1100Transition(&n); err != nil { + + return newcfg, stored, err + + } +++======= ++ newcfg := genesis.configOrDefault(stored) ++ if overrideBerlin != nil { ++ newcfg.BerlinBlock = overrideBerlin ++ } ++ if err := newcfg.CheckConfigForkOrder(); err != nil { ++ return newcfg, common.Hash{}, err +++>>>>>>> c2d2f4ed8f + } + + + storedcfg := rawdb.ReadChainConfig(db, stored) + if storedcfg == nil { + log.Warn("Found genesis block without chain config") +@@@ -130,16 -217,9 +146,15 @@@ + // 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 { + + // + + // (meowsbits): The idea here is to use stored configs when they are not upgrade-able via defaults. + + // Pre-existing logic only upgraded mainnet, when it should upgrade all defaulty chains. + + // New logic (below) checks _inequality_ between a defaulty config and a stored config. If different, + + // the stored config is used. This breaks auto-upgrade magic for defaulty chains. + + if genesis == nil && !confp.Identical(storedcfg, newcfg, []string{"NetworkID", "ChainID"}) { + + log.Info("Found non-defaulty stored config, using it.") + return storedcfg, stored, nil + } +- + // Check config compatibility and write the config. Compatibility errors + // are returned to the caller unless we're already at block zero. + height := rawdb.ReadHeaderNumber(db, rawdb.ReadHeadHeaderHash(db)) +@@@ -164,14 -246,8 +179,19 @@@ func configOrDefault(g *genesisT.Genesi + return params.RinkebyChainConfig + case ghash == params.GoerliGenesisHash: + return params.GoerliChainConfig +++<<<<<<< HEAD + + case ghash == params.KottiGenesisHash: + + return params.KottiChainConfig + + case ghash == params.MordorGenesisHash: + + return params.MordorChainConfig + + case ghash == params.RopstenGenesisHash: + + return params.RopstenChainConfig + + case ghash == params.YoloV2GenesisHash: + + return params.YoloV2ChainConfig +++======= ++ case ghash == params.YoloV3GenesisHash: ++ return params.YoloV3ChainConfig +++>>>>>>> c2d2f4ed8f + default: + return params.AllEthashProtocolChanges + } +@@@ -215,13 -291,13 +235,13 @@@ func GenesisToBlock(g *genesisT.Genesis + statedb.Commit(false) + statedb.Database().TrieDB().Commit(root, true, nil) + +- return types.NewBlock(head, nil, nil, nil, new(trie.Trie)) ++ return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)) + } + + -// Commit writes the block and state of a genesis specification to the database. + +// CommitGenesis 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 := g.ToBlock(db) + +func CommitGenesis(g *genesisT.Genesis, db ethdb.Database) (*types.Block, error) { + + block := GenesisToBlock(g, db) + if block.Number().Sign() != 0 { + return nil, fmt.Errorf("can't commit genesis block with number > 0") + } +@@@ -252,6 -331,105 +272,110 @@@ func MustCommitGenesis(db ethdb.Databas + + // 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 { +++<<<<<<< HEAD + + g := genesisT.Genesis{Alloc: genesisT.GenesisAlloc{addr: {Balance: balance}}} + + return MustCommitGenesis(db, &g) +++======= ++ g := Genesis{Alloc: GenesisAlloc{addr: {Balance: balance}}} ++ return g.MustCommit(db) ++ } ++ ++ // 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), ++ } ++ } ++ ++ // DefaultRopstenGenesisBlock returns the Ropsten network genesis block. ++ func DefaultRopstenGenesisBlock() *Genesis { ++ return &Genesis{ ++ Config: params.RopstenChainConfig, ++ Nonce: 66, ++ ExtraData: hexutil.MustDecode("0x3535353535353535353535353535353535353535353535353535353535353535"), ++ GasLimit: 16777216, ++ Difficulty: big.NewInt(1048576), ++ Alloc: decodePrealloc(ropstenAllocData), ++ } ++ } ++ ++ // DefaultRinkebyGenesisBlock returns the Rinkeby network genesis block. ++ func DefaultRinkebyGenesisBlock() *Genesis { ++ return &Genesis{ ++ Config: params.RinkebyChainConfig, ++ Timestamp: 1492009146, ++ ExtraData: hexutil.MustDecode("0x52657370656374206d7920617574686f7269746168207e452e436172746d616e42eb768f2244c8811c63729a21a3569731535f067ffc57839b00206d1ad20c69a1981b489f772031b279182d99e65703f0076e4812653aab85fca0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), ++ GasLimit: 4700000, ++ Difficulty: big.NewInt(1), ++ Alloc: decodePrealloc(rinkebyAllocData), ++ } ++ } ++ ++ // DefaultGoerliGenesisBlock returns the Görli network genesis block. ++ func DefaultGoerliGenesisBlock() *Genesis { ++ return &Genesis{ ++ Config: params.GoerliChainConfig, ++ Timestamp: 1548854791, ++ ExtraData: hexutil.MustDecode("0x22466c6578692069732061207468696e6722202d204166726900000000000000e0a2bd4258d2768837baa26a28fe71dc079f84c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), ++ GasLimit: 10485760, ++ Difficulty: big.NewInt(1), ++ Alloc: decodePrealloc(goerliAllocData), ++ } ++ } ++ ++ func DefaultYoloV3GenesisBlock() *Genesis { ++ // Full genesis: https://gist.github.com/holiman/c6ed9269dce28304ad176314caa75e97 ++ return &Genesis{ ++ Config: params.YoloV3ChainConfig, ++ Timestamp: 0x6027dd2e, ++ ExtraData: hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000001041afbcb359d5a8dc58c15b2ff51354ff8a217d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), ++ GasLimit: 0x47b760, ++ Difficulty: big.NewInt(1), ++ Alloc: decodePrealloc(yoloV3AllocData), ++ } ++ } ++ ++ // DeveloperGenesisBlock returns the 'geth --dev' genesis block. ++ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis { ++ // Override the default period to the user requested one ++ config := *params.AllCliqueProtocolChanges ++ config.Clique.Period = period ++ ++ // Assemble and return the genesis with the precompiles and faucet pre-funded ++ return &Genesis{ ++ Config: &config, ++ ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...), ++ GasLimit: 11500000, ++ Difficulty: big.NewInt(1), ++ Alloc: map[common.Address]GenesisAccount{ ++ common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover ++ common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256 ++ common.BytesToAddress([]byte{3}): {Balance: big.NewInt(1)}, // RIPEMD ++ common.BytesToAddress([]byte{4}): {Balance: big.NewInt(1)}, // Identity ++ common.BytesToAddress([]byte{5}): {Balance: big.NewInt(1)}, // ModExp ++ common.BytesToAddress([]byte{6}): {Balance: big.NewInt(1)}, // ECAdd ++ common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul ++ common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing ++ common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b ++ faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))}, ++ }, ++ } ++ } ++ ++ 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 ga +++>>>>>>> c2d2f4ed8f + } +diff --cc core/headerchain.go +index 77a0506c2c,dcd3644cd1..0000000000 +--- a/core/headerchain.go ++++ b/core/headerchain.go +@@@ -234,16 -299,23 +299,30 @@@ func (hc *HeaderChain) writeHeaders(hea + 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++ { +++<<<<<<< HEAD + + if chain[i] == nil { + + return 0, fmt.Errorf("header was nil") + + } + + if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 || chain[i].ParentHash != chain[i-1].Hash() { +++======= ++ if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 { ++ hash := chain[i].Hash() ++ parentHash := chain[i-1].Hash() +++>>>>>>> c2d2f4ed8f + // Chain broke ancestry, log a message (programming error) and skip insertion +- log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", chain[i].Hash(), +- "parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", chain[i-1].Hash()) ++ log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", hash, ++ "parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", parentHash) + + return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].Number, +- chain[i-1].Hash().Bytes()[:4], i, chain[i].Number, chain[i].Hash().Bytes()[:4], chain[i].ParentHash[:4]) ++ parentHash.Bytes()[:4], i, chain[i].Number, hash.Bytes()[:4], chain[i].ParentHash[:4]) ++ } ++ // If the header is a banned one, straight out abort ++ if BadHashes[chain[i].ParentHash] { ++ return i - 1, ErrBlacklistedHash ++ } ++ // If it's the last header in the cunk, we need to check it too ++ if i == len(chain)-1 && BadHashes[chain[i].Hash()] { ++ return i, ErrBlacklistedHash + } + } + +@@@ -285,58 -353,41 +360,75 @@@ + return 0, nil + } + +- // InsertHeaderChain attempts to insert the given header chain in to the local +- // chain, possibly creating a reorg. If an error is returned, it will return the +- // index number of the failing header as well an error describing what went wrong. ++ // InsertHeaderChain inserts the given headers. + // +++<<<<<<< HEAD + +// The verify parameter can be used to fine tune whether nonce verification + +// 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, 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 import") + + return i, errors.New("aborted") + + } + + if header == nil { + + return i, fmt.Errorf("nil header") + + } + + // If the header's already known, skip it, otherwise store + + hash := header.Hash() + + if hc.HasHeader(hash, header.Number.Uint64()) { + + externTd := hc.GetTd(hash, header.Number.Uint64()) + + localTd := hc.GetTd(hc.currentHeaderHash, hc.CurrentHeader().Number.Uint64()) + + if externTd == nil || externTd.Cmp(localTd) <= 0 { + + stats.ignored++ + + continue + + } + + } + + if err := writeHeader(header); err != nil { + + return i, err + + } + + stats.processed++ +++======= ++ // The validity of the headers is NOT CHECKED by this method, i.e. they need to be ++ // validated by ValidateHeaderChain before calling InsertHeaderChain. ++ // ++ // This insert is all-or-nothing. If this returns an error, no headers were written, ++ // otherwise they were all processed successfully. ++ // ++ // The returned 'write status' says if the inserted headers are part of the canonical chain ++ // or a side chain. ++ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, start time.Time) (WriteStatus, error) { ++ if hc.procInterrupt() { ++ return 0, errors.New("aborted") +++>>>>>>> c2d2f4ed8f + } +- // Report some public statistics so the user has a clue what's going on +- last := chain[len(chain)-1] ++ res, err := hc.writeHeaders(chain) + ++ // Report some public statistics so the user has a clue what's going on + context := []interface{}{ +- "count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)), +- "number", last.Number, "hash", last.Hash(), ++ "count", res.imported, ++ "elapsed", common.PrettyDuration(time.Since(start)), + } +- if timestamp := time.Unix(int64(last.Time), 0); time.Since(timestamp) > time.Minute { +- context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...) ++ if err != nil { ++ context = append(context, "err", err) ++ } ++ if last := res.lastHeader; last != nil { ++ context = append(context, "number", last.Number, "hash", res.lastHash) ++ if timestamp := time.Unix(int64(last.Time), 0); time.Since(timestamp) > time.Minute { ++ context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...) ++ } + } +- if stats.ignored > 0 { +- context = append(context, []interface{}{"ignored", stats.ignored}...) ++ if res.ignored > 0 { ++ context = append(context, []interface{}{"ignored", res.ignored}...) + } + log.Info("Imported new block headers", context...) +- +- return 0, nil ++ return res.status, err + } + + // GetBlockHashesFromHash retrieves a number of block hashes starting at a given +diff --cc core/state_prefetcher.go +index 53fc79cdf1,05394321f7..0000000000 +--- a/core/state_prefetcher.go ++++ b/core/state_prefetcher.go +@@@ -50,11 -49,14 +49,14 @@@ func newStatePrefetcher(config ctypes.C + // only goal is to pre-cache transaction signatures and state trie nodes. + func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *uint32) { + var ( +- header = block.Header() +- gaspool = new(GasPool).AddGas(block.GasLimit()) ++ header = block.Header() ++ gaspool = new(GasPool).AddGas(block.GasLimit()) ++ blockContext = NewEVMBlockContext(header, p.bc, nil) ++ evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) ++ signer = types.MakeSigner(p.config, header.Number) + ) + // Iterate over and process the individual transactions + - byzantium := p.config.IsByzantium(block.Number()) + + byzantium := p.config.IsEnabled(p.config.GetEIP161abcTransition, block.Number()) + for i, tx := range block.Transactions() { + // If block precaching was interrupted, abort + if interrupt != nil && atomic.LoadUint32(interrupt) == 1 { +@@@ -79,16 -85,10 +85,24 @@@ + // precacheTransaction attempts to apply a transaction to the given state database + // and uses the input parameters for its environment. The goal is not to execute + // the transaction successfully, rather to warm up touched data slots. +++<<<<<<< HEAD + +func precacheTransaction(config ctypes.ChainConfigurator, bc ChainContext, author *common.Address, gaspool *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, cfg vm.Config) error { + + // Convert the transaction into an executable message and pre-cache its sender + + msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) + + if err != nil { + + return err + + } + + // Create the EVM and execute the transaction + + context := NewEVMContext(msg, header, bc, author) + + vm := vm.NewEVM(context, statedb, config, cfg) + + + + _, err = ApplyMessage(vm, msg, gaspool) +++======= ++ func precacheTransaction(msg types.Message, config *params.ChainConfig, gaspool *GasPool, statedb *state.StateDB, header *types.Header, evm *vm.EVM) error { ++ // Update the evm with the new transaction context. ++ evm.Reset(NewEVMTxContext(msg), statedb) ++ // Add addresses to access list if applicable ++ _, err := ApplyMessage(evm, msg, gaspool) +++>>>>>>> c2d2f4ed8f + return err + } +diff --cc core/state_processor.go +index 8e386d7c17,40a953f0d4..0000000000 +--- a/core/state_processor.go ++++ b/core/state_processor.go +@@@ -62,16 -64,14 +64,17 @@@ func (p *StateProcessor) Process(block + gp = new(GasPool).AddGas(block.GasLimit()) + ) + // Mutate 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 { + - misc.ApplyDAOHardFork(statedb) + + isDAOSupport := p.config.IsEnabled(p.config.GetEthashEIP779Transition, block.Number()) + + if isDAOSupport { + + if daoNumber := p.config.GetEthashEIP779Transition(); daoNumber != nil && *daoNumber == block.NumberU64() { + + misc.ApplyDAOHardFork(statedb) + + } + } ++ blockContext := NewEVMBlockContext(header, p.bc, nil) ++ vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) + // Iterate over and process the individual transactions + for i, tx := range block.Transactions() { +- statedb.Prepare(tx.Hash(), block.Hash(), i) +- receipt, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg) ++ msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number)) + if err != nil { + return nil, nil, 0, err + } +@@@ -84,44 -89,23 +92,52 @@@ + return receipts, allLogs, *usedGas, nil + } + +++<<<<<<< HEAD + +// 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 ctypes.ChainConfigurator, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) { + + msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) + + if err != nil { + + return nil, err + + } + + // Create a new context to be used in the EVM environment + + 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) + + + + if config.IsEnabled(config.GetEIP2929Transition, header.Number) { + + statedb.AddAddressToAccessList(msg.From()) + + if dst := msg.To(); dst != nil { + + statedb.AddAddressToAccessList(*dst) + + // If it's a create-tx, the destination will be added inside evm.create + + } + + for addr := range vm.PrecompiledContractsForConfig(config, header.Number) { + + statedb.AddAddressToAccessList(addr) + + } + + } +++======= ++ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { ++ // Create a new context to be used in the EVM environment. ++ txContext := NewEVMTxContext(msg) ++ evm.Reset(txContext, statedb) +++>>>>>>> c2d2f4ed8f + +- // Apply the transaction to the current state (included in the env) +- result, err := ApplyMessage(vmenv, msg, gp) ++ // Apply the transaction to the current state (included in the env). ++ result, err := ApplyMessage(evm, msg, gp) + if err != nil { + return nil, err + } +- // Update the state with pending changes ++ ++ // Update the state with pending changes. + var root []byte + - if config.IsByzantium(header.Number) { + - statedb.Finalise(true) + + eip161d := config.IsEnabled(config.GetEIP161dTransition, header.Number) + + if config.IsEnabled(config.GetEIP658Transition, header.Number) { + + statedb.Finalise(eip161d) + } else { + - root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() + + root = statedb.IntermediateRoot(eip161d).Bytes() + } + *usedGas += result.UsedGas + +diff --cc core/state_transition.go +index 610716bb55,d511e40bd6..0000000000 +--- a/core/state_transition.go ++++ b/core/state_transition.go +@@@ -21,8 -22,9 +22,9 @@@ import + "math/big" + + "github.com/ethereum/go-ethereum/common" ++ "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + - "github.com/ethereum/go-ethereum/params" + + "github.com/ethereum/go-ethereum/params/vars" + ) + + /* +@@@ -104,13 -107,13 +107,21 @@@ func (result *ExecutionResult) Revert( + } + + // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. +++<<<<<<< HEAD + +func IntrinsicGas(data []byte, contractCreation, isEIP2 bool, isEIP2028 bool) (uint64, error) { + + // Set the starting gas for the raw transaction + + var gas uint64 + + if contractCreation && isEIP2 { + + gas = vars.TxGasContractCreation +++======= ++ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool) (uint64, error) { ++ // Set the starting gas for the raw transaction ++ var gas uint64 ++ if isContractCreation && isHomestead { ++ gas = params.TxGasContractCreation +++>>>>>>> c2d2f4ed8f + } else { + - gas = params.TxGas + + gas = vars.TxGas + } + // Bump the required gas by the amount of transactional data + if len(data) > 0 { +@@@ -132,11 -135,15 +143,15 @@@ + gas += nz * nonZeroGas + + z := uint64(len(data)) - nz + - if (math.MaxUint64-gas)/params.TxDataZeroGas < z { + + if (math.MaxUint64-gas)/vars.TxDataZeroGas < z { + return 0, ErrGasUintOverflow + } + - gas += z * params.TxDataZeroGas + + gas += z * vars.TxDataZeroGas + } ++ if accessList != nil { ++ gas += uint64(len(accessList)) * params.TxAccessListAddressGas ++ gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas ++ } + return gas, nil + } + +@@@ -230,12 -239,12 +247,21 @@@ func (st *StateTransition) TransitionDb + } + msg := st.msg + sender := vm.AccountRef(msg.From()) +++<<<<<<< HEAD + + eip2f := st.evm.ChainConfig().IsEnabled(st.evm.ChainConfig().GetEIP2Transition, st.evm.BlockNumber) + + eip2028f := st.evm.ChainConfig().IsEnabled(st.evm.ChainConfig().GetEIP2028Transition, st.evm.BlockNumber) + + contractCreation := msg.To() == nil + + + + // Pay intrinsic gas + + gas, err := IntrinsicGas(st.data, contractCreation, eip2f, eip2028f) +++======= ++ homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber) ++ istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.Context.BlockNumber) ++ contractCreation := msg.To() == nil ++ ++ // Check clauses 4-5, subtract intrinsic gas if everything is correct ++ gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, istanbul) +++>>>>>>> c2d2f4ed8f + if err != nil { + return nil, err + } +diff --cc core/tx_pool.go +index 1203024130,4c1bd809fd..0000000000 +--- a/core/tx_pool.go ++++ b/core/tx_pool.go +@@@ -223,6 -228,9 +228,12 @@@ type TxPool struct + signer types.Signer + mu sync.RWMutex + +++<<<<<<< HEAD +++======= ++ istanbul bool // Fork indicator whether we are in the istanbul stage. ++ eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions. ++ +++>>>>>>> c2d2f4ed8f + currentState *state.StateDB // Current state in the blockchain head + pendingNonces *txNoncer // Pending state tracking virtual nonces + currentMaxGas uint64 // Current gas limit for transaction caps +@@@ -264,7 -269,7 +275,11 @@@ func NewTxPool(config TxPoolConfig, cha + config: config, + chainconfig: chainconfig, + chain: chain, +++<<<<<<< HEAD + + signer: types.NewEIP155Signer(chainconfig.GetChainID()), +++======= ++ signer: types.LatestSigner(chainconfig), +++>>>>>>> c2d2f4ed8f + pending: make(map[common.Address]*txList), + queue: make(map[common.Address]*txList), + beats: make(map[common.Address]time.Time), +@@@ -557,7 -559,7 +575,11 @@@ func (pool *TxPool) validateTx(tx *type + return ErrInsufficientFunds + } + // Ensure the transaction has more gas than the basic tx fee. +++<<<<<<< HEAD + + intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, pool.eip2f, pool.eip2028f) +++======= ++ intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul) +++>>>>>>> c2d2f4ed8f + if err != nil { + return err + } +@@@ -1195,7 -1203,8 +1232,12 @@@ func (pool *TxPool) reset(oldHead, newH + + // Update all fork indicator by next pending block number. + next := new(big.Int).Add(newHead.Number, big.NewInt(1)) +++<<<<<<< HEAD + + pool.eip2028f = pool.chainconfig.IsEnabled(pool.chainconfig.GetEIP2028Transition, next) +++======= ++ pool.istanbul = pool.chainconfig.IsIstanbul(next) ++ pool.eip2718 = pool.chainconfig.IsBerlin(next) +++>>>>>>> c2d2f4ed8f + } + + // promoteExecutables moves transactions that have become processable from the +diff --cc core/types/transaction_signing.go +index cf754856fb,b4594cb90b..0000000000 +--- a/core/types/transaction_signing.go ++++ b/core/types/transaction_signing.go +@@@ -24,12 -24,10 +24,10 @@@ import + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + - "github.com/ethereum/go-ethereum/params" + + "github.com/ethereum/go-ethereum/params/types/ctypes" + ) + +- var ( +- ErrInvalidChainId = errors.New("invalid chain id for signer") +- ) ++ var ErrInvalidChainId = errors.New("invalid chain id for signer") + + // sigCache is used to cache the derived sender and contains + // the signer used to derive it. +@@@ -39,12 -37,14 +37,20 @@@ type sigCache struct + } + + // MakeSigner returns a Signer based on the given chain config and block number. + -func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { + +func MakeSigner(config ctypes.ChainConfigurator, blockNumber *big.Int) Signer { + var signer Signer + switch { +++<<<<<<< HEAD + + case config.IsEnabled(config.GetEIP155Transition, blockNumber): + + signer = NewEIP155Signer(config.GetChainID()) + + case config.IsEnabled(config.GetEIP2Transition, blockNumber): +++======= ++ case config.IsBerlin(blockNumber): ++ signer = NewEIP2930Signer(config.ChainID) ++ case config.IsEIP155(blockNumber): ++ signer = NewEIP155Signer(config.ChainID) ++ case config.IsHomestead(blockNumber): +++>>>>>>> c2d2f4ed8f + signer = HomesteadSigner{} + default: + signer = FrontierSigner{} +diff --cc core/vm/contracts.go +index 2aa2d392d2,4e99a51618..0000000000 +--- a/core/vm/contracts.go ++++ b/core/vm/contracts.go +@@@ -50,50 -51,81 +50,123 @@@ var basePrecompiledContracts = map[comm + common.BytesToAddress([]byte{4}): &dataCopy{}, + } + +++<<<<<<< HEAD + +// PrecompiledContractsForConfig returns a map containing valid precompiled contracts for a given point in a chain config. + +func PrecompiledContractsForConfig(config ctypes.ChainConfigurator, bn *big.Int) map[common.Address]PrecompiledContract { + + // Copying to a new map is necessary because assigning to the original map + + // creates a memory reference. Further, setting the vals to nil in case of nonconfiguration causes + + // a panic during tests because they run asynchronously (also a valid reason for using an explicit copy). + + precompileds := make(map[common.Address]PrecompiledContract) + + for k, v := range basePrecompiledContracts { + + precompileds[k] = v +++======= ++ // PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum ++ // contracts used in the Byzantium release. ++ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ ++ common.BytesToAddress([]byte{1}): &ecrecover{}, ++ common.BytesToAddress([]byte{2}): &sha256hash{}, ++ common.BytesToAddress([]byte{3}): &ripemd160hash{}, ++ common.BytesToAddress([]byte{4}): &dataCopy{}, ++ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false}, ++ common.BytesToAddress([]byte{6}): &bn256AddByzantium{}, ++ common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{}, ++ common.BytesToAddress([]byte{8}): &bn256PairingByzantium{}, ++ } ++ ++ // PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum ++ // contracts used in the Istanbul release. ++ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ ++ common.BytesToAddress([]byte{1}): &ecrecover{}, ++ common.BytesToAddress([]byte{2}): &sha256hash{}, ++ common.BytesToAddress([]byte{3}): &ripemd160hash{}, ++ common.BytesToAddress([]byte{4}): &dataCopy{}, ++ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false}, ++ common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, ++ common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, ++ common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, ++ common.BytesToAddress([]byte{9}): &blake2F{}, ++ } ++ ++ // PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum ++ // contracts used in the Berlin release. ++ var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{ ++ common.BytesToAddress([]byte{1}): &ecrecover{}, ++ common.BytesToAddress([]byte{2}): &sha256hash{}, ++ common.BytesToAddress([]byte{3}): &ripemd160hash{}, ++ common.BytesToAddress([]byte{4}): &dataCopy{}, ++ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true}, ++ common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, ++ common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, ++ common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, ++ common.BytesToAddress([]byte{9}): &blake2F{}, ++ } ++ ++ // PrecompiledContractsBLS contains the set of pre-compiled Ethereum ++ // contracts specified in EIP-2537. These are exported for testing purposes. ++ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{ ++ common.BytesToAddress([]byte{10}): &bls12381G1Add{}, ++ common.BytesToAddress([]byte{11}): &bls12381G1Mul{}, ++ common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{}, ++ common.BytesToAddress([]byte{13}): &bls12381G2Add{}, ++ common.BytesToAddress([]byte{14}): &bls12381G2Mul{}, ++ common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{}, ++ common.BytesToAddress([]byte{16}): &bls12381Pairing{}, ++ common.BytesToAddress([]byte{17}): &bls12381MapG1{}, ++ common.BytesToAddress([]byte{18}): &bls12381MapG2{}, ++ } ++ ++ var ( ++ PrecompiledAddressesBerlin []common.Address ++ PrecompiledAddressesIstanbul []common.Address ++ PrecompiledAddressesByzantium []common.Address ++ PrecompiledAddressesHomestead []common.Address ++ ) ++ ++ func init() { ++ for k := range PrecompiledContractsHomestead { ++ PrecompiledAddressesHomestead = append(PrecompiledAddressesHomestead, k) +++>>>>>>> c2d2f4ed8f + } + - for k := range PrecompiledContractsByzantium { + - PrecompiledAddressesHomestead = append(PrecompiledAddressesByzantium, k) + + if config.IsEnabled(config.GetEIP198Transition, bn) { + + precompileds[common.BytesToAddress([]byte{5})] = &bigModExp{} + } + - for k := range PrecompiledContractsIstanbul { + - PrecompiledAddressesIstanbul = append(PrecompiledAddressesIstanbul, k) + + if config.IsEnabled(config.GetEIP213Transition, bn) { + + if config.IsEnabled(config.GetEIP1108Transition, bn) { + + precompileds[common.BytesToAddress([]byte{6})] = &bn256AddIstanbul{} + + precompileds[common.BytesToAddress([]byte{7})] = &bn256ScalarMulIstanbul{} + + } else { + + precompileds[common.BytesToAddress([]byte{6})] = &bn256AddByzantium{} + + precompileds[common.BytesToAddress([]byte{7})] = &bn256ScalarMulByzantium{} + + } + } +++<<<<<<< HEAD + + if config.IsEnabled(config.GetEIP212Transition, bn) { + + if config.IsEnabled(config.GetEIP1108Transition, bn) { + + precompileds[common.BytesToAddress([]byte{8})] = &bn256PairingIstanbul{} + + } else { + + precompileds[common.BytesToAddress([]byte{8})] = &bn256PairingByzantium{} + + } + + } + + if config.IsEnabled(config.GetEIP152Transition, bn) { + + precompileds[common.BytesToAddress([]byte{9})] = &blake2F{} +++======= ++ for k := range PrecompiledContractsBerlin { ++ PrecompiledAddressesBerlin = append(PrecompiledAddressesBerlin, k) +++>>>>>>> c2d2f4ed8f + + } + + if config.IsEnabled(config.GetEIP2537Transition, bn) { + + precompileds[common.BytesToAddress([]byte{10})] = &bls12381G1Add{} + + precompileds[common.BytesToAddress([]byte{11})] = &bls12381G1Mul{} + + precompileds[common.BytesToAddress([]byte{12})] = &bls12381G1MultiExp{} + + precompileds[common.BytesToAddress([]byte{13})] = &bls12381G2Add{} + + precompileds[common.BytesToAddress([]byte{14})] = &bls12381G2Mul{} + + precompileds[common.BytesToAddress([]byte{15})] = &bls12381G2MultiExp{} + + precompileds[common.BytesToAddress([]byte{16})] = &bls12381Pairing{} + + precompileds[common.BytesToAddress([]byte{17})] = &bls12381MapG1{} + + precompileds[common.BytesToAddress([]byte{18})] = &bls12381MapG2{} + } + + + + return precompileds + } + + // RunPrecompiledContract runs and evaluates the output of a precompiled contract. +@@@ -246,25 -311,36 +352,40 @@@ func (c *bigModExp) RequiredGas(input [ + adjExpLen.Mul(big8, adjExpLen) + } + adjExpLen.Add(adjExpLen, big.NewInt(int64(msb))) +- + // Calculate the gas cost of the operation + gas := new(big.Int).Set(math.BigMax(modLen, baseLen)) +- switch { +- case gas.Cmp(big64) <= 0: ++ if c.eip2565 { ++ // EIP-2565 has three changes ++ // 1. Different multComplexity (inlined here) ++ // in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565): ++ // ++ // def mult_complexity(x): ++ // ceiling(x/8)^2 ++ // ++ //where is x is max(length_of_MODULUS, length_of_BASE) ++ gas = gas.Add(gas, big7) ++ gas = gas.Div(gas, big8) + gas.Mul(gas, gas) +- case gas.Cmp(big1024) <= 0: +- gas = new(big.Int).Add( +- new(big.Int).Div(new(big.Int).Mul(gas, gas), big4), +- new(big.Int).Sub(new(big.Int).Mul(big96, gas), big3072), +- ) +- default: +- gas = new(big.Int).Add( +- new(big.Int).Div(new(big.Int).Mul(gas, gas), big16), +- new(big.Int).Sub(new(big.Int).Mul(big480, gas), big199680), +- ) ++ ++ gas.Mul(gas, math.BigMax(adjExpLen, big1)) ++ // 2. Different divisor (`GQUADDIVISOR`) (3) ++ gas.Div(gas, big3) ++ if gas.BitLen() > 64 { ++ return math.MaxUint64 ++ } ++ // 3. Minimum price of 200 gas ++ if gas.Uint64() < 200 { ++ return 200 ++ } ++ return gas.Uint64() + } ++ gas = modexpMultComplexity(gas) + gas.Mul(gas, math.BigMax(adjExpLen, big1)) +++<<<<<<< HEAD + + gas.Div(gas, new(big.Int).SetUint64(vars.ModExpQuadCoeffDiv)) +++======= ++ gas.Div(gas, big20) +++>>>>>>> c2d2f4ed8f + + if gas.BitLen() > 64 { + return math.MaxUint64 +diff --cc core/vm/contracts_test.go +index 13164752d8,30d9b49f71..0000000000 +--- a/core/vm/contracts_test.go ++++ b/core/vm/contracts_test.go +@@@ -48,6 -43,30 +48,33 @@@ type precompiledFailureTest struct + Name string + } + +++<<<<<<< HEAD +++======= ++ // allPrecompiles does not map to the actual set of precompiles, as it also contains ++ // repriced versions of precompiles at certain slots ++ var allPrecompiles = map[common.Address]PrecompiledContract{ ++ common.BytesToAddress([]byte{1}): &ecrecover{}, ++ common.BytesToAddress([]byte{2}): &sha256hash{}, ++ common.BytesToAddress([]byte{3}): &ripemd160hash{}, ++ common.BytesToAddress([]byte{4}): &dataCopy{}, ++ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false}, ++ common.BytesToAddress([]byte{0xf5}): &bigModExp{eip2565: true}, ++ common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, ++ common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, ++ common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, ++ common.BytesToAddress([]byte{9}): &blake2F{}, ++ common.BytesToAddress([]byte{10}): &bls12381G1Add{}, ++ common.BytesToAddress([]byte{11}): &bls12381G1Mul{}, ++ common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{}, ++ common.BytesToAddress([]byte{13}): &bls12381G2Add{}, ++ common.BytesToAddress([]byte{14}): &bls12381G2Mul{}, ++ common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{}, ++ common.BytesToAddress([]byte{16}): &bls12381Pairing{}, ++ common.BytesToAddress([]byte{17}): &bls12381MapG1{}, ++ common.BytesToAddress([]byte{18}): &bls12381MapG2{}, ++ } ++ +++>>>>>>> c2d2f4ed8f + // EIP-152 test vectors + var blake2FMalformedInputTests = []precompiledFailureTest{ + { +diff --cc core/vm/evm.go +index 71e97a8d99,7346d76e5b..0000000000 +--- a/core/vm/evm.go ++++ b/core/vm/evm.go +@@@ -45,8 -42,33 +45,38 @@@ type + GetHashFunc func(uint64) common.Hash + ) + +++<<<<<<< HEAD + +func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { + + var precompiles = PrecompiledContractsForConfig(evm.ChainConfig(), evm.BlockNumber) +++======= ++ // ActivePrecompiles returns the addresses of the precompiles enabled with the current ++ // configuration ++ func (evm *EVM) ActivePrecompiles() []common.Address { ++ switch { ++ case evm.chainRules.IsBerlin: ++ return PrecompiledAddressesBerlin ++ case evm.chainRules.IsIstanbul: ++ return PrecompiledAddressesIstanbul ++ case evm.chainRules.IsByzantium: ++ return PrecompiledAddressesByzantium ++ default: ++ return PrecompiledAddressesHomestead ++ } ++ } ++ ++ func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { ++ var precompiles map[common.Address]PrecompiledContract ++ switch { ++ case evm.chainRules.IsBerlin: ++ precompiles = PrecompiledContractsBerlin ++ case evm.chainRules.IsIstanbul: ++ precompiles = PrecompiledContractsIstanbul ++ case evm.chainRules.IsByzantium: ++ precompiles = PrecompiledContractsByzantium ++ default: ++ precompiles = PrecompiledContractsHomestead ++ } +++>>>>>>> c2d2f4ed8f + p, ok := precompiles[addr] + return p, ok + } +@@@ -132,28 -158,36 +167,61 @@@ type EVM struct + + // NewEVM returns a new EVM. The returned EVM is not thread safe and should + // only ever be used *once*. +++<<<<<<< HEAD + +func NewEVM(ctx Context, statedb StateDB, chainConfig ctypes.ChainConfigurator, vmConfig Config) *EVM { + + evm := &EVM{ + + Context: ctx, + + StateDB: statedb, + + vmConfig: vmConfig, + + chainConfig: chainConfig, + + // The list of interpreters, space reserved for both EVM and EWASM ones. + + interpreters: make([]Interpreter, 0, 2), + + } + + + + // In some implementations, EWASM may be configured with a block number. + + // In this implementation, the interpreter is configured globally instead. + + if vmConfig.EWASMInterpreter != "" { + + evm.interpreters = append(evm.interpreters, &EVMC{ewasmModule, evm, evmc.CapabilityEWASM, false}) + + } + + + + if vmConfig.EVMInterpreter != "" { + + evm.interpreters = append(evm.interpreters, &EVMC{evmModule, evm, evmc.CapabilityEVM1, false}) + + } else { + + evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig)) + + } + + +++======= ++ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { ++ evm := &EVM{ ++ Context: blockCtx, ++ TxContext: txCtx, ++ StateDB: statedb, ++ vmConfig: vmConfig, ++ chainConfig: chainConfig, ++ chainRules: chainConfig.Rules(blockCtx.BlockNumber), ++ interpreters: make([]Interpreter, 0, 1), ++ } ++ ++ if chainConfig.IsEWASM(blockCtx.BlockNumber) { ++ // to be implemented by EVM-C and Wagon PRs. ++ // if vmConfig.EWASMInterpreter != "" { ++ // extIntOpts := strings.Split(vmConfig.EWASMInterpreter, ":") ++ // path := extIntOpts[0] ++ // options := []string{} ++ // if len(extIntOpts) > 1 { ++ // options = extIntOpts[1..] ++ // } ++ // evm.interpreters = append(evm.interpreters, NewEVMVCInterpreter(evm, vmConfig, options)) ++ // } else { ++ // evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig)) ++ // } ++ panic("No supported ewasm interpreter yet.") ++ } ++ ++ // vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here ++ // as we always want to have the built-in EVM as the failover option. ++ evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig)) +++>>>>>>> c2d2f4ed8f + evm.interpreter = evm.interpreters[0] + + return evm +@@@ -395,21 -436,19 +470,25 @@@ func (c *codeAndHash) Hash() common.Has + func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) { + // Depth check execution. Fail if we're trying to execute above the + // limit. + - if evm.depth > int(params.CallCreateDepth) { + + if evm.depth > int(vars.CallCreateDepth) { + return nil, common.Address{}, gas, ErrDepth + } +- if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { ++ if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { + return nil, common.Address{}, gas, ErrInsufficientBalance + } + nonce := evm.StateDB.GetNonce(caller.Address()) + evm.StateDB.SetNonce(caller.Address(), nonce+1) + + + // We add this to the access list _before_ taking a snapshot. Even if the creation fails, + // the access-list change should not be rolled back +++<<<<<<< HEAD + + if evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP2929Transition, evm.BlockNumber) { +++======= ++ if evm.chainRules.IsBerlin { +++>>>>>>> c2d2f4ed8f + evm.StateDB.AddAddressToAccessList(address) + } + + + // Ensure there's no existing contract already at the designated address + contractHash := evm.StateDB.GetCodeHash(address) + if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { +@@@ -418,10 -457,10 +497,10 @@@ + // Create a new account on the state + snapshot := evm.StateDB.Snapshot() + evm.StateDB.CreateAccount(address) + - if evm.chainRules.IsEIP158 { + + if evm.ChainConfig().IsEnabled(evm.chainConfig.GetEIP161abcTransition, evm.BlockNumber) { + evm.StateDB.SetNonce(address, 1) + } +- evm.Transfer(evm.StateDB, caller.Address(), address, value) ++ evm.Context.Transfer(evm.StateDB, caller.Address(), address, value) + + // Initialise a new contract and set the code that is to be used by the EVM. + // The contract is a scoped environment for this execution context only. +diff --cc core/vm/gas_table_test.go +index 55b8a52e76,6cd126c9b4..0000000000 +--- a/core/vm/gas_table_test.go ++++ b/core/vm/gas_table_test.go +@@@ -91,8 -91,7 +91,12 @@@ func TestEIP2200(t *testing.T) + CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, + Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, + } +++<<<<<<< HEAD + + + + vmenv := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) +++======= ++ vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) +++>>>>>>> c2d2f4ed8f + + _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int)) + if err != tt.failure { +diff --cc core/vm/interpreter.go +index 0df124224a,06a3b962b2..0000000000 +--- a/core/vm/interpreter.go ++++ b/core/vm/interpreter.go +@@@ -97,8 -96,25 +96,30 @@@ func NewEVMInterpreter(evm *EVM, cfg Co + // the jump table was initialised. If it was not + // we'll set the default jump table. + if cfg.JumpTable[STOP] == nil { +++<<<<<<< HEAD + + var jt = instructionSetForConfig(evm.chainConfig, evm.BlockNumber) + + +++======= ++ var jt JumpTable ++ switch { ++ case evm.chainRules.IsBerlin: ++ jt = berlinInstructionSet ++ case evm.chainRules.IsIstanbul: ++ jt = istanbulInstructionSet ++ case evm.chainRules.IsConstantinople: ++ jt = constantinopleInstructionSet ++ case evm.chainRules.IsByzantium: ++ jt = byzantiumInstructionSet ++ case evm.chainRules.IsEIP158: ++ jt = spuriousDragonInstructionSet ++ case evm.chainRules.IsEIP150: ++ jt = tangerineWhistleInstructionSet ++ case evm.chainRules.IsHomestead: ++ jt = homesteadInstructionSet ++ default: ++ jt = frontierInstructionSet ++ } +++>>>>>>> c2d2f4ed8f + for i, eip := range cfg.ExtraEips { + if err := EnableEIP(eip, &jt); err != nil { + // Disable it, so caller can check if it's activated or not +diff --cc core/vm/jump_table.go +index 0a3cda5141,7b61762456..0000000000 +--- a/core/vm/jump_table.go ++++ b/core/vm/jump_table.go +@@@ -51,142 -48,154 +51,188 @@@ type operation struct + returns bool // determines whether the operations sets the return data content + } + +++<<<<<<< HEAD + +// JumpTable contains the EVM opcodes supported at a given fork. + +type JumpTable [256]*operation + + + +// instructionSetForConfig determines an instruction set for the vm using + +// the chain config params and a current block number + +func instructionSetForConfig(config ctypes.ChainConfigurator, bn *big.Int) JumpTable { + + instructionSet := newBaseInstructionSet() + + + + // Homestead + + if config.IsEnabled(config.GetEIP7Transition, bn) { + + instructionSet[DELEGATECALL] = &operation{ + + execute: opDelegateCall, + + dynamicGas: gasDelegateCall, + + constantGas: vars.CallGasFrontier, + + minStack: minStack(6, 1), + + maxStack: maxStack(6, 1), + + memorySize: memoryDelegateCall, + + returns: true, + + } +++======= ++ var ( ++ frontierInstructionSet = newFrontierInstructionSet() ++ homesteadInstructionSet = newHomesteadInstructionSet() ++ tangerineWhistleInstructionSet = newTangerineWhistleInstructionSet() ++ spuriousDragonInstructionSet = newSpuriousDragonInstructionSet() ++ byzantiumInstructionSet = newByzantiumInstructionSet() ++ constantinopleInstructionSet = newConstantinopleInstructionSet() ++ istanbulInstructionSet = newIstanbulInstructionSet() ++ berlinInstructionSet = newBerlinInstructionSet() ++ ) ++ ++ // JumpTable contains the EVM opcodes supported at a given fork. ++ type JumpTable [256]*operation ++ ++ // newBerlinInstructionSet returns the frontier, homestead, byzantium, ++ // contantinople, istanbul, petersburg and berlin instructions. ++ func newBerlinInstructionSet() JumpTable { ++ instructionSet := newIstanbulInstructionSet() ++ enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929 ++ return instructionSet ++ } ++ ++ // newIstanbulInstructionSet returns the frontier, homestead, byzantium, ++ // contantinople, istanbul and petersburg instructions. ++ func newIstanbulInstructionSet() JumpTable { ++ instructionSet := newConstantinopleInstructionSet() ++ ++ enable1344(&instructionSet) // ChainID opcode - https://eips.ethereum.org/EIPS/eip-1344 ++ enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884 ++ enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200 ++ ++ return instructionSet ++ } ++ ++ // newConstantinopleInstructionSet returns the frontier, homestead, ++ // byzantium and contantinople instructions. ++ func newConstantinopleInstructionSet() JumpTable { ++ instructionSet := newByzantiumInstructionSet() ++ instructionSet[SHL] = &operation{ ++ execute: opSHL, ++ constantGas: GasFastestStep, ++ minStack: minStack(2, 1), ++ maxStack: maxStack(2, 1), +++>>>>>>> c2d2f4ed8f + } + - instructionSet[SHR] = &operation{ + - execute: opSHR, + - constantGas: GasFastestStep, + - minStack: minStack(2, 1), + - maxStack: maxStack(2, 1), + + // Tangerine Whistle + + if config.IsEnabled(config.GetEIP150Transition, bn) { + + instructionSet[BALANCE].constantGas = vars.BalanceGasEIP150 + + instructionSet[EXTCODESIZE].constantGas = vars.ExtcodeSizeGasEIP150 + + instructionSet[SLOAD].constantGas = vars.SloadGasEIP150 + + instructionSet[EXTCODECOPY].constantGas = vars.ExtcodeCopyBaseEIP150 + + instructionSet[CALL].constantGas = vars.CallGasEIP150 + + instructionSet[CALLCODE].constantGas = vars.CallGasEIP150 + + instructionSet[DELEGATECALL].constantGas = vars.CallGasEIP150 + } + - instructionSet[SAR] = &operation{ + - execute: opSAR, + - constantGas: GasFastestStep, + - minStack: minStack(2, 1), + - maxStack: maxStack(2, 1), + + // Spurious Dragon + + if config.IsEnabled(config.GetEIP160Transition, bn) { + + instructionSet[EXP].dynamicGas = gasExpEIP158 + } + - instructionSet[EXTCODEHASH] = &operation{ + - execute: opExtCodeHash, + - constantGas: params.ExtcodeHashGasConstantinople, + - minStack: minStack(1, 1), + - maxStack: maxStack(1, 1), + + // Byzantium + + if config.IsEnabled(config.GetEIP140Transition, bn) { + + instructionSet[REVERT] = &operation{ + + execute: opRevert, + + dynamicGas: gasRevert, + + minStack: minStack(2, 0), + + maxStack: maxStack(2, 0), + + memorySize: memoryRevert, + + reverts: true, + + returns: true, + + } + } + - instructionSet[CREATE2] = &operation{ + - execute: opCreate2, + - constantGas: params.Create2Gas, + - dynamicGas: gasCreate2, + - minStack: minStack(4, 1), + - maxStack: maxStack(4, 1), + - memorySize: memoryCreate2, + - writes: true, + - returns: true, + + if config.IsEnabled(config.GetEIP214Transition, bn) { + + instructionSet[STATICCALL] = &operation{ + + execute: opStaticCall, + + constantGas: vars.CallGasEIP150, + + dynamicGas: gasStaticCall, + + minStack: minStack(6, 1), + + maxStack: maxStack(6, 1), + + memorySize: memoryStaticCall, + + returns: true, + + } + } + - return instructionSet + -} + - + -// newByzantiumInstructionSet returns the frontier, homestead and + -// byzantium instructions. + -func newByzantiumInstructionSet() JumpTable { + - instructionSet := newSpuriousDragonInstructionSet() + - instructionSet[STATICCALL] = &operation{ + - execute: opStaticCall, + - constantGas: params.CallGasEIP150, + - dynamicGas: gasStaticCall, + - minStack: minStack(6, 1), + - maxStack: maxStack(6, 1), + - memorySize: memoryStaticCall, + - returns: true, + + if config.IsEnabled(config.GetEIP211Transition, bn) { + + instructionSet[RETURNDATASIZE] = &operation{ + + execute: opReturnDataSize, + + constantGas: GasQuickStep, + + minStack: minStack(0, 1), + + maxStack: maxStack(0, 1), + + } + + instructionSet[RETURNDATACOPY] = &operation{ + + execute: opReturnDataCopy, + + constantGas: GasFastestStep, + + dynamicGas: gasReturnDataCopy, + + minStack: minStack(3, 0), + + maxStack: maxStack(3, 0), + + memorySize: memoryReturnDataCopy, + + } + } + - instructionSet[RETURNDATASIZE] = &operation{ + - execute: opReturnDataSize, + - constantGas: GasQuickStep, + - minStack: minStack(0, 1), + - maxStack: maxStack(0, 1), + + // Constantinople + + if config.IsEnabled(config.GetEIP145Transition, bn) { + + instructionSet[SHL] = &operation{ + + execute: opSHL, + + constantGas: GasFastestStep, + + minStack: minStack(2, 1), + + maxStack: maxStack(2, 1), + + } + + instructionSet[SHR] = &operation{ + + execute: opSHR, + + constantGas: GasFastestStep, + + minStack: minStack(2, 1), + + maxStack: maxStack(2, 1), + + } + + instructionSet[SAR] = &operation{ + + execute: opSAR, + + constantGas: GasFastestStep, + + minStack: minStack(2, 1), + + maxStack: maxStack(2, 1), + + } + } + - instructionSet[RETURNDATACOPY] = &operation{ + - execute: opReturnDataCopy, + - constantGas: GasFastestStep, + - dynamicGas: gasReturnDataCopy, + - minStack: minStack(3, 0), + - maxStack: maxStack(3, 0), + - memorySize: memoryReturnDataCopy, + + if config.IsEnabled(config.GetEIP1014Transition, bn) { + + instructionSet[CREATE2] = &operation{ + + execute: opCreate2, + + constantGas: vars.Create2Gas, + + dynamicGas: gasCreate2, + + minStack: minStack(4, 1), + + maxStack: maxStack(4, 1), + + memorySize: memoryCreate2, + + writes: true, + + returns: true, + + } + } + - instructionSet[REVERT] = &operation{ + - execute: opRevert, + - dynamicGas: gasRevert, + - minStack: minStack(2, 0), + - maxStack: maxStack(2, 0), + - memorySize: memoryRevert, + - reverts: true, + - returns: true, + + if config.IsEnabled(config.GetEIP1052Transition, bn) { + + instructionSet[EXTCODEHASH] = &operation{ + + execute: opExtCodeHash, + + constantGas: vars.ExtcodeHashGasConstantinople, + + minStack: minStack(1, 1), + + maxStack: maxStack(1, 1), + + } + + } + + if config.IsEnabled(config.GetEIP1344Transition, bn) { + + enable1344(&instructionSet) // ChainID opcode - https://eips.ethereum.org/EIPS/eip-1344 + + } + + if config.IsEnabled(config.GetEIP1884Transition, bn) { + + enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884 + + } + + if config.IsEnabled(config.GetECIP1080Transition, bn) { + + enableSelfBalance(&instructionSet) + } + - return instructionSet + -} + - + -// EIP 158 a.k.a Spurious Dragon + -func newSpuriousDragonInstructionSet() JumpTable { + - instructionSet := newTangerineWhistleInstructionSet() + - instructionSet[EXP].dynamicGas = gasExpEIP158 + - return instructionSet + + -} + + if config.IsEnabled(config.GetEIP2200Transition, bn) && !config.IsEnabled(config.GetEIP2200DisableTransition, bn) { + + enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200 + + } + + -// EIP 150 a.k.a Tangerine Whistle + -func newTangerineWhistleInstructionSet() JumpTable { + - instructionSet := newHomesteadInstructionSet() + - instructionSet[BALANCE].constantGas = params.BalanceGasEIP150 + - instructionSet[EXTCODESIZE].constantGas = params.ExtcodeSizeGasEIP150 + - instructionSet[SLOAD].constantGas = params.SloadGasEIP150 + - instructionSet[EXTCODECOPY].constantGas = params.ExtcodeCopyBaseEIP150 + - instructionSet[CALL].constantGas = params.CallGasEIP150 + - instructionSet[CALLCODE].constantGas = params.CallGasEIP150 + - instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150 + - return instructionSet + -} + + if config.IsEnabled(config.GetEIP2315Transition, bn) { + + enable2315(&instructionSet) // Subroutines - https://eips.ethereum.org/EIPS/eip-2315 + + } + + -// newHomesteadInstructionSet returns the frontier and homestead + -// instructions that can be executed during the homestead phase. + -func newHomesteadInstructionSet() JumpTable { + - instructionSet := newFrontierInstructionSet() + - instructionSet[DELEGATECALL] = &operation{ + - execute: opDelegateCall, + - dynamicGas: gasDelegateCall, + - constantGas: params.CallGasFrontier, + - minStack: minStack(6, 1), + - maxStack: maxStack(6, 1), + - memorySize: memoryDelegateCall, + - returns: true, + + if config.IsEnabled(config.GetEIP2929Transition, bn) { + + enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929 + } + + + return instructionSet + } + +diff --cc core/vm/runtime/runtime.go +index 321f11e82c,9cb69e1c76..0000000000 +--- a/core/vm/runtime/runtime.go ++++ b/core/vm/runtime/runtime.go +@@@ -113,14 -114,9 +114,19 @@@ func Execute(code, input []byte, cfg *C + vmenv = NewEnv(cfg) + sender = vm.AccountRef(cfg.Origin) + ) +++<<<<<<< HEAD + + if cfg.ChainConfig.IsEnabled(cfg.ChainConfig.GetEIP2929Transition, vmenv.BlockNumber) { + + cfg.State.AddAddressToAccessList(cfg.Origin) + + cfg.State.AddAddressToAccessList(address) + + for addr := range vm.PrecompiledContractsForConfig(cfg.ChainConfig, vmenv.BlockNumber) { + + cfg.State.AddAddressToAccessList(addr) + + } +++======= ++ if cfg.ChainConfig.IsBerlin(vmenv.Context.BlockNumber) { ++ cfg.State.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil) +++>>>>>>> c2d2f4ed8f + } + + + cfg.State.CreateAccount(address) + // set the receiver's (the executing contract) code for execution. + cfg.State.SetCode(address, code) +@@@ -150,11 -146,8 +156,16 @@@ func Create(input []byte, cfg *Config) + vmenv = NewEnv(cfg) + sender = vm.AccountRef(cfg.Origin) + ) +++<<<<<<< HEAD + + if cfg.ChainConfig.IsEnabled(cfg.ChainConfig.GetEIP2929Transition, vmenv.BlockNumber) { + + cfg.State.AddAddressToAccessList(cfg.Origin) + + for addr := range vm.PrecompiledContractsForConfig(cfg.ChainConfig, vmenv.BlockNumber) { + + cfg.State.AddAddressToAccessList(addr) + + } +++======= ++ if cfg.ChainConfig.IsBerlin(vmenv.Context.BlockNumber) { ++ cfg.State.PrepareAccessList(cfg.Origin, nil, vmenv.ActivePrecompiles(), nil) +++>>>>>>> c2d2f4ed8f + } + + // Call the code with the given configuration. +@@@ -178,12 -171,9 +189,18 @@@ func Call(address common.Address, inpu + vmenv := NewEnv(cfg) + + sender := cfg.State.GetOrNewStateObject(cfg.Origin) +++<<<<<<< HEAD + + if cfg.ChainConfig.IsEnabled(cfg.ChainConfig.GetEIP2929Transition, vmenv.BlockNumber) { + + cfg.State.AddAddressToAccessList(cfg.Origin) + + cfg.State.AddAddressToAccessList(address) + + for addr := range vm.PrecompiledContractsForConfig(cfg.ChainConfig, vmenv.BlockNumber) { + + cfg.State.AddAddressToAccessList(addr) + + } +++======= ++ statedb := cfg.State ++ if cfg.ChainConfig.IsBerlin(vmenv.Context.BlockNumber) { ++ statedb.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil) +++>>>>>>> c2d2f4ed8f + } + + // Call the code with the given configuration. +diff --cc eth/api.go +index dd4485fc51,53ef91392b..0000000000 +--- a/eth/api.go ++++ b/eth/api.go +@@@ -67,12 -67,12 +67,19 @@@ func (api *PublicEthereumAPI) Hashrate( + } + + // ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config. +++<<<<<<< HEAD + +func (api *PublicEthereumAPI) ChainId() hexutil.Uint64 { + + chainID := new(big.Int) + + if config := api.e.blockchain.Config(); config.IsEnabled(config.GetEIP155Transition, api.e.blockchain.CurrentBlock().Number()) { + + chainID = config.GetChainID() +++======= ++ func (api *PublicEthereumAPI) ChainId() (hexutil.Uint64, error) { ++ // if current block is at or past the EIP-155 replay-protection fork block, return chainID from config ++ if config := api.e.blockchain.Config(); config.IsEIP155(api.e.blockchain.CurrentBlock().Number()) { ++ return (hexutil.Uint64)(config.ChainID.Uint64()), nil +++>>>>>>> c2d2f4ed8f + } +- return (hexutil.Uint64)(chainID.Uint64()) ++ return hexutil.Uint64(0), fmt.Errorf("chain not synced beyond EIP-155 replay-protection fork block") + } + + // PublicMinerAPI provides an API to control the miner. +@@@ -355,22 -331,29 +362,34 @@@ type BadBlockArgs struct + // GetBadBlocks returns a list of the last 'bad blocks' that the client has seen on the network + // and returns them as a JSON list of block-hashes + func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs, error) { +- blocks := api.eth.BlockChain().BadBlocks() +- results := make([]*BadBlockArgs, len(blocks)) +- +- var err error +- for i, block := range blocks { +- results[i] = &BadBlockArgs{ +- Hash: block.Hash(), +- } ++ var ( ++ err error ++ blocks = rawdb.ReadAllBadBlocks(api.eth.chainDb) ++ results = make([]*BadBlockArgs, 0, len(blocks)) ++ ) ++ for _, block := range blocks { ++ var ( ++ blockRlp string ++ blockJSON map[string]interface{} ++ ) + if rlpBytes, err := rlp.EncodeToBytes(block); err != nil { +- results[i].RLP = err.Error() // Hacky, but hey, it works ++ blockRlp = err.Error() // Hacky, but hey, it works + } else { +- results[i].RLP = fmt.Sprintf("0x%x", rlpBytes) ++ blockRlp = fmt.Sprintf("0x%x", rlpBytes) + } +++<<<<<<< HEAD + + if results[i].Block, err = ethapi.RPCMarshalBlock(block, true, true); err != nil { + + results[i].Block = ðapi.RPCMarshalBlockT{Error: err.Error()} +++======= ++ if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true); err != nil { ++ blockJSON = map[string]interface{}{"error": err.Error()} +++>>>>>>> c2d2f4ed8f + } ++ results = append(results, &BadBlockArgs{ ++ Hash: block.Hash(), ++ RLP: blockRlp, ++ Block: blockJSON, ++ }) + } + return results, nil + } +diff --cc eth/backend.go +index bfc214690c,76ce5137f4..0000000000 +--- a/eth/backend.go ++++ b/eth/backend.go +@@@ -47,13 -51,7 +51,12 @@@ import + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" +- "github.com/ethereum/go-ethereum/p2p/enr" + "github.com/ethereum/go-ethereum/params" + + "github.com/ethereum/go-ethereum/params/confp" + + "github.com/ethereum/go-ethereum/params/types/coregeth" + + "github.com/ethereum/go-ethereum/params/types/ctypes" + + "github.com/ethereum/go-ethereum/params/types/goethereum" + + "github.com/ethereum/go-ethereum/params/vars" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + ) +@@@ -95,12 -98,8 +103,12 @@@ type Ethereum struct + + // New creates a new Ethereum object (including the + // initialisation of the common Ethereum object) +- func New(stack *node.Node, config *Config) (*Ethereum, error) { ++ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { + // Ensure configuration values are compatible and sane + + var ( + + chainDb ethdb.Database + + err error + + ) + if config.SyncMode == downloader.LightSync { + return nil, errors.New("can't run eth.Ethereum in light sync mode, use les.LightEthereum") + } +@@@ -131,8 -126,8 +139,13 @@@ + if err != nil { + return nil, err + } +++<<<<<<< HEAD + + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.ECBP1100) + + if _, ok := genesisErr.(*confp.ConfigCompatError); genesisErr != nil && !ok { +++======= ++ chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideBerlin) ++ if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { +++>>>>>>> c2d2f4ed8f + return nil, genesisErr + } + log.Info("Initialised chain configuration", "config", chainConfig) +@@@ -148,7 -146,7 +164,11 @@@ + gasPrice: config.Miner.GasPrice, + etherbase: config.Miner.Etherbase, + bloomRequests: make(chan chan *bloombits.Retrieval), +++<<<<<<< HEAD + + bloomIndexer: NewBloomIndexer(chainDb, vars.BloomBitsBlocks, vars.BloomConfirms), +++======= ++ bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms), +++>>>>>>> c2d2f4ed8f + p2pServer: stack.Server(), + } + +@@@ -157,7 -155,7 +177,11 @@@ + if bcVersion != nil { + dbVer = fmt.Sprintf("%d", *bcVersion) + } +++<<<<<<< HEAD + + log.Info("Initialising Ethereum protocol", "versions", config.ProtocolVersions, "network", config.NetworkId, "dbversion", dbVer) +++======= ++ log.Info("Initialising Ethereum protocol", "network", config.NetworkId, "dbversion", dbVer) +++>>>>>>> c2d2f4ed8f + + if !config.SkipBcVersionCheck { + if bcVersion != nil && *bcVersion > core.BlockChainVersion { +@@@ -211,13 -204,19 +236,23 @@@ + cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit + checkpoint := config.Checkpoint + if checkpoint == nil { + - checkpoint = params.TrustedCheckpoints[genesisHash] + + if p, ok := chainConfig.(*coregeth.CoreGethChainConfig); ok { + + checkpoint = p.TrustedCheckpoint + + } else if p, ok := chainConfig.(*goethereum.ChainConfig); ok { + + checkpoint = p.TrustedCheckpoint + + } + } +- if eth.protocolManager, err = NewProtocolManager(chainConfig, checkpoint, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb, cacheLimit, config.Whitelist); err != nil { ++ if eth.handler, err = newHandler(&handlerConfig{ ++ Database: chainDb, ++ Chain: eth.blockchain, ++ TxPool: eth.txPool, ++ Network: config.NetworkId, ++ Sync: config.SyncMode, ++ BloomCache: uint64(cacheLimit), ++ EventMux: eth.eventMux, ++ Checkpoint: checkpoint, ++ Whitelist: config.Whitelist, ++ }); err != nil { + return nil, err + } + eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock) +@@@ -262,46 -280,6 +316,49 @@@ func makeExtraData(extra []byte) []byt + return extra + } + +++<<<<<<< HEAD + +// CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service + +func CreateConsensusEngine(stack *node.Node, chainConfig ctypes.ChainConfigurator, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine { + + // If proof-of-authority is requested, set it up + + if chainConfig.GetConsensusEngineType().IsClique() { + + return clique.New(&ctypes.CliqueConfig{ + + Period: chainConfig.GetCliquePeriod(), + + Epoch: chainConfig.GetCliqueEpoch(), + + }, db) + + } + + // Otherwise assume proof-of-work + + switch config.PowMode { + + case ethash.ModeFake: + + log.Warn("Ethash used in fake mode") + + return ethash.NewFaker() + + case ethash.ModePoissonFake: + + log.Warn("Ethash used in fake Poisson mode") + + return ethash.NewPoissonFaker() + + case ethash.ModeTest: + + log.Warn("Ethash used in test mode") + + return ethash.NewTester(nil, noverify) + + case ethash.ModeShared: + + log.Warn("Ethash used in shared mode") + + return ethash.NewShared() + + default: + + engine := ethash.New(ethash.Config{ + + CacheDir: stack.ResolvePath(config.CacheDir), + + CachesInMem: config.CachesInMem, + + CachesOnDisk: config.CachesOnDisk, + + CachesLockMmap: config.CachesLockMmap, + + DatasetDir: config.DatasetDir, + + DatasetsInMem: config.DatasetsInMem, + + DatasetsOnDisk: config.DatasetsOnDisk, + + DatasetsLockMmap: config.DatasetsLockMmap, + + ECIP1099Block: chainConfig.GetEthashECIP1099Transition(), + + }, notify, noverify) + + engine.SetThreads(-1) // Disable CPU mining + + return engine + + } + +} + + +++======= +++>>>>>>> c2d2f4ed8f + // APIs return 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 { +@@@ -523,21 -497,17 +580,32 @@@ func (s *Ethereum) EventMux() *event.Ty + 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 +++<<<<<<< HEAD + +func (s *Ethereum) EthVersion() int { return int(s.config.ProtocolVersions[0]) } + +func (s *Ethereum) NetVersion() uint64 { return s.networkID } + +func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } + +func (s *Ethereum) Synced() bool { return atomic.LoadUint32(&s.protocolManager.acceptTxs) == 1 } +++======= ++ func (s *Ethereum) Downloader() *downloader.Downloader { return s.handler.downloader } ++ func (s *Ethereum) Synced() bool { return atomic.LoadUint32(&s.handler.acceptTxs) == 1 } +++>>>>>>> c2d2f4ed8f + func (s *Ethereum) ArchiveMode() bool { return s.config.NoPruning } + func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer } + + // Protocols returns all the currently configured + // network protocols to start. + func (s *Ethereum) Protocols() []p2p.Protocol { +++<<<<<<< HEAD + + protos := make([]p2p.Protocol, len(s.config.ProtocolVersions)) + + for i, vsn := range s.config.ProtocolVersions { + + protos[i] = s.protocolManager.makeProtocol(vsn) + + protos[i].Attributes = []enr.Entry{s.currentEthEntry()} + + protos[i].DialCandidates = s.dialCandidates +++======= ++ protos := eth.MakeProtocols((*ethHandler)(s.handler), s.networkID, s.ethDialCandidates) ++ if s.config.SnapshotCache > 0 { ++ protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...) +++>>>>>>> c2d2f4ed8f + } + return protos + } +@@@ -545,10 -515,10 +613,10 @@@ + // Start implements node.Lifecycle, starting all internal goroutines needed by the + // Ethereum protocol implementation. + func (s *Ethereum) Start() error { +- s.startEthEntryUpdate(s.p2pServer.LocalNode()) ++ eth.StartENRUpdater(s.blockchain, s.p2pServer.LocalNode()) + + // Start the bloom bits servicing goroutines + - s.startBloomHandlers(params.BloomBitsBlocks) + + s.startBloomHandlers(vars.BloomBitsBlocks) + + // Figure out a max peers count based on the server limits + maxPeers := s.p2pServer.MaxPeers +diff --cc eth/downloader/downloader.go +index 0aa046a567,5ddd2f9848..0000000000 +--- a/eth/downloader/downloader.go ++++ b/eth/downloader/downloader.go +@@@ -808,36 -814,29 +824,59 @@@ func (d *Downloader) findAncestor(p *pe + } + } + +++<<<<<<< HEAD + + // Only attempt span search if local head is "close" to reported remote + + if math.Abs(float64(remoteHeight-localHeight)) < float64(MaxHeaderFetch) { + + a, err := d.findAncestorSpanSearch(p, remoteHeight, localHeight, floor) + + if err == nil { + + if a > localHeight { + + a = localHeight + + } + + return a, nil + + } else if err != errNoAncestor { + + return 0, err + + } + + } + + + + // Ancestor not found, we need to binary search over our chain + + a, err := d.findAncestorBinarySearch(p, remoteHeight, floor) + + if err != nil { + + return 0, err + + } + + if a > localHeight { + + a = localHeight + + } + + return a, nil + +} + + + +// findAncestorSpanSearch looks for a common ancestor by requesting a spaced set of headers between ours and theirs. + +func (d *Downloader) findAncestorSpanSearch(p *peerConnection, remoteHeight, localHeight uint64, floor int64) (uint64, error) { +++======= ++ ancestor, err := d.findAncestorSpanSearch(p, mode, remoteHeight, localHeight, floor) ++ if err == nil { ++ return ancestor, nil ++ } ++ // The returned error was not nil. ++ // If the error returned does not reflect that a common ancestor was not found, return it. ++ // If the error reflects that a common ancestor was not found, continue to binary search, ++ // where the error value will be reassigned. ++ if !errors.Is(err, errNoAncestorFound) { ++ return 0, err ++ } ++ ++ ancestor, err = d.findAncestorBinarySearch(p, mode, remoteHeight, floor) ++ if err != nil { ++ return 0, err ++ } ++ return ancestor, nil ++ } ++ ++ func (d *Downloader) findAncestorSpanSearch(p *peerConnection, mode SyncMode, remoteHeight, localHeight uint64, floor int64) (commonAncestor uint64, err error) { +++>>>>>>> c2d2f4ed8f + from, count, skip, max := calculateRequestSpan(remoteHeight, localHeight) + + - p.log.Trace("Span searching for common ancestor", "count", count, "from", from, "skip", skip) + + p.log.Debug("Span searching for common ancestor", "count", count, "from", from, "skip", skip) + + + go p.peer.RequestHeadersByNumber(uint64(from), count, skip, false) + + // Wait for the remote response to the head fetch +@@@ -917,11 -914,13 +956,21 @@@ + p.log.Debug("Found common ancestor", "number", number, "hash", hash) + return number, nil + } +++<<<<<<< HEAD + + return 0, errNoAncestor + +} + + + +// findAncestorBinarySearch negotiates a binary search using their reported chain. + +func (d *Downloader) findAncestorBinarySearch(p *peerConnection, remoteHeight uint64, floor int64) (uint64, error) { +++======= ++ return 0, errNoAncestorFound ++ } ++ ++ func (d *Downloader) findAncestorBinarySearch(p *peerConnection, mode SyncMode, remoteHeight uint64, floor int64) (commonAncestor uint64, err error) { ++ hash := common.Hash{} ++ ++ // Ancestor not found, we need to binary search over our chain +++>>>>>>> c2d2f4ed8f + start, end := uint64(0), remoteHeight + if floor > 0 { + start = uint64(floor) +diff --cc eth/ethconfig/config.go +index 8d812751fb,5d0eece067..0000000000 +--- a/eth/ethconfig/config.go ++++ b/eth/ethconfig/config.go +@@@ -29,14 -32,15 +32,21 @@@ import + "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/ethdb" ++ "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/miner" +++<<<<<<< HEAD:eth/config.go + + "github.com/ethereum/go-ethereum/params/types/ctypes" + + "github.com/ethereum/go-ethereum/params/types/genesisT" + + "github.com/ethereum/go-ethereum/params/vars" +++======= ++ "github.com/ethereum/go-ethereum/node" ++ "github.com/ethereum/go-ethereum/params" +++>>>>>>> c2d2f4ed8f:eth/ethconfig/config.go + ) + +- // DefaultFullGPOConfig contains default gasprice oracle settings for full node. +- var DefaultFullGPOConfig = gasprice.Config{ ++ // FullNodeGPO contains default gasprice oracle settings for full node. ++ var FullNodeGPO = gasprice.Config{ + Blocks: 20, + Percentile: 60, + MaxPrice: gasprice.DefaultMaxPrice, +@@@ -61,7 -65,8 +71,12 @@@ var Defaults = Config + DatasetsOnDisk: 2, + DatasetsLockMmap: false, + }, +++<<<<<<< HEAD:eth/config.go + + NetworkId: vars.DefaultNetworkID, +++======= ++ NetworkId: 1, ++ TxLookupLimit: 2350000, +++>>>>>>> c2d2f4ed8f:eth/ethconfig/config.go + LightPeers: 100, + UltraLightFraction: 75, + DatabaseCache: 512, +@@@ -186,15 -194,44 +206,55 @@@ type Config struct + RPCTxFeeCap float64 `toml:",omitempty"` + + // Checkpoint is a hardcoded checkpoint which can be nil. + - Checkpoint *params.TrustedCheckpoint `toml:",omitempty"` + + Checkpoint *ctypes.TrustedCheckpoint `toml:",omitempty"` + + // CheckpointOracle is the configuration for checkpoint oracle. +++<<<<<<< HEAD:eth/config.go + + CheckpointOracle *ctypes.CheckpointOracleConfig `toml:",omitempty"` + + + + // Manual configuration field for ECBP1100 activation number. Used for modifying genesis config via CLI flag. + + ECBP1100 *big.Int + + + + // ECBP1100NoDisable overrides + + // When this value is *true, ECBP100 will not (ever) be disabled; when *false, it will never be enabled. + + ECBP1100NoDisable *bool `toml:",omitempty"` +++======= ++ CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` ++ ++ // Berlin block override (TODO: remove after the fork) ++ OverrideBerlin *big.Int `toml:",omitempty"` ++ } ++ ++ // CreateConsensusEngine creates a consensus engine for the given chain configuration. ++ func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine { ++ // If proof-of-authority is requested, set it up ++ if chainConfig.Clique != nil { ++ return clique.New(chainConfig.Clique, db) ++ } ++ // Otherwise assume proof-of-work ++ switch config.PowMode { ++ case ethash.ModeFake: ++ log.Warn("Ethash used in fake mode") ++ return ethash.NewFaker() ++ case ethash.ModeTest: ++ log.Warn("Ethash used in test mode") ++ return ethash.NewTester(nil, noverify) ++ case ethash.ModeShared: ++ log.Warn("Ethash used in shared mode") ++ return ethash.NewShared() ++ default: ++ engine := ethash.New(ethash.Config{ ++ CacheDir: stack.ResolvePath(config.CacheDir), ++ CachesInMem: config.CachesInMem, ++ CachesOnDisk: config.CachesOnDisk, ++ CachesLockMmap: config.CachesLockMmap, ++ DatasetDir: config.DatasetDir, ++ DatasetsInMem: config.DatasetsInMem, ++ DatasetsOnDisk: config.DatasetsOnDisk, ++ DatasetsLockMmap: config.DatasetsLockMmap, ++ }, notify, noverify) ++ engine.SetThreads(-1) // Disable CPU mining ++ return engine ++ } +++>>>>>>> c2d2f4ed8f:eth/ethconfig/config.go + } +diff --cc eth/fetcher/block_fetcher_test.go +index b19db24083,a6eef71da0..0000000000 +--- a/eth/fetcher/block_fetcher_test.go ++++ b/eth/fetcher/block_fetcher_test.go +@@@ -40,7 -39,7 +40,11 @@@ var + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testAddress = crypto.PubkeyToAddress(testKey.PublicKey) + genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000)) +++<<<<<<< HEAD + + unknownBlock = types.NewBlock(&types.Header{GasLimit: vars.GenesisGasLimit}, nil, nil, nil, new(trie.Trie)) +++======= ++ unknownBlock = types.NewBlock(&types.Header{GasLimit: params.GenesisGasLimit}, nil, nil, nil, trie.NewStackTrie(nil)) +++>>>>>>> c2d2f4ed8f + ) + + // makeChain creates a chain of n blocks starting at and including parent. +diff --cc eth/filters/filter_system_test.go +index 732744cb81,52150366c1..0000000000 +--- a/eth/filters/filter_system_test.go ++++ b/eth/filters/filter_system_test.go +@@@ -170,8 -168,8 +175,13 @@@ func TestBlockSubscription(t *testing.T + var ( + db = rawdb.NewMemoryDatabase() + backend = &testBackend{db: db} +++<<<<<<< HEAD + + api = NewPublicFilterAPI(backend, false) + + genesis = core.MustCommitGenesis(db, new(genesisT.Genesis)) +++======= ++ api = NewPublicFilterAPI(backend, false, deadline) ++ genesis = new(core.Genesis).MustCommit(db) +++>>>>>>> c2d2f4ed8f + chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {}) + chainEvents = []core.ChainEvent{} + ) +diff --cc eth/gasprice/gasprice_test.go +index a1741fbdab,4fd2df10e2..0000000000 +--- a/eth/gasprice/gasprice_test.go ++++ b/eth/gasprice/gasprice_test.go +@@@ -62,11 -59,11 +62,15 @@@ func newTestBackend(t *testing.T) *test + var ( + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr = crypto.PubkeyToAddress(key.PublicKey) + - gspec = &core.Genesis{ + + gspec = &genesisT.Genesis{ + Config: params.TestChainConfig, + - Alloc: core.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}}, + + Alloc: genesisT.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}}, + } +++<<<<<<< HEAD + + signer = types.NewEIP155Signer(gspec.Config.GetChainID()) +++======= ++ signer = types.LatestSigner(gspec.Config) +++>>>>>>> c2d2f4ed8f + ) + engine := ethash.NewFaker() + db := rawdb.NewMemoryDatabase() +diff --cc eth/handler.go +index 132d7d98da,13fa701935..0000000000 +--- a/eth/handler.go ++++ b/eth/handler.go +@@@ -37,10 -36,7 +36,14 @@@ import + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p" +++<<<<<<< HEAD + + "github.com/ethereum/go-ethereum/p2p/enode" + + "github.com/ethereum/go-ethereum/params/types/ctypes" + + "github.com/ethereum/go-ethereum/params/vars" + + "github.com/ethereum/go-ethereum/rlp" +++======= ++ "github.com/ethereum/go-ethereum/params" +++>>>>>>> c2d2f4ed8f + "github.com/ethereum/go-ethereum/trie" + ) + +@@@ -95,24 -123,23 +130,29 @@@ type handler struct + chainSync *chainSyncer + wg sync.WaitGroup + peerWG sync.WaitGroup +- +- // Test fields or hooks +- broadcastTxAnnouncesOnly bool // Testing field, disable transaction propagation + } + +++<<<<<<< HEAD + +// NewProtocolManager returns a new Ethereum sub protocol manager. The Ethereum sub protocol manages peers capable + +// with the Ethereum network. + +func NewProtocolManager(config ctypes.ChainConfigurator, checkpoint *ctypes.TrustedCheckpoint, mode downloader.SyncMode, networkID uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database, cacheLimit int, whitelist map[uint64]common.Hash) (*ProtocolManager, error) { +++======= ++ // newHandler returns a handler for all Ethereum chain management protocol. ++ func newHandler(config *handlerConfig) (*handler, error) { +++>>>>>>> c2d2f4ed8f + // Create the protocol manager with the base fields +- manager := &ProtocolManager{ +- networkID: networkID, +- forkFilter: forkid.NewFilter(blockchain), +- eventMux: mux, +- txpool: txpool, +- blockchain: blockchain, +- chaindb: chaindb, ++ if config.EventMux == nil { ++ config.EventMux = new(event.TypeMux) // Nicety initialization for tests ++ } ++ h := &handler{ ++ networkID: config.Network, ++ forkFilter: forkid.NewFilter(config.Chain), ++ eventMux: config.EventMux, ++ database: config.Database, ++ txpool: config.TxPool, ++ chain: config.Chain, + peers: newPeerSet(), +- whitelist: whitelist, ++ whitelist: config.Whitelist, + txsyncCh: make(chan *txsync), + quitSync: make(chan struct{}), + } +@@@ -137,16 -163,17 +176,23 @@@ + log.Warn("Switch sync mode from fast sync to full sync") + } else { + // If fast sync was requested and our database is empty, grant it +- manager.fastSync = uint32(1) ++ h.fastSync = uint32(1) ++ if config.Sync == downloader.SnapSync { ++ h.snapSync = uint32(1) ++ } + } + } +- + // If we have trusted checkpoints, enforce them on the chain +++<<<<<<< HEAD + + if checkpoint != nil { + + manager.checkpointNumber = (checkpoint.SectionIndex+1)*vars.CHTFrequency - 1 + + manager.checkpointHash = checkpoint.SectionHead +++======= ++ if config.Checkpoint != nil { ++ h.checkpointNumber = (config.Checkpoint.SectionIndex+1)*params.CHTFrequency - 1 ++ h.checkpointHash = config.Checkpoint.SectionHead +++>>>>>>> c2d2f4ed8f + } +- + // Construct the downloader (long sync) and its backing state bloom if fast + // sync is requested. The downloader is responsible for deallocating the state + // bloom when it's done. +@@@ -198,121 -224,27 +243,98 @@@ + } + return p.RequestTxs(hashes) + } +- manager.txFetcher = fetcher.NewTxFetcher(txpool.Has, txpool.AddRemotes, fetchTx) +- +- manager.chainSync = newChainSyncer(manager) +- +- return manager, nil +- } +- +- func (pm *ProtocolManager) makeProtocol(version uint) p2p.Protocol { +- length, ok := protocolLengths[version] +- if !ok { +- panic("makeProtocol for unknown version") +- } +- +- return p2p.Protocol{ +- Name: protocolName, +- Version: version, +- Length: length, +- Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { +- return pm.runPeer(pm.newPeer(int(version), p, rw, pm.txpool.Get)) +- }, +- NodeInfo: func() interface{} { +- return pm.NodeInfo() +- }, +- PeerInfo: func(id enode.ID) interface{} { +- if p := pm.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil { +- return p.Info() +- } +- return nil +- }, +- } ++ h.txFetcher = fetcher.NewTxFetcher(h.txpool.Has, h.txpool.AddRemotes, fetchTx) ++ h.chainSync = newChainSyncer(h) ++ return h, nil + } + +++<<<<<<< HEAD + +func (pm *ProtocolManager) removePeer(id string) { + + // Short circuit if the peer was already removed + + peer := pm.peers.Peer(id) + + if peer == nil { + + return + + } + + log.Debug("Removing Ethereum peer", "peer", id) + + + + // Unregister the peer from the downloader and Ethereum peer set + + pm.downloader.UnregisterPeer(id) + + pm.txFetcher.Drop(id) + + + + if err := pm.peers.Unregister(id); err != nil { + + log.Error("Peer removal failed", "peer", id, "err", err) + + } + + // Hard disconnect at the networking layer + + peer.Peer.Disconnect(p2p.DiscUselessPeer) + +} + + + +func (pm *ProtocolManager) Start(maxPeers int) { + + pm.maxPeers = maxPeers + + + + // broadcast transactions + + pm.wg.Add(1) + + pm.txsCh = make(chan core.NewTxsEvent, txChanSize) + + pm.txsSub = pm.txpool.SubscribeNewTxsEvent(pm.txsCh) + + go pm.txBroadcastLoop() + + + + // broadcast mined blocks + + pm.wg.Add(1) + + pm.minedBlockSub = pm.eventMux.Subscribe(core.NewMinedBlockEvent{}) + + go pm.minedBroadcastLoop() + + + + // start sync handlers + + pm.wg.Add(2) + + go pm.chainSync.loop() + + go pm.txsyncLoop64() // TODO(karalabe): Legacy initial tx echange, drop with eth/64. + + + + // start artificial finality safety loop + + pm.wg.Add(1) + + go pm.artificialFinalitySafetyLoop() + +} + + + +func (pm *ProtocolManager) Stop() { + + pm.txsSub.Unsubscribe() // quits txBroadcastLoop + + pm.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop + + + + // Quit chainSync and txsync64. + + // After this is done, no new peers will be accepted. + + close(pm.quitSync) + + pm.wg.Wait() + + + + // Disconnect existing sessions. + + // This also closes the gate for any new registrations on the peer set. + + // sessions which are already established but not added to pm.peers yet + + // will exit when they try to register. + + pm.peers.Close() + + pm.peerWG.Wait() + + + + log.Info("Ethereum protocol stopped") + +} + + + +func (pm *ProtocolManager) newPeer(pv int, p *p2p.Peer, rw p2p.MsgReadWriter, getPooledTx func(hash common.Hash) *types.Transaction) *peer { + + return newPeer(pv, p, rw, getPooledTx) + +} + + + +func (pm *ProtocolManager) runPeer(p *peer) error { + + if !pm.chainSync.handlePeerEvent(p) { +- return p2p.DiscQuitting +++======= ++ // runEthPeer registers an eth peer into the joint eth/snap peerset, adds it to ++ // various subsistems and starts handling messages. ++ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error { ++ // If the peer has a `snap` extension, wait for it to connect so we can have ++ // a uniform initialization/teardown mechanism ++ snap, err := h.peers.waitSnapExtension(peer) ++ if err != nil { ++ peer.Log().Error("Snapshot extension barrier failed", "err", err) ++ return err + } +- pm.peerWG.Add(1) +- defer pm.peerWG.Done() +- return pm.handle(p) +- } +- +- // handle is the callback invoked to manage the life cycle of an eth peer. When +- // this function terminates, the peer is disconnected. +- func (pm *ProtocolManager) handle(p *peer) error { +- // Ignore maxPeers if this is a trusted peer +- if pm.peers.Len() >= pm.maxPeers && !p.Peer.Info().Network.Trusted { +- return p2p.DiscTooManyPeers ++ // TODO(karalabe): Not sure why this is needed ++ if !h.chainSync.handlePeerEvent(peer) { +++>>>>>>> c2d2f4ed8f ++ return p2p.DiscQuitting + } +- p.Log().Debug("Ethereum peer connected", "name", p.Name()) ++ h.peerWG.Add(1) ++ defer h.peerWG.Done() + + // Execute the Ethereum handshake + var ( +@@@ -926,25 -527,3 +617,28 @@@ func (h *handler) txBroadcastLoop() + } + } + } +++<<<<<<< HEAD + + + +// NodeInfo represents a short summary of the Ethereum sub-protocol metadata + +// known about the host peer. + +type NodeInfo struct { + + Network uint64 `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3, Rinkeby=4, Kotti=6) + + Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain + + Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block + + Config ctypes.ChainConfigurator `json:"config"` // Chain configuration for the fork rules + + Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block + +} + + + +// NodeInfo retrieves some protocol metadata about the running host node. + +func (pm *ProtocolManager) NodeInfo() *NodeInfo { + + currentBlock := pm.blockchain.CurrentBlock() + + return &NodeInfo{ + + Network: pm.networkID, + + Difficulty: pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()), + + Genesis: pm.blockchain.Genesis().Hash(), + + Config: pm.blockchain.Config(), + + Head: currentBlock.Hash(), + + } + +} +++======= +++>>>>>>> c2d2f4ed8f +diff --cc eth/handler_test.go +index b40cced79f,a90ef5c348..0000000000 +--- a/eth/handler_test.go ++++ b/eth/handler_test.go +@@@ -33,665 -29,142 +29,407 @@@ import + "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" +++<<<<<<< HEAD + + "github.com/ethereum/go-ethereum/p2p" + + "github.com/ethereum/go-ethereum/params/types/coregeth" + + "github.com/ethereum/go-ethereum/params/types/ctypes" + + "github.com/ethereum/go-ethereum/params/types/genesisT" + + "github.com/ethereum/go-ethereum/params/vars" +++======= ++ "github.com/ethereum/go-ethereum/params" +++>>>>>>> c2d2f4ed8f + ) + +- // Tests that block headers can be retrieved from a remote chain based on user queries. +- func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) } +- func TestGetBlockHeaders64(t *testing.T) { testGetBlockHeaders(t, 64) } ++ var ( ++ // testKey is a private key to use for funding a tester account. ++ testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + +- func testGetBlockHeaders(t *testing.T, protocol int) { +- pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil) +- peer, _ := newTestPeer("peer", protocol, pm, true) +- defer peer.close() +- +- // Create a "random" unknown hash for testing +- var unknown common.Hash +- for i := range unknown { +- unknown[i] = byte(i) +- } +- // Create a batch of tests for various scenarios +- limit := uint64(downloader.MaxHeaderFetch) +- tests := []struct { +- query *getBlockHeadersData // The query to execute for header retrieval +- expect []common.Hash // The hashes of the block whose headers are expected +- }{ +- // A single random block should be retrievable by hash and number too +- { +- &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, +- []common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, +- }, { +- &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, +- []common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, +- }, +- // Multiple headers should be retrievable in both directions +- { +- &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, +- []common.Hash{ +- pm.blockchain.GetBlockByNumber(limit / 2).Hash(), +- pm.blockchain.GetBlockByNumber(limit/2 + 1).Hash(), +- pm.blockchain.GetBlockByNumber(limit/2 + 2).Hash(), +- }, +- }, { +- &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, +- []common.Hash{ +- pm.blockchain.GetBlockByNumber(limit / 2).Hash(), +- pm.blockchain.GetBlockByNumber(limit/2 - 1).Hash(), +- pm.blockchain.GetBlockByNumber(limit/2 - 2).Hash(), +- }, +- }, +- // Multiple headers with skip lists should be retrievable +- { +- &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, +- []common.Hash{ +- pm.blockchain.GetBlockByNumber(limit / 2).Hash(), +- pm.blockchain.GetBlockByNumber(limit/2 + 4).Hash(), +- pm.blockchain.GetBlockByNumber(limit/2 + 8).Hash(), +- }, +- }, { +- &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, +- []common.Hash{ +- pm.blockchain.GetBlockByNumber(limit / 2).Hash(), +- pm.blockchain.GetBlockByNumber(limit/2 - 4).Hash(), +- pm.blockchain.GetBlockByNumber(limit/2 - 8).Hash(), +- }, +- }, +- // The chain endpoints should be retrievable +- { +- &getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1}, +- []common.Hash{pm.blockchain.GetBlockByNumber(0).Hash()}, +- }, { +- &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1}, +- []common.Hash{pm.blockchain.CurrentBlock().Hash()}, +- }, +- // Ensure protocol limits are honored +- { +- &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, +- pm.blockchain.GetBlockHashesFromHash(pm.blockchain.CurrentBlock().Hash(), limit), +- }, +- // Check that requesting more than available is handled gracefully +- { +- &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, +- []common.Hash{ +- pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), +- pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()).Hash(), +- }, +- }, { +- &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, +- []common.Hash{ +- pm.blockchain.GetBlockByNumber(4).Hash(), +- pm.blockchain.GetBlockByNumber(0).Hash(), +- }, +- }, +- // Check that requesting more than available is handled gracefully, even if mid skip +- { +- &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, +- []common.Hash{ +- pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), +- pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 1).Hash(), +- }, +- }, { +- &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, +- []common.Hash{ +- pm.blockchain.GetBlockByNumber(4).Hash(), +- pm.blockchain.GetBlockByNumber(1).Hash(), +- }, +- }, +- // Check a corner case where requesting more can iterate past the endpoints +- { +- &getBlockHeadersData{Origin: hashOrNumber{Number: 2}, Amount: 5, Reverse: true}, +- []common.Hash{ +- pm.blockchain.GetBlockByNumber(2).Hash(), +- pm.blockchain.GetBlockByNumber(1).Hash(), +- pm.blockchain.GetBlockByNumber(0).Hash(), +- }, +- }, +- // Check a corner case where skipping overflow loops back into the chain start +- { +- &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1}, +- []common.Hash{ +- pm.blockchain.GetBlockByNumber(3).Hash(), +- }, +- }, +- // Check a corner case where skipping overflow loops back to the same header +- { +- &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(1).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64}, +- []common.Hash{ +- pm.blockchain.GetBlockByNumber(1).Hash(), +- }, +- }, +- // Check that non existing headers aren't returned +- { +- &getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1}, +- []common.Hash{}, +- }, { +- &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() + 1}, Amount: 1}, +- []common.Hash{}, +- }, +- } +- // Run each of the tests and verify the results against the chain +- for i, tt := range tests { +- // Collect the headers to expect in the response +- headers := []*types.Header{} +- for _, hash := range tt.expect { +- headers = append(headers, pm.blockchain.GetBlockByHash(hash).Header()) +- } +- // Send the hash request and verify the response +- p2p.Send(peer.app, 0x03, tt.query) +- if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil { +- t.Errorf("test %d: headers mismatch: %v", i, err) +- } +- // If the test used number origins, repeat with hashes as the too +- if tt.query.Origin.Hash == (common.Hash{}) { +- if origin := pm.blockchain.GetBlockByNumber(tt.query.Origin.Number); origin != nil { +- tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0 +- +- p2p.Send(peer.app, 0x03, tt.query) +- if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil { +- t.Errorf("test %d: headers mismatch: %v", i, err) +- } +- } +- } +- } +- } ++ // testAddr is the Ethereum address of the tester account. ++ testAddr = crypto.PubkeyToAddress(testKey.PublicKey) ++ ) + +- // Tests that block contents can be retrieved from a remote chain based on their hashes. +- func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) } +- func TestGetBlockBodies64(t *testing.T) { testGetBlockBodies(t, 64) } ++ // testTxPool is a mock transaction pool that blindly accepts all transactions. ++ // Its goal is to get around setting up a valid statedb for the balance and nonce ++ // checks. ++ type testTxPool struct { ++ pool map[common.Hash]*types.Transaction // Hash map of collected transactions + +- func testGetBlockBodies(t *testing.T, protocol int) { +- pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil) +- peer, _ := newTestPeer("peer", protocol, pm, true) +- defer peer.close() ++ txFeed event.Feed // Notification feed to allow waiting for inclusion ++ lock sync.RWMutex // Protects the transaction pool ++ } + +- // Create a batch of tests for various scenarios +- limit := downloader.MaxBlockFetch +- tests := []struct { +- random int // Number of blocks to fetch randomly from the chain +- explicit []common.Hash // Explicitly requested blocks +- available []bool // Availability of explicitly requested blocks +- expected int // Total number of existing blocks to expect +- }{ +- {1, nil, nil, 1}, // A single random block should be retrievable +- {10, nil, nil, 10}, // Multiple random blocks should be retrievable +- {limit, nil, nil, limit}, // The maximum possible blocks should be retrievable +- {limit + 1, nil, nil, limit}, // No more than the possible block count should be returned +- {0, []common.Hash{pm.blockchain.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable +- {0, []common.Hash{pm.blockchain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable +- {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned +- +- // Existing and non-existing blocks interleaved should not cause problems +- {0, []common.Hash{ +- {}, +- pm.blockchain.GetBlockByNumber(1).Hash(), +- {}, +- pm.blockchain.GetBlockByNumber(10).Hash(), +- {}, +- pm.blockchain.GetBlockByNumber(100).Hash(), +- {}, +- }, []bool{false, true, false, true, false, true, false}, 3}, +- } +- // Run each of the tests and verify the results against the chain +- for i, tt := range tests { +- // Collect the hashes to request, and the response to expect +- hashes, seen := []common.Hash{}, make(map[int64]bool) +- bodies := []*blockBody{} +- +- for j := 0; j < tt.random; j++ { +- for { +- num := rand.Int63n(int64(pm.blockchain.CurrentBlock().NumberU64())) +- if !seen[num] { +- seen[num] = true +- +- block := pm.blockchain.GetBlockByNumber(uint64(num)) +- hashes = append(hashes, block.Hash()) +- if len(bodies) < tt.expected { +- bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) +- } +- break +- } +- } +- } +- for j, hash := range tt.explicit { +- hashes = append(hashes, hash) +- if tt.available[j] && len(bodies) < tt.expected { +- block := pm.blockchain.GetBlockByHash(hash) +- bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) +- } +- } +- // Send the hash request and verify the response +- p2p.Send(peer.app, 0x05, hashes) +- if err := p2p.ExpectMsg(peer.app, 0x06, bodies); err != nil { +- t.Errorf("test %d: bodies mismatch: %v", i, err) +- } ++ // newTestTxPool creates a mock transaction pool. ++ func newTestTxPool() *testTxPool { ++ return &testTxPool{ ++ pool: make(map[common.Hash]*types.Transaction), + } + } + +- // Tests that the node state database can be retrieved based on hashes. +- func TestGetNodeData63(t *testing.T) { testGetNodeData(t, 63) } +- func TestGetNodeData64(t *testing.T) { testGetNodeData(t, 64) } ++ // Has returns an indicator whether txpool has a transaction ++ // cached with the given hash. ++ func (p *testTxPool) Has(hash common.Hash) bool { ++ p.lock.Lock() ++ defer p.lock.Unlock() + +++<<<<<<< HEAD + +func testGetNodeData(t *testing.T, protocol int) { + + // Define three accounts to simulate transactions with + + acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + + acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + + acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) + + acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) + + + + signer := types.HomesteadSigner{} + + // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) + + generator := func(i int, block *core.BlockGen) { + + switch i { + + case 0: + + // In block 1, the test bank sends account #1 some ether. + + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), vars.TxGas, 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), acc1Addr, big.NewInt(1000), vars.TxGas, nil, nil), signer, testBankKey) + + tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), vars.TxGas, nil, nil), signer, acc1Key) + + block.AddTx(tx1) + + block.AddTx(tx2) + + case 2: + + // Block 3 is empty but was mined by account #2. + + block.SetCoinbase(acc2Addr) + + block.SetExtra([]byte("yeehaw")) + + case 3: + + // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). + + b2 := block.PrevBlock(1).Header() + + b2.Extra = []byte("foo") + + block.AddUncle(b2) + + b3 := block.PrevBlock(2).Header() + + b3.Extra = []byte("foo") + + block.AddUncle(b3) + + } + + } + + // Assemble the test environment + + pm, db := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil) + + peer, _ := newTestPeer("peer", protocol, pm, true) + + defer peer.close() + + + + // Fetch for now the entire chain db + + hashes := []common.Hash{} + + + + it := db.NewIterator(nil, nil) + + for it.Next() { + + if key := it.Key(); len(key) == common.HashLength { + + hashes = append(hashes, common.BytesToHash(key)) + + } + + } + + it.Release() + + + + p2p.Send(peer.app, 0x0d, hashes) + + msg, err := peer.app.ReadMsg() + + if err != nil { + + t.Fatalf("failed to read node data response: %v", err) + + } + + if msg.Code != 0x0e { + + t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, 0x0c) + + } + + var data [][]byte + + if err := msg.Decode(&data); err != nil { + + t.Fatalf("failed to decode response node data: %v", err) + + } + + // Verify that all hashes correspond to the requested data, and reconstruct a state tree + + for i, want := range hashes { + + if hash := crypto.Keccak256Hash(data[i]); hash != want { + + t.Errorf("data hash mismatch: have %x, want %x", hash, want) + + } + + } + + statedb := rawdb.NewMemoryDatabase() + + for i := 0; i < len(data); i++ { + + statedb.Put(hashes[i].Bytes(), data[i]) + + } + + accounts := []common.Address{testBank, acc1Addr, acc2Addr} + + for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { + + trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), state.NewDatabase(statedb), nil) + + + + for j, acc := range accounts { + + state, _ := pm.blockchain.State() + + bw := state.GetBalance(acc) + + bh := trie.GetBalance(acc) + + + + if (bw != nil && bh == nil) || (bw == nil && bh != nil) { + + t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) + + } + + if bw != nil && bh != nil && bw.Cmp(bw) != 0 { + + t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) + + } + + } + + } +++======= ++ return p.pool[hash] != nil +++>>>>>>> c2d2f4ed8f + } + +- // Tests that the transaction receipts can be retrieved based on hashes. +- func TestGetReceipt63(t *testing.T) { testGetReceipt(t, 63) } +- func TestGetReceipt64(t *testing.T) { testGetReceipt(t, 64) } ++ // Get retrieves the transaction from local txpool with given ++ // tx hash. ++ func (p *testTxPool) Get(hash common.Hash) *types.Transaction { ++ p.lock.Lock() ++ defer p.lock.Unlock() + +++<<<<<<< HEAD + +func testGetReceipt(t *testing.T, protocol int) { + + // Define three accounts to simulate transactions with + + acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + + acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + + acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) + + acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) + + + + signer := types.HomesteadSigner{} + + // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) + + generator := func(i int, block *core.BlockGen) { + + switch i { + + case 0: + + // In block 1, the test bank sends account #1 some ether. + + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), vars.TxGas, 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), acc1Addr, big.NewInt(1000), vars.TxGas, nil, nil), signer, testBankKey) + + tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), vars.TxGas, nil, nil), signer, acc1Key) + + block.AddTx(tx1) + + block.AddTx(tx2) + + case 2: + + // Block 3 is empty but was mined by account #2. + + block.SetCoinbase(acc2Addr) + + block.SetExtra([]byte("yeehaw")) + + case 3: + + // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). + + b2 := block.PrevBlock(1).Header() + + b2.Extra = []byte("foo") + + block.AddUncle(b2) + + b3 := block.PrevBlock(2).Header() + + b3.Extra = []byte("foo") + + block.AddUncle(b3) + + } + + } + + // Assemble the test environment + + pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil) + + peer, _ := newTestPeer("peer", protocol, pm, true) + + defer peer.close() + + + + // Collect the hashes to request, and the response to expect + + hashes, receipts := []common.Hash{}, []types.Receipts{} + + for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { + + block := pm.blockchain.GetBlockByNumber(i) + + + + hashes = append(hashes, block.Hash()) + + receipts = append(receipts, pm.blockchain.GetReceiptsByHash(block.Hash())) + + } + + // Send the hash request and verify the response + + p2p.Send(peer.app, 0x0f, hashes) + + if err := p2p.ExpectMsg(peer.app, 0x10, receipts); err != nil { + + t.Errorf("receipts mismatch: %v", err) + + } +++======= ++ return p.pool[hash] +++>>>>>>> c2d2f4ed8f + } + +- // Tests that post eth protocol handshake, clients perform a mutual checkpoint +- // challenge to validate each other's chains. Hash mismatches, or missing ones +- // during a fast sync should lead to the peer getting dropped. +- func TestCheckpointChallenge(t *testing.T) { +- tests := []struct { +- syncmode downloader.SyncMode +- checkpoint bool +- timeout bool +- empty bool +- match bool +- drop bool +- }{ +- // If checkpointing is not enabled locally, don't challenge and don't drop +- {downloader.FullSync, false, false, false, false, false}, +- {downloader.FastSync, false, false, false, false, false}, +- +- // If checkpointing is enabled locally and remote response is empty, only drop during fast sync +- {downloader.FullSync, true, false, true, false, false}, +- {downloader.FastSync, true, false, true, false, true}, // Special case, fast sync, unsynced peer +- +- // If checkpointing is enabled locally and remote response mismatches, always drop +- {downloader.FullSync, true, false, false, false, true}, +- {downloader.FastSync, true, false, false, false, true}, +- +- // If checkpointing is enabled locally and remote response matches, never drop +- {downloader.FullSync, true, false, false, true, false}, +- {downloader.FastSync, true, false, false, true, false}, +- +- // If checkpointing is enabled locally and remote times out, always drop +- {downloader.FullSync, true, true, false, true, true}, +- {downloader.FastSync, true, true, false, true, true}, +- } +- for _, tt := range tests { +- t.Run(fmt.Sprintf("sync %v checkpoint %v timeout %v empty %v match %v", tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match), func(t *testing.T) { +- testCheckpointChallenge(t, tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match, tt.drop) +- }) ++ // AddRemotes appends a batch of transactions to the pool, and notifies any ++ // listeners if the addition channel is non nil ++ func (p *testTxPool) AddRemotes(txs []*types.Transaction) []error { ++ p.lock.Lock() ++ defer p.lock.Unlock() ++ ++ for _, tx := range txs { ++ p.pool[tx.Hash()] = tx + } ++ p.txFeed.Send(core.NewTxsEvent{Txs: txs}) ++ return make([]error, len(txs)) + } + +++<<<<<<< HEAD + +func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpoint bool, timeout bool, empty bool, match bool, drop bool) { + + // Reduce the checkpoint handshake challenge timeout + + defer func(old time.Duration) { syncChallengeTimeout = old }(syncChallengeTimeout) + + syncChallengeTimeout = 250 * time.Millisecond + + + + // Initialize a chain and generate a fake CHT if checkpointing is enabled + + var ( + + db = rawdb.NewMemoryDatabase() + + config = new(coregeth.CoreGethChainConfig) + + ) + + core.MustCommitGenesis(db, &genesisT.Genesis{Config: config}) // Commit genesis block + + // If checkpointing is enabled, create and inject a fake CHT and the corresponding + + // chllenge response. + + var response *types.Header + + var cht *ctypes.TrustedCheckpoint + + if checkpoint { + + index := uint64(rand.Intn(500)) + + number := (index+1)*vars.CHTFrequency - 1 + + response = &types.Header{Number: big.NewInt(int64(number)), Extra: []byte("valid")} + + + + cht = &ctypes.TrustedCheckpoint{ + + SectionIndex: index, + + SectionHead: response.Hash(), + + } +- } +- // Create a checkpoint aware protocol manager +- blockchain, err := core.NewBlockChain(db, nil, config, ethash.NewFaker(), vm.Config{}, nil, nil) +- if err != nil { +- t.Fatalf("failed to create new blockchain: %v", err) +- } +- pm, err := NewProtocolManager(config, cht, syncmode, DefaultConfig.NetworkId, new(event.TypeMux), &testTxPool{pool: make(map[common.Hash]*types.Transaction)}, ethash.NewFaker(), blockchain, db, 1, nil) +- if err != nil { +- t.Fatalf("failed to start test protocol manager: %v", err) +- } +- pm.Start(1000) +- defer pm.Stop() +- +- // Connect a new peer and check that we receive the checkpoint challenge +- peer, _ := newTestPeer("peer", eth63, pm, true) +- defer peer.close() +- +- if checkpoint { +- challenge := &getBlockHeadersData{ +- Origin: hashOrNumber{Number: response.Number.Uint64()}, +- Amount: 1, +- Skip: 0, +- Reverse: false, +- } +- if err := p2p.ExpectMsg(peer.app, GetBlockHeadersMsg, challenge); err != nil { +- t.Fatalf("challenge mismatch: %v", err) +- } +- // Create a block to reply to the challenge if no timeout is simulated +- if !timeout { +- if empty { +- if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{}); err != nil { +- t.Fatalf("failed to answer challenge: %v", err) +- } +- } else if match { +- if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{response}); err != nil { +- t.Fatalf("failed to answer challenge: %v", err) +- } +- } else { +- if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{{Number: response.Number}}); err != nil { +- t.Fatalf("failed to answer challenge: %v", err) +- } +- } +- } +- } +- // Wait until the test timeout passes to ensure proper cleanup +- time.Sleep(syncChallengeTimeout + 300*time.Millisecond) +- +- // Verify that the remote peer is maintained or dropped +- if drop { +- if peers := pm.peers.Len(); peers != 0 { +- t.Fatalf("peer count mismatch: have %d, want %d", peers, 0) +- } +- } else { +- if peers := pm.peers.Len(); peers != 1 { +- t.Fatalf("peer count mismatch: have %d, want %d", peers, 1) +- } +- } +++======= ++ // Pending returns all the transactions known to the pool ++ func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) { ++ p.lock.RLock() ++ defer p.lock.RUnlock() ++ ++ batches := make(map[common.Address]types.Transactions) ++ for _, tx := range p.pool { ++ from, _ := types.Sender(types.HomesteadSigner{}, tx) ++ batches[from] = append(batches[from], tx) +++>>>>>>> c2d2f4ed8f ++ } ++ for _, batch := range batches { ++ sort.Sort(types.TxByNonce(batch)) ++ } ++ return batches, nil + } + +- func TestBroadcastBlock(t *testing.T) { +- var tests = []struct { +- totalPeers int +- broadcastExpected int +- }{ +- {1, 1}, +- {2, 1}, +- {3, 1}, +- {4, 2}, +- {5, 2}, +- {9, 3}, +- {12, 3}, +- {16, 4}, +- {26, 5}, +- {100, 10}, +- } +- for _, test := range tests { +- testBroadcastBlock(t, test.totalPeers, test.broadcastExpected) +- } ++ // SubscribeNewTxsEvent should return an event subscription of NewTxsEvent and ++ // send events to the given channel. ++ func (p *testTxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { ++ return p.txFeed.Subscribe(ch) + } + +++<<<<<<< HEAD + +func testBroadcastBlock(t *testing.T, totalPeers, broadcastExpected int) { + + var ( + + evmux = new(event.TypeMux) + + pow = ethash.NewFaker() + + db = rawdb.NewMemoryDatabase() + + config = &coregeth.CoreGethChainConfig{} + + gspec = &genesisT.Genesis{Config: config} + + genesis = core.MustCommitGenesis(db, gspec) + + ) + + blockchain, err := core.NewBlockChain(db, nil, config, pow, vm.Config{}, nil, nil) + + if err != nil { + + t.Fatalf("failed to create new blockchain: %v", err) + + } + + pm, err := NewProtocolManager(config, nil, downloader.FullSync, DefaultConfig.NetworkId, evmux, &testTxPool{pool: make(map[common.Hash]*types.Transaction)}, pow, blockchain, db, 1, nil) + + if err != nil { + + t.Fatalf("failed to start test protocol manager: %v", err) + + } + + pm.Start(1000) + + defer pm.Stop() + + var peers []*testPeer + + for i := 0; i < totalPeers; i++ { + + peer, _ := newTestPeer(fmt.Sprintf("peer %d", i), eth63, pm, true) + + defer peer.close() + + + + peers = append(peers, peer) + + } + + chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, func(i int, gen *core.BlockGen) {}) + + pm.BroadcastBlock(chain[0], true /*propagate*/) + + + + errCh := make(chan error, totalPeers) + + doneCh := make(chan struct{}, totalPeers) + + for _, peer := range peers { + + go func(p *testPeer) { + + if err := p2p.ExpectMsg(p.app, NewBlockMsg, &newBlockData{Block: chain[0], TD: big.NewInt(131136)}); err != nil { + + errCh <- err + + } else { + + doneCh <- struct{}{} + + } + + }(peer) + + } + + var received int + + for { + + select { + + case <-doneCh: + + received++ + + if received > broadcastExpected { + + // We can bail early here + + t.Errorf("broadcast count mismatch: have %d > want %d", received, broadcastExpected) + + return + + } + + case <-time.After(2 * time.Second): + + if received != broadcastExpected { + + t.Errorf("broadcast count mismatch: have %d, want %d", received, broadcastExpected) + + } + + return + + case err = <-errCh: + + t.Fatalf("broadcast failed: %v", err) + + } + + } +++======= ++ // testHandler is a live implementation of the Ethereum protocol handler, just ++ // preinitialized with some sane testing defaults and the transaction pool mocked ++ // out. ++ type testHandler struct { ++ db ethdb.Database ++ chain *core.BlockChain ++ txpool *testTxPool ++ handler *handler ++ } +++>>>>>>> c2d2f4ed8f + ++ // newTestHandler creates a new handler for testing purposes with no blocks. ++ func newTestHandler() *testHandler { ++ return newTestHandlerWithBlocks(0) + } + +++<<<<<<< HEAD + +// Tests that a propagated malformed block (uncles or transactions don't match + +// with the hashes in the header) gets discarded and not broadcast forward. + +func TestBroadcastMalformedBlock(t *testing.T) { + + // Create a live node to test propagation with + + var ( + + engine = ethash.NewFaker() + + db = rawdb.NewMemoryDatabase() + + config = &coregeth.CoreGethChainConfig{} + + gspec = &genesisT.Genesis{Config: config} + + genesis = core.MustCommitGenesis(db, gspec) + + ) + + blockchain, err := core.NewBlockChain(db, nil, config, engine, vm.Config{}, nil, nil) + + if err != nil { + + t.Fatalf("failed to create new blockchain: %v", err) +++======= ++ // newTestHandlerWithBlocks creates a new handler for testing purposes, with a ++ // given number of initial blocks. ++ func newTestHandlerWithBlocks(blocks int) *testHandler { ++ // Create a database pre-initialize with a genesis block ++ db := rawdb.NewMemoryDatabase() ++ (&core.Genesis{ ++ Config: params.TestChainConfig, ++ Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(1000000)}}, ++ }).MustCommit(db) ++ ++ chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) ++ ++ bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, nil) ++ if _, err := chain.InsertChain(bs); err != nil { ++ panic(err) ++ } ++ txpool := newTestTxPool() ++ ++ handler, _ := newHandler(&handlerConfig{ ++ Database: db, ++ Chain: chain, ++ TxPool: txpool, ++ Network: 1, ++ Sync: downloader.FastSync, ++ BloomCache: 1, ++ }) ++ handler.Start(1000) ++ ++ return &testHandler{ ++ db: db, ++ chain: chain, ++ txpool: txpool, ++ handler: handler, +++>>>>>>> c2d2f4ed8f + } +- pm, err := NewProtocolManager(config, nil, downloader.FullSync, DefaultConfig.NetworkId, new(event.TypeMux), new(testTxPool), engine, blockchain, db, 1, nil) +- if err != nil { +- t.Fatalf("failed to start test protocol manager: %v", err) +- } +- pm.Start(2) +- defer pm.Stop() +- +- // Create two peers, one to send the malformed block with and one to check +- // propagation +- source, _ := newTestPeer("source", eth63, pm, true) +- defer source.close() +- +- sink, _ := newTestPeer("sink", eth63, pm, true) +- defer sink.close() +- +- // Create various combinations of malformed blocks +- chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, func(i int, gen *core.BlockGen) {}) ++ } + +- malformedUncles := chain[0].Header() +- malformedUncles.UncleHash[0]++ +- malformedTransactions := chain[0].Header() +- malformedTransactions.TxHash[0]++ +- malformedEverything := chain[0].Header() +- malformedEverything.UncleHash[0]++ +- malformedEverything.TxHash[0]++ +- +- // Keep listening to broadcasts and notify if any arrives +- notify := make(chan struct{}, 1) +- go func() { +- if _, err := sink.app.ReadMsg(); err == nil { +- notify <- struct{}{} +- } +- }() +- // Try to broadcast all malformations and ensure they all get discarded +- for _, header := range []*types.Header{malformedUncles, malformedTransactions, malformedEverything} { +- block := types.NewBlockWithHeader(header).WithBody(chain[0].Transactions(), chain[0].Uncles()) +- if err := p2p.Send(source.app, NewBlockMsg, []interface{}{block, big.NewInt(131136)}); err != nil { +- t.Fatalf("failed to broadcast block: %v", err) +- } +- select { +- case <-notify: +- t.Fatalf("malformed block forwarded") +- case <-time.After(100 * time.Millisecond): +- } +- } ++ // close tears down the handler and all its internal constructs. ++ func (b *testHandler) close() { ++ b.handler.Stop() ++ b.chain.Stop() + } +diff --cc eth/peer.go +index ee3f28b43f,1cea9c640e..0000000000 +--- a/eth/peer.go ++++ b/eth/peer.go +@@@ -294,546 -54,20 +54,48 @@@ func (p *ethPeer) info() *ethPeerInfo + } + } + +- // Head retrieves a copy of the current head hash and total difficulty of the +- // peer. +- func (p *peer) Head() (hash common.Hash, td *big.Int) { +- p.lock.RLock() +- defer p.lock.RUnlock() +- +- copy(hash[:], p.head[:]) +- return hash, new(big.Int).Set(p.td) +- } +- +- // SetHead updates the head hash and total difficulty of the peer. +- func (p *peer) SetHead(hash common.Hash, td *big.Int) { +- p.lock.Lock() +- defer p.lock.Unlock() +- +- copy(p.head[:], hash[:]) +- p.td.Set(td) +- } +- +- // MarkBlock marks a block as known for the peer, ensuring that the block will +- // never be propagated to this particular peer. +- func (p *peer) MarkBlock(hash common.Hash) { +- // If we reached the memory allowance, drop a previously known block hash +- for p.knownBlocks.Cardinality() >= maxKnownBlocks { +- p.knownBlocks.Pop() +- } +- p.knownBlocks.Add(hash) +- } +- +- // MarkTransaction marks a transaction as known for the peer, ensuring that it +- // will never be propagated to this particular peer. +- func (p *peer) MarkTransaction(hash common.Hash) { +- // If we reached the memory allowance, drop a previously known transaction hash +- for p.knownTxs.Cardinality() >= maxKnownTxs { +- p.knownTxs.Pop() +- } +- p.knownTxs.Add(hash) +- } +- +- // SendTransactions64 sends transactions to the peer and includes the hashes +- // in its transaction hash set for future reference. +- // +- // This method is legacy support for initial transaction exchange in eth/64 and +- // prior. For eth/65 and higher use SendPooledTransactionHashes. +- func (p *peer) SendTransactions64(txs types.Transactions) error { +- return p.sendTransactions(txs) +- } +- +- // sendTransactions sends transactions to the peer and includes the hashes +- // in its transaction hash set for future reference. +- // +- // This method is a helper used by the async transaction sender. Don't call it +- // directly as the queueing (memory) and transmission (bandwidth) costs should +- // not be managed directly. +- func (p *peer) sendTransactions(txs types.Transactions) error { +- // Mark all the transactions as known, but ensure we don't overflow our limits +- for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(txs)) { +- p.knownTxs.Pop() +- } +- for _, tx := range txs { +- p.knownTxs.Add(tx.Hash()) +- } +- return p2p.Send(p.rw, TransactionMsg, txs) +- } +- +- // AsyncSendTransactions queues a list of transactions (by hash) to eventually +- // propagate to a remote peer. The number of pending sends are capped (new ones +- // will force old sends to be dropped) +- func (p *peer) AsyncSendTransactions(hashes []common.Hash) { +- select { +- case p.txBroadcast <- hashes: +- // Mark all the transactions as known, but ensure we don't overflow our limits +- for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { +- p.knownTxs.Pop() +- } +- for _, hash := range hashes { +- p.knownTxs.Add(hash) +- } +- case <-p.term: +- p.Log().Debug("Dropping transaction propagation", "count", len(hashes)) +- } +- } +- +- // sendPooledTransactionHashes sends transaction hashes to the peer and includes +- // them in its transaction hash set for future reference. +- // +- // This method is a helper used by the async transaction announcer. Don't call it +- // directly as the queueing (memory) and transmission (bandwidth) costs should +- // not be managed directly. +- func (p *peer) sendPooledTransactionHashes(hashes []common.Hash) error { +- // Mark all the transactions as known, but ensure we don't overflow our limits +- for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { +- p.knownTxs.Pop() +- } +- for _, hash := range hashes { +- p.knownTxs.Add(hash) +- } +- return p2p.Send(p.rw, NewPooledTransactionHashesMsg, hashes) +- } +- +- // AsyncSendPooledTransactionHashes queues a list of transactions hashes to eventually +- // announce to a remote peer. The number of pending sends are capped (new ones +- // will force old sends to be dropped) +- func (p *peer) AsyncSendPooledTransactionHashes(hashes []common.Hash) { +- select { +- case p.txAnnounce <- hashes: +- // Mark all the transactions as known, but ensure we don't overflow our limits +- for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { +- p.knownTxs.Pop() +- } +- for _, hash := range hashes { +- p.knownTxs.Add(hash) +- } +- case <-p.term: +- p.Log().Debug("Dropping transaction announcement", "count", len(hashes)) +- } +- } +- +- // SendPooledTransactionsRLP sends requested transactions to the peer and adds the +- // hashes in its transaction hash set for future reference. +- // +- // Note, the method assumes the hashes are correct and correspond to the list of +- // transactions being sent. +- func (p *peer) SendPooledTransactionsRLP(hashes []common.Hash, txs []rlp.RawValue) error { +- // Mark all the transactions as known, but ensure we don't overflow our limits +- for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { +- p.knownTxs.Pop() +- } +- for _, hash := range hashes { +- p.knownTxs.Add(hash) +- } +- return p2p.Send(p.rw, PooledTransactionsMsg, txs) +- } +- +- // SendNewBlockHashes announces the availability of a number of blocks through +- // a hash notification. +- func (p *peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error { +- // Mark all the block hashes as known, but ensure we don't overflow our limits +- for p.knownBlocks.Cardinality() > max(0, maxKnownBlocks-len(hashes)) { +- p.knownBlocks.Pop() +- } +- for _, hash := range hashes { +- p.knownBlocks.Add(hash) +- } +- request := make(newBlockHashesData, len(hashes)) +- for i := 0; i < len(hashes); i++ { +- request[i].Hash = hashes[i] +- request[i].Number = numbers[i] +- } +- return p2p.Send(p.rw, NewBlockHashesMsg, request) +- } +- +- // AsyncSendNewBlockHash queues the availability of a block for propagation to a +- // remote peer. If the peer's broadcast queue is full, the event is silently +- // dropped. +- func (p *peer) AsyncSendNewBlockHash(block *types.Block) { +- select { +- case p.queuedBlockAnns <- block: +- // Mark all the block hash as known, but ensure we don't overflow our limits +- for p.knownBlocks.Cardinality() >= maxKnownBlocks { +- p.knownBlocks.Pop() +- } +- p.knownBlocks.Add(block.Hash()) +- default: +- p.Log().Debug("Dropping block announcement", "number", block.NumberU64(), "hash", block.Hash()) +- } +- } +- +- // SendNewBlock propagates an entire block to a remote peer. +- func (p *peer) SendNewBlock(block *types.Block, td *big.Int) error { +- // Mark all the block hash as known, but ensure we don't overflow our limits +- for p.knownBlocks.Cardinality() >= maxKnownBlocks { +- p.knownBlocks.Pop() +- } +- p.knownBlocks.Add(block.Hash()) +- return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, td}) +- } +- +- // AsyncSendNewBlock queues an entire block for propagation to a remote peer. If +- // the peer's broadcast queue is full, the event is silently dropped. +- func (p *peer) AsyncSendNewBlock(block *types.Block, td *big.Int) { +- select { +- case p.queuedBlocks <- &propEvent{block: block, td: td}: +- // Mark all the block hash as known, but ensure we don't overflow our limits +- for p.knownBlocks.Cardinality() >= maxKnownBlocks { +- p.knownBlocks.Pop() +- } +- p.knownBlocks.Add(block.Hash()) +- default: +- p.Log().Debug("Dropping block propagation", "number", block.NumberU64(), "hash", block.Hash()) +- } +- } +- +- // SendBlockHeaders sends a batch of block headers to the remote peer. +- func (p *peer) SendBlockHeaders(headers []*types.Header) error { +- return p2p.Send(p.rw, BlockHeadersMsg, headers) +- } +- +- // SendBlockBodies sends a batch of block contents to the remote peer. +- func (p *peer) SendBlockBodies(bodies []*blockBody) error { +- return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies)) +- } +- +- // SendBlockBodiesRLP sends a batch of block contents to the remote peer from +- // an already RLP encoded format. +- func (p *peer) SendBlockBodiesRLP(bodies []rlp.RawValue) error { +- return p2p.Send(p.rw, BlockBodiesMsg, bodies) +- } +- +- // SendNodeDataRLP sends a batch of arbitrary internal data, corresponding to the +- // hashes requested. +- func (p *peer) SendNodeData(data [][]byte) error { +- return p2p.Send(p.rw, NodeDataMsg, data) +- } +- +- // SendReceiptsRLP sends a batch of transaction receipts, corresponding to the +- // ones requested from an already RLP encoded format. +- func (p *peer) SendReceiptsRLP(receipts []rlp.RawValue) error { +- return p2p.Send(p.rw, ReceiptsMsg, receipts) +- } +- +- // RequestOneHeader is a wrapper around the header query functions to fetch a +- // single header. It is used solely by the fetcher. +- func (p *peer) RequestOneHeader(hash common.Hash) error { +- p.Log().Debug("Fetching single header", "hash", hash) +- return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: uint64(1), Skip: uint64(0), Reverse: false}) +- } +- +- // RequestHeadersByHash fetches a batch of blocks' headers corresponding to the +- // specified header query, based on the hash of an origin block. +- func (p *peer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error { +- p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse) +- return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) +- } +- +- // RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the +- // specified header query, based on the number of an origin block. +- func (p *peer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error { +- p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse) +- return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) +- } +- +- // RequestBodies fetches a batch of blocks' bodies corresponding to the hashes +- // specified. +- func (p *peer) RequestBodies(hashes []common.Hash) error { +- p.Log().Debug("Fetching batch of block bodies", "count", len(hashes)) +- return p2p.Send(p.rw, GetBlockBodiesMsg, hashes) +- } +- +- // RequestNodeData fetches a batch of arbitrary data from a node's known state +- // data, corresponding to the specified hashes. +- func (p *peer) RequestNodeData(hashes []common.Hash) error { +- p.Log().Debug("Fetching batch of state data", "count", len(hashes)) +- return p2p.Send(p.rw, GetNodeDataMsg, hashes) +- } +- +- // RequestReceipts fetches a batch of transaction receipts from a remote node. +- func (p *peer) RequestReceipts(hashes []common.Hash) error { +- p.Log().Debug("Fetching batch of receipts", "count", len(hashes)) +- return p2p.Send(p.rw, GetReceiptsMsg, hashes) +- } +- +- // RequestTxs fetches a batch of transactions from a remote node. +- func (p *peer) RequestTxs(hashes []common.Hash) error { +- p.Log().Debug("Fetching batch of transactions", "count", len(hashes)) +- return p2p.Send(p.rw, GetPooledTransactionsMsg, hashes) +- } +- +- // Handshake executes the eth protocol handshake, negotiating version number, +- // network IDs, difficulties, head and genesis blocks. +- func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) error { +- // Send out own handshake in a new thread +- errc := make(chan error, 2) +- +- var ( +- status63 statusData63 // safe to read after two values have been received from errc +- status statusData // safe to read after two values have been received from errc +- ) +- go func() { +- switch { +- case p.version == eth63: +- errc <- p2p.Send(p.rw, StatusMsg, &statusData63{ +- ProtocolVersion: uint32(p.version), +- NetworkId: network, +- TD: td, +- CurrentBlock: head, +- GenesisBlock: genesis, +- }) +- case p.version >= eth64: +- errc <- p2p.Send(p.rw, StatusMsg, &statusData{ +- ProtocolVersion: uint32(p.version), +- NetworkID: network, +- TD: td, +- Head: head, +- Genesis: genesis, +- ForkID: forkID, +- }) +- default: +- panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) +- } +- }() +- go func() { +- switch { +- case p.version == eth63: +- errc <- p.readStatusLegacy(network, &status63, genesis) +- case p.version >= eth64: +- errc <- p.readStatus(network, &status, genesis, forkFilter) +- default: +- panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) +- } +- }() +- timeout := time.NewTimer(handshakeTimeout) +- defer timeout.Stop() +- for i := 0; i < 2; i++ { +- select { +- case err := <-errc: +- if err != nil { +- return err +- } +- case <-timeout.C: +- return p2p.DiscReadTimeout +- } +- } +- switch { +- case p.version == eth63: +- p.td, p.head = status63.TD, status63.CurrentBlock +- case p.version >= eth64: +- p.td, p.head = status.TD, status.Head +- default: +- panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) +- } +- return nil +- } +- +- func (p *peer) readStatusLegacy(network uint64, status *statusData63, genesis common.Hash) error { +- msg, err := p.rw.ReadMsg() +- if err != nil { +- return err +- } +- if msg.Code != StatusMsg { +- return errResp(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg) +- } +- if msg.Size > protocolMaxMsgSize { +- return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, protocolMaxMsgSize) +- } +- // Decode the handshake and make sure everything matches +- if err := msg.Decode(&status); err != nil { +- return errResp(ErrDecode, "msg %v: %v", msg, err) +- } +- if status.GenesisBlock != genesis { +- return errResp(ErrGenesisMismatch, "%x (!= %x)", status.GenesisBlock[:8], genesis[:8]) +- } +- if status.NetworkId != network { +- return errResp(ErrNetworkIDMismatch, "%d (!= %d)", status.NetworkId, network) +- } +- if int(status.ProtocolVersion) != p.version { +- return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version) +- } +- return nil +- } +- +- func (p *peer) readStatus(network uint64, status *statusData, genesis common.Hash, forkFilter forkid.Filter) error { +- msg, err := p.rw.ReadMsg() +- if err != nil { +- return err +- } +- if msg.Code != StatusMsg { +- return errResp(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg) +- } +- if msg.Size > protocolMaxMsgSize { +- return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, protocolMaxMsgSize) +- } +- // Decode the handshake and make sure everything matches +- if err := msg.Decode(&status); err != nil { +- return errResp(ErrDecode, "msg %v: %v", msg, err) +- } +- if status.NetworkID != network { +- return errResp(ErrNetworkIDMismatch, "%d (!= %d)", status.NetworkID, network) +- } +- if int(status.ProtocolVersion) != p.version { +- return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version) +- } +- if status.Genesis != genesis { +- return errResp(ErrGenesisMismatch, "%x (!= %x)", status.Genesis, genesis) +- } +- if err := forkFilter(status.ForkID); err != nil { +- return errResp(ErrForkIDRejected, "%v", err) +- } +- return nil +- } +- +- // String implements fmt.Stringer. +- func (p *peer) String() string { +- return fmt.Sprintf("Peer %s [%s]", p.id, +- fmt.Sprintf("eth/%2d", p.version), +- ) +- } +- +- // peerSet represents the collection of active peers currently participating in +- // the Ethereum sub-protocol. +- type peerSet struct { +- peers map[string]*peer +- lock sync.RWMutex +- closed bool +- } +- +- // newPeerSet creates a new peer set to track the active participants. +- func newPeerSet() *peerSet { +- return &peerSet{ +- peers: make(map[string]*peer), +- } +- } +- +- // Register injects a new peer into the working set, or returns an error if the +- // peer is already known. If a new peer it registered, its broadcast loop is also +- // started. +- func (ps *peerSet) Register(p *peer, removePeer func(string)) error { +- ps.lock.Lock() +- defer ps.lock.Unlock() +- +- if ps.closed { +- return errClosed +- } +- if _, ok := ps.peers[p.id]; ok { +- return errAlreadyRegistered +- } +- ps.peers[p.id] = p +- +- go p.broadcastBlocks(removePeer) +- go p.broadcastTransactions(removePeer) +- if p.version >= eth65 { +- go p.announceTransactions(removePeer) +- } +- return nil +- } +- +- // Unregister removes a remote peer from the active set, disabling any further +- // actions to/from that particular entity. +- func (ps *peerSet) Unregister(id string) error { +- ps.lock.Lock() +- defer ps.lock.Unlock() +- +- p, ok := ps.peers[id] +- if !ok { +- return errNotRegistered +- } +- delete(ps.peers, id) +- p.close() +- +- return nil +- } +- +- // Peer retrieves the registered peer with the given id. +- func (ps *peerSet) Peer(id string) *peer { +- ps.lock.RLock() +- defer ps.lock.RUnlock() +- +- return ps.peers[id] +- } +- +- // Len returns if the current number of peers in the set. +- func (ps *peerSet) Len() int { +- ps.lock.RLock() +- defer ps.lock.RUnlock() +- +- return len(ps.peers) +- } +- +- // PeersWithoutBlock retrieves a list of peers that do not have a given block in +- // their set of known hashes. +- func (ps *peerSet) PeersWithoutBlock(hash common.Hash) []*peer { +- ps.lock.RLock() +- defer ps.lock.RUnlock() +- +- list := make([]*peer, 0, len(ps.peers)) +- for _, p := range ps.peers { +- if !p.knownBlocks.Contains(hash) { +- list = append(list, p) +- } +- } +- return list +- } +- +- // PeersWithoutTx retrieves a list of peers that do not have a given transaction +- // in their set of known hashes. +- func (ps *peerSet) PeersWithoutTx(hash common.Hash) []*peer { +- ps.lock.RLock() +- defer ps.lock.RUnlock() +- +- list := make([]*peer, 0, len(ps.peers)) +- for _, p := range ps.peers { +- if !p.knownTxs.Contains(hash) { +- list = append(list, p) +- } +- } +- return list ++ // snapPeerInfo represents a short summary of the `snap` sub-protocol metadata known ++ // about a connected peer. ++ type snapPeerInfo struct { ++ Version uint `json:"version"` // Snapshot protocol version negotiated + } + +- // BestPeer retrieves the known peer with the currently highest total difficulty. +- func (ps *peerSet) BestPeer() *peer { +- ps.lock.RLock() +- defer ps.lock.RUnlock() +- +- var ( +- bestPeer *peer +- bestTd *big.Int +- ) +- for _, p := range ps.peers { +- if _, td := p.Head(); bestPeer == nil || td.Cmp(bestTd) > 0 { +- bestPeer, bestTd = p, td +- } +- } +- return bestPeer ++ // snapPeer is a wrapper around snap.Peer to maintain a few extra metadata. ++ type snapPeer struct { ++ *snap.Peer + } + +++<<<<<<< HEAD + +// WorstPeer retrieves the known peer with the currently lowest total difficulty. + +func (ps *peerSet) WorstPeer() *peer { + + ps.lock.RLock() + + defer ps.lock.RUnlock() + + + + var ( + + worstPeer *peer + + worstTD *big.Int + + ) + + for _, p := range ps.peers { + + if _, td := p.Head(); worstPeer == nil || td.Cmp(worstTD) < 0 { + + worstPeer, worstTD = p, td + + } + + } + + return worstPeer + +} + + + +// Close disconnects all peers. + +// No new peers can be registered after Close has returned. + +func (ps *peerSet) Close() { + + ps.lock.Lock() + + defer ps.lock.Unlock() + + + + for _, p := range ps.peers { + + p.Disconnect(p2p.DiscQuitting) +++======= ++ // info gathers and returns some `snap` protocol metadata known about a peer. ++ func (p *snapPeer) info() *snapPeerInfo { ++ return &snapPeerInfo{ ++ Version: p.Version(), +++>>>>>>> c2d2f4ed8f + } +- ps.closed = true + } +diff --cc eth/sync.go +index 4bed453996,dc72e88388..0000000000 +--- a/eth/sync.go ++++ b/eth/sync.go +@@@ -26,9 -26,9 +26,10 @@@ import + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/downloader" ++ "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" + + "github.com/ethereum/go-ethereum/params/vars" + ) + + const ( +@@@ -40,54 -40,13 +41,54 @@@ + txsyncPackSize = 100 * 1024 + ) + + +var ( + + // minArtificialFinalityPeers defines the minimum number of peers our node must be connected + + // to in order to enable artificial finality features. + + // A minimum number of peer connections mitigates the risk of lower-powered eclipse attacks. + + minArtificialFinalityPeers = defaultMinSyncPeers + + + + // artificialFinalitySafetyInterval defines the interval at which the local head is checked for staleness. + + // If the head is found to be stale across this interval, artificial finality features are disabled. + + // This prevents an abandoned victim of an eclipse attack from being forever destitute. + + artificialFinalitySafetyInterval = time.Second * time.Duration(30*vars.DurationLimit.Uint64()) + +) + + + type txsync struct { +- p *peer ++ p *eth.Peer + txs []*types.Transaction + } + + +// artificialFinalitySafetyLoop compares our local head across timer intervals. + +// If it changes, assuming the interval is sufficiently long, + +// it means we're syncing ok: there has been a steady flow of blocks. + +// If it doesn't change, it means that we've stalled syncing for some reason, + +// and should disable the permapoint feature in case that's keeping + +// us on a dead chain. + +func (pm *ProtocolManager) artificialFinalitySafetyLoop() { + + defer pm.wg.Done() + + + + t := time.NewTicker(artificialFinalitySafetyInterval) + + defer t.Stop() + + + + for { + + select { + + case <-t.C: + + if pm.blockchain.IsArtificialFinalityEnabled() { + + // Check if your chain has grown stale. + + // If it has, disable artificial finality, we could be on an attacker's + + // chain getting starved. + + if time.Since(time.Unix(int64(pm.blockchain.CurrentHeader().Time), 0)) > artificialFinalitySafetyInterval { + + pm.blockchain.EnableArtificialFinality(false, "reason", "stale safety interval", "interval", artificialFinalitySafetyInterval) + + } + + } + + case <-pm.quitSync: + + return + + } + + } + +} + + + // syncTransactions starts sending all currently pending transactions to the given peer. +- func (pm *ProtocolManager) syncTransactions(p *peer) { ++ func (h *handler) syncTransactions(p *eth.Peer) { + // Assemble the set of transaction to broadcast or announce to the remote + // peer. Fun fact, this is quite an expensive operation as it needs to sort + // the transactions if the sorting is not cached yet. However, with a random +@@@ -287,38 -244,24 +286,45 @@@ func (cs *chainSyncer) nextSyncOp() *ch + minPeers := defaultMinSyncPeers + if cs.forced { + minPeers = 1 +- } else if minPeers > cs.pm.maxPeers { +- minPeers = cs.pm.maxPeers ++ } else if minPeers > cs.handler.maxPeers { ++ minPeers = cs.handler.maxPeers + } +++<<<<<<< HEAD + + if cs.pm.peers.Len() < minArtificialFinalityPeers { + + if cs.pm.blockchain.IsArtificialFinalityEnabled() { + + // If artificial finality state is forcefully set (overridden) this will just be a noop. + + cs.pm.blockchain.EnableArtificialFinality(false, "reason", "low peers", "peers", cs.pm.peers.Len()) + + } + + } + + if cs.pm.peers.Len() < minPeers { +++======= ++ if cs.handler.peers.len() < minPeers { +++>>>>>>> c2d2f4ed8f + return nil + } +- +- // We have enough peers, check TD. +- peer := cs.pm.peers.BestPeer() ++ // We have enough peers, check TD ++ peer := cs.handler.peers.peerWithHighestTD() + if peer == nil { + return nil + } + mode, ourTD := cs.modeAndLocalHead() ++ if mode == downloader.FastSync && atomic.LoadUint32(&cs.handler.snapSync) == 1 { ++ // Fast sync via the snap protocol ++ mode = downloader.SnapSync ++ } + op := peerToSyncOp(mode, peer) + if op.td.Cmp(ourTD) <= 0 { + + // Enable artificial finality if parameters if should. + + // - In full sync mode. + + if op.mode == downloader.FullSync && + + // - Have enough peers. + + cs.pm.peers.Len() >= minArtificialFinalityPeers && + + // - Head is not stale. + + !(time.Since(time.Unix(int64(cs.pm.blockchain.CurrentHeader().Time), 0)) > artificialFinalitySafetyInterval) && + + // - AF is disabled (so we should reenable). + + !cs.pm.blockchain.IsArtificialFinalityEnabled() { + + cs.pm.blockchain.EnableArtificialFinality(true, "reason", "synced", "peers", cs.pm.peers.Len()) + + } + return nil // We're in sync. + } + return op +diff --cc eth/sync_test.go +index 7e9b42e372,9cc806b18a..0000000000 +--- a/eth/sync_test.go ++++ b/eth/sync_test.go +@@@ -21,65 -21,60 +21,96 @@@ import + "testing" + "time" + + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/eth/downloader" ++ "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" + + "github.com/ethereum/go-ethereum/params" + ) + +++<<<<<<< HEAD + +// blockGenContemporaryTime creates a block gen function that will bump the block times to within throwing + +// distance of the current time. + +// Without this, block times are built on a genesis with a 0 (or 10) timestamp, which is in 1970. + +// This causes the apparent age of the chain to exceed the safety interval and thus interferes with + +// reasonable expectations of staleness. + +func blockGenContemporaryTime(numBlocks int64) func(i int, gen *core.BlockGen) { + + startTimeUnix := time.Now().Unix() + + return func(i int, gen *core.BlockGen) { + + if i == 0 { + + // Here, 1024 is the max number of block we'll create. + + // 10 is the magic number that the test helper block gen will use for the block time offsets. + + gen.OffsetTime(startTimeUnix - (numBlocks * 10)) + + } + + } + +} + + + +func TestFastSyncDisabling63(t *testing.T) { testFastSyncDisabling(t, 63) } +++======= ++ // Tests that fast sync is disabled after a successful sync cycle. +++>>>>>>> c2d2f4ed8f + func TestFastSyncDisabling64(t *testing.T) { testFastSyncDisabling(t, 64) } + func TestFastSyncDisabling65(t *testing.T) { testFastSyncDisabling(t, 65) } + + // Tests that fast sync gets disabled as soon as a real block is successfully + // imported into the blockchain. +- func testFastSyncDisabling(t *testing.T, protocol int) { ++ func testFastSyncDisabling(t *testing.T, protocol uint) { + t.Parallel() + +++<<<<<<< HEAD + + maxBlocksCreated := 1024 + + genFunc := blockGenContemporaryTime(int64(maxBlocksCreated)) + + + + // Create a pristine protocol manager, check that fast sync is left enabled + + pmEmpty, _ := newTestProtocolManagerMust(t, downloader.FastSync, 0, genFunc, 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, downloader.FastSync, maxBlocksCreated, genFunc, nil) + + if atomic.LoadUint32(&pmFull.fastSync) == 1 { +++======= ++ // Create an empty handler and ensure it's in fast sync mode ++ empty := newTestHandler() ++ if atomic.LoadUint32(&empty.handler.fastSync) == 0 { ++ t.Fatalf("fast sync disabled on pristine blockchain") ++ } ++ defer empty.close() ++ ++ // Create a full handler and ensure fast sync ends up disabled ++ full := newTestHandlerWithBlocks(1024) ++ if atomic.LoadUint32(&full.handler.fastSync) == 1 { +++>>>>>>> c2d2f4ed8f + t.Fatalf("fast sync not disabled on non-empty blockchain") + } ++ defer full.close() ++ ++ // Sync up the two handlers ++ emptyPipe, fullPipe := p2p.MsgPipe() ++ defer emptyPipe.Close() ++ defer fullPipe.Close() + +- // Sync up the two peers +- io1, io2 := p2p.MsgPipe() +- go pmFull.handle(pmFull.newPeer(protocol, p2p.NewPeer(enode.ID{}, "empty", nil), io2, pmFull.txpool.Get)) +- go pmEmpty.handle(pmEmpty.newPeer(protocol, p2p.NewPeer(enode.ID{}, "full", nil), io1, pmEmpty.txpool.Get)) ++ emptyPeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), emptyPipe, empty.txpool) ++ fullPeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), fullPipe, full.txpool) ++ defer emptyPeer.Close() ++ defer fullPeer.Close() + ++ go empty.handler.runEthPeer(emptyPeer, func(peer *eth.Peer) error { ++ return eth.Handle((*ethHandler)(empty.handler), peer) ++ }) ++ go full.handler.runEthPeer(fullPeer, func(peer *eth.Peer) error { ++ return eth.Handle((*ethHandler)(full.handler), peer) ++ }) ++ // Wait a bit for the above handlers to start + time.Sleep(250 * time.Millisecond) +- op := peerToSyncOp(downloader.FastSync, pmEmpty.peers.BestPeer()) +- if err := pmEmpty.doSync(op); err != nil { +- t.Fatal("sync failed:", err) +- } + + // Check that fast sync was disabled +- if atomic.LoadUint32(&pmEmpty.fastSync) == 1 { ++ op := peerToSyncOp(downloader.FastSync, empty.handler.peers.peerWithHighestTD()) ++ if err := empty.handler.doSync(op); err != nil { ++ t.Fatal("sync failed:", err) ++ } ++ if atomic.LoadUint32(&empty.handler.fastSync) == 1 { + t.Fatalf("fast sync not disabled after successful synchronisation") + } + } +diff --cc eth/tracers/api.go +index 7af92b2b41,61fe055689..0000000000 +--- a/eth/tracers/api.go ++++ b/eth/tracers/api.go +@@@ -36,13 -36,12 +37,12 @@@ import + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" +- "github.com/ethereum/go-ethereum/eth/tracers" ++ "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/log" + - "github.com/ethereum/go-ethereum/params" + + "github.com/ethereum/go-ethereum/params/types/ctypes" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" +- "github.com/ethereum/go-ethereum/trie" + ) + + const ( +@@@ -209,16 -266,14 +268,24 @@@ func (api *API) traceChain(ctx context. + // Trace all the transactions contained within + for i, tx := range task.block.Transactions() { + msg, _ := tx.AsMessage(signer) +++<<<<<<< HEAD:eth/api_tracer.go + + vmctx := core.NewEVMContext(msg, task.block.Header(), api.eth.blockchain, nil) + + + + res, err := traceTx(ctx, api.eth, msg, vmctx, task.statedb, nil, config) +++======= ++ res, err := api.traceTx(ctx, msg, blockCtx, task.statedb, config) +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + if err != nil { + task.results[i] = &txTraceResult{Error: err.Error()} + log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) + break + } + // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect +++<<<<<<< HEAD:eth/api_tracer.go + + task.statedb.Finalise(api.eth.blockchain.Config().IsEnabled(api.eth.blockchain.Config().GetEIP161dTransition, task.block.Number())) +++======= ++ task.statedb.Finalise(api.backend.ChainConfig().IsEIP158(task.block.Number())) +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + task.results[i] = &txTraceResult{Result: res} + } + // Stream the result back to the user or abort on teardown +@@@ -297,28 -329,14 +341,35 @@@ + failed = err + break + } +++<<<<<<< HEAD:eth/api_tracer.go + + // Finalize the state so any modifications are written to the trie + + root, err := statedb.Commit(api.eth.blockchain.Config().IsEnabled(api.eth.blockchain.Config().GetEIP161dTransition, block.Number())) + + if err != nil { + + failed = err + + break + + } + + if err := statedb.Reset(root); err != nil { + + failed = err + + break + + } + + // Reference the trie twice, once for us, once for the tracer + + database.TrieDB().Reference(root, common.Hash{}) + + if number >= origin { + + database.TrieDB().Reference(root, common.Hash{}) + + } + + // Dereference all past tries we ourselves are done working with + + if proot != (common.Hash{}) { + + database.TrieDB().Dereference(proot) +++======= ++ // Send the block over to the concurrent tracers (if not in the fast-forward phase) ++ txs := block.Transactions() ++ select { ++ case tasks <- &blockTraceTask{statedb: states[int(number-start.NumberU64()-1)], block: block, results: make([]*txTraceResult, len(txs))}: ++ case <-notifier.Closed(): ++ return +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + } +- proot = root +- +- // TODO(karalabe): Do we need the preimages? Won't they accumulate too much? ++ traced += uint64(len(txs)) + } + }() + +@@@ -355,29 -370,12 +403,36 @@@ + + // TraceBlockByNumber returns the structured logs created during the execution of + // EVM and returns them as a JSON object. +++<<<<<<< HEAD:eth/api_tracer.go + +func traceBlockByNumber(ctx context.Context, eth *Ethereum, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { + + // Fetch the block that we want to trace + + var block *types.Block + + + + switch number { + + case rpc.PendingBlockNumber: + + block = eth.miner.PendingBlock() + + case rpc.LatestBlockNumber: + + block = eth.blockchain.CurrentBlock() + + default: + + block = eth.blockchain.GetBlockByNumber(uint64(number)) + + } + + // Trace the block if it was found + + if block == nil { + + return nil, fmt.Errorf("block #%d not found", number) +++======= ++ func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { ++ block, err := api.blockByNumber(ctx, number) ++ if err != nil { ++ return nil, err +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + } + - return api.traceBlock(ctx, block, config) + + return traceBlock(ctx, eth, block, config) + +} + + + +// TraceBlockByNumber returns the structured logs created during the execution of + +// EVM and returns them as a JSON object. + +func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { + + return traceBlockByNumber(ctx, api.eth, number, config) + } + + // TraceBlockByHash returns the structured logs created during the execution of +@@@ -390,9 -388,9 +445,13 @@@ func (api *API) TraceBlockByHash(ctx co + return api.traceBlock(ctx, block, config) + } + + -// TraceBlock returns the structured logs created during the execution of EVM + +// traceBlockRLP returns the structured logs created during the execution of EVM + // and returns them as a JSON object. +++<<<<<<< HEAD:eth/api_tracer.go + +func traceBlockRLP(ctx context.Context, eth *Ethereum, blob []byte, config *TraceConfig) ([]*txTraceResult, error) { +++======= ++ func (api *API) TraceBlock(ctx context.Context, blob []byte, config *TraceConfig) ([]*txTraceResult, error) { +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + block := new(types.Block) + if err := rlp.Decode(bytes.NewReader(blob), block); err != nil { + return nil, fmt.Errorf("could not decode block: %v", err) +@@@ -456,27 -446,27 +513,47 @@@ func (api *API) StandardTraceBadBlockTo + // traceBlock configures a new tracer according to the provided configuration, and + // executes all the transactions contained within. The return value will be one item + // per transaction, dependent on the requestd tracer. +++<<<<<<< HEAD:eth/api_tracer.go + +func traceBlock(ctx context.Context, eth *Ethereum, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { + + // Create the parent state database + + if err := eth.engine.VerifyHeader(eth.blockchain, block.Header(), true); err != nil { + + return nil, err + + } + + parent := eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) + + if parent == nil { + + return nil, fmt.Errorf("parent %#x not found", block.ParentHash()) +++======= ++ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { ++ if block.NumberU64() == 0 { ++ return nil, errors.New("genesis is not traceable") ++ } ++ parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) ++ if err != nil { ++ return nil, err +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + } + reexec := defaultTraceReexec + if config != nil && config.Reexec != nil { + reexec = *config.Reexec + } +++<<<<<<< HEAD:eth/api_tracer.go + + statedb, err := computeStateDB(eth, parent, reexec) +++======= ++ statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec) +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + if err != nil { + return nil, err + } ++ defer release() ++ + // Execute all the transaction contained within the block concurrently + var ( +++<<<<<<< HEAD:eth/api_tracer.go + + signer = types.MakeSigner(eth.blockchain.Config(), block.Number()) + + +++======= ++ signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + txs = block.Transactions() + results = make([]*txTraceResult, len(txs)) + +@@@ -494,10 -485,7 +572,13 @@@ + // Fetch and execute the next transaction trace tasks + for task := range jobs { + msg, _ := txs[task.index].AsMessage(signer) +++<<<<<<< HEAD:eth/api_tracer.go + + vmctx := core.NewEVMContext(msg, block.Header(), eth.blockchain, nil) + + + + res, err := traceTx(ctx, eth, msg, vmctx, task.statedb, task.taskExtraContext, config) +++======= ++ res, err := api.traceTx(ctx, msg, blockCtx, task.statedb, config) +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + if err != nil { + results[task.index] = &txTraceResult{Error: err.Error()} + continue +@@@ -521,9 -502,9 +602,15 @@@ + + // Generate the next state snapshot fast without tracing + msg, _ := tx.AsMessage(signer) +++<<<<<<< HEAD:eth/api_tracer.go + + vmctx := core.NewEVMContext(msg, block.Header(), eth.blockchain, nil) + + + + vmenv := vm.NewEVM(vmctx, statedb, eth.blockchain.Config(), vm.Config{}) +++======= ++ txContext := core.NewEVMTxContext(msg) ++ ++ vmenv := vm.NewEVM(blockCtx, txContext, statedb, api.backend.ChainConfig(), vm.Config{}) +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { + failed = err + break +@@@ -568,7 -544,7 +654,11 @@@ func (api *API) standardTraceBlockToFil + if config != nil && config.Reexec != nil { + reexec = *config.Reexec + } +++<<<<<<< HEAD:eth/api_tracer.go + + statedb, err := computeStateDB(api.eth, parent, reexec) +++======= ++ statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec) +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + if err != nil { + return nil, err + } +@@@ -599,26 -576,13 +690,36 @@@ + // Therefore, it's perfectly valid to specify `"futureForkBlock": 0`, to enable `futureFork` + + if config != nil && config.Overrides != nil { +++<<<<<<< HEAD:eth/api_tracer.go + + + + overrideEIP2929 := config.Overrides.GetEIP2929Transition() + + existingEIP2929 := chainConfig.GetEIP2929Transition() + + + + // We only need to make a copy if this transition is actually going to be overridden. + + // This very ugly logic assumes that testing value equivalence 4 times is cheaper than copying an arbitrary + + // (and probably biggish) interface value unnecessarily. + + if (overrideEIP2929 == nil && existingEIP2929 != nil) || + + (overrideEIP2929 != nil && existingEIP2929 == nil) || + + (overrideEIP2929 != nil && existingEIP2929 != nil && *overrideEIP2929 != *existingEIP2929) { + + + + // Copy the config, to not screw up the main config + + // Note: the Clique-part is _not_ deep copied + + + + // We know that the underlying configurator value will always be a pointer. + + chainConfig = reflect.New(reflect.ValueOf(chainConfig).Elem().Type()).Interface().(ctypes.ChainConfigurator) + + + + if err := chainConfig.SetEIP2929Transition(overrideEIP2929); err != nil { + + return nil, err + + } +++======= ++ // Copy the config, to not screw up the main config ++ // Note: the Clique-part is _not_ deep copied ++ chainConfigCopy := new(params.ChainConfig) ++ *chainConfigCopy = *chainConfig ++ chainConfig = chainConfigCopy ++ if berlin := config.LogConfig.Overrides.BerlinBlock; berlin != nil { ++ chainConfig.BerlinBlock = berlin +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + canon = false + } + } +@@@ -690,160 -653,78 +790,193 @@@ func containsTx(block *types.Block, has + return false + } + +++<<<<<<< HEAD:eth/api_tracer.go + +// computeStateDB retrieves the state database associated with a certain block. + +// If no state is locally available for the given block, a number of blocks are + +// attempted to be reexecuted to generate the desired state. + +func computeStateDB(eth *Ethereum, block *types.Block, reexec uint64) (*state.StateDB, error) { + + // If we have the state fully available, use that + + statedb, err := eth.blockchain.StateAt(block.Root()) + + if err == nil { + + return statedb, nil + + } + + // Otherwise try to reexec blocks until we find a state or reach our limit + + origin := block.NumberU64() + + database := state.NewDatabaseWithCache(eth.ChainDb(), 16, "") + + + + for i := uint64(0); i < reexec; i++ { + + block = eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) + + if block == nil { + + break + + } + + if statedb, err = state.New(block.Root(), database, nil); err == nil { + + break + + } + + } + + if err != nil { + + switch err.(type) { + + case *trie.MissingNodeError: + + return nil, fmt.Errorf("required historical state unavailable (reexec=%d)", reexec) + + default: + + return nil, err + + } + + } + + // State was available at historical point, regenerate + + var ( + + start = time.Now() + + logged time.Time + + proot common.Hash + + ) + + for block.NumberU64() < origin { + + // Print progress logs if long enough time elapsed + + if time.Since(logged) > 8*time.Second { + + log.Info("Regenerating historical state", "block", block.NumberU64()+1, "target", origin, "remaining", origin-block.NumberU64()-1, "elapsed", time.Since(start)) + + logged = time.Now() + + } + + // Retrieve the next block to regenerate and process it + + if block = eth.blockchain.GetBlockByNumber(block.NumberU64() + 1); block == nil { + + return nil, fmt.Errorf("block #%d not found", block.NumberU64()+1) + + } + + _, _, _, err := eth.blockchain.Processor().Process(block, statedb, vm.Config{}) + + if err != nil { + + return nil, fmt.Errorf("processing block %d failed: %v", block.NumberU64(), err) + + } + + // Finalize the state so any modifications are written to the trie + + root, err := statedb.Commit(eth.blockchain.Config().IsEnabled(eth.blockchain.Config().GetEIP161dTransition, block.Number())) + + if err != nil { + + return nil, err + + } + + if err := statedb.Reset(root); err != nil { + + return nil, fmt.Errorf("state reset after block %d failed: %v", block.NumberU64(), err) + + } + + database.TrieDB().Reference(root, common.Hash{}) + + if proot != (common.Hash{}) { + + database.TrieDB().Dereference(proot) + + } + + proot = root + + } + + nodes, imgs := database.TrieDB().Size() + + log.Info("Historical state regenerated", "block", block.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs) + + return statedb, nil + +} + + + +func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*state.StateDB, error) { + + return computeStateDB(api.eth, block, reexec) + +} + + + +// traceTransaction returns the structured logs created during the execution of EVM + +// and returns them as a JSON object. + +func traceTransaction(ctx context.Context, eth *Ethereum, hash common.Hash, config *TraceConfig) (interface{}, error) { + + // Retrieve the transaction and assemble its EVM context + + tx, blockHash, _, index := rawdb.ReadTransaction(eth.ChainDb(), hash) + + if tx == nil { + + return nil, fmt.Errorf("transaction %#x not found", hash) +++======= ++ // TraceTransaction returns the structured logs created during the execution of EVM ++ // and returns them as a JSON object. ++ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { ++ _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) ++ if err != nil { ++ return nil, err ++ } ++ // It shouldn't happen in practice. ++ if blockNumber == 0 { ++ return nil, errors.New("genesis is not traceable") +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + } + reexec := defaultTraceReexec + if config != nil && config.Reexec != nil { + reexec = *config.Reexec + } +++<<<<<<< HEAD:eth/api_tracer.go + + // Retrieve the block + + block := eth.blockchain.GetBlockByHash(blockHash) + + if block == nil { + + return nil, fmt.Errorf("block %#x not found", blockHash) + + } + + msg, vmctx, statedb, err := computeTxEnv(eth, block, int(index), reexec) + + if err != nil { + + return nil, err + + } + + + + taskExtraContext := map[string]interface{}{ + + "blockNumber": block.NumberU64(), + + "blockHash": blockHash.Hex(), + + "transactionHash": tx.Hash().Hex(), + + "transactionPosition": index, + + } + + + + // Trace the transaction and return + + return traceTx(ctx, eth, msg, vmctx, statedb, taskExtraContext, config) + +} + + + +// TraceTransaction returns the structured logs created during the execution of EVM + +// and returns them as a JSON object. + +func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { + + return traceTransaction(ctx, api.eth, hash, config) +++======= ++ block, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNumber), blockHash) ++ if err != nil { ++ return nil, err ++ } ++ msg, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec) ++ if err != nil { ++ return nil, err ++ } ++ defer release() ++ ++ return api.traceTx(ctx, msg, vmctx, statedb, config) +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + } + +- // TraceCall lets you trace a given eth_call. It collects the structured logs created during the execution of EVM +- // if the given transaction was added on top of the provided block and returns them as a JSON object. ++ // TraceCall lets you trace a given eth_call. It collects the structured logs ++ // created during the execution of EVM if the given transaction was added on ++ // top of the provided block and returns them as a JSON object. + // You can provide -2 as a block number to trace on top of the pending block. +- func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { +- // First try to retrieve the state +- statedb, header, err := api.eth.APIBackend.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) ++ func (api *API) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { ++ // Try to retrieve the specified block ++ var ( ++ err error ++ block *types.Block ++ ) ++ if hash, ok := blockNrOrHash.Hash(); ok { ++ block, err = api.blockByHash(ctx, hash) ++ } else if number, ok := blockNrOrHash.Number(); ok { ++ block, err = api.blockByNumber(ctx, number) ++ } + if err != nil { +- // Try to retrieve the specified block +- var block *types.Block +- if hash, ok := blockNrOrHash.Hash(); ok { +- block = api.eth.blockchain.GetBlockByHash(hash) +- } else if number, ok := blockNrOrHash.Number(); ok { +- block = api.eth.blockchain.GetBlockByNumber(uint64(number)) +- } +- if block == nil { +- return nil, fmt.Errorf("block %v not found: %v", blockNrOrHash, err) +- } +- // try to recompute the state +- reexec := defaultTraceReexec +- if config != nil && config.Reexec != nil { +- reexec = *config.Reexec +- } +- _, _, statedb, err = api.computeTxEnv(block, 0, reexec) +- if err != nil { +- return nil, err +- } ++ return nil, err + } ++ // try to recompute the state ++ reexec := defaultTraceReexec ++ if config != nil && config.Reexec != nil { ++ reexec = *config.Reexec ++ } ++ statedb, release, err := api.backend.StateAtBlock(ctx, block, reexec) ++ if err != nil { ++ return nil, err ++ } ++ defer release() + + // Execute the trace +- msg := args.ToMessage(api.eth.APIBackend.RPCGasCap()) +- vmctx := core.NewEVMContext(msg, header, api.eth.blockchain, nil) ++ msg := args.ToMessage(api.backend.RPCGasCap()) ++ vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + return api.traceTx(ctx, msg, vmctx, statedb, config) + } + + // traceTx configures a new tracer according to the provided configuration, and + // executes the given message in the provided environment. The return value will + // be tracer dependent. +++<<<<<<< HEAD:eth/api_tracer.go + +func traceTx(ctx context.Context, eth *Ethereum, message core.Message, vmctx vm.Context, statedb *state.StateDB, extraContext map[string]interface{}, config *TraceConfig) (interface{}, error) { +++======= ++ func (api *API) traceTx(ctx context.Context, message core.Message, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + // Assemble the structured logger or the JavaScript tracer + var ( +- tracer vm.Tracer +- err error ++ tracer vm.Tracer ++ err error ++ txContext = core.NewEVMTxContext(message) + ) + switch { + case config != nil && config.Tracer != nil: +@@@ -876,9 -753,9 +1009,14 @@@ + default: + tracer = vm.NewStructLogger(config.LogConfig) + } +++<<<<<<< HEAD:eth/api_tracer.go + + // Run the transaction with tracing enabled. + + vmenv := vm.NewEVM(vmctx, statedb, eth.blockchain.Config(), vm.Config{Debug: true, Tracer: tracer}) +++======= +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + ++ // Run the transaction with tracing enabled. ++ vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer}) + result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas())) + if err != nil { + return nil, fmt.Errorf("tracing failed: %v", err) +@@@ -906,51 -784,15 +1045,63 @@@ + } + } + +++<<<<<<< HEAD:eth/api_tracer.go + +// traceTx configures a new tracer according to the provided configuration, and + +// executes the given message in the provided environment. The return value will + +// be tracer dependent. + +func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, vmctx vm.Context, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { + + return traceTx(ctx, api.eth, message, vmctx, statedb, nil, config) + +} + + + +// computeTxEnv returns the execution environment of a certain transaction. + +func computeTxEnv(eth *Ethereum, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, error) { + + // Create the parent state database + + parent := eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) + + if parent == nil { + + return nil, vm.Context{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash()) + + } + + statedb, err := computeStateDB(eth, parent, reexec) + + if err != nil { + + return nil, vm.Context{}, nil, err + + } + + + + if txIndex == 0 && len(block.Transactions()) == 0 { + + return nil, vm.Context{}, statedb, nil + + } + + + + // Recompute transactions up to the target index. + + signer := types.MakeSigner(eth.blockchain.Config(), block.Number()) + + + + for idx, tx := range block.Transactions() { + + // Assemble the transaction call message and return if the requested offset + + msg, _ := tx.AsMessage(signer) + + context := core.NewEVMContext(msg, block.Header(), eth.blockchain, nil) + + if idx == txIndex { + + return msg, context, statedb, nil + + } + + // Not yet the searched for transaction, execute on top of the current state + + vmenv := vm.NewEVM(context, statedb, eth.blockchain.Config(), vm.Config{}) + + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { + + return nil, vm.Context{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) + + } + + // Ensure any modifications are committed to the state + + // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect + + statedb.Finalise(vmenv.ChainConfig().IsEnabled(vmenv.ChainConfig().GetEIP161dTransition, block.Number())) +++======= ++ // APIs return the collection of RPC services the tracer package offers. ++ func APIs(backend Backend) []rpc.API { ++ // Append all the local APIs and return ++ return []rpc.API{ ++ { ++ Namespace: "debug", ++ Version: "1.0", ++ Service: NewAPI(backend), ++ Public: false, ++ }, +++>>>>>>> c2d2f4ed8f:eth/tracers/api.go + } +- return nil, vm.Context{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) + } + + + +func (api *PrivateDebugAPI) computeTxEnv(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, error) { + + return computeTxEnv(api.eth, block, txIndex, reexec) + +} +diff --cc eth/tracers/internal/tracers/assets.go +index 5fd3cb7898,7f45ab286e..0000000000 +--- a/eth/tracers/internal/tracers/assets.go ++++ b/eth/tracers/internal/tracers/assets.go +@@@ -1,15 -1,15 +1,28 @@@ + -// Code generated by go-bindata. DO NOT EDIT. + +// Package tracers Code generated by go-bindata. (@generated) DO NOT EDIT. + // sources: +++<<<<<<< HEAD + +// 4byte_tracer.js + +// bigram_tracer.js + +// call_tracer.js + +// call_tracer_parity.js + +// evmdis_tracer.js + +// noop_tracer.js + +// opcount_tracer.js + +// prestate_tracer.js + +// trigram_tracer.js + +// unigram_tracer.js +++======= ++ // 4byte_tracer.js (2.933kB) ++ // bigram_tracer.js (1.712kB) ++ // call_tracer.js (8.956kB) ++ // evmdis_tracer.js (4.195kB) ++ // noop_tracer.js (1.271kB) ++ // opcount_tracer.js (1.372kB) ++ // prestate_tracer.js (4.287kB) ++ // trigram_tracer.js (1.788kB) ++ // unigram_tracer.js (1.469kB) ++ +++>>>>>>> c2d2f4ed8f + package tracers + + import ( +@@@ -226,7 -197,7 +239,11 @@@ func opcount_tracerJs() (*asset, error + return a, nil + } + +++<<<<<<< HEAD + +var _prestate_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x57\xdd\x6f\xdb\x38\x12\x7f\x96\xfe\x8a\x41\x5f\x6c\xa3\xae\xdc\x64\x81\x3d\xc0\xb9\x1c\xa0\xba\x6e\x1b\x20\x9b\x04\xb6\x7b\xb9\xdc\x62\x1f\x28\x72\x24\x73\x4d\x93\x02\x49\xd9\xf1\x15\xf9\xdf\x0f\x43\x7d\xf8\xa3\x49\xd3\xdd\x37\x9b\x1c\xfe\xe6\xfb\x37\xa3\xd1\x08\x26\xa6\xdc\x59\x59\x2c\x3d\x9c\xbf\x3f\xfb\x07\x2c\x96\x08\x85\x79\x87\x7e\x89\x16\xab\x35\xa4\x95\x5f\x1a\xeb\xe2\xd1\x08\x16\x4b\xe9\x20\x97\x0a\x41\x3a\x28\x99\xf5\x60\x72\xf0\x27\xf2\x4a\x66\x96\xd9\x5d\x12\x8f\x46\xf5\x9b\x67\xaf\x09\x21\xb7\x88\xe0\x4c\xee\xb7\xcc\xe2\x18\x76\xa6\x02\xce\x34\x58\x14\xd2\x79\x2b\xb3\xca\x23\x48\x0f\x4c\x8b\x91\xb1\xb0\x36\x42\xe6\x3b\x82\x94\x1e\x2a\x2d\xd0\x06\xd5\x1e\xed\xda\xb5\x76\x7c\xbe\xf9\x0a\xd7\xe8\x1c\x5a\xf8\x8c\x1a\x2d\x53\x70\x57\x65\x4a\x72\xb8\x96\x1c\xb5\x43\x60\x0e\x4a\x3a\x71\x4b\x14\x90\x05\x38\x7a\xf8\x89\x4c\x99\x37\xa6\xc0\x27\x53\x69\xc1\xbc\x34\x7a\x08\x28\xc9\x72\xd8\xa0\x75\xd2\x68\xf8\xa5\x55\xd5\x00\x0e\xc1\x58\x02\xe9\x33\x4f\x0e\x58\x30\x25\xbd\x1b\x00\xd3\x3b\x50\xcc\xef\x9f\xfe\x44\x40\xf6\x7e\x0b\x90\x3a\xa8\x59\x9a\x12\xc1\x2f\x99\x27\xaf\xb7\x52\x29\xc8\x10\x2a\x87\x79\xa5\x86\x84\x96\x55\x1e\xee\xaf\x16\x5f\x6e\xbf\x2e\x20\xbd\x79\x80\xfb\x74\x36\x4b\x6f\x16\x0f\x17\xb0\x95\x7e\x69\x2a\x0f\xb8\xc1\x1a\x4a\xae\x4b\x25\x51\xc0\x96\x59\xcb\xb4\xdf\x81\xc9\x09\xe1\xb7\xe9\x6c\xf2\x25\xbd\x59\xa4\x1f\xae\xae\xaf\x16\x0f\x60\x2c\x7c\xba\x5a\xdc\x4c\xe7\x73\xf8\x74\x3b\x83\x14\xee\xd2\xd9\xe2\x6a\xf2\xf5\x3a\x9d\xc1\xdd\xd7\xd9\xdd\xed\x7c\x9a\xc0\x1c\xc9\x2a\xa4\xf7\xaf\xc7\x3c\x0f\xd9\xb3\x08\x02\x3d\x93\xca\xb5\x91\x78\x30\x15\xb8\xa5\xa9\x94\x80\x25\xdb\x20\x58\xe4\x28\x37\x28\x80\x01\x37\xe5\xee\xa7\x93\x4a\x58\x4c\x19\x5d\x04\x9f\x5f\x2c\x48\xb8\xca\x41\x1b\x3f\x04\x87\x08\xff\x5c\x7a\x5f\x8e\x47\xa3\xed\x76\x9b\x14\xba\x4a\x8c\x2d\x46\xaa\x86\x73\xa3\x7f\x25\x31\x61\x96\x16\x9d\x67\x1e\x17\x96\x71\xb4\x60\x2a\x5f\x56\xde\x81\xab\xf2\x5c\x72\x89\xda\x83\xd4\xb9\xb1\xeb\x50\x29\xe0\x0d\x70\x8b\xcc\x23\x30\x50\x86\x33\x05\xf8\x88\xbc\x0a\x77\x75\xa4\x43\xb9\x5a\xa6\x1d\xe3\xe1\x34\xb7\x66\x4d\xbe\x56\xce\xd3\x0f\xe7\x70\x9d\x29\x14\x50\xa0\x46\x27\xdd\x02\x32\x65\xf8\x2a\x89\xbf\xc5\xd1\x81\x35\x54\x28\xc1\xc5\x56\x2a\x54\xc7\x16\x7b\x16\x21\xab\xa4\x12\x52\x17\x49\x1c\xb5\xe2\x63\xd0\x95\x52\xc3\x38\x60\x28\x63\x56\x55\x99\x72\x6e\xaa\x60\xfd\x9f\xc8\x7d\x8d\xe6\x4a\xe4\x32\xa7\xf2\x60\xdd\xad\x37\xe1\xaa\x53\x6c\x32\x92\x4f\xe2\xe8\x08\x66\x0c\x79\xa5\x83\x43\x7d\x26\x84\x1d\x82\xc8\x06\xdf\xe2\x28\xda\x30\x4b\x58\x70\x09\xde\x7c\xc1\xc7\x70\x39\xb8\x88\xa3\x48\xe6\xd0\xf7\x4b\xe9\x92\x16\xf8\x77\xc6\xf9\x1f\x70\x79\x79\x19\xda\x3a\x97\x1a\xc5\x00\x08\x22\x7a\x4e\xac\xbe\x89\x32\xa6\x98\xe6\x38\x86\xde\xfb\xc7\x1e\xbc\x05\x91\x25\x05\xfa\x0f\xf5\x69\xad\x2c\xf1\x66\xee\xad\xd4\x45\xff\xec\xd7\xc1\x30\xbc\xd2\x26\xbc\x81\x46\xfc\xc6\x74\xc2\xf5\x3d\x37\x22\x5c\x37\x36\xd7\x52\x13\x23\x1a\xa1\x46\xca\x79\x63\x59\x81\x63\xf8\xf6\x44\xff\x9f\xc8\xab\xa7\x38\x7a\x3a\x8a\xf2\xbc\x16\x7a\x21\xca\x0d\x04\xa0\xf6\xb6\xab\xf4\x42\x52\xaf\x1e\x26\x20\xe0\xfd\x28\x09\xf3\xd6\x94\x93\x24\xac\x70\xf7\x7a\x26\xe8\x42\x8a\xc7\xee\x62\x85\xbb\xc1\x45\xfc\x62\x8a\x92\xc6\xe8\xdf\xa5\x78\xfc\xd9\x7c\x9d\xbc\x39\x8a\xeb\x9c\xa4\xf6\xf6\x0e\x06\x27\x71\xb4\xe8\x2a\xe5\xa9\xde\xa5\xde\x98\x15\x51\xd7\x92\xe2\xa3\x54\x08\x89\x29\x29\x5b\xae\xe6\x8e\x0c\x51\x83\xf4\x68\x19\x91\xa7\xd9\xa0\xa5\xb9\x01\x16\x7d\x65\xb5\xeb\xc2\x98\x4b\xcd\x54\x0b\xdc\x44\xdd\x5b\xc6\xeb\x9e\xa9\xcf\x0f\x62\xc9\xfd\x63\x88\x62\xf0\x6e\x34\x82\xd4\x03\xb9\x08\xa5\x91\xda\x0f\x61\x8b\xa0\x11\x05\xb5\xbe\x40\x51\x71\x1f\xf0\x7a\x1b\xa6\x2a\xec\xd5\xed\x4d\x24\x19\x9e\x9a\x8a\x66\xc1\x41\xfb\x0f\x83\x81\x6b\xb3\x09\x43\x2e\x63\x7c\x05\x4d\xc3\x19\x2b\x0b\xa9\xe3\x26\x9c\x47\xcd\x46\x16\x25\x04\x1c\xcc\x0a\xb9\xa2\x24\xd2\xc9\x07\xa6\xe0\x12\x32\x59\x5c\x69\x7f\x92\xbc\x3a\xe8\xed\xd3\xc1\x1f\x49\xd3\x3c\x89\x23\xca\xeb\x9f\x0f\x86\x70\xf6\x6b\x57\x11\xde\x10\x14\xbc\x0e\xe6\xcd\xcb\x50\xf1\x69\x31\x3c\xff\x2c\xa8\xa1\x0e\x7e\x1b\xb4\x26\xae\xca\x28\x1d\xb5\x9f\x21\x8e\xc7\x5d\x7c\xf1\x03\xdc\x63\xdf\x5a\xdc\x26\x34\x09\x13\xe2\x65\xd0\x3a\x45\x1f\x91\x5b\x5c\x13\xaf\x53\x16\x38\x53\x0a\x6d\xcf\x41\xe0\x8c\x61\x53\x4e\x21\x5f\xb8\x2e\xfd\xae\x65\x7b\xcf\x6c\x81\xde\xbd\x6e\x58\xc0\x79\xf7\xae\xa5\xc0\x10\x8a\x5d\x89\x70\x79\x09\xbd\xc9\x6c\x9a\x2e\xa6\xbd\xa6\x8d\x46\x23\xb8\xc7\xb0\x0b\x65\x4a\x66\x42\xed\x40\xa0\x42\x8f\xb5\x5d\x46\x87\x10\x75\x94\x30\xa4\xa5\x86\xd6\x0d\x7c\x94\xce\x4b\x5d\x40\xcd\x14\x5b\x9a\xac\x0d\x5c\xe8\x11\xce\x2a\x47\xd5\x7a\x32\x86\xbc\xa1\x9d\xc2\x22\xf1\x0a\xf1\x7f\x68\x37\xa6\x64\xb7\x83\xe4\xd2\x3a\x0f\xa5\x62\x1c\x13\xc2\xeb\x8c\x79\x39\xbf\x4d\x27\x93\xea\x59\x68\xc1\x00\xb4\x1f\x71\x4c\xd1\x88\x24\xf5\x0e\xfa\x2d\xc6\x20\x8e\x22\xdb\x4a\x1f\x60\x5f\xec\x29\xc1\x79\x2c\x0f\x09\x81\x56\x0b\xdc\x20\x51\x68\x60\x83\x7a\x18\x92\xae\x7f\xff\xd6\xcc\x5f\x74\x49\x1c\xd1\xbb\x83\xbe\x56\xa6\x38\xee\x6b\x51\x87\x85\x57\xd6\x52\xfe\x3b\x0a\xce\xa9\xc7\xff\xac\x9c\xa7\x98\x5a\x0a\x4f\xc3\x16\xcf\x91\x64\xa0\x44\x9a\xb6\x83\xef\xc9\x90\xe6\x56\x98\x13\xa4\xae\x99\x52\xf5\x3e\x57\x1a\x8f\xda\x4b\xa6\xd4\x8e\xf2\xb0\xb5\xb4\xc8\xd0\xea\x32\x04\x27\x49\x2a\x30\x4e\x10\x95\x9a\xab\x4a\xd4\x65\x10\xea\xb8\xc1\x73\xc1\xe6\xe3\x0d\x68\x8d\xce\xb1\x02\x13\xaa\xa4\x5c\x3e\x36\x3b\xa4\x86\x5e\x4d\x72\xfd\x41\x2f\xe9\x8c\x3c\xa6\x18\x65\x8a\xa4\x2d\x32\xa2\xe9\x54\x08\x8b\xce\xf5\x07\x0d\xe7\x74\x99\xbd\x5f\xa2\xa6\xe0\x83\xc6\x2d\x74\xbb\x09\xe3\x9c\x96\x35\x31\x04\x26\x04\x51\xdb\xc9\x1a\x11\x47\x91\xdb\x4a\xcf\x97\x10\x34\x99\x72\xdf\x8b\x83\xa6\xfe\x39\x73\x08\x6f\xa6\xff\x59\x4c\x6e\x3f\x4e\x27\xb7\x77\x0f\x6f\xc6\x70\x74\x36\xbf\xfa\xef\xb4\x3b\xfb\x90\x5e\xa7\x37\x93\xe9\x9b\x71\x98\xcd\xcf\x38\xe4\x4d\xeb\x02\x29\x74\x9e\xf1\x55\x52\x22\xae\xfa\xef\x8f\x79\x60\xef\x60\x14\x65\x16\xd9\xea\x62\x6f\x4c\xdd\xa0\x8d\x8e\x96\x72\xe1\x12\x5e\x0c\xd6\xc5\xcb\xd6\x4c\x1a\xf9\x7e\x4b\xe4\xfb\x55\x24\x50\xc5\xeb\x76\x9c\xff\x65\x43\x42\xef\x30\xbe\x1a\x83\x63\x8a\x76\x60\xf9\x3f\xfa\x76\xc9\x73\x87\x7e\x08\xa8\x85\xd9\x12\xf3\x75\xa8\xf5\x4d\x83\x7b\x10\xb2\xb3\x41\xcd\xa0\xb7\x79\x7f\xd0\x09\x13\xd8\xf7\xa2\xe7\xcf\x89\xa2\x16\x70\xd9\xa2\xbf\x0d\x2f\x5f\x0f\xd4\x79\x13\xa9\x13\x05\xbf\x9c\x6c\x78\xe1\x7e\x8d\x6b\x63\x77\xcd\x38\x3a\xf0\xef\xc7\x51\x4d\xaf\xaf\xbb\x7a\xa2\x3f\x54\x64\xdd\xc1\xc7\xe9\xf5\xf4\x73\xba\x98\x1e\x49\xcd\x17\xe9\xe2\x6a\x52\x1f\xfd\xe5\xc2\x3b\xfb\xe9\xc2\xeb\xcd\xe7\x8b\xdb\xd9\xb4\x37\x6e\xfe\x5d\xdf\xa6\x1f\x7b\xdf\x29\x6c\xb6\xc0\x1f\xb5\xae\x37\xf7\xc6\x8a\xbf\xd3\x01\x07\x1b\x59\xce\x9e\x5b\xc8\x02\xb5\x73\x5f\x9d\x7c\xf2\x00\xd3\x2d\x2b\xe7\xf5\x67\x5f\x14\xde\x3f\xcb\xc3\x4f\xf1\x53\xfc\xff\x00\x00\x00\xff\xff\x06\x73\xb8\x1e\x8c\x10\x00\x00") +++======= ++ var _prestate_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x57\xdd\x6f\xdb\x38\x12\x7f\xb6\xfe\x8a\x41\x5f\x6c\x5d\x5d\xb9\xcd\x02\x7b\x80\x73\x39\x40\x75\xdd\x36\x40\x36\x09\x6c\xe7\x72\xb9\xc5\x3e\x50\xe4\x48\xe6\x9a\x26\x05\x92\xb2\xe3\x2b\xf2\xbf\x1f\x86\xfa\xf0\x47\x93\xa6\x7b\x6f\x16\x39\xfc\xcd\xf7\x6f\xc6\xa3\x11\x4c\x4c\xb9\xb3\xb2\x58\x7a\x38\x7b\xff\xe1\xef\xb0\x58\x22\x14\xe6\x1d\xfa\x25\x5a\xac\xd6\x90\x56\x7e\x69\xac\x8b\x46\x23\x58\x2c\xa5\x83\x5c\x2a\x04\xe9\xa0\x64\xd6\x83\xc9\xc1\x9f\xc8\x2b\x99\x59\x66\x77\x49\x34\x1a\xd5\x6f\x9e\xbd\x26\x84\xdc\x22\x82\x33\xb9\xdf\x32\x8b\x63\xd8\x99\x0a\x38\xd3\x60\x51\x48\xe7\xad\xcc\x2a\x8f\x20\x3d\x30\x2d\x46\xc6\xc2\xda\x08\x99\xef\x08\x52\x7a\xa8\xb4\x40\x1b\x54\x7b\xb4\x6b\xd7\xda\xf1\xe5\xfa\x0e\xae\xd0\x39\xb4\xf0\x05\x35\x5a\xa6\xe0\xb6\xca\x94\xe4\x70\x25\x39\x6a\x87\xc0\x1c\x94\x74\xe2\x96\x28\x20\x0b\x70\xf4\xf0\x33\x99\x32\x6f\x4c\x81\xcf\xa6\xd2\x82\x79\x69\xf4\x10\x50\x92\xe5\xb0\x41\xeb\xa4\xd1\xf0\x4b\xab\xaa\x01\x1c\x82\xb1\x04\x32\x60\x9e\x1c\xb0\x60\x4a\x7a\x17\x03\xd3\x3b\x50\xcc\xef\x9f\xfe\x44\x40\xf6\x7e\x0b\x90\x3a\xa8\x59\x9a\x12\xc1\x2f\x99\x27\xaf\xb7\x52\x29\xc8\x10\x2a\x87\x79\xa5\x86\x84\x96\x55\x1e\xee\x2f\x17\x5f\x6f\xee\x16\x90\x5e\x3f\xc0\x7d\x3a\x9b\xa5\xd7\x8b\x87\x73\xd8\x4a\xbf\x34\x95\x07\xdc\x60\x0d\x25\xd7\xa5\x92\x28\x60\xcb\xac\x65\xda\xef\xc0\xe4\x84\xf0\xdb\x74\x36\xf9\x9a\x5e\x2f\xd2\x8f\x97\x57\x97\x8b\x07\x30\x16\x3e\x5f\x2e\xae\xa7\xf3\x39\x7c\xbe\x99\x41\x0a\xb7\xe9\x6c\x71\x39\xb9\xbb\x4a\x67\x70\x7b\x37\xbb\xbd\x99\x4f\x13\x98\x23\x59\x85\xf4\xfe\xf5\x98\xe7\x21\x7b\x16\x41\xa0\x67\x52\xb9\x36\x12\x0f\xa6\x02\xb7\x34\x95\x12\xb0\x64\x1b\x04\x8b\x1c\xe5\x06\x05\x30\xe0\xa6\xdc\xfd\x74\x52\x09\x8b\x29\xa3\x8b\xe0\xf3\x8b\x05\x09\x97\x39\x68\xe3\x87\xe0\x10\xe1\x1f\x4b\xef\xcb\xf1\x68\xb4\xdd\x6e\x93\x42\x57\x89\xb1\xc5\x48\xd5\x70\x6e\xf4\xcf\x24\x22\xcc\xd2\xa2\xf3\xcc\xe3\xc2\x32\x8e\x16\x4c\xe5\xcb\xca\x3b\x70\x55\x9e\x4b\x2e\x51\x7b\x90\x3a\x37\x76\x1d\x2a\x05\xbc\x01\x6e\x91\x79\x04\x06\xca\x70\xa6\x00\x1f\x91\x57\xe1\xae\x8e\x74\x28\x57\xcb\xb4\x63\x3c\x9c\xe6\xd6\xac\xc9\xd7\xca\x79\xfa\xe1\x1c\xae\x33\x85\x02\x0a\xd4\xe8\xa4\x83\x4c\x19\xbe\x4a\xa2\x6f\x51\xef\xc0\x18\xaa\x93\xe0\x61\x23\x14\x6a\x63\x8b\x7d\x8b\x90\x55\x52\x09\xa9\x8b\x24\xea\xb5\xd2\x63\xd0\x95\x52\xc3\x28\x40\x28\x63\x56\x55\x99\x72\x6e\xaa\x60\xfb\x9f\xc8\x7d\x0d\xe6\x4a\xe4\x32\xa7\xe2\x60\xdd\xad\x37\xe1\xaa\xd3\x6b\x32\x92\x4f\xa2\xde\x11\xcc\x18\xf2\x4a\x07\x77\x06\x4c\x08\x3b\x04\x91\xc5\xdf\xa2\x5e\x6f\xc3\x2c\x61\xc1\x05\x78\xf3\x15\x1f\xc3\x65\x7c\x1e\xf5\x7a\x32\x87\x81\x5f\x4a\x97\xb4\xc0\xbf\x33\xce\xff\x80\x8b\x8b\x8b\xd0\xd4\xb9\xd4\x28\x62\x20\x88\xde\x73\x62\xf5\x4d\x2f\x63\x8a\x69\x8e\x63\xe8\xbf\x7f\xec\xc3\x5b\x10\x59\x52\xa0\xff\x58\x9f\xd6\xca\x12\x6f\xe6\xde\x4a\x5d\x0c\x3e\xfc\x1a\x0f\xc3\x2b\x6d\xc2\x1b\x68\xc4\xaf\x4d\x27\x5c\xdf\x73\x23\xc2\x75\x63\x73\x2d\x35\x31\xa2\x11\x6a\xa4\x9c\x37\x96\x15\x38\x86\x6f\x4f\xf4\xfd\x44\x5e\x3d\x45\xbd\xa7\xa3\x28\xcf\x6b\xa1\x17\xa2\xdc\x40\x00\x6a\x6f\xbb\x3a\x2f\x24\x75\xea\x61\x02\x02\xde\x8f\x92\x30\x6f\x4d\x39\x49\xc2\x0a\x77\xaf\x67\x82\x2e\xa4\x78\xec\x2e\x56\xb8\x8b\xcf\xa3\x17\x53\x94\x34\x46\xff\x2e\xc5\xe3\xcf\xe6\xeb\xe4\xcd\x51\x5c\xe7\x24\xb5\xb7\x37\x8e\x4f\xe2\x68\xd1\x55\xca\x53\xb9\x4b\xbd\x31\x2b\x22\xae\x25\xc5\x47\xa9\x10\x12\x53\x52\xb6\x5c\xcd\x1c\x19\xa2\x06\xe9\xd1\x32\xa2\x4e\xb3\x41\x4b\x53\x03\x2c\xfa\xca\x6a\xd7\x85\x31\x97\x9a\xa9\x16\xb8\x89\xba\xb7\x8c\xd7\x3d\x53\x9f\x1f\xc4\x92\xfb\xc7\x10\xc5\xe0\xdd\x68\x04\xa9\x07\x72\x11\x4a\x23\xb5\x1f\xc2\x16\x41\x23\x0a\x6a\x7c\x81\xa2\xe2\x3e\xe0\xf5\x37\x4c\x55\xd8\xaf\x9b\x9b\x28\x32\x3c\x35\x15\x4d\x82\x83\xe6\x1f\x06\x03\xd7\x66\x13\x46\x5c\xc6\xf8\x0a\x9a\x86\x33\x56\x16\x52\x47\x4d\x38\x8f\x9a\x8d\x2c\x4a\x08\x38\x98\x15\x72\x45\x49\xa4\x93\x8f\x4c\xc1\x05\x64\xb2\xb8\xd4\xfe\x24\x79\x75\xd0\xdb\xa7\xf1\x1f\x49\xd3\x3c\x89\x23\xc2\x1b\x9c\xc5\x43\xf8\xf0\x6b\x57\x11\xde\x10\x14\xbc\x0e\xe6\xcd\xcb\x50\xd1\x69\x31\x3c\xff\x2c\xa8\xa1\x0e\x7e\x1b\xb4\x26\xae\xca\x28\x1d\xb5\x9f\x21\x8e\xc7\x5d\x7c\xfe\x03\xdc\x63\xdf\x5a\xdc\x26\x34\x09\x13\xe2\x10\x94\x3e\xc3\x77\xc1\xdc\x9d\x43\x01\x6f\x81\xbe\xa4\x26\x55\x4e\xf2\x2f\xcc\xc5\xf0\x37\x68\x24\x6e\xad\xe4\xdf\x59\x52\xe7\xf5\x13\x72\x8b\x6b\x1a\x05\x94\x3a\xce\x94\x42\xdb\x77\x10\x88\x66\xd8\xd4\x60\x48\x32\xae\x4b\xbf\x6b\x07\x84\x67\xb6\x40\xef\x5e\xf7\x26\xe0\xbc\x7b\xd7\xf2\x66\x88\xdf\xae\x44\xb8\xb8\x80\xfe\x64\x36\x4d\x17\xd3\x7e\xd3\x7b\xa3\x11\xdc\x63\x58\x9f\x32\x25\x33\xa1\x76\x20\x50\xa1\xc7\xda\x2e\xa3\x43\x5c\x3b\x1e\x19\xd2\x1e\x44\x1b\x0a\x3e\x4a\xe7\xa5\x2e\xa0\xa6\x97\x2d\x0d\xe3\x06\x2e\x34\x16\x67\x15\x85\xe7\x74\x72\x79\x43\x6b\x88\x45\x22\x23\x1a\x1a\xa1\x47\x99\x92\xdd\xda\x92\x4b\xeb\x3c\x94\x8a\x71\x4c\x08\xaf\x33\xe6\xe5\xa2\x68\xda\x9f\x54\xcf\x42\xdf\x06\xa0\xfd\x54\x64\x8a\xa6\x2a\xa9\x77\x30\x68\x31\xe2\xa8\xd7\xb3\xad\xf4\x01\xf6\xf9\x9e\x47\x9c\xc7\xf2\x90\x45\x68\x1b\xc1\x0d\x12\xef\x06\x0a\xa9\x27\x28\xe9\xfa\xd7\x6f\xcd\xc8\x46\x97\x44\x3d\x7a\x77\x40\x06\xca\x14\xc7\x64\x20\xea\xb0\xf0\xca\x5a\xca\x7f\xc7\xdb\x39\x11\xc3\x9f\x95\xf3\x14\x53\x4b\xe1\x69\x28\xe6\x39\x66\x0d\x3c\x4a\x23\x3a\xfe\x9e\x41\x69\xd8\x85\xe1\x42\xea\x9a\xd1\x56\xaf\x80\xa5\xf1\xa8\xbd\x64\x4a\xed\x28\x0f\x5b\x4b\xbb\x0f\x6d\x3b\x43\x70\x92\xa4\x02\x4d\x05\x51\xa9\xb9\xaa\x44\x5d\x06\xa1\xf8\x1b\x3c\x17\x6c\x3e\x5e\x9a\xd6\xe8\x1c\x2b\x30\xa1\x4a\xca\xe5\x63\xb3\x76\x6a\xe8\xd7\xcc\x38\x88\xfb\x49\x67\xe4\x31\x2f\x29\x53\x24\x6d\x91\x11\xb7\xa7\x42\x58\x74\x6e\x10\x37\x44\xd5\x65\xf6\x7e\x89\x9a\x82\x0f\x1a\xb7\xd0\xed\x33\x8c\x73\xda\xef\xc4\x10\x98\x10\xc4\x87\x27\xbb\x47\xd4\xeb\xb9\xad\xf4\x7c\x09\x41\x93\x29\xf7\xbd\x18\x37\xf5\xcf\x99\x43\x78\x33\xfd\xf7\x62\x72\xf3\x69\x3a\xb9\xb9\x7d\x78\x33\x86\xa3\xb3\xf9\xe5\x7f\xa6\xdd\xd9\xc7\xf4\x2a\xbd\x9e\x4c\xdf\x8c\xc3\x40\x7f\xc6\x21\x6f\x5a\x17\x48\xa1\xf3\x8c\xaf\x92\x12\x71\x35\x78\x7f\xcc\x03\x7b\x07\x7b\xbd\xcc\x22\x5b\x9d\xef\x8d\xa9\x1b\xb4\xd1\xd1\xf2\x34\x5c\xc0\x8b\xc1\x3a\x7f\xd9\x9a\x49\x23\x3f\x68\xd9\x7f\xbf\xbf\x04\xaa\x78\xdd\x8e\xb3\xbf\x6c\x48\xe8\x1d\xc6\x57\x63\x70\x4c\xd1\xda\x2c\xff\x4b\x7f\x77\xf2\xdc\xa1\x1f\x02\x6a\x61\xb6\xc4\x7c\x1d\x6a\x7d\xd3\xe0\x1e\x84\xec\x43\x5c\xd3\xee\x4d\x3e\x88\x3b\x61\x02\xfb\x5e\xf4\xec\x39\x51\xd4\x02\x2e\x5a\xf4\xb7\xe1\xe5\xeb\x81\x3a\x6b\x22\x75\xa2\xe0\x97\x93\xb5\x30\xdc\xaf\x71\x6d\xec\xae\x99\x61\x07\xfe\xfd\x38\xaa\xe9\xd5\x55\x57\x4f\xf4\x41\x45\xd6\x1d\x7c\x9a\x5e\x4d\xbf\xa4\x8b\xe9\x91\xd4\x7c\x91\x2e\x2e\x27\xf5\xd1\x5f\x2e\xbc\x0f\x3f\x5d\x78\xfd\xf9\x7c\x71\x33\x9b\xf6\xc7\xcd\xd7\xd5\x4d\xfa\xa9\xff\x9d\xc2\x66\x75\xfc\x51\xeb\x7a\x73\x6f\xac\xf8\x7f\x3a\xe0\x60\x8d\xcb\xd9\x73\x5b\x5c\xa0\x76\xee\xab\x93\x7f\x49\xc0\x74\xcb\xca\x79\xfd\x4f\xb1\x17\xde\x3f\xcb\xc3\x4f\xd1\x53\xf4\xbf\x00\x00\x00\xff\xff\x3a\xb7\x37\x41\xbf\x10\x00\x00") +++>>>>>>> c2d2f4ed8f + + func prestate_tracerJsBytes() ([]byte, error) { + return bindataRead( +@@@ -242,7 -213,7 +259,11 @@@ func prestate_tracerJs() (*asset, error + } + + info := bindataFileInfo{name: "prestate_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} +++<<<<<<< HEAD + + a := &asset{bytes: bytes, info: info} +++======= ++ a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd4, 0x9, 0xf9, 0x44, 0x13, 0x31, 0x89, 0xf7, 0x35, 0x9a, 0xc6, 0xf0, 0x86, 0x9d, 0xb2, 0xe3, 0x57, 0xe2, 0xc0, 0xde, 0xc9, 0x3a, 0x4c, 0x4a, 0x94, 0x90, 0xa5, 0x92, 0x2f, 0xbf, 0xc0, 0xb8}} +++>>>>>>> c2d2f4ed8f + return a, nil + } + +@@@ -282,7 -253,7 +303,11 @@@ func unigram_tracerJs() (*asset, error + } + + info := bindataFileInfo{name: "unigram_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} +++<<<<<<< HEAD + + a := &asset{bytes: bytes, info: info} +++======= ++ a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc, 0xe6, 0x5c, 0x88, 0x18, 0xa7, 0x85, 0x61, 0x18, 0xc6, 0xec, 0x17, 0xfc, 0xdf, 0x9d, 0xc0, 0x1b, 0x49, 0xf8, 0x8d, 0xf1, 0xeb, 0x35, 0xf3, 0xd, 0x3e, 0xf6, 0xa3, 0xac, 0x8c, 0xba, 0x74}} +++>>>>>>> c2d2f4ed8f + return a, nil + } + +@@@ -338,17 -348,19 +363,32 @@@ func AssetNames() []string + + // _bindata is a table, holding each asset generator, mapped to its name. + var _bindata = map[string]func() (*asset, error){ +++<<<<<<< HEAD + + "4byte_tracer.js": _4byte_tracerJs, + + "bigram_tracer.js": bigram_tracerJs, + + "call_tracer.js": call_tracerJs, + + "call_tracer_parity.js": call_tracer_parityJs, + + "evmdis_tracer.js": evmdis_tracerJs, + + "noop_tracer.js": noop_tracerJs, + + "opcount_tracer.js": opcount_tracerJs, + + "prestate_tracer.js": prestate_tracerJs, + + "trigram_tracer.js": trigram_tracerJs, + + "unigram_tracer.js": unigram_tracerJs, +- } +++======= ++ "4byte_tracer.js": _4byte_tracerJs, ++ "bigram_tracer.js": bigram_tracerJs, ++ "call_tracer.js": call_tracerJs, ++ "evmdis_tracer.js": evmdis_tracerJs, ++ "noop_tracer.js": noop_tracerJs, ++ "opcount_tracer.js": opcount_tracerJs, ++ "prestate_tracer.js": prestate_tracerJs, ++ "trigram_tracer.js": trigram_tracerJs, ++ "unigram_tracer.js": unigram_tracerJs, +++>>>>>>> c2d2f4ed8f ++ } ++ ++ // AssetDebug is true if the assets were built with the debug flag enabled. ++ const AssetDebug = false + + // AssetDir returns the file names below a certain + // directory embedded in the file by go-bindata. +diff --cc eth/tracers/tracer.go +index 9a9a3bcec5,4c398edf34..0000000000 +--- a/eth/tracers/tracer.go ++++ b/eth/tracers/tracer.go +@@@ -30,8 -31,7 +31,12 @@@ import + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" +++<<<<<<< HEAD + + "github.com/ethereum/go-ethereum/params" + + duktape "gopkg.in/olebedev/go-duktape.v3" +++======= ++ "gopkg.in/olebedev/go-duktape.v3" +++>>>>>>> c2d2f4ed8f + ) + + // bigIntegerJS is the minified version of https://github.com/peterolson/BigInteger.js. +@@@ -330,22 -323,21 +335,24 @@@ func New(code string, txCtx vm.TxContex + code = tracer + } + tracer := &Tracer{ + - vm: duktape.New(), + - ctx: make(map[string]interface{}), + - opWrapper: new(opWrapper), + - stackWrapper: new(stackWrapper), + - memoryWrapper: new(memoryWrapper), + - contractWrapper: new(contractWrapper), + - dbWrapper: new(dbWrapper), + - pcValue: new(uint), + - gasValue: new(uint), + - costValue: new(uint), + - depthValue: new(uint), + - refundValue: new(uint), + + vm: duktape.New(), + + ctx: make(map[string]interface{}), + + opWrapper: new(opWrapper), + + stackWrapper: new(stackWrapper), + + memoryWrapper: new(memoryWrapper), + + contractWrapper: new(contractWrapper), + + dbWrapper: new(dbWrapper), + + pcValue: new(uint), + + gasValue: new(uint), + + availableGasValue: new(uint), + + costValue: new(uint), + + depthValue: new(uint), + + returnData: new([]byte), + + refundValue: new(uint), + + callTracerCallstackLength: new(uint), + } ++ tracer.ctx["gasPrice"] = txCtx.GasPrice ++ + // Set up builtins for this environment + tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int { + ctx.PushString(hexutil.Encode(popSlice(ctx))) +@@@ -576,15 -543,8 +583,15 @@@ func (jst *Tracer) CaptureStart(from co + return nil + } + + +func (jst *Tracer) CaptureExtraContext(inputs map[string]interface{}) error { + + for key, val := range inputs { + + jst.ctx[key] = val + + } + + return nil + +} + + + // CaptureState implements the Tracer interface to trace a single step of VM execution. +- func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rdata []byte, contract *vm.Contract, depth int, err error) error { ++ func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rdata []byte, contract *vm.Contract, depth int, err error) error { + if jst.err == nil { + // Initialize the context if it wasn't done yet + if !jst.inited { +@@@ -671,12 -595,8 +690,12 @@@ + + // CaptureFault implements the Tracer interface to trace an execution fault + // while running an opcode. +- func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error { ++ func (jst *Tracer) CaptureFault(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 { + + // clean up op error when CaptureFault is being called directly + + jst.opErrorValue = nil + + env.CallErrorTemp = nil // clean temp error storage, for debug tracing + + + // Apart from the error, everything matches the previous invocation + jst.errorValue = new(string) + *jst.errorValue = err.Error() +diff --cc ethclient/ethclient_test.go +index 0e887d9878,9a5a45e34f..0000000000 +--- a/ethclient/ethclient_test.go ++++ b/ethclient/ethclient_test.go +@@@ -36,10 -34,10 +37,15 @@@ import + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" ++ "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" +++<<<<<<< HEAD + + "github.com/ethereum/go-ethereum/params/types/genesisT" + + meta_schema "github.com/open-rpc/meta-schema" +++======= ++ "github.com/ethereum/go-ethereum/rpc" +++>>>>>>> c2d2f4ed8f + ) + + // Verify that Client implements the ethereum interfaces. +@@@ -278,49 -312,7 +320,53 @@@ func testHeader(t *testing.T, chain []* + } + } + +++<<<<<<< HEAD + +func TestHeader_TxesUnclesNotEmpty(t *testing.T) { + + backend, _ := newTestBackend(t) + + client, _ := backend.Attach() + + defer backend.Close() + + defer client.Close() + + + + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + + defer cancel() + + + + res := make(map[string]interface{}) + + err := client.CallContext(ctx, &res, "eth_getBlockByNumber", "latest", false) + + if err != nil { + + log.Fatalln(err) + + } + + + + // Sanity check response + + if v, ok := res["number"]; !ok { + + t.Fatal("missing 'number' field") + + } else if n, err := hexutil.DecodeBig(v.(string)); err != nil || n == nil { + + t.Fatal(err) + + } else if n.Cmp(big.NewInt(1)) != 0 { + + t.Fatalf("unexpected 'latest' block number: %v", n) + + } + + // 'transactions' key should exist as [] + + if v, ok := res["transactions"]; !ok { + + t.Fatal("missing transactions field") + + } else if len(v.([]interface{})) != 0 { + + t.Fatal("'transactions' value not []") + + } + + // 'uncles' key should exist as [] + + if v, ok := res["uncles"]; !ok { + + t.Fatal("missing uncles field") + + } else if len(v.([]interface{})) != 0 { + + t.Fatal("'uncles' value not []'") + + } + +} + + + +func TestBalanceAt(t *testing.T) { + + backend, _ := newTestBackend(t) + + client, _ := backend.Attach() + + defer backend.Close() + + defer client.Close() + + +++======= ++ func testBalanceAt(t *testing.T, client *rpc.Client) { +++>>>>>>> c2d2f4ed8f + tests := map[string]struct { + account common.Address + block *big.Int +@@@ -409,411 -398,177 +452,584 @@@ func testGetBlock(t *testing.T, client + if blockNumber != 1 { + t.Fatalf("BlockNumber returned wrong number: %d", blockNumber) + } ++ // Get current block by number ++ block, err := ec.BlockByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ if block.NumberU64() != blockNumber { ++ t.Fatalf("BlockByNumber returned wrong block: want %d got %d", blockNumber, block.NumberU64()) ++ } ++ // Get current block by hash ++ blockH, err := ec.BlockByHash(context.Background(), block.Hash()) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ if block.Hash() != blockH.Hash() { ++ t.Fatalf("BlockByHash returned wrong block: want %v got %v", block.Hash().Hex(), blockH.Hash().Hex()) ++ } ++ // Get header by number ++ header, err := ec.HeaderByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ if block.Header().Hash() != header.Hash() { ++ t.Fatalf("HeaderByNumber returned wrong header: want %v got %v", block.Header().Hash().Hex(), header.Hash().Hex()) ++ } ++ // Get header by hash ++ headerH, err := ec.HeaderByHash(context.Background(), block.Hash()) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ if block.Header().Hash() != headerH.Hash() { ++ t.Fatalf("HeaderByHash returned wrong header: want %v got %v", block.Header().Hash().Hex(), headerH.Hash().Hex()) ++ } ++ } ++ ++ func testStatusFunctions(t *testing.T, client *rpc.Client) { ++ ec := NewClient(client) ++ ++ // Sync progress ++ progress, err := ec.SyncProgress(context.Background()) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ if progress != nil { ++ t.Fatalf("unexpected progress: %v", progress) ++ } ++ // NetworkID ++ networkID, err := ec.NetworkID(context.Background()) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ if networkID.Cmp(big.NewInt(0)) != 0 { ++ t.Fatalf("unexpected networkID: %v", networkID) ++ } ++ // SuggestGasPrice (should suggest 1 Gwei) ++ gasPrice, err := ec.SuggestGasPrice(context.Background()) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ if gasPrice.Cmp(big.NewInt(1000000000)) != 0 { ++ t.Fatalf("unexpected gas price: %v", gasPrice) ++ } ++ } ++ ++ func testCallContract(t *testing.T, client *rpc.Client) { ++ ec := NewClient(client) ++ ++ // EstimateGas ++ msg := ethereum.CallMsg{ ++ From: testAddr, ++ To: &common.Address{}, ++ Gas: 21000, ++ GasPrice: big.NewInt(1), ++ Value: big.NewInt(1), ++ } ++ gas, err := ec.EstimateGas(context.Background(), msg) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ if gas != 21000 { ++ t.Fatalf("unexpected gas price: %v", gas) ++ } ++ // CallContract ++ if _, err := ec.CallContract(context.Background(), msg, big.NewInt(1)); err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ // PendingCallCOntract ++ if _, err := ec.PendingCallContract(context.Background(), msg); err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ } ++ ++ func testAtFunctions(t *testing.T, client *rpc.Client) { ++ ec := NewClient(client) ++ // send a transaction for some interesting pending status ++ sendTransaction(ec) ++ time.Sleep(100 * time.Millisecond) ++ // Check pending transaction count ++ pending, err := ec.PendingTransactionCount(context.Background()) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ if pending != 1 { ++ t.Fatalf("unexpected pending, wanted 1 got: %v", pending) ++ } ++ // Query balance ++ balance, err := ec.BalanceAt(context.Background(), testAddr, nil) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ penBalance, err := ec.PendingBalanceAt(context.Background(), testAddr) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ if balance.Cmp(penBalance) == 0 { ++ t.Fatalf("unexpected balance: %v %v", balance, penBalance) ++ } ++ // NonceAt ++ nonce, err := ec.NonceAt(context.Background(), testAddr, nil) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ penNonce, err := ec.PendingNonceAt(context.Background(), testAddr) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ if penNonce != nonce+1 { ++ t.Fatalf("unexpected nonce: %v %v", nonce, penNonce) ++ } ++ // StorageAt ++ storage, err := ec.StorageAt(context.Background(), testAddr, common.Hash{}, nil) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ penStorage, err := ec.PendingStorageAt(context.Background(), testAddr, common.Hash{}) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ if !bytes.Equal(storage, penStorage) { ++ t.Fatalf("unexpected storage: %v %v", storage, penStorage) ++ } ++ // CodeAt ++ code, err := ec.CodeAt(context.Background(), testAddr, nil) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ penCode, err := ec.PendingCodeAt(context.Background(), testAddr) ++ if err != nil { ++ t.Fatalf("unexpected error: %v", err) ++ } ++ if !bytes.Equal(code, penCode) { ++ t.Fatalf("unexpected code: %v %v", code, penCode) ++ } ++ } ++ ++ func sendTransaction(ec *Client) error { ++ // Retrieve chainID ++ chainID, err := ec.ChainID(context.Background()) ++ if err != nil { ++ return err ++ } ++ // Create transaction ++ tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, big.NewInt(1), nil) ++ signer := types.LatestSignerForChainID(chainID) ++ signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey) ++ if err != nil { ++ return err ++ } ++ signedTx, err := tx.WithSignature(signer, signature) ++ if err != nil { ++ return err ++ } ++ // Send transaction ++ return ec.SendTransaction(context.Background(), signedTx) + } + + + +func TestRPCDiscover(t *testing.T) { + + backend, _ := newTestBackend(t) + + client, _ := backend.Attach() + + defer backend.Close() + + defer client.Close() + + + + var res meta_schema.OpenrpcDocument + + err := client.Call(&res, "rpc.discover") + + if err != nil { + + t.Fatal(err) + + } + + + + sliceContains := func(sl []string, str string) bool { + + for _, s := range sl { + + if str == s { + + return true + + } + + } + + return false + + } + + + + methodNamesSlice := func() (names []string) { + + for _, m := range *res.Methods { + + names = append(names, string(*m.Name)) + + } + + return + + }() + + + + over, under := []string{}, []string{} + + + + for _, name := range methodNamesSlice { + + if !sliceContains(allRPCMethods, name) { + + under = append(under, name) + + } + + } + + for _, name := range allRPCMethods { + + if !sliceContains(methodNamesSlice, name) { + + over = append(over, name) + + } + + } + + + + if len(over) > 0 || len(under) > 0 { + + t.Fatalf("over: %v, under: %v", over, under) + + } + +} + + + +func subscriptionTestSetup(t *testing.T) (genesisBlock *genesisT.Genesis, backend *node.Node) { + + // Generate test chain. + + // Code largely taken from generateTestChain() + + chainConfig := params.TestChainConfig + + genesis := &genesisT.Genesis{ + + Config: chainConfig, + + Alloc: genesisT.GenesisAlloc{testAddr: {Balance: testBalance}}, + + ExtraData: []byte("test genesis"), + + Timestamp: 9000, + + } + + + + // Create node + + // Code largely taken from newTestBackend(t) + + backend, err := node.New(&node.Config{}) + + if err != nil { + + t.Fatalf("can't create new node: %v", err) + + } + + + + // Create Ethereum Service + + config := ð.Config{Genesis: genesis} + + config.Ethash.PowMode = ethash.ModeFake + + + + return genesis, backend + +} + + + +func TestEthSubscribeNewSideHeads(t *testing.T) { + + + + genesis, backend := subscriptionTestSetup(t) + + + + db := rawdb.NewMemoryDatabase() + + chainConfig := genesis.Config + + + + gblock := core.GenesisToBlock(genesis, db) + + engine := ethash.NewFaker() + + originalBlocks, _ := core.GenerateChain(chainConfig, gblock, engine, db, 10, func(i int, gen *core.BlockGen) { + + gen.OffsetTime(5) + + gen.SetExtra([]byte("test")) + + }) + + originalBlocks = append([]*types.Block{gblock}, originalBlocks...) + + + + // Create Ethereum Service + + config := ð.Config{Genesis: genesis} + + config.Ethash.PowMode = ethash.ModeFake + + ethservice, err := eth.New(backend, config) + + if err != nil { + + t.Fatalf("can't create new ethereum service: %v", err) + + } + + + + // Import the test chain. + + if err := backend.Start(); err != nil { + + t.Fatalf("can't start test node: %v", err) + + } + + if _, err := ethservice.BlockChain().InsertChain(originalBlocks[1:]); err != nil { + + t.Fatalf("can't import test blocks: %v", err) + + } + + + + // Create the client and newSideHeads subscription. + + client, err := backend.Attach() + + defer backend.Close() + + defer client.Close() + + if err != nil { + + t.Fatal(err) + + } + + ec := NewClient(client) + + defer ec.Close() + + + + sideHeadCh := make(chan *types.Header) + + sub, err := ec.SubscribeNewSideHead(context.Background(), sideHeadCh) + + if err != nil { + + t.Fatal(err) + + } + + defer sub.Unsubscribe() + + + + // Create and import the second-seen chain. + + replacementBlocks, _ := core.GenerateChain(chainConfig, originalBlocks[len(originalBlocks)-5], ethservice.Engine(), db, 5, func(i int, gen *core.BlockGen) { + + gen.OffsetTime(-9) // difficulty++ + + }) + + if _, err := ethservice.BlockChain().InsertChain(replacementBlocks); err != nil { + + t.Fatalf("can't import test blocks: %v", err) + + } + + + + headersOf := func(bs []*types.Block) (headers []*types.Header) { + + for _, b := range bs { + + headers = append(headers, b.Header()) + + } + + return + + } + + + + expectations := []*types.Header{} + + + + // Why do we expect the replacement (second-seen) blocks reported as side events? + + // Because they'll be inserted in ascending order, and until their segment exceeds the total difficulty + + // of the incumbent chain, they won't achieve canonical status, despite having greater difficulty per block + + // (see the time offset in the block generator function above). + + expectations = append(expectations, headersOf(replacementBlocks[:3])...) + + + + // Once the replacement blocks exceed the total difficulty of the original chain, the + + // blocks they replace will be reported as side chain events. + + expectations = append(expectations, headersOf(originalBlocks[7:])...) + + + + // This is illustrated in the logs called below. + + for i, b := range originalBlocks { + + t.Log("incumbent", i, b.NumberU64(), b.Hash().Hex()[:8]) + + } + + for i, b := range replacementBlocks { + + t.Log("replacement", i, b.NumberU64(), b.Hash().Hex()[:8]) + + } + + + + const timeoutDura = 5 * time.Second + + timeout := time.NewTimer(timeoutDura) + + + + got := []*types.Header{} + +waiting: + + for { + + select { + + case head := <-sideHeadCh: + + t.Log("<-newSideHeads", head.Number.Uint64(), head.Hash().Hex()[:8]) + + got = append(got, head) + + if len(got) == len(expectations) { + + timeout.Stop() + + break waiting + + } + + timeout.Reset(timeoutDura) + + case err := <-sub.Err(): + + t.Fatal(err) + + case <-timeout.C: + + t.Fatal("timed out") + + } + + } + + for i, b := range expectations { + + if got[i] == nil { + + t.Error("missing expected header (test will improvise a fake value)") + + // Set a nonzero value so I don't have to refactor this... + + got[i] = &types.Header{Number: big.NewInt(math.MaxInt64)} + + } + + if got[i].Number.Uint64() != b.Number.Uint64() { + + t.Errorf("number: want: %d, got: %d", b.Number.Uint64(), got[i].Number.Uint64()) + + } else if got[i].Hash() != b.Hash() { + + t.Errorf("hash: want: %s, got: %s", b.Hash().Hex()[:8], got[i].Hash().Hex()[:8]) + + } + + } + +} + + + +// mustNewTestBackend is the same logic as newTestBackend(t *testing.T) but without the testing.T argument. + +// This function is used exclusively for the benchmarking tests, and will panic if it encounters an error. + +func mustNewTestBackend() (*node.Node, []*types.Block) { + + // Generate test chain. + + genesis, blocks := generateTestChain() + + // Create node + + n, err := node.New(&node.Config{}) + + if err != nil { + + panic(fmt.Sprintf("can't create new node: %v", err)) + + } + + // Create Ethereum Service + + config := ð.Config{Genesis: genesis} + + config.Ethash.PowMode = ethash.ModeFake + + ethservice, err := eth.New(n, config) + + if err != nil { + + panic(fmt.Sprintf("can't create new ethereum service: %v", err)) + + } + + // Import the test chain. + + if err := n.Start(); err != nil { + + panic(fmt.Sprintf("can't start test node: %v", err)) + + } + + if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil { + + panic(fmt.Sprintf("can't import test blocks: %v", err)) + + } + + return n, blocks + +} + + + +// BenchmarkRPC_Discover shows that rpc.discover by reflection is slow. + +func BenchmarkRPC_Discover(b *testing.B) { + + backend, _ := mustNewTestBackend() + + client, _ := backend.Attach() + + defer backend.Close() + + defer client.Close() + + + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + + var res meta_schema.OpenrpcDocument + + err := client.Call(&res, "rpc.discover") + + if err != nil { + + b.Fatal(err) + + } + + } + +} + + + +// BenchmarkRPC_BlockNumber shows that eth_blockNumber is a lot faster than rpc.discover. + +func BenchmarkRPC_BlockNumber(b *testing.B) { + + backend, _ := mustNewTestBackend() + + client, _ := backend.Attach() + + defer backend.Close() + + defer client.Close() + + + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + + var res hexutil.Uint64 + + err := client.Call(&res, "eth_blockNumber") + + if err != nil { + + b.Fatalf("unexpected error: %v", err) + + } + + } + +} + + + +// allRPCMethods lists all methods exposed over JSONRPC. + +var allRPCMethods = []string{ + + "admin_addPeer", + + "admin_addTrustedPeer", + + "admin_datadir", + + "admin_ecbp1100", + + "admin_exportChain", + + "admin_importChain", + + "admin_maxPeers", + + "admin_nodeInfo", + + "admin_peers", + + "admin_removePeer", + + "admin_removeTrustedPeer", + + "admin_startRPC", + + "admin_startWS", + + "admin_stopRPC", + + "admin_stopWS", + + "debug_accountRange", + + "debug_backtraceAt", + + "debug_blockProfile", + + "debug_chaindbCompact", + + "debug_chaindbProperty", + + "debug_cpuProfile", + + "debug_dumpBlock", + + "debug_freeOSMemory", + + "debug_gcStats", + + "debug_getBadBlocks", + + "debug_getBlockRlp", + + "debug_getModifiedAccountsByHash", + + "debug_getModifiedAccountsByNumber", + + "debug_goTrace", + + "debug_memStats", + + "debug_mutexProfile", + + "debug_preimage", + + "debug_printBlock", + + "debug_removePendingTransaction", + + "debug_seedHash", + + "debug_setBlockProfileRate", + + "debug_setGCPercent", + + "debug_setHead", + + "debug_setMutexProfileFraction", + + "debug_stacks", + + "debug_standardTraceBadBlockToFile", + + "debug_standardTraceBlockToFile", + + "debug_startCPUProfile", + + "debug_startGoTrace", + + "debug_stopCPUProfile", + + "debug_stopGoTrace", + + "debug_storageRangeAt", + + "debug_testSignCliqueBlock", + + "debug_traceBadBlock", + + "debug_traceBlock", + + "debug_traceBlockByHash", + + "debug_traceBlockByNumber", + + "debug_traceBlockFromFile", + + "debug_traceCall", + + "debug_traceTransaction", + + "debug_verbosity", + + "debug_vmodule", + + "debug_writeBlockProfile", + + "debug_writeMemProfile", + + "debug_writeMutexProfile", + + "eth_accounts", + + "eth_blockNumber", + + "eth_call", + + "eth_chainId", + + "eth_chainId", + + "eth_coinbase", + + "eth_estimateGas", + + "eth_etherbase", + + "eth_fillTransaction", + + "eth_gasPrice", + + "eth_getBalance", + + "eth_getBlockByHash", + + "eth_getBlockByNumber", + + "eth_getBlockTransactionCountByHash", + + "eth_getBlockTransactionCountByNumber", + + "eth_getCode", + + "eth_getFilterChanges", + + "eth_getFilterLogs", + + "eth_getHashrate", + + "eth_getHeaderByHash", + + "eth_getHeaderByNumber", + + "eth_getLogs", + + "eth_getProof", + + "eth_getRawTransactionByBlockHashAndIndex", + + "eth_getRawTransactionByBlockNumberAndIndex", + + "eth_getRawTransactionByHash", + + "eth_getStorageAt", + + "eth_getTransactionByBlockHashAndIndex", + + "eth_getTransactionByBlockNumberAndIndex", + + "eth_getTransactionByHash", + + "eth_getTransactionCount", + + "eth_getTransactionReceipt", + + "eth_getUncleByBlockHashAndIndex", + + "eth_getUncleByBlockNumberAndIndex", + + "eth_getUncleCountByBlockHash", + + "eth_getUncleCountByBlockNumber", + + "eth_getWork", + + "eth_hashrate", + + "eth_mining", + + "eth_newBlockFilter", + + "eth_newSideBlockFilter", + + "eth_newFilter", + + "eth_newPendingTransactionFilter", + + "eth_pendingTransactions", + + "eth_protocolVersion", + + "eth_resend", + + "eth_sendRawTransaction", + + "eth_sendTransaction", + + "eth_sign", + + "eth_signTransaction", + + "eth_submitHashRate", + + "eth_submitWork", + + "eth_subscribe", + + "eth_syncing", + + "eth_uninstallFilter", + + "eth_unsubscribe", + + "ethash_getHashrate", + + "ethash_getWork", + + "ethash_submitHashRate", + + "ethash_submitWork", + + "miner_getHashrate", + + "miner_setEtherbase", + + "miner_setExtra", + + "miner_setGasPrice", + + "miner_setRecommitInterval", + + "miner_start", + + "miner_stop", + + "net_listening", + + "net_peerCount", + + "net_version", + + "personal_deriveAccount", + + "personal_ecRecover", + + "personal_importRawKey", + + "personal_initializeWallet", + + "personal_listAccounts", + + "personal_listWallets", + + "personal_lockAccount", + + "personal_newAccount", + + "personal_openWallet", + + "personal_sendTransaction", + + "personal_sign", + + "personal_signAndSendTransaction", + + "personal_signTransaction", + + "personal_unlockAccount", + + "personal_unpair", + + "trace_block", + + "trace_filter", + + "trace_transaction", + + "txpool_content", + + "txpool_inspect", + + "txpool_status", + + "web3_clientVersion", + + "web3_sha3", + +} +diff --cc ethstats/ethstats.go +index f82cffab1f,e0f4f95ff3..0000000000 +--- a/ethstats/ethstats.go ++++ b/ethstats/ethstats.go +@@@ -466,13 -444,15 +466,20 @@@ func (s *Service) login(conn *connWrapp + // Construct and send the login authentication + infos := s.server.NodeInfo() + +- var network, protocol string ++ var protocols []string ++ for _, proto := range s.server.Protocols { ++ protocols = append(protocols, fmt.Sprintf("%s/%d", proto.Name, proto.Version)) ++ } ++ var network string + if info := infos.Protocols["eth"]; info != nil { +++<<<<<<< HEAD + + network = fmt.Sprintf("%d", info.(*eth.NodeInfo).Network) + + protocol = fmt.Sprintf("eth/%d", s.backend.ProtocolVersion()) +++======= ++ network = fmt.Sprintf("%d", info.(*ethproto.NodeInfo).Network) +++>>>>>>> c2d2f4ed8f + } else { + network = fmt.Sprintf("%d", infos.Protocols["les"].(*les.NodeInfo).Network) +- protocol = fmt.Sprintf("les/%d", les.ClientProtocolVersions[0]) + } + auth := &authMsg{ + ID: s.node, +diff --cc go.mod +index c35cee81b3,96a217ed06..0000000000 +mode 100755,100644..100644 +--- a/go.mod ++++ b/go.mod +@@@ -5,70 -5,52 +5,115 @@@ go 1.1 + require ( + github.com/Azure/azure-storage-blob-go v0.7.0 + github.com/VictoriaMetrics/fastcache v1.5.7 +++<<<<<<< HEAD + + github.com/alecthomas/jsonschema v0.0.0-20200530073317-71f438968921 + + github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847 +++======= +++>>>>>>> c2d2f4ed8f + github.com/aws/aws-sdk-go v1.25.48 + github.com/btcsuite/btcd v0.20.1-beta + github.com/cespare/cp v0.1.0 + github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9 ++ github.com/consensys/gurvy v0.3.8 + github.com/davecgh/go-spew v1.1.1 + github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea +++<<<<<<< HEAD + + github.com/dlclark/regexp2 v1.4.0 // indirect + + github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf + + github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 + + github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c + + github.com/etclabscore/go-openrpc-reflect v0.0.35 + + github.com/fatih/color v1.7.0 + + github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc + + github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff + + github.com/go-ole/go-ole v1.2.4 // indirect + + github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect + + github.com/go-stack/stack v1.8.0 + + github.com/go-test/deep v1.0.5 + + github.com/golang/protobuf v1.4.2 + + github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26 + + github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa + + github.com/gorilla/websocket v1.4.2 + + github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277 + + github.com/hashicorp/golang-lru v0.5.4 + + github.com/holiman/uint256 v1.1.1 + + github.com/huin/goupnp v1.0.0 + + github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 + + github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883 + + github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 +++======= ++ github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf ++ github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 ++ github.com/edsrzf/mmap-go v1.0.0 ++ github.com/fatih/color v1.7.0 ++ github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 ++ github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff ++ github.com/go-stack/stack v1.8.0 ++ github.com/golang/protobuf v1.4.3 ++ github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3 ++ github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa ++ github.com/google/uuid v1.1.5 ++ github.com/gorilla/websocket v1.4.2 ++ github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29 ++ github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d ++ github.com/holiman/bloomfilter/v2 v2.0.3 ++ github.com/holiman/uint256 v1.1.1 ++ github.com/huin/goupnp v1.0.1-0.20200620063722-49508fba0031 ++ github.com/influxdata/influxdb v1.8.3 ++ github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 ++ github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e +++>>>>>>> c2d2f4ed8f + github.com/julienschmidt/httprouter v1.2.0 + github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 + github.com/mattn/go-colorable v0.1.0 + github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035 +++<<<<<<< HEAD + + github.com/mitchellh/go-homedir v1.1.0 + + github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 + + github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c + + github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 + + github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222 + + github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 + + github.com/prometheus/tsdb v0.7.1 + + github.com/rjeczalik/notify v0.9.1 + + github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00 + + github.com/rs/xhandler v0.0.0-20170707052532-1eb70cf1520d // indirect +++======= ++ github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 ++ github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c ++ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 ++ github.com/prometheus/tsdb v0.7.1 ++ github.com/rjeczalik/notify v0.9.1 ++ github.com/rs/cors v1.7.0 +++>>>>>>> c2d2f4ed8f + github.com/shirou/gopsutil v2.20.5+incompatible + + github.com/spf13/cobra v1.0.0 + + github.com/spf13/viper v1.7.0 + github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 +++<<<<<<< HEAD + + github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 + + github.com/stretchr/testify v1.4.0 +++======= ++ github.com/stretchr/testify v1.7.0 +++>>>>>>> c2d2f4ed8f + github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca + + github.com/tidwall/gjson v1.6.0 + + github.com/tidwall/pretty v1.0.0 + github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef +++<<<<<<< HEAD + + github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 + + github.com/xeipuuv/gojsonschema v1.2.0 + + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 + + golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 + + golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f +++======= ++ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 ++ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c +++>>>>>>> c2d2f4ed8f + golang.org/x/text v0.3.3 + golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 + + gonum.org/v1/gonum v0.8.1 + + gonum.org/v1/plot v0.8.0 + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce + gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 + gopkg.in/urfave/cli.v1 v1.20.0 +diff --cc go.sum +index 0a4689810b,af76759c51..0000000000 +mode 100755,100644..100644 +--- a/go.sum ++++ b/go.sum +@@@ -1,18 -1,24 +1,38 @@@ + cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= + cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= + cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +++<<<<<<< HEAD +++======= ++ cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= +++>>>>>>> c2d2f4ed8f + cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= + cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= + cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= + cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +++<<<<<<< HEAD + +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= + +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= + +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= + +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= + +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= + +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= + +gioui.org v0.0.0-20200628203458-851255f7a67b/go.mod h1:jiUwifN9cRl/zmco43aAqh0aV+s9GbhG13KcD+gEpkU= + +github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZOMdj5HYo= +++======= ++ cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= ++ cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= ++ cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= ++ cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= ++ cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= ++ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= ++ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= ++ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= ++ cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= ++ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= ++ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= ++ collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= ++ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +++>>>>>>> c2d2f4ed8f + github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= + github.com/Azure/azure-pipeline-go v0.2.2 h1:6oiIS9yaG6XCCzhgAgKFfIWyo4LLCiDhZot6ltoThhY= + github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= +@@@ -34,36 -40,24 +54,48 @@@ github.com/Azure/go-autorest/logger v0. + github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= + github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= + github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= + +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= + github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= + github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +++<<<<<<< HEAD + +github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic= + +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= + +github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= +++======= ++ github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +++>>>>>>> c2d2f4ed8f + github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= + +github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= + +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= + +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= + +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= + +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= + github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= + github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= + +github.com/VictoriaMetrics/fastcache v1.5.3/go.mod h1:+jv9Ckb+za/P1ZRg/sulP5Ni1v49daAVERr0H3CuscE= + github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw= + github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= + github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +++<<<<<<< HEAD + +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af h1:wVe6/Ea46ZMeNkQjjBW6xcqyQA/j5e0D6GytH95g0gQ= + +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= + +github.com/alecthomas/jsonschema v0.0.0-20200530073317-71f438968921 h1:T3+cD5fYvuH36h7EZq+TDpm+d8a6FSD4pQsbmuGGQ8o= + +github.com/alecthomas/jsonschema v0.0.0-20200530073317-71f438968921/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60= +++======= ++ github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +++>>>>>>> c2d2f4ed8f + github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= + github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= + github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= + github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +- github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847 h1:rtI0fD4oG/8eVokGVPYJEW1F88p1ZNgXiEIs9thEE4A= ++ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= ++ github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= + github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= + github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +++<<<<<<< HEAD + +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +++======= +++>>>>>>> c2d2f4ed8f + github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= + github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= + github.com/aws/aws-sdk-go v1.25.48 h1:J82DYDGZHOKHdhx6hD24Tm30c2C3GchYGfN0mf9iKUk= +@@@ -72,7 -66,8 +104,12 @@@ github.com/beorn7/perks v0.0.0-20180321 + github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= + github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= + github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +++<<<<<<< HEAD + +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +++======= ++ github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= ++ github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +++>>>>>>> c2d2f4ed8f + github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= + github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= + github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +@@@ -83,27 -78,32 +120,53 @@@ github.com/btcsuite/goleveldb v0.0.0-20 + github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= + github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= + github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +++<<<<<<< HEAD +++======= ++ github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= ++ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +++>>>>>>> c2d2f4ed8f + github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= + github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= + github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= + github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= + +github.com/cespare/xxhash/v2 v2.0.1-0.20190104013014-3767db7a7e18/go.mod h1:HD5P3vAIAh+Y2GAxg0PrPN1P8WkepXGpjbUPDHJqqKM= + github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= + github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +++<<<<<<< HEAD + +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= + +github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9 h1:J82+/8rub3qSy0HxEnoYD8cs+HDlHWYrqYXe2Vqxluk= + +github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= + +github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= + +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= + +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= + +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= + +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= + +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= + +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= + +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= + +github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU= + +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= + +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +++======= ++ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= ++ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= ++ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= ++ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= ++ github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9 h1:J82+/8rub3qSy0HxEnoYD8cs+HDlHWYrqYXe2Vqxluk= ++ github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= ++ github.com/consensys/bavard v0.1.8-0.20210105233146-c16790d2aa8b/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= ++ github.com/consensys/goff v0.3.10/go.mod h1:xTldOBEHmFiYS0gPXd3NsaEqZWlnmeWcRLWgD3ba3xc= ++ github.com/consensys/gurvy v0.3.8 h1:H2hvjvT2OFMgdMn5ZbhXqHt+F8DJ2clZW7Vmc0kFFxc= ++ github.com/consensys/gurvy v0.3.8/go.mod h1:sN75xnsiD593XnhbhvG2PkOy194pZBzqShWF/kwuW/g= ++ github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= ++ github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= ++ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= ++ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= ++ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= ++ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= ++ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= ++ github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= +++>>>>>>> c2d2f4ed8f + github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= + github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= + github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +@@@ -115,102 -116,78 +179,165 @@@ github.com/dgryski/go-bitstream v0.0.0- + github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= + github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= + github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= + +github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= + +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= + github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M= + github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= + +github.com/dop251/goja v0.0.0-20200219165308-d1232e640a87/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= + github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bacAU/eSnV3dAxVpepaghAdhGoQ= + github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +++<<<<<<< HEAD + +github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c h1:JHHhtb9XWJrGNMcrVP6vyzO4dusgi/HnceHTgxSejUM= + +github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= + +github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= + +github.com/etclabscore/go-jsonschema-walk v0.0.6 h1:DrNzoKWKd8f8XB5nFGBY00IcjakRE22OTI12k+2LkyY= + +github.com/etclabscore/go-jsonschema-walk v0.0.6/go.mod h1:VdfDY72AFAiUhy0ZXEaWSpveGjMT5JcDIm903NGqFwQ= + +github.com/etclabscore/go-openrpc-reflect v0.0.35 h1:BHr8JJtMB46eVVc3sAUuLH3s9YFp8KsBEm3VkRF6mGY= + +github.com/etclabscore/go-openrpc-reflect v0.0.35/go.mod h1:XgVhDkb6BeOtc4qf04k4K6lNI5gpcEYrAPgB8GGnIJc= + +github.com/ethereum/go-ethereum v1.9.12/go.mod h1:PvsVkQmhZFx92Y+h2ylythYlheEDt/uBgFbl61Js/jo= + +github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= + +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= + +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= + +github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc h1:jtW8jbpkO4YirRSyepBOH8E+2HEw6/hKkBvFPwhUN8c= + +github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= + +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= + +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= + +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= + +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +++======= ++ github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= ++ github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= ++ github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= ++ github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= ++ github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= ++ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= ++ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= ++ github.com/ethereum/go-ethereum v1.9.25/go.mod h1:vMkFiYLHI4tgPw4k2j4MHKoovchFE8plZ0M9VMk4/oM= ++ github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= ++ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= ++ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= ++ github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= ++ github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= ++ github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= ++ github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +++>>>>>>> c2d2f4ed8f + github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= + github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= + github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= + github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= + github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= + github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +++<<<<<<< HEAD + +github.com/go-fonts/dejavu v0.1.0 h1:JSajPXURYqpr+Cu8U9bt8K+XcACIHWqWrvWCKyeFmVQ= + +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= + +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= + +github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= + +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= + +github.com/go-latex/latex v0.0.0-20200518072620-0806b477ea35 h1:uroDDLmuCK5Pz5J/Ef5vCL6F0sJmAtZFTm0/cF027F4= + +github.com/go-latex/latex v0.0.0-20200518072620-0806b477ea35/go.mod h1:PNI+CcWytn/2Z/9f1SGOOYn0eILruVyp0v2/iAs8asQ= + +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= + +github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= + +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +++======= ++ github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= ++ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= ++ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= ++ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= ++ github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= ++ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= ++ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= ++ github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= ++ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= ++ github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= +++>>>>>>> c2d2f4ed8f + github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= + +github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= + +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= + +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= + +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= + +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= + +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= + +github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= + +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= + +github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg= + +github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= + +github.com/go-openapi/spec v0.19.7 h1:0xWSeMd35y5avQAThZR2PkEuqSosoS5t6gDH4L8n11M= + +github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= + +github.com/go-openapi/spec v0.19.11 h1:ogU5q8dtp3MMPn59a9VRrPKVxvJHEs5P7yNMR5sNnis= + +github.com/go-openapi/spec v0.19.11/go.mod h1:vqK/dIdLGCosfvYsQV3WfC7N3TiZSnGY2RZKoFK7X28= + +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= + +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= + +github.com/go-openapi/swag v0.19.8 h1:vfK6jLhs7OI4tAXkvkooviaE1JEPcw3mutyegLHHjmk= + +github.com/go-openapi/swag v0.19.8/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= + +github.com/go-openapi/swag v0.19.11 h1:RFTu/dlFySpyVvJDfp/7674JY4SDglYWKztbiIGFpmc= + +github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY= + github.com/go-sourcemap/sourcemap v2.1.2+incompatible h1:0b/xya7BKGhXuqFESKM4oIiRo9WOt2ebz7KxfreD6ug= + github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +++<<<<<<< HEAD + +github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= + +github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= + +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= + +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= + +github.com/go-test/deep v1.0.5 h1:AKODKU3pDH1RzZzm6YZu77YWtEAq6uh1rLIAQlay2qc= + +github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= + +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= + +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= + +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= + +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= + +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= + +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= + +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= + +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= + +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= + +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= + +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= + +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= + +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= + +github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +++======= ++ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= ++ github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= ++ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= ++ github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= ++ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= ++ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= ++ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= ++ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= ++ github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= ++ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= ++ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= ++ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= ++ github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= ++ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= ++ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= ++ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= ++ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= ++ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +++>>>>>>> c2d2f4ed8f + github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= + github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= + github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= + github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= + github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= + github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +- github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= + github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +++<<<<<<< HEAD + +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= + +github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26 h1:lMm2hD9Fy0ynom5+85/pbdkiYcBqM1JWmhpAXLmy0fw= + +github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= + +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= + +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +++======= ++ github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= ++ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= ++ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= ++ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= ++ github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3 h1:ur2rms48b3Ep1dxh7aUV2FZEQ8jEVO2F6ILKx8ofkAg= ++ github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= ++ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= ++ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= ++ github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +++>>>>>>> c2d2f4ed8f + github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= + github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= + github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +@@@ -221,17 -198,19 +348,33 @@@ github.com/google/gofuzz v1.1.1-0.20200 + github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= + github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= + github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +++<<<<<<< HEAD + +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= + +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= + +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= + +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= + +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= + +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= + +github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= + +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= + +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= + +github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277 h1:E0whKxgp2ojts0FDgUA8dl62bmH0LxKanMoBr6MDTDM= + +github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +++======= ++ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= ++ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= ++ github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I= ++ github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= ++ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= ++ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= ++ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= ++ github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= ++ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= ++ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= ++ github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= ++ github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29 h1:sezaKhEfPFg8W0Enm61B9Gs911H8iesGY5R8NDPtd1M= ++ github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +++>>>>>>> c2d2f4ed8f + github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= + github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +@@@ -248,60 -227,67 +391,121 @@@ github.com/hashicorp/go-syslog v1.0.0/g + github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= + github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= + github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +++<<<<<<< HEAD + +github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= + +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= + +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= + +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= + +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= + +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +++======= ++ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= ++ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= ++ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= ++ github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= ++ github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +++>>>>>>> c2d2f4ed8f + github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= + github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= + github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= + github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= + github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +++<<<<<<< HEAD + +github.com/holiman/uint256 v1.1.1 h1:4JywC80b+/hSfljFlEBLHrrh+CIONLDz9NuFl0af4Mw= + +github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= + +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= + +github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag= + +github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= +++======= ++ github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= ++ github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= ++ github.com/holiman/uint256 v1.1.1 h1:4JywC80b+/hSfljFlEBLHrrh+CIONLDz9NuFl0af4Mw= ++ github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= ++ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +++>>>>>>> c2d2f4ed8f + github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= ++ github.com/huin/goupnp v1.0.1-0.20200620063722-49508fba0031 h1:HarGZ5h9HD9LgEg1yRVMXyfiw4wlXiLiYM2oMjeA/SE= ++ github.com/huin/goupnp v1.0.1-0.20200620063722-49508fba0031/go.mod h1:nNs7wvRfN1eKaMknBydLNQU6146XQim8t4h+q90biWo= + github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +++<<<<<<< HEAD + +github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk= + +github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= + +github.com/iancoleman/orderedmap v0.1.0 h1:2orAxZBJsvimgEBmMWfXaFlzSG2fbQil5qzP3F6cCkg= + +github.com/iancoleman/orderedmap v0.1.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= + +github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8= + +github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= + +github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo= + +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= + +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= + +github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883 h1:FSeK4fZCo8u40n2JMnyAsd6x7+SbvoOMHvQOU/n10P4= +++======= ++ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= ++ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= ++ github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= +++>>>>>>> c2d2f4ed8f + github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= ++ github.com/influxdata/influxdb v1.8.3 h1:WEypI1BQFTT4teLM+1qkEcvUi0dAvopAI/ir0vAiBg8= ++ github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= ++ github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= ++ github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= ++ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= ++ github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= ++ github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= ++ github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= + github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA= + github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +++<<<<<<< HEAD +++======= ++ github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e h1:UvSe12bq+Uj2hWd8aOlwPmoZ+CITRFrdit+sDGfAg8U= ++ github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= +++>>>>>>> c2d2f4ed8f + github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= + github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= + github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= + github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +++<<<<<<< HEAD + +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= + +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= + +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= + +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= + +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= + +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +++======= ++ github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= ++ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= ++ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= ++ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= ++ github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= +++>>>>>>> c2d2f4ed8f + github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= + github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= + github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= + github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +++<<<<<<< HEAD + +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= + +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= + +github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc= + +github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0= + +github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 h1:I/yrLt2WilKxlQKCM52clh5rGzTKpVctGT1lH4Dc8Jw= + +github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= + +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= + +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= + +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +++======= ++ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= ++ github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= ++ github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 h1:I/yrLt2WilKxlQKCM52clh5rGzTKpVctGT1lH4Dc8Jw= ++ github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= ++ github.com/kilic/bls12-381 v0.0.0-20201226121925-69dacb279461/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= ++ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= ++ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= ++ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= ++ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= ++ github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= ++ github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= ++ github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= ++ github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +++>>>>>>> c2d2f4ed8f + github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= + github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= + github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +@@@ -312,15 -298,11 +516,23 @@@ github.com/kr/text v0.1.0 h1:45sCR5RtlF + github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= + github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= + github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +++<<<<<<< HEAD + +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= + +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= + +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= + +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= + +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= + +github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8= + +github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= + +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= + +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +++======= ++ github.com/leanovate/gopter v0.2.8/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8= ++ github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= ++ github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= ++ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= ++ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +++>>>>>>> c2d2f4ed8f + github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= + github.com/mattn/go-colorable v0.1.0 h1:v2XXALHHh6zHfYTJ+cSkwtyffnaOyR1MXaA91mTrb8o= + github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +@@@ -329,6 -310,7 +541,10 @@@ github.com/mattn/go-ieproxy v0.0.0-2019 + github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d h1:oNAwILwmgWKFpuU+dXvI6dl9jG2mAWAZLX3r9s0PPiw= + github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= + github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +++<<<<<<< HEAD +++======= ++ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +++>>>>>>> c2d2f4ed8f + github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035 h1:USWjF42jDCSEeikX/G1g40ZWnsPXN5WkZ4jMHZWyBK4= + github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= + github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +@@@ -338,16 -322,15 +556,26 @@@ github.com/matttproud/golang_protobuf_e + github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= + github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= + github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +++<<<<<<< HEAD + +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +++======= +++>>>>>>> c2d2f4ed8f + github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= + github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= + github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= + github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= + github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +++<<<<<<< HEAD + +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= + +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= + +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= + +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +++======= ++ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= ++ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= ++ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= ++ github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= +++>>>>>>> c2d2f4ed8f + github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= + github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= + github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +@@@ -369,18 -351,19 +597,34 @@@ github.com/onsi/gomega v1.4.3/go.mod h1 + github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= + github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= + github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +++<<<<<<< HEAD + +github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 h1:CznVS40zms0Dj5he4ERo+fRPtO0qxUk8lA8Xu3ddet0= + +github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333/go.mod h1:Ag6rSXkHIckQmjFBCweJEEt1mrTPBv8b9W4aU/NQWfI= + +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= + +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= + +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= + +github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222 h1:goeTyGkArOZIVOMA0dQbyuPWGNQJZGPwPu/QS9GlpnA= + +github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= + +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= + +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= + +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= + +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= + +github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +++======= ++ github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= ++ github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= ++ github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= ++ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= ++ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= ++ github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= ++ github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= ++ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= ++ github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= ++ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= ++ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= ++ github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= ++ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +++>>>>>>> c2d2f4ed8f + github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= + github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= + github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +@@@ -389,52 -373,50 +634,96 @@@ github.com/pmezard/go-difflib v1.0.0/go + github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= + github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= + github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +++<<<<<<< HEAD +++github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +++github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +++github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +++github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +++github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +++github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +++github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +++github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= +++github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +++======= ++ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= + github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= + github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= ++ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= + github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= + github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= ++ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= ++ github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= + github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= + github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= ++ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= + github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= + github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= + github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= ++ github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= +++>>>>>>> c2d2f4ed8f + github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE= + github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= + github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= + github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +++<<<<<<< HEAD + +github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00 h1:8DPul/X0IT/1TNMIxoKLwdemEOBBHDC/K4EB16Cw5WE= +++======= +++>>>>>>> c2d2f4ed8f + github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +- github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521 h1:3hxavr+IHMsQBrYUPQM5v0CgENFktkkbg1sfpgM3h20= ++ github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= ++ github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= + github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= + +github.com/rs/xhandler v0.0.0-20170707052532-1eb70cf1520d h1:8Tt7DYYdFqLlOIuyiE0RluKem4T+048AUafnIjH80wg= + +github.com/rs/xhandler v0.0.0-20170707052532-1eb70cf1520d/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= + github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +++<<<<<<< HEAD + +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= + +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= + +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +++======= ++ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= ++ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= ++ github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= ++ github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= ++ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +++>>>>>>> c2d2f4ed8f + github.com/shirou/gopsutil v2.20.5+incompatible h1:tYH07UPoQt0OCQdgWWMgYHy3/a9bcxNpBIysykNIP7I= + github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= + github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= + github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +++<<<<<<< HEAD + +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= + +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= + +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= + +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= + +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= + +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= + +github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= + +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= + +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= + +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= + +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= + +github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= + +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= + +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= + +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= + +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= + +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= + +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= + +github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= +++======= ++ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= ++ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= ++ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= ++ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= ++ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= ++ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= ++ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= ++ github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= ++ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= ++ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= ++ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +++>>>>>>> c2d2f4ed8f + github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= + github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg= + github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= +@@@ -443,43 -424,32 +731,68 @@@ github.com/steakknife/bloomfilter v0.0. + github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= + github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= + github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +++<<<<<<< HEAD + +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= + +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= + +github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= + +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= + +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= + +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= + +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= + +github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= + +github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk= + +github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= + +github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= + +github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= + +github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= + +github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= + +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= + +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +++======= ++ github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= ++ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= ++ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= ++ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= ++ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= ++ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= ++ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= ++ github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk= ++ github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= ++ github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +++>>>>>>> c2d2f4ed8f + github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= + github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4= + github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= + +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= + github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +- github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk= ++ github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= + github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= +++<<<<<<< HEAD + +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= + +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= + +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= + +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= + +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= + +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= + +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= + +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= + +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= + +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= + +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= + +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= + +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +++======= ++ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= ++ github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= ++ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= ++ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= ++ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= ++ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= ++ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= ++ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= ++ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= ++ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +++>>>>>>> c2d2f4ed8f + go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= + golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= + golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +@@@ -487,7 -457,8 +800,12 @@@ golang.org/x/crypto v0.0.0-201810290212 + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= + golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= + golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +++<<<<<<< HEAD + +golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +++======= ++ golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= ++ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +++>>>>>>> c2d2f4ed8f + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= + golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +@@@ -496,17 -467,14 +814,28 @@@ golang.org/x/exp v0.0.0-20190121172915- + golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= + golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= + golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +++<<<<<<< HEAD + +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= + +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= + +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= + +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= + +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= + +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= + +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= + +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= + +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= + +golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34= + +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +++======= ++ golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= ++ golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= ++ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= ++ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= ++ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= ++ golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= ++ golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= ++ golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +++>>>>>>> c2d2f4ed8f + golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= + golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= + golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +@@@ -514,10 -482,14 +843,21 @@@ golang.org/x/lint v0.0.0-20190313153728 + golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= + golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= + golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +++<<<<<<< HEAD +++golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +++golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +++golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +++golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +++======= ++ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= + golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= + golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= ++ golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= + golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= + golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= ++ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= ++ golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +++>>>>>>> c2d2f4ed8f + golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= + golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= + golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +@@@ -532,25 -504,28 +872,48 @@@ golang.org/x/net v0.0.0-20190311183353- + golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= + golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= + golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +++<<<<<<< HEAD + +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= + +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= + +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= + +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= + +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= + +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= + +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= + +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= + +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= + +golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY= + +golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= + +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= + +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= + +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +++======= ++ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= ++ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= ++ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= ++ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= ++ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= ++ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= ++ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= ++ golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= ++ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= ++ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= ++ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= ++ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= ++ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= ++ golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +++>>>>>>> c2d2f4ed8f + golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= + golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= + golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= + golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= + golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +++<<<<<<< HEAD +++======= ++ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= ++ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= ++ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +++>>>>>>> c2d2f4ed8f + golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= + golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= + golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +@@@ -565,17 -540,21 +928,35 @@@ golang.org/x/sys v0.0.0-20190502145724- + golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= + golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= + golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +++<<<<<<< HEAD + +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= + +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= + +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= + +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= + +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= + +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= + +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= + +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= + +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= + +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= + +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +++======= ++ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ++ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ++ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ++ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ++ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ++ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ++ golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ++ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ++ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ++ golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ++ golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ++ golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ++ golang.org/x/sys v0.0.0-20210105210732-16f7687f5001/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ++ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= ++ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +++>>>>>>> c2d2f4ed8f + golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= + golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= + golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +@@@ -587,6 -566,7 +968,10 @@@ golang.org/x/time v0.0.0-20190308202827 + golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= + golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= + golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +++<<<<<<< HEAD +++======= ++ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +++>>>>>>> c2d2f4ed8f + golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= + golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= + golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +@@@ -596,48 -576,64 +981,100 @@@ golang.org/x/tools v0.0.0-2019031217024 + golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= + golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= + golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +++<<<<<<< HEAD +++======= ++ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +++>>>>>>> c2d2f4ed8f + golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= + golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= + golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= + golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= + golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +++<<<<<<< HEAD + +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= + +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= + +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= + +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +++======= ++ golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= ++ golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= ++ golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= ++ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= ++ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= ++ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= ++ golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= ++ golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= ++ golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= ++ golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= ++ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= ++ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +++>>>>>>> c2d2f4ed8f + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= + gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +++<<<<<<< HEAD + +gonum.org/v1/gonum v0.8.1 h1:wGtP3yGpc5mCLOLeTeBdjeui9oZSz5De0eOjMLC/QuQ= + +gonum.org/v1/gonum v0.8.1/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= + +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= + +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= + +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= + +gonum.org/v1/plot v0.8.0 h1:dNgubmltsMoehfn6XgbutHpicbUfbkcGSxkICy1bC4o= + +gonum.org/v1/plot v0.8.0/go.mod h1:3GH8dTfoceRTELDnv+4HNwbvM/eMfdDUGHFG2bo3NeE= +++======= ++ gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= ++ gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= ++ gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= ++ gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= ++ gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +++>>>>>>> c2d2f4ed8f + google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= + google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= + google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= + google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= + google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +++<<<<<<< HEAD +++======= ++ google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= ++ google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +++>>>>>>> c2d2f4ed8f + google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= + google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= + google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= + google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +++<<<<<<< HEAD +++======= ++ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +++>>>>>>> c2d2f4ed8f + google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= + google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= + google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= + google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= + google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +++<<<<<<< HEAD +++======= ++ google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +++>>>>>>> c2d2f4ed8f + google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= + google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= + google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= + google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +++<<<<<<< HEAD + +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= + +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= + +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= + +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +++======= ++ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= ++ google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= ++ google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= ++ google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= ++ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= ++ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= ++ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= ++ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= ++ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +++>>>>>>> c2d2f4ed8f + google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= + google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= + google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +@@@ -651,7 -647,6 +1088,10 @@@ gopkg.in/check.v1 v1.0.0-20180628173108 + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= + gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= + gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +++<<<<<<< HEAD + +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +++======= +++>>>>>>> c2d2f4ed8f + gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +@@@ -675,9 -671,7 +1117,16 @@@ gotest.tools v2.2.0+incompatible/go.mo + honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= + honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= + honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +++<<<<<<< HEAD + +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= + +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= + +rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= + +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= + +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= + +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +++======= ++ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= ++ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= ++ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= ++ rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +++>>>>>>> c2d2f4ed8f +diff --cc les/client.go +index 99d495db0b,605c4d03ca..0000000000 +--- a/les/client.go ++++ b/les/client.go +@@@ -42,10 -43,9 +43,16 @@@ import + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" +++<<<<<<< HEAD + + "github.com/ethereum/go-ethereum/params/confp" + + "github.com/ethereum/go-ethereum/params/types/coregeth" + + "github.com/ethereum/go-ethereum/params/types/goethereum" + + "github.com/ethereum/go-ethereum/params/vars" +++======= ++ "github.com/ethereum/go-ethereum/p2p/enr" ++ "github.com/ethereum/go-ethereum/params" ++ "github.com/ethereum/go-ethereum/rlp" +++>>>>>>> c2d2f4ed8f + "github.com/ethereum/go-ethereum/rpc" + ) + +@@@ -87,8 -87,8 +94,13 @@@ func New(stack *node.Node, config *ethc + if err != nil { + return nil, err + } +++<<<<<<< HEAD + + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis) + + if _, isCompat := genesisErr.(*confp.ConfigCompatError); genesisErr != nil && !isCompat { +++======= ++ chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideBerlin) ++ if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat { +++>>>>>>> c2d2f4ed8f + return nil, genesisErr + } + log.Info("Initialised chain configuration", "config", chainConfig) +@@@ -107,28 -108,26 +120,37 @@@ + eventMux: stack.EventMux(), + reqDist: newRequestDistributor(peers, &mclock.System{}), + accountManager: stack.AccountManager(), +- engine: eth.CreateConsensusEngine(stack, chainConfig, &config.Ethash, nil, false, chainDb), ++ engine: ethconfig.CreateConsensusEngine(stack, chainConfig, &config.Ethash, nil, false, chainDb), + bloomRequests: make(chan chan *bloombits.Retrieval), +++<<<<<<< HEAD + + bloomIndexer: eth.NewBloomIndexer(chainDb, vars.BloomBitsBlocksClient, vars.HelperTrieConfirmations), + + valueTracker: lpc.NewValueTracker(lespayDb, &mclock.System{}, requestList, time.Minute, 1/float64(time.Hour), 1/float64(time.Hour*100), 1/float64(time.Hour*1000)), +++======= ++ bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations), +++>>>>>>> c2d2f4ed8f + p2pServer: stack.Server(), ++ p2pConfig: &stack.Config().P2P, + } +- peers.subscribe((*vtSubscription)(leth.valueTracker)) + +- dnsdisc, err := leth.setupDiscovery(&stack.Config().P2P) +- if err != nil { +- return nil, err ++ var prenegQuery vfc.QueryFunc ++ if leth.p2pServer.DiscV5 != nil { ++ prenegQuery = leth.prenegQuery + } +- leth.serverPool = newServerPool(lespayDb, []byte("serverpool:"), leth.valueTracker, dnsdisc, time.Second, nil, &mclock.System{}, config.UltraLightServers) +- peers.subscribe(leth.serverPool) +- leth.dialCandidates = leth.serverPool.dialIterator ++ leth.serverPool, leth.serverPoolIterator = vfc.NewServerPool(lesDb, []byte("serverpool:"), time.Second, prenegQuery, &mclock.System{}, config.UltraLightServers, requestList) ++ leth.serverPool.AddMetrics(suggestedTimeoutGauge, totalValueGauge, serverSelectableGauge, serverConnectedGauge, sessionValueMeter, serverDialedMeter) + +- leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool.getTimeout) ++ leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool.GetTimeout) + leth.relay = newLesTxRelay(peers, leth.retriever) + +++<<<<<<< HEAD + + leth.odr = NewLesOdr(chainDb, light.DefaultClientIndexerConfig, leth.retriever) + + leth.chtIndexer = light.NewChtIndexer(chainDb, leth.odr, vars.CHTFrequency, vars.HelperTrieConfirmations, config.LightNoPrune) + + leth.bloomTrieIndexer = light.NewBloomTrieIndexer(chainDb, leth.odr, vars.BloomBitsBlocksClient, vars.BloomTrieFrequency, config.LightNoPrune) +++======= ++ leth.odr = NewLesOdr(chainDb, light.DefaultClientIndexerConfig, leth.peers, leth.retriever) ++ leth.chtIndexer = light.NewChtIndexer(chainDb, leth.odr, params.CHTFrequency, params.HelperTrieConfirmations, config.LightNoPrune) ++ leth.bloomTrieIndexer = light.NewBloomTrieIndexer(chainDb, leth.odr, params.BloomBitsBlocksClient, params.BloomTrieFrequency, config.LightNoPrune) +++>>>>>>> c2d2f4ed8f + leth.odr.SetIndexers(leth.chtIndexer, leth.bloomTrieIndexer, leth.bloomIndexer) + + checkpoint := config.Checkpoint +@@@ -293,10 -346,15 +373,15 @@@ func (s *LightEthereum) Protocols() []p + func (s *LightEthereum) Start() error { + log.Warn("Light client mode is an experimental feature") + +- s.serverPool.start() ++ discovery, err := s.setupDiscovery(s.p2pConfig) ++ if err != nil { ++ return err ++ } ++ s.serverPool.AddSource(discovery) ++ s.serverPool.Start() + // Start bloom request workers. + s.wg.Add(bloomServiceThreads) + - s.startBloomHandlers(params.BloomBitsBlocksClient) + + s.startBloomHandlers(vars.BloomBitsBlocksClient) + s.handler.start() + + return nil +diff --cc les/client_handler.go +index 5c7f7750ce,f8e9edc9fe..0000000000 +--- a/les/client_handler.go ++++ b/les/client_handler.go +@@@ -37,18 -38,23 +39,27 @@@ import + // responses. + type clientHandler struct { + ulc *ulc +++<<<<<<< HEAD + + checkpoint *ctypes.TrustedCheckpoint +++======= ++ forkFilter forkid.Filter ++ checkpoint *params.TrustedCheckpoint +++>>>>>>> c2d2f4ed8f + fetcher *lightFetcher + downloader *downloader.Downloader + backend *LightEthereum + +- closeCh chan struct{} +- wg sync.WaitGroup // WaitGroup used to track all connected peers. +- syncDone func() // Test hooks when syncing is done. ++ closeCh chan struct{} ++ wg sync.WaitGroup // WaitGroup used to track all connected peers. ++ ++ // Hooks used in the testing ++ syncStart func(header *types.Header) // Hook called when the syncing is started ++ syncEnd func(header *types.Header) // Hook called when the syncing is done + } + + -func newClientHandler(ulcServers []string, ulcFraction int, checkpoint *params.TrustedCheckpoint, backend *LightEthereum) *clientHandler { + +func newClientHandler(ulcServers []string, ulcFraction int, checkpoint *ctypes.TrustedCheckpoint, backend *LightEthereum) *clientHandler { + handler := &clientHandler{ ++ forkFilter: forkid.NewFilter(backend.blockchain), + checkpoint: checkpoint, + backend: backend, + closeCh: make(chan struct{}), +diff --cc les/commons.go +index 977a23e623,d090fc21fc..0000000000 +--- a/les/commons.go ++++ b/les/commons.go +@@@ -33,10 -33,8 +33,9 @@@ import + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" +- "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params" + + "github.com/ethereum/go-ethereum/params/types/ctypes" + ) + + func errResp(code errCode, format string, v ...interface{}) error { +@@@ -61,10 -48,10 +49,15 @@@ type chainReader interface + // lesCommons contains fields needed by both server and client. + type lesCommons struct { + genesis common.Hash +++<<<<<<< HEAD + + config *eth.Config + + chainConfig ctypes.ChainConfigurator +++======= ++ config *ethconfig.Config ++ chainConfig *params.ChainConfig +++>>>>>>> c2d2f4ed8f + iConfig *light.IndexerConfig +- chainDb ethdb.Database ++ chainDb, lesDb ethdb.Database + chainReader chainReader + chtIndexer, bloomTrieIndexer *core.ChainIndexer + oracle *checkpointoracle.CheckpointOracle +diff --cc light/txpool.go +index d0c92834ee,1296389e3b..0000000000 +--- a/light/txpool.go ++++ b/light/txpool.go +@@@ -31,8 -31,7 +31,12 @@@ import + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" +++<<<<<<< HEAD + + "github.com/ethereum/go-ethereum/params/types/ctypes" + + "github.com/ethereum/go-ethereum/rlp" +++======= ++ "github.com/ethereum/go-ethereum/params" +++>>>>>>> c2d2f4ed8f + ) + + const ( +@@@ -68,8 -67,8 +72,13 @@@ type TxPool struct + mined map[common.Hash][]*types.Transaction // mined transactions by block hash + clearIdx uint64 // earliest block nr that can contain mined tx info + +++<<<<<<< HEAD + + eip2f bool + + eip2028f bool +++======= ++ istanbul bool // Fork indicator whether we are in the istanbul stage. ++ eip2718 bool // Fork indicator whether we are in the eip2718 stage. +++>>>>>>> c2d2f4ed8f + } + + // TxRelayBackend provides an interface to the mechanism that forwards transacions +@@@ -88,10 -87,10 +97,14 @@@ type TxRelayBackend interface + } + + // NewTxPool creates a new light transaction pool + -func NewTxPool(config *params.ChainConfig, chain *LightChain, relay TxRelayBackend) *TxPool { + +func NewTxPool(config ctypes.ChainConfigurator, chain *LightChain, relay TxRelayBackend) *TxPool { + pool := &TxPool{ + config: config, +++<<<<<<< HEAD + + signer: types.NewEIP155Signer(config.GetChainID()), +++======= ++ signer: types.LatestSigner(config), +++>>>>>>> c2d2f4ed8f + nonce: make(map[common.Address]uint64), + pending: make(map[common.Hash]*types.Transaction), + mined: make(map[common.Hash][]*types.Transaction), +@@@ -332,7 -313,8 +345,12 @@@ func (pool *TxPool) setNewHead(head *ty + + // Update fork indicator by next pending block number + next := new(big.Int).Add(head.Number, big.NewInt(1)) +++<<<<<<< HEAD + + pool.eip2028f = pool.config.IsEnabled(pool.config.GetEIP2028Transition, next) +++======= ++ pool.istanbul = pool.config.IsIstanbul(next) ++ pool.eip2718 = pool.config.IsBerlin(next) +++>>>>>>> c2d2f4ed8f + } + + // Stop stops the light transaction pool +@@@ -400,7 -382,7 +418,11 @@@ func (pool *TxPool) validateTx(ctx cont + } + + // Should supply enough intrinsic gas +++<<<<<<< HEAD + + gas, err := core.IntrinsicGas(tx.Data(), tx.To() == nil, pool.eip2f, pool.eip2028f) +++======= ++ gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul) +++>>>>>>> c2d2f4ed8f + if err != nil { + return err + } +diff --cc miner/worker.go +index d260d9cec5,2cee6af0c3..0000000000 +--- a/miner/worker.go ++++ b/miner/worker.go +@@@ -643,8 -651,10 +652,14 @@@ func (w *worker) makeCurrent(parent *ty + if err != nil { + return err + } ++ state.StartPrefetcher("miner") ++ + env := &environment{ +++<<<<<<< HEAD + + signer: types.NewEIP155Signer(w.chainConfig.GetChainID()), +++======= ++ signer: types.MakeSigner(w.chainConfig, header.Number), +++>>>>>>> c2d2f4ed8f + state: state, + ancestors: mapset.NewSet(), + family: mapset.NewSet(), +@@@ -858,17 -876,6 +881,20 @@@ func (w *worker) commitNewWork(interrup + if parent.Time() >= uint64(timestamp) { + timestamp = int64(parent.Time() + 1) + } +++<<<<<<< HEAD + + // this will ensure we're not going off too far in the future + + if now := time.Now().Unix(); timestamp > now+1 { + + wait := time.Duration(timestamp-now) * time.Second + + logFn := log.Trace + + if w.isRunning() { + + logFn = log.Info + + } + + logFn("Mining too far in the future", "wait", common.PrettyDuration(wait)) + + time.Sleep(wait) + + } + + +++======= +++>>>>>>> c2d2f4ed8f + num := parent.Number() + header := &types.Header{ + ParentHash: parent.Hash(), +diff --cc miner/worker_test.go +index ce9e9d80f4,0fe62316e1..0000000000 +--- a/miner/worker_test.go ++++ b/miner/worker_test.go +@@@ -80,13 -77,29 +80,38 @@@ func init() + testTxPoolConfig.Journal = "" + ethashChainConfig = params.TestChainConfig + cliqueChainConfig = params.TestChainConfig +++<<<<<<< HEAD + + cliqueChainConfig.MustSetConsensusEngineType(ctypes.ConsensusEngineT_Clique) + + cliqueChainConfig.SetCliquePeriod(10) + + cliqueChainConfig.SetCliqueEpoch(30000) + + tx1, _ := types.SignTx(types.NewTransaction(0, testUserAddress, big.NewInt(1000), vars.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) + + pendingTxs = append(pendingTxs, tx1) + + tx2, _ := types.SignTx(types.NewTransaction(1, testUserAddress, big.NewInt(1000), vars.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) +++======= ++ cliqueChainConfig.Clique = ¶ms.CliqueConfig{ ++ Period: 10, ++ Epoch: 30000, ++ } ++ ++ signer := types.LatestSigner(params.TestChainConfig) ++ tx1 := types.MustSignNewTx(testBankKey, signer, &types.AccessListTx{ ++ ChainID: params.TestChainConfig.ChainID, ++ Nonce: 0, ++ To: &testUserAddress, ++ Value: big.NewInt(1000), ++ Gas: params.TxGas, ++ }) ++ pendingTxs = append(pendingTxs, tx1) ++ ++ tx2 := types.MustSignNewTx(testBankKey, signer, &types.LegacyTx{ ++ Nonce: 1, ++ To: &testUserAddress, ++ Value: big.NewInt(1000), ++ Gas: params.TxGas, ++ }) +++>>>>>>> c2d2f4ed8f + newTxs = append(newTxs, tx2) ++ + rand.Seed(time.Now().UnixNano()) + } + +diff --cc mobile/geth.go +index 1f3e39191d,704d432e04..0000000000 +--- a/mobile/geth.go ++++ b/mobile/geth.go +@@@ -24,8 -24,9 +24,13 @@@ import + "fmt" + "path/filepath" + +++<<<<<<< HEAD + + "github.com/ethereum/go-ethereum/eth" +++======= ++ "github.com/ethereum/go-ethereum/core" +++>>>>>>> c2d2f4ed8f + "github.com/ethereum/go-ethereum/eth/downloader" ++ "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/ethstats" + "github.com/ethereum/go-ethereum/internal/debug" +@@@ -195,10 -182,10 +200,10 @@@ func NewNode(datadir string, config *No + } + // Register the Ethereum protocol if requested + if config.EthereumEnabled { +- ethConf := eth.DefaultConfig ++ ethConf := ethconfig.Defaults + ethConf.Genesis = genesis + ethConf.SyncMode = downloader.LightSync + - ethConf.NetworkId = uint64(config.EthereumNetworkID) + + ethConf.NetworkId = *genesis.Config.GetNetworkID() + ethConf.DatabaseCache = config.EthereumDatabaseCache + lesBackend, err := les.New(rawStack, ðConf) + if err != nil { +diff --cc mobile/params.go +index ee461d065a,0fc197c9e5..0000000000 +--- a/mobile/params.go ++++ b/mobile/params.go +@@@ -21,7 -21,8 +21,12 @@@ package get + import ( + "encoding/json" + +++<<<<<<< HEAD + + "github.com/ethereum/go-ethereum/p2p/discv5" +++======= ++ "github.com/ethereum/go-ethereum/core" ++ "github.com/ethereum/go-ethereum/p2p/enode" +++>>>>>>> c2d2f4ed8f + "github.com/ethereum/go-ethereum/params" + ) + +diff --cc params/alloc.go +index 5d6d5692b3,5b0e933d7a..0000000000 +--- a/params/alloc.go ++++ b/params/alloc.go +@@@ -12,13 -12,17 +12,21 @@@ + // 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 . + +// along with the multi-geth library. If not, see . + + -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. + +package params + + // nolint: misspell +++<<<<<<< HEAD:params/alloc.go + +const MainnetAllocData = "\xfa\x04]X\u0793\r\x83b\x011\x8e\u0189\x9agT\x06\x908'\x80t2\x80\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0793\x17bC\x0e\xa9\u00e2nWI\xaf\xdbp\xda_x\u077b\x8c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0793\x1d\x14\x80K9\x9cn\xf8\x0edWoev`\x80O\xec\v\x89\u3bb5sr@\xa0\x00\x00\u07932@5\x87\x94{\x9f\x15b*h\xd1\x04\xd5M3\xdb\xd1\u0349\x043\x87Oc,\xc6\x00\x00\u0793I~\x92\xcd\xc0\xe0\xb9c\xd7R\xb2)j\u02c7\u0682\x8b$\x89\n\x8fd\x9f\xe7\xc6\x18\x00\x00\u0793K\xfb\xe1Tk\xc6\xc6[\\~\xaaU0K8\xbb\xfe\xc6\u04c9lk\x93[\x8b\xbd@\x00\x00\u0793Z\x9c\x03\xf6\x9d\x17\xd6l\xbb\x8a\xd7!\x00\x8a\x9e\xbb\xb86\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u0793]\x0e\xe8\x15^\xc0\xa6\xffh\bU,\xa5\xf1k\xb5\xbe2:\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u0793v\"\xd8J#K\xb8\xb0x#\x0f\u03c4\xb6z\u9a2c\xae\x89%\xe1\xccQ\x99R\xf8\x00\x00\u0793{\x9f\xc3\x19\x05\xb4\x99K\x04\xc9\xe2\xcf\xdc^'pP?B\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u0793\u007fJ#\xca\x00\xcd\x04=%\u0088\x8c\x1a\xa5h\x8f\x81\xa3D\x89)\xf0\xa9[\xfb\xf7)\x00\x00\u0793\x869\u06bb\u3bac\x88{]\xc0\xe4>\x13\xbc\u0487\xd7l\x89\x10\xd0\xe3\xc8}n,\x00\x00\u0793\x89P\x86y\xab\xf8\xc7\x1b\xf6x\x16\x87\x12\x0e>j\x84XM\x89a\x94\x04\x9f0\xf7 \x00\x00\u0793\x8f\xc7\u02ed\xff\xbd\r\u007f\xe4O\x8d\xfd`\xa7\x9dr\x1a\x1c\x9c\x8965\u026d\xc5\u07a0\x00\x00\u0793\x95`\xa3\xdebxh\xf9\x1f\xa8\xbf\xe1\xc1\xb7\xaf\xaf\b\x18k\x89\x1cg\xf5\xf7\xba\xa0\xb0\x00\x00\u0793\x96\x97G\xf7\xa5\xb3\x06E\xfe\x00\xe4I\x01CZ\xce$\xcc7\x89\\(=A\x03\x94\x10\x00\x00\u0793\x9am}\xb3&g\x9bw\xc9\x03\x91\xa7Gm#\x8f;\xa3>\x89\n\xdaUGK\x814\x00\x00\u0793\x9e\xef\n\b\x86\x05n?i!\x18S\xb9\xb7E\u007f7\x82\u4262\xa8x\x06\x9b(\xe0\x00\x00\u0793\x9f\xdb\xf4N\x1fJcb\xb7i\u00daG_\x95\xa9l+\u01c9\x1e\x93\x12\x83\xcc\xc8P\x00\x00\u07d3\xa5y\u007fR\xc9\u054f\x18\x9f6\xb1\xd4]\x1b\xf6\x04\x1f/k\x8a\x01'\u0473F\x1a\xcd\x1a\x00\x00\u0793\xaaS\x81\xb2\x13\x8e\xbe\xff\xc1\x91\xd5\xd8\u00d1u;p\x98\u04895\xab\xb0\x9f\xfe\u07b6\x80\x00\u0793\xaa\xda%\xea\"\x86p\x9a\xbbB-A\x92?\u04c0\xcd\x04\u01c9#=\xf3)\x9far\x00\x00\u0793\xac\xbf\xb2\xf2ZT\x85\xc79\xefp\xa4N\xee\xeb|e\xa6o\x89\x05k\xc7^-c\x10\x00\x00\u07d3\xac\xc6\xf0\x82\xa4B\x82\x87d\xd1\x1fX\u0589J\xe4\b\xf0s\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u0793\xb2w\xb0\x99\xa8\xe8f\xca\x0e\xc6[\u02c7(O\xd1B\xa5\x82\x89j\xcb=\xf2~\x1f\x88\x00\x00\u0753\xbd\xd4\x01:\xa3\x1c\x04al+\xc9x_'\x88\xf9\x15g\x9b\x88\xb9\xf6]\x00\xf6<\x00\x00\u0793\xc2}c\xfd\xe2K\x92\xee\x8a\x1e~\xd5\xd2m\x8d\xc5\xc8;\x03\x89lk\x93[\x8b\xbd@\x00\x00\u0553\xc4\x0f\xe2\tT#P\x9b\x9f\u0677T21X\xaf#\x10\xf3\x80\u0793\xd7^\xd6\fwO\x8b:ZQs\xfb\x183\xadq\x05\xa2\u0649l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d3\u05cd\x89\xb3_G'\x16\xec\xea\xfe\xbf`\x05'\u04e1\xf9i\x8a\x05\xe0T\x9c\x962\xe1\xd8\x00\x00\u0793\xda\xe2{5\v\xae \xc5e!$\xaf]\x8b\\\xba\x00\x1e\xc1\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u0793\xdc\x01\xcb\xf4Ix\xa4.\x8d\xe8\xe46\xed\xf9B\x05\u03f6\xec\x89O\x0f\xeb\xbc\u068c\xb4\x00\x00\u07d3\u607c-\x10\xdbb\u0785\x84\x83$I\"P4\x8e\x90\xbf\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d3\xf4c\xe17\xdc\xf6%\xfb\U000fc8de\u0298\u04b9h\xcf\u007f\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\x01\x00\a9K\x8bue\xa1e\x8a\xf8\x8c\xe4cI\x915\u05b7\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x01\r\xf1\xdfK\xed#v\r-\x1c\x03x\x15\x86\xdd\xf7\x91\x8eT\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\x01\x0fJ\x98\u07e1\xd9y\x9b\xf5\u01d6\xfbU\x0e\xfb\xe7\xec\xd8w\x8a\x01\xb2\xf2\x92#b\x92\xc7\x00\x00\u07d4\x01\x15PW\x00/k\r\x18\xac\xb98\x8d;\xc8\x12\x9f\x8fz \x89H\xa4\xa9\x0f\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01k`\xbbmg\x92\x8c)\xfd\x03\x13\xc6f\u068f\x16\x98\xd9\u0149lk\x93[\x8b\xbd@\x00\x00\u07d4\x01l\x85\xe1a;\x90\x0f\xa3W\xb8(;\x12\x0ee\xae\xfc\xdd\b\x89+]\x97\x84\xa9|\xd5\x00\x00\u07d4\x01\x84\x92H\x8b\xa1\xa2\x924\"G\xb3\x18U\xa5Y\x05\xfe\xf2i\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x01\x8f \xa2{'\xecD\x1a\xf7#\xfd\x90\x99\xf2\u02f7\x9dbc\x89uy*\x8a\xbd\xef|\x00\x00\u07d4\x01\x91\xebT~{\xf6\x97k\x9b\x1bWuFv\x1d\xe6V\"\xe2\x89lkLM\xa6\u077e\x00\x00\u07d4\x01\x9dp\x95y\xffK\xc0\x9f\xdc\xdd\xe41\xdc\x14G\xd2\xc2`\xbc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x01\xa2Z_Z\xf0\x16\x9b0\x86L;\xe4\xd7V<\xcdD\xf0\x9e\x89M\x85<\x8f\x89\b\x98\x00\x00\xe0\x94\x01\xa7\xd9\xfa}\x0e\xb1\x18\\g\xe5M\xa8<.u\xdbi\u37ca\x01\x9dJ\xdd\xd0\u063c\x96\x00\x00\u07d4\x01\xa8\x18\x13ZAB\x10\xc3|b\xb6%\xac\xa1\xa5F\x11\xac6\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\x01\xb1\xca\xe9\x1a;\x95Y\xaf\xb3<\xdcmh\x94B\xfd\xbf\xe07\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01\xb5\xb5\xbcZ\x11\u007f\xa0\x8b4\xed\x1d\xb9D\x06\bYz\xc5H\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01\xbb\xc1Og\xaf\x069\xaa\xb1D\x1ej\b\xd4\xceqb\t\x0f\x89F\xfc\xf6\x8f\xf8\xbe\x06\x00\x00\xe0\x94\x01\xd08\x15\xc6\x1fAkq\xa2a\n-\xab\xa5\x9f\xf6\xa6\xde[\x8a\x02\x05\xdf\xe5\v\x81\xc8.\x00\x00\u07d4\x01\u0559\xee\r_\x8c8\xab-9.,e\xb7L<\xe3\x18 \x89\x1b\xa5\xab\xf9\xe7y8\x00\x00\u07d4\x01\xe4\x05!\x12%0\u066c\x91\x11<\x06\xa0\x19\vmc\x85\v\x89Hz\x9a0E9D\x00\x00\u07d4\x01\xe6A]X{\x06T\x90\xf1\xed\u007f!\xd6\xe0\xf3\x86\xeegG\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x01\xe8d\xd3Tt\x1bB>oB\x85\x17$F\x8ct\xf5\xaa\x9c\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x01\xed_\xba\x8d.\xabg:\xec\x04-0\xe4\xe8\xa6\x11\xd8\xc5Z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01\xfb\x8e\xc1$%\xa0O\x81>F\xc5L\x05t\x8c\xa6\xb2\x9a\xa9\x89\x0e\x15s\x03\x85F|\x00\x00\u07d4\x01\xff\x1e\xb1\u07adP\xa7\xf2\xf9c\x8f\xde\xe6\xec\xcf:{*\u0209 \x86\xac5\x10R`\x00\x00\u07d4\x02\x03b\u00ed\xe8x\u0290\u05b2\u0609\xa4\xccU\x10\xee\xd5\xf3\x898\x88\xe8\xb3\x11\xad\xb3\x80\x00\u07d4\x02\x03\xae\x01\xd4\xc4\x1c\xae\x18e\xe0K\x1f[S\xcd\xfa\xec\xae1\x896\x89\xcd\u03b2\x8c\xd7\x00\x00\u07d4\x02\b\x93a\xa3\xfetQ\xfb\x1f\x87\xf0\x1a-\x86fS\xdc\v\a\x89\x02*\xc7H2\xb5\x04\x00\x00\u07d4\x02\x1fi\x04=\xe8\x8cI\x17\xca\x10\xf1\x84(\x97\xee\xc0X\x9c|\x89kD\u03f8\x14\x87\xf4\x00\x00\u07d4\x02)\x0f\xb5\xf9\xa5\x17\xf8(E\xac\xde\xca\x0f\xc8F\x03\x9b\xe23\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x029\xb4\xf2\x1f\x8e\x05\xcd\x01Q++\xe7\xa0\xe1\x8am\x97F\a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x02Gr\x12\xff\xddu\xe5\x15VQ\xb7e\x06\xb1dfq\xa1\xeb\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x02J\t\x8a\xe7\x02\xbe\xf5@l\x9c\"\xb7\x8b\xd4\xeb,\u01e2\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x02K\xdd,{\xfdP\x0e\xe7@O\u007f\xb3\xe9\xfb1\xdd \xfb\u0449\t\xc2\x00vQ\xb2P\x00\x00\u07d4\x02Sg\x96\x03\x04\xbe\xee4Y\x11\x18\xe9\xac-\x13X\xd8\x02\x1a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x02V\x14\x9f[Pc\xbe\xa1N\x15f\x1f\xfbX\xf9\xb4Y\xa9W\x89&)\xf6n\fS\x00\x00\x00\u07d4\x02`=z;\xb2\x97\xc6|\x87~]4\xfb\u0579\x13\xd4\xc6:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x02a\xad:\x17*\xbf\x13\x15\xf0\xff\xec2p\x98j\x84\t\xcb%\x89\v\b!;\u03cf\xfe\x00\x00\u07d4\x02d2\xaf7\xdcQ\x13\xf1\xf4mH\nM\u0c80R#~\x89\x13I\xb7\x86\xe4\v\xfc\x00\x00\u07d4\x02f\xab\x1ck\x02\x16#\v\x93\x95D=_\xa7^hEh\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x02u\x1d\u018c\xb5\xbdsp'\xab\xf7\u0777s\x90\xcdw\xc1k\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x02w\x8e9\x0f\xa1u\x10\xa3B\x8a\xf2\x87\fBsT}8l\x8a\x03lw\x80\x18\x8b\xf0\xef\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x037|\x0eUkd\x01\x03(\x9aa\x89\u1baecI4g\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03IcM\u00a9\xe8\f?w!\xee+PF\xae\xaa\xed\xfb\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x03U\xbc\xac\xbd!D\x1e\x95\xad\xee\xdc0\xc1r\x18\u0224\b\u0389\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x03n\xef\xf5\xba\x90\xa6\x87\x9a\x14\xdf\xf4\xc5\x04;\x18\xca\x04`\u0249\x05k\xc7^-c\x10\x00\x00\xe0\x94\x03qKA\u04a6\xf7Q\x00\x8e\xf8\xddM+)\xae\u02b8\xf3n\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x03r\xe8RX.\t44J\x0f\xed!x0M\xf2]F(\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03r\xeeU\b\xbf\x81c\xed(N^\xef\x94\xceMsg\xe5\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x03}\xd0V\xe7\xfd\xbdd\x1d\xb5\xb6\xbe\xa2\xa8x\n\x83\xfa\u1009\a\x96\xe3\xea?\x8a\xb0\x00\x00\xe0\x94\x03\x83#\xb1\x84\xcf\xf7\xa8*\xe2\u1f67y?\xe41\x9c\xa0\xbf\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03\x87y\xca-\xbef>c\xdb?\xe7V\x83\xea\x0e\xc6.#\x83\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\x03\x8eE\xea\xdd=\x88\xb8\u007f\xe4\u06b0fh\x05\"\xf0\xdf\xc8\xf9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x03\x92T\x9ar\u007f\x81eT)\u02d2\x8bR\x9f%\xdfM\x13\x85\x89\x01lC\xa0\xee\xa0t\x00\x00\u07d4\x03\x94\xb9\x0f\xad\xb8`O\x86\xf4?\xc1\xe3]1$\xb3*Y\x89\x89)j\xa1@'\x8ep\x00\x00\u0794\x03\x9ezN\xbc(N,\xcdB\xb1\xbd\xd6\v\xd6Q\x1c\x0fw\x06\x88\xf0\x15\xf2W6B\x00\x00\u07d4\x03\x9e\xf1\xceR\xfeyc\xf1f\u0562u\u0131\x06\x9f\xe3\xa82\x89\x15\xaf9\u4ab2t\x00\x00\u07d4\x03\xa2l\xfcL\x181op\u055e\x9e\x1ay\xee>\x8b\x96/L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x03\xaab(\x81#m\xd0\xf4\x94\f$\xc3$\xff\x8b{~!\x86\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4\x03\xafz\xd9\xd5\"<\xf7\xc8\xc1? \xdfg\xeb\xe5\xff\u017bA\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x03\xb0\xf1|\xd4F\x9d\xdc\u03f7\xdai~\x82\xa9\x1a_\x9ewt\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x03\xb4\x1bQ\xf4\x1d\xf2\r\xd2y\xba\xe1\x8c\x12w_w\xadw\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x03\xbe[F)\xae\xfb\xbc\xab\x9d\xe2m9Wl\xb7\xf6\x91\xd7d\x89\n\xdf0\xbap\u0217\x00\x00\u07d4\x03\xc6G\xa9\xf9)\xb0x\x1f\xe9\xae\x01\u02a3\xe1\x83\xe8vw~\x89\x18*\xb7\xc2\f\xe5$\x00\x00\u07d4\x03\xc9\x1d\x92\x946\x03\xe7R >\x054\x0eV`\x13\xb9\x00E\x89+|\xc2\xe9\xc3\"\\\x00\x00\u07d4\x03\xcbLOE\x16\xc4\xffy\xa1\xb6$O\xbfW.\x1c\u007f\xeay\x89\x94\x89#z\u06daP\x00\x00\u07d4\x03\u02d8\u05ec\xd8\x17\u079d\x88m\"\xfa\xb3\xf1\xb5}\x92\xa6\b\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x03\u031d-!\xf8k\x84\xac\x8c\xea\xf9q\u06e7\x8a\x90\xe6%p\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\x03\xd1rO\xd0\x0eT\xaa\xbc\xd2\xde*\x91\xe8F+\x10I\xdd:\x89\x8f\x1d\\\x1c\xae7@\x00\x00\u07d4\x03\xde\xdf\xcd\v<.\x17\xc7\x05\xda$\x87\x90\uf626\xbdWQ\x89Hz\x9a0E9D\x00\x00\u07d4\x03\u8c04SuW\xe7\t\xea\xe2\xe1\u1966\xbc\xe1\xef\x83\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x03\xeam&\u0400\xe5z\xee9&\xb1\x8e\x8e\xd7:N[(&\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x03\xeb<\xb8`\xf6\x02\x8d\xa5T\xd3D\xa2\xbbZP\n\xe8\xb8o\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x03\xeb\xc6?\xdaf`\xa4e\x04^#_\xben\\\xf1\x95s_\x89\a\xb0l\xe8\u007f\xddh\x00\x00\xe0\x94\x03\xefj\xd2\x0f\xf7\xbdO\x00+\xacX\xd4uD\u03c7\x9a\xe7(\x8a\x01u\xc7X\u0439n\\\x00\x00\u07d4\x03\xf7\xb9 \b\x81:\xe0\xa6v\xeb!(\x14\xaf\xab5\"\x10i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\x11p\xf5\x81\u0780\xe5\x8b*\x04\\\x8f|\x14\x93\xb0\x01\xb7\u02c90\xc8\xeca2\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\x04i\xe8\xc4@E\v\x0eQ&&\xfe\x81~gT\xa8\x15(0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04m'K\x1a\xf6\x15\xfbPZvJ\xd8\u0767p\xb1\xdb/=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x04}Z&\u05ed\x8f\x8ep`\x0fp\xa3\x98\u076a\x1c-\xb2o\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x04~\x87\xc8\xf7\xd1\xfc\xe3\xb0\x13S\xa8Xb\xa9H\xac\x04\x9f>\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x04\u007f\x9b\xf1R\x9d\xaf\x87\xd4\a\x17^o\x17\x1b^Y\xe9\xff>\x89#<\x8f\xe4'\x03\xe8\x00\x00\xe0\x94\x04\x85'2\xb4\xc6R\xf6\xc2\u53b3e\x87\xe6\nb\xda\x14\u06ca\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x04\x8a\x89p\xeaAE\xc6MU\x17\xb8\xde[F\xd0YZ\xad\x06\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x04\x9c]K\xc6\xf2]NEli{R\xa0x\x11\xcc\u045f\xb1\x89\x10D\x00\xa2G\x0eh\x00\x00\u07d4\x04\xa1\xca\xda\x1c\xc7Q\b/\xf8\u0692\x8e<\xfa\x00\b \xa9\xe9\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x04\xa8\n\xfa\xd5>\xf1\xf8Ae\xcf\xd8R\xb0\xfd\xf1\xb1\xc2K\xa8\x89\x03$\xe9d\xb3\xec\xa8\x00\x00\u07d4\x04\xaa\xfc\x8a\xe5\xceoI\x03\u021d\u007f\xac\x9c\xb1\x95\x12\"Gw\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x04\xbaK\xb8q@\x02,!Jo\xacB\xdbZ\x16\u0755@E\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x04\xba\x8a?\x03\xf0\x8b\x89P\x95\x99M\xdaa\x9e\u06ac\xee>z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\xc2\xc6K\xb5L>\xcc\xd0U\x85\xe1\x0e\xc6\xf9\x9a\f\xdb\x01\xa3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x04\xceE\xf6\x00\xdb\x18\xa9\u0405\x1b)\xd99>\xbd\xaa\xfe=\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x04\u05b8\xd4\u0686t\a\xbb\x99wI\u07bb\xcd\xc0\xb3XS\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x04\xd78\x96\xcfe\x93\xa6\x91\x97*\x13\xa6\xe4\x87\x1f\xf2\xc4+\x13\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\xd8*\xf9\xe0\x1a\x93m\x97\xf8\xf8Y@\xb9p\xf9\xd4\u06d96\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x04\xe5\xf5\xbc|\x92?\xd1\xe3\x175\xe7.\xf9h\xfdg\x11\fn\x89WU\x1d\xbc\x8ebL\x00\x00\u07d4\x04\xec\xa5\x01c\n\xbc\xe3R\x18\xb1t\x95k\x89\x1b\xa2^\xfb#\x8966\x9e\xd7t}&\x00\x00\u07d4\x05\x05\xa0\x8e\"\xa1\t\x01Z\"\xf6\x850STf*U1\u0549\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\x05\x14\x95L\xe8\x81\xc807\x03d\x00\x89lO\xd1\xee$nx\x00\x00\u07d4\x05\x1dBBv\xb2\x129fQ\x86\x13=e;\xb8\xb1\x86/\x89\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x05!\xbc:\x9f\x87\x11\xfe\xcb\x10\xf5\a\x97\xd7\x10\x83\xe3A\ub749\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x05#mL\x90\xd0e\xf9\u34c3X\xaa\xff\xd7w\xb8j\xecI\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x05*X\xe05\xf1\xfe\x9c\xdd\x16\x9b\xcf \x97\x03E\xd1+\x9cQ\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x05.\xab\x1fa\xb6\xd4U\x17(?A\xd1D\x18$\x87\x87I\u0409\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x053n\x9ar'(\xd9c\xe7\xa1\xcf'Y\xfd\x02tS\x0f\u02891\xa2D?\x88\x8ay\x80\x00\u07d4\x054q\u035aA\x92[9\x04\xa5\xa8\xff\xca6Y\xe04\xbe#\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4\x056\x1d\x8e\xb6\x94\x1dN\x90\xfb~\x14\x18\xa9Z2\xd5%w2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x05B:T\xc8\xd0\xf9p~pAs\xd9#\xb9F\xed\xc8\xe7\x00\x89\x06\xea\x03\u00bf\x8b\xa5\x80\x00\u07d4\x05D\f[\a;R\x9bH) \x9d\xff\x88\t\x0e\a\xc4\xf6\xf5\x89E\u04977\xe2/ \x00\x00\u07d4\x05Z\xb6X\xc6\xf0\xedO\x87^\xd6t.K\xc7)-\x1a\xbb\xf0\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\u07d4\x05[\xd0,\xaf\x19\xd6 +\xbc\u0703m\x18{\xd1\xc0\x1c\xf2a\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x05^\xacO\x1a\xd3\xf5\x8f\v\xd0$\u058e\xa6\r\xbe\x01\u01af\xb3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x05fQU\xccI\xcb\xf6\xaa\xbd\u056e\x92\xcb\xfa\xad\x82\xb8\xc0\xc1\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x05f\x86\a\x8f\xb6\xbc\xf9\xba\n\x8a\x8d\xc6:\x90o_\xea\xc0\xea\x89\x1b\x18\x1eK\xf24<\x00\x00\u07d4\x05iks\x91k\xd3\x03>\x05R\x1e2\x11\xdf\xec\x02n\x98\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x05k\x15F\x89O\x9a\x85\xe2\x03\xfb3m\xb5i\xb1l%\xe0O\x89\t.\xdb\t\xff\b\u0600\x00\u07d4\x05yI\xe1\xca\x05pF\x9eL\xe3\u0190\xaea:k\x01\xc5Y\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x05}\u049f-\x19\xaa=\xa4#'\xeaP\xbc\xe8o\xf5\xc9\x11\u0649\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x05\u007f\u007f\x81\xcdz@o\xc4Y\x94@\x8bPI\x91,Vdc\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x05\x91]N\"Zf\x81b\xae\xe7\xd6\xc2_\xcf\xc6\xed\x18\xdb\x03\x89\x03\x98\xc3ry%\x9e\x00\x00\u07d4\x05\x96\xa2}\xc3\xee\x11_\xce/\x94\xb4\x81\xbc z\x9e&\x15%\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x05\xa80rC\x02\xbc\x0fn\xbd\xaa\x1e\xbe\xee\xb4nl\xe0\v9\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\x05\xae\u007f\u053b\u0300\xca\x11\xa9\n\x1e\u01e3\x01\xf7\xcc\u0303\u06c91T\xc9r\x9d\x05x\x00\x00\u07d4\x05\xbbd\xa9\x16\xbef\xf4`\xf5\xe3\xb6C2\x11\r \x9e\x19\xae\x89\u3bb5sr@\xa0\x00\x00\xe0\x94\x05\xbfO\xcf\xe7r\xe4[\x82dC\x85.l5\x13P\xcer\xa2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\x05\xc6@\x04\xa9\xa8&\xe9N^N\xe2g\xfa*v2\xddNo\x8a\x03m\xc4.\xbf\xf9\v\u007f\x80\x00\xe0\x94\x05\xc76\xd3e\xaa7\xb5\xc0\xbe\x9c\x12\u022d\\\xd9\x03\xc3,\xf9\x8a\x01E^{\x80\n\x86\x88\x00\x00\xe0\x94\x05\xcbl;\x00r\xd3\x11ga\xb52\xb2\x18D;S\xe8\xf6\u014a\x1e\x02\xc3\xd7\xfc\xa9\xb6(\x00\x00\u07d4\x05\xd0\xf4\xd7(\xeb\xe8.\x84\xbfYu\x15\xadA\xb6\v\xf2\x8b9\x89\u3bb5sr@\xa0\x00\x00\u07d4\x05\u058d\xada\u04fb\u07f3\xf7y&\\IGJ\xff?\xcd0\x89\x02\"\xc5]\xc1Q\x9d\x80\x00\u07d4\x05\xe6q\xdeU\xaf\xec\x96K\aM\xe5t\xd5\x15\x8d]!\xb0\xa3\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x05\xe9{\tI,\u058fc\xb1+\x89.\xd1\xd1\x1d\x15,\x0e\u02897\b\xba\xed=h\x90\x00\x00\u07d4\x05\xf3c\x1fVd\xbd\xad]\x012\xc88\x8d6\xd7\u0612\t\x18\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x06\t\xd8:l\xe1\xff\u0276\x90\xf3\xe9\xa8\x1e\x98>\x8b\xdcM\x9d\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\u07d4\x06\x1e\xa4\x87|\u0409D\xebd\u0096n\x9d\xb8\xde\xdc\xfe\xc0k\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x06%\xd0`V\x96\x8b\x00\"\x06\xff\x91\x98\x01@$+\xfa\xa4\x99\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x06(\xbf\xbeU5x/\xb5\x88@k\xc9f`\xa4\x9b\x01\x1a\xf5\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\x061\u044b\xbb\xbd0\xd9\xe1s+\xf3n\xda\xe2\u0389\x01\xab\x80\x89\xa3\xf9\x88U\xec9\x90\x00\x00\u07d4\x061\xdc@\xd7NP\x95\xe3r\x9e\xdd\xf4\x95D\xec\xd49og\x89\b\xacr0H\x9e\x80\x00\x00\xe0\x94\x067Y\xdd\x1cN6.\xb1\x93\x98\x95\x1f\xf9\xf8\xfa\xd1\xd3\x10h\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x06_\xf5u\xfd\x9c\x16\xd3\xcbo\u058f\xfc\x8fH?\xc3.\xc85\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x06a\x8e\x9dWb\xdfb\x02\x86\x01\xa8\x1dD\x87\u05a0\xec\xb8\x0e\x89Hz\x9a0E9D\x00\x00\xe0\x94\x06fG\xcf\xc8]#\xd3v\x05W= \x8c\xa1T\xb2D\xd7l\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x06xeJ\xc6v\x1d\xb9\x04\xa2\xf7\xe8Y^\xc1\xea\xacsC\b\x89/\x98\xb2\x9c(\x18\xf8\x00\x00\u07d4\x06\x86\n\x93RYU\xffbI@\xfa\xdc\xff\xb8\xe1I\xfdY\x9c\x89lh\xcc\u041b\x02,\x00\x00\xe0\x94\x06\x8c\xe8\xbdn\x90*E\u02c3\xb5\x15A\xb4\x0f9\xc4F\x97\x12\x8a\x01\x1c\x0f\x9b\xadJF\xe0\x00\x00\u07d4\x06\x8e)\xb3\xf1\x91\xc8\x12\xa699\x18\xf7\x1a\xb93\xaehG\xf2\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x06\x8eeWf\xb9D\xfb&6\x19e\x87@\xb8P\xc9J\xfa1\x89\x01\xe8\u007f\x85\x80\x9d\xc0\x00\x00\u0794\x06\x96N-\x17\xe9\x18\x9f\x88\xa8 96\xb4\n\xc9nS<\x06\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x06\x99L\xd8:\xa2d\n\x97\xb2`\vA3\x9d\x1e\r>\xdel\x89\r\x8drkqw\xa8\x00\x00\u07d4\x06\x9e\u042bz\xa7}\xe5q\xf1a\x06\x05\x1d\x92\xaf\xe1\x95\xf2\u0409\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x06\xac&\xad\x92\u02c5\x9b\u0550]\xdc\xe4&j\xa0\xecP\xa9\u0149*\x03I\x19\u07ff\xbc\x00\x00\u07d4\x06\xb0\xc1\xe3\u007fZ^\u013b\xf5\b@T\x8f\x9d:\xc0(\x88\x97\x89\xd8\u0602\u148e}\x00\x00\u07d4\x06\xb0\xff\x83@s\xcc\xe1\xcb\xc9\xeaU~\xa8{`Yc\u8d09\x10CV\x1a\x88)0\x00\x00\xe0\x94\x06\xb1\x06d\x9a\xa8\xc4!\xdd\xcd\x1b\x8c2\xcd\x04\x18\xcf0\xda\x1f\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x06\xb5\xed\xe6\xfd\xf1\xd6\xe9\xa3G!7\x9a\xea\xa1|q=\xd8*\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x06\xcb\xfa\b\xcd\xd4\xfb\xa77\xba\xc4\a\xbe\x82$\xf4\xee\xf3X(\x89 +\xe5\xe88.\x8b\x80\x00\u07d4\x06\xd6\xcb0\x84\x81\xc36\xa6\xe1\xa2%\xa9\x12\xf6\xe65Y@\xa1\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x06\xdc\u007f\x18\xce\xe7\xed\xab[yS7\xb1\xdfj\x9e\x8b\u062eY\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x06\xf6\x8d\xe3\xd79\xdbA\x12\x1e\xac\xf7y\xaa\xda=\xe8v!\a\x89\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\x06\xf7\u070d\x1b\x94b\xce\xf6\xfe\xb13h\xa7\xe3\x97K\t\u007f\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\a\x01\xf9\xf1G\xecHhV\xf5\xe1\xb7\x1d\xe9\xf1\x17\xe9\x9e!\x05\x89\te\xdaq\u007f\u0578\x00\x00\u07d4\a\r]6L\xb7\xbb\xf8\"\xfc,\xa9\x1a5\xbd\xd4A\xb2\x15\u0549lk\x93[\x8b\xbd@\x00\x00\xe0\x94\a\x1d\xd9\r\x14\xd4\x1fO\xf7\xc4\x13\xc2B8\xd35\x9c\xd6\x1a\a\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\u07d4\a&\xc4.\x00\xf4T\x04\x83n\xb1\xe2\x80\xd0s\xe7\x05\x96\x87\xf5\x89X\x00>?\xb9G\xa3\x80\x00\xe0\x94\a'\xbe\n*\x00! H\xb5R\x0f\xbe\xfb\x95>\xbc\x9dT\xa0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\a)\xa8\xa4\xa5\xba#\xf5y\xd0\x02[\x1a\xd0\xf8\xa0\xd3\\\xdf\u048a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4\a)\xb4\xb4|\t\xeb\x16\x15\x84d\u022a\u007f\xd9i\vC\x889\x89lh\xcc\u041b\x02,\x00\x00\u0794\a4\xa0\xa8\x1c\x95b\xf4\xd9\xe9\xe1\n\x85\x03\xda\x15\xdbF\xd7n\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\a\xa7\xef[G\x00\x00\xe0\x94\ap\xc6\x1b\xe7\x87r#\f\xb5\xa3\xbb$)\xa7&\x14\xa0\xb36\x8a\x01n\u0899\xb7\x13A\x80\x00\u07d4\ar><0\xe8\xb71\xeeEj)\x1e\xe0\u7630 Jw\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\as\xee\xac\xc0P\xf7G \xb4\xa1\xbdW\x89[\x1c\xce\xebI]\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\a\x80\r/\x80h\xe4H\u01daOi\xb1\xf1^\xf6\x82\xaa\xe5\xf6\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\a\xa8\xda\xde\xc1BW\x1a}S\xa4)pQxm\a,\xbaU\x89\x01;m\xa1\x13\x9b\u0680\x00\u07d4\a\xaf\x93\x8c\x127\xa2|\x900\tM\xcf$\aP$n=,\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\a\xb1\xa3\x06\xcbC\x12\xdffH,,\xaer\xd1\xe0a@\x0f\u034a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\a\xb7\xa5p3\xf8\xf1\x130\xe4f^\x18]#N\x83\xec\x14\v\x89\xea~\xe9*\f\x9a\v\x80\x00\u07d4\a\xbc,\xc8\xee\xdc\x01\x97\a\x00\xef\xc9\xc4\xfb6s^\x98\xcdq\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\a\xd4\x12\x17\xba\u0725\xe0\xe6\x03'\xd8E\xa3FO\x0f'\xf8J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\a\xd43N\u00c5\xe8\xaaT\xee\xda\xea\xdb0\x02/\f\u07e4\xab\x89\x8e\x91\xd5 \xf2\xeby\x00\x00\u07d4\a\xda\xe6\"c\r\x1168\x193\u04adk\"\xb89\xd8!\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\a\xdc+\xf8;\u01af\x19\xa8B\xff\xeaf\x1a\xf5\xb4\x1bg\xfd\xa1\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\a\u070c\x8b\x92z\xdb\xed\xfa\x8f]c\x9bCR5\x1f/6\u0489\x11\n\xed;U0\xdb\x00\x00\u07d4\a\xdd\xd0B,\x86\xefe\xbf\f\u007f\xc3E(b\xb1\"\x8b\b\xb8\x89o\xf5\u04aa\x8f\x9f\xcf\x00\x00\u07d4\a\xe1\x16,\xea\xe3\xcf!\xa3\xf6-\x10Y\x900.0\u007fN;\x89R\xf1\x03\xed\xb6k\xa8\x00\x00\u07d4\a\xe2\xb4\xcd\xee\xd9\u0407\xb1.Um\x9ew\f\x13\xc0\x99a_\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\a\xfe\xefT\xc16\x85\b)\xba\xdcKI\xc3\xf2\xa7<\x89\xfb\x9e\x89\x06hZ\xc1\xbf\xe3,\x00\x00\u07d4\b\x05FP\x8a=&\x82\u0239\x88O\x13c{\x88G\xb4M\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\t\bv\xba\xad\xfe\xe6\\=6;\xa5S\x12t\x8c\xfa\x87=\x89\\*\x997\x1c\xff\xe1\x00\x00\u07d4\b\x16o\x021?\xea\u12f0D\xe7\x87|\x80\x8bU\xb5\xbfX\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\b)\xd0\xf7\xbb|Dl\xfb\xb0\u07ad\xb29M\x9d\xb7$\x9a\x87\x89\x02,\xa3X|\xf4\xeb\x00\x00\u07d4\b0m\xe5\x19\x81\u7b21\x85hY\xb7\xc7xijki\xf9\x89\xadx\xeb\u016cb\x00\x00\x00\xe0\x94\b7S\x9b_jR*H,\xdc\u04e9\xbbpC\xaf9\xbd\u048a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\b8\xa7v\x8d\x9c*\u028b\xa2y\xad\xfe\xe4\xb1\xf4\x91\xe3&\xf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\bA\x16R\xc8qq6\t\xaf\x00b\xa8\xa1(\x1b\xf1\xbb\xcf\u0649K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\bM\x102Tu\x9b4<\xb2\xb9\xc2\xd8\xff\x9e\x1a\xc5\xf1E\x96\x8a\x01\x9b\xff/\xf5yh\xc0\x00\x00\u07d4\bPO\x05d?\xabY\x19\xf5\xee\xa5Y%\u05e3\xed}\x80z\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\b[J\xb7]\x83b\xd9\x14C\\\xed\xee\x1d\xaa+\x1e\xe1\xa2;\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\b[\xa6_\xeb\xe2>\xef\xc2\xc8\x02fj\xb1&#\x82\xcf\u0114\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\bt\x98\xc0FFh\xf3\x11P\xf4\xd3\u013c\u0765\"\x1b\xa1\x02\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\bw\uebabx\xd5\xc0\x0e\x83\xc3+-\x98\xfay\xadQH/\x89\x17\xd2-q\xdab&\x00\x00\u0794\b\x93j7\u07c5\xb3\xa1X\xca\xfd\x9d\xe0!\xf5\x817h\x13G\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\b\xa9\xa4N\x1fA\xde=\xbb\xa7\xa3c\xa3\xabA,\x12L\xd1^\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\b\xb7\xbd\u03d4MUp\x83\x8b\xe7\x04`$:\x86\x94HXX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xb8E6\xb7L\x8c\x01T=\xa8\x8b\x84\u05cb\xb9WG\xd8\"\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\b\xc2\xf26\xacJ\xdc\xd3\xfd\xa9\xfb\xc6\xe4S\"S\xf9\xda;\xec\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\b\xc8\x02\xf8wX4\x9f\xa0>k\xc2\xe2\xfd\a\x91\x19~\ua689lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xc9\U0007fd89\xfd\xf8\x04\xd7i\xf8!#6\x02\x15\xaf\xf9;\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\b\xca\u0215&A\xd8\xfcRn\xc1\xabO-\xf8&\xa5\xe7q\x0f\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\b\xcc\xdaP\xe4\xb2j\x0f\xfc\x0e\xf9.\x92\x051\a\x06\xbe\xc2\u01ca\x01Iul8W\xc6\x00\x00\x00\u07d4\b\u0406M\xc3/\x9a\xcb6\xbfN\xa4G\xe8\xddg&\x90j\x15\x89lnY\xe6|xT\x00\x00\u07d4\b\xd4&\u007f\xeb\x15\u0697\x00\xf7\xcc\xc3\xc8J\x89\x18\xbf\x17\xcf\u0789a\t=|,m8\x00\x00\xe0\x94\b\xd41\x1c\x9c\x1b\xba\xf8\u007f\xab\xe1\xa1\xd0\x14c\x82\x8d]\x98\u038a\x13\x0e\xe8\xe7\x17\x90D@\x00\x00\u07d4\b\xd5N\x83\xadHj\x93L\xfa\xea\u20e3>\xfd\"|\x0e\x99\x898S\x05\x83$^\xdc\x00\x00\u07d4\b\xd9~\xad\xfc\xb7\xb0d\xe1\xcc\xd9\u0217\x9f\xbe\xe5\xe7z\x97\x19\x89\x0el]\xa8\xd6z\xc1\x80\x00\u07d4\b\xda:z\x0fE!a\u03fc\xec1\x1b\xb6\x8e\xbf\xde\xe1~\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xe3\x8e\xe0\xceH\xc9\xcad\\\x10\x19\xf7;SUX\x1cV\xe6\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\b\xef?\xa4\xc4<\xcd\xc5{\"\xa4\xb9\xb23\x1a\x82\xe58\x18\xf2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\t\td\x8c\x18\xa3\xce[\xaez\x04~\xc2\xf8h\xd2L\u0768\x1d\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\t\f\xd6{`\xe8\x1dT\xe7\xb5\xf6\a\x8f>\x02\x1b\xa6[\x9a\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\f\xeb\xef),>\xb0\x81\xa0_\u062a\xf7\u04db\xf0{\x89\u0509\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\t\x0f\xa96{\xdaW\xd0\xd3%:\n\x8f\xf7l\xe0\xb8\xe1\x9as\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\x14n\xa3\x88Qv\xf0w\x82\xe1\xfe0\xdc\xe3\xce$\u011e\x1f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\t!`_\x99\x16N;\xcc(\xf3\x1c\xae\xcex\x971\x82V\x1d\x89+\ai*\x90e\xa8\x00\x00\xe0\x94\t&\x1f\x9a\xcbE\x1c7\x88\x84O\f\x14Q\xa3[\xadP\x98\xe3\x8a\x01\u056d'P) `\x00\x00\xe0\x94\t'\"\x04\x92\x19K.\u069f\u013b\xe3\x8f%\u0581\xdf\xd3l\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\t*\xcbbK\b\xc0U\x10\x18\x9b\xbb\xe2\x1ee$\xd6D\u032d\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\t.\x81UX@-g\xf9\rk\xfem\xa0\xb2\xff\xfa\x91EZ\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\tP0\xe4\xb8&\x92\xdc\xf8\xb8\u0411$\x94\xb9\xb3x\xec\x93(\x89H\xa4zu\x80\x00\u07d4\t\x89\xc2\x00D\v\x87\x89\x91\xb6\x9d`\x95\xdf\xe6\x9e3\xa2.p\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\t\x90\xe8\x1c\u05c5Y\x9e\xa26\xbd\x19f\xcfRc\x02\xc3[\x9c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\x98\xd8'1\x15\xb5j\xf4%\xff\xc8>!\x8c\x1e\n\xfe\x89(\u01c8\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\t\xaeI\xe3\u007f\x12\x1d\xf5\xdc\x15\x8c\xfd\xe8\x06\xf1s\xa0k\f\u007f\x89\xd80\x9e&\xab\xa1\xd0\x00\x00\u07d4\t\xaf\xa7;\xc0G\xefF\xb9w\xfd\x97c\xf8r\x86\xa6\xbeh\u0189\x1b/\xb5\xe8\xf0jf\x00\x00\u07d4\t\xb4f\x86\x96\xf8j\b\x0f\x8b\xeb\xb9\x1d\xb8\xe6\xf8p\x15\x91Z\x89#\x8f\xf7\xb3O`\x01\x00\x00\xe0\x94\t\xb5\x9b\x86\x98\xa7\xfb\xd3\xd2\xf8\xc7:\x00\x89\x88\xde>@k+\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94\t\xb7\xa9\x88\xd1?\xf8\x91\x86so\x03\xfd\xf4au\xb5=\x16\xe0\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\t\xc1w\xf1\xaeD$\x11\u076c\xf1\x87\xd4m\xb9V\x14\x83`\xe7\x8a\x01\xe5.3l\xde\"\x18\x00\x00\xe0\x94\t\u020f\x91~Mj\xd4s\xfa\x12\u93a3\xc4G*^\xd6\u068a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\t\u0438\xcd\a|i\xd9\xf3-\x9c\xcaC\xb3\xc2\b\xa2\x1e\u050b\x89\b!\xd2!\xb5)\x1f\x80\x00\xe0\x94\t\xd6\xce\xfdu\xb0\u0133\xf8\xf1\u0587\xa5\"\xc9a#\xf1\xf59\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\t\xe47\xd4H\x86\x12(\xa22\xb6.\xe8\xd3ye\xa9\x04\ud70a\x04\x98\xcf@\x1d\xf8\x84.\x80\x00\u07d4\t\xee\x12\xb1\xb4+\x05\xaf\x9c\xf2\a\xd5\xfc\xac%[.\xc4\x11\xf2\x89\x031\xcd\xddG\xe0\xfe\x80\x00\u07d4\t\xf3\xf6\x01\xf6\x05D\x11@Xl\xe0eo\xa2J\xa5\xb1\u066e\x89Sswo\xe8\xc4T\x00\x00\u07d4\t\xf9W[\xe5}\x00G\x93\u01e4\ub137\x15\x87\xf9|\xbbj\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n\x06P\x86\x1fx^\xd8\xe4\xbf\x10\x05\xc4P\xbb\xd0n\xb4\x8f\xb6\x89\xa6A;y\x14N~\x00\x00\u07d4\n\x06\xfa\xd7\xdc\u05e4\x92\xcb\xc0S\xee\xab\xdei4\xb3\x9d\x867\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\n\a}\xb1?\xfe\xb0\x94\x84\xc2\x17p\x9dX\x86\xb8\xbf\x9cZ\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\n\x0e\u0366cow\x16\xef\x19saF\x87\xfd\x89\xa8 \xa7\x06\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\n)\xa8\xa4\xd5\xfd\x95\x00u\xff\xb3Mw\xaf\xeb-\x82;\u0589\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n*\u0795\xb2\xe8\xc6m\x8a\xe6\xf0\xbad\xcaW\u05c3\xbemD\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\n+O\xc5\xd8\x1a\xceg\xdcK\xba\x03\xf7\xb4UA=F\xfe=\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\n-\xcbzg\x17\x01\u06f8\xf4\x95r\x80\x88&Xs5l\x8e\x89\b?\x16\xce\b\xa0l\x00\x00\u07d4\n=\xe1U\xd5\xec\xd8\xe8\x1c\x1f\xf9\xbb\xf07\x83\x01\xf8\xd4\xc6#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\nG\xad\x90Y\xa2I\xfc\x93k&b5=\xa6\x90_u\u00b9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\nH)ov1p\x8c\x95\u04b7Iu\xbcJ\xb8\x8a\xc19*\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\nJ\x01\x19\x95\u0181\xbc\x99\x9f\xddyuN\x9a2J\xe3\xb3y\x8a\b\xc1\x9a\xb0n\xb8\x9a\xf6\x00\x00\u07d4\nX\xfd\xddq\x89\x8d\xe7s\xa7O\xda\xe4^{\xd8N\xf46F\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\n[y\xd8\xf2;d\x83\xdb\u2f6ab\xb1\x06L\xc7cf\xae\x89j\u0202\x10\tR\u01c0\x00\u07d4\ne.*\x8bw\xbd\x97\xa7\x90\xd0\xe9\x13a\u0248\x90\u06f0N\x8965\u026d\xc5\u07a0\x00\x00\u07d4\nn\xber;n\xd1\xf9\xa8ji\xdd\xdah\xdcGF\\+\x1b\x89@=-\xb5\x99\xd5\xe4\x00\x00\u07d4\nw\xe7\xf7+C{WO\x00\x12\x8b!\xf2\xac&Q3R\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\n\x91\u007f;\\\xb0\xb8\x83\x04\u007f\u0676Y=\xbc\xd5W\xf4S\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\n\x93\x1bD\x9e\xa8\xf1,\xdb\xd5\xe2\xc8\xccv\xba\xd2\xc2|\x069\x89\x01?\x9e\x8cy\xfe\x05\x80\x00\u0794\n\x98\x04\x13x\x03\xbahh\xd9:U\xf9\x98_\xcdT\x04Q\u4239\x8b\xc8)\xa6\xf9\x00\x00\u07d4\n\x9a\xb2c\x8b\x1c\xfdeM%\u06b0\x18\xa0\xae\xbd\u07c5\xfdU\x89\x01.\x8c\xb5\xfeLJ\x80\x00\u07d4\n\xb3f\xe6\xe7\u056b\xbc\xe6\xb4JC\x8di\xa1\u02bb\x90\xd13\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\n\xb4(\x1e\xbb1\x85\x90\xab\xb8\x9a\x81\xdf\a\xfa:\xf9\x04%\x8a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\n\xb5\x9d9\a\x02\xc9\xc0Y\xdb\x14\x8e\xb4\xf3\xfc\xfa}\x04\xc7\xe7\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\n\xbf\xb3\x9b\x11HmyW(f\x19[\xa2lc\vg\x84\u06ca\x19\xba\x877\xf9i(\xf0\x00\x00\u07d4\n\u029aV&\x91;\b\xcf\u0266m@P\x8d\xceR\xb6\x0f\x87\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\n\xd3\xe4M<\x00\x1f\xa2\x90\xb3\x93ap0TA\b\xacn\xb9\x89j\xbd\xa0\xbc0\xb2\u07c0\x00\u07d4\n\xec.Bn\xd6\xcc\f\xf3\xc2I\xc1\x89~\xacG\xa7\xfa\xa9\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n\xf6_\x14xNU\xa6\xf9Vg\xfds%*\x1c\x94\a-*\x89\nv;\x8e\x02\xd4O\x80\x00\u07d4\n\xf6\xc8\xd59\xc9mP%\x9e\x1b\xa6q\x9e\x9c\x80`\xf3\x88\u008965\u026d\xc5\u07a0\x00\x00\u07d4\v\x069\x0f$7\xb2\x0e\u0123\xd3C\x1b2y\xc6X>^\u05c9\n\x84Jt$\xd9\xc8\x00\x00\u07d4\v\v8b\x11*\xee\u00e04\x92\xb1\xb0_D\x0e\xcaT%n\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\v\x0e\x05[(\xcb\xd0=\xc5\xffD\xaad\xf3\xdc\xe0O^c\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\x11\x9d\xf9\x9ck\x8d\xe5\x8a\x1e,?)zgD\xbfU\"w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\x14\x89\x19\x99\xa6\\\x9e\xf73\b\xef\xe3\x10\f\xa1\xb2\x0e\x81\x92\x89+^:\xf1k\x18\x80\x00\x00\u07d4\v!\x13PE4d*\x1d\xaf\x10.\xee\x10\xb9\xeb\xdev\xe2a\x89\x94,\xdd|\x95\xf2\xbd\x80\x00\xe0\x94\v(\x8aZ\x8bu\xf3\xdcA\x91\xeb\x04W\xe1\xc8=\xbd M%\x8a\x01\a\x14\xe7{\xb4:\xb4\x00\x00\u07d4\v6\x9e\x00.\x1bLy\x13\xfc\xf0\x0f-^\x19\u0141eG\x8f\x89\x03\u007fe\x16(\x8c4\x00\x00\u07d4\vC\xbd#\x91\x02U\x81\u0615l\xe4*\a%y\u02ff\xcb\x14\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u07d4\vP|\xf5SV\x8d\xaa\xf6U\x04\xaeN\xaa\x17\xa8\xea<\xdb\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v]f\xb1<\x87\xb3\x92\xe9M\x91\xd5\xf7l\rE\nU(C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v^ \x11\xeb\xc2Z\x00\u007f!6)`I\x8a\xfb\x8a\xf2\x80\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vd\x9d\xa3\xb9j\x10,\xdcm\xb6R\xa0\xc0}e\xb1\xe4C\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vi \xa6K6;\x8d]\x90\x80$\x94\xcfVKT|C\r\x89A\rXj \xa4\xc0\x00\x00\u07d4\vp\x11\x01\xa4\x10\x9f\x9c\xb3`\xdcW\xb7tBg=^Y\x83\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vq\xf5T\x12$i\uf5ce/\x1f\xef\xd7\u02f4\x10\x98'r\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\xe0\x94\v{\xb3B\xf0\x1b\u0248\x8ej\x9a\xf4\xa8\x87\xcb\xf4\xc2\xdd,\xaf\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\v}3\x93q\xe5\xbeg'\xe6\xe31\xb5\x82\x1f\xa2K\u06ddZ\x89.\u007f\x81\x86\x82b\x01\x00\x00\u07d4\v\u007f\xc9\xdd\xf7\x05v\xf63\x06i\xea\xaaq\xb6\xa81\xe9\x95(\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\v\x80\xfcp(,\xbd\xd5\xfd\xe3[\xf7\x89\x84\xdb;\xdb\x12\x01\x88\x8968\x02\x1c\xec\u06b0\x00\x00\u07d4\v\x92M\xf0\a\xe9\xc0\x87\x84\x17\xcf\xe6;\x97n\xa1\xa3\x82\xa8\x97\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\v\x93\xfc\xa4\xa4\xf0\x9c\xac \xdb`\xe0e\xed\xcc\xcc\x11\u0976\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\v\x9d\xf8\x0f\xbe# \t\xda\xcf\n\xa8\xca\u0153v\xe2Gb\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xa6\xe4j\xf2Z\x13\xf5qi%Z4\xa4\xda\xc7\xce\x12\xbe\x04\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\v\xa8p[\xf5\\\xf2\x19\xc0\x95k^?\xc0\x1cDt\xa6\xcd\xc1\x89\x05%\xe0Y]Mk\x80\x00\u07d4\v\xafn\u0379\x1a\xcb6\x06\xa85|\v\xc4\xf4\\\xfd-~o\x8965\u026d\xc5\u07a0\x00\x00\u07d4\v\xb0_r$\xbbX\x04\x85eV\xc0~\xea\xdb\ud1fa\x8f|\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\v\xb0\xc1&\x82\xa2\xf1\\\x9bWA\xb28\\\xbeA\xf04\x06\x8e\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\v\xb2\\\xa7\u0448\xe7\x1eMi={\x17\a\x17\xd6\xf8\xf0\xa7\n\x89\x12C\x02\xa8/\xad\xd7\x00\x00\u07d4\v\xb2e\x0e\xa0\x1a\xcau[\xc0\xc0\x17\xb6K\x1a\xb5\xa6m\x82\xe3\x89Hz\x9a0E9D\x00\x00\u07d4\v\xb5Lr\xfdf\x10\xbf\xa463\x97\xe0 8K\x02+\fI\x89Hz\x9a0E9D\x00\x00\u07d4\v\xb7\x16\n\xba)7b\xf8sO>\x03&\xff\u0264\xca\xc1\x90\x8965\u026d\xc5\u07a0\x00\x00\u07d4\v\xc9\\\xb3-\xbbWL\x83/\xa8\x17J\x815m8\xbc\x92\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xd6}\xbd\xe0z\x85n\xbd\x89;^\xdcO:[\xe4 &\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xdb\xc5L\u023d\xbb\xb4\x02\xa0\x89\x11\xe2#*T`\u0386k\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\v\xddX\xb9n|\x91m\xd2\xfb05o*\xeb\xfa\xaf\x1d\x860\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\v\u1f39\x03C\xfa\xe501s\xf4a\xbd\x91JH9\x05l\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\v\xe1\xfd\xf6&\xeea\x89\x10-p\xd1;1\x01,\x95\xcd\x1c\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xe2\xb9J\xd9P\xa2\xa6&@\xc3[\xfc\xcdlg\xda\xe4P\xf6\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\v\u681eC\a\xfeH\xd4\x12\xb8\u0461\xa8(M\xceHba\x8a\x04\x0f\xbf\xf8\\\x0180\x00\x00\u07d4\v\xef\xb5G\a\xf6\x1b,\x9f\xb0G\x15\xab\x02n\x1b\xb7 B\xbd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\v\xf0dB\x8f\x83bg\"\xa7\xb5\xb2j\x9a\xb2\x04!\xa7r>\x89\a?u\u0460\x85\xba\x00\x00\u07d4\v\xfb\xb6\x92]\xc7^R\xcf&\x84\"K\xbe\x05P\xfe\xa6\x85\u04c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\f\b\x80\x06\xc6K0\xc4\u076f\xbc6\xcb_\x05F\x9e\xb6(4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f s\xbaD\xd3\u077d\xb69\xc0N\x19\x109\xa7\x17\x16#\u007f\x89M\x85<\x8f\x89\b\x98\x00\x00\xe0\x94\f\",|A\u0270H\xef\xcc\xe0\xa22CCb\xe1-g;\x8a\x02\x1e\x83Yivw8\x00\x00\xe0\x94\f(\b\xb9Q\ud787-{2y\x0f\xccY\x94\xaeA\xff\u070a\x15\x99n[<\u05b3\xc0\x00\x00\u07d4\f(\x84~O\t\xdf\xce_\x9b%\xaf|NS\x0fY\u0200\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f-\\\x92\x058\xe9S\u02af$\xf0s\u007fUL\u0192wB\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f0\xca\xcc?r&\x9f\x8bO\x04\xcf\a=+\x05\xa8=\x9a\u0449lyt\x12?d\xa4\x00\x00\u07d4\f29\xe2\xe8A$-\xb9\x89\xa6\x15\x18\xc2\"G\xe8\xc5R\b\x89\x0eJ\xf6G\x174d\x00\x00\xe0\x94\fH\r\xe9\xf7F\x10\x02\x90\x8bI\xf6\x0f\xc6\x1e+b\xd3\x14\v\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\fH\xaeb\xd1S\x97\x88\xeb\xa0\x13\xd7^\xa6\vd\xee\xbaN\x80\x89w\xfb\xdcC\xe00\x99\x80\x00\u07d4\fU\x89\xa7\xa8\x9b\x9a\xd1[\x02u\x190AYH\xa8u\xfb\xef\x89\x06\u0519\xeclc8\x00\x00\u07d4\fg\x03=\xd8\xee\u007f\f\x8a\xe54\xd4*Q\xf7\xd9\xd4\xf7\x97\x8f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\fhE\xbfA\xd5\xee'<>\u6d70\u059fo\xd5\xea\xbb\xf7\x89\xa2\xa1\xb9h.X\t\x00\x00\xe0\x94\f\u007f\x86\x9f\x8e\x90\xd5?\xdc\x03\u8c81\x9b\x01k\x9d\x18\xeb&\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\f\x86\x92\xee\xff*S\xd6\xd1h\x8e\xd5j\x9d\u06fdh\u06bb\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f\x8ff\xc6\x01{\xce[ 4r\x04\xb6\x02\xb7C\xba\u05cd`\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\f\x8f\xd7w^T\xa6\xd9\u0263\xbf\x89\x0ev\x1fewi?\xf0\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\f\x92Z\xd5\xeb5,\x8e\xf7m\f\"-\x11[\a\x91\xb9b\xa1\x89\xacc]\u007f\xa3N0\x00\x00\u07d4\f\x96~0a\xb8zu>\x84P~\xb6\t\x86x,\x8f0\x13\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\f\xa1*\xb0\xb9fl\xf0\xce\xc6g\x1a\x15)/&SGj\xb2\x8a,x'\xc4-\"\xd0|\x00\x00\u07d4\f\xa6p\xeb,\x8b\x96\u02e3y!\u007fY)\u00b8\x92\xf3\x9e\xf6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f\xae\x10\x8em\xb9\x9b\x9ecxv\xb0d\xc60>\u068ae\u0209\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\f\xbd\x92\x1d\xbe\x12\x15c\xb9\x8ahq\xfe\xcb\x14\xf1\xcc~\x88\u05c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\f\xbf\x87p\xf0\xd1\b.\\ \u016e\xad4\xe5\xfc\xa9\xaez\xe2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f\xc6\u007f\x82s\xe1\xba\xe0\x86\u007f\xd4.\x8b\x81\x93\xd7&y\xdb\xf8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\f\u05a1A\x91\x8d\x12k\x10m\x9f.\xbfi\xe1\x02\xdeM2w\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\f\xda\x12\xbfr\xd4a\xbb\xc4y\xeb\x92\xe6I\x1d\x05~kZ\u044a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\f\u0716\v\x99\x8c\x14\x19\x98\x16\r\xc1y\xb3l\x15\u0484p\xed\x89\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\f\xfb\x17#5\xb1l\x87\xd5\x19\xcd\x14uS\r W\u007f^\x0e\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\r\x1f*Wq>\xbcn\x94\xde)\x84n\x88D\xd3vfWc\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\r2e\xd3\u7f79=^\x8e\x8b\x1c\xa4\u007f!\ny>\u030e\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\r5@\x8f\"ef\x11o\xb8\xac\u06a9\xe2\xc9\u055bvh?\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\rU\x1e\xc1\xa2\x13<\x98\x1d_\u01a8\xc8\x17?\x9e|OG\xaf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r]\x98V\\d|\xa5\xf1w\xa2\xad\xb9\xd3\x02/\xac(\u007f!\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\re\x80\x14\xa1\x99\x06\x1c\xf6\xb3\x943\x14\x03\x03\xc2\x0f\xfdNZ\x8a\x01\xbc\x85\xdc*\x89\xbb \x00\x00\u07d4\rg\x87\x06\xd07\x18\u007f>\"\xe6\xf6\x9b\x99\xa5\x92\xd1\x1e\xbcY\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\ri\x10\f9\\\xe6\xc5\xea\xad\xf9]\x05\xd8r\x83~\xde\xdd!\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\rt~\u559b\xf7\x9dW8\x1do\xe3\xa2@l\xd0\xd8\xce'\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\r\x80#\x92\x9d\x91r4\xae@Q+\x1a\xab\xb5\xe8\xa4Q'q\x89\b\x05\xe9\x9f\xdc\xc5\xd0\x00\x00\xe0\x94\r\x8a\xab\x8ft\xea\x86,\xdfvh\x05\x00\x9d?>B\xd8\xd0\v\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\r\x8c@\xa7\x9e\x18\x99O\xf9\x9e\xc2Q\xee\x10\u0408\u00d1.\x80\x89\x066d\xfc\u04bb\xc4\x00\x00\u07d4\r\x8e\xd7\xd0\xd1V83\x0e\xd7\xe4\xea\u032b\x8aE\x8dus~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r\x92X/\u06e0^\xab\xc3\xe5\x158\xc5m\xb8\x817\x85\xb3(\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\r\x94C\xa7\x94h\xa5\xbb\xf7\xc1\xe5\xb9\x15\xb3d\x87\xf9\x16\x1f\x19\x84m\x10\x1431\x8a\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\r\xbdA|7+\x8b\r\x01\xbc\xd9Dpk\xd3.`\xae(\u0449\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\r\xc1\x00\xb1\a\x01\x1c\u007f\xc0\xa13\x96\x12\xa1l\xce\xc3(R\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r\u03dd\x8c\x98\x04E\x9fd|\x14\x13\x8e\xd5\x0f\xadV;AT\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4\r\xcf\xe87\xea\x1c\xf2\x8ce\xfc\xce\u00fe\xf1\xf8NY\xd1P\xc0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\r\xd4\xe6t\xbb\xad\xb1\xb0\u0702D\x98q=\xce;QV\xda)\x89\t79SM(h\x00\x00\u07d4\r\xfb\u0501pP\xd9\x1d\x9db\\\x02\x05<\xf6\x1a>\xe2\x85r\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\x0e\x02N\u007f\x02\x9cj\xaf:\x8b\x91\x0f^\b\bs\xb8W\x95\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0e\tdl\x99\xafC\x8e\x99\xfa'L\xb2\xf9\xc8V\xcbe\xf76\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x0e\f\x9d\x00^\xa0\x16\u0095\xcdy\\\xc9!>\x87\xfe\xbc3\xeb\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\x0e\rf3\xdb\x1e\f\u007f#Jm\xf1c\xa1\x0e\n\xb3\x9c \x0f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x0e\x11\xd7z\x89w\xfa\xc3\r&\x84E\xe51\x14\x9b1T\x1a$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\x12=}\xa6\xd1\xe6\xfa\xc2\u072d\xd2p)$\v\xb3\x90R\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0e\x18\x01\xe7\vbb\x86\x1b\x114\u033c9\x1fV\x8a\xfc\x92\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x0e \x94\xac\x16T\xa4k\xa1\xc4\u04e4\v\xb8\xc1}\xa7\U000d6209\x13h?\u007f<\x15\xd8\x00\x00\u07d4\x0e!\xaf\x1b\x8d\xbf'\xfc\xf6?7\xe0G\xb8z\x82\\\xbe|'\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x0e.PJ-\x11\"\xb5\xa9\xfe\xee\\\xb1E\x1b\xf4\u00ac\xe8{\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x0e/\x8e(\xa6\x81\xf7|X;\xd0\xec\xde\x16cK\xdd~\x00\u0349\x05'8\xf6Y\xbc\xa2\x00\x00\u07d4\x0e2\x02\x19\x83\x8e\x85\x9b/\x9f\x18\xb7.=@s\xcaP\xb3}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e3\xfc\xbb\xc0\x03Q\v\xe3W\x85\xb5*\x9c]!k\xc0\x05\xf4\x89e\xea=\xb7UF`\x00\x00\u07d4\x0e6\x96\xcf\x1fB\x17\xb1c\u047c\x12\xa5\xeas\x0f\x1c2\xa1J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x0e9\x0fD\x05=\xdf\xce\xf0\xd6\b\xb3^M\x9c,\xbe\x98q\xbb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x0e:(\xc1\u07ef\xb0P[\xdc\xe1\x9f\xe0%\xf5\x06\xa6\xd0\x1c\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e=\xd7\xd4\xe4)\xfe90\xa6A@5\xf5+\xdcY\x9dxM\x89\x02,\xa3X|\xf4\xeb\x00\x00\u07d4\x0eGey\x03Rek\xc6Vh,$\xfc^\xf3\xe7j#\u01c9\x02\x86\xd7\xfc\f\xb4\xf5\x00\x00\u07d4\x0eI\x88\x00Dqw\xb8\u022f\xc3\xfd\xfa\u007fi\xf4\x05\x1b\xb6)\x89t\x05\xb6\x9b\x8d\xe5a\x00\x00\u07d4\x0ek\xaa\xa3\u07b9\x89\xf2\x89b\x00vf\x86\x18\xe9\xac3(e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x0el\xd6d\xad\x9c\x1e\xd6K\xf9\x87I\xf4\x06D\xb6&\xe3y,\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\xe0\x94\x0em\xfdU;.\x87=*\xec\x15\xbd_\xbb?\x84r\xd8\u04d4\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\x0en\xc3\x137bq\xdf\xf5T#\xabT\"\xcc:\x8b\x06\xb2+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x0en\u0399\x11\x1c\xad\x19a\xc7H\xed=\xf5\x1e\xddi\u04a3\xb1\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x0e\x83\xb8PH\x1a\xb4MI\xe0\xa2)\xa2\xe4d\x90,iS\x9b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0e\x89\xed\xdd?\xa0\xd7\x1d\x8a\xb0\xff\x8d\xa5X\x06\x86\xe3\xd4\xf7O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\x90\x96\xd3C\xc0`\xdbX\x1a\x12\x01\x12\xb2x`~\xc6\xe5+\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x0e\x9cQ\x18d\xa1w\xf4\x9b\xe7\x82\x02w?`H\x9f\xe0NR\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x0e\xa2\xa2\x101+>\x86~\xe0\xd1\xcch,\xe1\xd6f\xf1\x8e\u054a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x0e\xb1\x89\xef,-Wb\xa9c\u05b7\xbd\xf9i\x8e\xa8\u7d0a\x89Hz\x9a0E9D\x00\x00\xe0\x94\x0e\xb5\xb6b\xa1\xc7\x18`\x8f\xd5/\f%\xf97\x880\x17\x85\x19\x8a\x01J7(\x1aa.t\x00\x00\xe0\x94\x0e\xc4f\x96\xff\xac\x1fX\x00_\xa8C\x98$\xf0\x8e\xed\x1d\xf8\x9b\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x0e\xc5\n\xa8#\xf4e\xb9FK\v\xc0\u0125w$\xa5U\xf5\u058a\f\x83\xd1Bj\u01f1\xf0\x00\x00\u07d4\x0e\xc50\x8b1(.!\x8f\xc9\xe7Y\xd4\xfe\xc5\xdb7\b\xce\u01096C\xaady\x86\x04\x00\x00\u07d4\x0e\xcc\xf6\x17\x84O\xd6\x1f\xbab\xcb\x0eD[z\u018b\xcc\x1f\xbe\x89\x14\xfeO\xe65e\xc6\x00\x00\u07d4\x0e\u04fb:N\xb5T\xcf\u0297\x94}WU\a\xcd\xfdm!\u0609\x1d\xb3 _\xcc#\u0540\x00\u07d4\x0e\xd7l,;]P\xff\x8f\xb5\v>\xea\xcdh\x15\x90\xbe\x1c-\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0e\u0680\xf4\xed\aJ\xeaiz\xed\xdf(;c\xdb\xca=\xc4\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\xddKX\x0f\xf1\x0f\xe0lJ\x03\x11b9\xef\x96b+\xae5\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x0e\xe3\x91\xf0^\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x0f\x92\x9c\xf8\x95\xdb\x01z\xf7\x9f>\xad\"\x16\xb1\xbdi\xc3}\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f\xa0\x10\xce\fs\x1d;b\x8e6\xb9\x1fW\x13\x00\u477e\xab\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\x0f\xa5\xd8\u0173\xf2\x94\xef\u0515\xabi\xd7h\xf8\x18rP\x85H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f\xa6\u01f0\x97=\v\xae)@T\x0e$}6'\xe3|\xa3G\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0f\xad\x05P|\u070f$\xb2\xbeL\xb7\xfa]\x92}\u06d1\x1b\x88\x89\xa2\xdf\x13\xf4A\xf0\t\x80\x00\u07d4\x0f\xb5\xd2\xc6s\xbf\xb1\xdd\xca\x14\x1b\x98\x94\xfdm?\x05\xdag \x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0f\u0260\xe3AE\xfb\xfd\xd2\xc9\u04a4\x99\xb6\x17\u05e0)i\xb9\x89\t\xc2\x00vQ\xb2P\x00\x00\xe0\x94\x0f\xcf\xc4\x06P\b\xcf\xd3#0_b\x86\xb5zM\xd7\xee\xe2;\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x0f\xdde@#\x95\u07db\u045f\xeeE\a\xefSE\xf7E\x10L\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x0f\xecN\xe0\xd7\xca\x18\x02\x90\xb6\xbd \xf9\x99#B\xf6\x0f\xf6\x8d\x89\x12 \u007f\x0e\xdc\xe9q\x80\x00\u07d4\x0f\ue06c3\x1e\xfd\x8f\x81\x16\x1cW8+\xb4P{\xb9\xeb\xec\x89\x15\xaf\x88\r\x8c\u06c3\x00\x00\u07d4\x0f\xfe\xa0mq\x13\xfbj\xec(i\xf4\xa9\u07f0\x90\a\xfa\xce\xf4\x89\f8F\x81\xb1\xe1t\x00\x00\u07d4\x10\tq\x98\xb4\xe7\xee\x91\xff\x82\xcc/;\xd9_\xeds\xc5@\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x10\vM\tw\xfc\xba\xd4\u07bd^d\xa0Iz\xea\xe5\x16\x8f\xab\x89\x11\f\x90s\xb5$Z\x00\x00\xe0\x94\x10\x1a\nd\xf9\xaf\xccD\x8a\x8a\x13\rM\xfc\xbe\xe8\x957\xd8T\x8a\x037\xfe_\xea\xf2\u0440\x00\x00\u07d4\x10,G}i\xaa\u06e9\xa0\xb0\xf6+tY\xe1\u007f\xbb\x1c\x15a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x101\xe0\xec\xb5I\x85\xae!\xaf\x17\x93\x95\r\xc8\x11\x88\x8f\xde|\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x104d\x14\xbe\xc6\xd3\xdc\xc4NP\xe5MT\u00b8\xc3sN>\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x108\x98X\xb8\x00\xe8\xc0\xec2\xf5\x1e\xd6\x1a5YF\xcc@\x9b\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x10Y\xcb\xc6>6\xc4>\x88\xf3\x00\b\xac\xa7\xce\x05\x8e\ua816\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\x10n\xd5\xc7\x19\xb5&\x14w\x89\x04%\xaeuQ\xdcY\xbd%\\\x8a\x02\x89jX\xc9[\xe5\x88\x00\x00\u07d4\x10q\x1c=\xda21x\x85\xf0\xa2\xfd\x8a\xe9.\x82\x06\x9b\r\v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x10sy\xd4\xc4gFO#[\xc1\x8eU\x93\x8a\xad>h\x8a\u05c9\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x10v!-Ou\x8c\x8e\xc7\x12\x1c\x1c}t%I&E\x92\x84\x8a\ai[Y\xb5\xc1{L\x00\x00\u07d4\x10x\xd7\xf6\x1b\x0eV\xc7N\xe6c[.\x18\x19\xef\x1e=\x87\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x10z\x03\xcf\bB\xdb\u07b0a\x8f\xb5\x87\xcai\x18\x9e\xc9/\xf5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x10\x80\xc1\xd85\x8a\x15\xbc\x84\xda\xc8%\x89\u0392\xb9\x81\x89t\xc1\xfa\xb8\xad\xb4T\x00\x00\u07d4\x10\xe1\xe37x\x85\xc4-}\xf2\x18R.\xe7vh\x87\xc0^j\x89\x10C\xc4<\xde\x1d9\x80\x00\u07d4\x10\u342d+\xa3=\x82\xb3s\x88\u041cED\u01b0\"]\xe5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x10\xf4\xbf\xf0\u02a5\x02|\nj-\xcf\xc9R\x82M\xe2\x94\t\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11\x00\x1b\x89\xed\x87>:\xae\xc1\x15V4\xb4h\x16C\x98c#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x11\x027\u03d1\x17\xe7g\x92/\u0121\xb7\x8dyd\u0682\xdf \x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x11\x11\xe5\xdb\xf4^o\x90mb\x86o\x17\b\x10\x17\x88\xdd\xd5q\x89F{\xe6S>\xc2\xe4\x00\x00\xe0\x94\x11\x17+'\x8d\xddD\xee\xa2\xfd\xf4\xcb\x1d\x16\x96#\x91\xc4S\u064a\xc6/=\x9b\xfdH\x95\xf0\x00\x00\u07d4\x11&4\xb4\xec0\xffxn\x02AY\xf7\x96\xa5y9\xea\x14N\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x110l}WX\x867x\x0f\xc9\xfd\xe8\xe9\x8e\xcb\x00\x8f\x01d\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x116\x12\xbc;\xa0\xeeH\x98\xb4\x9d\xd2\x023\x90_/E\x8fb\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x11A_\xaba\xe0\xdf\u0539\x06v\x14\x1aUz\x86\x9b\xa0\xbd\xe9\x89o\x05\xb5\x9d; \x00\x00\x00\u07d4\x11L\xbb\xbfo\xb5*\xc4\x14\xbe~\xc6\x1f{\xb7\x14\x95\xce\x1d\xfa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x11L\xfe\xfeP\x17\r\xd9z\xe0\x8f\nDTIx\u0159T\x8d\x89.\u0207\xe7\xa1J\x1c\x00\x00\u07d4\x11a\b\xc1 \x84a.\xed\xa7\xa9=\xdc\xf8\xd2`.'\x9e\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11d\u02aa\x8c\u0157z\xfe\x1f\xad\x8a}`(\xce-W)\x9b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x11gZ%UF\a\xa3\xb6\xc9*\x9e\xe8\xf3ou\xed\xd3\xe36\x89\b\xa9\xab\xa5W\xe3l\x00\x00\u07d4\x11j\t\xdff\xcb\x15\x0e\x97W\x8e)\u007f\xb0n\x13\x04\f\x89<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11o\xef^`\x16B\xc9\x18\u02c9\x16\x0f\xc2);\xa7\x1d\xa96\x89+|\xc2\xe9\xc3\"\\\x00\x00\u07d4\x11xP\x1f\xf9J\xdd\x1cX\x81\xfe\x88a6\xf6\xdf\xdb\xe6\x1a\x94\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x11y\xc6\r\xbd\x06\x8b\x15\v\aM\xa4\xbe#\x03; \u0185X\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\x11}\x9a\xa3\xc4\xd1;\xee\x12\xc7P\x0f\t\xf5\xdd\x1cf\xc4e\x04\x89\v*\xd3\x04\x90\xb2x\x00\x00\xe0\x94\x11}\xb867\u007f\xe1TU\xe0,.\xbd\xa4\v\x1c\xebU\x1b\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x11\x8c\x18\xb2\xdc\xe1p\xe8\xf4Eu;\xa5\xd7Q<\xb7cm-\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\x11\x8f\xbdu;\x97\x929Z\xefzMx\xd2c\xcd\u02ab\xd4\xf7\x8963\x03\"\xd5#\x8c\x00\x00\xe0\x94\x11\x92\x83x\xd2}U\xc5 \xce\xed\xf2L\xeb\x1e\x82-\x89\r\xf0\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x11\x9a\xa6M[}\x18\x1d\xae\x9d<\xb4I\x95\\\x89\xc1\xf9c\xfa\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\x11\xc05\x8a\xa6G\x9d\xe2\x18f\xfe!\a\x19$\xb6^p\xf8\xb9\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\xe0\x94\x11\xd2$z\"\x1ep\xc2\xd6m\x17\xee\x13\x8d8\xc5_\xfb\x86@\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x11\u05c4JG\x1e\xf8\x9a\x8d\x87uUX<\xee\xbd\x149\xea&\x8a\x02#i\u6e80\u0188\x00\x00\u07d4\x11\xdda\x85\u0668\xd7=\xdf\u06a7\x1e\x9bwtC\x1cM\xfe\u008965\u026d\xc5\u07a0\x00\x00\u07d4\x11\xe7\x99~\u0750E\x03\xd7}\xa6\x03\x8a\xb0\xa4\xc84\xbb\xd5c\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\x11\xec\x00\xf8I\xb61\x9c\xf5\x1a\xa8\u074ff\xb3U)\xc0\xbew\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11\ufe22\x04Q\x16\x1bdJ\x8c\u03bb\xc1\xd3C\xa3\xbb\xcbR\x89\xadx\xeb\u016cb\x00\x00\x00\xe0\x94\x11\xfe\xfb]\xc1\xa4Y\x8a\xa7\x12d\fQwu\u07e1\xd9\x1f\x8c\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x12\x0f\x9d\xe6\xe0\xaf~\xc0*\a\xc6\t\u0284G\xf1W\xe64L\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x12\x10\xf8\v\u06c2l\x17Tb\xab\a\x16\xe6\x9eF\xc2J\xd0v\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x12\x13N\u007fk\x01{\xf4\x8e\x85Z9\x9c\xa5\x8e.\x89/\xa5\u020965\u026d\xc5\u07a0\x00\x00\u07d4\x12\x170t\x98\x01S\xae\xaaK\r\xcb\xc7\x13.\xad\xce\xc2\x1bd\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4\x12\x1f\x85[p\x14\x9a\xc84s\xb9po\xb4MG\x82\x8b\x98;\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x12'\xe1\nM\xbf\x9c\xac\xa3\x1b\x17\x80#\x9fUv\x15\xfc5\xc1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x12-\xcf\xd8\x1a\u0779}\x1a\x0eI%\u0135I\x80n\x9f;\xeb\x89R 5\xccn\x01!\x00\x00\u07d4\x12/V\x12%I\xd1h\xa5\xc5\xe2g\xf5&b\xe5\xc5\xcc\xe5\u0209\n\ad\a\xd3\xf7D\x00\x00\xe0\x94\x121o\xc7\xf1x\xea\xc2.\xb2\xb2Z\xed\xea\xdf=u\xd0\x01w\x8a\x04<3\xbe\x05\xf6\xbf\xb9\x80\x00\xe0\x94\x127Y\xf33\xe1>0i\xe2\x03KO\x059\x89\x18\x11\x9d6\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x12\\\xc5\xe4\xd5k+\xcc.\xe1\xc7\t\xfb\x9eh\xfb\x17t@\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x12c#\x88\xb2v^\xe4E+P\x16\x1d\x1f\xff\xd9\x1a\xb8\x1fJ\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\u07d4\x12h\x97\xa3\x11\xa1J\xd4;x\xe0\x92\x01\x00\xc4Bk\xfdk\u07494\xc7&\x89?-\x94\x80\x00\u07d4\x12m\x91\xf7\xad\x86\u07bb\x05W\xc6\x12\xca'n\xb7\xf9m\x00\xa1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x12}?\xc5\x00;\xf6<\r\x83\xe99W\x83e\x15\xfd'\x90E\x89\x06\x10\xc9\".nu\x00\x00\xe0\x94\x12}\xb1\xca\xdf\x1bw\x1c\xbdtu\xe1\xb2ri\x0fU\x8c\x85e\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x12\x84\xf0\xce\xe9\xd2\xff)\x89\xb6Ut\xd0o\xfd\x9a\xb0\xf7\xb8\x05\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x12\x8b\x90\x8f\xe7C\xa44 =\xe2\x94\xc4A\xc7\xe2\n\x86\xeag\x89&\xab\x14\xe0\xc0\xe1<\x00\x00\xe0\x94\x12\x93\u01cc}jD;\x9dt\xb0\xba^\xe7\xbbG\xfdA\x85\x88\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\x12\x96\xac\xde\xd1\xe0c\xaf9\xfe\x8b\xa0\xb4\xb6=\xf7\x89\xf7\x05\x17\x89\x05k\xf9\x1b\x1ae\xeb\x00\x00\u07d4\x12\xaa}\x86\xdd\xfb\xad0\x16\x92\xfe\xac\x8a\b\xf8A\xcb!\\7\x89\amA\xc6$\x94\x84\x00\x00\xe0\x94\x12\xaf\xbc\xba\x14'\xa6\xa3\x9e{\xa4\x84\x9fz\xb1\xc45\x8a\xc3\x1b\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x12\xb5\xe2\x89E\xbb)i\xf9\xc6Lc\xcc\x05\xb6\xf1\xf8\xd6\xf4\u054a\x01\xa2\x9e\x86\x91;t\x05\x00\x00\u0794\x12\u03cb\x0eFR\x13!\x1a[S\u07f0\xdd'\x1a(,\x12\u0248\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x12\xd2\a\x90\xb7\xd3\xdb\u060c\x81\xa2y\xb8\x12\x03\x9e\x8a`;\u0409V\xf9\x85\u04c6D\xb8\x00\x00\xe0\x94\x12\xd6\re\xb7\xd9\xfcH\x84\v\xe5\xf8\x91\xc7E\xcev\xeeP\x1e\x8a\x04\x85\xe58\x8d\fv\x84\x00\x00\u0794\x12\xd9\x1a\x92\xd7O\xc8a\xa7)dm\xb1\x92\xa1%\xb7\x9fSt\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\x12\u992d*\xd5t\x84\xddp\x05e\xbd\xdbFB;\u067d1\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x12\xf3,\n\x1f-\xaa\xb6v\xfei\xab\xd9\xe0\x185-L\xcdE\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x12\xf4`\xaedl\xd2x\x0f\xd3\\P\xa6\xafK\x9a\xcc\xfa\x85\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x12\xff\xc1\x12\x86\x05\xcb\f\x13p\x9ar\x90Po&\x90\x97q\x93\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x13\x03$F\xe7\xd6\x10\xaa\x00\xec\x8cV\u0275t\xd3l\xa1\xc0\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\x1cy,\x19}\x18\xbd\x04]p$\x93|\x1f\x84\xb6\x0fD8\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13\x1d\xf8\xd30\xeb|\xc7\x14}\nUWo\x05\u078d&\xa8\xb7\x89\n1\x06+\xee\xedp\x00\x00\u07d4\x13\x1f\xae\xd1%a\xbbz\xee\x04\xe5\x18Z\xf8\x02\xb1\xc3C\x8d\x9b\x89\v\xdf\x0e\u0733\x90\xc9\xc8V\b\xb7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13!\xcc\xf2\x979\xb9t\xe5\xa5\x16\xf1\x8f:\x846q\xe3\x96B\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13'\xd7Y\xd5n\n\xb8z\xf3~\xcfc\xfe\x01\xf3\x10\xbe\x10\n\x89#\xbc<\xdbh\xa1\x80\x00\x00\u07d4\x13)\xdd\x19\xcdK\xaa\x9f\xc6C\x10\xef\xec\xea\xb2!\x17%\x1f\x12\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x137\x1f\x92\xa5n\xa88\x1eC\x05\x9a\x95\x12\x8b\xdcMC\u0166\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x13O\x15\xe1\xe3\x9cSCY0\xaa\xed\xf3\xe0\xfeV\xfd\xe8C\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x13Ac\xbe\x9f\xbb\xe1\xc5in\xe2U\xe9\v\x13%C\x95\xc3\x18\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x13\\\xec\xd9U\xe5y\x83pv\x920\x15\x93\x03\u0671\x83\x9ff\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x13]\x17\x19\xbf\x03\xe3\xf8f1$y\xfe3\x81\x18\xcd8~p\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x13^\xb8\xc0\xe9\xe1\x01\xde\xed\xec\x11\xf2\xec\xdbf\xae\x1a\xae\x88g\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x13`\xe8}\xf2Li\xeemQ\xc7nsv\u007f\xfe\x19\xa2\x13\x1c\x89\x04\xfc\xc1\xa8\x90'\xf0\x00\x00\u07d4\x13l\x83K\xf1\x112m s\x95)[.X>\xa7\xf35r\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x13mKf+\xbd\x10\x80\xcf\xe4D[\x0f\xa2\x13\x86D5\xb7\xf1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13oI\a\u02b4\x1e'\bK\x98E\x06\x9f\xf2\xfd\f\x9a\xdey\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13t\xfa\xcd{?\x8dhd\x9d`\xd4U\x0e\xe6\x9f\xf0HA3\x89\x0e\x9e\xd6\xe1\x11r\xda\x00\x00\u07d4\x13|\xf3A\xe8Ql\x81X\x14\xeb\xcds\xe6V\x9a\xf1L\xf7\xbc\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x13\x84\x8bF\xeau\xbe\xb7\xea\xa8_Y\xd8f\xd7\u007f\xd2L\xf2\x1a\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x13\x9d51\u0252*\xd5bi\xf60\x9a\xa7\x89\xfb$\x85\xf9\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13\x9eG\x97d\xb4\x99\xd6f \x8cJ\x8a\x04z\x97\x041c\u0749 w!*\xffm\xf0\x00\x00\u07d4\x13\xa5\xee\xcb80]\xf9Iq\xef-\x9e\x17\x9a\xe6\u03ba\xb37\x89\x11\u3ac3\x95\xc6\xe8\x00\x00\u07d4\x13\xac\xad\xa8\x98\n\xff\xc7PI!\xbe\x84\xebID\xc8\xfb\xb2\xbd\x89V\u04aa:\\\t\xa0\x00\x00\u07d4\x13\xb9\xb1\a\x15qL\t\xcf\xd6\x10\u03dc\x98F\x05\x1c\xb1\xd5\x13\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x13\xce3-\xffe\xa6\xab\x938\x97X\x8a\xa2>\x00\t\x80\xfa\x82\x89\x0e\x02\x056\xf0(\xf0\x00\x00\u07d4\x13\xd6z~%\xf2\xb1,\u06c5XP\t\xf8\xac\u011b\x96s\x01\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x13\xde\xe0>7\x99\x95-\a8\x84=K\xe8\xfc\n\x80?\xb2\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\xe0/\xb4H\xd6\xc8J\xe1}\xb3\x10\xad(m\x05a`\u0695\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\xe3!r\x8c\x9cWb\x80X\xe9?\xc8f\xa02\xdd\v\u0690\x89&\xbc\xca#\xfe.\xa2\x00\x00\u07d4\x13\xec\x81\"\x84\x02n@\x9b\xc0f\xdf\xeb\xf9\u0564\xa2\xbf\x80\x1e\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\x14\x01)\xea\xa7f\xb5\xa2\x9f[:\xf2WND\t\xf8\xf6\xd3\xf1\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u07d4\x14\x05\x18\xa3\x19K\xad\x13P\xb8\x94\x9ee\x05e\u07bem\xb3\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14\x06\x85M\x14\x9e\b\x1a\xc0\x9c\xb4\xcaV\r\xa4c\xf3\x120Y\x89Hz\x9a0E9D\x00\x00\u07d4\x14\f\xa2\x8f\xf3;\x9ff\xd7\xf1\xfc\x00x\xf8\xc1\xee\xf6\x9a\x1b\xc0\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x14\x0f\xbaX\xdb\xc0H\x03\xd8L!0\xf0\x19x\xf9\xe0\xc71)\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\x14\x1a^9\xee/h\n`\x0f\xbfo\xa2\x97\u0790\xf3\"\\\u074a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x14%N\xa1&\xb5-\x01B\xda\n~\x18\x8c\xe2U\xd8\xc4qx\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\x14+\x87\xc5\x04?\xfbZ\x91\xdf\x18\xc2\xe1\t\xce\xd6\xfeJq\u06c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x14\x87\xf5\xa5$\u0288Q^\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4\x14\xa75 f6D\x04\xdbP\xf0\xd0\u05cduJ\"\x19\x8e\xf4\x89e\xea=\xb7UF`\x00\x00\u07d4\x14\xab\x16K;RL\x82\u05ab\xfb\xc0\u0783\x11&\xae\x8d\x13u\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14\xb1`>\xc6+ \x02 3\xee\xc4\xd6\xd6eZ\xc2J\x01Z\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x14\xc6;\xa2\u0731\xddM\xf3=\u06b1\x1cO\x00\a\xfa\x96\xa6-\x8a\x03HA\xb6\x05z\xfa\xb0\x00\x00\xe0\x94\x14\xcd\u077c\x8b\t\xe6gZ\x9e\x9e\x05\t\x1c\xb9\"8\u00de\x1e\x8a\x01\x14x\xb7\xc3\n\xbc0\x00\x00\u07d4\x14\xd0\n\xad9\xa0\xa7\u045c\xa0SP\xf7\xb07'\xf0\x8d\xd8.\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x14\xee\xc0\x9b\xf0>5+\xd6\xff\x1b\x1e\x87k\xe6d\xce\xff\xd0\u03c9\x01\x16\xdc:\x89\x94\xb3\x00\x00\u07d4\x14\xf2!\x15\x95\x18x;\u0127\x06go\xc4\xf3\xc5\xee@X)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x14\xfc\xd19\x1e}s/Avl\xda\u0344\xfa\x1d\xeb\x9f\xfd\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x0e=\xbc\xbc\xfc\x84\xcc\xf8\x9bsBwc\xa5e\xc2>`\u0409\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\x15\x18b{\x885\x1f\xed\xe7\x96\xd3\xf3\b3d\xfb\u0508{\f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u0794\x15\"J\xd1\xc0\xfa\xceF\xf9\xf5V\xe4wJ0%\xad\x06\xbdR\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x15/+\xd2)\xdd\xf3\xcb\x0f\xda\xf4U\xc1\x83 \x9c\x0e\x1e9\xa2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x15/N\x86\x0e\xf3\xee\x80jP'w\xa1\xb8\xdb\xc9\x1a\x90vh\x89 \x86\xac5\x10R`\x00\x00\u07d4\x15<\b\xaa\x8b\x96\xa6\x11\xefc\xc0%>*C4\x82\x9eW\x9d\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\x15<\xf2\x84,\xb9\u0787l'o\xa6Gg\u0468\xec\xf5s\xbb\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15>\xf5\x8a\x1e.z>\xb6\xb4Y\xa8\n\xb2\xa5G\xc9A\x82\xa2\x8a\x14T+\xa1*3|\x00\x00\x00\u07d4\x15DY\xfa/!1\x8e44D\x97\x89\xd8&\xcd\xc1W\f\xe5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15G\xb9\xbfz\xd6bt\xf3A8'#\x1b\xa4\x05\ue308\xc1\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\x15H\xb7p\xa5\x11\x8e\u0787\u06e2\xf6\x903\u007fam\u60eb\x89\x1c\x99V\x85\u0fc7\x00\x00\u07d4\x15R\x83P\xe0\xd9g\n.\xa2\u007f{J3\xb9\xc0\xf9b\x1d!\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x15[7y\xbbmV4./\u0681{[-\x81\xc7\xf4\x13'\x89\x02\xb8\xaa:\al\x9c\x00\x00\u07d4\x15e\xaf\x83~\xf3\xb0\xbdN+#V\x8dP#\xcd4\xb1d\x98\x89\x15Q\xe9rJ\u013a\x00\x00\u07d4\x15f\x91\x80\xde\u2558\x86\x9b\b\xa7!\xc7\xd2LL\x0e\xe6?\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x15r\xcd\xfa\xb7*\x01\u0396\x8ex\xf5\xb5D\x8d\xa2\x98S\xfb\u074a\x01\x12blI\x06\x0f\xa6\x00\x00\xe0\x94\x15uY\xad\xc5Wd\xccm\xf7\x93#\t%4\xe3\xd6dZf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x15x\xbd\xbc7\x1bM$8E3\x05V\xff\xf2\xd5\xefM\xffg\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x15~\xb3\xd3\x11;\u04f5\x97qM:\x95N\xdd\x01\x89\x82\xa5\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x84\xa2\xc0f\xb7\xa4U\xdb\u05ae(\a\xa73N\x83\xc3_\xa5\x89\a\f\x1c\xc7;\x00\xc8\x00\x00\u07d4\x15\x87F\x86\xb6s=\x10\xd7\x03\xc9\xf9\xbe\xc6\xc5.\xb8b\x8dg\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x8a\ra\x92S\xbfD2\xb5\xcd\x02\u01f8b\xf7\u00b7V6\x89\a[\xac|[\x12\x18\x80\x00\u07d4\x15\x98\x12y\x82\xf2\xf8\xad;k\x8f\xc3\xcf'\xbfax\x01\xba+\x89\t`\xdbwh\x1e\x94\x00\x00\xe0\x94\x15\x9a\xdc\xe2z\xa1\vG#d)\xa3JZ\xc4,\xad[d\x16\x8a\x06\xbf\x90\xa9n\xdb\xfaq\x80\x00\u07d4\x15\xa0\xae\xc3\u007f\xf9\xff=T\t\xf2\xa4\xf0\xc1!*\xac\xcb\x02\x96\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x15\xaaS\r\xc3iX\xb4\xed\xb3\x8e\xeem\xd9\xe3\xc7}L\x91E\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15\xac\xb6\x15h\xecJ\xf7\xea(\x198a\x81\xb1\x16\xa6\xc5\xeep\x8a\x06\x90\x83l\n\xf5\xf5`\x00\x00\u07d4\x15\xb9o0\xc2;\x86d\xe7I\x06Q\x06k\x00\xc49\x1f\xbf\x84\x89\x16B\xe9\xdfHv)\x00\x00\u07d4\x15\xc7\xed\xb8\x11\x8e\xe2{4\"\x85\xebY&\xb4z\x85[\u01e5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x15\u0654hPz\xa0A?\xb6\r\xca*\xdc\u007fV\x9c\xb3kT\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15\u06f4\x8c\x980\x97d\xf9\x9c\xed6\x92\xdc\xca5\xee0k\xac\x8a\x1f\u00c4+\xd1\xf0q\xc0\x00\x00\xe0\x94\x15\u072f\xcc+\xac\xe7\xb5[T\xc0\x1a\x1cQF&\xbfa\xeb\u060a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x15\u3d44\x05kb\xc9s\xcf^\xb0\x96\xf1s>T\xc1\\\x91\x892\xc7Z\x02#\xdd\xf3\x00\x00\u07d4\x15\xeb\xd1\xc7\xca\u04af\xf1\x92u\xc6W\xc4\xd8\b\xd0\x10\xef\xa0\xf5\x89\n\xdf0\xbap\u0217\x00\x00\u07d4\x15\xee\x0f\xc6>\xbf\x1b\x1f\u011d{\xb3\x8f\x88c\x82:.\x17\u0489g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x15\xf1\xb3R\x11\rh\x90\x1d\x8fg\xaa\xc4jl\xfa\xfe\x03\x14w\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x15\xf2\xb7\xb1d2\xeeP\xa5\xf5[A#/c4\xedX\xbd\xc0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x16\x01\x9aM\xaf\xabC\xf4\u067fAc\xfa\xe0\x84}\x84\x8a\xfc\xa2\x89\x01[\xc7\x019\xf7J\x00\x00\u07d4\x16\x02&\xef\xe7\xb5:\x8a\xf4b\xd1\x17\xa0\x10\x80\x89\xbd\xec\xc2\u0449\n\xdf0\xbap\u0217\x00\x00\u07d4\x16\f\xebo\x98\x0e\x041_S\xc4\xfc\x98\x8b+\xf6\x9e(M}\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\xe0\x94\x16\x1c\xafZ\x97*\u0383y\xa6\u0420J\xe6\xe1c\xfe!\xdf+\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x16\x1d&\xefgY\xba[\x9f \xfd\xcdf\xf1a2\xc3RA^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16!\x10\xf2\x9e\xac_}\x02\xb5C\xd8\xdc\u057bY\xa5\xe3;s\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x16+\xa5\x03'b\x14\xb5\t\xf9u\x86\xbd\x84!\x10\xd1\x03\xd5\x17\x8a\x01\xe7\xff\u0609\\\"h\x00\x00\u07d4\x16-v\xc2\xe6QJ:\xfbo\xe3\xd3\u02d3\xa3\\Z\xe7\x83\xf1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16;\xadJ\x12+E}d\xe8\x15\nA>\xaeM\a\x02>k\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u07d4\x16<\u023e\"vF\xcb\tq\x91Y\xf2\x8e\u041c]\xc0\xdc\xe0\x89Hz\x9a0E9D\x00\x00\u07d4\x16=\xcas\xd7\xd6\xea?>`b2*\x874\x18\f\vx\uf25ft \x03\xcb}\xfc\x00\x00\u07d4\x16Mz\xac>\xec\xba\uc86dQ\x91\xb7S\xf1s\xfe\x12\xec3\x89(VR\xb8\xa4hi\x00\x00\u07d4\x16Rl\x9e\u07d4>\xfaOm\x0f\v\xae\x81\xe1\x8b1\xc5@y\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x16S\x05\xb7\x872.%\xdcj\xd0\xce\xfelo3Fx\xd5i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16e\xab\x179\xd7\x11\x19\xeea2\xab\xbd\x92j'\x9f\xe6yH\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x16k\xf6\u06b2-\x84\x1bHl8\xe7\xbaj\xb3:\x14\x87\ud30a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x16v\x99\xf4\x8ax\xc6\x15Q%\x15s\x99X\x993\x12WO\a\x89\x02\x1d;\xd5^\x80<\x00\x00\u07d4\x16x\xc5\xf2\xa5\"92%\x19ca\x89OS\xccu/\xe2\xf3\x89h\xf3e\xae\xa1\xe4@\x00\x00\u07d4\x16|\xe7\xdee\xe8G\bYZRT\x97\xa3\xeb^ZfPs\x89\x1f1Gsfo\xc4\x00\x00\u07d4\x16~>:\xe2\x003HE\x93\x92\xf7\xdf\xceD\xaf|!\xadY\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x16\x80\xce\xc5\x02\x1e\xe90P\xf8\xae\x12rQ\x83\x9et\xc1\xf1\xfd\x8a\x02\xc6\x14a\xe5\xd7C\u0580\x00\u07d4\x16\x81j\xac\x0e\xde\r-<\xd4B\xday\xe0c\x88\x0f\x0f\x1dg\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x16\x8bP\x19\xb8\x18i\x16D\x83_\xe6\x9b\xf2)\xe1q\x12\xd5,\x8a\x05\xed\xe2\x0f\x01\xa4Y\x80\x00\x00\u07d4\x16\x8b\xde\xc8\x18\xea\xfcm)\x92\xe5\xefT\xaa\x0e\x16\x01\xe3\xc5a\x8967Pz0\xab\xeb\x00\x00\u07d4\x16\x8d0\xe5?\xa6\x81\t+R\xe9\xba\xe1Z\r\xcbA\xa8\u027b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x16\x9b\xbe\xfcA\xcf\xd7\xd7\u02f8\xdf\xc60 \xe9\xfb\x06\u0515F\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xa5\x8e\x98]\xcc\xd7\a\xa5\x94\u0453\xe7\u0327\x8b]\x02xI\x89I\xb9\u029aiC@\x00\x00\u07d4\x16\xa9\xe9\xb7:\u92c6M\x17(y\x8b\x87f\xdb\xc6\xea\x8d\x12\x893\xe7\xb4K\r\xb5\x04\x00\x00\u07d4\x16\xaaR\xcb\vUG#\xe7\x06\x0f!\xf3'\xb0\xa6\x83\x15\xfe\xa3\x89\r\x8drkqw\xa8\x00\x00\u07d4\x16\xab\xb8\xb0!\xa7\x10\xbd\u01ce\xa54\x94\xb2\x06\x14\xffN\xaf\xe8\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x16\xaf\xa7\x87\xfc\x9f\x94\xbd\xffiv\xb1\xa4/C\n\x8b\xf6\xfb\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xba\xe5\xd2N\xff\x91w\x8c\u064bM:\x1c\xc3\x16/D\xaaw\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\x16\xbc@!Z\xbb\u066e](\v\x95\xb8\x01\vE\x14\xff\x12\x92\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x16\xbeu\u9299Z9R\"\xd0\v\u05df\xf4\xb6\xe68\u144a\a\x9f\x90\\o\xd3N\x80\x00\x00\u07d4\x16\xc1\xbf[}\xc9\xc8<\x17\x9e\xfa\xcb\xcf.\xb1t\xe3V\x1c\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x16\u01f3\x1e\x8c7b\x82\xac\"qr\x8c1\xc9^5\xd9R\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xf3\x13\u03ca\xd0\x00\x91J\n\x17m\u01a44+y\xec%8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xff\xac\x84\x03)@\xf0\x12\x1a\tf\x8b\x85\x8a~y\xff\xa3\xbb\x89\xd2J\xdan\x10\x87\x11\x00\x00\xe0\x94\x17\x03\xb4\xb2\x92\xb8\xa9\xde\xdd\xed\xe8\x1b\xb2]\x89\x17\x9fdF\xb6\x8a\x04+e\xa4U\xe8\xb1h\x00\x00\u07d4\x17\x04\x93\x11\x10\x1d\x81~\xfb\x1de\x91\x0ff6b\xa6\x99\u024c\x89lh\xcc\u041b\x02,\x00\x00\u07d4\x17\x04\xce\xfc\xfb\x131\xeczx8\x8b)9>\x85\xc1\xafy\x16\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17\n\x88\xa8\x99\u007f\x92\xd287\x0f\x1a\xff\xde\xe64pP\xb0\x13\x89\xa2\xacw5\x14\x880\x00\x00\u07d4\x17\x10\x8d\xab,P\xf9\x9d\xe1\x10\u1cf3\xb4\u0342\xf5\xdf(\xe7\x895 ;g\xbc\xca\xd0\x00\x00\xe0\x94\x17\x12[Y\xacQ\xce\xe0)\xe4\xbdx\xd7\xf5\x94}\x1e\xa4\x9b\xb2\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4\x17\x1a\u0660K\xed\u0238a\xe8\xedK\xdd\xf5qx\x13\xb1\xbbH\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17\x1c\xa0*\x8bmb\xbfL\xa4~\x90i\x14\a\x98a\x97,\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x17\"\xc4\xcb\xe7\n\x94\xb6U\x9dBP\x84\xca\xee\xd4\xd6\xe6n!\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x17X\vvotSR\\\xa4\u01a8\x8b\x01\xb5\x05p\xea\b\x8c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x17X\x9al\x00jT\xca\xd7\x01\x03\x12:\xae\n\x82\x13_\u07b4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x17Z\x18::#_\xfb\xb0;\xa85gRg\"\x94\x17\xa0\x91\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x17_\xee\xea*\xa4\xe0\xef\xda\x12\xe1X\x8d/H2\x90\xed\xe8\x1a\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x17e6\x1c.\xc2\xf86\x16\u0383c\xaa\xe2\x10%\xf2Vo@\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x17gR\\_Z\"\xed\x80\xe9\xd4\xd7q\x0f\x03b\u049e\xfa3\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17v%`\xe8*\x93\xb3\xf5\"\xe0\xe5$\xad\xb8a,:tp\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x17}\xaex\xbc\x01\x13\xd8\u04dcD\x02\xf2\xa6A\xae*\x10Z\xb8\x89b\x92BV \xb4H\x00\x00\xe0\x94\x17\x84\x94\x8b\xf9\x98H\u021eDV8PM\u0598'\x1bY$\x8a\x01GLA\r\x87\xba\xee\x00\x00\u07d4\x17\x88\u069bW\xfd\x05\xed\xc4\xff\x99\xe7\xfe\xf3\x01Q\x9c\x8a\n\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\x8e\xafk\x85T\xc4]\xfd\xe1kx\xce\f\x15\u007f.\xe3\x13Q\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\x17\x96\x1dc;\xcf \xa7\xb0)\xa7\xd9K}\xf4\xda.\xc5B\u007f\x89\fo\xf0p\U000532c0\x00\u07d4\x17\x96\xbc\xc9{\x8a\xbcq\u007fKJ|k\x106\xea!\x82c\x9f\x89\x13A\xf9\x1c\xd8\xe3Q\x00\x00\u07d4\x17\x99=1*\xa1\x10iW\x86\x8fjU\xa5\xe8\xf1/w\xc8C\x89\x18e\xe8\x14\xf4\x14.\x80\x00\u07d4\x17\x9a\x82^\x0f\x1fn\x98S\tf\x84e\xcf\xfe\xd46\xf6\xae\xa9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x17\xb2\xd6\xcfe\xc6\xf4\xa3G\xdd\xc6W&U5M\x8aA+)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xb8\a\xaf\xa3\xdd\xd6G\xe7#T.{R\xfe\xe3\x95'\xf3\x06\x89\x15\xaf@\xff\xa7\xfc\x01\x00\x00\u07d4\x17\xc0G\x86W\xe1\xd3\xd1z\xaa3\x1d\xd4)\xce\u03d1\xf8\xae]\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\x17\xc0\xfe\xf6\x98l\xfb.@A\xf9\x97\x9d\x99@\xb6\x9d\xff=\xe2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x17\u0511\x8d\xfa\xc1]w\xc4\u007f\x9e\xd4\x00\xa8P\x19\rd\xf1Q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xd5!\xa8\xd9w\x90#\xf7\x16M#<;d \xff\xd2#\xed\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x17\xd91\xd4\xc5b\x94\u073ew\xc8e[\xe4i_\x00mJ<\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xdfIQ\x8ds\xb1)\xf0\xda6\xb1\u0274\f\xb6d \xfd\u01ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x17\xe4\xa0\xe5+\xac>\xe4N\xfe\tT\xe7S\u0538]dN\x05\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xe5\x84\xe8\x10\xe5gp,a\xd5]CK4\u0375\xee0\xf6\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x17\xe8.px\xdcO\xd9\xe8y\xfb\x8aPf\u007fS\xa5\xc5E\x91\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x17\xe8o;[0\xc0\xbaY\xf2\xb2\xe8XB[\xa8\x9f\n\x10\xb0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xee\x9fT\xd4\xdd\xc8Mg\x0e\xff\x11\xe5Je\x9f\xd7/DU\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94\x17\xefJ\xcc\x1b\xf1G\xe3&t\x9d\x10\xe6w\xdc\xff\xd7o\x9e\x06\x8a\bwQ\xf4\xe0\xe1\xb50\x00\x00\u07d4\x17\xf1F2\xa7\xe2\x82\v\xe6\xe8\xf6\u07c25X(=\xad\xab-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xf5#\xf1\x17\xbc\x9f\xe9x\xaaH\x1e\xb4\xf5V\x17\x117\x1b\u0209li\xf7>)\x13N\x00\x00\u07d4\x17\xfd\x9bU\x1a\x98\xcba\xc2\xe0\u007f\xbfA\xd3\xe8\u02650\u02e5\x89\x01v\x8c0\x81\x93\x04\x80\x00\u07d4\x18\x04x\xa6U\u05cd\x0f;\fO +aH[\xc4\x00/\u0549lk\x93[\x8b\xbd@\x00\x00\u07d4\x18\x13l\x9d\xf1g\xaa\x17\xb6\xf1\x8e\"\xa7\x02\u020fK\u0082E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x18\x15'\x9d\xff\x99R\xda;\xe8\xf7rI\xdb\xe2\"C7{\xe7\x8a\x01\x01|\xb7n{&d\x00\x00\u07d4\x18\x1f\xbb\xa8R\xa7\xf5\x01x\xb1\xc7\xf0>\xd9\xe5\x8dT\x16))\x89$\x1a\x9bOaz(\x00\x00\xe0\x94\x18'\x03\x9f\tW\x02\x94\b\x8f\xdd\xf0G\x16\\3\u65a4\x92\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\x18-\xb8R\x93\xf6\x06\u8248\xc3pL\xb3\xf0\xc0\xbb\xbf\xcaZ\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x18H\x00<%\xbf\u052a\x90\xe7\xfc\xb5\u05f1k\xcd\f\xff\xc0\u060965\u026d\xc5\u07a0\x00\x00\xe0\x94\x18JO\v\xebq\xff\xd5X\xa6\xb6\xe8\xf2(\xb7\x87\x96\xc4\xcf>\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94\x18M\x86\xf3Fj\xe6h;\x19r\x99\x82\xe7\xa7\u1903G\xb2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x18Q\xa0c\xcc\xdb0T\x90w\xf1\xd19\xe7-\xe7\x97\x11\x97\u0549lk\x93[\x8b\xbd@\x00\x00\u07d4\x18UF\xe8v\x8dPhs\x81\x8a\xc9u\x1c\x1f\x12\x11j;\xef\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x18X\xcf\x11\xae\xa7\x9fS\x98\xad+\xb2\"g\xb5\xa3\xc9R\xeat\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\xe0\x94\x18Z\u007f\u012c\xe3h\xd23\xe6 \xb2\xa4Y5f\x12\x92\xbd\xf2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x18d\xa3\u01f4\x81UD\x8cT\u020cp\x8f\x16g\tsm1\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x18j\xfd\xc0\x85\xf2\xa3\xdc\xe4a^\xdf\xfb\xad\xf7\x1a\x11x\x0fP\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x18k\x95\xf8\xe5\xef\xfd\xdc\xc9O\x1a1[\xf0)];\x1e\xa5\x88\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x18}\x9f\f\a\xf8\xebt\xfa\xaa\xd1^\xbc{\x80Dt\x17\xf7\x82\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x18\x95\xa0\xebJCrr/\xcb\u016f\xe6\x93o(\x9c\x88\xa4\x19\x891T\xc9r\x9d\x05x\x00\x00\u07d4\x18\x99\xf6\x9fe;\x05\xa5\xa6\xe8\x1fH\a\x11\u041b\xbf\x97X\x8c\x89i\xfb\x13=\xf7P\xac\x00\x00\u07d4\x18\xa6\xd2\xfcR\xbes\b@#\xc9\x18\x02\xf0[\xc2JK\xe0\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x18\xb0@|\xda\xd4\xceR`\x06#\xbd^\x1fj\x81\xaba\xf0&\x89\x11Q\xcc\xf0\xc6T\u0180\x00\u07d4\x18\xb8\xbc\xf9\x83!\xdaa\xfbN>\xac\xc1\xecT\x17'-\xc2~\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\x18\xc6r:gS)\x9c\xb9\x14G}\x04\xa3\xbd!\x8d\xf8\xc7u\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x18\xe1\x13\xd8\x17|i\x1aa\xbexXR\xfa[\xb4z\uef6f\x89Hz\x9a0E9D\x00\x00\xe0\x94\x18\xe4\xceGH;S\x04\n\u06eb5\x17,\x01\xefdPn\f\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\xe0\x94\x18\xe52C\x98\x1a\xab\xc8v}\xa1\fsD\x9f\x13\x91V\x0e\xaa\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x18\xfa\x86%\xc9\u0704>\x00\x15\x9e\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94\x193\xe34\xc4\x0f:\u02ed\f\v\x85\x11X i$\xbe\xca:\x8a\x01\x99^\xaf\x01\xb8\x96\x18\x80\x00\xe0\x94\x197\xc5\xc5\x15\x05uS\u033dF\u0546dU\xcef)\x02\x84\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\u07d4\x19:\xc6Q\x83e\x18\x00\xe25\x80\xf8\xf0\xea\u04fbY~\xb8\xa4\x89\x02\xb6*\xbc\xfb\x91\n\x00\x00\u07d4\x19=7\xed4}\x1c/N55\r\x9aDK\xc5|\xa4\xdbC\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\x19@\u0713d\xa8R\x16_GAN'\xf5\x00$E\xa4\xf1C\x8a\x02L-\xffj<|H\x00\x00\u07d4\x19E\xfe7\u007f\xe6\u0537\x1e>y\x1fo\x17\xdb$<\x9b\x8b\x0f\x89vy\u7fb9\x886\x00\x00\u07d4\x19Jk\xb3\x02\xb8\xab\xa7\xa5\xb5y\u07d3\xe0\xdf\x15t\x96v%\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x19L\ubd12\x98\x82\xbf;K\xf9\x86L+\x1b\x0fb\u0083\xf9\x89\x1e\xf8aS\x1ft\xaa\x00\x00\u07d4\x19O\xf4J\xef\xc1{\xd2\x0e\xfdz LG\xd1b\f\x86\xdb]\x89\xa2\x99\th\u007fj\xa4\x00\x00\xe0\x94\x19O\xfex\xbb\xf5\xd2\r\u044a\x1f\x01\xdaU.\x00\xb7\xb1\x1d\xb1\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x19S1>*\xd7F#\x9c\xb2'\x0fH\xaf4\u063b\x9cDe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19W\x1a+\x8f\x81\u01bc\xf6j\xb3\xa1\x00\x83)V\x17\x15\x00\x03\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\xe0\x94\x19h}\xaa9\xc3h\x13\x9bn{\xe6\r\xc1u:\x9f\f\xbe\xa3\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x19l\x02!\nE\n\xb0\xb3cpe_qz\xa8{\xd1\xc0\x04\x89\x0e\x10\xac\xe1W\xdb\xc0\x00\x00\u07d4\x19n\x85\xdf~s+J\x8f\x0e\xd06#\xf4\u06dd\xb0\xb8\xfa1\x89\x01%\xb9/\\\xef$\x80\x00\u07d4\x19s+\xf9s\x05]\xbd\x91\xa4S:\u06a2\x14\x9a\x91\u04c3\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19vr\xfd9\xd6\xf2F\xcef\xa7\x90\xd1:\xa9\"\xd7\x0e\xa1\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x19y\x8c\xbd\xa7\x15\ua69b\x9dj\xab\x94,U\x12\x1e\x98\xbf\x91\x89A\rXj \xa4\xc0\x00\x00\u07d4\x19\x8b\xfc\xf1\xb0z\xe3\b\xfa,\x02\x06\x9a\xc9\xda\xfeq5\xfbG\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x19\x8e\xf1\xec2Z\x96\xcc5Lrf\xa08\xbe\x8b\\U\x8fg\x8a\x80\xd1\xe47>\u007f!\xda\x00\x00\xe0\x94\x19\x91\x8a\xa0\x9e}IN\x98\xff\xa5\xdbP5\b\x92\xf7\x15j\u018a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x19\xb3k\f\x87\xeafN\xd8\x03\x18\xdcw\xb6\x88\xdd\xe8}\x95\xa5\x89i\x9fI\x98\x020=\x00\x00\u07d4\x19\u07d4E\xa8\x1c\x1b=\x80J\xea\xebon NB6f?\x89\x02\x06\xd9NjI\x87\x80\x00\u07d4\x19\xe5\u07a37\n,tj\xae4\xa3|S\x1fA\xda&N\x83\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x19\xe7\xf3\xeb{\xf6\u007f5\x99 \x9e\xbe\b\xb6*\xd32\u007f\x8c\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\x19\xe9Nb\x00P\xaa\xd7f\xb9\xe1\xba\xd91#\x83\x12\u053fI\x89\x81\xe3-\xf9r\xab\xf0\x00\x00\u07d4\x19\xec\xf2\xab\xf4\f\x9e\x85{%/\xe1\xdb\xfd=L]\x8f\x81n\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19\xf5\xca\xf4\xc4\x0ei\b\x81<\aE\xb0\xae\xa9Xm\x9d\xd91\x89#\xfe\xd9\xe1\xfa+`\x00\x00\u07d4\x19\xf6C\xe1\xa8\xfa\x04\xae\x16\x00`(\x13\x833\xa5\x9a\x96\u0787\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x19\xf9\x9f,\vF\u0389\x06\x87]\xc9\xf9\n\xe1\x04\xda\xe3U\x94\x89\xf4WZ]M\x16*\x00\x00\u07d4\x19\xff$O\xcf\xe3\xd4\xfa/O\u065f\x87\xe5[\xb3\x15\xb8\x1e\xb6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1a\x04\xce\xc4 \xadC\"\x15$mw\xfe\x17\x8d3\x9e\u0435\x95\x89\x11!a\x85\u009fp\x00\x00\xe0\x94\x1a\x04\xd58\x9e\xb0\x06\xf9\u0388\f0\xd1SS\xf8\xd1\x1cK1\x8a\x03\x9d\x84\xb2\x18m\xc9\x10\x00\x00\u07d4\x1a\bA\xb9*\u007fpuV\x9d\xc4b~kv\u02b0Z\u0791\x89Rf<\u02b1\xe1\xc0\x00\x00\xe0\x94\x1a\b]C\xec\x92AN\xa2{\x91O\xe7g\xb6\xd4k\x1e\xefD\x8a\x06A\xe8\xa15c\xd8\xf8\x00\x00\u07d4\x1a\t\xfd\xc2\u01e2\x0e#WK\x97\u019e\x93\u07bag\xd3r \x89lO\xd1\xee$nx\x00\x00\u07d4\x1a\n\x1d\u07f01\xe5\xc8\xcc\x1dF\xcf\x05\x84-P\xfd\xdcq0\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1a\x1c\x9a&\xe0\xe0$\x18\xa5\xcfh}\xa7Z'\\b,\x94@\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x1a \x1bC'\u03a7\xf3\x99\x04bF\xa3\xc8~n\x03\xa3\u0368\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a$4\xccwD\"\u050dS\u055c]V,\u0384\a\xc9K\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\x1a%\xe1\u017c~_P\xec\x16\xf8\x88_!\x0e\xa1\xb98\x80\x0e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x1a&\x94\xec\a\xcf^Mh\xba@\xf3\xe7\xa1LS\xf3\x03\x8cn\x8966\xcd\x06\xe2\xdb:\x80\x00\u07d4\x1a5 E5\x82\xc7\x18\xa2\x1cB7[\xc5\as%RS\xe1\x89*\xd3s\xcef\x8e\x98\x00\x00\xe0\x94\x1a7n\x1b-/Y\ai\xbb\x85\x8dEu2\rN\x14\x99p\x8a\x01\x06q%v9\x1d\x18\x00\x00\u07d4\x1a:3\x0eO\xcbi\xdb\xef^i\x01x;\xf5\x0f\xd1\xc1SB\x89\u3bb5sr@\xa0\x00\x00\u07d4\x1aN\u01a0\xae\u007fZ\x94'\xd2=\xb9rL\r\f\xff\xb2\xab/\x89\t\xb4\x1f\xbf\x9e\n\xec\x00\x00\u07d4\x1aP^b\xa7N\x87\xe5wG>O:\xfa\x16\xbe\xdd<\xfaR\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x1a^\xe53\xac\xbf\xb3\xa2\xd7m[hRw\xb7\x96\xc5j\x05+\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1adJP\xcb\u00ae\xe8#\xbd+\xf2C\xe8%\xbeMG\xdf\x02\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\x1apD\xe28?\x87\b0[I[\xd1\x17k\x92\xe7\xef\x04:\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1ay\xc7\xf4\x03\x9cg\xa3\x9du\x13\x88L\xdc\x0e,4\"$\x90\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1a\x89\x89\x9c\xbe\xbd\xbbd\xbb&\xa1\x95\xa6<\bI\x1f\u035e\xee\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1a\x8a\\\xe4\x14\u079c\xd1r\x93~7\xf2\u055c\xffq\xceW\xa0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1a\x95\xa8\xa8\b.FR\xe4\x17\r\xf9'\x1c\xb4\xbbC\x05\xf0\xb2\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x1a\x95\u0277Tk]\x17\x86\u00c5\x8f\xb1#dF\xbc\f\xa4\u0389j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1a\x98~?\x83\xdeu\xa4/\x1b\xde|\x99|\x19!{J_$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1a\x9ep/8]\xcd\x10^\x8b\x9f\xa4(\xee\xa2\x1cW\xffR\x8a\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1a\xa1\x02\x1fU\n\xf1X\xc7Gf\x8d\xd1;F1`\xf9Z@\x89O\xb0Y\x1b\x9b08\x00\x00\u07d4\x1a\xa2v\x99\xca\u068d\u00e7oy3\xaaf\xc7\x19\x19\x04\x0e\x88\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x1a\xa4\x02p\xd2\x1e\\\u0786\xb61m\x1a\xc3\xc53IKy\xed\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1a\xb5:\x11\xbc\xc6=\u07ea@\xa0+\x9e\x18d\x96\u037b\x8a\xff\x89l?*\xac\x80\f\x00\x00\x00\u07d4\x1a\xbcN%;\b\n\xebCy\x84\xab\x05\xbc\xa0\x97\x9a\xa4>\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a\xc0\x89\u00fcM\x82\xf0j \x05\x1a\x9ds-\xc0\xe74\xcba\x89%\xf6\x9dc\xa6\xce\x0e\x00\x00\xe0\x94\x1a\xd4V>\xa5xk\xe1\x15\x995\xab\xb0\xf1\u0547\x9c>sr\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1a\xd7- \xa7n\u007f\xcckv@X\xf4\x8dA}Io\xa6\u0349lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1a\xda\xf4\xab\xfa\x86}\xb1\u007f\x99\xafj\xbe\xbfpz<\xf5]\xf6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1a\xf6\x03C6\x0e\v-u%R\x107W \xdf!\xdb\\}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a\xfc\u0145\x89l\xd0\xed\xe1)\xee-\xe5\xc1\x9e\xa8\x11T\vd\x89\xaf*\xba\f\x8e[\xef\x80\x00\u07d4\x1b\x05\xeajj\u022f|\xb6\xa8\xb9\x11\xa8\xcc\xe8\xfe\x1a*\xcf\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\v1\xaf\xffKm\xf3e:\x94\xd7\xc8yx\xae5\xf3J\xae\x89\x139\x10E?\xa9\x84\x00\x00\u07d4\x1b\r\ah\x17\xe8\u058e\xe2\xdfN\x1d\xa1\xc1\x14-\x19\x8cD5\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\x1b\x13\ro\xa5\x1d\\H\xec\x8d\x1dR\u070a\"{\xe8s\\\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b#\u02c6cUHq\xfb\xbe\r\x9e`9~\xfbo\xae\xdc>\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1b&9X\x8bU\xc3D\xb0#\xe8\xde_\xd4\b{\x1f\x04\x03a\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x1b9 \xd0\x01\xc4>r\xb2N|\xa4o\x0f\xd6\xe0\xc2\n_\xf2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1b<\xb8\x1eQ\x01\x1bT\x9dx\xbfr\v\r\x92J\xc7c\xa7\u008av\x95\xa9, \xd6\xfe\x00\x00\x00\u07d4\x1bC#,\xcdH\x80\xd6\xf4o\xa7Q\xa9l\xd8$s1XA\x89\x04V9\x18$O@\x00\x00\u07d4\x1bK\xbc\xb1\x81e!\x1b&[(\a\x16\xcb?\x1f!!v\xe8\x89\x19\x9a\xd3}\x03\xd0`\x80\x00\u07d4\x1bM\a\xac\u04c1\x83\xa6\x1b\xb2x=+{\x17\x8d\xd5\x02\xac\x8d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1bckzIo\x04MsYYn5:\x10F\x16Cok\x89\x13\x88\xea\x95\xc3?\x1d\x00\x00\u07d4\x1bd\x95\x89\x12@\xe6NYD\x93\xc2f!q\xdb^0\xce\x13\x89\tX\x87\u0595\xedX\x00\x00\u07d4\x1bf\x10\xfbh\xba\xd6\xed\x1c\xfa\xa0\xbb\xe3:$\xeb.\x96\xfa\xfb\x89\b=lz\xabc`\x00\x00\u07d4\x1by\x903\xefm\xc7\x12x\"\xf7EB\xbb\"\xdb\xfc\t\xa3\b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x1b~\xd9t\xb6\xe24\u0381$t\x98B\x9a[\u0520\xa2\xd19\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\x82o\xb3\xc0\x12\xb0\xd1Y\u253a[\x8aI\x9f\xf3\xc0\xe0<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\x8a\xa0\x16\f\u05df\x00_\x88Q\nqI\x13\xd7\n\u04fe3\x89\n\xef\xfb\x83\a\x9a\xd0\x00\x00\xe0\x94\x1b\x8b\xd6\xd2\xec\xa2\x01\x85\xa7\x8e}\x98\xe8\xe1\x85g\x8d\xacH0\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\x1b\x9b-\u0096\x0eL\xb9@\x8ft\x05\x82|\x9bY\a\x16\x12\xfd\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1b\xa9\"\x8d8\x87'\xf3\x89\x15\x0e\xa0;s\xc8-\xe8\xeb.\t\x8a\x01\x89t\xfb\xe1w\xc9(\x00\x00\u07d4\x1b\xa9\xf7\x99~S\x87\xb6\xb2\xaa\x015\xac$R\xfe6\xb4\xc2\r\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\x1b\xba\x03\xffkJ\u057f\x18\x18J\xcb!\xb1\x88\xa3\x99\xe9\xebJ\x89a\t=|,m8\x00\x00\u07d4\x1b\xbc\x19\x9eXg\x90\xbe\x87\xaf\xed\xc8I\xc0G&t\\]{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x1b\xbc`\xbc\xc8\x0e\\\xdc5\xc5Aj\x1f\n@\xa8=\xae\x86{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xc4L\x87a#\x1b\xa1\xf1\x1f_\xaa@\xfaf\x9a\x01>\x12\u0389\v\tR\xc4Z\xea\xad\x00\x00\u07d4\x1b\xcf4A\xa8f\xbd\xbe\x960\t\xce3\xc8\x1c\xbb\x02a\xb0,\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\x1b\u048c\xd5\u01ca\xeeQ5|\x95\xc1\xef\x925\xe7\xc1\x8b\xc8T\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xd8\xeb\xaavt\xbb\x18\u1458\xdb$OW\x03\x13\a_C\x89\b!\xab\rD\x14\x98\x00\x00\u07d4\x1b\xd9\t\xac\rJ\x11\x02\xec\x98\xdc\xf2\u0329j\n\xdc\u05e9Q\x89\x01\x16Q\xac>zu\x80\x00\u07d4\x1b\xe3T,6\x13hte\xf1Zp\xae\xeb\x81f+e\u0328\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xeaM\xf5\x12/\xaf\u07b3`~\xdd\xda\x1e\xa4\xff\u06da\xbf*\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d4\x1b\xecM\x02\u0385\xfcH\xfe\xb6$\x89\x84\x1d\x85\xb1pXj\x9b\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\x1b\xf9t\u0650OE\u0381\xa8E\xe1\x1e\xf4\xcb\xcf'\xafq\x9e\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x1c\x04VI\xcdS\xdc#T\x1f\x8e\xd4\xd3A\x81(\b\xd5\u075c\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x1c\x12\x8b\xd6\u0365\xfc\xa2uu\xe4\xb4;2S\xc8\xc4\x17*\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\x13\u04c67\xb9\xa4|\xe7\x9d7\xa8oP\xfb@\x9c\x06\a(\x89Hz\x9a0E9D\x00\x00\u07d4\x1c \x10\xbdf-\xf4\x17\xf2\xa2q\x87\x9a\xfb\x13\xefL\x88\xa3\xae\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x1c%z\u0525Q\x05\xea;X\xed7K\x19\x8d\xa2f\xc8_c\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x1c.6\a\xe1'\xca\xca\x0f\xbd\\YH\xad\xad}\xd80\xb2\x85\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x1c5l\xfd\xb9_\xeb\xb7\x14c;(\xd5\xc12\u0744\xa9\xb46\x89\x01Z\xf1\u05cbX\xc4\x00\x00\u07d4\x1c5\xaa\xb6\x88\xa0\u034e\xf8.vT\x1b\xa7\xac9R\u007ft;\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x1c>\xf0]\xae\x9d\xcb\u0509\xf3\x02D\bf\x9d\xe2D\xc5*\x02\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x1cJ\xf0\xe8c\xd2el\x865\xbco\xfe\xc8\u0759(\x90\x8c\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c`\x19\x93x\x92\a\xf9e\xbb\x86\\\xbbL\xd6W\xcc\xe7o\xc0\x89\x05T\x1ap7P?\x00\x00\u07d4\x1cc\xfa\x9e,\xbb\xf21a\xda3\xa1\xda}\xf7\r\x1b\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1c\xb6\xb2\xd7\xcf\xc5Y\xb7\xf4\x1eoV\xab\x95\xc7\xc9X\xcd\x0eL\x89Hz\x9a0E9D\x00\x00\u07d4\x1c\xc1\xd3\xc1O\x0f\xb8d\x0e6rM\xc42)\xd2\xeaz\x1eH\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x1c\xc9\bv\x00A\t\xcdy\xa3\u07a8f\u02c4\n\xc3d\xba\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xd1\xf0\xa3\x14\u02f2\x00\xde\n\f\xb1\xef\x97\xe9 p\x9d\x97\u0089lk\x93[\x8b\xbd@\x00\x00\u0794\x1c\xdaA\x1b\xd5\x16;\xae\xca\x1eU\x85c`\x1c\xe7 \xe2N\xe1\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1c\xe8\x1d1\xa7\x920\"\xe1%\xbfH\xa3\xe06\x93\xb9\x8d\xc9\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xeb\xf0\x98]\u007fh\n\xaa\x91\\D\xccb\xed\xb4\x9e\xab&\x9e\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1c\xedg\x15\xf8b\xb1\xff\x86\x05\x82\x01\xfc\xceP\x82\xb3nb\xb2\x8a\x01j^`\xbe\xe2s\xb1\x00\x00\u07d4\x1c\xf0L\xb1C\x80\x05\x9e\xfd?#\x8be\u057e\xb8j\xfa\x14\u0609\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1c\xf1\x05\xab#\x02;ULX>\x86\u05d2\x11y\xee\x83\x16\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1c\xf2\xebz\x8c\xca\u00ad\xea\xef\x0e\xe8sG\xd55\u04f9@X\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xfc\xf7Q\u007f\f\bE\x97 \x94+dz\u0452\xaa\x9c\x88(\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\x1d\t\xad$\x12i\x1c\u0141\xc1\xab6\xb6\xf9CL\xd4\xf0\x8bT\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x1d\x15|Xv\xc5\xca\xd5S\xc9\x12\xca\xf6\xce-Rw\xe0\\s\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1d&\x15\xf8\xb6\xcaP\x12\xb6c\xbd\u0414\xb0\xc5\x13|w\x8d\u07ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1d)\u01ea\xb4+ H\u04b2R%\u0518\u06e6z\x03\xfb\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0794\x1d4\x1f\xa5\xa3\xa1\xbd\x05\x1f}\xb8\a\xb6\xdb/\u01faO\x9bE\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1d4N\x96%g\xcb'\xe4M\xb9\xf2\xfa\u01f6\x8d\xf1\xc1\xe6\xf7\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x1d6h0c\xb7\xe9\xeb\x99F-\xab\xd5i\xbd\xdc\xe7\x16\x86\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1d7aky?\x94\x91\x188\xac\x8e\x19\xee\x94I\u07d2\x1e\u0109QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94\x1d9[0\xad\xda\x1c\xf2\x1f\t\x1aOJ{u3q\x18\x94A\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x1dEXn\xb8\x03\xca!\x90e\v\xf7H\xa2\xb1t1+\xb5\a\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1dW.\xdd-\x87\xca'\x1ag\x14\xc1Z;7v\x1d\u0320\x05\x89\x06\xeb\xd5*\x8d\xdd9\x00\x00\u07d4\x1dc0\x97\xa8R%\xa1\xffC!\xb1)\x88\xfd\xd5\\+8D\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x1di\xc8=(\xff\x04t\xce\xeb\xea\xcb:\xd2'\xa1D\xec\u78ca\x01(\xcc\x03\x92\nb\u0480\x00\u07d4\x1d\x96\xbc\u0544W\xbb\xf1\xd3\u00a4o\xfa\xf1m\xbf}\x83hY\x89\tIr\t\xd8F~\x80\x00\u07d4\x1d\x9ej\xaf\x80\x19\xa0_#\x0e]\xef\x05\xaf]\x88\x9b\xd4\xd0\xf2\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x1d\xab\x17.\xff\xa6\xfb\xeeSL\x94\xb1~yN\xda\xc5OU\xf8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1d\xb9\xac\x9a\x9e\xae\xec\nR7W\x05\fq\xf4rx\xc7-P\x89Hz\x9a0E9D\x00\x00\u07d4\x1d\xbe\x8e\x1c+\x8a\x00\x9f\x85\xf1\xad<\xe8\r.\x055\x0e\u3709\aW\rn\x9e\xbb\xe4\x00\x00\u07d4\x1d\xc7\xf7\xda\xd8]\xf5?\x12q\x15$\x03\xf4\xe1\xe4\xfd\xb3\xaf\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1d\u03bc\xb7em\xf5\u072a3h\xa0U\xd2/\x9e\xd6\xcd\xd9@\x89\x1b\x18\x1eK\xf24<\x00\x00\xe0\x94\x1d\xd7tA\x84J\xfe\x9c\xc1\x8f\x15\xd8\xc7{\xcc\xfbe^\xe04\x8a\x01\x06\xebEW\x99D\x88\x00\x00\u07d4\x1d\xde\xfe\xfd5\xab\x8fe\x8b$q\xe5G\x90\xbc\x17\xaf\x98\u07a4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1d\xee\xc0\x1a\xbe\\\r\x95-\xe9\x10l=\xc3\x069\xd8P\x05\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\x1d\xf6\x91\x16rg\x9b\xb0\xef5\t\x03\x8c\f'\xe3\x94\xfd\xfe0\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\x1d\xfa\xee\ar\x12\xf1\xbe\xaf\x0eo/\x18@Sz\xe1T\xad\x86\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1e\x06\r\xc6\xc5\xf1\u02cc\xc7\xe1E.\x02\xee\x16u\b\xb5eB\x8a\x02\xb1O\x02\xc8d\xc7~\x00\x00\xe0\x94\x1e\x13\xecQ\x14,\ubde2`\x83A,<\xe3QD\xbaV\xa1\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x1e\x1aH(\x11\x9b\xe3\t\xbd\x88#nMH+PM\xc5W\x11\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\x1e\x1a\ud178leb\u02cf\xa1\xebo\x8f;\xc9\u072eny\x89\xf4\xd2\u0744%\x9b$\x00\x00\u07d4\x1e\x1ccQwj\xc3\x10\x919~\xcf\x16\x00-\x97\x9a\x1b-Q\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1e\x1dz_$h\xb9N\xa8&\x98-\xbf!%yR*\xb7\xdf\n\u02ac\x9e\xee\xd3Y09\xe5\xacuy\x8a+\x14F\xddj\xef\xe4\x1c\x00\x00\u07d4\x1e{^M\x1fW+\xec\xf2\xc0\x0f\xc9\f\xb4v{Jn3\u0509\x06\x1f\xc6\x10u\x93\xe1\x00\x00\u07d4\x1e\x8eh\x9b\x02\x91|\xdc)$]\f\x9ch\xb0\x94\xb4\x1a\x9e\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xa34\xb5u\b\a\xeat\xaa\u016b\x86\x94\xec_(\xaaw\u03c9\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\x1e\xa4qU\x04\u01af\x10{\x01\x94\xf4\xf7\xb1\xcbo\xcc\xcdoK\x89 \x041\x97\xe0\xb0'\x00\x00\u07d4\x1e\xa4\x92\xbc\xe1\xad\x10~3\u007fK\u0527\xac\x9a{\xab\xcc\u036b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x1e\xa6\xbf/\x15\xae\x9c\x1d\xbcd\u06a7\xf8\xeaM\r\x81\xaa\xd3\xeb\x89\u3bb5sr@\xa0\x00\x00\u07d4\x1e\xb4\xbfs\x15j\x82\xa0\xa6\x82 \x80\xc6\xed\xf4\x9cF\x9a\xf8\xb9\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\x1e\xba\xcbxD\xfd\xc3\"\xf8\x05\x90O\xbf\x19b\x80-\xb1S|\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1e\xc4\xecKw\xbf\x19\u0411\xa8h\xe6\xf4\x91T\x18\x05A\xf9\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xd0n\xe5\x16b\xa8lcE\x88\xfbb\xdcC\xc8\xf2~|\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1e\u063b?\x06w\x8b\x03\x9e\x99a\xd8\x1c\xb7\x1as\xe6x|\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xda\bNye\x00\xba\x14\xc5\x12\x1c\r\x90\x84of\xe4\xbeb\x89\x1c\xfd\xd7F\x82\x16\xe8\x00\x00\u07d4\x1e\xeel\xbe\xe4\xfe\x96\xadaZ\x9c\xf5\x85zdy@\u07ccx\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4\x1e\xf2\u073f\xe0\xa5\x00A\x1d\x95n\xb8\u0213\x9c=l\xfef\x9d\x89*\x11)\u0413g \x00\x00\xe0\x94\x1e\xf5\xc9\xc76P\u03fb\xde\\\x88U1\xd4'\xc7\xc3\xfeUD\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1f\x04\x12\xbf\xed\u0356N\x83}\t,q\xa5\xfc\xba\xf3\x01&\xe2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\x17O@\xa0Dr4\xe6fS\x91Mu\xbc\x00>V\x90\u0709\b\xacr0H\x9e\x80\x00\x00\u07d4\x1f!\x86\xde\xd2>\f\xf9R\x16\x94\xe4\xe1dY>i\n\x96\x85\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x1f*\xfc\n\xed\x11\xbf\xc7\x1ew\xa9\ae{6\xeav\xe3\xfb\x99\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\x1f9Y\xfc)\x11\x10\xe8\x822\xc3kvg\xfcx\xa3ya?\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1f=\xa6\x8f\xe8~\xafC\xa8)\xabm~\u0166\xe0\t\xb2\x04\xfb\x89\x1e\x16\x01u\x8c,~\x00\x00\u07d4\x1fI\xb8m\r9EY\x06\x98\xa6\xaa\xf1g<7u\\\xa8\r\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\x1f_;4\xbd\x13K'\x81\xaf\xe5\xa0BJ\u0144l\xde\xfd\x11\x89\x05]\xe6\xa7y\xbb\xac\x00\x00\u07d4\x1fo\x0004\x97R\x06\x1c\x96\a+\xc3\xd6\xeb5I \x8dk\x89\x01K\x8d\xe1\xeb\x88\u06c0\x00\u07d4\x1f}\x8e\x86\xd6\xee\xb0%E\xaa\xd9\x0e\x912{\xd3i\xd7\xd2\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\x81\x16\xbd\n\xf5W\x0e\xaf\fV\u011cz\xb5\xe3zX\x04X\x89lk\x93[\x8b\xbd@\x00\x00\u0794\x1f\x88\xf8\xa13\x8f\xc7\xc1\tv\xab\xcd?\xb8\u04c5T\xb5\uc708\xb9\xf6]\x00\xf6<\x00\x00\u07d4\x1f\x9c2hE\x8d\xa3\x01\xa2\xbeZ\xb0\x82W\xf7{\xb5\xa9\x8a\xa4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1f\xa21\x9f\xed\x8c-F*\xdf.\x17\xfe\xecjo0Qn\x95\x89\x06\xca\xe3\x06!\xd4r\x00\x00\u07d4\x1f\xb4c\xa08\x99\x83\xdf}Y?{\xddmxI\u007f\xed\x88y\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\xb7\xbd1\r\x95\xf2\xa6\u067a\xaf\x8a\x8aC\n\x9a\x04E:\x8b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x1f\xcc|\xe6\xa8HX\x95\xa3\x19\x9e\x16H\x1fr\xe1\xf7b\xde\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1f\xcf\xd1\xd5\u007f\x87\"\x90V\f\xb6-`\x0e\x1d\xef\xbe\xfc\xcc\x1c\x89P\xc5\xe7a\xa4D\b\x00\x00\u0794\x1f\u0496\xbe\x03\xads|\x92\xf9\u0186\x9e\x8d\x80\xa7\x1cW\x14\xaa\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x1f\xdd\xd8_\u024b\xe9\xc4\x04Ya\xf4\x0f\x93\x80^\xccEI\xe5\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4 \x01\xbe\xf7{f\xf5\x1e\x15\x99\xb0/\xb1\x10\x19J\x00\x99\xb7\x8d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4 \x02d\xa0\x9f\x8ch\xe3\xe6b\x97\x95(\x0fV%O\x86@\u0409\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \x03qy\a\xa7%`\xf40\u007f\x1b\xee\xccT6\xf4=!\xe7\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4 \r\xfc\vq\xe3Y\xb2\xb4eD\n6\xa6\xcd\xc3Rw0\a\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4 \x13L\xbf\xf8\x8b\xfa\xdcFkR\xec\ua9d8W\x89\x1d\x83\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4 \x14&\x1f\x01\b\x9fSyV0\xba\x9d\xd2O\x9a4\xc2\xd9B\x89Hz\x9a0E9D\x00\x00\u07d4 \x16\x89]\xf3,\x8e\xd5G\x82iF\x84#\xae\xa7\xb7\xfb\xceP\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \x18\x1cKA\xf6\xf9r\xb6iX!_\x19\xf5p\xc1]\xdf\xf1\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4 \x18d\xa8\xf7\x84\xc2'{\v|\x9e\xe74\xf7\xb3w\xea\xb6H\x89\xf2(\x14\x00\xd1\xd5\xec\x00\x00\u07d4 \xb8\x1a\xe59&\xac\xe9\xf7\xd7AZ\x05\f\x03\x1dX_ \x89\x12\u007f\x19\xe8>\xb3H\x00\x00\xe0\x94 \x1d\x9e\xc1\xbc\v\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4 \xa1RV\xd5\f\xe0X\xbf\x0e\xacC\xaaS:\xa1n\u0273\x80\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \xa2\x9cPy\xe2k?\x181\x8b\xb2\xe5\x0e\x8e\x8b4n[\xe8\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4 \xa8\x16\x80\xe4e\xf8\x87\x90\xf0\aO`\xb4\xf3_]\x1ej\xa5\x89Ea\x80'\x8f\fw\x80\x00\u07d4 \xb9\xa9\u6f48\x80\u0659J\xe0\r\u0439(*\v\xea\xb8\x16\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4 \u0084\xba\x10\xa2\b0\xfc=i\x9e\xc9}-\xfa'\xe1\xb9^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4 \xd1A\u007f\x99\xc5i\u3fb0\x95\x85e0\xfe\x12\xd0\xfc\uaa89@\x15\xf9K\x11\x83i\x80\x00\u07d4 \u074f\u02f4n\xa4o\u3066\x8b\x8c\xa0\xea[\xe2\x1f\u9949lk\x93[\x8b\xbd@\x00\x00\xe0\x94 \xff>\u078c\xad\xb5\xc3{H\xcb\x14X\x0f\xb6^#\t\n{\x8a\b\xe4\xd3\x16\x82v\x86@\x00\x00\xe0\x94!\x008\x1d`\xa5\xb5J\xdc\t\u0456\x83\xa8\xf6\u057bK\xfb\u02ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94!\x18\xc1\x16\xab\f\xdfo\xd1\x1dT\xa40\x93\a\xb4w\xc3\xfc\x0f\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94!\x1b)\xce\xfcy\xae\x97gD\xfd\xeb\u03bd<\xbb2\xc5\x13\x03\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4! l\xe2.\xa4\x80\xe8Y@\xd3\x13\x14\xe0\xd6ONM:\x04\x8965\u026d\xc5\u07a0\x00\x00\u07d4!2\xc0Qj.\x17\x17J\xc5G\xc4;{\x00 \xd1\xebLY\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94!@\x8bMz,\x0en\xcaAC\xf2\xca\u037b\u033a\x12\x1b\u060a\x04<3\xc1\x93ud\x80\x00\x00\u07d4!Kt9U\xa5\x12\xden\r\x88j\x8c\xbd\x02\x82\xbe\xe6\u04a2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!L\x89\u017d\x8e}\"\xbcWK\xb3^H\x95\x02\x11\xc6\xf7v\x89\x01\x06T\xf2X\xfd5\x80\x00\xe0\x94!Ti\x14\xdf\u04ef*\xddA\xb0\xff>\x83\xff\xdat\x14\xe1\xe0\x8a\x01C\x95\xe78ZP.\x00\x00\u07d4!X.\x99\xe5\x02\xcb\xf3\xd3\xc2;\xdf\xfbv\xe9\x01\xacmV\xb2\x89\x05k\xc7^-c\x10\x00\x00\u07d4!Y$\b\x13\xa70\x95\xa7\xeb\xf7\u00f3t>\x80(\xae_\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!`\xb4\xc0,\xac\n\x81\u0791\b\xdeCE\x90\xa8\xbf\xe6\x875\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94!nA\x86N\xf9\x8f\x06\r\xa0\x8e\xca\xe1\x9a\xd1\x16j\x17\xd06\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4!\x84o/\xdfZA\xed\x8d\xf3n^\xd8TM\xf7Y\x88\xec\xe3\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94!\xa6\xdbe'F{\xc6\xda\xd5K\xc1n\x9f\xe2\x95;g\x94\xed\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4!\xa6\xfe\xb6\xab\x11\xc7f\xfd\xd9w\xf8\xdfA!\x15_G\xa1\xc0\x89\x03\x19\xcf8\xf1\x00X\x00\x00\u07d4!\xb1\x82\xf2\xda+8D\x93\xcf_5\xf8=\x9d\x1e\xe1O*!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!\xbf\xe1\xb4\\\xac\xdebt\xfd\x86\b\u0661x\xbf>\xebn\u0709l\xee\x06\u077e\x15\xec\x00\x00\u07d4!\xc0s\x80HOl\xbc\x87$\xad2\xbc\x86L;Z\xd5\x00\xb7\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94!\u00e8\xbb\xa2g\xc8\u0322{\x1a\x9a\xfa\xba\xd8o`z\xf7\b\x8a\x01\xe4\xa3lI\u06580\x00\x00\u07d4!\xcem[\x90\x18\xce\xc0J\u0596yD\xbe\xa3\x9e\x800\xb6\xb8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4!\xd0'\x05\xf3\xf6I\x05\xd8\x0e\xd9\x14y\x13\xea\x8cs\a\u0595\x89I\xed\xb1\xc0\x98\x876\x00\x00\u07d4!\xd1?\f@$\xe9g\xd9G\a\x91\xb5\x0f\"\xde:\xfe\xcf\x1b\x89\xf1Z\xd3^.1\xe5\x00\x00\xe0\x94!\xdb\u06c1z\r\x84\x04\u01bd\xd6\x15\x047N\x9cC\xc9!\x0e\x8a\x02\x1e\x18\xb9\xe9\xabE\xe4\x80\x00\xe0\x94!\xdf\x1e\xc2KNK\xfey\xb0\xc0\x95\u03ba\xe1\x98\xf2\x91\xfb\u044a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94!\xdf-\u036ft\xb2\xbf\x804\x04\xddM\xe6\xa3^\xab\xec\x1b\xbd\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4!\xe2\x19\u021c\xa8\xac\x14\xaeL\xbaa0\xee\xb7}\x9em9b\x89*\u035f\xaa\xa08\xee\x00\x00\u07d4!\xe5\u04ba\xe9\x95\xcc\xfd\b\xa5\xc1k\xb5$\xe1\xf60D\x8f\x82\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4!\xe5\xd7s 0L \x1c\x1eS\xb2a\xa1#\u0421\x06>\x81\x89\x04\xb6\xfa\x9d3\xddF\x00\x00\xe0\x94!\xea\xe6\xfe\xff\xa9\xfb\xf4\u0347OG9\xac\xe50\u033eY7\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4!\xec\xb2\u07e6Wy\xc7Y-\x04\x1c\xd2\x10Z\x81\xf4\xfdNF\x8965\u026d\xc5\u07a0\x00\x00\u07d4!\uff20\x9b5\x80\xb9\x8es\xf5\xb2\xf7\xf4\xdc\v\xf0,R\x9c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!\xfd\v\xad\xe5\xf4\xeftt\xd0X\xb7\xf3\xd8T\xcb\x13\x00RN\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94!\xfdG\xc5%`\x12\x19\x8f\xa5\xab\xf11\xc0mj\xa1\x96_u\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4!\xfdl]\x97\xf9\xc6\x00\xb7h!\xdd\xd4\xe7v5\x0f\xce+\xe0\x89lj\u04c2\xd4\xfba\x00\x00\u07d4\"\r\u018d\xf0\x19\xb6\xb0\u033f\xfbxKZZ\xb4\xb1]@`\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\"\x0e+\x92\xc0\xf6\xc9\x02\xb5\x13\xd9\xf1\xe6\xfa\xb6\xa8\xb0\xde\xf3\u05c9+^:\xf1k\x18\x80\x00\x00\u07d4\"V\x1cY1\x14560\x9c\x17\xe82X{b\\9\v\x9a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\"W\xfc\xa1jn\\*d|<)\xf3l\xe2)\xab\x93\xb1~\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\"]5\xfa\xed\xb3\x91\u01fc-\xb7\xfa\x90q\x16\x04\x05\x99m\x00\x89\t\x18T\xfc\x18bc\x00\x00\u07d4\"_\x9e\xb3\xfbo\xf3\xe9\xe3\xc8D~\x14\xa6n\x8dO7y\xf6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\"r\x18n\xf2}\xcb\xe2\xf5\xfc70P\xfd\xae\u007f*\xce#\x16\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4\"s\xba\u05fcNHv\"\xd1u\xefzf\x98\x8bj\x93\xc4\xee\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\"v&K\xec\x85&\xc0\xc0\xf2pgz\xba\xf4\xf0\xe4A\xe1g\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\"\x82B\xf83n\xec\xd8$.\x1f\x00\x0fA\x93~q\xdf\xfb\xbf\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\"\x84*\xb80\xdaP\x99\x13\xf8\x1d\xd1\xf0O\x10\xaf\x9e\xdd\x1cU\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\"\x94O\xbc\xa9\xb5yc\bN\xb8M\xf7\xc8_\xb9\xbc\u07f8V\x89\xfc\x11\x8f\uf43a8\x80\x00\u07d4\"\x9c\xc4q\x1bbu^\xa2\x96DZ\u00f7\u007f\xc63\x82\x1c\xf2\x89\x02#\xe8\xb0R\x192\x80\x00\u0794\"\x9eC\r\xe2\xb7OD&Q\xdd\u0377\x01v\xbc\x05L\xadT\x88\xbb\xf9\x81\xbcJ\xaa\x80\x00\u07d4\"\x9fO\x1a*OT\atP[G\a\xa8\x1d\xe4D\x10%[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\"\x9f\xf8\v\xf5p\x80\t\xa9\xf79\xe0\xf8\xb5`\x91@\x16\u0566\x89\x12\x11\xec\xb5m\x13H\x80\x00\u07d4\"\xa2X\x12\xabV\xdc\xc4#\x17^\xd1\u062d\xac\xce3\xcd\x18\x10\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94\"\xb9j\xb2\xca\xd5]\xb1\x00\xb50\x01\xf9\xe4\xdb7\x81\x04\xc8\a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\"\xbd\xff\xc2@\xa8\x8f\xf7C\x1a\xf3\xbf\xf5\x0e\x14\xda7\xd5\x18>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\"\xce4\x91Y\xee\xb1D\xef\x06\xff&6X\x8a\xefy\xf6(2\x89\n1\x06+\xee\xedp\x00\x00\u07d4\"\xdbU\x9f,<\x14u\xa2\xe6\xff\xe8:YyY\x91\x96\xa7\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\"\xe1QX\xb5\xee>\x86\xeb\x032\xe3\u6a6cl\u0675^\u0349\b\xacr0H\x9e\x80\x00\x00\u07d4\"\xe2H\x8e-\xa2jI\xae\x84\xc0\x1b\xd5K!\xf2\x94x\x91\u0189]\u0212\xaa\x111\xc8\x00\x00\u07d4\"\xe5\x12\x14\x9a\x18\xd3i\xb7\x86\xc9\xed\xab\xaf\x1d\x89N\xe0.g\x14a\\\x00\x00\u07d4\"\xeb}\xb0\xbaV\xb0\xf8\xb8\x16\u0332\x06\xe6\x15\xd9)\x18[\r\x89\x04])s~\"\xf2\x00\x00\u07d4\"\xee\xd3'\xf8\xeb\x1d\x138\xa3\xcb{\x0f\x8aK\xaaY\a\u0355\x89\x01E]_Hw\b\x80\x00\xe0\x94\"\xf0\x04\u07cd\xe9\xe6\xeb\xf5#\u032c\xe4W\xac\xcb&\xf9r\x81\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794\"\xf2\xdc\xffZ\u05cc>\xb6\x85\v\\\xb9Q\x12{e\x95\"\u623e -j\x0e\xda\x00\x00\u07d4\"\xf3\xc7y\xddy\x02>\xa9*x\xb6\\\x1a\x17\x80\xf6-\\J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\"\xfe\x88M\x907)\x1bMR\xe6(Z\xe6\x8d\xea\v\xe9\xff\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\x06\u07d3\x1a\x94\rX\xc0\x16e\xfaM\b\x00\x80,\x02\xed\xfe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94#\t\xd3@\x91D[22Y\v\xd7\x0fO\x10\x02[,\x95\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4#\x12\x00F\xf6\x83!\x02\xa7R\xa7fVi\x1c\x86>\x17\u5709\x11\xe0\xe4\xf8\xa5\v\xd4\x00\x00\u07d4#\x1a\x15\xac\xc1\x99\u021f\xa9\xcb\"D\x1c\xc7\x030\xbd\xcc\xe6\x17\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4#\x1d\x94\x15]\xbc\xfe*\x93\xa3\x19\xb6\x17\x1fc\xb2\v\u04b6\xfa\x89\xcf\x14{\xb9\x06\xe2\xf8\x00\x00\u07d4#(2\xcdYw\xe0\nL0\xd0\x16?.$\xf0\x88\xa6\xcb\t\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4#,m\x03\xb5\xb6\xe6q\x1e\xff\xf1\x90\xe4\x9c(\xee\xf3l\x82\xb0\x89Hz\x9a0E9D\x00\x00\xe0\x94#,\xb1\xcdI\x99<\x14J?\x88\xb3a\x1e#5i\xa8k\u058a\x03L`lB\u042c`\x00\x00\u07d4#,\xe7\x82Pb%\xfd\x98`\xa2\xed\xc1Jz0Gsm\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4#/R]U\x85\x9b}N`\x8d H\u007f\xaa\xdb\x00)15\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94#4\u0150\u01e4\x87i\x100E\u0176SL\x8a4i\xf4J\x8a\x03\xb1\x99\a=\xf7-\xc0\x00\x00\u07d4#7n\u02bftl\xe53!\xcfB\xc8fI\xb9+g\xb2\xff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#7\x8fB\x92m\x01\x84\xb7\x93\xb0\xc8'\xa6\xdd>=3O\u0349\x03\t'\xf7L\x9d\xe0\x00\x00\u07d4#8B\xb1\xd0i/\xd1\x11@\xcfZ\u0364\xbf\x960\xba\xe5\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#9\xe9I(p\xaf\xea%7\xf3\x89\xac/\x83\x83\x02\xa3<\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#;\xdd\xdd]\xa9HR\xf4\xad\xe8\xd2\x12\x88V\x82\xd9\ak\u0189\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#OF\xba\xb7?\xe4]1\xbf\x87\xf0\xa1\xe0Fa\x99\xf2\ubb09\x1aJ\xba\"\\ t\x00\x00\u07d4#U\x1fV\x97_\xe9+1\xfaF\x9cI\xeaf\xeefb\xf4\x1e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4#V\x95B\xc9}V`\x18\xc9\a\xac\xfc\xf3\x91\xd1@g\xe8~\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94#_\xa6l\x02^\xf5T\x00p\xeb\xcf\r7-\x81w\xc4g\xab\x8a\a\x12\x9e\x1c\xdf7>\xe0\x00\x00\xe0\x94#r\xc4\xc1\u0253\x9fz\xafl\xfa\xc0@\x90\xf0\x04t\x84\n\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4#s\f5z\x91\x02nD\xb1\xd0\xe2\xfc*Q\xd0q\xd8\xd7{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#v\xad\xa9\x033\xb1\u0441\bL\x97\xe6E\xe8\x10\xaa[v\xf1\x89(\xa8WBTf\xf8\x00\x00\u07d4#x\xfdC\x82Q\x1e\x96\x8e\u0452\x10g7\xd3$\xf4T\xb55\x8965\u026d\xc5\u07a0\x00\x00\u07d4#\x82\xa9\u050e\xc8>\xa3e(\x90\xfd\x0e\u7710{[-\xc1\x89\a?u\u0460\x85\xba\x00\x00\u07d4#\x83\xc2\"\xe6~\x96\x91\x90\xd3!\x9e\xf1M\xa3xP\xe2lU\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\x8akv5%/RDHl\n\xf0\xa7: s\x85\xe09\x89JD\x91\xbdm\xcd(\x00\x00\u07d4#\x9as>k\x85Z\u0152\xd6c\x15a\x86\xa8\xa1t\xd2D\x9e\x89X\xbe7X\xb2A\xf6\x00\x00\xe0\x94#\xab\t\xe7?\x87\xaa\x0f;\xe0\x13\x9d\xf0\xc8\xebk\xe5cO\x95\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94#\xab\xd9\xe9>yW\xe5\xb66\xbeey\x05\x1c\x15\xe5\xce\v\x0e\x8a\x03\xa3\xc8\xf7\xcb\xf4,8\x00\x00\u07d4#\xb1\u0111\u007f\xbd\x93\xee=H8\x93\x06\x95s\x84\xa5Il\xbf\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94#\xba8d\xdaX=\xabV\xf4 \x87<7g\x96\x90\xe0/\x00\x8a\x02\x13BR\r_\xec \x00\x00\u07d4#\xc5Z\xebW9\x87o\n\xc8\xd7\xeb\xea\x13\xber\x96\x85\xf0\x00\x89Hz\x9a0E9D\x00\x00\u07d4#\u025b\xa0\x87D\x8e\x19\xc9p\x1d\xf6n\f\xabR6\x831\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xcc\xc3\u01ac\xd8\\.F\fO\xfd\xd8+\xc7]\xc8I\xea\x14\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#\xcd%\x98\xa2\x0e\x14\x9e\xad*\u0593yWn\xce\xdb`\u3389lk\x93[\x8b\xbd@\x00\x00\u07d4#\u07cfH\xee\x00\x92V\xeay~\x1f\xa3i\xbe\xeb\xcfk\xc6c\x89|\xd3\xfa\xc2m\x19\x81\x80\x00\u07d4#\xe2\u01a8\xbe\x8e\n\u03e5\xc4\xdf^6\x05\x8b\xb7\u02ecZ\x81\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xeaf\x9e5d\x81\x9a\x83\xb0\xc2l\x00\xa1m\x9e\x82olF\x89M\x8dl\xa9h\xca\x13\x00\x00\u07d4#\xebo\xd8Vq\xa9\x06:\xb7g\x8e\xbe&Z \xf6\x1a\x02\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xf9\xec\xf3\xe5\xdd\u0723\x88\x15\xd3\xe5\x9e\xd3K[\x90\xb4\xa3S\x89\v\x17\x81\xa3\xf0\xbb \x00\x00\u07d4#\xfa~\xb5\x1aH\"\x95\x98\xf9~v+\xe0\x86\x96R\xdf\xfcf\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94$\x03\x05rs\x13\xd0\x1esT,w_\xf5\x9d\x11\xcd5\xf8\x19\x8a\x01A\x88Vf\x80\u007f\\\x80\x00\u07d4$\x04k\x91\u069ba\xb6)\u02cb\x8e\xc0\xc3Q\xa0~\a\x03\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4$\x0eU\x9e'J\xae\xf0\xc2X\x99\x8c\x97\x9fg\x1d\x11s\xb8\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94$\x13aU\x9f\xee\xf8\x0e\xf170!S\xbd\x9e\xd2\xf2]\xb3\xef\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94$;;\xcaj)\x93Y\xe8\x86\xce3\xa3\x03A\xfa\xfeMW=\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4$<\x84\xd1$ W\f\xc4\xef;\xab\xa1\xc9Y\u0083$\x95 \x89\u007f\x1fi\x93\xa8S\x04\x00\x00\xe0\x94$CJ>2\xe5N\xcf'/\xe3G\v_oQ/gU \x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4$HYo\x91\xc0\x9b\xaa0\xbc\x96\x10j-7\xb5p^](\x89lk\x93[\x8b\xbd@\x00\x00\u0794$Xn\xc5E\x175\xee\xaa\xebG\r\xc8sj\xaeu/\x82\xe5\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4$X\xd6U_\xf9\x8a\x12\x9c\xce@7\x95=\x00 n\xffB\x87\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4$b\x91\x16[Y3-\xf5\xf1\x8c\xe5\u0248V\xfa\xe9X\x97\u0589\\(=A\x03\x94\x10\x00\x00\u07d4$g\u01a5\u0196\xed\xe9\xa1\xe5B\xbf\x1a\xd0k\xccK\x06\xac\xa0\x89\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4$v\xb2\xbbu\x1c\xe7H\xe1\xa4\xc4\xff{#\v\xe0\xc1]\"E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4$z\n\x11\xc5\u007f\x03\x83\xb9I\xdeT\vf\xde\xe6\x86\x04\xb0\xa1\x899\xfb\xae\x8d\x04-\xd0\x00\x00\u07d4$\x87\xc3\u013e\x86\xa2r=\x91|\x06\xb4XU\x01p\xc3\xed\xba\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\x89\xac\x12i4\xd4\u05a9M\xf0\x87C\xda{v\x91\xe9y\x8e\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\x9d\xb2\x9d\xbc\x19\xd1#]\xa7)\x8a\x04\b\x1c1WB\u9b09a\xac\xff\x81\xa7\x8a\xd4\x00\x00\u07d4$\xa4\xeb6\xa7\xe4\x98\xc3o\x99\x97\\\x1a\x8dr\x9f\u05b3\x05\u05c9\r\xfcx!\x0e\xb2\xc8\x00\x00\u07d4$\xa7P\xea\xe5\x87G\x11\x11m\xd7\xd4{q\x86\u0399\r1\x03\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4$\xaa\x11Q\xbbv_\xa3\xa8\x9c\xa5\x0e\xb6\xe1\xb1\xc7\x06A\u007f\u0509\xa8\r$g~\xfe\xf0\x00\x00\u0794$\xac\xa0\x8d[\xe8^\xbb\x9f12\xdf\xc1\xb6 \x82N\xdf\xed\xf9\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4$\xb2\xbe\x11\x8b\x16\u0632\x17Gi\xd1{L\xf8O\a\u0294m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4$\xb8\xb4F\u07bd\x19G\x95]\u0404\xf2\xc5D\x933F\u04ed\x89\xeaim\x90@9\xbd\x80\x00\u07d4$\xb9^\xbe\xf7\x95\x00\xba\xa0\xed\xa7.w\xf8wA]\xf7\\3\x891T\xc9r\x9d\x05x\x00\x00\u07d4$\xb9\xe6dOk\xa4\xcd\xe1&'\r\x81\xf6\xab`\xf2\x86\xdf\xf4\x89\a?u\u0460\x85\xba\x00\x00\u07d4$\xbdY\x04\x05\x90\x91\xd2\xf9\xe1-j&\xa0\x10\xca\"\xab\x14\xe8\x89e\xea=\xb7UF`\x00\x00\u07d4$\xc0\u020bT\xa3TG\t\x82\x8a\xb4\xab\x06\x84\x05Y\xf6\xc5\u2250\xf54`\x8ar\x88\x00\x00\u07d4$\xc1\x17\xd1\u04b3\xa9z\xb1\x1aFy\u025awJ\x9e\xad\xe8\u044965\u026d\xc5\u07a0\x00\x00\u07d4$\xcf\xf0\xe93j\x9f\x80\xf9\xb1\u02d6\x8c\xafk\x1d\x1cI2\xa4\x89\n\xdaUGK\x814\x00\x00\u07d4$\u06aa\xdd\xf7\xb0k\xbc\ua6c0Y\x00\x85\xa8\x85gh+N\x89\x11K \x15\u04bb\xd0\x00\x00\u07d4$\xdc\xc2K\xd9\xc7!\f\xea\u03f3\r\xa9\x8a\xe0JM{\x8a\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\xf7E\r\xdb\xf1\x8b\x02\x0f\xeb\x1a 2\xd9\xd5Kc>\xdf7\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4$\xfcs\xd2\a\x93\t\x8e\t\u076bW\x98Pb$\xfa\x1e\x18P\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4$\xfd\x9al\x87L/\xab?\xf3n\x9a\xfb\xf8\xce\r2\xc7\u0792\x89Hz\x9a0E9D\x00\x00\u07d4%\n@\xce\xf3 #\x97\xf2@F\x95H\xbe\xb5bj\xf4\xf2<\x89\x05\x03\xb2\x03\xe9\xfb\xa2\x00\x00\u07d4%\niC\av\xf64w\x03\xf9R\x97\x83\x95Za\x97\xb6\x82\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4%\x0e\xb7\xc6o\x86\x9d\xdfI\u0685\xf39>\x98\f\x02\x9a\xa44\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%\x10j\xb6u]\xf8mkc\xa1\x87p;\f\xfe\xa0\u5520\x89\x01|@Z\xd4\x1d\xb4\x00\x00\xe0\x94%\x18_2Z\xcf-dP\x06\x98\xf6\\v\x9d\xdfh0\x16\x02\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4%\x1c\x12r,hy\"y\x92\xa3\x04\xeb5v\xcd\x18CN\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\x1eh8\xf7\xce\u0173\x83\xc1\xd9\x01F4\x12t\xda\xf8\xe5\x02\x89\a\xff\x1c\xcbua\xdf\x00\x00\u07d4%%\x9d\x97Z!\xd8:\xe3\x0e3\xf8\x00\xf5?7\u07e0\x198\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4%({\x81_\\\x828\ns\xb0\xb1?\xba\xf9\x82\xbe$\xc4\u04c9\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94%+eU\xaf\u0700\xf2\xd9m\x97-\x17\u06c4\xeaZ\xd5!\xac\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4%8S)6\x81<\x91\xe6S(O\x01|\x80\u00f8\xf8\xa3o\x89l\x87T\xc8\xf3\f\b\x00\x00\xe0\x94%>2\xb7N\xa4I\n\xb9&\x06\xfd\xa0\xaa%{\xf2=\u02cb\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94%?\x1et*,\uc1b0\u05f3\x06\xe5\xea\xcbl\xcb/\x85T\x8a\x04>^\xde\x1f\x87\x8c \x00\x00\u07d4%A1J\v@\x8e\x95\xa6\x94DIwq*Pq5\x91\xab\x89X\x9e\x1a]\xf4\u05f5\x00\x00\u07d4%L\x1e\xccc\f(w\u0780\x95\xf0\xa8\u06e1\xe8\xbf\x1fU\f\x89\\(=A\x03\x94\x10\x00\x00\u07d4%Z\xbc\x8d\b\xa0\x96\xa8\x8f=j\xb5_\xbcsR\xbd\u0739\u0389\x04t6\x821>\u0780\x00\u07d4%[\xdddt\u0302b\xf2j\"\u00cfE\x94\x0e\x1c\ue99b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%`\xb0\x9b\x89\xa4\xaehI\xedZ<\x99XBf1qDf\x89\\(=A\x03\x94\x10\x00\x00\u07d4%a\xa18\xdc\xf8;\xd8\x13\xe0\xe7\xf1\bd+\xe3\xde=o\x05\x8964\xf4\x84\x17@\x1a\x00\x00\u0794%a\xec\x0f7\x92\x18\xfe^\xd4\xe0(\xa3\xf7D\xaaAuLr\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u0794%b\x92\xa1\x91\xbd\xda4\xc4\xdakk\u0591G\xbfu\u2a6b\x88\xc2\xff.\r\xfb\x03\x80\x00\u07d4%i~\xf2\f\u032ap\xd3-7o\x82r\xd9\xc1\a\f=x\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4%o\xa1P\u0307\xb5\x05j\a\xd0\x04\xef\xc8E$s\x9eb\xb5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4%r\x1c\x87\xb0\xdc!7|r\x00\xe5$\xb1J\"\xf0\xafi\xfb\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%\x899\xbb\xf0\f\x9d\xe9\xafS8\xf5\xd7\x14\xab\xf6\xd0\xc1\xc6q\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94%\x90\x12hp\xe0\xbd\xe8\xa6c\xab\x04\nr\xa5W=\x8dA\u008a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4%\x9e\xc4\xd2e\xf3\xabSk|p\xfa\x97\xac\xa1Bi,\x13\xfc\x89\x01\x1b\x1b[\xea\x89\xf8\x00\x00\xe0\x94%\xa5\x00\xee\xeczf*\x84\x15R\xb5\x16\x8bp{\r\xe2\x1e\x9e\x8a\x02\x1f/o\x0f\xc3\xc6\x10\x00\x00\xe0\x94%\xa5\xa4M8\xa2\xf4Lj\x9d\xb9\u037ck\x1e.\x97\xab\xb5\t\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4%\xa7L*\xc7]\u023a\xa8\xb3\x1a\x9c|\xb4\xb7\x82\x9b$V\u0689lk\x93[\x8b\xbd@\x00\x00\xe0\x94%\xad\xb8\xf9o9I,\x9b\xb4|^\u0708bNF\aV\x97\x8a\x05\xa9\x94\v\xc5hyP\x00\x00\u07d4%\xae\xe6\x8d\t\xaf\xb7\x1d\x88\x17\xf3\xf1\x84\xecV/x\x97\xb74\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\xb0S;\x81\xd0*a{\x92)\xc7\xec]o/g.[Z\x8965\u026d\xc5\u07a0\x00\x00\u07d4%\xb7\x8c\x9f\xad\x85\xb43C\xf0\xbf\xcd\x0f\xac\x11\u0254\x9c\xa5\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\xbcI\xef(\x8c\xd1e\xe5%\xc6a\xa8\x12\u03c4\xfb\xec\x8f3\x89\x12Y!\xae\xbd\xa9\xd0\x00\x00\u07d4%\xbd\xfa>\xe2o8Ia{#\x00bX\x8a\x97\xe3\xca\xe7\x01\x8965\xe6\x19\xbb\x04\xd4\x00\x00\u07d4%\xc1\xa3~\xe5\xf0\x82e\xa1\xe1\r=\x90\xd5G)U\xf9x\x06\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4%\xc6\xe7O\xf1\xd9(\u07d8\x13z\xf4\u07c40\xdf$\xf0|\u05c9\x15$VU\xb1\x02X\x00\x00\xe0\x94%\xcf\xc4\xe2\\5\xc1;i\xf7\xe7}\xbf\xb0\x8b\xafXuk\x8d\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94%\xda\u0515\xa1\x1a\x86\xb9\xee\xec\xe1\xee\xec\x80^W\xf1W\xfa\xff\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94%\xe07\xf0\n\x18'\v\xa5\xec4 \"\x9d\xdb\n,\u33e2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4%\xe6a\xc99\x86:\xcc\x04No\x17\xb5i\x8c\xce7\x9e\xc3\u0309JD\x91\xbdm\xcd(\x00\x00\u07d4&\x04\x8f\xe8M\x9b\x01\nb\xe71b~I\xbc.\xb7?@\x8f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4&\x06\u00f3\xb4\xca\x1b\t\x14\x98`,\xb1\x97\x8b\xf3\xb9R!\xc0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4&\n#\x0eDe\a~\v\x14\xeeDB\xa4\x82\u0570\xc9\x14\xbf\x89Z\xf6\x06\xa0k[\x11\x80\x00\u07d4&\r\xf8\x94:\x8c\x9a]\xbayE2\u007f\xd7\xe0\x83|\x11\xad\a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4&\x14\xf4-]\xa8D7ux\xe6\xb4H\xdc$0[\xef+\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\x15\x10\x0e\xa7\xe2[\xba\x9b\xcat`X\xaf\xbb\xb4\xff\xbeBD\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4&\x15u\xe9\xcfY\xc8\"o\xa7\xaa\xf9\x1d\xe8o\xb7\x0fZ\u00ee\x89\x10C\xa4CjR?\x00\x00\xe0\x94&\x1e\x0f\xa6LQ\x13te\xee\xcf[\x90\xf1\x97\xf7\x93\u007f\xdb\x05\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4&*\x8b\xfd}\x9d\xc5\xdd:\u05c1a\xb6\xbbV\b$76U\x89?j\x83\x84\a+v\x00\x00\xe0\x94&*\xedK\xc0\xf4\xa4\xb2\xc6\xfb5y>\x83ZI\x18\x9c\xdf\xec\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94&-\xc16L\xcfm\xf8\\C&\x8e\xe1\x82UM\xaei.)\x8a\x01\v /\xect\xce\xd8\x00\x00\u07d4&8\x140\x9d\xe4\xe65\xcfX^\r6Tw\xfc@\xe6l\xf7\x89\a\xea(2uw\b\x00\x00\u07d4&9\xee\xe9\x87<\xee\xc2o\u0314T\xb5H\xb9\xe7\xc5J\xa6\\\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94&>W\xda\xcb\xe0\x14\x9f\x82\xfee\xa2fH\x98\x86o\xf5\xb4c\x8a\b\v\xfb\xef\xcb_\v\xc0\x00\x00\u07d4>\x19\xc0m_\x14z\xa5\x97$\x8e\xb4l\xf7\xbe\xfad\xa5\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4&L\xc8\bj\x87\x10\xf9\x1b!r\t\x05\x91,\u05d6J\xe8h\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94&S\x83\u058bR\xd04\x16\x1b\xfa\xb0\x1a\xe1\xb0G\x94/\xbc2\x8a\x04rq\xde\xe2\rt\\\x00\x00\u07d4&Y\xfa\xcb\x1e\x83CeS\xb5\xb4)\x89\xad\xb8\a_\x99S\xed\x89\x01\x97evw\x1a^\x00\x00\xe0\x94&o-\xa7\xf0\b^\xf3\xf3\xfa\t\xba\xee#+\x93\xc7D\xdb.\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4&qH\xfdr\xc5Ob\nY/\xb9'\x991\x9c\xc4S+\\\x89\x169\u46fa\x16(\x00\x00\xe0\x94&xJ\u0791\u0228:\x8e9e\x8c\x8d\x82wA<\u0319T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4&z~n\x82\xe1\xb9\x1dQ\xde\u0776D\xf0\xe9m\xbb\x1f\u007f~\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4&\x80q=@\x80\x8e*P\xed\x011P\xa2\xa6\x94\xb9j\u007f\x1d\x89a\t=|,m8\x00\x00\u07d4&\x97\xb39\x81;\f-\x96K$q\xeb\x1c`oN\u02d6\x16\x89>\x8e\xf7\x95\u0610\xc8\x00\x00\u07d4&\xa6\x8e\xab\x90Z\x8b=\xce\x00\xe3\x170\x82%\u06b1\xb9\xf6\xb8\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4&\xb1\x1d\x06e\x88\xcet\xa5r\xa8Zc(s\x92\x12\xaa\x8b@\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xba\xbfB\xb2g\xfd\xcf8a\xfd\xd4#j^GHH\xb3X\x8965\u026d\xc5\u07a0\x00\x00\u07d4&\xc0\x05Kp\r:|-\xcb\xe2uh\x9dOL\xad\x16\xa35\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xc2\xff\xc3\x0e\xfd\xc5'>v\x18:\x16\xc2i\x8dnS\x12\x86\x89*\x11)\u0413g \x00\x00\u07d4&\u025f\x88I\u0240+\x83\xc8a!\u007f\xd0z\x9e\x84\u0377\x9d\x89\x10CV\x1a\x88)0\x00\x00\u07d4&\xcf\xff\xd0R\x15+\xb3\xf9W\xb4x\xd5\xf9\x8b#:|+\x92\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4&\u0521h\x91\xf5)\"x\x92\x17\xfc\u0606\xf7\xfc\xe2\x96\xd4\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xd4\xec\x17\xd5\u03b2\u0214\xbd\u015d\nji]\xad+C\u0309\x9f\x1fxv\x1d4\x1a\x00\x00\u07d4&\xe8\x01\xb6,\x82q\x91\xddh\xd3\x1a\x01\x19\x90\x94\u007f\xd0\xeb\xe0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4&\xe9\xe2\xadr\x97\x02bd\x17\xef%\xde\r\xc8\x00\xf7\xa7y\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4&\xf9\xf7\xce\xfd~9K\x9d9$A+\xf2\u0083\x1f\xaf\x1f\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94&\xfe\x17L\xbfRfP\xe0\xcd\x00\x9b\xd6\x12e\x02\u038ehM\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\xe0\x94&\xff\nQ\xe7\xce\u0384\x00'ix\xdb\xd6#n\xf1b\xc0\xe6\x8a\x15.\x18V'T\nP\x00\x00\u07d4'\x10\x1a\x0fV\u04da\x88\u0168O\x9b2L\xdd\xe3>\\\xb6\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'\x14L\xa9\xa7w\x1a\x83j\xd5\x0f\x80?d\xd8i\xb2\xae+ \x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4'\x14i\x13V:\xa7E\xe2X\x840\xd94\x8e\x86\xea|5\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4'\x1d=H\x1c\xb8\x8evq\xad!iI\xb66^\x060=\xe0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4' \xf9\xcaBn\xf2\xf2\xcb\xd2\xfe\xcd9\x92\fO\x1a\x89\xe1m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'*\x13\x1aZejz:\xca5\u023d \"\"\xa7Y\"X\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4'D\xffgFA!\xe3Z\xfc)\"\x17qd\xfa/\xcb\x02g\x89\x05k\xc7^-c\x10\x00\x00\u07d4'J=w\x1a=p\x97\x96\xfb\xc4\xd5\xf4\x8f\xce/\xe3\x8cy\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4'Mi\x17\x0f\xe7\x14\x14\x01\x88+\x88j\xc4a\x8cj\xe4\x0e\u06c93\xc5I\x901r\f\x00\x00\u07d4'R\x1d\xeb;n\xf1An\xa4\u01c1\xa2\xe5\u05f3n\xe8\x1ca\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'Xu\xffO\xbb\f\xf3\xa40!1'H\u007fv\b\xd0L\xba\x89\x1b\x1c\x01\x0evmX\x00\x00\u07d4'j\x00n0(\xec\xd4L\xdbb\xba\nw\u0394\xeb\xd9\xf1\x0f\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4'k\x05!\xb0\xe6\x8b'}\xf0\xbb2\xf3\xfdH2cP\xbf\xb2\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4'o\xd7\xd2O\x8f\x88?Zz()[\xf1qQ\u01e8K\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'p\xf1N\xfb\x16]\u07bay\xc1\v\xb0\xaf1\xc3\x1eY3L\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4'vw\xab\xa1\xe5,;S\xbf\xa2\a\x1dN\x85\x9a\n\xf7\xe8\xe1\x8965\u026d\xc5\u07a0\x00\x00\u07d4'\x82Ff\xd2x\xd7\x04#\xf0=\xfe\x1d\u01e3\xf0/C\u2d4966\xc2^f\xec\xe7\x00\x00\u07d4'\x83\f_`#\xaf\xaa\xf7\x97Egl J\x0f\xac\u0360\xba\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94'\x84\x90?\x1d|\x1b\\\xd9\x01\xf8\x87]\x14\xa7\x9b<\xbe*V\x8a\x04\xbd\xa7\xe9\xd7J\xd5P\x00\x00\u07d4'\x8c\v\xdec\x0e\u00d3\xb1\xe7&\u007f\xc9\xd7\xd9p\x19\xe4\x14[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'\x98q\x10\"\x1a\x88\b&\xad\xb2\xe7\xab^\xcax\xc6\xe3\x1a\xec\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94'\xac\a;\xe7\x9c\xe6W\xa9:\xa6\x93\xeeC\xbf\x0f\xa4\x1f\xef\x04\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4'\xb1iN\xaf\xa1e\xeb\xd7\xcc{\u025et\x81J\x95\x14\x19\u0709+^:\xf1k\x18\x80\x00\x00\u07d4'\xb6(\x16\xe1\xe3\xb8\u045by\xd1Q=]\xfa\x85[\f:*\x89\x05j\xf5\xc1\xfdiP\x80\x00\u07d4'\xbf\x94<\x163\xfe2\xf8\xbc\xcc\xdbc\x02\xb4\a\xa5rND\x892\xf8Lm\xf4\b\xc0\x80\x00\u07d4'\xbf\x9fD\xba}\x05\xc35@\u00e5;\xb0,\xbb\xff\xe7\xc3\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4'\xc2\xd7\xcaPM\xaa=\x90f\xdc\t\x13}\xc4/:\xaa\xb4R\x89 \x86\xac5\x10R`\x00\x00\u07d4'\xd1X\xac=>\x11\t\xabnW\x0e\x90\xe8]8\x92\xcdv\x80\x89\x05k\xc7^-c\x10\x00\x00\u07d4'\xe69\x89\xca\x1e\x90;\xc6 \xcf\x1b\x9c?g\xb9\xe2\xaee\x81\x89Hz\x9a0E9D\x00\x00\xe0\x94'\xf0<\xf1\xab\xc5\xe1\xb5\x1d\xbcDK(\x9eT,\x9d\u07f0\xe6\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4'\xfc\x85\xa4\x9c\xff\x90\xdb\xcf\xda\u071d\xdd@\u05b9\xa2!\nl\x89\x05k\xc7^-c\x10\x00\x00\u07d4(\x05A^\x1d\u007f\xde\xc6\xde\u07f8\x9eR\x1d\x10Y-t<\x10\x89\x05k\xc7^-c\x10\x00\x00\u07d4(\a>\xfc\x17\xd0\\\xab1\x95\xc2\xdb3+a\x98Gw\xa6\x12\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x12P\xa2\x91!'\nN\xe5\u05cd$\xfe\xaf\xe8,p\xba:\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x13\xd2c\xfc_\xf2G\x9e\x97\x05\x95\u05b6\xb5`\xf8\xd6\xd6\u0449lk\x93[\x8b\xbd@\x00\x00\u07d4(.\x80\xa5T\x87ZVy\x9f\xa0\xa9\u007fU\x10\u7557LN\x8965\u026d\xc5\u07a0\x00\x00\u07d4(3\x96\xce<\xac9\x8b\xcb\xe7\"\u007f2>x\xff\x96\u0407g\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4(4\x9f~\xf9t\xeaU\xfe6\xa1X;4\xce\xc3\xc4Pe\xf0\x89\f\xb63\u051eeY\x00\x00\u07d4(6\x120F\xb2\x84\xe5\xef\x10+\xfd\"\xb1v^P\x81\x16\xad\x89\x16S\xfb\xb5\xc4'\xe4\x00\x00\u07d4(<#\x14(<\x92\u0530d\xf0\xae\xf9\xbbRF\xa7\x00\u007f9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4(>\x11 7I\xb1\xfaO2\xfe\xbbq\xe4\x9d\x13Y\x198*\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94(>bR\xb4\xef\xcfFT9\x1a\xcbu\xf9\x03\u015bx\xc5\xfb\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94(Q\x0en\xff\x1f\xc8)\xb6WoC(\xbc98\xecze\x80\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4(X\xac\xac\xaf!\xea\x81\u02b7Y\x8f\xdb\xd8kE.\x9e\x8e\x15\x89$\x1a\x9bOaz(\x00\x00\u07d4(Z\xe5\x1b\x95\x00\u014dT\x13e\xd9ui\xf1K\xb2\xa3p\x9b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(f\xb8\x1d\xec\xb0.\xe7\n\xe2P\xce\xe5\xcd\xc7{Y\u05f6y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(i\x06\xb6\xbdIr\xe3\xc7\x16U\xe0K\xaf6&\f|\xb1S\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4(k\x18ma\xea\x1f\u05cd\x990\xfe\x12\xb0e7\xb0\\=Q\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94(t\xf3\xe2\x98]_{@f'\xe1{\xaaw+\x01\xab\u031e\x8a\x01F\x05\x04\x10v_8\x00\x00\xe0\x94(|\xf9\u0410.\xf8\x19\xa7\xa5\xf1ID[\xf1w^\xe8\xc4|\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4(\x81\x8e\x18\xb6\x10\x00\x13!\xb3\x1d\xf6\xfe}(\x15\u036d\xc9\xf5\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x86\x83$3~\x11\xba\x10l\xb4\x81\u0696/:\x84S\x80\x8d\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94(\x90K\xb7\xc40)C\xb7\t\xb1Myp\xe4+\x83$\u184a\x02\x1f\x97\x84j\a-~\x00\x00\u07d4(\x95\xe8\t\x99\xd4\x06\xadY.+&'7\xd3_}\xb4\xb6\x99\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4(\x96r\x80!N!\x8a\x12\f]\xda7\x04\x1b\x11\x1e\xa3mt\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4(\xa3\xda\t\xa8\x19H\x19\xae\x19\x9f.m\x9d\x13\x04\x81~(\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(\xab\x16_\xfbi\xed\xa0\xc5I\xae8\xe9\x82o_\u007f\x92\xf8S\x89FM\xf6\xd7\xc8DY\x00\x00\u07d4(\xb7u\x85\xcb=U\xa1\x99\xab)\x1d:\x18\u018f\u8684\x8a\x89j@v\xcfy\x95\xa0\x00\x00\xe0\x94(\xd4\xeb\xf4\x1e=\x95\xf9\xbb\x9a\x89u#\\\x1d\x009>\x80\x00\u07d4)\nV\xd4\x1fn\x9e\xfb\xdc\xea\x03B\u0dd2\x9a\x8c\xdf\xcb\x05\x89\x12\xa5\xf5\x81h\xee`\x00\x00\u07d4)\x15bK\xcbg\x917\xb8\xda\xe9\xabW\xd1\x1bI\x05\xea\xeeK\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4)\x1e\xfe\x00\x81\xdc\xe8\xc1G\x99\xf7\xb2\xa46\x19\xc0\u00f3\xfc\x1f\x89A\rXj \xa4\xc0\x00\x00\u07d4)\x1f\x92\x9c\xa5\x9bT\xf8D>=Mu\xd9]\xee$<\xefx\x89\x1b\x1a\b\x927\a=\x00\x00\xe0\x94))\x8c\xcb\xdf\xf6\x89\xf8\u007f\xe4\x1a\xa6\xe9\x8f\u07f5=\xea\xf3z\x8a\x041\\2\xd7\x1a\x9e`\x00\x00\u07d4)/\"\x8b\n\x94t\x8c\x8e\xeca-$o\x98\x93c\xe0\x8f\b\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4)3\x84\xc4+o\x8f)\x05\xceR\xb7 \\\"t7la+\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4)4\xc0\xdf{\xbc\x17+l\x18k\vrTz\u038b\xf7TT\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4)<#\x06\xdf6\x04\xaeO\xda\r z\xbasog\xde\a\x92\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94)I\xfd\x1d\xef\\v\xa2\x86\xb3\x87$$\x80\x9a\a\xdb9f\xf3\x8a\x01\x1b\xd9\x06\u06a0\xc9C\x80\x00\u07d4)OIK?.\x14\xa3\xf8\xab\x00\x00\x00\u07d4)U\xc3W\xfd\x8fu\xd5\x15\x9a=\xfai\u0178z5\x9d\ua309lk\x93[\x8b\xbd@\x00\x00\u07d4)a\xfb9\x1ca\x95|\xb5\xc9\xe4\a\u0762\x938\u04f9,\x80\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4)h\x1d\x99\x12\xdd\xd0~\xaa\xbb\x88\xd0]\x90\xf7f\xe8bA}\x8965\u026d\xc5\u07a0\x00\x00\u07d4)kq\xc0\x01X\x19\xc2B\xa7\x86\x1eo\xf7\xed\xed\x8a_q\xe3\x89lh\xcc\u041b\x02,\x00\x00\u07d4)mf\xb5!W\x1aNA\x03\xa7\xf5b\xc5\x11\xe6\xaas-\x81\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4)o\x00\xde\x1d\u00fb\x01\xd4z\x8c\xcd\x1e]\x1d\u0661\xebw\x91\x8965\u026d\xc5\u07a0\x00\x00\u07d4)s\x85\xe8\x864FV\x85\xc21\xa3\x14\xa0\xd5\xdc\xd1F\xaf\x01\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4)v=\xd6\u069a|\x16\x11s\x88\x83!\ub9b6<\x8f\xb8E\x89\x11\xc7\xea\x16.x \x00\x00\u07d4)yt\x11t\xa8\xc1\xea\v\u007f\x9e\xdfe\x81w\x85\x94\x17\xf5\x12\x89\x19\x01\x96l\x84\x96\x83\x80\x00\u07d4)z\x88\x92\x1b_\xca\x10\u5edd\xed`\x02T7\xae\"\x16\x94\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94)}]\xbe\"//\xb5%1\xac\xbd\v\x01=\xc4F\xacsh\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4)\x82N\x94\xccCH\xbc\x962y\xdc\xdfG9\x17\x152L\u04c9i*\xe8\x89p\x81\xd0\x00\x00\u07d4)\x82\xd7j\x15\xf8G\xddA\xf1\x92*\xf3h\xfeg\x8d\x0eh\x1e\x89\x05k\xc7^-c\x10\x00\x00\u07d4)\x88\x87\xba\xb5|[\xa4\xf0aR)\xd7R_\xa1\x13\xb7\ua249\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94)\x8e\xc7kD\r\x88\a\xb3\xf7\x8b_\x90\x97\x9b\xeeB\xedC\u06ca\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4)\x93h`\x90B\xa8X\xd1\xec\xdf\x1f\xc0\xad\xa5\xea\xce\xca)\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4)\x9e\v\xcaU\xe0i\u0785\x04\xe8\x9a\xcan\xca!\u04ca\x9a]\x89\x03\x027\x9b\xf2\xca.\x00\x00\u07d4)\xac+E\x84T\xa3l~\x96\xc7:\x86g\"*\x12$,q\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94)\xad\u03c3\xb6\xb2\n\u01a44\xab\xb1\x99<\xbd\x05\xc6\x0e\xa2\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94)\xae\xf4\x8d\xe8\xc9\xfb\xadK\x9eL\xa9pyzU3\xebr-\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4)\xb3\xf5a\xeezn%\x94\x1e\x98\xa52[x\xad\u01d7\x85\xf3\x89\x05k\xc7^-c\x10\x00\x00\u07d4)\xbd\xc4\xf2\x8d\xe0\x18\x0fC<&\x94\xebt\xf5PL\xe9C7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4)\u0300M\x92+\xe9\x1fY\t\xf3H\xb0\xaa\xa5\xd2\x1b`x0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94)\xda>5\xb2;\xb1\xf7/\x8e\"X\xcf\u007fU3Y\xd2K\xac\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94)\xe6y\x90\xe1\xb6\xd5.\x10U\xff\xe0I\xc51\x95\xa8\x15B\u03ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4)\uab82v\x17b\xf4\xd2\xdbS\xa9\u018b\x0fk\vmNf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4)\xeb~\xef\xda\xe9\xfe\xb4I\xc6?\xf5\xf2y\xd6u\x10\xeb\x14\"\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4)\xf0\xed\xc6\x038\xe7\x11 \x85\xa1\xd1\x14\u068cB\u038fU\u0589\xa0Z\u007f\x0f\xd8%x\x00\x00\u07d4)\xf8\xfb\xa4\xc3\ar\xb0W\xed\xbb\xe6*\xe7B\f9\x05r\xe1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94)\xf9(l\x0es\x8d\x17!\xa6\x91\u01b9Z\xb3\u0667\x97\xed\xe8\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4*\b^%\xb6Hb\xf5\xe6\x8dv\x8e+\x0fz\x85)\x85\x8e\xee\x89k\x88:\xcdWf\xcd\x00\x00\u07d4**\xb6\xb7Lz\xf1\xd9Gk\xb5\xbc\xb4RG\x97\xbe\xdc5R\x8965\u026d\xc5\u07a0\x00\x00\u07d4*9\x19\nO\u0783\u07f3\xdd\xcbL_\xbb\x83\xaclIu\\\x8965\u026d\xc5\u07a0\x00\x00\u07d4*@\r\xff\x85\x94\xder(\xb4\xfd\x15\xc3#\"\xb7[\xb8}\xa8\x89\x051\xa1\u007f`z-\x00\x00\xe0\x94*D\xa7!\x8f\xe4Me\xa1\xb4\xb7\xa7\u0671\xc2\xc5,\x8c>4\x8a\r-\x06\xc3\x05\xa1\xebW\x80\x00\u07d4*F\xd3Swqv\xff\x8e\x83\xff\xa8\x00\x1fOp\xf9s:\xa5\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4*Y_\x16\xee\xe4\xcb\f\x17\u0662\xd99\xb3\xc1\x0flgrC\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4*Y\xe4~\xa5\xd8\xf0\xe7\xc0(\xa3\xe8\xe0\x93\xa4\x9c\x1bP\xb9\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4*[\xa9\xe3L\u054d\xa5L\x9a'\x12f:;\xe2t\xc8\xe4{\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94*^:@\xd2\xcd\x03%vm\xe7:=g\x18\x96\xb3b\xc7;\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94*cY\x0e\xfe\x99\x86\xc3\xfe\xe0\x9b\n\n3\x8b\x15\xbe\xd9\x1f!\x8a\x01^\x1cN\x05\xee&\xd0\x00\x00\u07d4*gf\n\x13h\xef\xcdbn\xf3k+\x1b`\x19\x80\x94\x1c\x05\x89\a?u\u0460\x85\xba\x00\x00\u07d4*t+\x89\x10\x94\x1e\t2\x83\n\x1d\x96\x92\xcf\u0484\x94\xcf@\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4*tl\xd4@'\xaf>\xbd7\xc3x\xc8^\xf7\xf7T\xab_(\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4*\x81\xd2|\xb6\xd4w\x0f\xf4\xf3\u0123\xba\x18\xe5\xe5\u007f\aQ|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4*\x91\xa9\xfe\xd4\x1b}\x0e\\\xd2\xd81X\xd3\xe8\xa4\x1a\x9a-q\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94*\x9cW\xfe{k\x13\x8a\x92\rgo{\x1a%\x10\x80\xff\xb9\x8a4\xf0\x86\xf3\xb3;h@\x00\x00\u07d4+p\x1d\x16\xc0\xd3\xcc\x1eL\xd8TE\xe6\xad\x02\ue92c\x01-\x89 \x86\xac5\x10R`\x00\x00\xe0\x94+q|\xd42\xa3#\xa4e\x909\x84\x8d;\x87\xde&\xfc\x95F\x8ai\xe1\r\xe7fv\u0400\x00\x00\u07d4+t\xc3s\xd0K\xfb\x0f\xd6\n\x18\xa0\x1a\x88\xfb\xe8Gp\u5309\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4+w\xa4\u060c\rV\xa3\xdb\xe3\xba\xe0J\x05\xf4\xfc\u0477W\xe1\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94+\x84\x88\xbd-<\x19z=&\x15\x18\x15\xb5\xa7\x98\xd2qh\u070a\x01j\x1f\x9f_\xd7\xd9`\x00\x00\u07d4+\x8a\r\xee\\\xb0\xe1\xe9~\x15\xcf\xcan\x19\xad!\xf9\x95\ufb49\x1bUC\x8d\x9a$\x9b\x00\x00\xe0\x94+\x8f\xe4\x16n#\xd1\x19c\xc0\x93+\x8a\u078e\x01E\xea\ap\x8a\t(\x96R\x9b\xad\u0708\x00\x00\xe0\x94+\x99\xb4.OBa\x9e\xe3k\xaa~J\xf2\xd6^\xac\xfc\xba5\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4+\xab\x0f\xbe(\u0544 \xb5 6w\n\x12\xf9\x95*\xeai\x11\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4+\xad\xe9\x1d\x15E\x17b\x0f\u05349\xac\x97\x15zA\x02\xa9\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4+\xaf\x8dn\"\x11t\x12H \xeeI+\x94Y\xecO\xad\xaf\xbb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4+\xaf\xbf\x9e\x9e\xd2\xc2\x19\xf7\xf2y\x13t\xe7\xd0\\\xb0gw\xe7\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\xe0\x94+\xb3f\xb9\xed\xcb\r\xa6\x80\xf0\xe1\v;n(t\x81\x90\xd6\u00ca\x01:b\u05f5v@d\x00\x00\xe0\x94+\xb6\xf5x\xad\xfb\u7ca1\x16\xb3UO\xac\xf9\x96\x98\x13\xc3\x19\x8a\x01\x91'\xa19\x1e\xa2\xa0\x00\x00\u07d4+\xbeb\xea\xc8\f\xa7\xf4\xd6\xfd\xee~}\x8e(\xb6:\xcfw\x0e\x89\x81\xe3-\xf9r\xab\xf0\x00\x00\u07d4+\xbeg*\x18WP\x8fc\x0f*^\xdbV=\x9e\x9d\xe9(\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4+\xc4)\xd6\x18\xa6jL\xf8-\xbb-\x82N\x93V\xef\xfa\x12j\x89lj\xccg\u05f1\xd4\x00\x00\u07d4+\xd2R\xe0\xd72\xff\x1d|x\xf0\xa0.l\xb2T#\xcf\x1b\x1a\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4+\xdd\x03\xbe\xbb\xee';l\xa1\x05\x9b4\x99\x9a[\xbda\xbby\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,\x04\x11\\>R\x96\x1b\r\xc0\xb0\xbf1\xfb\xa4ToYf\xfd\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94,\x06\u0752+aQJ\xaf\xed\xd8D\x88\xc0\u008em\xcf\x0e\x99\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94,\f\xc3\xf9QH,\u0222\x92X\x15hN\xb9\xf9N\x06\x02\x00\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4,\x0e\xe14\u0633aE\xb4{\xee\u7bcd'8\xdb\xda\b\xe8\x89\n\xe5os\x0em\x84\x00\x00\u07d4,\x0f[\x9d\xf46%y\x8e~\x03\xc1\xa5\xfdjm\t\x1a\xf8+\x89\x01\xb0\xfc\xaa\xb2\x000\x00\x00\u07d4,\x12\x8c\x95\xd9W!Q\x01\xf0C\u074f\u0142EmA\x01m\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4,\x18\x00\xf3_\xa0->\xb6\xff[%(_^J\xdd\x13\xb3\x8d\x891\"\u04ed\xaf\xde\x10\x00\x00\u07d4,\x1c\x19\x11N=m\xe2xQHK\x8d'\x15\xe5\x0f\x8a\x10e\x89\x05k\xc7^-c\x10\x00\x00\u07d4,\x1c\xc6\xe1\x8c\x15$\x88\xba\x11\xc2\xcc\x1b\xce\xfa-\xf3\x06\xab\u0449Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94,\x1d\xf8\xa7oH\xf6\xb5K\u03dc\xafV\xf0\xee\x1c\xf5z\xb3=\x8a\x02$\u007fu\x00\x89\xdaX\x00\x00\u07d4,!G\x94z\xe3?\xb0\x98\xb4\x89\xa5\xc1k\xff\xf9\xab\xcdN*\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4,#OP\\\xa8\xdc\xc7}\x9b~\x01\xd2W\xc3\x18\xcc\x199m\x89\x05k\xc7^-c\x10\x00\x00\u07d4,$(\xe4\xa6it\xed\xc8\"\xd5\xdb\xfb$\x1b'(\aQX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,-\x15\xff9V\x1c\x1br\xed\xa1\xcc\x02\u007f\xfe\xf27C\xa1D\x89\u0500\xed\x9e\xf3+@\x00\x00\u07d4,-\xb2\x8c3\t7^\xea1\x82\x1b\x84\xd4\b\x93\x0e\xfa\x1a\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4,Z-\n\xbd\xa0;\xbe!W\x81\xb4\xff)l\x8ca\xbd\xba\xf6\x89\x01\xa8\xe5oH\xc0\"\x80\x00\u07d4,[}{\x19Z7\x1b\xf9\xab\u0774/\xe0O/\x1d\x9a\x99\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4,]\xf8ffj\x19K&\u03bb@~J\x1f\xd7> \x8d^\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94,`?\xf0\xfe\x93alCW>\xf2y\xbf\xea@\x88\x8dj\xe7\x8a\x01\x00\xf4\xb6\xd6gW\x90\x00\x00\xe0\x94,hF\xa1\xaa\x99\x9a\"F\xa2\x87\x05`\x00\xbaM\u02e8\xe6=\x8a\x02\x1f/o\x0f\xc3\xc6\x10\x00\x00\u0794,j\xfc\xd4\x03|\x1e\xd1O\xa7O\xf6u\x8e\tE\xa1\x85\xa8\xe8\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4,ki\x9d\x9e\xad4\x9f\x06\u007fEq\x1a\aJd\x1d\xb6\xa8\x97\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,o\\\x12L\u01c9\xf8\xbb9\x8e?\x88\x97Q\xbcK`-\x9e\x89\x01Y\xf2\v\xed\x00\xf0\x00\x00\u07d4,\x83\xae\xb0/\xcf\x06}e\xa4p\x82\xfd\x97x3\xab\x1c\uc449\b'8#%\x8a\xc0\x00\x00\xe0\x94,\x89\xf5\xfd\xca=\x15T\t\xb68\xb9\x8at.U\xebFR\xb7\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4,\x96HI\xb1\xf6\x9c\xc7\u03a4D%8\xed\x87\xfd\xf1l\xfc\x8f\x89lk\x93[\x8b\xbd@\x00\x00\u0794,\x9f\xa7,\x95\xf3}\b\xe9\xa3`\t\u7930\u007f)\xba\xd4\x1a\x88\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4,\xafk\xf4\xec}Z\x19\xc5\xe0\x89z^\xeb\x01\x1d\xce\xceB\x10\x89\a\x93H5\xa01\x16\x00\x00\u07d4,\xb4\xc3\xc1k\xb1\xc5^|kz\x19\xb1'\xa1\xac\x93\x90\xcc\t\x89\xb8'\x94\xa9$O\f\x80\x00\xe0\x94,\xb5IZPS6\xc2FT\x10\xd1\xca\xe0\x95\xb8\xe1\xba\\\u074a\x04<3\xc1\x93ud\x80\x00\x00\u07d4,\xb6\x15\a:@\xdc\u06d9\xfa\xa8HW.\x98{;\x05n\xfb\x89+X\xad\u06c9\xa2X\x00\x00\u07d4,\xbam]\r\xc2\x04\xea\x8a%\xad\xa2\xe2oVu\xbd_/\u0709H#\xef}\u06da\xf3\x80\x00\u07d4,\xbb\fs\u07d1\xb9\x17@\xb6i;wJ}\x05\x17~\x8eX\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4,\xcbfIM\n\xf6\x89\xab\xf9H=6]x$D\xe7\u07ad\x8965\u026d\xc5\u07a0\x00\x00\u07d4,\xcc\x1f\x1c\xb5\xf4\xa8\x00.\x18k \x88]\x9d\xbc\x03\f\b\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,\u03c0\xe2\x18\x98\x12^\xb4\xe8\a\u0342\xe0\x9b\x9d(Y/n\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,\u0456\x94\u0452j\x0f\xa9\x18\x9e\u07ba\xfcg\x1c\xf1\xb2\u02a5\x8965\u026d\xc5\u07a0\x00\x00\u07d4,\u04d34\xac~\xacyrW\xab\xe3sa\x95\xf5\xb4\xb5\xce\x0f\x89\x05kGx^7&\x00\x00\u07d4,\u05de\xb5 '\xb1,\x18\x82\x8e>\xaa\xb2\x96\x9b\xfc\u0487\xe9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,\xd8xfV\x8d\xd8\x1a\xd4}\x9d:\u0404nZePss\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4,\xdb9De\x06\x16\xe4|\xb1\x82\xe0`2/\xa1Hyx\u0389b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4,\xe1\x1a\x92\xfa\xd0$\xff+>\x87\xe3\xb5B\xe6\xc6\r\xcb\u0656\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4-\x03&\xb2?\x04\t\xc0\xc0\xe9#hc\xa13\aZ\x94\xba\x18\x89\vg\x9b\xe7[\xe6\xae\x00\x00\u07d4-\r\xecQ\xa6\xe8s0\xa6\xa8\xfa*\x0fe\u060dJ\xbc\xdfs\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4-#vkok\x05s}\xad\x80\xa4\x19\xc4\x0e\xdaMw\x10>\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4-+\x03#Y\xb3c\x96O\xc1\x1aQ\x82c\xbf\xd0T1\xe8g\x89\b\x1c\x1d\xf7b\x9ep\x00\x00\u07d4-4\x80\xbf\be\aJr\xc7u\x9e\xe5\x13{Mp\xc5\x1c\xe9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4-5\xa9\xdfbu\u007f\u007f\xfa\xd1\x04\x9a\xfb\x06\xcaJ\xfcFLQ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4-@U\x8b\x06\xf9\n9#\x14U\x92\x12;gt\xe4n1\xf4\x8965\u026d\xc5\u07a0\x00\x00\u07d4-Bi\x12\xd0Y\xfa\xd9t\v.9\n.\xea\xc0To\xf0\x1b\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4-S-\xf4\xc69\x11\xd1\u0391\xf6\xd1\xfc\xbf\xf7\x96\x0fx\xa8\x85\x89Z\x85\x96\x8aXx\u0680\x00\u07d4-S\x91\xe98\xb3HX\u03d6[\x84\x051\xd5\xef\xdaA\v\t\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94-[B\xfcY\xeb\xda\r\xfdf\xae\x91K\u008c\x1b\nn\xf8:\x8a+\u0235\x9f\xdc\xd86c\x80\x00\u07d4-]s5\xac\xb06+G\u07e3\xa8\xa4\xd3\xf5\x94\x95D\u04c0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-a\xbf\xc5hs\x92<+\x00\t]\xc3\xea\xa0\xf5\x90\u062e\x0f\x8a\x04ef\xdf\xf8\xceU`\x00\x00\u07d4-e\x11\xfdz8\x00\xb2hT\xc7\xec9\xc0\u0735\xf4\xc4\xe8\xe8\x89\x15\xad\u077a/\x9ew\x00\x00\u07d4-}\\@\u076f\xc4P\xb0Jt\xa4\u06bc+\xb5\xd6e\x00.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4-\x89\xa8\x00jO\x13z \xdc+\xecF\xfe.\xb3\x12\xea\x96T\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4-\x8cR2\x9f8\u04a2\xfa\x9c\xba\xf5\u0143\xda\xf1I\v\xb1\x1c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4-\x8e\x06\x18\x92\xa5\xdc\xce!\x96j\xe1\xbb\a\x88\xfd>\x8b\xa0Y\x89\r\x8e\\\xe6\x17\xf2\xd5\x00\x00\u07d4-\x8e[\xb8\xd3R\x16\x95\xc7~|\x83N\x02\x91\xbf\xac\xeet\b\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4-\x90\xb4\x15\xa3\x8e.\x19\xcd\xd0/\U000ed069z\xf7\xcb\xf6r\x89\x05\xf3\xc7\xf6A1\xe4\x00\x00\u07d4-\x9b\xado\x1e\xe0*p\xf1\xf1=\xef\\\u0332z\x9a'@1\x89a\t=|,m8\x00\x00\u07d4-\x9c_\xec\u04b4O\xbbj\x1e\xc72\xea\x05\x9fO\x1f\x9d+\\\x896\xca2f\x1d\x1a\xa7\x00\x00\xe0\x94-\xa6\x17iP\t\xccW\xd2j\u0510\xb3*]\xfb\xeb\x93N^\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4-\xa7k|9\xb4 \u323a,\x10 \xb0\x85k\x02pd\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4-\u01ddn\u007fU\xbc\xe2\xe2\xd0\xc0*\xd0|\uca3bR\x93T\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\xe0\x94-\xca\x0eD\x9a\xb6F\xdb\xdf\u04d3\xa9fb\x96\v\u02b5\xae\x1e\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4-\xd3%\xfd\xff\xb9{\x19\x99R\x84\xaf\xa5\xab\xdbWJ\x1d\xf1j\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4-\xd5x\xf7@}\xfb\xd5H\xd0^\x95\xcc\u00dcHT)bj\x89\u3bb5sr@\xa0\x00\x00\u07d4-\xd8\xee\xef\x87\x19J\xbc,\xe7X]\xa1\xe3[|\xeax\f\xb7\x8965\xc6 G9\u0640\x00\u07d4-\xdf@\x90Wi\xbc\xc4&\xcb,)8\xff\xe0w\xe1\u8758\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4-\xe0\x96D\x00\u0082\xbd\u05ca\x91\x9ck\xf7|k_yay\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-\xe3\x1a\xfd\x18\x9a\x13\xa7o\xf6\xfes\xea\xd9\xf7K\xb5\u0126)\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4-\xec\x982\x9d\x1f\x96\u00e5\x9c\xaay\x81uTR\xd4\xdaI\u0549\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-\ue422\x8f\x19-gj\x87s#+V\xf1\x8f#\x9e/\xad\x8a\x03\xef\xa7\xe7G\xb6\u046d\x00\x00\xe0\x94.\b\x80\xa3E\x96#\a \xf0Z\xc8\xf0e\xaf\x86\x81\u0736\u008a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4.\fW\xb4qP\xf9Z\xa6\xa7\xe1j\xb9\xb1\xcb\xf5C(\x97\x9a\x89\x05k\xc7^-c\x10\x00\x00\u07d4.\x10\x91\v\xa6\xe0\xbc\x17\xe0UUf\x14\u02c7\t\x0fM~[\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94.$\xb5\x97\x87;\xb1A\xbd\xb27\xea\x8aZ\xb7Gy\x9a\xf0-\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4.(\x10\xde\xe4J\xe4\xdf\xf3\xd8cB\xab\x12fW\xd6S\xc36\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4.,\xbdz\xd8%G\xb4\xf5\xff\x8b:\xb5o\x94*dE\xa3\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4.-~\xa6k\x9fG\xd8\xccR\xc0\x1cR\xb6\u147c}G\x86\x89\xd8\xd4`,&\xbfl\x00\x00\u07d4.C\x93H\u07caBw\xb2*v\x84W\xd1\x15\x8e\x97\xc4\t\x04\x89*\x1e\x9f\xf2o\xbfA\x00\x00\xe0\x94.F\xfc\xeej;\xb1E\xb5\x94\xa2C\xa3\x91?\xce]\xado\xba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794.G\xf2\x87\xf4\x98#7\x13\x85\r1&\x82<\xc6}\xce\xe2U\x88\u029d\x9e\xa5X\xb4\x00\x00\u07d4.N\u1b99j\xa0\xa1\xd9$(\xd0fR\xa6\xbe\xa6\xd2\xd1]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.R\x91+\xc1\x0e\xa3\x9dT\xe2\x93\xf7\xae\u05b9\x9a\x0fLs\xbe\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4.a\x9fW\xab\xc1\u91ea\x93j\xe3\xa2&Ib\xe7\xeb-\x9a\x89(\xfb\x9b\x8a\x8aSP\x00\x00\u07d4.d\xa8\xd7\x11\x11\xa2/L]\xe1\xe09\xb36\xf6\x8d9\x8a|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.i3T=O,\xc0\vSP\xbd\x80h\xba\x92C\u05be\xb0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94.~\x05\xe2\x9e\u0767\xe4\xae%\xc5\x175C\xef\xd7\x1fm=\x80\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4.\u007fFU \xec5\xcc#\u058eue\x1b\xb6h\x95D\xa1\x96\x898\xec[r\x1a\x1a&\x80\x00\u07d4.\x8e\xb3\nqn_\xe1\\t#>\x03\x9b\xfb\x11\x06\xe8\x1d\x12\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94.\x98$\xb5\xc12\x11\x1b\xca$\xdd\xfb\xa7\xe5u\xa5\xcdr\x96\xc1\x8a\x03\xa4\x84Qnm\u007f\xfe\x00\x00\u07d4.\xa5\xfe\xe6?3z7nK\x91\x8e\xa8!H\xf9MH\xa6&\x89e\x0f\x8e\r\u0493\xc5\x00\x00\u07d4.\xafN*F\xb7\x89\xcc\u0088\xc8\xd1\xd9)N?\xb0\x858\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.\xaf\xf9\xf8\xf8\x110d\u04d5z\xc6\xd6\xe1\x1e\xeeB\xc8\x19]\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4.\xba\fn\xe5\xa1\x14\\\x1cW9\x84\x96:`]\x88\nz \x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4.\xc9X\"\xeb\x88{\xc1\x13\xb4q*M\xfd\u007f\x13\xb0\x97\xb5\xe7\x8965\u026d\xc5\u07a0\x00\x00\u07d4.\xcaj<]\x9fD\x9d\tV\xbdC\xfa{M{\xe8CYX\x89lk\xdaip\x9c\xc2\x00\x00\xe0\x94.\xca\xc5\x04\xb23\x86n\xb5\xa4\xa9\x9e{\u0490\x13Y\xe4;=\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4.\xeb\xf5\x942\xb5(\x92\xf98\v\xd1@\xaa\x99\xdc\xf8\xad\f\x0f\x89\b=lz\xabc`\x00\x00\u07d4.\xee\xd5\x04q\xa1\xa2\xbfS\xee0\xb1#.n\x9d\x80\xef\x86m\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4.\xefk\x14\x17\u05f1\x0e\xcf\xc1\x9b\x12:\x8a\x89\xe7>RlX\x89 \x86\xac5\x10R`\x00\x00\u07d4.\xf8i\xf05\vW\xd54x\xd7\x01\xe3\xfe\xe5)\xbc\x91\x1cu\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4.\xf9\xe4eqj\xca\u03f8\xc8%/\xa8\xe7\xbcyi\xeb\xf6\u4255\x9e\xb1\xc0\xe4\xae \x00\x00\xe0\x94.\xfcLd}\xacj\xca\xc3Uw\xad\"\x17X\xfe\xf6ao\xaa\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4/\x13eu&\xb1w\xca\xd5G\u00d0\x8c\x84\x0e\xffd{E\u0649?v\x84\x9c\xf1\xee,\x80\x00\u07d4/\x18}ZpMZ3\x8c[(v\xa0\x90\xdc\xe9d(N)\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94/%#\u0303O\x00\x86\x05$\x02bb\x96gQ\x86\xa8\u508a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4/(*\xbb\xb6\u0523\xc3\xcd;\\\xa8\x12\xf7d>\x800_\x06\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4/+\xba\x1b\x17\x96\x82\x1avo\xced\xb8O(\xech\xf1Z\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4/1]\x90\x16\xe8\xee_Sf\x81 /\x90\x84\xb02TMM\x898<\xd1+\x9e\x86<\x00\x00\u07d4/M\xa7SC\x0f\xc0\x9es\xac\xbc\xcd\xcd\xe9\xdad\u007f+]7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4/P\x80\xb8?~-\xc0\xa1\xdd\x11\xb0\x92\xad\x04+\xffx\x8fL\x89\xb4\xf8\xfby#\x1d+\x80\x00\u07d4/a\uf941\x9dp_+\x1eN\xe7T\xae\xb8\xa8\x19Pju\x89O%\x91\xf8\x96\xa6P\x00\x00\xe0\x94/f\xbf\xbf\"b\xef\u030d+\xd0DO\u0170ib\x98\xff\x1e\x8a\x02\x1a\xd95\xf7\x9fv\xd0\x00\x00\u07d4/m\xce\x130\u015e\xf9!`!TW-MK\xac\xbd\x04\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4/}2\x90\x85\x1b\xe5\u01b4\xb4?}Et2\x9fa\xa7\x92\u00c9\x05k\xc7^-c\x10\x00\x00\u07d4/\x858\x17\xaf\u04f8\xf3\xb8n\x9f`\xeew\xb5\xd9ws\xc0\xe3\x89N\xae\xeaD\xe3h\xb9\x00\x00\u07d4/\xa4\x91\xfbY \xa6WN\xbd(\x9f9\xc1\xb2C\r-\x9aj\x89lk\x93[\x8b\xbd@\x00\x00\u07d4/\xb5f\xc9K\xbb\xa4\xe3\xcbg\xcd\xda}_\xadq1S\x91\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4/\xbbPJ]\xc5'\xd3\xe3\xeb\x00\x85\xe2\xfc<}\xd58\xcbz\x89C\u00b1\x8a\xec<\n\x80\x00\u07d4/\xbc\x85y\x8aX5\x98\xb5\"\x16mn\x9d\xda\x12\x1db}\xbc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4/\xbc\xef3\x84\xd4 \xe4\xbfa\xa0f\x99\x90\xbcpT\U00065bc9lk\x93[\x8b\xbd@\x00\x00\xe0\x94/\xc8.\xf0v\x93#A&Oaz\f\x80\xddW\x1ej\xe99\x8a\x01\x84$\xf5\xf0\xb1\xb4\xe0\x00\x00\u07d4/\u075by\u07cd\xf50\xadc\xc2\x0eb\xafC\x1a\xe9\x92\x16\xb8\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4/\xe0\x02?W\"e\x0f:\x8a\xc0\x10\t\x12^t\xe3\xf8.\x9b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4/\xe0\xccBKS\xa3\x1f\t\x16\xbe\b\xec\x81\xc5\v\xf8\xea\xb0\xc1\x89 \x86\xac5\x10R`\x00\x00\u07d4/\xe1:\x8d\a\x85\u0787X\xa5\xe4\x18v\xc3n\x91l\xf7Pt\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4/\xea\x1b/\x83O\x02\xfcT3?\x8a\x80\x9f\x048\xe5\x87\n\xa9\x89\x01\x18T\xd0\xf9\xce\xe4\x00\x00\u07d4/\xee6\xa4\x9e\xe5\x0e\xcfqo\x10G\x91VFw\x9f\x8b\xa0?\x899B\"\xc4\u0686\xd7\x00\x00\u07d4/\xef\x81G\x8aK.\x80\x98\xdb_\xf3\x87\xba!S\xf4\xe2+y\x896'\xe8\xf7\x127<\x00\x00\u07d4/\xf1`\xc4Or\xa2\x99\xb5\xec-q\xe2\x8c\xe5Dm/\u02ef\x89\x13\x84\x00\xec\xa3d\xa0\x00\x00\u07d4/\xf1\xcaU\xfd\x9c\xec\x1b\x1f\xe9\U00029af7LQ<\x1e*\xaa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94/\xf5\u02b1,\r\x95\u007f\xd33\xf3\x82\xee\xb7Q\a\xa6L\xb8\xe8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94/\xf80\xcfU\xfb\x00\u0560\xe05\x14\xfe\xcdD1K\xd6\xd9\xf1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4/\xfe\x93\xec\x1aV6\xe9\xee4\xafp\xdf\xf5&\x82\xe6\xffpy\x89lk\x93[\x8b\xbd@\x00\x00\u07d40\x03y\x88p&q\xac\xbe\x89,\x03\xfeW\x88\xaa\x98\xaf(z\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d40$\x8dX\xe4\x14\xb2\x0f\xed:lH+Y\xd9\xd8\xf5\xa4\xb7\xe2\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4019\xbcYd\x03\xd5\u04d3\x1fwLf\u013aFtT\u06c9\\%\xe1J\xea(?\x00\x00\u079408\x00\x87xie\x14\x9e\x81B;\x15\xe3\x13\xba2\xc5\u01c3\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d40:0\xacB\x86\xae\x17\xcfH=\xad{\x87\fk\xd6M{J\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d40?\xba\xeb\xbeF\xb3[n[t\x94j_\x99\xbc\x15\x85\xca\xe7\x89/\x9a\xc0i_[\xba\x00\x00\u07d40ADZ3\xba\x15\x87A\x16\r\x9c4N\xb8\x8e\\0o\x94\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d40H\x01d\xbc\xd8It\xeb\xc0\xd9\f\x9b\x9a\xfa\xb6&\xcd\x1cs\x89+^:\xf1k\x18\x80\x00\x00\u07d40N\u019atTW!\xd71j\xefM\u03f4\x1a\u015e\xe2\xf0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x940Q\x182\x91\x8d\x804\xa7\xbe\xe7.\xf2\xbf\xeeD\x0e\u02fc\xf6\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d40Q?\u029f6\xfdx\x8c\xfe\xa7\xa3@\xe8m\xf9\x82\x94\xa2D\x89\x18;_\x03\xb1G\x9c\x00\x00\u07d40U\xef\xd2`)\xe0\xd1\x1b\x93\r\xf4\xf5;\x16,\x8c?\xd2\u0389\x1b\x1a\b\x927\a=\x00\x00\u07d40]&\xc1\v\xdc\x10?k\x9c!'.\xb7\xcb-\x91\b\xc4~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d40_x\xd6\x18\xb9\x90\xb4)[\xac\x8a-\xfa&(\x84\xf8\x04\xea\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x940d\x89\x9a\x96\x1a>\x1d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d40\x98\xb6]\xb9>\xca\xca\xf75\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d40\uc4d2$J!\b\u0247\xbc\\\xdd\xe0\ud7c3z\x81{\x89T\x99%\xf6\xc9\xc5%\x00\x00\xe0\x940\xed\x11\xb7{\xc1~^f\x94\u023c[nG\x98\xf6\x8d\x9c\xa7\x8a\x1eo\xb3B\x1f\xe0)\x9e\x00\x00\u07d40\xf7\xd0%\xd1o{\xee\x10U\x80Ho\x9fV\x1c{\xae?\xef\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x940\xfb\xe5\x88_\x9f\xcc\xe9\xea^\u06c2\xedJ\x11\x96\xdd%\x9a\xed\x8a\x01\x19\xe4\u007f!8\x1f@\x00\x00\u07d41\x04}p?c\xb94$\xfb\xbdn/\x1f\x9et\xde\x13\xe7\t\x89\x9a\x81f\xf7\u6ca7\x80\x00\u07d411?\xfdc[\xf2\xf32HA\xa8\x8c\a\xed\x14aD\xce\xeb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d41Y\xe9\fH\xa9\x15\x90J\xdf\u24b2/\xa5\xfd^ryk\x896\xaf\xe9\x8f&\x06\x10\x00\x00\u07d41]\xb7C\x9f\xa1\u0574#\xaf\xa7\xddq\x98\xc1\xcft\xc9\x18\xbc\x89 \x86\xac5\x10R`\x00\x00\u07d41^\xf2\xdab\x0f\xd30\xd1.\xe5]\xe5\xf3)\xa6\x96\xe0\xa9h\x89\b!\xab\rD\x14\x98\x00\x00\u07d41n\x92\xa9\x1b\xbd\xa6\x8b\x9e/\x98\xb3\xc0H\x93N<\xc0\xb4\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d41n\xb4\xe4}\xf7\x1bB\xe1mo\xe4h%\xb72{\xaf1$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d41q\x87~\x9d\x82\f\xc6\x18\xfc\t\x19\xb2\x9e\xfd3?\xdaI4\x8965\u026d\xc5\u07a0\x00\x00\u07d41|\xf4\xa2<\xb1\x91\xcd\xc5c\x12\u009d\x15\xe2\x10\xb3\xb9\xb7\x84\x89\a\xcef\xc5\x0e(@\x00\x00\u07d41\x8b.\xa5\xf0\xaa\xa8y\xc4\xd5\xe5H\xac\x9d\x92\xa0\xc6t\x87\xb7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d41\x8cv\xec\xfd\x8a\xf6\x8dpUSR\xe1\xf6\x01\xe3Y\x88\x04-\x89\x1b1\x19.h\xc7\xf0\x00\x00\u07d41\x8f\x1f\x8b\xd2 \xb0U\x8b\x95\xfb3\x10\x0f\xfd\xbbd\r|\xa6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d41\xaa;\x1e\xbe\x8cM\xbc\xb6\xa7\b\xb1\xd7H1\xe6\x0eIv`\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d41\xab\b\x89f\xec\xc7\"\x92X\xf6\t\x8f\xceh\xcf9\xb3\x84\x85\x8965\u026d\xc5\u07a0\x00\x00\xe0\x941\xadM\x99F\xef\t\xd8\xe9\x88\xd9F\xb1\"\u007f\x91A\x90\x176\x8a\x04\xd8S\xc8\xf8\x90\x89\x80\x00\x00\xe0\x941\xb4;\x01]\x00\x81d~h\x00\x00\u07d424\x86\xcad\xb3uGO\xb2\xb7Y\xa9\xe7\xa15\x85\x9b\xd9\xf6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d427I\xa3\xb9q\x95\x9eF\u0234\x82-\xca\xfa\xf7\xaa\xf9\xbdn\x89\x01\x16q\xa5\xb2Ep\x00\x00\u07d42:\xadA\xdfKo\xc8\xfe\u038c\x93\x95\x8a\xa9\x01\xfah\bC\x894\x95tD\xb8@\xe8\x00\x00\xe0\x942;<\xfe>\xe6+\xbd\xe2\xa2a\xe5<\xb3\xec\xc0X\x10\xf2\u018a\x02\ub3b1\xa1r\u0738\x00\x00\u07d42?\xca^\xd7\u007fi\x9f\x9d\x990\xf5\xce\xef\xf8\xe5oY\xf0<\x89Hz\x9a0E9D\x00\x00\u07d42H\\\x81\x87(\xc1\x97\xfe\xa4\x87\xfb\xb6\xe8)\x15\x9e\xba\x83p\x899!\xb4\x13\xbcN\xc0\x80\x00\xe0\x942P\xe3\xe8X\xc2j\xde\u032d\xf3jVc\xc2*\xa8LAp\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x942Y\xbd/\xdd\xfb\xbco\xba\u04f6\xe8t\xf0\xbb\xc0,\xda\x18\xb5\x8a\x02\x84`VI[\r\x18\x80\x00\u07d42uIo\xd4\u07491\xfdi\xfb\n\v\x04\xc4\xd1\xff\x87\x9e\xf5\x89\x18-~L\xfd\xa08\x00\x00\u07d42{\xb4\x9euOo\xb4\xf73\xc6\xe0o9\x89\xb4\xf6]K\xee\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d42\x82y\x1do\xd7\x13\xf1\xe9OK\xfdV^\xaax\xb3\xa0Y\x9d\x89Hz\x9a0E9D\x00\x00\u07d42\x83\xeb\u007f\x917\xdd9\xbe\xd5_\xfek\x8d\xc8E\xf3\xe1\xa0y\x89\x03\x97\n\xe9!Ux\x00\x00\u07d42\x86\t\x97\xd70\xb2\xd8;s$\x1a%\xd3f}Q\xc9\b\xef\x89\x1b\x1a\b\x927\a=\x00\x00\xe0\x942\x86\u047cez1,\x88G\xd9<\xb3\xcbyP\xf2\xb0\xc6\xe3\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x942\xa2\r\x02\x8e,b\x18\xb9\xd9[D\\w\x15$cj\"\xef\x8a\x02\x02\xfe\xfb\xf2\xd7\xc2\xf0\x00\x00\u07d42\xa7\x06\x91%\\\x9f\xc9y\x1aOu\u0238\x1f8\x8e\n%\x03\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d42\xb7\xfe\xeb\xc5\u015b\xf6^\x86\x1cL\v\xe4*v\x11\xa5T\x1a\x89w\u9aa8R\\\x10\x00\x00\xe0\x942\xba\x9a}\x04#\xe0:R_\xe2\xeb\xebf\x1d \x85w\x8b\u060a\x04<3\xc1\x93ud\x80\x00\x00\u07d42\xbb.\x96\x93\xe4\xe0\x854M/\r\xbdF\xa2\x83\u3807\xfd\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x942\xc2\xfd\u2daa\xbb\x80\u5ba2\xb9I\xa2\x17\xf3\xcb\t\"\x83\x8a\x010a`\xaf\xdf 7\x80\x00\u07d42\xd9P\xd5\xe9>\xa1\u0574\x8d\xb4qO\x86{\x03 \xb3\x1c\x0f\x897\b\xba\xed=h\x90\x00\x00\u07d42\u06f6qlT\xe81e\x82\x9aJ\xbb6uxI\xb6\xe4}\x8965\u026d\xc5\u07a0\x00\x00\u07d42\xebd\xbe\x1b]\xed\xe4\b\u01bd\xef\xben@\\\x16\xb7\xed\x02\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d42\xef\\\xdcg\x1d\xf5V*\x90\x1a\xee]\xb7\x16\xb9\xbev\xdc\xf6\x89lk\x93[\x8b\xbd@\x00\x00\u07d42\xf2\x9e\x87'\xa7LkC\x01\xe3\xff\xff\x06\x87\xc1\xb8p\xda\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d42\xfa\x0e\x86\xcd\b}\u058di1\x90\xf3-\x931\t\t\xedS\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d42\xfb\xee\xd6\xf6&\xfc\xdf\xd5\x1a\xca\xfbs\v\x9e\xef\xf6\x12\xf5d\x89lk\x93[\x8b\xbd@\x00\x00\u07943\x00\xfb\x14\x9a\xde\xd6[\u02e6\xc0N\x9c\u05b7\xa0;\x89;\xb1\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x943\x01\xd9\xca/;\xfe\x02by\xcdh\x19\xf7\x9a)=\x98\x15n\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\xe0\x943\b\xb04f\xc2z\x17\xdf\xe1\xaa\xfc\xeb\x81\xe1m)4Vo\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07943\x1a\x1c&\xcci\x94\xcd\xd3\xc1K\xec\xe2v\xff\xffK\x9d\xf7|\x88\xfaz\xed\xdfO\x06\x80\x00\xe0\x943&\xb8\x8d\xe8\x06\x18DT\xc4\v'\xf3\t\xd9\xddm\u03f9x\x8a\x03\xca\\f\u067cD0\x00\x00\xe0\x943)\xeb;\xafCE\xd6\x00\xce\xd4\x0en\x99ueo\x117B\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d432\r\xd9\x0f+\xaa\x11\r\xd34\x87*\x99\x8f\x14\x84&E<\x8965f3\xeb\xd8\xea\x00\x00\u07d436\xc3\xefn\x8bP\xee\x90\xe07\xb1d\xb7\xa8\xea_\xaa\xc6]\x89\x0e\u0223\xa7\x1c\"T\x00\x00\xe0\x9438\fo\xffZ\xcd&Q0\x96)\u06daq\xbf? \u017a\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d43:\xd1Yd\x01\xe0Z\xea-6\xcaG1\x8e\xf4\xcd,\xb3\u07c9\x9d\xc0\\\xce(\u00b8\x00\x00\u07d43C@\xeeK\x9c\u0701\xf8P\xa7Q\x16\xd5\x0e\u9d98%\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d43H\x1e\x85n\xbe\u050e\xa7\b\xa2t&\xef(\xe8g\xf5|\u0449\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x943V[\xa9\xda,\x03\xe7x\xce\x12)O\b\x1d\xfe\x81\x06M$\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07943X\x1c\xee#0\x88\xc0\x86\r\x94N\f\xf1\u03ab\xb8&\x1c.\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d43XX\xf7I\xf1i\u02bc\xfeR\xb7\x96\xe3\xc1\x1e\xc4~\xa3\u0089\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x943^\"\x02[zw\u00e0t\u01cb\x8e=\xfe\a\x13A\x94n\x8a\x02'\xcas\n\xb3\xf6\xac\x00\x00\u07d43b\x9b\xd5/\x0e\x10{\xc0q\x17ld\xdf\x10\x8fdw}I\x89\x01\xcf\xddth!n\x80\x00\u07d43{;\u07c6\xd7\x13\xdb\xd0{]\xbf\xcc\x02+z{\x19F\xae\x89\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d43|\xfe\x11W\xa5\u0191 \x10\xddV\x153y\x17i\u00b6\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d43\xb36\xf5\xba^\xdb{\x1c\xcc~\xb1\xa0\u0644\xc1#\x1d\x0e\u0709lk\x93[\x8b\xbd@\x00\x00\u07d43\xc4\a\x13;\x84\xb3\xcaL=\xed\x1fFX\x90\f8\x10\x16$\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\xe0\x943\xd1r\xab\a\\Q\xdb\x1c\xd4\n\x8c\xa8\xdb\xff\r\x93\xb8C\xbb\x8a\x016x\x05\x10\xd1-\xe3\x80\x00\u07d43\xe9\xb7\x18#\x95.\x1ff\x95\x8c'\x8f\u008b\x11\x96\xa6\u0164\x89\x05k\xc7^-c\x10\x00\x00\u07d43\xeakxU\xe0[\a\xab\x80\u06b1\xe1M\xe9\xb6I\xe9\x9bl\x89\x1c\xd6\xfb\xadW\xdb\xd0\x00\x00\u07d43\xf1R#1\rD\u078bf6h_:L=\x9cVU\xa5\x89\r\x94b\xc6\xcbKZ\x00\x00\u07d43\xf4\xa6G\x1e\xb1\xbc\xa6\xa9\xf8[;Hr\xe1\aU\xc8+\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d43\xfbWzM!O\xe0\x10\xd3,\xca|>\xed\xa6?\x87\xce\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d43\xfdq\x8f\v\x91\xb5\xce\u020a]\xc1^\xec\xf0\xec\xef\xa4\xef=\x89\x17r$\xaa\x84Lr\x00\x00\u07d44\x14\x80\u030c\xb4v\xf8\xd0\x1f\xf3\b\x12\xe7\xc7\x0e\x05\xaf\xaf]\x89lk\x93[\x8b\xbd@\x00\x00\u07d44'-^ut1]\xca\u9afd1{\xac\x90(\x9dGe\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x9440\xa1c\x81\xf8i\xf6\xeaT#\x91XU\xe8\x00\x885%\xa9\x8a\x03\xca\\f\u067cD0\x00\x00\u07d441\x86%\x81\x8e\xc1?\x11\x83Z\xe9sS\xce7}oY\n\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d449<]\x91\xb9\xdeYr\x03\xe7[\xacC\t\xb5\xfa=(\u00c9\n\x84Jt$\xd9\xc8\x00\x00\u07d449\x99\x8b$|\xb4\xbf\x8b\xc8\nm+5'\xf1\xdf\xe9\xa6\u0489\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d44C}\x14ed\v\x13l\xb5\x84\x1c?\x93O\x9b\xa0\xb7\t}\x89\t`\xdbwh\x1e\x94\x00\x00\u07d44J\x8d\xb0\x86\xfa\xedN\xfc7\x13\x1b:\"\xb0x-\xadp\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x944fM\"\x0f\xa7\xf3yX\x02J32\u0584\xbc\xc6\xd4\u023d\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d44f\xf6~9cl\x01\xf4;:!\xa0\xe8R\x93%\xc0\x86$\x89-\xb1\x16vP\xac\xd8\x00\x00\u07d44\x856\x1e\xe6\xbf\x06\xefe\b\xcc\xd2=\x94d\x1f\x81M>/\x89lk\x93[\x8b\xbd@\x00\x00\u07d44\x85\xf6!%d3\xb9\x8aB\x00\xda\xd8W\xef\xe5Y7\uc609lk\x93[\x8b\xbd@\x00\x00\u07d44\x95\x8aF\xd3\x0e0\xb2s\xec\xc6\xe5\xd3X\xa2\x12\xe50~\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d44\x97\xddf\xfd\x11\x80q\xa7\x8c,\xb3n@\xb6e\x1c\xc8%\x98\x89\x05\xf1\x01kPv\xd0\x00\x00\xe0\x944\x9a\x81k\x17\xab='\xbb\xc0\xae\x00Q\xf6\xa0p\xbe\x1f\xf2\x9d\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d44\x9d,\x91\x8f\u041e(\a1\x8ef\xceC)\t\x17k\xd5\v\x89<\xb7\x1fQ\xfcU\x80\x00\x00\u07d44\xa0C\x1f\xff^\xad\x92\u007f\xb6`\f\x1e\xa8\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d44\xff&\xeb`\xa8\u0469ZH\x9f\xae\x13n\xe9\x1dNX\bL\x89 \x86\xac5\x10R`\x00\x00\u07d44\xffX)R\xff$E\x8f{\x13\xd5\x1f\vO\x98p\"\xc1\xfe\x89\x98\x06\xde=\xa6\xe9x\x00\x00\u07d45\x10k\xa9N\x85c\u0533\xcb<\\i,\x10\xe6\x04\xb7\xce\u0609lk\x93[\x8b\xbd@\x00\x00\xe0\x945\x14_b\x03\x97\u019c\xb8\xe0\tb\x96\x1f\x0fH\x86d9\x89\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d45\x14t0\xc3\x10e\x00\u77e2\xf5\x02F.\x94p<#\xb1\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x945\x17\x87\x845\x05\xf8\xe4\xef\xf4ef\xcc\u695fM\x1c_\xe7\x8a\x01\xf5q\x89\x87fKH\x00\x00\xe0\x945\x1f\x16\xe5\xe0sZ\xf5gQ\xb0\xe2%\xb2B\x11q9@\x90\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\xe0\x945$\xa0\x00#N\xba\xaf\a\x89\xa14\xa2\xa4\x178<\xe5(*\x8a\x011yU\x94}\x8e,\x00\x00\u07d45&\xee\xce\x1ak\xdc>\xe7\xb4\x00\xfe\x93[HF?1\xbe\u05c9\x04w\x87\x9bm\x140\x00\x00\u07d45*x_J\x92\x162PL\xe5\xd0\x15\xf8\xd7FO\xa3\xdb\x14\xe7r\x92\x13\u03aa7\x8c\t^\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d45\xaf\x04\n\f\xc23zv\xaf(\x81T\xc7V\x1e\x1a#3I\x8965\u026d\xc5\u07a0\x00\x00\u07d45\xb0>\xa4$W6\xf5{\x85\xd2\xebyb\x8f\x03m\xdc\xd7\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d45\xbd$he\xfa\xb4\x90\xac\bz\xc1\xf1\xd4\xf2\xc1\r\f\xda\x03\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x945\xbff\x88R/5Fz\u007fu0#\x14\xc0+\xa1v\x80\x0e\x8a\x03\xafA\x82\x02\xd9T\xe0\x00\x00\u07d45\u022d\xc1\x11%C+;w\xac\xd6F%\xfeX\xeb\xee\x9df\x89lk\x93[\x8b\xbd@\x00\x00\u07d45\u0497\x0fI\xdc\xc8\x1e\xa9\xeep~\x9c\x8a\n\xb2\xa8\xbbtc\x89N\x10\x03\xb2\x8d\x92\x80\x00\x00\u07d45\xe0\x96\x12\r\xea\xa5\xc1\xec\xb1d^,\u02cbN\xdb\xd9)\x9a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d45\xea!c\xa3\x8c\u07da\x12?\x82\xa5\xec\x00%\x8d\xae\v\xc7g\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d45\xf1\xda\x12{\x837o\x1b\x88\xc8*3Y\xf6z^g\xddP\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d45\xf2\x94\x9c\xf7\x8b\xc2\x19\xbbO\x01\x90|\xf3\xb4\xb3\u04c6T\x82\x89\x0f\xb5\xc8l\x92\xe44\x00\x00\u07d45\xf5\x86\x01I\xe4\xbb\xc0K\x8a\u0172r\xbeU\xad\x1a\xcaX\xe0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d46\x02E\x8d\xa8omj\x9d\x9e\xb0=\xaf\x97\xfeV\x19\xd4B\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x057-\x93\xa9\x01\t\x88\x01\x8f\x9f1]\x03.\u0448\x0f\xa1\x89\x1b\x1b\xcfQ\x89j}\x00\x00\u07d46\x16\xd4H\x98_]2\xae\xfa\x8b\x93\xa9\x93\xe0\x94\xbd\x85I\x86\x89\v\"\u007fc\xbe\x81<\x00\x00\u07d46\x16\xfbF\xc8\x15x\xc9\xc8\xebM;\xf8\x80E\x1a\x887\x9d}\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d46\x1cu\x93\x16\x96\xbc=B}\x93\xe7lw\xfd\x13\xb2A\xf6\xf4\x89\x1d\xc5\xd8\xfc&m\xd6\x00\x00\u07d46\x1d\x9e\xd8\v[\xd2|\xf9\xf1\"o&u2X\xee_\x9b?\x89\xbfi\x14\xba}r\xc2\x00\x00\u07d46\x1f;\xa9\xed\x95kw\x0f%}6r\xfe\x1f\xf9\xf7\xb0$\f\x89 \x86\xac5\x10R`\x00\x00\u07d46\"|\u07e0\xfd;\x9d~jtF\x85\xf5\xbe\x9a\xa3f\xa7\xf0\x89\n\xc2s\x0e\xe9\xc6\xc1\x80\x00\u07d46/\xbc\xb1\x06b7\n\x06\x8f\xc2e&\x02\xa2Wy7\xcc\xe6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d460\xc5\xe5e\u03aa\x8a\x0f\x0f\xfe2\x87^\xae*l\xe6<\x19\x89\t7r+7t\xd0\x00\x00\u07d463\x9f\x84\xa5\u00b4L\xe5=\xfd\xb6\xd4\xf9}\xf7\x82\x12\xa7\u07c9\x11o\x18\xb8\x17\x15\xa0\x00\x00\u07d464:\xec\xa0{n\u054a\x0eb\xfaN\xcbI\x8a\x12O\xc9q\x89\x10CV\x1a\x88)0\x00\x00\u07d46au@4\x81\xe0\xab\x15\xbbQF\x15\u02f9\x89\xeb\u018f\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d46ro;\x88Z$\xf9)\x96\u0681b^\u022d\x16\xd8\xcb\xe6\x89S\xafu\u0441HW\x80\x00\xe0\x946s\x95C\x99\xf6\u07feg\x18\x18%\x9b\xb2x\xe2\xe9.\xe3\x15\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d46u\x8e\x04\x9c\u064b\u03a1\"w\xa6v\xf9)sb\x89\x00#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d46\u007fY\u0302yS)8NA\xe1(1\x15\xe7\x91\xf2j\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x81\x0f\xf9\xd2\x13\xa2q\xed\xa2\xb8\xaay\x8b\xe6T\xfaK\xbe\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x8cT\x14\xb5k\x84U\x17\x1f\xbf\ab \xc1\u02e4\xb5\xca1\x89\x1e>\xf9\x11\xe8=r\x00\x00\xe0\x946\x90$k\xa3\xc8\x06y\xe2.\xacD\x12\xa1\xae\xfc\xe6\xd7\u0342\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d46\x92\x8bU\xbc\x86\x15\t\xd5\x1c\x8c\xf1\xd5F\xbf\xecn>\x90\xaf\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d46\x98\"\xf5W\x8b@\xdd\x1fDqpk\"\u0357\x13R\xdak\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d46\x9e\xf7a\x19_:7>$\xec\xe6\xcd\"R\x0f\xe0\xb9\xe8n\x89\x1c\xff\xaf\xc9M\xb2\b\x80\x00\u07d46\xa0\x8f\xd6\xfd\x1a\xc1|\xe1^\xd5~\xef\xb1*+\u2048\xbf\x89Hz\x9a0E9D\x00\x00\u07d46\xa0\xe6\x1e\x1b\xe4\u007f\xa8~0\xd3(\x88\xee\x030\x90\x1c\xa9\x91\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x946\xb2\xc8^:\xee\xeb\xb7\rc\u0124s\f\xe2\xe8\xe8\x8a6$\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x946\xbfC\xff5\u07d0\x90\x88$3l\x9b1\xce3\x06~/P\x8aIr\x15\x10\xc1\xc1\xe9H\x00\x00\u07d46\xbf\xe1\xfa;{p\xc1r\xeb\x04/h\x19\xa8\x97%\x95A>\x8965\u026d\xc5\u07a0\x00\x00\xe0\x946\xc5\x10\xbf\x8dnV\x9b\xf2\xf3}G&]\xbc\xb5\x02\xff+\u038a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x946\xd8]\xc3h1V\xe6;\xf8\x80\xa9\xfa\xb7x\x8c\xf8\x14:'\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d46\u07cf\x88<\x12s\xec\x8a\x17\x1fz3\xcf\xd6I\xb1\xfe`u\x89\fRHJ\xc4\x16\x89\x00\x00\xe0\x946\xe1Va\f\xd8\xffd\xe7\x80\u061d\x00T8\\\xa7gU\xaa\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d46\xfe\xc6,,B^!\x9b\x18D\x8a\xd7W\x00\x9d\x8cT\x02o\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d47\x00\xe3\x02t$\xd99\xdb\xde]B\xfbx\xf6\xc4\xdb\xec\x1a\x8f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d47\x02\xe7\x04\xcc!at9\xadN\xa2zW\x14\xf2\xfd\xa1\xe92\x8965\u026d\xc5\u07a0\x00\x00\u07d47\x035\fMo\xe374,\xdd\xc6[\xf1\xe28k\xf3\xf9\xb2\x89m\x81!\xa1\x94\xd1\x10\x00\x00\xe0\x947\b\xe5\x9d\xe6\xb4\x05P\x88x)\x02\xe0W\x9cr\x01\xa8\xbfP\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d47\x126~^U\xa9mZ\x19\x16\x8fn\xb2\xbc~\x99q\xf8i\x8965\u026d\xc5\u07a0\x00\x00\u07d47\x19Zc]\xccb\xf5jq\x80I\xd4~\x8f\x9f\x96\x83(\x91\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d47'4\x1f&\xc1 \x01\xe3x@^\xe3\x8b-\x84d\xecq@\x89lk\x93[\x8b\xbd@\x00\x00\u07d47.E:kb\x9f'g\x8c\u022e\xb5\xe5|\xe8^\xc0\xae\xf9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d474\xcb\x18t\x91\xed\xe7\x13\xae[;-\x12(J\xf4k\x81\x01\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d477!n\xe9\x1f\x17w2\xfbX\xfa@\x97&r\a\xe2\xcfU\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d47M;\xbb\x057Q\xf9\xf6\x8d\xdb\a\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d48r\xf4\x8d\xc5\xe3\xf8\x17\xbck*\xd2\xd00\xfc^\x04q\x19=\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d48~\xea\xfdk@\t\u07af\x8b\u0578Zr\x98:\x8d\xcc4\x87\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d48\x81\xde\xfa\xe1\xc0{<\xe0Lx\xab\xe2k\f\u070ds\xf0\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d48\x83\xbe\xcc\b\xb9\xbeh\xad;\b6\xaa\u00f6 \xdc\x00\x17\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d48\x85\xfe\xe6q\a\xdc:\xa9\x8a\x1d\x99:t\xdf\\\xd7T\xb9\x8dR\x9a\x89a\t=|,m8\x00\x00\u07d48\xe4m\xe4E<8\xe9A\xe7\x93\x0fC0O\x94\xbb{+\xe8\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d48\xe7\u06e8\xfdO\x1f\x85\r\xbc&I\xd8\xe8O\tR\xe3\xeb<\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d48\xe8\xa3\x1a\xf2\xd2e\xe3\x1a\x9f\xff-\x8fF(m\x12E\xa4g\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d48\xeao[Z{\x88AuQ\xb4\x12=\xc1'\xdf\xe94-\xa6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d48\xee\xc6\xe2\x17\xf4\xd4\x1a\xa9 \xe4$\xb9RQ\x97\x04\x1c\xd4\u0189\xf0\r%\xeb\x92.g\x00\x00\xe0\x948\xf3\x87\xe1\xa4\xedJs\x10n\xf2\xb4b\xe4t\xe2\xe3\x14:\u040a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d49\x11a\xb0\xe4<0 f\u898d,\xe7\xe1\x99\xec\xdb\x1dW\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x949\x15\uad6b.Yw\xd0u\xde\xc4}\x96\xb6\x8bK\\\xf5\x15\x8a\r\a\x01\x81\x85\x12\x0f@\x00\x00\u07d49\x1aw@\\\t\xa7+^\x846#z\xaa\xf9]h\xda\x17\t\x89\x02\xa9&J\xf3\u0479\x00\x00\u07d49\x1f \x17m\x126\rrMQG\n\x90p6uYJM\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d49$3\xd2\u0383\xd3\xfbJv\x02\u0323\xfa\xcaN\xc1@\xa4\xb0\x89\x02\xc3\xc4e\xcaX\xec\x00\x00\xe0\x949?x;\\\u06c6\"\x1b\xf0)O\xb7\x14\x95\x9c{E\x89\x9c\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d49?\xf4%^\\e\x8f.\u007f\x10\xec\xbd)%rg\x1b\xc2\u0489lk\x93[\x8b\xbd@\x00\x00\u07d49A2`\x0fAU\xe0\u007fME\xbc>\xb8\xd9\xfbr\xdc\u05c4\x89\x9fn\x92\xed\xea\a\xd4\x00\x00\u07d49Q\xe4\x8e<\x86\x9ekr\xa1C\xb6\xa4Ph\u0379\xd4f\u0409\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x949T\xbd\xfe\v\xf5\x87\u0195\xa3\x05\xd9$L=[\xdd\xda\u027b\x8a\x04\x10'\x83'\xf9\x85`\x80\x00\u07d49]m%U \xa8\xdb)\xab\xc4}\x83\xa5\u06ca\x1a}\xf0\x87\x89\x05k\xc7^-c\x10\x00\x00\u07d49ck%\x81\x1b\x17j\xbf\xcf\xee\xcad\xbc\x87E/\x1f\xdf\xf4\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d49i\xb4\xf7\x1b\xb8u\x1e\xdeC\xc0\x166:zaOv\x11\x8e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x949x/\xfe\x06\xacx\x82*<:\x8a\xfe0^P\xa5a\x88\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d49zn\xf8v:\x18\xf0\x0f\xac!~\x05\\\r0\x94\x10\x10\x11\x89lk\x93[\x8b\xbd@\x00\x00\u07d49|\u06cc\x80\xc6yP\xb1\x8deB)a\x0e\x93\xbf\xa6\xee\x1a\x89?\x95\xc8\xe0\x82\x15!\x00\x00\u07d49\x82O\x8b\xce\xd1v\xfd>\xa2.\u01a4\x93\xd0\xcc\xc3?\xc1G\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d49\x93l'\x19E\v\x94 \xcc%\"\u03d1\xdb\x01\xf2'\xc1\xc1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d49\x95\xe0\x96\xb0\x8aZrh\x00\xfc\xd1}\x9cd\xc6N\b\x8d+\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d49\x9a\xa6\xf5\xd0x\xcb\tp\x88+\u0259 \x06\xf8\xfb\xdf4q\x8965\u026d\xc5\u07a0\x00\x00\u07d49\xaa\x05\xe5m}28T!\u03d36\xe9\r=\x15\xa9\xf8Y\x89\x01h\u048e?\x00(\x00\x00\u07d49\xaa\xf0\x85M\xb6\xeb9\xbc{.C\x84jv\x17\x1c\x04E\u0789dI\xe8NG\xa8\xa8\x00\x00\u07d49\xb1\xc4q\xae\x94\xe1!dE.\x81\x1f\xbb\xe2\xb3\xcdru\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d49\xb2\x992t\x90\xd7/\x9a\x9e\xdf\xf1\x1b\x83\xaf\xd0\xe9\xd3\xc4P\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d49\xba\u018d\x94xY\xf5\x9e\x92&\b\x9c\x96\xd6.\x9f\xbe<\u0789\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x949\xbf\xd9xh\x9b\xec\x04\x8f\xc7v\xaa\x15$\u007f^\x1d|9\xa2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d49\xc7s6|\x88%\xd3YlhoB\xbf\r\x141\x9e?\x84\x89\a?u\u0460\x85\xba\x00\x00\u07d49\u05291@,\fy\xc4W\x18o$\u07c7)\u03d5p1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d49\xd6\xca\xca\"\xbc\xcdjr\xf8~\xe7\u05b5\x9e\v\xde!\xd7\x19\x89l\x87T\xc8\xf3\f\b\x00\x00\u07d49\xe0\xdbM`V\x8c\x80\v\x8cU\x00\x02l%\x94\xf5v\x89`\x8965\u026d\xc5\u07a0\x00\x00\xe0\x949\xeeO\xe0\x0f\xbc\xeddph\xd4\xf5|\x01\xcb\"\xa8\v\xcc\u044a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d49\xf1\x983\x1eK!\xc1\xb7`\xa3\x15_J\xb2\xfe\x00\xa7F\x19\x89lk\x93[\x8b\xbd@\x00\x00\u07d49\xf4Fc\xd9%a\t\x1b\x82\xa7\r\xcfY=u@\x05\x97:\x89\n\u05cb.\xdc!Y\x80\x00\u07d4:\x03U\x94\xc7GGmB\xd1\xee\x96l6\"L\xdd\"I\x93\x89\x13J\xf7Ei\xf9\xc5\x00\x00\u07d4:\x04W(G\xd3\x1e\x81\xf7v\\\xa5\xbf\xc9\xd5W\x15\x9f6\x83\x89\a6-\r\xab\xea\xfd\x80\x00\xe0\x94:\x06\xe3\xbb\x1e\xdc\xfd\fD\xc3\aM\xe0\xbb`k\x04\x98\x94\xa2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794:\x10\x88\x8b~\x14\x9c\xae',\x010,2}\n\xf0\x1a\v$\x88\xeb\xec!\xee\x1d\xa4\x00\x00\u07d4:1\b\xc1\u6023;3l!\x13\x134@\x9d\x97\xe5\xad\xec\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4:6\x8e\xfeJ\u05c6\xe2c\x95\xec\x9f\u01adi\x8c\xae)\xfe\x01\x89\"E\x89\x96u\xf9\xf4\x00\x00\u07d4:=\xd1\x04\xcd~\xb0O!\x93/\xd43\xeaz\xff\u04d3i\xf5\x89\x13aO#\xe2B&\x00\x00\u07d4:B\x97\xda\xc4.\x1eO\xb8\xcb1\xec\xddC\xaew<\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94:\x99`&m\xf6I cS\x8a\x99\xf4\x87\xc9P\xa3\xa5\uc78a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4:\x9b\x11\x10)\xce\x1f \xc9\x10\x9czt\xee\xee\xf3OO.\xb2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4:\x9eTA\xd4K$;\xe5[u\x02z\x1c\ub7ac\xf5\r\xf2\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94:\xa0z4\xa1\xaf\u0216}=\x13\x83\xb9kb\u03d6\xd5\xfa\x90\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94:\xa4,!\xb9\xb3\x1c>'\xcc\xd1~\t\x9a\xf6y\xcd\xf5i\a\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4:\xa9H\xea\x029wU\xef\xfb/\x9d\xc99-\xf1\x05\x8f~3\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4:\xad\xf9\x8ba\xe5\u0216\xe7\xd1\x00\xa39\x1d2P\"]a\u07c9\f\xafg\x007\x01h\x00\x00\u07d4:\xaeHr\xfd\x90\x93\xcb\xca\xd1@o\x1e\x80x\xba\xb5\x03Y\xe2\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4:\xbb\x8a\xdf\xc6\x04\xf4\x8dY\x84\x81\x1d\u007f\x1dR\xfe\xf6u\x82p\x89\xf2\x97\x19\xb6o\x11\f\x00\x00\u07d4:\xc2\xf0\xff\x16\x12\xe4\xa1\xc3F\xd53\x82\xab\xf6\u0622[\xaaS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4:\xc9\xdczCj\xe9\x8f\xd0\x1cz\x96!\xaa\x8e\x9d\v\x8bS\x1d\x89a\t=|,m8\x00\x00\xe0\x94:\xd0aI\xb2\x1cU\xff\x86|\xc3\xfb\x97@\u04bc\xc7\x10\x121\x8a)\xb7d2\xb9DQ \x00\x00\u07d4:\xd7\x02C\u060b\xf0@\x0fW\xc8\xc1\xfdW\x81\x18H\xaf\x16*\x89.\x9e\xe5\u00c6S\xf0\x00\x00\u07d4:\xd9\x15\xd5P\xb7#AV \xf5\xa9\xb5\xb8\x8a\x85\xf3\x82\xf05\x8965\u026d\xc5\u07a0\x00\x00\u07d4:\xe1`\xe3\xcd`\xae1\xb9\xd6t-h\xe1Nv\xbd\x96\xc5\x17\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4:\xe6+\xd2q\xa7`c\u007f\xady\xc3\x1c\x94\xffb\xb4\xcd\x12\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4:\xeaN\x82\xd2@\x02H\xf9\x98q\xa4\x1c\xa2W\x06\r:\"\x1b\x8965\u026d\xc5\u07a0\x00\x00\u07d4:\xf6[>(\x89ZJ\x00\x11S9\x1d\x1ei\xc3\x1f\xb9\xdb9\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4;\a\xdbZ5\u007fZ\xf2HL\xbc\x9dw\xd7;\x1f\xd0Q\x9f\u01c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4;\n\u032fK`|\xfea\xd1s4\xc2\x14\xb7\\\xde\xfd\xbd\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\x13c\x1a\x1b\x89\xcbVeH\x89\x9a\x1d`\x91\\\xdc\xc4 [\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\x15\x90\x99\aR\a\u0180vc\xb1\xf0\xf7\xed\xa5J\xc8\xcc\xe3\x89j\xc4\xe6[i\xf9-\x80\x00\u07d4;\x197\xd5\u74f8\x9bc\xfb\x8e\xb5\xf1\xb1\xc9\xcak\xa0\xfa\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\"\xda*\x02q\xc8\xef\xe1\x02S'scji\xb1\xc1~\t\x89\x1b6\xa6DJ>\x18\x00\x00\u07d4;\"\u07a3\xc2_\x1bY\u01fd'\xbb\x91\u04e3\xea\xec\xef9\x84\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94;#g\xf8IK_\xe1\x8dh<\x05]\x89\x99\x9c\x9f=\x1b4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4;,E\x99\x0e!GDQ\xcfOY\xf0\x19U\xb31\xc7\xd7\u0249lk\x93[\x8b\xbd@\x00\x00\xe0\x94;A\x00\xe3\ns\xb0\xc74\xb1\x8f\xfa\x84&\u045b\x191/\x1a\x8a\v\xb5\u046ap\n\xfd\x90\x00\x00\u07d4;B\xa6m\x97\x9fX(4tz\x8b`B\x8e\x9bN\xec\xcd#\x89!\xa1\u01d0\xfa\xdcX\x00\x00\u07d4;Gh\xfdq\xe2\xdb,\xbe\u007f\xa0PH<'\xb4\xeb\x93\x1d\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;Vj\x8a\xfa\u0456\x82\xdc,\xe8g\x9a<\xe4D\xa5\xb0\xfdO\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94;\\%\x1d\u007f\u05c9;\xa2\t\xfeT\x1c\xec\xd0\xce%:\x99\r\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4;^\x8b\x17w\xca\x18A\x896\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94;\x93\xb1a6\xf1\x1e\xaf\x10\x99l\x95\x99\r;'9\xcc\xea_\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4;\xabK\x01\xa7\xc8K\xa1?\uea70\xbb\x19\x1bw\xa3\xaa\u0723\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4;\xb55\x98\xcc \xe2\x05]\xc5S\xb0I@J\u0277\xdd\x1e\x83\x89!W\x1d\xf7|\x00\xbe\x00\x00\u07d4;\xbc\x13\xd0J\xcc\xc0pz\xeb\u072e\xf0\x87\u0438~\v^\u327e\xd1\xd0&=\x9f\x00\x00\x00\u07d4;\xc6\xe3\xeezV\u038f\x14\xa3u2Y\x0fcqk\x99f\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\xc8]ls[\x9c\xdaK\xba_H\xb2K\x13\xe7\x0600{\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4;\xd6$\xb5H\xcbe\x976\x90~\u062a<\fp^$\xb5u\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\u0660m\x1b\xd3lN\xdd'\xfc\r\x1f[\b\x8d\xda\xe3\xc7*\x89\x1b\x1azB\v\xa0\r\x00\x00\u0794;\u077c\x814\xf7}UY\u007f\xc9|&\xd2f\x98\t\x06\x04\ub23e -j\x0e\xda\x00\x00\xe0\x94;\xf8n\u0623\x15>\xc93xj\x02\xac\t\x03\x01\x85^Wk\x8a_J\x8c\x83u\xd1U@\x00\x00\u07d4;\xfb\u04c4|\x17\xa6\x1c\xf3\xf1{R\xf8\ub879`\xb3\U000df262\xa1]\tQ\x9b\xe0\x00\x00\u07d4<\x03\xbb\xc0#\xe1\xe9?\xa3\xa3\xa6\xe4(\xcf\f\xd8\xf9^\x1e\u0189Rf<\u02b1\xe1\xc0\x00\x00\u07d4<\f=\ufb1c\xeaz\xcc1\x9a\x96\xc3\v\x8e\x1f\xed\xabEt\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4<\x15\xb3Q\x1d\xf6\xf04.sH\u0309\xaf9\xa1h\xb7s\x0f\x8965\u026d\xc5\u07a0\x00\x00\u07d4<\x1f\x91\xf3\x01\xf4\xb5e\xbc\xa2GQ\xaa\x1fv\x13\"p\x9d\u0749a\t=|,m8\x00\x00\xe0\x94<(l\xfb0\x14n_\u05d0\xc2\xc8T\x15RW\x8d\xe34\u060a\x02)\x1b\x11\xaa0n\x8c\x00\x00\u07d4<2.a\x1f\u06c2\rG\xc6\xf8\xfcd\xb6\xfa\xd7L\xa9_^\x89\r%\x8e\xce\x1b\x13\x15\x00\x00\u07d4\xa5\xe5\xbfb\xbb\u0309\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4<\x86\x9c\tie#\xce\xd8$\xa0pAF\x05\xbbv#\x1f\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4<\x92V\x19\u02731DF?\x057\u06165\x87\x06\xc5 \xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\x98YK\xf6\x8bW5\x1e\x88\x14\xae\x9em\xfd-%J\xa0o\x89\x10CV\x1a\x88)0\x00\x00\u07d4<\xad\xeb=>\xed?b1\x1dRU>p\xdfJ\xfc\xe5o#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94<\xae\xdbS\x19\xfe\x80eC\xc5nP!\xd3r\xf7\x1b\xe9\x06.\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4<\xaf\xaf^bPV\x15\x06\x8a\xf8\xeb\"\xa1:\u0629\xe5Pp\x89lf\x06E\xaaG\x18\x00\x00\u07d4<\xb1y\xcbH\x01\xa9\x9b\x95\u00f0\xc3$\xa2\xbd\xc1\x01\xa6S`\x89\x01h\u048e?\x00(\x00\x00\u07d4<\xb5a\u0386BK5\x98\x91\xe3d\xec\x92_\xfe\xff'}\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xcbq\xaah\x80\xcb\v\x84\x01-\x90\xe6\a@\xec\x06\xac\u05cf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\xce\xf8\x86yW9G\xe9I\x97y\x8a\x1e2~\b`:e\x89+\xc9\x16\u059f;\x02\x00\x00\xe0\x94<\xd1\xd9s\x1b\xd5H\xc1\xddo\u03a6\x1b\xebu\xd9\x17T\xf7\u04ca\x01\x16\x1d\x01\xb2\x15\xca\xe4\x80\x00\u07d4<\u04e6\xe95y\xc5mIAq\xfcS>z\x90\xe6\xf5\x94d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\u05b7Y<\xbe\xe7x0\xa8\xb1\x9d\b\x01\x95\x8f\xcdK\xc5z\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4<\xd7\xf7\xc7\xc257\x80\xcd\xe0\x81\xee\xecE\x82+%\xf2\x86\f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xe1\u0717\xfc\u05f7\xc4\u04e1\x8aI\xd6\xf2\xa5\xc1\xb1\xa9\x06\u05c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xea0*G*\x94\x03y\xdd9\x8a$\xea\xfd\xba\u07c8\xady\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94<\xec\xa9k\xb1\xcd\xc2\x14\x02\x9c\xbc^\x18\x1d9\x8a\xb9M=A\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4<\xf4\x84RO\xbd\xfa\xda\xe2m\xc1\x85\xe3++c\x0f\xd2\xe7&\x89\x18TR\xcb*\x91\xc3\x00\x00\u07d4<\xf9\xa1\xd4e\xe7\x8bp9\xe3iDx\xe2b{6\xfc\xd1A\x89J`S*\xd5\x1b\xf0\x00\x00\u07d4<\xfb\xf0fVYpc\x9e\x13\r\xf2\xa7\xd1k\x0e\x14\xd6\t\x1c\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94=\th\x8d\x93\xad\a\xf3\xab\xe6\x8cr'#\xcdh\t\x90C^\x8a\x06ZL\xe9\x9fv\x9en\x00\x00\u07d4=1X{_\u0546\x98Ex\x87%\xa6c)\nI\xd3g\x8c\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4=?\xadI\xc9\xe5\xd2u\x9c\x8e\x8eZzM`\xa0\xdd\x13V\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4=WO\xcf\x00\xfa\xe1\u064c\u023f\x9d\u07e1\xb3\x95;\x97A\xbc\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4=Z\x8b+\x80\xbe\x8b5\xd8\xec\xf7\x89\xb5\xedz\au\xc5\al\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4=f\xcdK\xd6M\\\x8c\x1b^\xea(\x1e\x10m\x1cZ\xad#s\x89i\xc4\xf3\xa8\xa1\x10\xa6\x00\x00\u0794=j\xe0S\xfc\xbc1\x8do\xd0\xfb\xc3S\xb8\xbfT.h\r'\x88\xc6s\xce<@\x16\x00\x00\u07d4=o\xf8,\x93w\x05\x9f\xb3\r\x92\x15r?`\xc7u\u0211\xfe\x89\r\x8e\\\xe6\x17\xf2\xd5\x00\x00\u07d4=y\xa8S\xd7\x1b\xe0b\x1bD\xe2\x97Yel\xa0u\xfd\xf4\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4=~\xa5\xbf\x03R\x81\x00\xed\x8a\xf8\xae\xd2e>\x92\x1bng%\x8965\u026d\xc5\u07a0\x00\x00\u07d4=\x81?\xf2\xb6\xedW\xb97\u06bf+8\x1d\x14\x8aA\x1f\xa0\x85\x89\x05k\xc7^-c\x10\x00\x00\u07d4=\x88\x143\xf0J}\r'\xf8ID\xe0\x8aQ-\xa3UR\x87\x89A\rXj \xa4\xc0\x00\x00\u07d4=\x89\xe5\x05\xcbF\xe2\x11\xa5?2\xf1g\xa8w\xbe\xc8\u007fK\n\x89\x01[5W\xf1\x93\u007f\x80\x00\xe0\x94=\x8d\a#r\x1es\xa6\xc0\xd8`\xaa\x05W\xab\xd1L\x1e\xe3b\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4=\x8f9\x88\x1b\x9e\xdf\xe9\x12'\xc3?\xa4\xcd\xd9\x1eg\x85D\xb0\x89\x04\xab\a\xbaC\xad\xa9\x80\x00\u07d4=\x9dk\xe5\u007f\xf8>\x06Y\x85fO\x12VD\x83\xf2\xe6\x00\xb2\x89n\xac\xe4?#\xbd\x80\x00\x00\u07d4=\xa3\x9c\xe3\xefJz9f\xb3.\xe7\xeaN\xbc#5\xa8\xf1\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4=\xaa\x01\u03b7\x0e\xaf\x95\x91\xfaR\x1b\xa4\xa2~\xa9\xfb\x8e\xdeJ\x89Zc\xd2\u027cvT\x00\x00\u07d4=\xb5\xfejh\xbd6\x12\xac\x15\xa9\x9aa\xe5U\x92\x8e\xec\xea\xf3\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4=\xb9\xed\u007f\x02L~&7/\xea\xcf+\x05\b\x03D^8\x10\x89E\xb1H\xb4\x99j0\x00\x00\u07d4=\xbf\r\xbf\xd7x\x90\x80\x053\xf0\x9d\xea\x83\x01\xb9\xf0%\u04a6\x8965\u026d\xc5\u07a0\x00\x00\u07d4=\xce\U0005c18b\x15\xd3N\xdaBn\xc7\xe0K\x18\xb6\x01p\x02\x89lh\xcc\u041b\x02,\x00\x00\xe0\x94=\xd1.Uj`76\xfe\xbaJo\xa8\xbdJ\xc4]f*\x04\x8a#u{\x91\x83\xe0x(\x00\x00\u07d4=\u078b\x15\xb3\u033a\xa5x\x01\x12\xc3\xd6t\xf3\x13\xbb\xa6\x80&\x89`\x1dQZ>O\x94\x00\x00\xe0\x94=\xde\xdb\xe4\x89#\xfb\xf9\xe56\xbf\x9f\xfb\aG\xc9\xcd\u04de\xef\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4=\xea\xe43'\x91?b\x80\x8f\xaa\x1bbv\xa2\xbdch\xea\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4=\xf7b\x04\x9e\u068a\u0192}\x90Lz\xf4/\x94\xe5Q\x96\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>\x04\r@\u02c0\xba\x01%\xf3\xb1_\xde\xfc\xc8?0\x05\xda\x1b\x898E$\xccp\xb7x\x00\x00\u07d4>\v\x8e\xd8n\xd6i\xe1'#\xafur\xfb\xac\xfe\x82\x9b\x1e\x16\x89QM\xe7\xf9\xb8\x12\xdc\x00\x00\xe0\x94>\f\xbejm\xcba\xf1\x10\xc4[\xa2\xaa6\x1d\u007f\xca\xd3\xdas\x8a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4>\x19KN\xce\xf8\xbbq\x1e\xa2\xff$\xfe\xc4\xe8{\xd02\xf7\u0449\x8b\x9d\xc1\xbc\x1a\x03j\x80\x00\xe0\x94>\x1b\"0\xaf\xbb\xd3\x10\xb4\x92jLwmZ\u705cf\x1d\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4>\x1cS0\x0eL\x16\x89\x12\x16<~\x99\xb9]\xa2h\xad(\n\x896b2\\\u044f\xe0\x00\x00\u07d4>\x1c\x96 c\xe0\xd5)YA\xf2\x10\u0723\xabS\x1e\xec\x88\t\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4>,\xa0\xd24\xba\xf6\a\xadFj\x1b\x85\xf4\xa6H\x8e\xf0\n\xe7\x89\x04\xda!\xa3H=V\x80\x00\u07d4>/&#^\x13zs$\xe4\xdc\x15K]\xf5\xafF\xea\x1aI\x89\x017\xaa\xd8\x03-\xb9\x00\x00\xe0\x94>1a\xf1\xea/\xbf\x12ny\xda\x18\x01\u0695\x12\xb3y\x88\u024a\nm\xd9\f\xaeQ\x14H\x00\x00\xe0\x94>6\xc1rS\xc1\x1c\xf3\x89t\xed\r\xb1\xb7Y\x16\r\xa67\x83\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4><\u04fe\xc0e\x91\xd64o%Kb\x1e\xb4\x1c\x89\x00\x8d1\x895\u07fe\u069f74\x00\x00\u07d4>E\xbdU\u06d0`\xec\xed\x92;\xb9\xcbs<\xb3W?\xb51\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4>M\x13\xc5Z\x84\xe4n\xd7\xe9\u02d0\xfd5^\x8a\u0651\u33c965\u026d\xc5\u07a0\x00\x00\u07d4>N\x92e\"<\x9782L\xf2\v\xd0`\x06\xd0\a>\u06cc\x89\a?u\u0460\x85\xba\x00\x00\xe0\x94>O\xbdf\x10\x15\xf6F\x1e\xd6s\\\xef\xef\x01\xf3\x14E\xde:\x8a\x03n4)\x98\xb8\xb0 \x00\x00\xe0\x94>S\xff!\a\xa8\u07be3(I:\x92\xa5\x86\xa7\xe1\xf4\x97X\x8a\x04\xe6\x9c*q\xa4\x05\xab\x00\x00\u07d4>Z9\xfd\xdap\xdf\x11&\xab\r\u011asx1\x1aSz\x1f\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\xe0\x94>Z\xbd\t\xceZ\xf7\xba\x84\x87\xc3Y\xe0\xf2\xa9:\x98k\v\x18\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4>\\\xb8\x92\x8cAx%\xc0:;\xfc\xc5!\x83\xe5\xc9\x1eB\u05c9\xe71\xd9\xc5,\x96/\x00\x00\u07d4>^\x93\xfbL\x9c\x9d\x12F\xf8\xf2G5\x8e\"\xc3\xc5\xd1{j\x89\b!\xab\rD\x14\x98\x00\x00\u07d4>a\x83P\xfa\x01ez\xb0\xef>\xba\xc8\xe3p\x12\xf8\xfc+o\x89\x98\x06\xde=\xa6\xe9x\x00\x00\u07d4>c\xce;$\xca(e\xb4\u0166\x87\xb7\xae\xa3Y~\xf6\xe5H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>f\xb8GiVj\xb6yE\xd5\xfa\x8175V\xbc\u00e1\xfa\x89\b=lz\xabc`\x00\x00\xe0\x94>v\xa6-\xb1\x87\xaat\xf68\x17S;0l\xea\xd0\xe8\u03be\x8a\x06\x9bZ\xfa\xc7P\xbb\x80\x00\x00\u07d4>z\x96k]\xc3W\xff\xb0~\x9f\xe0g\xc4W\x91\xfd\x8e0I\x89\x034-`\xdf\xf1\x96\x00\x00\xe0\x94>\x81w!u#~\xb4\xcb\xe0\xfe-\xca\xfd\xad\xff\xebj\x19\x99\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4>\x83I\xb6\u007fWED\x9fe\x93g\u066dG\x12\xdb[\x89Z\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4>\x83TO\x00\x82U%r\u01c2\xbe\xe5\xd2\x18\xf1\xef\x06J\x9d\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4>\x84\xb3\\[\"ePpa\xd3\vo\x12\xda\x03?\xe6\xf8\xb9\x89a\t=|,m8\x00\x00\u07d4>\x86A\xd4\x87E\xba2/_\xd6\xcbP\x12N\xc4f\x88\u01e6\x9a\u007f\xae\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4>\x91N0\x18\xac\x00D\x93A\u011d\xa7\x1d\x04\xdf\xee\xedb!\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4>\x94\x10\u04f9\xa8~\xd5\xe4Q\xa6\xb9\x1b\xb8\x92?\xe9\x0f\xb2\xb5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4>\x94\xdfS\x13\xfaR\x05p\xef#+\xc31\x1d_b/\xf1\x83\x89lk\x93[\x8b\xbd@\x00\x00\u0794>\x9b4\xa5\u007f3u\xaeY\xc0\xa7^\x19\u0136A\"\x8d\x97\x00\x88\xf8i\x93)g~\x00\x00\u07d4>\xad\xa8\xc9/V\x06~\x1b\xb7<\xe3x\xdaV\xdc,\xdf\xd3e\x89w\xcd\xe9:\xeb\rH\x00\x00\xe0\x94>\xaf\by\xb5\xb6\xdb\x15\x9bX\x9f\x84W\x8bjt\xf6\xc1\x03W\x8a\x01\x898\xb6q\xfae\xa2\x80\x00\u07d4>\xaf1k\x87a]\x88\xf7\xad\xc7|X\xe7\x12\xedMw\x96k\x89\x05m\xbcL\xee$d\x80\x00\u07d4>\xb8\xb3;!\xd2<\u0686\xd8(\x88\x84\xabG\x0e\x16F\x91\xb5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4>\xb9\xef\x06\xd0\xc2Y\x04\x03\x19\x94~\x8czh\x12\xaa\x02S\u0609\t\r\x97/22<\x00\x00\u07d4>\u030e\x16h\xdd\xe9\x95\xdcW\x0f\xe4\x14\xf4B\x11\xc54\xa6\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>\u03752\xe3\x97W\x96b\xb2\xa4aA\u73c25\x93j_\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4>\xeeo\x1e\x966\vv\x89\xb3\x06\x9a\xda\xf9\xaf\x8e\xb6\f\u404965\u026d\xc5\u07a0\x00\x00\xe0\x94?\b\u066d\x89O\x81>\x8e!H\xc1`\xd2K5:\x8et\xb0\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4?\f\x83\xaa\xc5qybsN\\\xea\xea\xec\u04db(\xad\x06\xbe\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94?\x10\x80\x02\x82\u0477\xdd\u01cf\xa9-\x820\aN\x1b\xf6\xae\xae\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4?\x123qO M\xe9\xdeN\xe9m\a;6\x8d\x81\x97\x98\x9f\x89\x02\x17\xc4\x10t\xe6\xbb\x00\x00\u07d4?\x17:\xa6\xed\xf4i\u0445\xe5\x9b\xd2j\xe4#k\x92\xb4\xd8\xe1\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4?\x1b\xc4 \xc5<\x00,\x9e\x90\x03|D\xfej\x8e\xf4\xdd\xc9b\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4?#a\b\xee\xc7\"\x89\xba\u00e6\\\u0483\xf9^\x04\x1d\x14L\x8964\xbf9\xab\x98x\x80\x00\u07d4?-\xa0\x93\xbb\x16\xeb\x06O\x8b\xfa\x9e0\xb9)\xd1_\x8e\x1cL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?-\xd5]\xb7\xea\xb0\xeb\xeee\xb3>\xd8 ,\x1e\x99.\x95\x8b\x89,s\xc97t,P\x00\x00\u07d4?/8\x14\x91y|\xc5\xc0\u0502\x96\xc1O\xd0\xcd\x00\xcd\xfa-\x89+\x95\xbd\xcc9\xb6\x10\x00\x00\u07d4?0\u04fc\x9f`\"2\xbcrB\x88\xcaF\xcd\v\a\x88\xf7\x15\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4?<\x8ea\xe5`L\xef\x06\x05\xd46\xdd\"\xac\u0346\"\x17\xfc\x89Hz\x9a0E9D\x00\x00\u07d4??F\xb7\\\xab\xe3{\xfa\u0307`(\x1fCA\xca\u007fF=\x89 \xacD\x825\xfa\xe8\x80\x00\u07d4?G)c\x19x\x83\xbb\xdaZ\x9b}\xfc\xb2-\xb1\x14@\xad1\x89\x1a\x19d<\xb1\xef\xf0\x80\x00\u07d4?L\xd19\x9f\x8a4\xed\u06da\x17\xa4q\xfc\x92+Xp\xaa\xfc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4?U\x1b\xa9<\xd5F\x93\xc1\x83\xfb\x9a\xd6\re\xe1`\x96s\u0249lk\x93[\x8b\xbd@\x00\x00\xe0\x94?bzv\x9ej\x95\x0e\xb8p\x17\xa7\u035c\xa2\bq\x13h1\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4?m\xd3e\x0e\xe4(\u0737u\x95S\xb0\x17\xa9j\x94(j\u0249Hz\x9a0E9D\x00\x00\u07d4?tr7\x80o\xed?\x82\x8ahR\xeb\bg\xf7\x90'\xaf\x89\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4?u\xaea\xcc\x1d\x80Be;[\xae\xc4D>\x05\x1c^z\xbd\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4?\xb7\u0457\xb3\xbaO\xe0E\xef\xc2=P\xa1E\x85\xf5X\u0672\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94?\xbc\x1eE\x18\xd74\x00\xc6\xd0F5\x949\xfbh\xea\x1aI\xf4\x8a\x03y\v\xb8U\x13v@\x00\x00\u07d4?\xbe\xd6\xe7\xe0\u029c\x84\xfb\xe9\xeb\u03ddN\xf9\xbbIB\x81e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?\u043bGy\x8c\xf4L\u07feM3=\xe67\xdfJ\x00\xe4\\\x89\x05lUy\xf7\"\x14\x00\x00\xe0\x94?\xe4\x0f\xbd\x91\x9a\xad(\x18\xdf\x01\xeeM\xf4lF\x84*\xc59\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4?\xe8\x01\xe6\x135\xc5\x14\r\xc7\xed\xa2\xefR\x04F\nP\x120\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?\xf86\xb6\xf5{\x90\x1bD\f0\xe4\xdb\xd0e\xcf7\xd3\u050c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4?\xfc\xb8p\xd4\x02=%]Qg\u0625\a\xce\xfc6kh\xba\x89#4^\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4@s\xfaI\xb8q\x17\u02d0\x8c\xf1\xabQ-\xa7T\xa92\xd4w\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4@\x8ai\xa4\a\x15\xe1\xb3\x13\xe15N`\b\x00\xa1\xe6\xdc\x02\xa5\x89\x01\u7e11\u0312T\x00\x00\u07d4@\x9b\xd7P\x85\x82\x1c\x1d\xe7\f\xdc;\x11\xff\xc3\xd9#\xc7@\x10\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4@\x9dZ\x96.\xde\uefa1x\x01\x8c\x0f8\xb9\u0372\x13\xf2\x89\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4@\xa31\x19[\x97s%\u00aa(\xfa/B\xcb%\xec<%<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4@\xa7\xf7(g\xa7\u0706w\v\x16+uW\xa44\xedP\xcc\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d4@\xab\n>\x83\xd0\u022c\x93f\x91\x05 \xea\xb1w+\xac;\x1a\x894\xf1\f-\xc0^|\x00\x00\u07d4@\xabf\xfe!>\xa5l:\xfb\x12\xc7[\xe3?\x8e2\xfd\b]\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94@\xadt\xbc\v\xce*E\xe5/6\xc3\u07bb\x1b:\xda\x1bv\x19\x8a\x01p\x16-\xe1\t\xc6X\x00\x00\u07d4@\u03c9\x05\x91\xea\u484f\x81*)T\xcb)_c3'\xe6\x89\x02\x9b\xf76\xfcY\x1a\x00\x00\u07d4@\u03d0\xef[v\x8c]\xa5\x85\x00,\xcb\xe6avP\xd8\xe87\x8963\x03\"\xd5#\x8c\x00\x00\xe0\x94@\xd4]\x9dv%\xd1QV\xc92\xb7q\xca{\x05'\x13\tX\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4@\xdb\x1b\xa5\x85\xce4S\x1e\xde\xc5IHI9\x13\x81\xe6\xcc\u04c9a\t=|,m8\x00\x00\xe0\x94@\xdfI^\xcf?\x8bL\xef*l\x18\x99W$\x8f\u813c+\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4@\xe0\xdb\xf3\xef\uf404\xea\x1c\xd7\xe5\x03\xf4\v;J\x84C\xf6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4@\xe2D\n\xe1B\u02006j\x12\xc6\xd4\x10/K\x844\xb6*\x8965\u026d\xc5\u07a0\x00\x00\u07d4@\xe3\u0083\xf7\xe2M\xe0A\f\x12\x1b\xee`\xa5`\u007f>)\xa6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94@\xeaPD\xb2\x04\xb20v\xb1\xa5\x80;\xf1\xd3\f\x0f\x88\x87\x1a\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94@\xed\xdbD\x8di\x0e\xd7.\x05\xc2%\xd3O\xc85\x0f\xa1\xe4\u014a\x01{x\x83\xc0i\x16`\x00\x00\xe0\x94@\xf4\xf4\xc0ls,\xd3[\x11\x9b\x89;\x12~}\x9d\aq\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4A\x01\x0f\u023a\xf8C}\x17\xa0Ci\x80\x9a\x16\x8a\x17\xcaV\xfb\x89\x05k\xc7^-c\x10\x00\x00\u07d4A\x03)\x96q\xd4gc\x97\x8f\xa4\xaa\x19\xee4\xb1\xfc\x95'\x84\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4A\x03<\x1bm\x05\xe1\u0289\xb0\x94\x8f\xc6DS\xfb\xe8z\xb2^\x89Hz\x9a0E9D\x00\x00\u07d4A\t\x8a\x81E#\x17\xc1\x9e>\xef\v\xd1#\xbb\xe1x\xe9\xe9\u0289\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4A\x16\x10\xb1x\xd5a}\xfa\xb94\u0493\xf5\x12\xa9>\\\x10\xe1\x89\t79SM(h\x00\x00\u07d4A\x1c\x83\x1c\xc6\xf4O\x19e\xecWW\xabN[<\xa4\xcf\xfd\x1f\x89\x17\n\x0fP@\xe5\x04\x00\x00\xe0\x94A*h\xf6\xc6EU\x9c\xc9w\xfcId\x04z \x1d\x1b\xb0\xe2\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4A?K\x02f\x9c\xcf\xf6\x80k\xc8&\xfc\xb7\xde\xca;\x0e\xa9\xbc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4AE\x99\t.\x87\x9a\xe2Sr\xa8MsZ\xf5\xc4\xe5\x10\xcdm\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4AHV\x12\xd04F\xecL\x05\xe5$NV?\x1c\xba\xe0\xf1\x97\x894\x95tD\xb8@\xe8\x00\x00\u07d4A]\tj\xb0b\x93\x18?<\x03=%\xf6\xcfqx\xac;\u01c9\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4Af\xfc\b\u0285\xf7f\xfd\xe81F\x0e\x9d\xc9<\x0e!\xaal\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Ag\x84\xaf`\x960\xb0p\u051a\x8b\xcd\x12#\\d(\xa4\b\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Ag\xcdH\xe73A\x8e\x8f\x99\xff\xd14\x12\x1cJJ\xb2x\u0109\xc5S%\xcat\x15\xe0\x00\x00\u07d4Al\x86\xb7 \x83\xd1\xf8\x90}\x84\xef\xd2\xd2\u05c3\xdf\xfa>\xfb\x89lj\xccg\u05f1\xd4\x00\x00\u07d4AsA\x9d\\\x9fc)U\x1d\xc4\xd3\xd0\u03ac\x1bp\x1b\x86\x9e\x89\x04\xc5>\xcd\xc1\x8a`\x00\x00\u07d4At\xfa\x1b\xc1*;q\x83\u02eb\xb7z\vYU{\xa5\xf1\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4Axj\x10\xd4G\xf4\x84\xd32D\u0337\xfa\u034bB{[\x8c\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Az<\u0454\x96S\nmB\x04\u00f5\xa1|\xe0\xf2\a\xb1\xa5\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4A~N&\x88\xb1\xfdf\xd8!R\x9eF\xedOB\xf8\xb3\xdb=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94A\x9aq\xa3l\x11\xd1\x05\xe0\xf2\xae\xf5\xa3\xe5\x98\a\x8e\x85\xc8\v\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94A\x9b\xdes\x16\xcc\x1e\u0495\u0205\xac\xe3B\u01db\xf7\xee3\xea\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4A\xa2\xf2\xe6\xec\xb8c\x94\xec\x0e3\x8c\x0f\xc9~\x9cU\x83\xde\u0489l\xee\x06\u077e\x15\xec\x00\x00\u07d4A\xa8\u0083\x00\x81\xb1\x02\xdfn\x011e|\a\xabc[T\u0389lj\xccg\u05f1\xd4\x00\x00\u07d4A\xa8\xe26\xa3\x0emc\xc1\xffdM\x13*\xa2\\\x89S~\x01\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4A\xa9\xa4\x04\xfc\x9f[\xfe\xe4\x8e\xc2e\xb1%#3\x8e)\xa8\xbf\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4A\xad6\x9fu\x8f\xef8\xa1\x9a\xa3\x14\x93y\x83,\x81\x8e\xf2\xa0\x8966\x9e\xd7t}&\x00\x00\u07d4A\xb2\xd3O\xde\v\x10)&+Ar\xc8\x1c\x15\x90@[\x03\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4A\xb2\xdb\u05dd\u069b\x86Ojp0'T\x19\u00dd>\xfd;\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4A\xc3\xc26u4\xd1;\xa2\xb3?\x18\\\xdb\xe6\xacC\xc2\xfa1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4A\u02d8\x96D_p\xa1\n\x14!R\x96\xda\xf6\x14\xe3,\xf4\u0549g\x8a\x93 b\xe4\x18\x00\x00\u07d4A\xcey\x95\t5\xcf\xf5[\xf7\x8eL\xce\xc2\xfec\x17\x85\u06d5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4A\u04f71\xa3&\xe7hX\xba\xa5\xf4\xbd\x89\xb5{6\x93#C\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\xe0\x94A\xe4\xa2\x02u\xe3\x9b\xdc\xef\xebe\\\x03\"tKvQ@\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4A\xed-\x8ep\x81H,\x91\x9f\xc2=\x8f\x00\x91\xb3\xc8,F\x85\x89F:\x1ev[\u05ca\x00\x00\xe0\x94A\xf2~tK\u049d\xe2\xb0Y\x8f\x02\xa0\xbb\x9f\x98\xe6\x81\ua90a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\u07d4A\xf4\x89\xa1\xect{\u009c>_\x9d\x8d\xb9xw\xd4\u0474\xe9\x89\a?u\u0460\x85\xba\x00\x00\u07d4B\x0f\xb8n}+Q@\x1f\xc5\xe8\xc7 \x15\xde\xcbN\xf8\xfc.\x8965\u026d\xc5\u07a0\x00\x00\u07d4B\x16\x84\xba\xa9\xc0\xb4\xb5\xf5S8\xe6\xf6\xe7\xc8\xe1F\xd4\x1c\xb7\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4B9\x96Y\xac\xa6\xa5\xa8c\xea\"E\xc93\xfe\x9a5\xb7\x88\x0e\x89n\xce2\xc2l\x82p\x00\x00\xe0\x94B;\xcaG\xab\xc0\fpW\xe3\xad4\xfc\xa6>7_\xbd\x8bJ\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4B<1\a\xf4\xba\xceANI\x9cd9\nQ\xf7F\x15\xca^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B<\xc4YL\xf4\xab\xb66\x8d\xe5\x9f\u04b1#\a4a!C\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94BD\xf13\x11X\xb9\xce&\xbb\xe0\xb9#k\x92\x03\xca5\x144\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794BQw\xebt\xad\n\x9d\x9aWR\"\x81G\xeemcV\xa6\u6239\x8b\xc8)\xa6\xf9\x00\x00\u07d4BW%\xc0\xf0\x8f\b\x11\xf5\xf0\x06\xee\xc9\x1c\\\\\x12k\x12\xae\x89\b!\xab\rD\x14\x98\x00\x00\xe0\x94BX\xfdf/\xc4\xce2\x95\xf0\xd4\xed\x8f{\xb1D\x96\x00\xa0\xa9\x8a\x01lE.\xd6\b\x8a\xd8\x00\x00\xe0\x94B\\\x18\x16\x86\x8fww\xcc+\xa6\xc6\u048c\x9e\x1eylR\xb3\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4B\\3\x8a\x13%\xe3\xa1W\x8e\xfa)\x9eW\u0646\xebGO\x81\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94BbY\xb0\xa7Vp\x1a\x8bf5(R!V\xc0(\x8f\x0f$\x8a\x02\x18\xae\x19k\x8dO0\x00\x00\u07d4Bm\x15\xf4\a\xa0\x115\xb1:kr\xf8\xf2R\v51\xe3\x02\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Box\xf7\r\xb2Y\xac\x854\x14[)4\xf4\xef\x10\x98\xb5\u0609\x13\x84\x00\xec\xa3d\xa0\x00\x00\u07d4Bs-\x8e\xf4\x9f\xfd\xa0K\x19x\x0f\xd3\xc1\x84i\xfb7A\x06\x89\x17\v\x00\xe5\u4a7e\x00\x00\u07d4Bt\x17\xbd\x16\xb1\xb3\xd2-\xbb\x90-\x8f\x96W\x01o$\xa6\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Btj\xee\xa1O'\xbe\xff\f\r\xa6BS\xf1\xe7\x97\x18\x90\xa0\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4B{F*\xb8NP\x91\xf4\x8aF\xeb\f\u0712\xdd\xcb&\xe0x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B~GQ\u00fa\xbex\xcf\xf8\x83\b\x86\xfe\xbc\x10\xf9\x90\x8dt\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94B~\xc6h\xac\x94\x04\xe8\x95\u0306\x15\x11\xd1b\nI\x12\xbe\x98\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4B\x80\xa5\x8f\x8b\xb1\v\x94@\u0794\xf4+OY! \x82\x01\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B\x8a\x1e\xe0\xed3\x1dyR\u033e\x1cyt\xb2\x85+\u0453\x8a\x89w\xb7JN\x8d\xe5e\x00\x00\u0794B\x9c\x06\xb4\x87\xe8Tj\xbd\xfc\x95\x8a%\xa3\xf0\xfb\xa5?o\x00\x88\xbbdJ\xf5B\x19\x80\x00\xe0\x94B\xa9\x8b\xf1`'\xceX\x9cN\xd2\xc9X1\xe2rB\x05\x06N\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4B\xc6\xed\xc5\x15\xd3UW\x80\x8d\x13\xcdD\xdc\xc4@\v%\x04\xe4\x89\n\xba\x14\u015b\xa72\x00\x00\u07d4B\xce\xcf\u0492\x10y\xc2\xd7\xdf?\b\xb0z\xa3\xbe\xee^!\x9a\x8965\u026d\xc5\u07a0\x00\x00\u07d4B\u04669\x9b0\x16\xa8Y\u007f\x8bd\t'\xb8\xaf\xbc\xe4\xb2\x15\x89\xa1\x8b\xce\xc3H\x88\x10\x00\x00\u07d4B\xd3I@\xed\xd2\xe7\x00]F\xe2\x18\x8eL\xfe\u0383\x11\xd7M\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4B\u04e5\xa9\x01\xf2\xf6\xbd\x93V\xf1\x12\xa7\x01\x80\xe5\xa1U\v`\x892$\xf4'#\xd4T\x00\x00\u07d4B\u05b2c\xd9\xe9\xf4\x11lA\x14$\xfc\x99Ux;\xa1\xc5\x1b\x81\x0f\xc4g\u057aM\xeaB\xf7\xa9\x88^i\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94C>\xb9J3\x90\x86\xed\x12\u067d\xe9\xcd\x1dE\x86\x03\xc9}\u058a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4CI\"Zb\xf7\n\xeaH\n\x02\x99\x15\xa0\x1eSy\xe6O\xa5\x89\x8c\xd6~#4\xc0\xd8\x00\x00\u07d4CT\"\x1eb\xdc\t\xe6@d6\x16:\x18^\xf0m\x11J\x81\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94CTC\xb8\x1d\xfd\xb9\xbd\x8cg\x87\xbc%\x18\xe2\xd4~W\xc1_\x8a\x01C\x8d\x93\x97\x88\x1e\xf2\x00\x00\u07d4Ca\u0504o\xaf\xb3w\xb6\xc0\xeeI\xa5\x96\xa7\x8d\xdf5\x16\xa3\x89\xc2\x12z\xf8X\xdap\x00\x00\xe0\x94Cd0\x9a\x9f\xa0p\x95`\x0fy\xed\xc6Q \xcd\xcd#\xdcd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Cg\xaeK\f\xe9d\xf4\xa5J\xfdK\\6\x84\x96\xdb\x16\x9e\x9a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Ct\x89(\xe8\xc3\xecD6\xa1\u0412\xfb\xe4:\xc7I\xbe\x12Q\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4Cv{\xf7\xfd*\xf9[r\xe91-\xa9D<\xb1h\x8eCC\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94Cy\x838\x8a\xb5\x9aO\xfc!_\x8e\x82iF\x10)\xc3\xf1\xc1\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4C\x89\x8cI\xa3MP\x9b\xfe\xd4\xf7`A\xee\x91\xca\xf3\xaaj\xa5\x89\x10CV\x1a\x88)0\x00\x00\u07d4C\x8c/T\xff\x8eb\x9b\xab6\xb1D+v\v\x12\xa8\x8f\x02\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\x98b\x8e\xa6c-9>\x92\x9c\xbd\x92\x84d\xc5h\xaaJ\f\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4C\x9d//Q\x10\xa4\u054b\x17W\x93P\x15@\x87@\xfe\xc7\xf8\x89\u03e5\xc5\x15\x0fL\x88\x80\x00\u07d4C\x9d\xee?vy\xff\x100s?\x93@\xc0\x96hkI9\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xb0y\xba\xf0ry\x99\xe6k\xf7C\u057c\xbfwl;\t\"\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xbc-M\xdc\xd6X;\xe2\u01fc\tK(\xfbr\xe6+\xa8;\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xc7\xeb\u0173\xe7\xaf\x16\xf4}\xc5az\xb1\x0e\x0f9\xb4\xaf\xbb\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4C\u02d6R\x81\x8coMg\x96\xb0\xe8\x94\t0ly\xdbcI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xcc\b\xd0s*\xa5\x8a\xde\xf7a\x9b\xedFU\x8a\xd7wAs\x89\xf0\xe7\u0730\x12*\x8f\x00\x00\xe0\x94C\u0567\x1c\xe8\xb8\xf8\xae\x02\xb2\xea\xf8\xea\xf2\xca(@\xb9?\xb6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794C\xdb\u007f\xf9Z\bm(\ubff8/\xb8\xfb_#\n^\xbc\u0348\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4C\xe7\xec\x84cX\xd7\xd0\xf97\xad\x1c5\v\xa0i\u05ffr\xbf\x89\x06p\xaeb\x92\x14h\x00\x00\u07d4C\xf1o\x1eu\xc3\xc0j\x94x\xe8\u0157\xa4\n<\xb0\xbf\x04\u0309\x9d\xf7\u07e8\xf7`H\x00\x00\u07d4C\xf4p\xede\x9e)\x91\xc3u\x95~]\xde\u017d\x1d8\"1\x89\x05k\xc7^-c\x10\x00\x00\u07d4C\xf7\xe8n8\x1e\xc5\x1e\u0110m\x14v\u02e9z=\xb5\x84\xe4\x8965\u026d\xc5\u07a0\x00\x00\u07d4C\xff8t>\xd0\xcdC0\x8c\x06e\t\u030e~r\xc8b\xaa\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94C\xff\x88S\xe9\x8e\xd8@k\x95\x00\n\u0684\x83b\u05a09*\x8a\x04\xae\v\x1cM.\x84\xd0\x00\x00\u07d4D\t\x88f\xa6\x9bh\xc0\xb6\xbc\x16\x82)\xb9`5\x87\x05\x89g\x89\n1\x06+\xee\xedp\x00\x00\u07d4D\x19\xaca\x8d]\xea|\xdc`w o\xb0}\xbd\xd7\x1c\x17\x02\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4D\x1aR\x00\x16a\xfa\xc7\x18\xb2\u05f3Q\xb7\xc6\xfbR\x1az\xfd\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94D\x1a\u0282c\x13$\xac\xbf\xa2F\x8b\xda2[\xbdxG{\xbf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4D\x1f7\xe8\xa0)\xfd\x02H/(\x9cI\xb5\xd0m\x00\xe4\b\xa4\x89\x12\x11\xec\xb5m\x13H\x80\x00\u07d4D \xaa5F[\xe6\x17\xad$\x98\xf3p\xde\n<\xc4\xd20\xaf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4D#/\xf6m\xda\xd1\xfd\x84\x12f8\x006\xaf\xd7\xcf}\u007fB\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4D%\rGn\x06$\x84\xe9\b\n9g\xbf:Js*\xd7?\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4D)\xa2\x9f\xee\x19\x84Pg,\f\x1d\a1b%\v\xecdt\x896*\xaf\x82\x02\xf2P\x00\x00\u07d4D5RS\xb2wH\xe3\xf3O\xe9\xca\xe1\xfbq\x8c\x8f$\x95)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4D8\xe8\x80\xcb'f\xb0\xc1\u03ae\xc9\xd2A\x8f\u03b9R\xa0D\x89\a?\xa0s\x90?\b\x00\x00\u07d4DL\xafy\xb7\x138\ue6a7\xc73\xb0*\u02a7\xdc\x02YH\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4D\\\xb8\xde^=\xf5 \xb4\x99\xef\u0240\xf5+\xff@\xf5\\v\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Dj\x809\xce\u03dd\xceHy\xcb\xca\xf3I;\xf5E\xa8\x86\x10\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4Dt)\x9d\x0e\xe0\x90\u0710x\x9a\x14\x86H\x9c=\rd^m\x8965\u026d\xc5\u07a0\x00\x00\u07d4D\x8b\xf4\x10\xad\x9b\xbc/\xec\xc4P\x8d\x87\xa7\xfc.K\x85a\xad\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4D\x90\x1e\r\x0e\b\xac=^\x95\xb8\xec\x9d^\x0f\xf5\xf1.\x03\x93\x89\x16\xa1\xf9\xf5\xfd}\x96\x00\x00\xe0\x94D\x93\x12<\x02\x1e\xce;3\xb1\xa4R\xc9&\x8d\xe1@\a\xf9\u04ca\x01je\x02\xf1Z\x1eT\x00\x00\xe0\x94D\x9a\xc4\xfb\xe3\x83\xe3g8\x85^6JW\xf4q\xb2\xbf\xa11\x8a)\xb7d2\xb9DQ \x00\x00\u07d4D\xa0\x1f\xb0J\xc0\xdb,\xce]\xbe(\x1e\x1cF\xe2\x8b9\xd8x\x89lj\xccg\u05f1\xd4\x00\x00\u07d4D\xa6=\x18BE\x87\xb9\xb3\a\xbf\xc3\xc3d\xae\x10\xcd\x04\xc7\x13\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94D\xa8\x98\x9e20\x81!\xf7$f\x97\x8d\xb3\x95\xd1\xf7l:K\x8a\x01\x88P)\x9fB\xb0j\x00\x00\u07d4D\xc1\x11\v\x18\x87\x0e\xc8\x11x\xd9=!X8\xc5Q\u050ed\x89\n\xd6\xf9\x85\x93\xbd\x8f\x00\x00\u07d4D\xc1Ge\x12|\xde\x11\xfa\xb4l],\xf4\u0532\x89\x00#\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94D\xc5N\xaa\x8a\xc9@\xf9\xe8\x0f\x1et\xe8/\xc1O\x16v\x85j\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4D\xcdwSZ\x89?\xa7\xc4\xd5\xeb:$\x0ey\u0419\xa7--\x89,s\xc97t,P\x00\x00\u07d4D\u07faP\xb8)\xbe\xcc_O\x14\u0470J\xab3 \xa2\x95\xe5\x8965\u026d\xc5\u07a0\x00\x00\u07d4D\xe2\xfd\xc6y\xe6\xbe\xe0\x1e\x93\xefJ:\xb1\xbc\xce\x01*\xbc|\x89\x16=\x19I\x00\xc5E\x80\x00\xe0\x94D\xf6/*\xaa\xbc)\xad:k\x04\xe1\xffo\x9c\xe4R\xd1\xc1@\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4D\xff\xf3{\xe0\x1a8\x88\u04f8\xb8\u1200\xa7\xdd\xef\xee\xea\u04c9\x0e\f[\xfc}\xae\x9a\x80\x00\u07d4E\x06\xfe\x19\xfaK\x00k\xaa9\x84R\x9d\x85\x16\xdb++P\xab\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\x1b6\x99G[\xed]y\x05\xf8\x90Z\xa3Eo\x1e\u05c8\xfc\x89\x8a\xc7#\x04\x89\xe8\x00\x00\x00\u0794E\x1bpp%\x9b\u06e2q\x00\xe3n#B\x8aS\xdf\xe3\x04\u9239\x8b\xc8)\xa6\xf9\x00\x00\u07d4E'+\x8fb\xe9\xf9\xfa\x8c\xe0D \u1ba3\xeb\xa9hn\xac\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94E+d\u06ce\xf7\xd6\u07c7\u01c8c\x9c\"\x90\xbe\x84\x82\xd5u\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4E>5\x9a3\x97\x94LZ'Z\xb1\xa2\xf7\n^Z?i\x89\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4EI\xb1Yy%_~e\xe9\x9b\rV\x04\u06d8\xdf\xca\u023f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4EKa\xb3D\xc0\xef\x96Qy#\x81U\xf2w\u00c2\x9d\v8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94EO\x01A\xd7!\xd3<\xbd\xc4\x10\x18\xbd\x01\x11\x9a\xa4xH\x18\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4ES3\x90\xe3@\xfe\r\xe3\xb3\xcf_\xb9\xfc\x8e\xa5R\xe2\x9eb\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4ES\x96\xa4\xbb\u067a\u8bdf\xb7\xc4\xd6MG\x1d\xb9\xc2E\x05\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4E[\x92\x96\x92\x1at\xd1\xfcAa\u007fC\xb80>o>\xd7l\x89\u3bb5sr@\xa0\x00\x00\u07d4E\\\xb8\xee9\xff\xbcu#1\xe5\xae\xfcX\x8e\xf0\xeeY4T\x8965F:x\r\xef\x80\x00\u07d4Ej\u0b24\x8e\xbc\xfa\xe1f\x06\x02PR_c\x96^v\x0f\x89\x10CV\x1a\x88)0\x00\x00\u07d4Eo\x8dtf\x82\xb2$g\x93I\x06M\x1b6\x8c|\x05\xb1v\x89\u0213\u041c\x8fQP\x00\x00\u07d4Ep)\xc4i\xc4T\x8d\x16\x8c\xec>e\x87.D(\xd4+g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Eq\xdeg+\x99\x04\xba\xd8t6\x92\xc2\x1cO\xdc\xeaL.\x01\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Ex\x1b\xbew\x14\xa1\xc8\xf7;\x1cty!\xdfO\x84'\x8bp\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E{\xce\xf3}\xd3\xd6\v-\xd0\x19\xe3\xfea\xd4k?\x1erR\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94E\x8e<\u025e\x94xD\xa1\x8ejB\x91\x8f\xef~\u007f_^\xb3\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\u07d4E\x93\x93\xd6:\x06>\xf3r\x1e\x16\xbd\x9f\xdeE\ue77dw\xfb\x89j\xba\u05a3\xc1S\x05\x00\x00\u07d4E\xa5p\xdc\xc2\t\f\x86\xa6\xb3\xea)\xa6\bc\xdd\xe4\x1f\x13\xb5\x89\f\x9a\x95\xee)\x86R\x00\x00\u07d4E\xa8 \xa0g/\x17\xdct\xa0\x81\x12\xbcd?\xd1\x16w6\u00c9\n\xd6\xc4;(\x15\xed\x80\x00\u07d4E\xb4q\x05\xfeB\xc4q-\xcen*!\xc0[\xff\xd5\xeaG\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\xbb\x82\x96R\u063f\xb5\x8b\x85'\xf0\xec\xb6!\u009e!.\u00c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\xc0\u045f\v\x8e\x05O\x9e\x8986\xd5\xec\xaey\x01\xaf(\x12\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4E\xc4\xec\xb4\xee\x89\x1e\xa9\x84\xa7\xc5\xce\xfd\x8d\xfb\x001\v(P\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4E\u028d\x95f\b\xf9\xe0\n/\x99t\x02\x86@\x88\x84ef\x8f\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\u0298b\x00;N@\xa3\x17\x1f\xb5\xca\xfa\x90(\xca\xc8\xde\x19\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4E\xd1\xc9\xee\xdf|\xabA\xa7y\x05{y9_T(\xd8\x05(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\u0535M7\xa8\xcfY\x98!#_\x06/\xa9\xd1p\xed\u8909\x11\x90g;_\u0690\x00\x00\xe0\x94E\xdb\x03\xbc\xcf\u05a5\xf4\xd0&k\x82\xa2*6\x87\x92\xc7}\x83\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4E\xe3\xa9>r\x14J\u0686\f\xbcV\xff\x85\x14Z\xda8\xc6\u0689WG=\x05\u06ba\xe8\x00\x00\u07d4E\u6378\u06fa\xba_\xc2\xcb3|b\xbc\xd0\xd6\x1b\x05\x91\x89\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\u6379L}\n\xb7\xacA\x85zq\xd6qG\x87\x0fNq\x8aT\xb4\v\x1f\x85+\xda\x00\x00\x00\u07d4E\xf4\xfc`\xf0\x8e\xac\xa1\x05\x98\xf03c)\x80\x1e<\x92\xcbF\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4F\rSU\xb2\xce\xebnb\x10}\x81\xe5\x12p\xb2k\xf4V \x89l\xb7\xe7Hg\xd5\xe6\x00\x00\xe0\x94F\"O2\xf4\xec\xe5\u0206p\x90\xd4@\x9dU\xe5\v\x18C-\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4F'\xc6\x06\x84&q\xab\u0782\x95\xee]\xd9L\u007fT\x954\xf4\x89\x0f\x89_\xbd\x872\xf4\x00\x00\u07d4F+g\x8bQ\xb5\x84\xf3\xedz\xda\a\v\\\u065c\v\xf7\xb8\u007f\x89\x05k\xc7^-c\x10\x00\x00\u07d4FM\x9c\x89\xcc\xe4\x84\xdf\x00\x02w\x19\x8e\xd8\a_\xa65r\u0449\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4FPNj!Z\xc8;\xcc\xf9V\xbe\xfc\x82\xabZg\x93q\u0209\x1c!(\x05\u00b4\xa5\x00\x00\xe0\x94FQ\xdcB\x0e\b\xc3);'\xd2Ix\x90\xebP\":\xe2\xf4\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4FS\x1e\x8b\x1b\xde\t\u007f\u07c4\x9dm\x11\x98\x85`\x8a\x00\x8d\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4Fb\x92\xf0\xe8\rC\xa7\x87t'u\x90\xa9\xebE\x96\x12\x14\xf4\x894\x95tD\xb8@\xe8\x00\x00\xe0\x94Fb\xa1v^\xe9!\x84-\u0708\x89\x8d\x1d\xc8bu\x97\xbd~\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Fe\xe4s\x96\xc7\u06d7\xeb*\x03\xd9\bc\xd5\u053a1\x9a\x94\x89 \x86\xac5\x10R`\x00\x00\u07d4Fo\xdak\x9bX\xc5S'P0j\x10\xa2\xa8\xc7h\x10;\a\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4Fq$\xae\u007fE/&\xb3\xd5t\xf6\b\x88\x94\xfa]\x1c\xfb;\x89\x92^\x06\xee\xc9r\xb0\x00\x00\u0794Fr*6\xa0\x1e\x84\x1d\x03\xf7\x80\x93^\x91}\x85\u0566z\xbd\x88\xce\xc7o\x0eqR\x00\x00\u07d4Fw\x9aVV\xff\x00\xd7>\xac:\xd0\u00cbl\x850\x94\xfb@\x89\f\x82S\xc9lj\xf0\x00\x00\u07d4Fw\xb0N\x03C\xa3!1\xfdj\xbb9\xb1\xb6\x15k\xba=[\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4F}Y\x88$\x9ahaG\x16e\x98@\xed\n\xe6\xf6\xf4W\xbc\x89\x15\x01\xa4\x8c\xef\xdf\xde\x00\x00\u07d4F~\x0e\xd5O;v\xae\x066\x17n\aB\b\x15\xa0!sn\x89lk\x93[\x8b\xbd@\x00\x00\u07d4F~\xa1\x04E\x82~\xf1\xe5\x02\xda\xf7k\x92\x8a \x9e\r@2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94F\u007f\xbfAD\x16\x00u\u007f\xe1X0\xc8\xcd_O\xfb\xbb\xd5`\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94F\x93Xp\x932\xc8+\x88~ \xbc\xdd\xd0\"\x0f\x8e\u06e7\u040a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4F\x97\xba\xaf\x9c\xcb`?\xd3\x040h\x9dCTE\xe9\u024b\xf5\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4F\xa3\v\x8a\x80\x891!tE\xc3\xf5\xa9>\x88,\x03E\xb4&\x89\r\x8d\xb5\xeb\u05f2c\x80\x00\u07d4F\xa40\xa2\u0528\x94\xa0\u062a?\xea\xc6\x156\x14\x15\xc3\xf8\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4F\xaaP\x18pg~\u007f\nPHv\xb4\xe8\x80\x1a\n\xd0\x1cF\x89+^:\xf1k\x18\x80\x00\x00\u07d4F\xbf\u0172\a\xeb \x13\xe2\xe6\x0fw_\xec\xd7\x18\x10\u0159\f\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4F\xc1\xaa\"D\xb9\u0229W\u028f\xacC\x1b\x05\x95\xa3\xb8h$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4F\xd8\x061(B\x03\xf6(\x8e\xcdNWX\xbb\x9dA\xd0]\xbe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\n\xc5\xd1\xf3\xef\xe2\x8f8\x02\xaf\x92[W\x1ec\x86\x8b9}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\x10\x10\xdaI/@\x18\x83;\b\x8d\x98r\x90\x1e\x06\x12\x91t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4G\x12T\x02e\xcb\xee\u00c4p\"\u015f\x1b1\x8dC@\n\x9e\x89\xbd\xbcA\xe04\x8b0\x00\x00\xe0\x94G\x14\u03e4\xf4k\u05bdps}u\x87\x81\x97\xe0\x8f\x88\xe61\x8a\x02\u007f>\u07f3Nn@\x00\x00\u07d4G H\xcc`\x9a\xeb$!e\uaa87\x05\x85\f\xf3\x12]\xe0\x8965\u026d\xc5\u07a0\x00\x00\u07d4G!\x92)\xe8\xcdVe\x9ae\u00a9C\xe2\u075a\x8fK\xfd\x89\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4G7\xd0B\xdcj\xe7>\xc7:\xe2Qz\u03a2\xfd\xd9d\x87\u014965\u026d\xc5\u07a0\x00\x00\u07d4GAX\xa1\xa9\xdci<\x13?e\xe4{\\:\xe2\xf7s\xa8o\x89\n\xdaUGK\x814\x00\x00\u07d4GE\xab\x18\x1a6\xaa\x8c\xbf\"\x89\xd0\xc4Qe\xbc~\xbe#\x81\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4GPf\xf9\xad&eQ\x96\xd5SS'\xbb\xeb\x9by)\xcb\x04\x89\xa4\xccy\x95c\u00c0\x00\x00\xe0\x94GR!\x8eT\xdeB?\x86\xc0P\x193\x91z\xea\b\xc8\xfe\u054a\x04<3\xc1\x93ud\x80\x00\x00\u07d4GZa\x93W-JNY\u05fe\t\u02d6\r\u074cS\x0e/\x89$,\xf7\x8c\xdf\a\xff\x80\x00\u07d4Gd\x8b\xed\x01\xf3\xcd2I\bNc]\x14\u06a9\xe7\xec<\x8a\x89\n\x84Jt$\xd9\xc8\x00\x00\u07d4Gh\x84\x10\xff%\xd6T\xd7.\xb2\xbc\x06\xe4\xad$\xf83\xb0\x94\x89\b\xb2\x8da\xf3\u04ec\x00\x00\u07d4GkU\x99\b\x9a?\xb6\xf2\x9clr\xe4\x9b.G@\ua00d\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4Gs\x0f_\x8e\xbf\x89\xacr\xef\x80\xe4l\x12\x19P8\xec\xdcI\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94G{$\xee\u80deO\u045d\x12P\xbd\vfEyJa\u028a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4G\x81\xa1\nM\xf5\uef02\xf4\xcf\xe1\a\xba\x1d\x8av@\xbdf\x89a\t=|,m8\x00\x00\u07d4G\x88Z\xba\xbe\xdfM\x92\x8e\x1c\x88\x83\xa6a\x9cl(\x11\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94G\xe2]\xf8\x82%8\xa8Yk(\xc67\x89kM\x14<5\x1d\x8a\x11\v\xe9\xeb$\xb8\x81P\x00\x00\u07d4G\xf4ik\xd4b\xb2\r\xa0\x9f\xb8>\xd2\x03\x98\x18\xd7v%\xb3\x89\b\x13\xcaV\x90m4\x00\x00\u07d4G\xfe\xf5\x85\x84FRH\xa0\x81\r`F>\xe9>Zn\xe8\u04c9\x0fX\xcd>\x12i\x16\x00\x00\u07d4G\xffo\xebC! `\xbb\x15\x03\u05e3\x97\xfc\b\xf4\xe7\x03R\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\xff\xf4,g\x85Q\xd1A\xebu\xa6\xee9\x81\x17\xdf>J\x8d\x89\x05k\xea\xe5\x1f\xd2\xd1\x00\x00\u07d4H\x01\x0e\xf3\xb8\xe9^?0\x8f0\xa8\xcb\u007fN\xb4\xbf`\xd9e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4H\n\xf5 v\x00\x9c\xa77\x81\xb7\x0eC\xb9Y\x16\xa6\"\x03\xab\x892\x19r\xf4\b=\x87\x80\x00\u07d4H\x0f1\xb9\x891\x1eA$\u01a7F_ZD\tM6\xf9\u04097\x90\xbb\x85Q7d\x00\x00\xe0\x94H\x11\x15)j\xb7\xdbRI/\xf7\xb6G\xd63)\xfb\\\xbck\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4H\x1e:\x91\xbf\xdc/\x1c\x84(\xa0\x11\x9d\x03\xa4\x16\x01A~\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4H(\xe4\xcb\xe3N\x15\x10\xaf\xb7,+\ueb0aE\x13\xea\xeb\u0649\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94H)\x82\xac\x1f\x1cm\x17!\xfe\xec\u0679\xc9l\xd9I\x80PU\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4H0,1\x1e\xf8\xe5\xdcfAX\xddX<\x81\x19Mn\rX\x89\xb6gl\xe0\xbc\xcb\\\x00\x00\u07d4H;\xa9\x904\xe9\x00\xe3\xae\xdfaI\x9d;+\xce9\xbe\xb7\xaa\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4HT\x8bK\xa6+\xcb/\r4\xa8\x8d\u019ah\x0eS\x9c\xf0F\x89\x05l\xf1\u02fbt2\x00\x00\u07d4Hc\x84\x979&Zc\xb0\xa2\xbf#jY\x13\xe6\xf9Y\xce\x15\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4He\x9d\x8f\x8c\x9a/\xd4Oh\u06a5]#\xa6\b\xfb\xe5\x00\u0709lk\x93[\x8b\xbd@\x00\x00\xe0\x94Hf\x9e\xb5\xa8\x01\u0637_\xb6\xaaX\xc3E\x1bpX\xc2C\xbf\x8a\x06\x8dB\xc18\u06b9\xf0\x00\x00\u07d4Hjl\x85\x83\xa8D\x84\xe3\xdfC\xa1#\x83\u007f\x8c~#\x17\u0409\x11\x87\xc5q\xab\x80E\x00\x00\u07d4Hz\xdf}p\xa6t\x0f\x8dQ\xcb\xddh\xbb?\x91\u0125\xceh\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4H~\x10\x85\x02\xb0\xb1\x89\uf70cm\xa4\xd0\xdbba\xee\xc6\xc0\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94H\x88\xfb%\xcdP\u06f9\xe0H\xf4\x1c\xa4}x\xb7\x8a'\xc7\u064a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u0794H\x934\u00b6\x95\xc8\xee\a\x94\xbd\x86B\x17\xfb\x9f\xd8\xf8\xb15\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4H\xa3\r\xe1\xc9\x19\xd3\xfd1\x80\xe9}_+*\x9d\xbd\x96M-\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4H\xbf\x14\u05f1\xfc\x84\xeb\xf3\xc9k\xe1/{\xce\x01\xaai\xb0>\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4H\xc2\ue465\aV\xd8\u039a\xbe\xebu\x89\xd2,o\xee]\xfb\x89\xae\x8ez\v\xb5u\xd0\x00\x00\u07d4H\xc5\u0197\v\x91a\xbb\x1c{z\xdf\xed\x9c\xde\u078a\x1b\xa8d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94H\xd2CKz}\xbb\xff\b\";c\x87\xb0]\xa2\xe5\t1&\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4H\xd4\xf2F\x8f\x96?\u05da\x00a\x98\xbbg\x89]-Z\xa4\u04c9K\xe4\xe7&{j\xe0\x00\x00\u07d4H\xe0\xcb\xd6\u007f\x18\xac\xdbzb\x91\xe1%M\xb3.\trs\u007f\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4H\xf6\n5HO\xe7y+\u030a{c\x93\xd0\u0761\xf6\xb7\x17\x89\xc3(\t>a\xee@\x00\x00\u07d4H\xf8\x83\xe5g\xb46\xa2{\xb5\xa3\x12M\xbc\x84\xde\xc7u\xa8\x00\x89)\xd7n\x86\x9d\u0340\x00\x00\xe0\x94I\x01E\xaf\xa8\xb5E\"\xbb!\xf3R\xf0m\xa5\xa7\x88\xfa\x8f\x1d\x8a\x01\xf4lb\x90\x1a\x03\xfb\x00\x00\u07d4I\t\xb3\x19\x98\xea\xd4\x14\xb8\xfb\x0e\x84k\xd5\xcb\xde995\xbe\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4I\x12\xd9\x02\x93\x16v\xff9\xfc4\xfe<<\xc8\xfb!\x82\xfaz\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4I\x13o\xe6\xe2\x8btS\xfc\xb1kk\xbb\u9aac\xba\x837\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94I\x15a\u06cbo\xaf\xb9\x00~b\xd0P\u0082\xe9,Kk\u020a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4I\x18]\xd7\xc262\xf4lu\x94s\ubb96`\b\xcd5\x98\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4I,\xb5\xf8a\xb1\x87\xf9\xdf!\xcdD\x85\xbe\xd9\vP\xff\xe2-\x89\x1b\x19\xe5\vD\x97|\x00\x00\u07d4I-\xe4j\xaf\x8f\x1dp\x8dY\u05da\xf1\xd0:\xd2\xcb`\x90/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I.p\xf0M\x18@\x8c\xb4\x1e%`70Pk5\xa2\x87k\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4I:g\xfe#\xde\xccc\xb1\r\xdau\xf3(v\x95\xa8\x1b\u056b\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4I=H\xbd\xa0\x15\xa9\xbf\xcf\x16\x03\x93n\xabh\x02L\xe5Q\xe0\x89\x018\xa3\x88\xa4<\x00\x00\x00\xe0\x94IBV\xe9\x9b\x0f\x9c\xd6\xe5\xeb\xca8\x99\x862R\x90\x01e\u020a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4IM\xecM^\xe8\x8a'q\xa8\x15\xf1\xeerd\x94/\xb5\x8b(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I[d\x1b\x1c\u07a3b\u00f4\u02fd\x0f\\\xc5\v\x1e\x17k\x9c\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Ih\xa2\xce\xdbEuU\xa19)Z\xea(wnT\x00<\x87\x8a\x02#\x1a\xef\u0266b\x8f\x00\x00\u07d4Im6U4S\n_\xc1W|\nRA\u02c8\xc4\xdapr\x89a\t=|,m8\x00\x00\xe0\x94In1\x95\x92\xb3A\xea\xcc\xd7x\u0767\xc8\x19mT\xca\xc7u\x8a\x01\xf5q\x89\x87fKH\x00\x00\u07d4IoXC\xf6\xd2L\u064d%^L#\xd1\xe1\xf0#\"uE\x89_\x17\x9f\u0526\xee\t\x80\x00\xe0\x94Ip\u04ec\xf7+[\x1f2\xa7\x00<\xf1\x02\xc6N\xe0TyA\x8a\x1d\xa5jK\b5\xbf\x80\x00\x00\u07d4Iw\xa7\x93\x9d\t9h\x94U\xce&9\xd0\xeeZL\xd9\x10\xed\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4Iy\x19N\xc9\xe9}\xb9\xbe\xe84;|w\xd9\xd7\xf3\xf1\u071f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Iy4c\xe1h\x10\x83\u05ab\xd6\xe7%\u057b\xa7E\xdc\xcd\xe8\x89\x1d\x98\xe9LNG\x1f\x00\x00\u07d4I\x81\xc5\xfff\xccN\x96\x80%\x1f\xc4\xcd/\xf9\a\xcb2xe\x89(\xa8WBTf\xf8\x00\x00\u07d4I\x89\u007f\xe92\xbb\xb3\x15L\x95\u04fc\xe6\xd9;ms)\x04\u0749\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4I\x89\xe1\xab^|\xd0\aF\xb3\x93\x8e\xf0\xf0\xd0d\xa2\x02[\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I\x8a\xbd\xeb\x14\xc2k{r4\xd7\x0f\u03ae\xf3a\xa7m\xffr\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4I\xa6E\xe0f}\xfd{2\xd0u\xcc$g\u074ch\t\a\u0109\a\x06\x01\x95\x8f\u02dc\x00\x00\xe0\x94I\xb7N\x16\x92e\xf0\x1a\x89\xecL\x90r\u0164\xcdr\xe4\xe85\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4I\xbd\xbc{\xa5\xab\xeb\xb68\x9e\x91\xa3(R \xd3E\x1b\xd2S\x8965\u026d\xc5\u07a0\x00\x00\u07d4I\xc9A\xe0\xe5\x01\x87&\xb7)\x0f\xc4s\xb4q\xd4\x1d\xae\x80\u0449\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94I\xc9w\x1f\xca\x19\u0579\xd2E\u0211\xf8\x15\x8f\xe4\x9fG\xa0b\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4I\xcf\x1eT\xbe61\x06\xb9 r\x9d-\v\xa4o\bg\x98\x9a\x89\x0e\x87?D\x13<\xb0\x00\x00\u07d4I\xd2\u008e\xe9\xbcT^\xaa\xf7\xfd\x14\xc2|@s\xb4\xbb_\x1a\x89O\xe9\xb8\x06\xb4\r\xaf\x00\x00\u07d4I\xdd\xee\x90.\x1d\f\x99\u0471\x1a\xf3\u030a\x96\xf7\x8eM\xcf\x1a\x89\n\u03a5\xe4\xc1\x8cS\x00\x00\u07d4I\xf0(9[Z\x86\xc9\xe0\u007fwxc\x0eL.=7:w\x89\x06\xa7JP8\u06d1\x80\x00\xe0\x94J\x19 5\xe2a\x9b$\xb0p\x9dVY\x0e\x91\x83\xcc\xf2\xc1\u064a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4J@S\xb3\x1d\x0e\xe5\u06ef\xb1\xd0k\u05ec\u007f\xf3\",G\u0589K\xe4\xe7&{j\xe0\x00\x00\u07d4JC\x01p\x15-\xe5\x17&3\u0742b\xd1\a\xa0\xaf\xd9j\x0f\x89\xabM\xcf9\x9a:`\x00\x00\u07d4JG\xfc>\x17\u007fVz\x1e8\x93\xe0\x00\xe3k\xba#R\n\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4JR\xba\xd2\x03W\"\x8f\xaa\x1e\x99k\xedy\f\x93gK\xa7\u0409Hz\x9a0E9D\x00\x00\u07d4JS\xdc\xdbV\xceL\xdc\xe9\xf8.\xc0\xeb\x13\xd6sR\xe7\u020b\x89\u3bb5sr@\xa0\x00\x00\u07d4J_\xae;\x03r\xc20\xc1%\xd6\xd4p\x14\x037\xab\x91VV\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4Jq\x90a\xf5(T\x95\xb3{\x9d~\xf8\xa5\x1b\a\xd6\u6b2c\x89\n\xd4\xc81j\v\f\x00\x00\u07d4Js8\x92\x98\x03\x1b\x88\x16\u0329FB\x1c\x19\x9e\x18\xb3C\u0589\"8h\xb8y\x14o\x00\x00\u07d4Js]\"G\x927m3\x13g\xc0\x93\xd3\x1c\x87\x944\x15\x82\x89f\xff\xcb\xfd^Z0\x00\x00\u07d4Jt\x94\xcc\xe4HU\u0300X(B\xbe\x95\x8a\r\x1c\x00r\ue242\x1a\xb0\xd4AI\x80\x00\x00\u07d4Ju\xc3\xd4\xfao\u033d]\u0567\x03\xc1Sy\xa1\xe7\x83\u9dc9b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94J\x81\xab\xe4\x98L|k\xefc\u0598 \xe5WC\xc6\x1f \x1c\x8a\x03d\x01\x00N\x9a\xa3G\x00\x00\u07d4J\x82iO\xa2\x9d\x9e!2\x02\xa1\xa2\t(]\xf6\xe7E\xc2\t\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4J\x83\\%\x82LG\xec\xbf\u01d49\xbf?\\4\x81\xaau\u0349K\xe4\xe7&{j\xe0\x00\x00\u07d4J\x91\x802C\x91Y\xbb1[g%\xb6\x83\r\xc86\x97s\x9f\x89\x12\xa3.\xf6x3L\x00\x00\u07d4J\x97\xe8\xfc\xf4c^\xa7\xfc^\x96\xeeQu.\u00c8qk`\x89\x1d\x99E\xab+\x03H\x00\x00\u07d4J\x9a&\xfd\n\x8b\xa1\x0f\x97}\xa4\xf7|1\x90\x8d\xabJ\x80\x16\x89a\t=|,m8\x00\x00\u07d4J\xa1H\xc2\xc34\x01\xe6j+Xnew\u0132\x92\xd3\xf2@\x89\v\xb8`\xb2\x85\xf7t\x00\x00\u07d4J\xa6\x93\xb1\"\xf3\x14H*G\xb1\x1c\xc7|h\xa4\x97\x87ab\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4J\xb2\xd3O\x04\x83O\xbftyd\x9c\xab\x92=,G%\xc5S\x89\xbe\xd1\xd0&=\x9f\x00\x00\x00\u07d4J\xc0vs\xe4/d\xc1\xa2^\xc2\xfa-\x86\xe5\xaa+4\xe09\x89lk\x93[\x8b\xbd@\x00\x00\u07d4J\u016c\xad\x00\v\x88w!L\xb1\xae\x00\xea\u0263}Y\xa0\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4J\u0250ZL\xb6\xab\x1c\xfdbTn\xe5\x91s\x00\xb8|O\u07897\b\xba\xed=h\x90\x00\x00\u07d4J\u03e9\xd9N\xdaf%\xc9\u07e5\xf9\xf4\xf5\xd1\a\xc4\x03\x1f\u07c9\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4J\xd0G\xfa\xe6~\xf1b\xfeh\xfe\xdb\xc2};e\xca\xf1\f6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4J\xd9]\x18\x8dddp\x9a\xdd%U\xfbM\x97\xfe\x1e\xbf1\x1f\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d4J\xdb\xf4\xaa\xe0\xe3\xefD\xf7\xddM\x89\x85\u03ef\tn\u010e\x98\x89\b!\xab\rD\x14\x98\x00\x00\u07d4J\xe2\xa0M9\t\xefENTL\xcf\xd6\x14\xbf\xef\xa7\x10\x89\xae\x89\x18\x01\x15\x9d\xf1\xee\xf8\x00\x00\xe0\x94J\xe90\x82\xe4Q\x87\xc2a`\xe6g\x92\xf5\u007f\xad5Q\xc7:\x8a\x04\x96\x15 \xda\xff\x82(\x00\x00\u07d4J\xf0\xdb\a{\xb9\xba^D>!\xe1H\xe5\x9f7\x91\x05\u0152\x89 \x86\xac5\x10R`\x00\x00\u07d4K\x06\x19\xd9\u062a1:\x951\xac}\xbe\x04\xca\rjZ\u0476\x89lk\x93[\x8b\xbd@\x00\x00\u07d4K\v\u062c\xfc\xbcS\xa6\x01\v@\xd4\u040d\xdd-\x9dib-\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4K\x19\xeb\f5K\xc199`\xeb\x06\x06;\x83\x92o\rg\xb2\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4K)C|\x97\xb4\xa8D\xbeq\u0323\xb6H\xd4\xca\x0f\u075b\xa4\x89\b$q\x984\u03ec\x00\x00\u07d4K1\xbfA\xab\xc7\\\x9a\xe2\u034f\u007f5\x16;n+tPT\x89\x14\xb5P\xa0\x13\xc78\x00\x00\u07d4K:|\u00e7\u05f0\x0e\xd5(\"!\xa6\x02Y\xf2[\xf6S\x8a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94K:\xab3^\xbb\xfa\xa8p\xccM`^}.t\xc6h6\x9f\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4K\xcd\xc1\x8a`\x00\x00\u07d4K`\xa3\xe2S\xbf8\xc8\xd5f \x10\xbb\x93\xa4s\xc9e\xc3\xe5\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4Kt\xf5\xe5\x8e.\xdfv\xda\xf7\x01Q\x96J\v\x8f\x1d\xe0f<\x89\x11\x90\xaeID\xba\x12\x00\x00\u07d4Kv!f\xdd\x11\x18\xe8Ci\xf8\x04\xc7_\x9c\xd6W\xbfs\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Ky.)h>\xb5\x86\u353b3Rl`\x01\xb3\x97\x99\x9e\x89 \x86\xac5\x10R`\x00\x00\u07d4K\x90N\x93K\xd0\u030b p_\x87\x9e\x90[\x93\xea\f\xcc0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94K\x92\x06\xbakT\x9a\x1a\u007f\x96\x9e\x1d]\xba\x86u9\xd1\xfag\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4K\x98N\xf2lWn\x81Z.\xae\xd2\xf5\x17\u007f\a\u06f1\xc4v\x89T\x91YV\xc4\t`\x00\x00\u07d4K\x9e\x06\x8f\xc4h\tv\xe6\x15\x04\x91)\x85\xfd\\\xe9K\xab\r\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4K\xa0\xd9\xe8\x96\x01w+IhG\xa2\xbbC@\x18g\x87\xd2e\x8965\u026d\xc5\u07a0\x00\x00\u07d4K\xa5:\xb5I\xe2\x01m\xfa\"<\x9e\u0563\x8f\xad\x91(\x8d\a\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94K\xa8\xe0\x11\u007f\xc0\xb6\xa3\xe5k$\xa3\xa5\x8f\xe6\xce\xf4B\xff\x98\x8a\x011\xbe\xb9%\xff\xd3 \x00\x00\u07d4K\xac\x84j\xf4\x16\x9f\x1d\x95C\x1b4\x1d\x88\x00\xb2!\x80\xaf\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4K\xb6\xd8k\x83\x14\xc2-\x8d7\xeaQm\x00\x19\xf1V\xaa\xe1-\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94K\xb9e\\\xfb*6\xea|cz{\x85\x9bJ1T\xe2n\xbe\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94K\xbc\xbf8\xb3\xc9\x01c\xa8K\x1c\u04a9;X\xb2\xa34\x8d\x87\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94K\xd6\xdd\f\xff#@\x0e\x170\xba{\x89E\x04W}\x14\xe7J\x8a+\xa0\xcc\xdd\xd0\xdfs\xb0\x00\x00\u07d4K\xe8b\x8a\x81T\x87N\x04\x8d\x80\xc1B\x18\x10\"\xb1\x80\xbc\xc1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4K\xe9\rA!)\u0564\xd0BCa\xd6d\x9dNG\xa6#\x16\x897\b\xba\xed=h\x90\x00\x00\xe0\x94K\xea(\x8e\xeaB\u0115^\xb9\xfa\xad*\x9f\xafG\x83\xcb\u076c\x8a\x06\x18\xbe\x16c\u012fI\x00\x00\u07d4K\xf4G\x97\x99\xef\x82\xee\xa2\tC7OV\xa1\xbfT\x00\x1e^\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4K\xf8\xbf\x1d5\xa211Wd\xfc\x80\x01\x80\x9a\x94\x92\x94\xfcI\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4K\xf8\xe2oL'\x90\xdae3\xa2\xac\x9a\xba\xc3\u019a\x19\x943\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0794L\n\xcaP\x8b<\xaf^\xe0(\xbcp}\xd1\xe8\x00\xb88\xf4S\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94L\v\x15\x15\xdf\xce\u05e1>\x13\xee\x12\xc0\xf5#\xaePO\x03+\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4L\x13\x98\f2\xdc\xf3\x92\vx\xa4\xa7\x903\x12\x90|\x1b\x12?\x89\x03A\x00\x15\xfa\xae\f\x00\x00\u07d4L\x15y\xaf3\x12\xe4\xf8\x8a\xe9\x95\xcc9W\xd2R\xce\v\xf0\xc8}[O\"4g.p\x89\x87\x86x2n\xac\x90\x00\x00\u07d4LB1y\x82i\x1d\x10\x89\x05k\xc7^-c\x10\x00\x00\u07d4LZ\xfe@\xf1\x8f\xfcH\u04e1\xae\xc4\x1f\u009d\xe1y\xf4\u0497\x89lk\x93[\x8b\xbd@\x00\x00\u07d4L[=\xc0\xe2\xb96\x0f\x91(\x9b\x1f\xe1<\xe1,\x0f\xbd\xa3\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Lfk\x86\xf1\xc5\ue324\x12\x85\xf5\xbd\xe4\xf7\x90R\b\x14\x06\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Lik\xe9\x9f:i\x04@\xc3CjY\xa7\xd7\xe97\u05ba\r\x89\xbb\x91%T\"c\x90\x00\x00\u07d4Lj$\x8f\xc9}p]\xefI\\\xa2\aY\x16\x9e\xf0\xd3dq\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4Lj\x9d\xc2\u02b1\n\xbb.|\x13p\x06\xf0\x8f\ucd77y\xe1\x89\x1b\r\x04 /G\xec\x00\x00\u07d4Lk\x93\xa3\xbe\xc1cIT\f\xbf\xca\xe9l\x96!\xd6dP\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Lu\x98\x13\xad\x13\x86\xbe\xd2\u007f\xfa\xe9\xe4\x81^60\u0323\x12\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Lv\f\xd9\xe1\x95\xeeO-k\xce%\x00\xff\x96\xda|C\ue44a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4Lv{e\xfd\x91\x16\x1fO\xbd\xccji\xe2\xf6\xadq\x1b\xb9\x18\x89'\b\x01\xd9F\xc9@\x00\x00\u07d4L~.+w\xad\f\xd6\xf4J\xcb(a\xf0\xfb\x8b(u\x0e\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4L\x85\xed6/$\xf6\xb9\xf0L\xdf\xcc\xd0\"\xaeSQG\u02f9\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4L\x93[\xb2Pw\x8b0\x9b==\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4L\xee\x90\x1bJ\u0231V\xc5\xe2\xf8\xa6\xf1\xbe\xf5r\xa7\xdc\xeb~\x8965\u026d\xc5\u07a0\x00\x00\u07d4L\xef\xbe#\x98\xe4}R\u73743L\x8bivu\U00053b89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4L\xf5S{\x85\x84/\x89\xcf\xee5\x9e\xaeP\x0f\xc4I\xd2\x11\x8f\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94M\bG\x1dh\x00z\xff*\xe2y\xbc^?\xe4\x15o\xbb\xe3\u078a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4M \x01\x10\x12@\b\xd5ov\x98\x12VB\f\x94jo\xf4\\\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4M$\xb7\xacG\xd2\xf2}\xe9\tt\xba=\xe5\xea\xd2\x03TK\u0349\x05k\xc7^-c\x10\x00\x00\u0794M)\xfcR:,\x16)S!!\u0699\x98\u9d6b\x9d\x1bE\x88\xdbD\xe0I\xbb,\x00\x00\u07d4M8\xd9\x0f\x83\xf4Q\\\x03\xccx2j\x15M5\x8b\u0602\xb7\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4ML\xf5\x80t)a^0\xcd\xfa\xce\x1eZ\xaeM\xad0U\xe6\x89 \x86\xac5\x10R`\x00\x00\u07d4MW\xe7\x16\x87l\f\x95\xef^\xae\xbd5\xc8\xf4\x1b\x06\x9bk\xfe\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Mg\U000ab159\xfe\xf5\xfcA9\x99\xaa\x01\xfd\u007f\xcep\xb4=\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Mn\x8f\xe1\t\xcc\xd2\x15\x8eM\xb1\x14\x13/\xe7_\xec\u023e[\x89\x01[5W\xf1\x93\u007f\x80\x00\xe0\x94Mq\xa6\xeb=\u007f2~\x184'\x8e(\v\x03\x9e\xdd\xd3\x1c/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4M|\xfa\xa8L\xb31\x06\x80\n\x8c\x80/\xb8\xaaF8\x96\u0159\x89a\t=|,m8\x00\x00\u07d4M\x80\x10\x93\xc1\x9c\xa9\xb8\xf3B\xe3<\xc9\xc7{\xbdL\x83\x12\u03c9\x12\xb3\xe7\xfb\x95\u0364\x80\x00\u07d4M\x82\x88\x94u/o%\x17]\xaf!w\tD\x87\x95Ko\x9f\x89O!+\xc2\u011c\x83\x80\x00\xe0\x94M\x82\xd7p\f\x12;\xb9\x19A\x9b\xba\xf0Fy\x9ck\x0e,f\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4M\x83m\x9d;\x0e,\xbdM\xe0PYo\xaaI\f\xff\xb6\r]\x89\x10CV\x1a\x88)0\x00\x00\u07d4M\x86\x97\xaf\x0f\xbf,\xa3n\x87h\xf4\xaf\"\x135phZ`\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4M\x92y\x96 )\xa8\xbdEc\x977\xe9\x8bQ\x1e\xff\aL!\x89Hz\x9a0E9D\x00\x00\u07d4M\x93io\xa2HY\xf5\u0493\x9a\xeb\xfaT\xb4\xb5\x1a\xe1\xdc\u0309\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4M\x9cw\xd0u\f^o\xbc$\u007f/\u05d2thl\xb3S\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4M\xa5\xed\u0188\xb0\xcbb\xe1@=\x17\x00\xd9\u0739\x9f\xfe?\u04c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94M\xa8\x03\ai\x84K\xc3A\x86\xb8\\\xd4\xc74\x88I\xffI\xe9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4M\xb1\xc4:\x0f\x83M}\x04x\xb8\x96\ag\xec\x1a\xc4L\x9a\xeb\x89/Q\x810V'7\x00\x00\u07d4M\xb2\x12\x84\xbc\xd4\xf7\x87\xa7Ue\x00\xd6\xd7\xd8\xf3f#\xcf5\x89i(7Ow\xa3c\x00\x00\u07d4M\xc3\xda\x13\xb2\xb4\xaf\xd4O]\r1\x89\xf4D\xd4\xdd\xf9\x1b\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4M\u013f^u\x89\xc4{(7\x8du\x03\u03d6H\x80a\u06fd\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4M\xc9\u057bK\x19\xce\u0354\xf1\x9e\xc2] \x0e\xa7/%\xd7\xed\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94M\xcd\x11\x81X\x18\xae)\xb8]\x016sI\xa8\xa7\xfb\x12\xd0k\x8a\x01\xacB\x86\x10\x01\x91\xf0\x00\x00\u07d4M\xcfb\xa3\xde?\x06\x1d\xb9\x14\x98\xfda\x06\x0f\x1fc\x98\xffs\x89lj\xccg\u05f1\xd4\x00\x00\u07d4M\xd11\xc7J\x06\x8a7\xc9\n\xde\xd4\xf3\t\xc2@\x9fdx\u04c9\x15\xaf9\u4ab2t\x00\x00\xe0\x94M\u0767Xk\"7\xb0S\xa7\xf3(\x9c\xf4`\xdcW\xd3z\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4M\xe3\xfe4\xa6\xfb\xf64\xc0Q\x99\u007fG\xcc\u007fHy\x1fX$\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4M\xf1@\xbaye\x85\xddT\x891[\xcaK\xbah\n\u06f8\x18\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4N\x02\ay\xb5\xdd\xd3\xdf\"\x8a\x00\xcbH\xc2\xfc\x97\x9d\xa6\xae8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4N\v\xd3$s\xc4\xc5\x1b\xf2VT\xde\xf6\x9fy|k)\xa22\x89V\xc9]\xe8\xe8\xca\x1d\x00\x00\u07d4N\"%\xa1\xbbY\xbc\x88\xa21ft\xd33\xb9\xb0\xaf\xcafU\x89\bg\x0e\x9e\xc6Y\x8c\x00\x00\u07d4N#\x10\x19\x1e\xad\x8d;\xc6H\x98s\xa5\xf0\xc2\xeck\x87\u1f8965\u026d\xc5\u07a0\x00\x00\u07d4N#-S\xb3\u6f8f\x89Sa\xd3\x1c4\xd4v+\x12\xc8.\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4N+\xfaJFo\x82g\x1b\x80\x0e\xeeBj\xd0\f\a\x1b\xa1p\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4N>\xda\u0506M\xabd\xca\xe4\xc5Azvw@S\xdcd2\x89 \b\xfbG\x8c\xbf\xa9\x80\x00\u07d4NC\x18\xf5\xe1>\x82JT\xed\xfe0\xa7\xedO&\xcd=\xa5\x04\x89lk\x93[\x8b\xbd@\x00\x00\u07d4N[w\xf9\x06aY\xe6\x15\x93?-\xdatw\xfaNG\xd6H\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94Nf\x00\x80b\x89EJ\u03630\xa2\xa3U`\x10\u07ec\xad\xe6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Ns\xcf#y\xf1$\x86\x0fs\xd6\xd9\x1b\xf5\x9a\xcc\\\xfc\x84[\x89\x02,\xa3X|\xf4\xeb\x00\x00\xe0\x94Nz\xa6~\x12\x18>\xf9\xd7F\x8e\xa2\x8a\xd29\xc2\xee\xf7\x1bv\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\xe0\x94N{TGM\x01\xfe\xfd8\x8d\xfc\xd5;\x9ff&$A\x8a\x05\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94N\x89.\x80\x81\xbf6\xe4\x88\xfd\xdb;&0\xf3\xf1\xe8\xda0\u048a\x02\x8a\xba0u$Q\xfc\x00\x00\xe0\x94N\x8amcH\x9c\xcc\x10\xa5\u007f\x88_\x96\xeb\x04\xec\xbbT`$\x8a\x03\xea\xe3\x13\x0e\u0316\x90\x00\x00\u07d4N\x8eG\xae;\x1e\xf5\f\x9dT\xa3\x8e\x14 \x8c\x1a\xbd6\x03\u0089y(\xdb\x12vf\f\x00\x00\u0794N\x90\u03312X\xac\xaa\x9fO\xeb\xc0\xa3B\x92\xf9Y\x91\xe20\x88\xdbD\xe0I\xbb,\x00\x00\u07d4N\xa5n\x11\x12d\x1c\x03\x8d\x05e\xa9\u0096\xc4c\xaf\xef\xc1~\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\xe0\x94N\xa7\x0f\x041?\xaee\xc3\xff\"J\x05\\=-\xab(\xdd\u07ca\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4N\xb1EKW8\x05\u022c\xa3~\xde\xc7\x14\x9aA\xf6\x12\x02\xf4\x89\x10CV\x1a\x88)0\x00\x00\u07d4N\xb8{\xa8x\x8e\xba\r\xf8~[\x9b\xd5\n\x8eE6\x80\x91\xc1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4N\xbcV)\xf9\xa6\xa6k,\xf36:\u0109\\\x03H\u8fc7\x8967\tlK\xcci\x00\x00\u07d4N\xc7h)^\xea\xba\xfcB\x95\x84\x15\xe2+\xe2\x16\xcd\xe7v\x18\x89\x03;\x1d\xbc9\xc5H\x00\x00\u07d4N\xcc\x19\x94\x8d\xd9\u0347\xb4\xc7 \x1a\xb4\x8eu\x8f(\xe7\xccv\x89\x1b\x1d\xaba\u04ead\x00\x00\u07d4N\xd1M\x81\xb6\v#\xfb%\x05M\x89%\u07e5s\u072eah\x89\x12nr\xa6\x9aP\xd0\x00\x00\xe0\x94N\xe1<\rA \vF\u045d\xee\\K\xce\xc7\x1d\x82\xbb\x8e8\x8a\x01\xab\xee\x13\u033e\ufbc0\x00\u07d4N\xea\xd4\n\xad\x8cs\xef\b\xfc\x84\xbc\n\x92\xc9\t/j6\xbf\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4N\xeb\xe8\f\xb6\xf3\xaeY\x04\xf6\xf4\xb2\x8d\x90\u007f\x90q\x89\xfc\xab\x89lj\xccg\u05f1\xd4\x00\x00\u07d4N\xeb\xf1 ]\f\xc2\f\xeel\u007f\x8f\xf3\x11_V\u050f\xba&\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4N\xf1\xc2\x14c:\xd9\xc0p;N#t\xa2\xe3>>B\x92\x91\x89Hz\x9a0E9D\x00\x00\u07d4N\xfc\xd9\u01df\xb43L\xa6${\n3\xbd\x9c\xc32\b\xe2r\x89Hz\x9a0E9D\x00\x00\xe0\x94O\x06$k\x8dK\u0496a\xf4>\x93v\"\x01\u0486\x93Z\xb1\x8a\x01\x059O\xfcF6\x11\x00\x00\u07d4O\x15+/\xb8e\x9dCwn\xbb\x1e\x81g:\xa8Ai\xbe\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4O\x17\u007f\x9dV\x95=\xedq\xa5a\x1f93\"\xc3\x02y\x89\\\x89\rU\uf422\xda\x18\x00\x00\u07d4O\x1a-\xa5JLm\xa1\x9d\x14$\x12\xe5n\x81WA\xdb#%\x89\x05k\xc7^-c\x10\x00\x00\u07d4O#\xb6\xb8\x17\xff\xa5\xc6d\xac\xda\u05db\xb7\xb7&\xd3\n\xf0\xf9\x89_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94O&i\f\x99+z1*\xb1.\x13\x85\xd9J\xcdX(\x8e{\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4O+G\xe2wZ\x1f\xa7\x17\x8d\xad\x92\x98Z[\xbeI;\xa6\u0589\n\u05ce\xbcZ\xc6 \x00\x00\u07d4O:HT\x91\x11E\xea\x01\xc6D\x04K\xdb.Z\x96\n\x98/\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4O?,g0i\xac\x97\xc2\x026\a\x15)\x81\xf5\xcd`c\xa0\x89 \x86\xac5\x10R`\x00\x00\xe0\x94OJ\x9b\xe1\f\xd5\xd3\xfb]\xe4\x8c\x17\xbe)o\x89V\x90d[\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4OR\xadap\xd2[*.\x85\x0e\xad\xbbRA?\xf20>\u007f\x89\xa4\xccy\x95c\u00c0\x00\x00\u07d4OX\x01\xb1\xeb0\xb7\x12\u0620WZ\x9aq\xff\x96]O4\xeb\x89\x10CV\x1a\x88)0\x00\x00\u07d4O]\xf5\xb9CW\u0794\x86\x04\xc5\x1bx\x93\xcd\xdf`v\xba\xad\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\u07d4Od\xa8^\x8e\x9a@I\x8c\fu\xfc\xeb\x037\xfbI\b>^\x8965\u026d\xc5\u07a0\x00\x00\u07d4Og9m%S\xf9\x98x_pN\a\xa69\x19}\u0454\x8d\x89\x10DrR\x1b\xa78\x00\x00\u07d4OmG7\u05e9@8$\x87&H\x86i|\xf7c\u007f\x80\x15\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4Os0\toy\xed&N\xe0\x12\u007f]0\xd2\xf7?!\xcb\u007f\x04\x89\x04\x82\xfe&\f\xbc\xa9\x00\x00\u07d4O\xeeP\xc5\xf9\x88 k\t\xa5sF\x9f\xb1\u0434.\xbbm\u0389l\xee\x06\u077e\x15\xec\x00\x00\u07d4O\xf6v\xe2\u007fh\x1a\x98-\x8f\xd9\xd2\x0ed\x8b=\xce\x05\xe9E\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4O\xf6\u007f\xb8\u007fn\xfb\xa9'\x990\u03fd\x1bz4L\u057a\x8bN\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94PFf\u03891\x17^\x11\xa5\xed\x11\xc1\u072a\x06\xe5\u007fNf\x8a\x02\u007f>\u07f3Nn@\x00\x00\u0794PXM\x92\x06\xa4l\xe1\\0\x11\x17\xee(\xf1\\0\xe6\x0eu\x88\xb9\xf6]\x00\xf6<\x00\x00\xe0\x94PZ3\xa1\x864\xddH\x00i)\x13N\x00\x00\u07d4P\u0286\xb5\xeb\x1d\x01\x87M\xf8\xe5\xf3IE\u051cl\x1a\xb8H\x8965\u026d\xc5\u07a0\x00\x00\u07d4P\u0357\xe97\x8b\\\xf1\x8f\x179c#l\x99Q\xeft8\xa5\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4P\u073c'\xbc\xad\x98@\x93\xa2\x12\xa9\xb4\x17\x8e\xab\xe9\x01ua\x89\a\xe3by\v\\\xa4\x00\x00\u07d4P\xe10#\xbd\x9c\xa9j\xd4\xc5?\xdf\xd4\x10\xcbk\x1fB\v\u07c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94P\xe1\xc8\xec\x98A[\xefD&\x18p\x87\x99C{\x86\xe6\xc2\x05\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4P\xf8\xfaK\xb9\xe2g|\x99\nN\xe8\xcep\xdd\x15#%\x1eO\x89\x01i=#\x16Ok\x00\x00\u07d4P\xfb6\xc2q\a\xee,\xa9\xa3#n'F\u0321\x9a\xcekI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4P\xfe\xf2\x96\x95U\x88\u02aet\xc6.\xc3*#\xa4T\xe0\x9a\xb8\x89A\x1d\xff\xab\xc5\a8\x00\x00\u07d4Q\x02\xa4\xa4 w\xe1\x1cX\xdfGs\u3b14F#\xa6m\x9f\x89lp\x15\xfdR\xed@\x80\x00\u07d4Q\x03\x93w\xee\xd0\xc5s\xf9\x86\xc5\xe8\xa9_\xb9\x9aY\xe93\x0f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Q\x03\xbc\t\x93>\x99!\xfdS\xdcSo\x11\xf0]\rG\x10}\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94Q\x04\xec\xc0\xe30\xdd\x1f\x81\xb5\x8a\xc9\u06f1\xa9\xfb\xf8\x8a<\x85\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4Q\r\x81Y\u0314Wh\xc7E\a\x90\xba\a>\xc0\xd9\xf8\x9e0\x89\x8a\xc7#\x04\x89\xe8\x00\x00\x00\u07d4Q\x0e\xdaV\x01I\x9a\r^\x1a\x00k\xff\xfd\x836r\xf2\xe2g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x12dF\xab=\x802U~\x8e\xbaeY}u\xfa\u0701\\\x89\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\xe0\x94Q\x18U}`\r\x05\xc2\xfc\xbf8\x06\xff\xbd\x93\xd0 %\xd70\x8a\x02g\u04ebd#\xf5\x80\x00\x00\u07d4Q\x1e\x0e\xfb\x04\xacN?\xf2\xe6U\x0eI\x82\x95\xbf\xcdV\xff\u0549$=M\x18\"\x9c\xa2\x00\x00\u07d4Q!\x16\x81{\xa9\xaa\xf8C\xd1P|e\xa5\xead\n{\x9e\xec\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4Q&F\ri,q\u026fo\x05WM\x93\x99\x83h\xa27\x99\x89\x02\u0465\x1c~\x00P\x00\x00\u07d4Q'\u007f\xe7\xc8\x1e\xeb\xd2R\xa0=\xf6\x9ak\x9f2n'\"\a\x89\x03@.y\u02b4L\x80\x00\u07d4Q)oPD'\r\x17pvF\x12\x9c\x86\xaa\xd1d^\xad\xc1\x89H|r\xb3\x10\xd4d\x80\x00\xe0\x94Q+\x91\xbb\xfa\xa9\xe5\x81\xefh?\xc9\r\x9d\xb2*\x8fI\xf4\x8b\x8aA\xa5\"8m\x9b\x95\xc0\x00\x00\u07d4Q5\xfb\x87W`\f\xf4tTbR\xf7M\xc0tm\x06&,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4QF2\xef\xbdd,\x04\xdel\xa3B1]@\u0750\xa2\u06e6\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4QKu\x12\u026e^\xa6<\xbf\x11q[c\xf2\x1e\x18\u0496\xc1\x89lj\xccg\u05f1\xd4\x00\x00\u07d4QS\xa0\xc3\u0211(\x81\xbf\x1c5\x01\xbfd\xb4VI\xe4\x82\"\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94QVQ\xd6\xdbO\xaf\x9e\xcd\x10:\x92\x1b\xbb\xbej\xe9p\xfd\u050a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94Q_0\xbc\x90\xcd\xf4W~\xe4}e\u05c5\xfb\xe2\xe87\u01bc\x8a\x02'\x1b^\x01\x8b\xa0X\x00\x00\u07d4Q`\xeda.\x1bH\xe7??\xc1[\xc42\x1b\x8f#\xb8\xa2K\x89\x1e\x82kB(e\xd8\x00\x00\u07d4Qa\xfdI\xe8G\xf6tU\xf1\u023bz\xbb6\xe9\x85&\r\x03\x89A\rXj \xa4\xc0\x00\x00\u07d4QiT\x02_\xca&\b\xf4}\xa8\x1c!^\xed\xfd\x84J\t\xff\x89\x14\xb5P\xa0\x13\xc78\x00\x00\u07d4Qi\xc6\n\xeeL\xee\u0444\x9a\xb3mfL\xff\x97\x06\x1e\x8e\xa8\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4Q|uC\r\xe4\x01\xc3A\x03&\x86\x11'\x90\xf4mM6\x9e\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4Q|\xd7`\x8e]\r\x83\xa2kq\u007f6\x03\xda\xc2'}\u00e4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x86]\xb1H\x88\x19Q\xf5\x12Qq\x0e\x82\xb9\xbe\r~\xad\xb2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x89\x1b,\xcd\xd2\xf5\xa4K*\x8b\u011a]\x9b\xcadw%\x1c\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4Q\x8c\xef'\xb1\x05\x82\xb6\xd1OiH=\u06a0\xdd<\x87\xbb\\\x89 \x86\xac5\x10R`\x00\x00\u07d4Q\xa6\xd6'\xf6j\x89#\u060d`\x94\xc4qS\x80\xd3\x05|\xb6\x89>s\xd2z5\x94\x1e\x00\x00\u07d4Q\xa8\xc2\x166\x02\xa3.\xe2L\xf4\xaa\x97\xfd\x9e\xa4\x14QiA\x89\x03h\xf7\xe6\xb8g,\x00\x00\u07d4Q\xb4u\x8e\x9e\x14P\xe7\xafBh\xc3\u01f1\xe7\xbdo\\uP\x8965\u026d\xc5\u07a0\x00\x00\u07d4Q\u028b\xd4\xdcdO\xacG\xafgUc\u0540J\r\xa2\x1e\xeb\x89*\xb7\xb2`\xff?\xd0\x00\x00\u07d4Q\xd2K\xc3so\x88\xddc\xb7\" &\x88f0\xb6\ub1cd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\u05cb\x17\x8dp~9n\x87\x10\x96\\OA\xb1\xa1\xd9\x17\x9d\x89\x05\xfe\xe2\"\x04\x1e4\x00\x00\u07d4Q\xe3/\x14\xf4\xca^(|\xda\xc0W\xa7y^\xa9\xe0C\x99S\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Q\xe4?\xe0\xd2\\x(`\xaf\x81\xea\x89\xddy<\x13\xf0\u02f1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4Q\xe7\xb5\\/\x98 \xee\xd78\x846\x1bPf\xa5\x9boE\u0189lk\x93[\x8b\xbd@\x00\x00\xe0\x94Q\xea\x1c\t4\xe3\xd0@\"\ud715\xa0\x87\xa1P\xefp^\x81\x8a\x01Tp\x81\xe7\"M \x00\x00\u07d4Q\xee\f\xca;\xcb\x10\xcd>\x987\"\xce\xd8I=\x92l\bf\x8965f3\xeb\xd8\xea\x00\x00\xe0\x94Q\xf4f:\xb4O\xf7\x93E\xf4'\xa0\xf6\xf8\xa6\u0225?\xf24\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Q\xf5^\xf4~dV\xa4\x18\xab2\xb9\"\x1e\xd2}\xbaf\b\xee\x89\u3bb5sr@\xa0\x00\x00\xe0\x94Q\xf9\xc42\xa4\xe5\x9a\xc8b\x82\u05ad\xabL.\xb8\x91\x91`\xeb\x8ap;[\x89\u00e6\xe7@\x00\x00\u07d4R\x0ff\xa0\xe2e\u007f\xf0\xacA\x95\xf2\xf0d\xcf/\xa4\xb2BP\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4R\x10#T\xa6\xac\xa9]\x8a.\x86\xd5\u07bd\xa6\xdei4`v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\x13\xf4Y\xe0x\xad:\xb9Z\t #\x9f\xcf\x163\xdc\x04\u0289\x8c\xf2\x18|*\xfb\x18\x80\x00\u07d4R\x15\x18;\x8f\x80\xa9\xbc\x03\xd2l\xe9\x12\a\x83*\r9\xe6 \x8965\u026d\xc5\u07a0\x00\x00\xe0\x94R!Cx\xb5@\x04\x05j|\xc0\x8c\x89\x13'y\x8a\u01b2H\x8a\x037\xfe_\xea\xf2\u0440\x00\x00\xe0\x94R##\xaa\xd7\x1d\xbc\x96\xd8Z\xf9\x0f\bK\x99\xc3\xf0\x9d\ucdca\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4R>\x14\r\xc8\x11\xb1\x86\xde\xe5\xd6\u020b\xf6\x8e\x90\xb8\xe0\x96\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R?mdi\x0f\xda\u0354(SY\x1b\xb0\xff \xd3em\x95\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4RO\xb2\x10R,^#\xbbg\u07ff\x8c&\xaaam\xa4\x99U\x8965b\xa6m4#\x80\x00\u07d4RU\xdci\x15ZE\xb9p\xc6\x04\xd3\x00G\xe2\xf50i\x0e\u007f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4R`\xdcQ\xee\a\xbd\u06ab\xab\xb9\xeetK9<\u007fG\x93\xa6\x89\x01\xd8f_\xa5\xfaL\x00\x00\u07d4Rg\xf4\xd4\x12\x92\xf3p\x86<\x90\u05d3)i\x03\x846%\u01c9K\xe4\xe7&{j\xe0\x00\x00\u07d4Rk\xb53\xb7n \xc8\xee\x1e\xbf\x12?\x1e\x9f\xf4\x14\x8e@\xbe\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4Rl\xb0\x9c\u3b63g.\xec\x1d\xebF [\xe8\x9aKV>\x89\x85\xcaa[\xf9\xc0\x10\x00\x00\u07d4Rs\x8c\x90\xd8`\xe0L\xb1/I\x8d\x96\xfd\xb5\xbf6\xfc4\x0e\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4Rz\x8c\xa1&\x863\xa6\xc99\xc5\xde\x1b\x92\x9a\ue4ae\xac\x8d\x890\xca\x02O\x98{\x90\x00\x00\u07d4R\x81\x01\xceF\xb7 \xa2!M\u036ef\x18\xa51w\xff\xa3w\x89\x1b\x96\x12\xb9\xdc\x01\xae\x00\x00\xe0\x94R\x81s4s\xe0\r\x87\xf1\x1e\x99U\u5275\x9fJ\u008ez\x8a\x8b\xd6/\xf4\xee\xc5Y \x00\x00\u07d4R\x98\xab\x18*\x195\x9f\xfc\xec\xaf\xd7\u0475\xfa!-\xed\xe6\u0749\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4R\x9a\xa0\x02\u0196*:\x85E\x02\u007f\u0630_\"\xb5\xbf\x95d\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4R\x9e\x82O\xa0rX+@2h:\xc7\xee\xcc\x1c\x04\xb4\xca\xc1\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94R\xa5\xe4\xdeC\x93\xee\xcc\xf0X\x1a\xc1\x1bR\u0183\xc7n\xa1]\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4R\xb4%|\xf4\x1bn(\x87\x8dP\xd5{\x99\x91O\xfa\x89\x87:\x89\xd5\r\u026a,Aw\x00\x00\u07d4R\xb8\xa9Y&4\xf70\v|\\Y\xa34[\x83_\x01\xb9\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\xbd\u066fYx\x85\v\xc2A\x10q\x8b7#u\x9bC~Y\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4R\xcd @;\xa7\xed\xa6\xbc0z=c\xb5\x91\x1b\x81|\x12c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794R\u04c0Q\x1d\xf1\x9d^\u0080{\xbc\xb6vX\x1bg\xfd7\xa3\x88\xb9\xf6]\x00\xf6<\x00\x00\xe0\x94R\xe1s\x13P\xf9\x83\xcc,A\x89\x84/\xde\x06\x13\xfa\xd5\f\xe1\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\u07d4R\xe4g\x832\x9av\x93\x01\xb1u\x00\x9d4gh\xf4\xc8~\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\xf0X\xd4aG\xe9\x00m)\xbf,\t0J\xd1\xcd\xddn\x15\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4R\xf1T#2<$\xf1\x9a\xe2\xabg7\x17\"\x9d?t}\x9b\x897\xa04\xcb\xe8\xe3\xf3\x80\x00\u07d4R\xf8\xb5\t\xfe\xe1\xa8t\xabo\x9d\x876\u007f\xbe\xaf\x15\xac\x13\u007f\x8965\u026d\xc5\u07a0\x00\x00\u07d4R\xfbF\xac]\x00\xc3Q\x8b,:\x1c\x17}D/\x81eU_\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4S\x00w\xc9\xf7\xb9\a\xff\x9c\xec\fw\xa4\x1ap\xe9\x02\x9a\xddJ\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\x03\x19\xdb\n\x8f\x93\xe5\xbb}M\xbfH\x161O\xbe\xd86\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\x04}\u022c\x90\x83\xd9\x06r\xe8\xb3G<\x10\f\xcd'\x83#\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4S\va\xe4/9Bm$\b\xd4\bR\xb9\xe3J\xb5\xeb\xeb\u0149\x0e~\xeb\xa3A\vt\x00\x00\u07d4S\x0f\xfa\u00fc4\x12\xe2\xec\x0e\xa4{y\x81\xc7p\xf5\xbb/5\x89\a?u\u0460\x85\xba\x00\x00\u07d4S\x17\xec\xb0#\x05,\xa7\xf5e+\xe2\xfa\x85L\xfeEc\xdfM\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4S\x19M\x8a\xfa>\x885\x02v~\xdb\xc3\x05\x86\xaf3\xb1\x14\u04c9lk\x93[\x8b\xbd@\x00\x00\u07d4S*}\xa0\xa5\xadt\aF\x8d;\xe8\xe0~i\xc7\xddd\xe8a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4S-2\xb0\x0f0[\xcc$\xdc\xefV\x81}b/4\xfb,$\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4S4DX@\x82\xeb\xa6T\xe1\xad0\xe1Is\\o{\xa9\"\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4S8\xefp\xea\xc9\u075a\xf5\xa0P;^\xfa\xd1\x03\x9eg\xe7%\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94S9oJ&\u00b4`D\x960lTB\xe7\xfc\xba'.6\x8a\x04?/\b\xd4\x0eZ\xfc\x00\x00\xe0\x94S:s\xa4\xa2\"\x8e\xee\x05\xc4\xff\xd7\x18\xbb\xf3\xf9\xc1\xb1)\xa7\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4S<\x06\x92\x8f\x19\u0429V\xcc(\x86k\xf6\xc8\xd8\xf4\x19\x1a\x94\x89\x0f\xd8\xc1C8\xe60\x00\x00\u07d4S@e6\x1c\xb8T\xfa\xc4+\xfb\\\x9f\xcd\xe0`J\xc9\x19\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4SC\u007f\xec\xf3J\xb9\xd45\xf4\u07b8\xca\x18\x15\x19\xe2Y 5\x89\n1\x06+\xee\xedp\x00\x00\u07d4SR\x01\xa0\xa1\xd74\"\x80\x1fU\xde\xd4\u07ee\xe4\xfb\xaan;\x89\x02&!\x1fy\x15B\x80\x00\xe0\x94S`\x81\x05\xceK\x9e\x11\xf8k\xf4\x97\xff\xca;x\x96{_\x96\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4SnM\x80)\xb7?Uy\u0723>p\xb2N\xba\x89\xe1\x1d~\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Sp\rS%MC\x0f\"x\x1aJv\xa4c\x93;]k\b\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94S\u007f\x9dM1\xefp\x83\x9d\x84\xb0\xd9\u0377+\x9a\xfe\xdb\xdf5\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\xe0\x94S\x81D\x85\x03\xc0\xc7\x02T+\x1d\xe7\xcc_\xb5\xf6\xab\x1c\xf6\xa5\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94S\x94.yI\xd6x\x8b\xb7\x80\xa7\xe8\xa0y'\x81\xb1aK\x84\x8a\x03]\xebFhO\x10\xc8\x00\x00\u07d4S\x95\xa4E]\x95\xd1x\xb4S*\xa4r[\x19?\xfeQ)a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94S\x98\x9e\xd30V?\xd5}\xfe\u027d4<7`\xb0y\x93\x90\x8a\x01P\x89N\x84\x9b9\x00\x00\x00\u07d4S\xa2Dg(\x95H\x0fJ+\x1c\xdf}\xa5\xe5\xa2B\xecM\xbc\x8965\u026d\xc5\u07a0\x00\x00\u07d4S\xa7\x14\xf9\x9f\xa0\x0f\xefu\x8e#\xa2\xe7F2m\xad$|\xa7\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4S\xaf2\xc2/\uf640?\x17\x8c\xf9\v\x80/\xb5q\xc6\x1c\xb9\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4S\xc0\xbb\u007f\u020e\xa4\"\xd2\xef~T\x0e-\x8f(\xb1\xbb\x81\x83\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94S\xc5\xfe\x01\x19\xe1\xe8Hd\f\xee0\xad\ua594\x0f*]\x8b\x8a\x04\x9a\xda_\xa8\xc1\f\x88\x00\x00\u07d4S\xc9\xec\xa4\ts\xf6;\xb5\x92{\xe0\xbcj\x8a\x8b\xe1\x95\x1ft\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\u0388\xe6lZ\xf2\U0009bf4fY*V\xa3\xd1_ l2\x89\a\xa2\x8c1\xcc6\x04\x00\x00\u07d4S\xce\xc6\u0200\x92\xf7V\xef\xe5o}\xb1\x12(\xa2\xdbE\xb1\"\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4S\xe3[\x12#\x1f\x19\xc3\xfdwL\x88\xfe\xc8\xcb\xee\xdf\x14\b\xb2\x89\x1b\xc1mgN\xc8\x00\x00\x00\u07d4S\xe4\xd9im\xcb?M{?p\u072aN\xec\xb7\x17\x82\xff\\\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4S\xfa\xf1e\xbe\x03\x1e\xc1\x830\xd9\xfc\xe5\xbd\x12\x81\xa1\xaf\b\u06c9\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4T\n\x18\x19\xbd|5\x86\x1ey\x18\x04\xe5\xfb\xb3\xbc\x97\u026b\xb1\x89N\xd7\xda\xc6B0 \x00\x00\xe0\x94T\f\a(\x02\x01N\xf0\xd5a4Z\xecH\x1e\x8e\x11\xcb5p\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94T\f\xf2=\xd9\\MU\x8a'\x9dw\x8d+75\xb3\x16A\x91\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4T\x10`\xfcX\xc7P\xc4\x05\x12\xf83i\xc0\xa63@\xc1\"\xb6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4T\x13\xc9\u007f\xfaJn*{\xba\x89a\u071f\u03850\xa7\x87\u05c965\u026d\xc5\u07a0\x00\x00\u07d4T\x1d\xb2\n\x80\xcf;\x17\xf1b\x1f\x1b?\xf7\x9b\x88/P\xde\xf3\x8965\u026d\xc5\u07a0\x00\x00\u07d4T.\x80\x96\xba\xfb\x88\x16&\x06\x00.\x8c\x8a>\u0458\x14\xae\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T1\v:\xa8\x87\x03\xa7%\u07e5}\xe6\xe6F\x93Qd\x80,\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4T1\xb1\u0447Q\xb9\x8f\xc9\u220a\xc7u\x9f\x155\xa2\xdbG\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T1\xcaB~ae\xa6D\xba\xe3&\xbd\tu\n\x17\x8ce\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T5\xc6\xc1y3\x17\xd3,\xe1;\xbaLO\xfe\xb9s\xb7\x8a\u0709\r\x8ek\x1c\x12\x85\xef\x00\x00\xe0\x94T6)\xc9\\\xde\xf4(\xad7\xd4S\u02958\xa9\xf9\t\x00\xac\x8a\t(\x96R\x9b\xad\u0708\x00\x00\u07d4T9\x1bM\x17mGl\xea\x16N_\xb55\u0197\x00\xcb%5\x89\x05l\xd5_\xc6M\xfe\x00\x00\xe0\x94T:\x8c\x0e\xfb\x8b\xcd\x15\xc5C\u29a4\xf8\aYv1\xad\xef\x8a\x01?\x80\xe7\xe1O-D\x00\x00\u07d4T?\x8cgN$b\xd8\xd5\u06a0\xe8\x01\x95\xa8p\x8e\x11\xa2\x9e\x89\x03wX\x83;:z\x00\x00\xe0\x94TK[5\x1d\x1b\xc8.\x92\x97C\x99H\xcfHa\xda\u026e\x11\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4TM\xdaB\x1d\xc1\xebs\xbb$\xe3\xe5j$\x80\x13\xb8|\x0fD\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4TW\\1\x14u\x1e\x14o\xfe\u00c7nE\xf2\x0e\xe8AJ\u07ba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4T\xb4B\x9b\x18/\x03w\xbe~bi9\xc5\xdbd@\xf7]z\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4T\xbc\xb8\xe7\xf7<\xda=s\xf4\u04cb-\bG\xe6\x00\xba\r\xf8\x89:pAX\x82\xdf\x18\x00\x00\u07d4T\xc9>\x03\xa9\xb2\xe8\xe4\xc3g(5\xa9\xeev\xf9a[\xc1N\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4T\u0388'YV\xde\xf5\xf9E\x8e;\x95\xde\xca\xcdH@!\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T\xdb^\x06\xb4\x81]1\xcbV\xa8q\x9b\xa3:\xf2\xd7>rR\x89$R\x1e*0\x17\xb8\x00\x00\xe0\x94T\xe0\x12\x83\u030b8E8\xdddgp\xb3W\xc9`\xd6\xca\u034a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4T\xecs\x00\xb8\x1a\xc8C3\xed\x1b\x03<\xd5\u05e39r\xe24\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4T\xfe\xbc\xce \xfez\x90\x98\xa7U\xbd\x90\x98\x86\x02\xa4\x8c\b\x9e\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4U\n\xad\xae\x12!\xb0z\xfe\xa3\x9f\xba.\xd6.\x05\u5df5\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4U\f0o\x81\xef]\x95\x80\xc0l\xb1\xab \x1b\x95\xc7H\xa6\x91\x89$\x17\xd4\xc4p\xbf\x14\x00\x00\xe0\x94U\x19\x99\xdd\xd2\x05V3'\xb9\xb50xZ\xcf\xf9\xbcs\xa4\xba\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4U\x1ew\x84w\x8e\xf8\xe0H\xe4\x95\xdfI\xf2aO\x84\xa4\xf1\u0709 \x86\xac5\x10R`\x00\x00\xe0\x94U)\x83\na\xc1\xf1<\x19~U\v\xed\xdf\u05bd\x19\\\x9d\x02\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4U)\x87\xf0e\x1b\x91[.\x1eS(\xc1!\x96\rK\xddj\xf4\x89a\t=|,m8\x00\x00\u07d4U;k\x1cW\x05\x0e\x88\xcf\f1\x06{\x8dL\xd1\xff\x80\xcb\t\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4U?7\xd9$fU\x0e\x9f\xd7u\xaet6-\xf00\x17\x912\x89lk\x93[\x8b\xbd@\x00\x00\u07d4UC6\xeeN\xa1U\xf9\xf2O\x87\xbc\xa9\xcar\xe2S\xe1,\u0489\x05k\xc7^-c\x10\x00\x00\u0794UC\xddm\x16\x9e\xec\x8a!;\xbfz\x8a\xf9\xff\xd1]O\xf7Y\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4UG\xfd\xb4\xae\x11\x95>\x01)+x\a\xfa\x92#\xd0\xe4`j\x89\x05]\x11}\xcb\x1d&\x00\x00\u07d4UR\xf4\xb3\xed>\x1d\xa7\x9a/x\xbb\x13\xe8\xaeZh\xa9\xdf;\x8965\u026d\xc5\u07a0\x00\x00\u07d4U\\\xa9\xf0\\\xc14\xabT\xae\x9b\xea\x1c?\xf8z\xa8Q\x98\u0289\x05k\xc7^-c\x10\x00\x00\xe0\x94U]\x8d<\xe1y\x8a\u0290'T\xf1d\xb8\xbe*\x022\x9cl\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4U]\xf1\x93\x90\xc1m\x01)\x87r\xba\xe8\xbc:\x11R\x19\x9c\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4U^\xbe\x84\u06a4+\xa2V\xeax\x91\x05\xce\u0136\x93\xf1/\x18\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94U\u007f^e\xe0\xda3\x99\x82\x19\xadN\x99W\x05E\xb2\xa9\xd5\x11\x8a\x02U\x9c\xbb\x98XB@\x00\x00\u07d4U\x83` h\x83\xdd\x1bmJYc\x9eV)\xd0\xf0\xc6u\u0409lk\x93[\x8b\xbd@\x00\x00\u07d4U\x84B0P\xe3\xc2\x05\x1f\v\xbd\x8fD\xbdm\xbc'\xec\xb6,\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4U\x85)CI)p\xf8\xd6)\xa1Sf\xcd\xda\x06\xa9OE\x13\x89lk\x93[\x8b\xbd@\x00\x00\u0794U\x86d\x86\xec\x16\x8fy\xdb\xe0\u1af1\x88d\u0649\x91\xae,\x88\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4U\x8cTd\x9a\x8an\x94r+\xd6\xd2\x1d\x14qOqx\x054\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\x91\x940O\x14\xb1\xb9:\xfeDO\x06$\xe0S\xc2:\x00\t\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4U\x93\xc9\u0536ds\x0f\xd9<\xa6\x01Q\xc2\\.\xae\xd9<;\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4U\x97\x06\xc32\xd2\ay\xc4_\x8am\x04ji\x91Y\xb7I!\x89\x14\x9bD.\x85\xa3\u03c0\x00\u07d4U\x98\xb3\xa7\x9aH\xf3+\x1f_\xc9\x15\xb8{d]\x80]\x1a\xfe\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4U\xa3\xdfW\xb7\xaa\xec\x16\xa1b\xfdS\x16\xf3[\xec\b(!\u03c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4U\xa4\xca\xc0\u02cbX-\x9f\xef8\xc5\xc9\xff\xf9\xbdS\t=\x1f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4U\xa6\x1b\x10\x94\x80\xb5\xb2\xc4\xfc\xfd\xef\x92\xd9\x05\x84\x16\f\r5\x89\x02lVM+S\xf6\x00\x00\u07d4U\xaa]1>\xbb\bM\xa0\xe7\x80\x10\x91\u2792\xc5\xde\u00ea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\xab\x99\xb0\xe0\xe5]{\xb8t\xb7\xcf\xe84\xdec\x1c\x97\xec#\x897\xe9\x8c\xe3h\x99\xe4\x00\x00\u07d4U\xaf\t/\x94\xbajy\x91\x8b\f\xf99\xea\xb3\xf0\x1b?Q\u01c9\b \xd5\xe3\x95v\x12\x00\x00\u07d4U\xc5dfAf\xa1\xed\xf3\x91>\x01i\xf1\xcdE\x1f\xdb]\f\x89\x82\x17\xeaIP\x8el\x00\x00\xe0\x94U\xcaj\xbey\xea$\x97\xf4o\u06f804`\x10\xfeF\x9c\xbe\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4U\xca\xffK\xba\x04\xd2 \u0265\xd2\x01\x86r\xec\x85\xe3\x1e\xf8>\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\xd0W\xbc\xc0K\xd0\xf4\xaf\x96BQ:\xa5\t\v\xb3\xff\x93\xfe\x89;\xfeE,\x8e\xddL\x00\x00\u07d4U\xd4.\xb4\x95\xbfF\xa64\x99{_.\xa3b\x81I\x18\u2c09\x05\xc0\xd2e\xb5\xb2\xa8\x00\x00\u07d4U\u069d\xcd\xcaa\xcb\xfe\x1f\x13<{\xce\xfc\x86{\x9c\x81\"\xf9\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4U\xe2 \x87bb\xc2\x18\xafOVxG\x98\xc7\xe5]\xa0\x9e\x91\x89\a=\x99\xc1VE\xd3\x00\x00\u07d4U\xfd\b\u0440d\xbd ,\x0e\xc3\xd2\xcc\xe0\xce\v\x9d\x16\x9cM\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4V\x00s\nU\xf6\xb2\x0e\xbd$\x81\x1f\xaa=\xe9m\x16b\xab\xab\x89e\xea=\xb7UF`\x00\x00\u07d4V\x03$\x1e\xb8\xf0\x8fr\x1e4\x8c\x9d\x9a\xd9/H\u342a$\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4V\x056yJ\x9e+\x00I\xd1\x023\xc4\x1a\xdc_A\x8a&J\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\aY\x00Y\xa9\xfe\xc1\x88\x11I\xa4K6\x94\x9a\xef\x85\xd5`\x89lk\x93[\x8b\xbd@\x00\x00\u07d4V\v\xec\xdfR\xb7\x1f=\x88'\xd9'a\x0f\x1a\x98\x0f3qo\x89\x17GMp_V\u0400\x00\xe0\x94V\r\xa3~\x95m\x86/\x81\xa7_\u0540\xa7\x13\\\x1b$cR\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94V\x0f\xc0\x8d\a\x9f\x04~\xd8\xd7\xdfuU\x1a\xa55\x01\xf5p\x13\x8a\x01\x9b\xff/\xf5yh\xc0\x00\x00\u07d4V\x1b\xe9)\x9b>k>c\xb7\x9b\t\x16\x9d\x1a\x94\x8a\xe6\xdb\x01\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94V \xe3\xedy-/\x185\xfe_UA}Q\x11F\fj\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4V \xf4m\x14Q\xc25=bC\xa5\u0534'\x13\v\xe2\xd4\a\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94V!\x05\xe8+\t\x975\xdeI\xf6&\x92\u0307\xcd8\xa8\xed\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94V*\x8d\u02fe\xee\xf7\xb3`h]'0;\u059e\tJ\xcc\xf6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4V+\xce\u04ca\xb2\xabl\b\x0f;\x05A\xb8Enp\x82K?\x89\"\xca5\x87\xcfN\xb0\x00\x00\xe0\x94V+\xe9Z\xba\x17\xc57\x1f\u2e82\x87\x99\xb1\xf5]!w\u058a\b\x16\xd3~\x87\xb9\xd1\xe0\x00\x00\u07d4V/\x16\u05da\xbf\xce\u00d4>4\xb2\x0f\x05\xf9{\xdf\u0366\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4V7=\xaa\xb4c\x16\xfd~\x15v\xc6\x1ej\xff\xcbeY\xdd\u05c9\v\xacq]\x14l\x9e\x00\x00\u07d4V9v8\xbb<\xeb\xf1\xf6 byK^\xb9B\xf9\x16\x17\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4V:\x03\xab\x9cV\xb6\x00\xf6\xd2[f\f!\xe1c5Qzu\x8965\u026d\xc5\u07a0\x00\x00\u07d4V<\xb8\x80<\x1d2\xa2['\xb6A\x14\x85+\xd0M\x9c \u0349\v\x14\x9e\xad\n\xd9\xd8\x00\x00\u07d4VXc\x91\x04\fW\xee\xc6\xf5\xaf\xfd\x8c\u052b\xde\x10\xb5\n\u0309\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Vl\x10\xd68\u8e0bG\xd6\xe6\xa4\x14Iz\xfd\xd0\x06\x00\u0509\x05k9Bc\xa4\f\x00\x00\u07d4Vl(\xe3L8\b\xd9vo\xe8B\x1e\xbfO+\x1cO}w\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4V\x8d\xf3\x18Vi\x9b\xb5\xac\xfc\x1f\xe1\u0580\u07d9`\xcaCY\x89J\xcfUR\xf3\xb2I\x80\x00\u07d4V\x91\xdd/gE\xf2\x0e\"\xd2\xe1\u0479U\xaa)\x03\xd6VV\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4V\xa1\xd6\r@\xf5\u007f0\x8e\xeb\xf0\x87\xde\xe3\xb3\u007f\x1e|,\xba\x89>\u072e\xc8-\x06\xf8\x00\x00\u07d4V\xac \xd6;\xd8\x03Y\\\xec\x03m\xa7\xed\x1d\xc6n\n\x9e\a\x89\x03w*S\xcc\xdce\x80\x00\u07d4V\xb6\xc2=\xd2\uc434r\x8f;\xb2\xe7d\xc3\xc5\f\x85\xf1D\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\xdf\x05\xba\xd4l?\x00\xaeGn\xcf\x01{\xb8\xc8w8?\xf1\x89\n\xb1]\xaa\xefp@\x00\x00\u07d4V\xee\x19\u007fK\xbf\x9f\x1b\x06b\xe4\x1c+\xbd\x9a\xa1\xf7\x99\xe8F\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\xf4\x93\xa3\xd1\b\xaa\xa2\u044d\x98\x92/\x8e\xfe\x16b\u03f7=\x89m\x81!\xa1\x94\xd1\x10\x00\x00\u07d4V\xfc\x1a{\xad@G#|\xe1\x16\x14b\x96#\x8e\a\x8f\x93\xad\x89\t\xa6?\b\xeac\x88\x00\x00\u07d4V\xfe\xbf\x9e\x10\x03\xaf\x15\xb1\xbdI\a\xec\b\x9aJ\x1b\x91\xd2h\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4W\x17\u0313\x01Q\x1dJ\x81\xb9\xf5\x83\x14\x8b\xee\xd3\xd3\u0303\t\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4W\x17\xf2\xd8\xf1\x8f\xfc\xc0\xe5\xfe$}:B\x19\x03|:d\x9c\x89\u063beI\xb0+\xb8\x00\x00\u07d4W\x19P\xea,\x90\xc1B}\x93\x9da\xb4\xf2\xdeL\xf1\u03ff\xb0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4W\x19\xf4\x9br\r\xa6\x88V\xf4\xb9\xe7\b\xf2VE\xbd\xbcKA\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4W*\xc1\xab\xa0\xde#\xaeA\xa7\xca\xe1\xdc\bB\u062b\xfc\x10;\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94W-\xd8\xcd?\xe3\x99\xd1\xd0\xec(\x121\xb7\xce\xfc \xb9\u4eca\x023\xc8\xfeBp>\x80\x00\x00\xe0\x94WI!\x83\x8c\xc7}l\x98\xb1}\x90::\xe0\xee\r\xa9[\u040a\vS(\x17\x8a\xd0\xf2\xa0\x00\x00\u07d4WJ\xd95S\x90\u421e\xf4*\xcd\x13\x8b*'\xe7\x8c\x00\xae\x89Tg\xb72\xa9\x134\x00\x00\u07d4WM\xe1\xb3\xf3\x8d\x91XF\xae7\x18VJZ\xda \xc2\xf3\xed\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94W\\\x00\u0081\x82\x10\u0085U\xa0\xff)\x01\x02\x89\xd3\xf8#\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94Ws\xb6\x02g!\xa1\xdd\x04\xb7\x82\x8c\xd6+Y\x1b\xfb4SL\x8a\x05\xb7\xacES\xdez\xe0\x00\x00\xe0\x94WwD\x1c\x83\xe0?\v\xe8\xdd4\v\xdechP\x84|b\v\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Wx\xff\u071b\x94\u0165\x9e\"N\xb9e\xb6\u0790\xf2\"\xd1p\x89\x12-\u007f\xf3f\x03\xfc\x00\x00\u07d4Wz\xee\xe8\u053c\b\xfc\x97\xab\x15n\xd5\u007f\xb9p\x92Sf\xbe\x89\x12\r\xf1\x14rX\xbf\x00\x00\u07d4W{-\a\xe9\xcfRJ\x18\u04c9\x15Vak\x96\x06g\x00\x00\u07d4W\xd5\xfd\x0e=0I3\x0f\xfc\xdc\xd0 Ei\x17e{\xa2\u0689k\xf2\x01\x95\xf5T\xd4\x00\x00\u07d4W\u0754q\xcb\xfa&'\t\xf5\U00106f37t\xc5\xf5'\xb8\xf8\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4W\xdf#\xbe\xbd\xc6^\xb7_\ub732\xfa\xd1\xc0si++\xaf\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4X\x00\u03410\x83\x9e\x94I]-\x84\x15\xa8\xea,\x90\xe0\xc5\u02c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94X\x03\xe6\x8b4\xda\x12\x1a\xef\b\xb6\x02\xba\u06ef\xb4\xd1$\x81\u028a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\xe0\x94X\x16\xc2hww\xb6\xd7\u04a2C-Y\xa4\x1f\xa0Y\xe3\xa4\x06\x8a\x1cO\xe4:\xdb\n^\x90\x00\x00\u07d4X\x1a:\xf2\x97\xef\xa4Cj)\xaf\x00r\x92\x9a\xbf\x98&\xf5\x8b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\x1b\x9f\xd6\xea\xe3r\xf3P\x1fB\xeb\x96\x19\xee\xc8 \xb7\x8a\x84\x8a\x04+\xe2\xc0\f\xa5;\x8d\x80\x00\u07d4X\x1b\xdf\x1b\xb2v\xdb\u0746\xae\xdc\xdb9z\x01\xef\xc0\xe0\f[\x8965\u026d\xc5\u07a0\x00\x00\u07d4X\x1f4\xb5#\xe5\xb4\x1c\t\xc8|)\x8e)\x9c\xbc\x0e)\xd0f\x89=X3\xaa\xfd9u\x80\x00\xe0\x94X$\xa7\xe2(8'q40\x8c_KP\u06b6^C\xbb1\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4X+pf\x9c\x97\xaa\xb7\u0581H\xd8\xd4\xe9\x04\x11\xe2\x81\rV\x8965f3\xeb\xd8\xea\x00\x00\u07d4X.|\xc4o\x1d{Nn\x9d\x95\x86\x8b\xfd7\x05s\x17\x8fL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4X>\x83\xbaU\xe6~\x13\xe0\xe7o\x83\x92\xd8s\xcd!\xfb\xf7\x98\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Xi\xfb\x86}q\xf18\u007f\x86;i\x8d\t\xfd\xfb\x87\u011b\\\x89\u01bb\xf8X\xb3\x16\b\x00\x00\u07d4X}hI\xb1h\xf6\xc33+z\xba\xe7\xeblB\xc3\u007fH\xbf\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4X\x87\xdcj3\xdf\xedZ\xc1\xed\xef\xe3^\xf9\x1a!b1\xac\x96\x89\r\x8drkqw\xa8\x00\x00\xe0\x94X\x8e\u0650\xa2\xaf\xf4J\x94\x10]X\xc3\x05%w5\xc8h\xac\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4X\xae-\xdc_L\x8a\u0697\xe0l\x00\x86\x17\x17g\xc4#\xf5\u05c9WG=\x05\u06ba\xe8\x00\x00\u07d4X\xae\xd6gJ\xff\xd9\xf6B3'*W\x8d\xd98k\x99\xc2c\x89\xb8Pz\x82\a( \x00\x00\xe0\x94X\xb8\b\xa6[Q\xe63\x89i\xaf\xb9^\xc7\a5\xe4Q\xd5&\x8a\bxK\xc1\xb9\x83z8\x00\x00\u07d4X\xb8\xae\x8fc\xef5\xed\ab\xf0\xb6#=J\xc1Nd\xb6M\x89lk\x93[\x8b\xbd@\x00\x00\u07d4X\xba\x15ie\x0e[\xbb\xb2\x1d5\xd3\xe1u\xc0\u05b0\xc6Q\xa9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4X\xc5U\xbc)<\xdb\x16\xc66.\xd9z\xe9U\v\x92\xea\x18\x0e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4X\xc6P\xce\xd4\v\xb6VA\xb8\xe8\xa9$\xa09\xde\xf4hT\u07c9\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4X\xc9\aT\xd2\xf2\n\x1c\xb1\xdd3\x06%\xe0KE\xfaa\x9d\\\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\xe2\xf1\x12#\xfc\x827\xf6\x9d\x99\xc6(\x9c\x14\x8c\x06\x04\xf7B\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4X\xe5T\xaf=\x87b\x96 \xdaa\xd58\xc7\xf5\xb4\xb5LJ\xfe\x89FP\x9diE4r\x80\x00\u07d4X\xe5\xc9\xe3D\xc8\x06e\r\xac\xfc\x90M3\xed\xbaQ\a\xb0\u0789\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4X\xe6a\u043as\xd6\xcf$\t\x9aUb\xb8\b\xf7\xb3g;h\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\xf0[&%`P<\xa7a\xc6\x18\x90\xa4\x03_Lsr\x80\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4X\xfb\x94sd\xe7iWe6\x1e\xbb\x1e\x80\x1f\xfb\x8b\x95\xe6\u0409\n\u05ce\xbcZ\xc6 \x00\x00\u07d4Y\x01\x81\xd4E\x00{\u0407Z\xaf\x06\x1c\x8dQ\x159\x00\x83j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y\x02\xe4J\xf7i\xa8rF\xa2\x1e\a\x9c\b\xbf6\xb0n\xfe\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4Y\n\xcb\xda7)\f\r>\xc8O\xc2\x00\rv\x97\xf9\xa4\xb1]\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94Y\f\xcbY\x11\xcfx\xf6\xf6\"\xf55\xc4t7_J\x12\xcf\u03ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y\x10\x10m\xeb\u0491\xa1\u0340\xb0\xfb\xbb\x8d\x8d\x9e\x93\xa7\xcc\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y\x16\x17I\xfe\xdc\xf1\xc7!\xf2 -\x13\xad\xe2\xab\xcfF\v=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Y\x1b\xef1q\xd1\u0155w\x17\xa4\xe9\x8d\x17\xeb\x14,!NV\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y <\xc3u\x99\xb6H1*|\xc9\xe0m\xac\xb5\x89\xa9\xaej\x89\b\x0fyq\xb6@\x0e\x80\x00\u07d4Y&\x81q\xb83\xe0\xaa\x13\xc5KR\xcc\xc0B.O\xa0:\ub262\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94Y'w&\x1e;\xd8R\u010e\u0295\xb3\xa4L[\u007f-B,\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y0Dg\x0f\xae\xff\x00\xa5[Z\xe0Q\xeb{\xe8p\xb1\x16\x94\x89\a?u\u0460\x85\xba\x00\x00\xe0\x94Y;E\xa1\x86J\xc5\xc7\xe8\xf0\u02ae\xba\r\x87<\xd5\xd1\x13\xb2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Y_^\xdajV\xf1N%\xe0\xc6\xf3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Z\x1a3ib\xd6\xe0\xc601\u0303\u01a5\u01a6\xf4G\x8e\u02c965\u026d\xc5\u07a0\x00\x00\u07d4Z\x1d--\x1dR\x03\x04\xb6 \x88IW\x047\xeb0\x91\xbb\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Z&s1\xfa\xcb&-\xaa\xec\xd9\xddc\xa9p\f_RY\u07c9\x05k\xc7^-c\x10\x00\x00\xe0\x94Z(WU9\x1e\x91NX\x02_\xaaH\xcch_O\xd4\xf5\xb8\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4Z)\x16\xb8\xd2\xe8\xcc\x12\xe2\a\xabFMC>#p\xd8#\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4Z+\x1c\x85:\xeb(\xc4U9\xafv\xa0\n\xc2\u0628$(\x96\x89\x01Z\xf1\u05cbX\xc4\x00\x00\u07d4Z-\xaa\xb2\\1\xa6\x1a\x92\xa4\xc8,\x99%\xa1\xd2\xefXX^\x89\f8\r\xa9\u01d5\f\x00\x00\u07d4Z0\xfe\xac7\xac\x9fr\u05f4\xaf\x0f+\xc79R\xc7O\xd5\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4ZTh\xfa\\\xa2&\xc7S.\xcf\x06\xe1\xbc\x1cE\"]~\u0249g\x8a\x93 b\xe4\x18\x00\x00\u07d4ZVR\x857JI\xee\xddPL\x95}Q\bt\xd0\x04U\xbc\x89\x05k\xc7^-c\x10\x00\x00\u07d4Z^\xe8\xe9\xbb\x0e\x8a\xb2\xfe\xcbK3\u0494x\xbeP\xbb\xd4K\x89*\x11)\u0413g \x00\x00\xe0\x94Z_\x85\b\xda\x0e\xbe\xbb\x90\xbe\x903\xbdM\x9e'A\x05\xae\x00\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4Z`q\xbc\xeb\xfc\xbaJ\xb5\u007fM\xb9o\u01e6\x8b\xec\xe2\xba[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z`\xc9$\x16(s\xfc~\xa4\xda\u007f\x97.5\x01g7`1\x89\x04\x87\xf2w\xa8\x85y\x80\x00\u07d4Zf\x86\xb0\xf1~\a\xed\xfcY\xb7Y\xc7}[\xef\x16M8y\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4Zp\x10o \xd6?\x87Re\xe4\x8e\r5\xf0\x0e\x17\xd0+\u0249\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794Zt\xbab\xe7\xc8\x1a4t\xe2}\x89O\xed3\xdd$\xad\x95\xfe\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94Zw5\x00}p\xb0hD\u0699\x01\xcd\xfa\xdb\x11\xa2X,/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Z\x82\xf9l\u0537\xe2\xd9=\x10\xf3\x18]\xc8\xf4=Ku\xaai\x89lc?\xba\xb9\x8c\x04\x00\x00\u07d4Z\x87\xf04\xe6\xf6\x8fNt\xff\xe6\fd\x81\x946\x03l\xf7\u05c9\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94Z\x89\x11U\xf5\x0eB\aCt\xc79\xba\xad\xf7\xdf&Q\x15:\x8a\x01\x02\xdao\xd0\xf7:<\x00\x00\u07d4Z\x9c\x8bi\xfcaMiVI\x99\xb0\r\xcbB\xdbg\xf9~\x90\x89\xb9\xe6\x15\xab\xad:w\x80\x00\xe0\x94Z\xaf\x1c1%Jn\x00_\xba\u007fZ\xb0\xecy\xd7\xfc+c\x0e\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4Z\xb1\xa5aSH\x00\x1c|w]\xc7WHf\x9b\x8b\xe4\xde\x14\x89%jr\xfb)\xe6\x9c\x00\x00\xe1\x94Z\xbf\xec%\xf7L\u06047c\x1aw1\x90i2wcV\xf9\x8b\t\xd8<\xc0\u07e1\x11w\xff\x80\x00\u07d4Z\u0090\x8b\x0f9\x8c\r\xf5\xba\xc2\xcb\x13\xcas\x14\xfb\xa8\xfa=\x89\n\xd4\xc81j\v\f\x00\x00\xe0\x94Z\u025a\u05c1j\xe9\x02\x0f\xf8\xad\xf7\x9f\xa9\x86\x9b|\xeaf\x01\x8a\x04ri\x8bA;C \x00\x00\u07d4Z\xd1,^\xd4\xfa\x82~!P\u03e0\u058c\n\xa3{\x17i\xb8\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94Z\xd5\xe4 uV\x13\x88o5\xaaV\xac@>\xeb\xdf\xe4\xb0\u040a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4Z\xdew\xfd\x81\xc2\\\n\xf7\x13\xb1\a\x02v\x8c\x1e\xb2\xf9u\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Z\xe6N\x85;\xa0\xa5\x12\x82\u02cd\xb5.Aa^|\x9fs?\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z\xed\x0el\xfe\x95\xf9\u0580\xc7dr\xa8\x1a+h\n\u007f\x93\xe2\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4Z\xef\x16\xa2&\xddh\a\x1f$\x83\xe1\xdaBY\x83\x19\xf6\x9b,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z\xf4j%\xac\t\xcbsakS\xb1O\xb4/\xf0\xa5\x1c\u0772\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Z\xf7\xc0r\xb2\u016c\xd7\x1cv\xad\xdc\xceS\\\xf7\xf8\xf95\x85\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94Z\xfd\xa9@\\\x8e\x976QEt\u0692\x8d\xe6tV\x01\t\x18\x8a\x01E\xb8\xb0#\x9aF\x92\x00\x00\u07d4[\x06\xd1\xe6\x93\f\x10Ti+y\xe3\xdb\xe6\xec\xceS\x96d \x89\v\"\u007fc\xbe\x81<\x00\x00\u07d4[%\xca\xe8m\xca\xfa*`\xe7r61\xfc_\xa4\x9c\x1a\xd8}\x89\x87\fXQ\x0e\x85 \x00\x00\u07d4[(|~sB\x99\xe7'bo\x93\xfb\x11\x87\xa6\rPW\xfe\x89\x05|\xd94\xa9\x14\xcb\x00\x00\u07d4[)\f\x01\x96|\x81.M\xc4\xc9\v\x17L\x1b@\x15\xba\xe7\x1e\x89\b \xeb4\x8dR\xb9\x00\x00\u07d4[+d\xe9\xc0X\u30a8\xb2\x99\"N\xec\xaa\x16\xe0\x9c\x8d\x92\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\xe0\x94[./\x16\x18U.\xab\r\xb9\x8a\xddUc|)Q\xf1\xfb\x19\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4[0`\x8cg\x8e\x1a\xc4d\xa8\x99L;3\xe5\xcd\xf3Iq\x12\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4[36\x96\xe0L\xca\x16\x92\xe7\x19\x86W\x9c\x92\rk)\x16\xf9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94[C\rw\x96\x96\xa3e?\xc6\x0et\xfb\u02ec\xf6\xb9\u00ba\xf1\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4[Cse\xae:\x9a/\xf9|h\xe6\xf9\nv \x18\x8c}\x19\x89l\x87T\xc8\xf3\f\b\x00\x00\u07d4[I\xaf\xcduDx8\xf6\xe7\xce\u068d!w}O\xc1\xc3\xc0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4[L\f`\xf1\x0e\u0489K\xdbB\xd9\xdd\x1d!\x05\x87\x81\n\r\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4[N\xa1m\xb6\x80\x9b\x03R\u0536\xe8\x1c9\x13\xf7jQ\xbb2\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4[[\xe0\xd8\xc6rv\xba\xab\xd8\xed\xb3\rH\xeaud\v\x8b)\x89,\xb1\xf5_\xb7\xbe\x10\x00\x00\u07d4[]Qp)2\x15b\x11\x1bC\bm\v\x045\x91\x10\x9ap\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\xe0\x94[]\x8c\x8e\xedl\x85\xac!Va\xde\x02fv\x82?\xaa\n\f\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4[mU\xf6q)g@\\e\x91)\xf4\xb1\xde\t\xac\xf2\xcb{\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4[p\u011c\u024b=\xf3\xfb\xe2\xb1Y\u007f\\\x1bcG\xa3\x88\xb7\x894\x95tD\xb8@\xe8\x00\x00\u07d4[sn\xb1\x83Sb\x9b\u0796v\xda\xdd\x16P4\xce^\xcch\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4[u\x9f\xa1\x10\xa3\x1c\x88F\x9fT\xd4K\xa3\x03\xd5}\xd3\xe1\x0f\x89[F\xdd/\x0e\xa3\xb8\x00\x00\u07d4[w\x84\xca\xea\x01y\x9c\xa3\x02'\x82vg\xce |\\\xbcv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4[x\xec\xa2\u007f\xbd\xeao&\xbe\xfb\xa8\x97+)^x\x146K\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94[\x80\v\xfd\x1b>\u0525}\x87Z\xed&\xd4/\x1aw\b\xd7*\x8a\x01Z\x82\xd1\u057b\x88\xe0\x00\x00\u07d4[\x85\xe6\x0e*\xf0TO/\x01\xc6N 2\x90\x0e\xbd8\xa3\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4[\xa2\xc6\xc3]\xfa\xec)h&Y\x19\x04\xd5DFJ\xea\xbd^\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94[\xafmt\x96 \x80>\x83H\xaf7\x10\xe5\xc4\xfb\xf2\x0f\u0214\x8a\x01\x0f@\x02a]\xfe\x90\x00\x00\u07d4[\xc1\xf9U\a\xb1\x01\x86B\xe4\\\xd9\xc0\xe2'3\xb9\xb1\xa3&\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94[\xd25GG\u007fm\t\u05f2\xa0\x05\xc5\xeee\fQ\fV\u05ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4[\xd2J\xac6\x12\xb2\f`\x9e\xb4gy\xbf\x95i\x84\a\xc5|\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4[\u0586-Q}M\xe4U\x9dN\xec\n\x06\xca\xd0^/\x94n\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4[\xe0EQ*\x02n?\x1c\xeb\xfdZ~\xc0\xcf\xc3o-\xc1k\x89\x06\x81U\xa46v\xe0\x00\x00\xe0\x94[\xf9\xf2\"nZ\xea\xcf\x1d\x80\xae\nY\xc6\xe3\x808\xbc\x8d\xb5\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4[\xfa\xfe\x97\xb1\xdd\x1dq+\xe8mA\xdfy\x89SE\x87Z\x87\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\\\x0f.Q7\x8fk\r{\xabas1X\vn9\xad<\xa5\x8a\x02\bj\xc3Q\x05&\x00\x00\x00\u07d4\\)\xf9\xe9\xa5#\xc1\xf8f\x94H\xb5\\H\xcb\xd4|%\xe6\x10\x894F\xa0\xda\xd0L\xb0\x00\x00\xe0\x94\\0\x8b\xacHW\xd3;\xae\xa0t\xf3\x95m6!\xd9\xfa(\xe1\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\\1*V\u01c4\xb1\"\t\x9bvM\x05\x9c!\xec\xe9^\x84\u0289\x05&c\u032b\x1e\x1c\x00\x00\u07d4\\1\x99m\xca\xc0\x15\xf9\xbe\x98[a\x1fF\x870\xef$M\x90\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\\24W\xe1\x87v\x1a\x82v\xe3Y\xb7\xb7\xaf?;n=\xf6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\\<\x1cd[\x91uC\x11;>l\x1c\x05M\xa1\xfet+\x9a\x89+^:\xf1k\x18\x80\x00\x00\u0794\\=\x19D\x1d\x19l\xb4Cf \xfc\xad\u007f\xbby\xb2\x9ex\x88\xc6s\xce<@\x16\x00\x00\u07d4\\?V\u007f\xaf\xf7\xba\u0475\x12\x00\"\xe8\xcb\u02a8+I\x17\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\\Ch\x91\x8a\xced\t\u01de\u0280\u036a\xe49\x1d+bN\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\\FA\x97y\x1c\x8a=\xa3\xc9%Co'z\xb1;\xf2\xfa\xa2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\\H\x81\x16\\\xb4+\xb8.\x979l\x8e\xf4J\xdb\xf1s\xfb\x99\x89\x05\xfe\xe2\"\x04\x1e4\x00\x00\xe0\x94\\H\x92\x90z\a \xdfo\xd3A>c\xffv}k9\x80#\x8a\x02\xcb\x00\x9f\u04f5y\x0f\x80\x00\u07d4\\O$\xe9\x94\ud3c5\x0e\xa7\x81\x8fG\x1c\x8f\xac;\xcf\x04R\x89]\x80h\x8d\x9e1\xc0\x00\x00\u07d4\\T\x19V\\:\xadNqN\a92\x8e5!\u024f\x05\u0309\x1c\x9fx\u0489>@\x00\x00\u07d4\\a6\xe2\x18\xde\na\xa17\xb2\xb3\x96-*a\x12\xb8\t\u05c9\x0f\xf3\u06f6_\xf4\x86\x80\x00\xe0\x94\\a\xaby\xb4\b\xdd2)\xf6bY7\x05\xd7/\x1e\x14{\xb8\x8a\x04\xd0$=4\x98\u0344\x00\x00\u07d4\\m\x04\x1d\xa7\xafD\x87\xb9\xdcH\xe8\xe1\xf6\af\u0425m\xbc\x89O\a\n\x00>\x9ct\x00\x00\u07d4\\o6\xaf\x90\xab\x1aeln\xc8\xc7\xd5!Q'b\xbb\xa3\xe1\x89lh\xcc\u041b\x02,\x00\x00\u07d4\\{\x9e\u01e2C\x8d\x1eD*\x86\x0f\x8a\x02\x1e\x18\x99\xf07z\xea\x00\x00\u07d4\\\xcc\xf1P\x8b\xfd5\xc2\x050\xaad%\x00\xc1\r\xeee\xea\xed\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\\\xcer\xd0h\xc7\xc3\xf5[\x1d(\x19T^w1|\xae\x82@\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\\\xd0\xe4u\xb5D!\xbd\xfc\f\x12\xea\x8e\b+\u05e5\xaf\nj\x89\x032\xca\x1bg\x94\f\x00\x00\u07d4\\\u0548\xa1N\xc6H\xcc\xf6G)\xf9\x16z\xa7\xbf\x8b\xe6\xeb=\x8965\u026d\xc5\u07a0\x00\x00\u07d4\\\u062f`\xdee\xf2M\xc3\xceW0\xba\x92e0\"\xdcYc\x89a\t=|,m8\x00\x00\u07d4\\\xdcG\b\xf1O@\xdc\xc1Zy_}\xc8\xcb\v\u007f\xaa\x9en\x89\x1d\x1c_>\xda \xc4\x00\x00\u07d4\\\u0d86,\u0391b\xe8~\bI\xe3\x87\xcb]\xf4\xf9\x11\x8c\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\\\xe2\xe7\u03aa\xa1\x8a\xf0\xf8\xaa\xfa\u007f\xba\xd7L\u021e<\xd46\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\\\xe4@h\xb8\xf4\xa3\xfey\x9ej\x83\x11\xdb\xfd\xed\xa2\x9d\xee\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u0794\\\xeb\xe3\v*\x95\xf4\xae\xfd\xa6ee\x1d\xc0\xcf~\xf5u\x81\x99\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\\\xf1\x8f\xa7\u0227\xc0\xa2\xb3\xd5\xef\u0459\x0fd\xdd\xc5i$,\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\\\xf4N\x10T\reqd#\xb1\xbc\xb5B\xd2\x1f\xf8:\x94\u034a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\\\xf8\xc0>\xb3\xe8r\xe5\x0f|\xfd\f/\x8d;?,\xb5\x18:\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\\\xfa\x8dV\x85ue\x8c\xa4\xc1\xa5\x93\xacL]\x0eD\xc6\aE\x89\x0f\xc6o\xae7F\xac\x00\x00\u07d4\\\xfa\x98w\xf7\x19\u01dd\x9eIJ\b\xd1\xe4\x1c\xf1\x03\xfc\x87\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4]\x1d\xc38{G\xb8E\x1eU\x10l\f\xc6}m\xc7+\u007f\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4]#\x1ap\xc1\xdf\xeb6\n\xbd\x97\xf6\x16\xe2\xd1\r9\xf3\u02b5\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4]$\xbd\xbc\x1cG\xf0\xeb\x83\xd1(\xca\xe4\x8a\xc3\xf4\xb5\x02bt\a\xda'/g\x81Jk\xec\u0509\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4]\x83\xb2\x1b\xd2q#`Ckg\xa5\x97\xee3x\xdb>z\xe4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94]\x87+\x12.\x99N\xf2|q\xd7\u07b4W\xbfeB\x9e\xcal\x8a\x01\xb1\xad\xed\x81\u04d4\x10\x80\x00\xe0\x94]\x8d1\xfa\xa8d\xe2!Y\xcdoQu\xcc\xec\xc5?\xa5Mr\x8a\x05\xb6\x96\xb7\r\xd5g\x10\x00\x00\xe0\x94]\x95\x8a\x9b\u0449\u0098_\x86\u014a\x8ci\xa7\xa7\x88\x06\xe8\u068a\x02(\xf1o\x86\x15x`\x00\x00\u07d4]\xa2\xa9\xa4\xc2\xc0\xa4\xa9$\xcb\xe0\xa5:\xb9\xd0\xc6'\xa1\u03e0\x89'\xbf8\xc6TM\xf5\x00\x00\u07d4]\xa4\u0288\x93\\'\xf5\\1\x10H\x84\x0eX\x9e\x04\xa8\xa0I\x89\x04V9\x18$O@\x00\x00\u07d4]\xa5G\x85\u027d0W\\\x89\u07b5\x9d A\xd2\n9\xe1{\x89j\xa2\t\xf0\xb9\x1de\x80\x00\xe0\x94]\xb6\x9f\xe9>o\xb6\xfb\xd4P\x96k\x97#\x8b\x11\n\xd8'\x9a\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4]\xb7\xbb\xa1\xf9W?$\x11]\x8c\x8cb\xe9\u0388\x95\x06\x8e\x9f\x89\x02\xb5\xaa\xd7,e \x00\x00\xe0\x94]\xb8D\x00W\x00i\xa9W<\xab\x04\xb4\u6d955\xe2\x02\xb8\x8a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4]\xc3m\xe55\x94P\xa1\xec\t\xcb\fD\xcf+\xb4+:\xe45\x89<\x94m\x89;3\x06\x00\x00\u07d4]\xc6\xf4_\xef&\xb0n3\x021?\x88M\xafH\xe2to\xb9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94]\u0376\xb8zP\xa9\xde\x02C\x80\x00\x00\u07d4^Q\xb8\xa3\xbb\t\xd3\x03\xea|\x86\x05\x15\x82\xfd`\x0f\xb3\xdc\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794^X\xe2U\xfc\x19\x87\n\x040_\xf2\xa0F1\xf2\xff)K\xb1\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4^ZD\x19t\xa8=t\u0187\xeb\xdcc?\xb1\xa4\x9e{\x1a\u05c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^eE\x8b\xe9d\xaeD\x9fqw7\x04\x97\x97f\xf8\x89\x87a\x89\x1c\xa7\xccs[o|\x00\x00\u07d4^g\u07c9i\x10\x1a\u06bd\x91\xac\xcdk\xb1\x99\x12t\xaf\x8d\xf2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4^n\x97G\xe1b\xf8\xb4\\en\x0fl\xaez\x84\xba\xc8\x0eN\x89lk\x93[\x8b\xbd@\x00\x00\u07d4^s\x1bU\xce\xd4R\xbb??\xe8q\xdd\xc3\xed~\xe6Q\n\x8f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^t\xed\x80\xe9eW\x88\xe1\xbb&\x97R1\x96g\xfeuNZ\x89\x03\t'\xf7L\x9d\xe0\x00\x00\u07d4^w.'\xf2\x88\x00\xc5\r\u0697;\xb3>\x10v.n\xea \x89a\t=|,m8\x00\x00\u07d4^{\x8cT\xdcW\xb0@ bq\x9d\xee~\xf5\xe3~\xa3]b\x89\x9b\xf9\x81\x0f\xd0\\\x84\x00\x00\u07d4^\u007fp7\x87uX\x9f\xc6j\x81\xd3\xf6S\xe9T\xf5U`\ub243\xf2\x89\x18\x1d\x84\xc8\x00\x00\xe0\x94^\x80n\x84W0\xf8\a>l\xc9\x01\x8e\xe9\x0f\\\x05\xf9\t\xa3\x8a\x02\x01\xe9m\xac\u03af \x00\x00\u07d4^\x8eM\xf1\x8c\xf0\xafw\tx\xa8\u07cd\xac\x90\x93\x15\x10\xa6y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4^\x90\xc8Xw\x19\x87V\xb06l\x0e\x17\xb2\x8eR\xb4FPZ\x89\x14JJ\x18\xef\xebh\x00\x00\u07d4^\x95\xfe_\xfc\xf9\x98\xf9\xf9\xac\x0e\x9a\x81\u06b8>\xadw\x00=\x89\x1dB\xc2\r2y\u007f\x00\x00\u07d4^\xad)\x03z\x12\x89dx\xb1)j\xb7\x14\xe9\u02d5B\x8c\x81\x89\x03\xe0C\a-@n\x00\x00\u07d4^\xb3q\xc4\a@lB{;}\xe2q\xad<\x1e\x04&\x95y\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^\u037a\xea\xb9\x10o\xfe]{Q\x96\x96`\x9a\x05\xba\ub16d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4^\xd0\xd63\x85Y\xefD\xdcza\xed\xeb\x89?\xa5\xd8?\xa1\xb5\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\xe0\x94^\u04fb\xc0R@\xe0\u04d9\xebm\xdf\xe6\x0fb\xdeM\x95\t\xaf\x8a)\x14\xc0$u\xf9\xd6\xd3\x00\x00\u0594^\xd3\xf1\xeb\xe2\xaegV\xb5\xd8\xdc\x19\xca\xd0,A\x9a\xa5w\x8b\x80\u07d4^\xd5a\x15\xbde\x05\xa8\x82s\xdf\\V\x83\x94p\xd2J-\xb7\x89\x03\x8ee\x91\xeeVf\x80\x00\xe0\x94^\xf8\xc9a\x86\xb3y\x84\xcb\xfe\x04\u0158@n;\n\xc3\x17\x1f\x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4^\xfb\xdf\xe58\x99\x99c<&`Z[\xfc,\x1b\xb5\x95\x93\x93\x89\x03\xc0W\xc9\\\xd9\b\x00\x00\xe0\x94_\x13\x15F1Fm\xcb\x13S\u0210\x93*|\x97\xe0\x87\x8e\x90\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4_\x16z\xa2B\xbcL\x18\x9a\xde\xcb:\u0127\xc4R\xcf\x19/\u03c9lkLM\xa6\u077e\x00\x00\xe0\x94_\x1c\x8a\x04\xc9\rs[\x8a\x15)\t\xae\xaeco\xb0\xce\x16e\x8a\x01{x'a\x8cZ7\x00\x00\u07d4_#\xba\x1f7\xa9lE\xbcI\x02YS\x8aT\u008b\xa3\xb0\u0549A\rXj \xa4\xc0\x00\x00\u07d4_&\xcf4Y\x9b\xc3n\xa6{\x9ez\x9f\x9bC0\xc9\xd5B\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4_)\xc9\xdev]\xde%\x85*\xf0}3\xf2\xceF\x8f\xd2\t\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_/\a\xd2\u0597\xe8\xc5g\xfc\xfd\xfe\x02\x0fI\xf3`\xbe!9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_2\x1b=\xaa\xa2\x96\xca\xdf)C\x9f\x9d\xab\x06*K\xff\xed\u0589\x04p%\x90>\xa7\xae\x00\x00\u07d4_3:;#\x10vZ\r\x182\xb9\xbeL\n\x03pL\x1c\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4_4K\x01\xc7\x19\x1a2\xd0v*\xc1\x88\xf0\xec-\xd4`\x91\x1d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94_6>\n\xb7G\xe0-\x1b;f\xab\xb6\x9e\xa5<{\xafR:\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\u07d4_7[\x86`\f@\u0328\xb2gkz\x1a\x1d\x16D\xc5\xf5,\x89\x04F\x18\xd7Lb?\x00\x00\u07d4_>\x1eg9\xb0\xc6\"\x00\xe0\n\x006\x91\xd9\xef\xb28\u061f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4_H?\xfb\x8fh\n\xed\xf2\xa3\x8fx3\xaf\xdc\xdeY\xb6\x1eK\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94_J\xceL\x1c\xc13\x91\xe0\x1f\x00\xb1\x98\xe1\xf2\v_\x91\xcb\xf5\x8a\x01\x0f\x0f\xa8\xb9\u04c1\x1a\x00\x00\xe0\x94_R\x12\x82\xe9\xb2x\u070c\x03Lr\xafS\xee)\xe5D=x\x8a\x01as-/\x8f:\xe0\x00\x00\u07d4_h\xa2L~\xb4\x11vgs{39?\xb3\xc2\x14\x8aS\xb6\x89\x02\xce\u0791\x8dE<\x00\x00\u07d4_p\x8e\xaf9\xd8#\x94lQ\xb3\xa3\u9df3\xc0\x03\xe2cA\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4_t.H~:\xb8\x1a\xf2\xf9J\xfd\xbe\x1b\x9b\x8f\\\u0301\xbc\x89u\xc4E\xd4\x11c\xe6\x00\x00\u07d4_t\xed\x0e$\xff\x80\u0672\u0124K\xaa\x99uB\x8c\u05b95\x89\xa1\x8b\xce\xc3H\x88\x10\x00\x00\u07d4_v\xf0\xa3\x06&\x9cx0k=e\r\xc3\xe9\xc3p\x84\xdba\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4_w\xa1\a\xab\x12&\xb3\xf9_\x10\ue0ee\xfcl]\xff>\u0709\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4_{;\xba\xc1m\xab\x83\x1aJ\x0f\xc5;\fT\x9d\xc3l1\u0289i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94_\x93\xff\x83't\xdbQ\x14\xc5[\xb4\xbfD\xcc\U000f53d0?\x8a(\xa9\xc9\x1a&4X)\x00\x00\u07d4_\x96\x16\xc4{Jg\xf4\x06\xb9Z\x14\xfeo\xc2h9o\x17!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4_\x98\x109\xfc\xf5\x02%\xe2\xad\xf7bu!\x12\xd1\xcc&\xb6\xe3\x89\x1b\x1aAj!S\xa5\x00\x00\u07d4_\x99\u070eI\xe6\x1dW\xda\xef`j\xcd\xd9\x1bMp\a2j\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4_\xa6\x1f\x15-\xe6\x125\x16\xc7Q$)y(_yj\u01d1\x89\v\x0f\x11\x97)c\xb0\x00\x00\u07d4_\xa7\xbf\xe0C\x88a'\xd4\x01\x1d\x83V\xa4~\x94yc\xac\xa8\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94_\xa8\xa5Nh\x17lO\xe2\xc0\x1c\xf6q\xc5\x15\xbf\xbd\xd5(\xa8\x8aE\xe1U\xfa\x01\x10\xfa@\x00\x00\u07d4_\xad\x96\x0fk,\x84V\x9c\x9fMG\xbf\x19\x85\xfc\xb2\xc6]\xa6\x8965f3\xeb\xd8\xea\x00\x00\u07d4_\xc6\xc1\x14&\xb4\xa1\xea\xe7\xe5\x1d\xd5\x12\xad\x10\x90\xc6\xf1\xa8[\x89\x93\xfe\\W\xd7\x10h\x00\x00\u07d4_\u0344Th\x96\xdd\b\x1d\xb1\xa3 \xbdM\x8c\x1d\xd1R\x8cL\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4_\u0368G\xaa\xf8\xd7\xfa\x8b\xca\b\x02\x9c\xa2\x84\x91f\xaa\x15\xa3\x89!\u02b8\x12Y\xa3\xbf\x00\x00\u07d4_\xd1\xc3\xe3\x17x'l\xb4.\xa7@\xf5\xea\xe9\xc6A\xdb\xc7\x01\x89\n\x84Jt$\xd9\xc8\x00\x00\u07d4_\xd3\xd6w~\xc2b\n\xe8:\x05R\x8e\xd4%\a-<\xa8\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_\xd9s\xaf6j\xa5\x15|Te\x9b\u03f2|\xbf\xa5\xac\x15\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4_\xe7w\x03\x80\x8f\x82>l9\x93R\x10\x8b\xdb,R|\xb8|\x89j@v\xcfy\x95\xa0\x00\x00\xe0\x94_\xecI\xc6e\xe6N\xe8\x9d\xd4A\xeet\x05n\x1f\x01\xe9(p\x8a\x01V\x9b\x9es4t\xc0\x00\x00\u07d4_\xf3&\xcd`\xfd\x13k$^)\xe9\bzj\u04e6R\u007f\r\x89e\xea=\xb7UF`\x00\x00\u07d4_\xf9=\xe6\xee\x05L\xadE\x9b-^\xb0\xf6\x87\x03\x89\xdf\xcbt\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\u07d4`\x06\xe3m\x92\x9b\xf4]\x8f\x16#\x1b\x12j\x01\x1a\xe2\x83\xd9%\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4`!\xe8Z\x88\x14\xfc\xe1\xe8*A\xab\xd1\u04f2\xda\xd2\xfa\xef\xe0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4`8t\n\xe2\x8df\xba\x93\xb0\xbe\bH+2\x05\xa0\xf7\xa0{\x89\x11!a\x85\u009fp\x00\x00\u07d4`?/\xabz\xfbn\x01{\x94v`i\xa4\xb4;8\x96I#\x89Y\xd2\xdb$\x14\u0699\x00\x00\u07d4`B'm\xf2\x98?\xe2\xbcGY\xdc\x19C\xe1\x8f\xdb\xc3Ow\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4`B\xc6D\xba\xe2\xb9o%\xf9M1\xf6x\xc9\r\xc9f\x90\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4`L\xdf\x18b\x8d\xbf\xa82\x91\x94\xd4x\xddR\x01\xee\xccK\xe7\x89\x01?0j$\t\xfc\x00\x00\u07d4`N\x94w\xeb\xf4r|t[\u02bb\xed\xcbl\xcf)\x99@\"\x8966\x9e\xd7t}&\x00\x00\u07d4`gm\x1f\xa2\x1f\xca\x05\"\x97\xe2K\xf9c\x89\u0171*p\u05c9\r\x17|Zzh\xd6\x00\x00\u07d4`gn\x92\u044b\x00\x05\t\xc6\x1d\xe5@\xe6\xc5\u0776v\xd5\t\x89A\rXj \xa4\xc0\x00\x00\u07d4`o\x17q!\xf7\x85\\!\xa5\x06#0\xc8v\"d\xa9{1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4`\x86B6\x93\r\x04\xd8@+]\xcb\xeb\x80\u007f<\xafa\x1e\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4`\xabq\xcd&\xeamnY\xa7\xa0\xf6'\xee\a\x9c\x88^\xbb\xf6\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4`\xaf\x0e\xe1\x18D<\x9b7\xd2\xfe\xadw\xf5\xe5!\u07be\x15s\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4`\xb3X\xcb=\xbe\xfa7\xf4}\xf2\xd76X@\u068e;\u024c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4`\xb8\u05b7;ySO\xb0\x8b\xb8\xcb\xce\xfa\xc7\xf3\x93\xc5{\xfe\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4`\xbeo\x95?*M%\xb6%o\xfd$#\xac\x148%.N\x89\b!\xab\rD\x14\x98\x00\x00\u0794`\xc3qO\xdd\xdbcFY\u48b1\xeaB\xc4r\x8c\u01f8\xba\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4`\xcc=D^\xbd\xf7j}z\xe5q\u0197\x1d\xffh\u0305\x85\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94`\xd5fq@\xd1&\x14\xb2\x1c\x8e^\x8a3\b.2\xdf\xcf#\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4`\xde\"\xa1Pt2\xa4{\x01\xcch\xc5*\v\xf8\xa2\xe0\u0418\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4`\xe0\xbd\u0422Y\xbb\x9c\xb0\x9d?7\xe5\u034b\x9d\xac\uafca\x89JD\x91\xbdm\xcd(\x00\x00\u07d4`\xe3\xccC\xbc\xdb\x02j\xadu\x9cpf\xf5U\xbb\xf2\xacf\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\x04+\x80\xfd`\x95\u0478{\xe2\xf0\x0f\x10\x9f\xab\xaf\xd1W\xa6\x89\x05k\xc7^-c\x10\x00\x00\u07d4a\a\xd7\x1d\xd6\xd0\xee\xfb\x11\xd4\xc9\x16@L\xb9\x8cu>\x11}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\x0f\xd6\xeeN\xeb\xab\x10\xa8\xc5]\vK\xd2\xe7\xd6\xef\x81qV\x89\x01\x15\x95a\x06]]\x00\x00\u07d4a\x14\xb0\xea\xe5Wi\x03\xf8\v\xfb\x98\x84-$\xed\x92#\u007f\x1e\x89\x05k\xc7^-c\x10\x00\x00\u07d4a!\xaf9\x8a[-\xa6\x9fe\xc68\x1a\xec\x88\u039c\xc6D\x1f\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4a&g\xf1r\x13[\x95\v,\xd1\xde\x10\xaf\xde\xcehW\xb8s\x8965\u026d\xc5\u07a0\x00\x00\u07d4a,\xed\x8d\xc0\u071e\x89\x9e\xe4oyb33\x15\xf3\xf5^D\x89\x12^5\xf9\xcd=\x9b\x00\x00\u07d4a4\xd9B\xf07\xf2\xcc=BJ#\f`=g\xab\xd3\xed\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a:\xc5;\xe5e\xd4e6\xb8 q[\x9b\x8d:\xe6\x8aK\x95\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\u07d4a?\xabD\xb1k\xbeUMD\xaf\xd1x\xab\x1d\x02\xf3z\ua949lk\x93[\x8b\xbd@\x00\x00\u07d4aN\x8b\xef=\xd2\u015bY\xa4\x14Vt@\x10\x185\x18\x84\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4aQ\x84d\xfd\u0637<\x1b\xb6\xacm\xb6\x00eI8\xdb\xf1z\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4aT}7nSi\xbc\xf9x\xfc\x16,1\xc9\b\"3\xb8%\xd0%\xbe?{\x10V\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4a\x91\xdd\u0276J\x8e\b\x90\xb427\t\u05e0|H\xb9*d\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4a\x96\xc3\xd3\xc0\x90\x8d%Cf\xb7\xbc\xa5WE\"-\x9dM\xb1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4a\x9f\x17\x14E\xd4+\x02\xe2\xe0p\x04\xad\x8a\xfeiO\xa5=j\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4a\xad\xf5\x92\x9a^)\x81hN\xa2C\xba\xa0\x1f}\x1f^\x14\x8a\x89\x05\xfa\xbfl\x98O#\x00\x00\u07d4a\xb1\xb8\xc0\x12\xcdLx\xf6\x98\xe4p\xf9\x02V\xe6\xa3\x0fH\u0749\n\u05ce\xbcZ\xc6 \x00\x00\u07d4a\xb3\xdf.\x9e\x9f\xd9h\x13\x1f\x1e\x88\xf0\xa0\xeb[\xd7eFM\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4a\xb9\x02\u0166s\x88X&\x82\r\x1f\xe1EI\xe4\x86_\xbd\u0089\x12$\xef\xed*\u1440\x00\u07d4a\xb9\x05\xdef?\xc1s\x86R;:(\xe2\xf7\xd07\xa6U\u0349\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4a\xba\x87\xc7~\x9bYm\xe7\xba\x0e2o\xdd\xfe\xec!c\xeff\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4a\xbf\x84\u056b\x02oX\xc8s\xf8o\xf0\xdf\u0282\xb5W3\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\xc4\xee|\x86LMk^7\xea\x131\xc2\x03s\x9e\x82k/\x89\x01\xa15;8*\x91\x80\x00\u07d4a\xc80\xf1eG\x18\xf0u\u032b\xa3\x16\xfa\xac\xb8[}\x12\v\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4a\xc8\xf1\xfaC\xbf\x84i\x99\xec\xf4{+2M\xfbkc\xfe:\x89+^:\xf1k\x18\x80\x00\x00\u07d4a\xc9\xdc\u8c98\x1c\xb4\x0e\x98\xb0@+\xc3\xeb(4\x8f\x03\xac\x89\n\xac\xac\u0679\xe2+\x00\x00\u07d4a\u03a7\x1f\xa4d\xd6*\a\x06?\x92\v\f\xc9\x17S\x973\u0609Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4a\xd1\x01\xa03\xee\x0e.\xbb1\x00\xed\xe7f\xdf\x1a\xd0$IT\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4a\xedU\x96\u0197 \u007f=U\xb2\xa5\x1a\xa7\xd5\x0f\a\xfa\t\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\xff\x8eg\xb3M\x9e\xe6\xf7\x8e\xb3o\xfe\xa1\xb9\xf7\xc1W\x87\xaf\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4b\x05\xc2\xd5dtp\x84\x8a8@\xf3\x88~\x9b\x01]4u\\\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4b(\xad\xe9^\x8b\xb1}\x1a\xe2;\xfb\x05\x18AMI~\x0e\xb8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94b)\xdc\xc2\x03\xb1\xed\xcc\xfd\xf0n\x87\x91\fE*\x1fMzr\x8a\x06\xe1\xd4\x1a\x8f\x9e\xc3P\x00\x00\u0794b+\xe4\xb4T\x95\xfc\xd91C\xef\xc4\x12\u0599\xd6\xcd\xc2=\u0148\xf0\x15\xf2W6B\x00\x00\u07d4b3\x1d\xf2\xa3\xcb\xee5 \xe9\x11\u07a9\xf7>\x90_\x89%\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4bVD\xc9Z\x87>\xf8\xc0l\u06de\x9fm\x8dv\x80\x04=b\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4be\xb2\xe7s\x0f6\xb7v\xb5-\f\x9d\x02\xad\xa5]\x8e<\xb6\x8965\u026d\xc5\u07a0\x00\x00\u07d4bh\n\x15\xf8\u0338\xbd\xc0/s`\xc2Z\xd8\u03f5{\x8c\u034965\u026d\xc5\u07a0\x00\x00\u07d4b\x94\xea\xe6\xe4 \xa3\xd5`\n9\xc4\x14\x1f\x83\x8f\xf8\xe7\xccH\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4b\x97\x1b\xf2cL\xee\v\xe3\u0249\x0fQ\xa5`\x99\u06f9Q\x9b\x89#\x8f\xd4,\\\xf0@\x00\x00\u07d4b\x9b\xe7\xab\x12jS\x98\xed\xd6\u069f\x18D~x\u0192\xa4\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4b\xb4\xa9\"nah\a\x1el\xbea\x11\xfe\xf0\xbcc\x8a\x03\xba\x19\x10\xbf4\x1b\x00\x00\x00\xe0\x94c\n\x91:\x901\xc9I*\xbdLA\u06f1PT\xcf\xecD\x16\x8a\x014X\xdbg\xaf5\xe0\x00\x00\xe0\x94c\fRs\x12mQ|\xe6q\x01\x81\x1c\xab\x16\xb8SL\xf9\xa8\x8a\x01\xfe\xcc\xc6%s\xbb\u04c0\x00\u07d4c\x100\xa5\xb2{\a(\x8aEio\x18\x9e\x11\x14\xf1*\x81\xc0\x89\x1b\x1azB\v\xa0\r\x00\x00\u07d4c\x10\xb0 \xfd\x98\x04IW\x99P\x92\t\x0f\x17\xf0NR\xcd\xfd\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4c+\x91I\xd7\x01x\xa7364'^\x82\u0555?'\x96{\x89%\xf2s\x93=\xb5p\x00\x00\u07d4c,\xec\xb1\f\xfc\xf3\x8e\u0246\xb4;\x87p\xad\xec\xe9 \x02!\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4c1\x02\x8c\xbbZ!H[\xc5\x1bVQB\x99;\xdb%\x82\xa9\x89\x1c\xfd\xd7F\x82\x16\xe8\x00\x00\u07d4c3O\xcf\x17E\x84\x0eK\tJ;\xb4\v\xb7o\x96\x04\xc0L\x89\u05e5\xd7\x03\xa7\x17\xe8\x00\x00\u07d4c4\nWqk\xfac\xebl\xd13r\x12\x02W[\xf7\x96\xf0\x89\va\xe0\xa2\f\x12q\x80\x00\u07d4cN\xfc$7\x11\a\xb4\xcb\xf0?y\xa9=\xfd\x93\xe41\xd5\xfd\x89B5\x82\xe0\x8e\xdc\\\x80\x00\xe0\x94c\\\x00\xfd\xf05\xbc\xa1_\xa3a\r\xf38N\x0f\xb7\x90h\xb1\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4ca.xb\xc2{X|\xfbm\xaf\x99\x12\xcb\x05\x1f\x03\n\x9f\x89\x02[\x19\u053f\xe8\xed\x00\x00\u07d4cfgU\xbdA\xb5\x98i\x97x<\x13\x040\b$+<\xb5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4c{\xe7\x1b:\xa8\x15\xffE=VB\xf70tE\vd\xc8*\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94c}g\xd8\u007fXo\nZG\x9e \xee\x13\xea1\n\x10\xb6G\x8a\n:Y&\xaf\xa1\xe70\x00\x00\u07d4c\u007fXi\xd6\xe4i_\x0e\xb9\xe2s\x11\u0107\x8a\xff33\x80\x89j\xc0Nh\xaa\xec\x86\x00\x00\u07d4c\x97|\xad}\r\xcd\xc5+\x9a\xc9\xf2\xff\xa16\xe8d(\x82\xb8\x89\x04\x10\u0546\xa2\nL\x00\x00\u07d4c\xa6\x1d\xc3\n\x8e;0\xa7c\xc4!<\x80\x1c\xbf\x98s\x81x\x8965\u026d\xc5\u07a0\x00\x00\u07d4c\xacT\\\x99\x12C\xfa\x18\xae\xc4\x1dOoY\x8eUP\x15\u0709 \x86\xac5\x10R`\x00\x00\u07d4c\xb9uMu\xd1-8@9\xeci\x06<\v\xe2\x10\xd5\xe0\u3252\v\x86\f\xc8\xec\xfd\x80\x00\u07d4c\xbbfO\x91\x17\x03v(YM\xa7\xe3\xc5\b\x9f\xd6\x18\xb5\xb5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4c\u00a3\xd25\xe5\xee\xab\xd0\u0526\xaf\u06c9\xd9F'9d\x95\x89CN\xf0[\x9d\x84\x82\x00\x00\u07d4c\xc8\xdf\xde\v\x8e\x01\xda\xdc.t\x8c\x82L\xc06\x9d\U00010cc9\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4c\xd5Z\u065b\x917\xfd\x1b \xcc+O\x03\xd4,\xba\xdd\xf34\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4c\xd8\x00H\x87u\x96\xe0\u0084\x89\xe6P\xcdJ\xc1\x80\tjI\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94c\xe4\x14`>\x80\xd4\xe5\xa0\xf5\xc1\x87t FB%\x82\b\xe4\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94c\xe8\x8e.S\x9f\xfbE\x03\x86\xb4\xe4g\x89\xb2#\xf5GlE\x8a\x01U\x17\nw\x8e%\xd0\x00\x00\u07d4c\xef/\xbc=\xaf^\xda\xf4\xa2\x95b\x9c\xcf1\xbc\xdf@8\xe5\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4c\xf0\xe5\xa7R\xf7\x9fg\x12N\xedc:\xd3\xfd'\x05\xa3\x97\u0509\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94c\xf5\xb5=y\xbf.A\x14\x89Re0\"8E\xfa\xc6\xf6\x01\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4c\xfc\x93\x00\x13\x05\xad\xfb\u0278])\xd9)\x1a\x05\xf8\xf1A\v\x8965\u026d\xc5\u07a0\x00\x00\u0794c\xfek\xccK\x8a\x98P\xab\xbeu\x8070\xc92%\x1f\x14[\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4d\x03\xd0bT\x96\x90\xc8\xe8\xb6>\xaeA\xd6\xc1\tGn%\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4d\x04+\xa6\x8b\x12\xd4\xc1Qe\x1c\xa2\x81;sR\xbdV\xf0\x8e\x89 \x86\xac5\x10R`\x00\x00\u0794d\x05\xdd\x13\xe9:\xbc\xff7~p\x0e<\x1a\x00\x86\xec\xa2})\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94d\n\xbam\xe9\x84\xd9E\x177x\x03p^\xae\xa7\t_J\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4d\v\xf8t\x15\xe0\xcf@s\x01\xe5Y\x9ah6m\xa0\x9b\xba\u0209\x1a\xbc\x9fA`\x98\x15\x80\x00\u07d4d \xf8\xbc\xc8\x16JaR\xa9\x9dk\x99i0\x05\xcc\xf7\xe0S\x8965f3\xeb\xd8\xea\x00\x00\u07d4d$\x1axD)\x0e\n\xb8U\xf1\u052au\xb5SE\x03\"$\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4d&J\xed\xd5-\xca\xe9\x18\xa0\x12\xfb\xcd\f\x03\x0e\xe6\xf7\x18!\x8965\u026d\xc5\u07a0\x00\x00\u07d4d7\x0e\x87 &E\x12Z5\xb2\a\xaf\x121\xfb`r\xf9\xa7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4d=\x9a\xee\u0531\x80\x94~\u04b9 |\xceL=\xdcU\xe1\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4dC\xb8\xaec\x9d\xe9\x1c\xf7\xf0p\xa5G\x03\xb7\x18NH'l\\\x00w\xefK4\x89\x11X\xe4`\x91=\x00\x00\x00\xe0\x94d\xe2\xde! \v\x18\x99\u00e0\xc0e;P@\x13m\r\xc8B\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4d\xec\x8a[t?4y\xe7\a\xda\xe9\xee \u076aO@\xf1\u0649\n\u05ce\xbcZ\xc6 \x00\x00\u07d4e\x03\x86\v\x19\x10\b\xc1U\x83\xbf\u0201X\t\x93\x01v((\x8965\u026d\xc5\u07a0\x00\x00\u07d4e\x051\x911\x9e\x06z%\xe66\x1dG\xf3\u007fc\x18\xf84\x19\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4e\t;#\x9b\xbf\xba#\xc7w\\\xa7\xdaZ\x86H\xa9\xf5L\xf7\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4e\t\xee\xb14~\x84/\xfbA>7\x15^,\xbcs\x82s\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94e\vBUU\xe4\xe4\xc5\x17\x18\x14h6\xa2\xc1\xeew\xa5\xb4!\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4e\f\xf6}\xb0`\xcc\xe1uh\xd5\xf2\xa4#h|Idv\t\x89\x05k\xc7^-c\x10\x00\x00\u07d4e\x10\xdfB\xa5\x99\xbc\xb0\xa5\x19\u0329a\xb4\x88u\x9aogw\x89lk\x93[\x8b\xbd@\x00\x00\u07d4e6u\xb8B\xd7\u0634a\xf7\"\xb4\x11|\xb8\x1d\xac\x8ec\x9d\x89\x01\xae6\x1f\xc1E\x1c\x00\x00\u07d4eK~\x80\x87\x99\xa8=r\x87\xc6w\x06\xf2\xab\xf4\x9aId\x04\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94eORHG\xb3\xa6\xac\xc0\xd3\xd5\xf1\xf3b\xb6\x03\xed\xf6_\x96\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4eY4\u068etN\xaa=\xe3M\xbb\xc0\x89LN\xda\va\xf2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4e]\\\xd7H\x96)\xe2ANIb?\xabb\xa1~M6\x11\x89\x05\fL\xb2\xa1\f`\x00\x00\u07d4e\xaf\x8d\x8b[\x1d\x1e\xed\xfaw\xbc\xbc\x96\xc1\xb13\xf83\x06\u07c9\x05P\x05\xf0\xc6\x14H\x00\x00\u07d4e\xaf\x90\x87\xe0QgqT\x97\u0265\xa7I\x18\x94\x89\x00M\xef\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u0794e\xb4/\xae\xcc\x1e\u07f1B\x83\u0297\x9a\xf5E\xf6;0\xe6\f\x88\xfc\x93c\x92\x80\x1c\x00\x00\u0794e\xd3>\xb3\x9c\xdadS\xb1\x9ea\xc1\xfeM\xb91p\xef\x9d4\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4e\xd8\xddN%\x1c\xbc\x02\x1f\x05\xb0\x10\xf2\xd5\xdcR\f8r\xe0\x89-CW\x9a6\xa9\x0e\x00\x00\u07d4e\xea&\xea\xbb\xe2\xf6L\xcc\xcf\xe0h)\xc2]F7R\x02%\x89%\xf2s\x93=\xb5p\x00\x00\u07d4e\xeag\xad?\xb5j\xd5\xfb\x948}\u04ce\xb3\x83\x00\x1d|h\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94e\xeb\xae\xd2~\u06dd\xcc\x19W\xae\xe5\xf4R\xac!\x05\xa6\\\x0e\x8a\t7\u07ed\xae%\u26c0\x00\u07d4e\xee \xb0m\x9a\u0549\xa7\xe7\xce\x04\xb9\xf5\xf7\x95\xf4\x02\xae\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4e\xf544m/\xfbx\u007f\xa9\xcf\x18]t[\xa4)\x86\xbdn\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94e\xf5\x87\x0f&\xbc\xe0\x89g}\xfc#\xb5\x00\x1e\xe4\x92H4(\x8a\x01\x12\xb1\xf1U\xaa2\xa3\x00\x00\u07d4e\xfd\x02\xd7\x04\xa1*M\xac\xe9G\x1b\x06E\xf9b\xa8\x96q\u0209\x01\x8d\x1c\xe6\xe4'\u0340\x00\u07d4e\xff\x87O\xaf\xceM\xa3\x18\xd6\xc9=W\xe2\u00ca\rs\xe8 \x8968\x02\x1c\xec\u06b0\x00\x00\xe0\x94f\x05W\xbbC\xf4\xbe:\x1b\x8b\x85\xe7\xdf{<[\xcdT\x80W\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4f\b,u\xa8\xde1\xa59\x13\xbb\xd4M\xe3\xa07O\u007f\xaaA\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4f\x11\xceY\xa9\x8b\a*\xe9Y\xdcI\xadQ\x1d\xaa\xaa\xa1\x9dk\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4f \x1b\xd2'\xaem\u01bd\xfe\xd5\xfb\u0781\x1f\xec\xfe^\x9d\u0649 >\x9e\x84\x92x\x8c\x00\x00\u07d4f#4\x81G$\x93[y1\xdd\xcaa\x00\xe0\rFw'\u0349\"\x88&\x9d\a\x83\xd4\x00\x00\u07d4f'O\xea\x82\xcd0\xb6\u009b#5\x0eOO=1\nX\x99\x89p7\x05P\xab\x82\x98\x00\x00\u07d4f,\xfa\x03\x8f\xab7\xa0\x17E\xa3d\u1e41'\xc5\x03tm\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4f5\xb4oq\x1d-\xa6\xf0\xe1cp\u034e\xe4>\xfb,-R\x89lk\x93[\x8b\xbd@\x00\x00\u07d4f6\x04\xb0P0F\xe6$\xcd&\xa8\xb6\xfbGB\xdc\xe0*o\x89\x03\x8b\x9by~\xf6\x8c\x00\x00\u07d4f6\u05ecczH\xf6\x1d8\xb1L\xfdHe\xd3m\x14(\x05\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4f@\xcc\xf0SU\\\x13\n\xe2\xb6Vd~\xa6\xe3\x167\xb9\xab\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4fBK\xd8x[\x8c\xb4a\x10*\x90\x02\x83\xc3]\xfa\a\xefj\x89\x02.-\xb2ff\xfc\x80\x00\u07d4fL\xd6}\xcc\u026c\x82(\xb4\\U\u06cdvU\ve\x9c\u0709\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4fNC\x11\x98p\xaf\x10zD\x8d\xb1'\x8b\x04H8\xff\u036f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4fQso\xb5\x9b\x91\xfe\xe9\xc9:\xa0\xbdn\xa2\xf7\xb2Pa\x80\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4f[\x00\x0f\vw'P\xcc\x89k\x91\x8a\xacIK\x16\x80\x00\xe0\x94g]\\\xaa`\x9b\xf7\n\x18\xac\xa5\x80F]\x8f\xb71\r\x1b\xbb\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4gc F\u0732ZT\x93i(\xa9oB?3 \xcb\ud489lk\x93[\x8b\xbd@\x00\x00\u07d4ge\xdf%(\x0e\x8eO8\u0531\xcfDo\xc5\xd7\xebe\x9e4\x89\x05k\xc7^-c\x10\x00\x00\u07d4gv\xe13\xd9\xdc5L\x12\xa9Q\b{c\x96P\xf59\xa43\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4g\x85Q<\xf72\xe4~\x87g\ap\xb5A\x9b\xe1\f\xd1\xfct\x89lk\x93[\x8b\xbd@\x00\x00\u07d4g\x947\xea\xcfCxx\xdc)=H\xa3\x9c\x87\xb7B\x1a!l\x89\x03\u007f\x81\x82\x1d\xb2h\x00\x00\u07d4g\x9b\x9a\x10\x990Q~\x89\x99\t\x9c\xcf*\x91LL\x8d\xd94\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4g\xa8\x0e\x01\x90r\x1f\x949\rh\x02r\x9d\xd1,1\xa8\x95\xad\x89lk\x13u\xbc\x91V\x00\x00\u07d4g\xb8\xa6\xe9\x0f\xdf\n\x1c\xacD\x17\x930\x1e\x87P\xa9\xfayW\x890\x84\x9e\xbe\x166\x9c\x00\x00\u07d4g\xbc\x85\xe8}\xc3LN\x80\xaa\xfa\x06k\xa8\u049d\xbb\x8eC\x8e\x89\x15\xd1\xcfAv\xae\xba\x00\x00\u07d4g\xc9&\t>\x9b\x89'\x938\x10\u0642\"\xd6.+\x82\x06\xbb\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4g\xcf\xdanp\xbfvW\u04d0Y\xb5\x97\x90\xe5\x14Z\xfd\xbea\x89#\x05\r\tXfX\x00\x00\u07d4g\u0582\xa2\x82\xefs\xfb\x8dn\x90q\xe2aOG\xab\x1d\x0f^\x8965\u026d\xc5\u07a0\x00\x00\u07d4g\u05a8\xaa\x1b\xf8\xd6\xea\xf78N\x99=\xfd\xf1\x0f\n\xf6\x8aa\x89\n\xbc\xbbW\x18\x97K\x80\x00\u07d4g\u0692.\xff\xa4r\xa6\xb1$\xe8N\xa8\xf8k$\xe0\xf5\x15\xaa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4g\xdf$-$\r\u0538\a\x1dr\xf8\xfc\xf3[\xb3\x80\x9dq\xe8\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4g\xee@n\xa4\xa7\xaej:8\x1e\xb4\xed\xd2\xf0\x9f\x17KI(\x898)c_\th\xb0\x00\x00\u07d4g\xf2\xbbx\xb8\xd3\xe1\x1f|E\x8a\x10\xb5\xc8\xe0\xa1\xd3tF}\x89a\t=|,m8\x00\x00\u07d4g\xfcR}\xce\x17\x85\xf0\xfb\x8b\xc7\xe5\x18\xb1\xc6i\xf7\xec\u07f5\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4h\x02}\x19U\x8e\xd73\x9a\b\xae\xe8\xde5Y\xbe\x06>\xc2\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4h\x06@\x83\x8b\xd0zD{\x16\x8dm\x92;\x90\xcflC\xcd\u0289]\u0212\xaa\x111\xc8\x00\x00\u07d4h\a\xdd\u020d\xb4\x89\xb03\xe6\xb2\xf9\xa8\x15SW\x1a\xb3\xc8\x05\x89\x01\x9f\x8euY\x92L\x00\x00\xe0\x94h\rY\x11\xed\x8d\xd9\xee\xc4\\\x06\f\"?\x89\xa7\xf6 \xbb\u054a\x04<3\xc1\x93ud\x80\x00\x00\u07d4h\x11\xb5L\u0456c\xb1\x1b\x94\xda\x1d\xe2D\x82\x85\u035fh\u0649;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4h\x19\f\xa8\x85\xdaB1\x87L\x1c\xfbB\xb1X\n!s\u007f8\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94h(\x97\xbcO\x8e\x89\x02\x91 \xfc\xff\xb7\x87\xc0\x1a\x93\xe6A\x84\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4h)^\x8e\xa5\xaf\xd9\t?\xc0\xa4e\xd1W\x92+]*\xe24\x89\x01\x15NS!}\xdb\x00\x00\u07d4h.\x96'oQ\x8d1\xd7\xe5n0\u07f0\t\xc1!\x82\x01\xbd\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4h5\xc8\xe8\xb7J,\xa2\xae?J\x8d\x0fk\x95J>*\x83\x92\x89\x03B\x9c3]W\xfe\x00\x00\u07d4h63\x01\n\x88hk\xeaZ\x98\xeaS\xe8y\x97\xcb\xf7>i\x89\x05k9Bc\xa4\f\x00\x00\u07d4h=\xba6\xf7\xe9O@\xeaj\xea\ry\xb8\xf5!\xdeU\an\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4hA\x9cm\xd2\xd3\xceo\u02f3\xc7>/\xa0y\xf0`Q\xbd\xe6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4hG;z}\x96Y\x04\xbe\u06e5V\u07fc\x17\x13l\xd5\xd44\x89\x05k\xc7^-c\x10\x00\x00\u07d4hG\x82[\xde\xe8$\x0e(\x04,\x83\xca\xd6B\U000868fd\u0709QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94hJD\xc0i3\x9d\b\xe1\x9auf\x8b\u06e3\x03\xbe\x85S2\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\u07d4hS\x1fM\u0680\x8fS vz\x03\x114(\xca\f\xe2\xf3\x89\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4hy'\xe3\x04\x8b\xb5\x16*\xe7\xc1\\\xf7k\xd1$\xf9I{\x9e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94h\x80\x9a\xf5\xd52\xa1\x1c\x1aMn2\xaa\xc7\\LR\xb0\x8e\xad\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4h\x86\xad\xa7\xbb\xb0a{\u0684!\x91\u018c\x92.\xa3\xa8\xac\x82\x89>\xe2;\xde\x0e} \x00\x00\xe0\x94h\x88>\x15.V`\xfe\xe5\x96&\xe7\xe3\xb4\xf0Q\x10\xe6\"/\x8a\v\x94c;\xe9u\xa6*\x00\x00\u07d4h\x8aV\x9e\x96U$\xeb\x1d\n\xc3\xd3s>\xab\x90\x9f\xb3\xd6\x1e\x89G\x8e\xae\x0eW\x1b\xa0\x00\x00\xe0\x94h\x8e\xb3\x85;\xbc\xc5\x0e\xcf\xee\x0f\xa8\u007f\n\xb6\x93\u02bd\xef\x02\x8a\x06\xb1\n\x18@\x06G\xc0\x00\x00\u07d4h\xa7B_\xe0\x9e\xb2\x8c\xf8n\xb1y>A\xb2\x11\xe5{\u058d\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4h\xa8l@#\x88\xfd\xdcY\x02\x8f\xecp!\u933f\x83\x0e\xac\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\xe0\x94h\xac\u06a9\xfb\x17\xd3\xc3\t\x91\x1aw\xb0_S\x91\xfa\x03N\xe9\x8a\x01\xe5.3l\xde\"\x18\x00\x00\u07d4h\xad\xdf\x01\x9dk\x9c\xabp\xac\xb1?\v1\x17\x99\x9f\x06.\x12\x89\x02\xb5\x12\x12\xe6\xb7\u0200\x00\u07d4h\xb3\x186\xa3\n\x01j\xda\x15{c\x8a\xc1]\xa7?\x18\xcf\u0789\x01h\u048e?\x00(\x00\x00\xe0\x94h\xb6\x85G\x88\xa7\xc6Il\xdb\xf5\xf8K\x9e\xc5\xef9+x\xbb\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4h\xc0\x84\x90\u021b\xf0\u05b6\xf3 \xb1\xac\xa9\\\x83\x12\xc0\x06\b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4h\xc7\xd1q\x1b\x01\x1a3\xf1o\x1fU\xb5\xc9\x02\xcc\xe9p\xbd\u05c9\b=lz\xabc`\x00\x00\u07d4h\xc8y\x1d\xc3B\xc3sv\x9e\xa6\x1f\xb7\xb5\x10\xf2Q\xd3 \x88\x8965\u026d\xc5\u07a0\x00\x00\u07d4h\u07d4|I[\ubbb8\u8273\xf9S\xd53\x87K\xf1\x06\x89\x1d\x99E\xab+\x03H\x00\x00\u07d4h\xe8\x02'@\xf4\xaf)\xebH\xdb2\xbc\xec\xdd\xfd\x14\x8d=\xe3\x8965\u026d\xc5\u07a0\x00\x00\u07d4h\xecy\u057eqUql@\x94\x1cy\u05cd\x17\u079e\xf8\x03\x89\x1b#8w\xb5 \x8c\x00\x00\u07d4h\xee\xc1\u222c1\xb6\xea\xba~\x1f\xbdO\x04\xadW\x9ak]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4h\xf5%\x92\x1d\xc1\x1c2\x9buO\xbf>R\x9f\xc7#\xc84\u0349WG=\x05\u06ba\xe8\x00\x00\u07d4h\xf7\x19\xae4+\xd7\xfe\xf1\x8a\x05\u02f0/pZ\u04ce\u0572\x898\xeb\xad\\\u0710(\x00\x00\xe0\x94h\xf7W<\xd4W\xe1L\x03\xfe\xa4>0-04|\x10p\\\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4h\xf8\xf4QU\xe9\x8cP)\xa4\xeb\u0175'\xa9.\x9f\xa81 \x89\xf0{D\xb4\a\x93 \x80\x00\u07d4h\xfe\x13W!\x8d\tXI\xcdW\x98B\u012a\x02\xff\x88\x8d\x93\x89lk\x93[\x8b\xbd@\x00\x00\u07d4i\x02(\xe4\xbb\x12\xa8\u0535\u09d7\xb0\xc5\xcf*u\t\x13\x1e\x89e\xea=\xb7UF`\x00\x00\u07d4i\x05\x94\xd3\x06a<\xd3\xe2\xfd$\xbc\xa9\x99J\u064a=s\xf8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94i\a2ir\x9ed\x14\xb2n\xc8\xdc\x0f\xd95\xc7;W\x9f\x1e\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x94i\x19\xdd^]\xfb\x1a\xfa@G\x03\xb9\xfa\xea\x8c\xee5\xd0\rp\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4i4\x92\xa5\xc5\x13\x96\xa4\x82\x88\x16i\xcc\xf6\xd8\xd7y\xf0\tQ\x89\x12\xbfPP:\xe3\x03\x80\x00\u07d4i=\x83\xbe\tE\x9e\xf89\v.0\xd7\xf7\u008d\xe4\xb4(N\x89lk\x93[\x8b\xbd@\x00\x00\u07d4iQp\x83\xe3\x03\xd4\xfb\xb6\xc2\x11E\x14!]i\xbcF\xa2\x99\x89\x05k\xc7^-c\x10\x00\x00\u07d4iUPel\xbf\x90\xb7]\x92\xad\x91\"\xd9\r#\xcah\xcaM\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94iX\xf8;\xb2\xfd\xfb'\xce\x04\t\xcd\x03\xf9\xc5\xed\xbfL\xbe\u074a\x04<3\xc1\x93ud\x80\x00\x00\u0794i[\x0fRBu7\x01\xb2d\xa6pq\xa2\u0708\b6\xb8\u06c8\u3601\x1b\xech\x00\x00\xe0\x94i[L\xce\bXV\xd9\xe1\xf9\xff>y\x94 #5\x9e_\xbc\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94if\x06:\xa5\xde\x1d\xb5\xc6q\xf3\xddi\x9dZ\xbe!>\xe9\x02\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4it\u0224\x14\u03ae\xfd<.M\xfd\xbe\xf40V\x8d\x9a\x96\v\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\xe0\x94iximQP\xa9\xa2cQ?\x8ft\u0196\xf8\xb19|\xab\x8a\x01g\xf4\x82\xd3\u0171\xc0\x00\x00\xe0\x94iy{\xfb\x12\u027e\u0582\xb9\x1f\xbcY5\x91\xd5\xe4\x027(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94i\u007fUSk\xf8Z\xdaQ\x84\x1f\x02\x87b:\x9f\x0e\u041a\x17\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4i\x82\xfe\x8a\x86~\x93\xebJ\v\xd0QX\x93\x99\xf2\xec\x9aR\x92\x89lk\x93[\x8b\xbd@\x00\x00\u07d4i\x8a\x8ao\x01\xf9\xabh/c|yi\xbe\x88_lS\x02\xbf\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4i\x8a\xb9\xa2\xf33\x81\xe0|\fGC=\r!\xd6\xf36\xb1'\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4i\x94\xfb21\xd7\xe4\x1dI\x1a\x9dh\xd1\xfaL\xae,\xc1Y`\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\x9c\x9e\xe4q\x95Q\x1f5\xf8b\xcaL\"\xfd5\xae\x8f\xfb\xf4\x89\x04V9\x18$O@\x00\x00\u07d4i\x9f\xc6\u058aGuW<\x1d\u036e\xc80\xfe\xfdP9|N\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4i\xaf(\xb0tl\xac\r\xa1p\x84\xb99\x8c^6\xbb:\r\xf2\x896w\x03n\xdf\n\xf6\x00\x00\u07d4i\xb8\x0e\xd9\x0f\x84\x83J\xfa?\xf8.\xb9dp;V\tw\u0589\x01s\x17\x90SM\xf2\x00\x00\xe0\x94i\xb8\x1dY\x81\x14\x1e\u01e7\x14\x10`\xdf\u03cf5\x99\xff\xc6>\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4i\xbc\xfc\x1dC\xb4\xba\x19\xde{'K\xdf\xfb5\x13\x94\x12\xd3\u05c95e\x9e\xf9?\x0f\xc4\x00\x00\u07d4i\xbd%\xad\xe1\xa34lY\xc4\xe90\xdb*\x9dq^\xf0\xa2z\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\xc0\x8dtGT\xdep\x9c\xe9n\x15\xae\r\x1d9[:\"c\x8965\u026d\xc5\u07a0\x00\x00\u07d4i\xc2\xd85\xf1>\xe9\x05\x80@\x8ej2\x83\xc8\u0326\xa44\xa2\x89#\x8f\xd4,\\\xf0@\x00\x00\u07d4i\xc9N\a\u0129\xbe3\x84\xd9]\xfa<\xb9)\x00Q\x87;{\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4i\xcb>!S\x99\x8d\x86\xe5\xee \xc1\xfc\u0466\xba\uec86?\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\u04ddQ\b\x89\xe5R\xa3\x96\x13[\xfc\xdb\x06\xe3~8v3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94i\u064f8\xa3\xba=\xbc\x01\xfa\\,\x14'\xd8b\x83//p\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94i\xe2\xe2\xe7\x040|\xcc[\\\xa3\xf1d\xfe\xce.\xa7\xb2\xe5\x12\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4i\xffB\x90t\u02dblc\xbc\x91B\x84\xbc\xe5\xf0\xc8\xfb\xf7\u0409\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4i\xff\x89\x01\xb5Av?\x81|_)\x98\xf0-\xcf\xc1\xdf)\x97\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4j\x02:\xf5}XM\x84^i\x876\xf10\u06dd\xb4\r\xfa\x9a\x89\x05[ \x1c\x89\x00\x98\x00\x00\u07d4j\x04\xf5\xd5?\xc0\xf5\x15\xbe\x94+\x8f\x12\xa9\xcbz\xb0\xf3\x97x\x89\xa9\xaa\xb3E\x9b\xe1\x94\x00\x00\u07d4j\x05\xb2\x1cO\x17\xf9\xd7?_\xb2\xb0\u02c9\xffSV\xa6\xcc~\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94j\x0f\x05`f\xc2\xd5f(\x85\x02s\xd7\xec\xb7\xf8\xe6\xe9\x12\x9e\x8a\x01\x0f\r)<\u01e5\x88\x00\x00\u07d4j\x13\xd5\xe3,\x1f\xd2m~\x91\xffn\x051`\xa8\x9b,\x8a\xad\x89\x02\xe6/ \xa6\x9b\xe4\x00\x00\u07d4j.\x86F\x9a[\xf3|\xee\x82\xe8\x8bL8c\x89](\xfc\xaf\x89\x1c\"\x92f8[\xbc\x00\x00\u07d4j6\x94BL|\u01b8\xbc\u067c\u02baT\f\xc1\xf5\xdf\x18\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94jB\u0297\x1cex\u056d\xe2\x95\xc3\xe7\xf4\xad3\x1d\xd3BN\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4jD\xaf\x96\xb3\xf02\xaed\x1b\xebg\xf4\xb6\xc83B\xd3|]\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4jL\x89\a\xb6\x00$\x80W\xb1\xe4cT\xb1\x9b\u0705\x9c\x99\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4jQNbB\xf6\xb6\x8c\x13~\x97\xfe\xa1\u73b5U\xa7\xe5\xf7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4jS\xd4\x1a\xe4\xa7R\xb2\x1a\xbe\xd57FI\x95:Q=\xe5\xe5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4jaY\aJ\xb5s\xe0\xeeX\x1f\x0f=\xf2\u05a5\x94b\x9bt\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4jc7\x83?\x8fjk\xf1\f\xa7\xec!\xaa\x81\x0e\xd4D\xf4\u02c97\xbd$4\\\xe8\xa4\x00\x00\u07d4jcS\xb9qX\x9f\x18\xf2\x95\\\xba(\xab\xe8\xac\xcejWa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4jc\xfc\x89\xab\xc7\xf3n(-\x80x{{\x04\xaf\xd6U>q\x89\b\xacr0H\x9e\x80\x00\x00\u07d4jg\x9e7\x8f\xdc\xe6\xbf\xd9\u007f\xe6/\x04)Z$\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4j\x8c\xea-\xe8J\x8d\xf9\x97\xfd?\x84\xe3\b=\x93\xdeW\u0369\x89\x05k\xe0<\xa3\xe4}\x80\x00\xe0\x94j\x97Xt;`>\xea:\xa0RKB\x88\x97#\xc4\x159H\x8a\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4j\xa5s/;\x86\xfb\x8c\x81\xef\xbek[G\xb5cs\v\x06\u020965\u026d\xc5\u07a0\x00\x00\u07d4j\xb3#\xaePV\xed\nE0r\u016b\xe2\xe4/\xcf]q9\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4j\xb5\xb4\xc4\x1c\u0778)i\f/\xda\u007f \xc8^b\x9d\xd5\u0549d\u052fqL2\x90\x00\x00\u07d4j\xc4\x0fS-\xfe\xe5\x11\x81\x17\u04ad5-\xa7}Om\xa2\u0209\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4j\xc4\u053e-\xb0\u065d\xa3\xfa\xaa\xf7RZ\xf2\x82\x05\x1dj\x90\x89\x04X\xcaX\xa9b\xb2\x80\x00\u07d4j\xcd\u0723\xcd+I\x90\xe2\\\xd6\\$\x14\x9d\t\x12\t\x9ey\x89\xa2\xa1\xe0|\x9fl\x90\x80\x00\u07d4j\xd9\v\xe2R\xd9\xcdFM\x99\x81%\xfa\xb6\x93\x06\v\xa8\xe4)\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4j\u0753!\x93\xcd8IJ\xa3\xf0:\xec\xccKz\xb7\xfa\xbc\xa2\x89\x04\xdbs%Gc\x00\x00\x00\xe0\x94j\xe5\u007f'\x91|V*\x13*M\x1b\xf7\xec\n\u01c5\x83)&\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4j\xeb\x9ftt.\xa4\x91\x81=\xbb\xf0\xd6\xfc\xde\x1a\x13\x1dM\xb3\x89\x17\xe5T0\x8a\xa00\x00\x00\u07d4j\xf25\u04bb\xe0P\xe6)\x16\x15\xb7\x1c\xa5\x82\x96X\x81\x01B\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4j\xf6\xc7\xee\x99\xdf'\x1b\xa1[\xf3\x84\xc0\xb7d\xad\xcbM\xa1\x82\x8965f3\xeb\xd8\xea\x00\x00\u07d4j\xf8\xe5Yih,q_H\xadO\xc0\xfb\xb6~\xb5\x97\x95\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4j\xf9@\xf6>\u0278\xd8v'*\u0296\xfe\xf6\\\xda\xce\xcd\ua262\xa1]\tQ\x9b\xe0\x00\x00\u07d4j\xf9\xf0\xdf\uebbb_d\xbf\x91\xabw\x16i\xbf\x05)US\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4j\xff\x14f\xc2b6u\xe3\xcb\x0eu\xe4#\xd3z%\xe4B\xeb\x89]\u0212\xaa\x111\xc8\x00\x00\xe0\x94k\r\xa2Z\xf2g\u05c3l\"k\xca\xe8\xd8r\xd2\xceR\xc9A\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4k\x10\xf8\xf8\xb3\xe3\xb6\r\xe9\n\xa1-\x15_\x9f\xf5\xff\xb2,P\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\x17Y\x8a\x8e\xf5Oyz\xe5\x15\u0336Q}\x18Y\xbf\x80\x11\x89\x05k\xc7^-c\x10\x00\x00\u07d4k \xc0\x80`jy\xc7;\xd8\xe7[\x11qzN\x8d\xb3\xf1\u00c9\x10?sX\x03\xf0\x14\x00\x00\u07d4k\"\x84D\x02!\xce\x16\xa88-\xe5\xff\x02)G\"i\xde\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4k0\xf1\x829\x10\xb8m:\xcbZj\xfc\x9d\xef\xb6\xf3\xa3\v\xf8\x89\u3bb5sr@\xa0\x00\x00\u07d4k8\u0784\x1f\xad\u007fS\xfe\x02\xda\x11[\xd8j\xaff$f\xbd\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4kK\x99\xcb?\xa9\xf7\xb7L\u3903\x17\xb1\xcd\x13\t\n\x1az\x89\x03\x1b2~i]\xe2\x00\x00\u07d4kZ\xe7\xbfx\xecu\xe9\f\xb5\x03\xc7x\xcc\u04f2KO\x1a\xaf\x89+^:\xf1k\x18\x80\x00\x00\u07d4kc\xa2\u07f2\xbc\xd0\xca\xec\x00\"\xb8\x8b\xe3\f\x14Q\xeaV\xaa\x89+\xdbk\xf9\x1f\u007fL\x80\x00\u07d4kew\xf3\x90\x9aMm\xe0\xf4\x11R-Ep8d\x004\\\x89e\xea=\xb7UF`\x00\x00\u07d4kr\xa8\xf0a\xcf\xe6\x99j\xd4G\xd3\xc7,(\xc0\xc0\x8a\xb3\xa7\x89\xe7\x8cj\u01d9\x12b\x00\x00\u07d4kv\rHw\xe6\xa6'\xc1\xc9g\xbe\xe4Q\xa8P}\xdd\u06eb\x891T\xc9r\x9d\x05x\x00\x00\u07d4k\x83\xba\xe7\xb5e$EXU[\xcfK\xa8\xda \x11\x89\x1c\x17\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\x92]\xd5\xd8\xeda2\xabm\b`\xb8,D\xe1\xa5\x1f\x1f\xee\x89P; >\x9f\xba \x00\x00\xe0\x94k\x94a]\xb7Pej\u00cc~\x1c\xf2\x9a\x9d\x13g\u007fN\x15\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4k\x95\x1aC'N\xea\xfc\x8a\t\x03\xb0\xaf.\xc9+\xf1\xef\xc89\x89\x05k\xc7^-c\x10\x00\x00\u07d4k\x99%!\xec\x85#p\x84\x8a\u0597\xcc-\xf6Nc\xcc\x06\xff\x8965\u026d\xc5\u07a0\x00\x00\u07d4k\xa8\xf7\xe2_\xc2\xd8qa\x8e$\xe4\x01\x84\x19\x917\xf9\xf6\xaa\x89\x15\xafd\x86\x9ak\xc2\x00\x00\u07d4k\xa9\xb2\x1b5\x10k\xe1Y\xd1\xc1\xc2ez\xc5l\u049f\xfdD\x89\xf2\xdc}G\xf1V\x00\x00\x00\u07d4k\xafz*\x02\xaex\x80\x1e\x89\x04\xadz\xc0Q\b\xfcV\xcf\xf6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94k\xb2\xac\xa2?\xa1bm\x18\xef\xd6w\u007f\xb9}\xb0-\x8e\n\xe4\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4k\xb4\xa6a\xa3:q\xd4$\u051b\xb5\xdf(b.\xd4\xdf\xfc\xf4\x89\",\x8e\xb3\xfff@\x00\x00\u07d4k\xb5\b\x13\x14j\x9a\xddB\xee\"\x03\x8c\x9f\x1fti\xd4\u007fG\x89\n\xdaUGK\x814\x00\x00\u07d4k\xbc?5\x8af\x8d\u0461\x1f\x03\x80\xf3\xf71\bBj\xbdJ\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4k\xbd\x1eq\x93\x90\xe6\xb9\x10C\xf8\xb6\xb9\u07c9\x8e\xa8\x00\x1b4\x89llO\xa6\xc3\xdaX\x80\x00\u07d4k\xc8Z\xcdY(r.\xf5\tS1\xee\x88\xf4\x84\xb8\u03c3W\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4k\xd3\xe5\x9f#\x9f\xaf\xe4wk\xb9\xbd\xddk\ue0fa]\x9d\x9f\x8965\u026d\xc5\u07a0\x00\x00\u07d4k\xd4W\xad\xe0Qy]\xf3\xf2F\\89\xae\xd3\xc5\xde\xe9x\x8964\xbf9\xab\x98x\x80\x00\u07d4k\xe1c\x13d>\xbc\x91\xff\x9b\xb1\xa2\xe1\x16\xb8T\xea\x93:E\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4k\xe7Y^\xa0\xf0hH\x9a'\x01\xecFI\x15\x8d\xdcC\xe1x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\xe9\x03\x0e\xe6\xe2\xfb\u0111\xac\xa3\xde@\"\xd3\x01w+{}\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94k\xec1\x1a\xd0P\b\xb4\xaf5<\x95\x8c@\xbd\x06s\x9a?\xf3\x8a\x03w\xf6*\x0f\nbp\x00\x00\u07d4k\xf7\xb3\xc0e\xf2\xc1\xe7\xc6\xeb\t+\xa0\xd1Pf\xf3\x93\u0478\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4k\xf8o\x1e/+\x802\xa9\\Mw8\xa1\t\xd3\xd0\xed\x81\x04\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4l\x05\xe3N^\xf2\xf4.\u041d\xef\xf1\x02l\xd6k\xcbi`\xbb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4l\b\xa6\xdc\x01s\xc74)U\xd1\xd3\xf2\xc0e\xd6/\x83\xae\u01c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4l\n\xe9\xf0C\xc84\xd4Bq\xf14\x06Y=\xfe\tO8\x9f\x89RD*\xe13\xb6*\x80\x00\u07d4l\f\xc9\x17\xcb\xee}|\t\x97c\xf1Nd\xdf}4\xe2\xbf\t\x89\r\x8drkqw\xa8\x00\x00\xe0\x94l\x0eq/@\\Yr_\xe8)\xe9wK\xf4\xdf\u007fM\xd9e\x8a\f(h\x88\x9c\xa6\x8aD\x00\x00\xe0\x94l\x10\x12\x05\xb3#\xd7uD\xd6\xdcR\xaf7\xac\xa3\xce\xc6\xf7\xf1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4l\x15\xec5 \xbf\x8e\xbb\xc8 \xbd\x0f\xf1\x97x7T\x94\u03dd\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\xe0\x94l\x1d\xdd3\xc8\x19f\u0706!w`q\xa4\x12\x94\x82\xf2\xc6_\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4l%2\u007f\x8d\u02f2\xf4^V\x1e\x86\xe3]\x88P\xe5:\xb0Y\x89;\xcd\xf9\xba\xfe\xf2\xf0\x00\x00\u07d4l.\x9b\xe6\u052bE\x0f\xd1%1\xf3?\x02\x8caFt\xf1\x97\x89\xc2\x12z\xf8X\xdap\x00\x00\u07d4l5\x9eX\xa1=Ex\xa93\x8e3\\g\xe7c\x9f_\xb4\u05c9\v\xd1[\x94\xfc\x8b(\x00\x00\u07d4l=\x18pA&\xaa\x99\xee3B\xce`\xf5\xd4\xc8_\x18g\u0349\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4lGK\xc6jTx\x00f\xaaOQ.\xef\xa7s\xab\xf9\x19\u01c9\x05\x18\x83\x15\xf7v\xb8\x00\x00\u07d4lNBn\x8d\xc0\x05\u07e3Ql\xb8\xa6\x80\xb0.\ua56e\x8e\x89Hz\x9a0E9D\x00\x00\u07d4lR\xcf\b\x95\xbb5\xe6V\x16\x1eM\xc4j\xe0\xe9m\xd3\xe6,\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4lT\"\xfbK\x14\xe6\u064b`\x91\xfd\xecq\xf1\xf0\x86@A\x9d\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4l\\:T\u0367\xc2\xf1\x18\xed\xbaCN\xd8\x1en\xbb\x11\xddz\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4lc\xf8EV\u0490\xbf\u0359\xe44\ue657\xbf\xd7yWz\x89lk\x93[\x8b\xbd@\x00\x00\u07d4lc\xfc\x85\x02\x9a&T\u05db+\xeaM\xe3I\xe4REw\u0149#\xc7W\a+\x8d\xd0\x00\x00\u07d4led\xe5\xc9\xc2N\xaa\xa7D\xc9\xc7\xc9h\xc9\xe2\xc9\xf1\xfb\xae\x89I\x9bB\xa2\x119d\x00\x00\xe0\x94lg\xd6\xdb\x1d\x03Ql\x12\x8b\x8f\xf24\xbf=I\xb2m)A\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4lg\xe0\u05f6.*\bPiE\xa5\xdf\xe3\x82c3\x9f\x1f\"\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4lj\xa0\xd3\vdr\x19\x90\xb9PJ\x86?\xa0\xbf\xb5\xe5}\xa7\x89\x92^\x06\xee\xc9r\xb0\x00\x00\u07d4lqJX\xff\xf6\xe9}\x14\xb8\xa5\xe3\x05\xeb$@eh\x8b\xbd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4l\x80\rKI\xba\a%\x04`\xf9\x93\xb8\xcb\xe0\v&j%S\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4l\x80\x8c\xab\xb8\xff_\xbbc\x12\xd9\xc8\xe8J\xf8\xcf\x12\xef\bu\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94l\x82 )!\x8a\xc8\xe9\x8a&\f\x1e\x06@)4\x889\x87[\x8a\x01\x0f\x97\xb7\x87\xe1\xe3\b\x00\x00\u07d4l\x84\u02e7|m\xb4\xf7\xf9\x0e\xf1=^\xe2\x1e\x8c\xfc\u007f\x83\x14\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94l\x86\x87\xe3Aw\x10\xbb\x8a\x93U\x90!\xa1F\x9ej\x86\xbcw\x8a\x02[-\xa2x\xd9k{\x80\x00\xe0\x94l\x88,'s,\xef\\|\x13\xa6\x86\xf0\xa2\xeawUZ\u0089\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4l\xa5\xde\x00\x81}\xe0\xce\xdc\xe5\xfd\x00\x01(\xde\xde\x12d\x8b<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4l\xa6\xa12\xce\x1c\u0488\xbe\xe3\x0e\xc7\xcf\xef\xfb\x85\xc1\xf5\nT\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94l\xb1\x1e\xcb2\xd3\u0382\x96\x011\x066\xf5\xa1\f\xf7\u03db_\x8a\x04?\u851c8\x01\xf5\x00\x00\u07d4l\xc1\xc8x\xfal\u078a\x9a\v\x83\x11$~t\x1eFB\xfem\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94l\xcb\x03\xac\xf7\xf5<\xe8z\xad\xcc!\xa9\x93-\xe9\x15\xf8\x98\x04\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4l\xd2\x12\xae\xe0N\x01?=*\xba\u04a0#`k\xfb\\j\u01c9lj\xccg\u05f1\xd4\x00\x00\u07d4l\xd2(\xdcq!i0\u007f\xe2|\xebtw\xb4\x8c\xfc\x82r\xe5\x89\x044\xea\x94\u06caP\x00\x00\u07d4l\xe1\xb0\xf6\xad\xc4pQ\xe8\xab8\xb3\x9e\xdbA\x86\xb0;\xab\u0309Ay\x97\x94\xcd$\xcc\x00\x00\u07d4l\xea\xe3s=\x8f\xa4=l\xd8\f\x1a\x96\xe8\xeb\x93\x10\x9c\x83\xb7\x89\x10'\x94\xad \xdah\x00\x00\u07d4m\x05i\xe5U\x8f\xc7\xdf'f\xf2\xba\x15\u070a\xef\xfc[\xebu\x89\xd8\xe6\x00\x1el0+\x00\x00\u07d4m\x12\x0f\f\xaa\xe4O\xd9K\xca\xfeU\xe2\xe2y\uf5ba\\z\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4m\x14V\xff\xf0\x10N\xe8D\xa31G7\x8438\xd2L\xd6l\x89\a\xb0l\xe8\u007f\xddh\x00\x00\u07d4m \xef\x97\x04g\nP\v\xb2i\xb5\x83.\x85\x98\x02\x04\x9f\x01\x89\a\f\x1c\xc7;\x00\xc8\x00\x00\xe0\x94m/\x97g4\xb9\xd0\a\r\x18\x83\xcfz\u02b8\xb3\xe4\x92\x0f\xc1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4m9\xa9\u93c1\xf7i\xd7:\xad,\xea\xd2v\xac\x13\x87\xba\xbe\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4m;x6\xa2\xb9\u0619r\x1aM#{R#\x85\xdc\xe8\xdf\u034966\xc2^f\xec\xe7\x00\x00\u07d4m?+\xa8V\u033b\x027\xfava\x15k\x14\xb0\x13\xf2\x12@\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94m@\b\xb4\xa8\x88\xa8&\xf2H\xeej\v\r\xfd\xe9\xf92\x10\xb9\x8a\x01'\xfc\xb8\xaf\xae \xd0\x00\x00\u07d4m@\xca'\x82m\x97s\x1b>\x86\xef\xfc\u05f9*Aa\xfe\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4mD\x97J1\u0447\xed\xa1m\xddG\xb9\xc7\xecP\x02\xd6\x1f\xbe\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94mK\\\x05\xd0j \x95~\x17H\xabm\xf2\x06\xf3C\xf9/\x01\x8a\x02\x1f6\x06\x99\xbf\x82_\x80\x00\xe0\x94mL\xbf=\x82\x84\x83:\xe9\x93D0>\b\xb4\xd6\x14\xbf\xda;\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4mY\xb2\x1c\xd0\xe2t\x88\x04\u066b\xe0d\xea\u00be\xf0\xc9_'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4mc\u04ce\xe8\xb9\x0e\x0en\xd8\xf1\x92\xed\xa0Q\xb2\u05a5\x8b\xfd\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4mf4\xb5\xb8\xa4\x01\x95\xd9I\x02z\xf4\x82\x88\x02\t,\ued89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94m}\x1c\x94\x95\x11\xf8\x83\x03\x80\x8c`\xc5\xea\x06@\xfc\xc0&\x83\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4m\x84m\xc1&W\xe9\x1a\xf2P\bQ\x9c>\x85\u007fQp}\u0589\xf8\xd3\v\xc9#B\xf8\x00\x00\u07d4m\x91\x93\x99k\x19F\x17!\x11\x06\xd1c^\xb2l\u0136ll\x89\x15\xaa\x1e~\x9d\xd5\x1c\x00\x00\u07d4m\x99\x97P\x98\x82\x02~\xa9G#\x14$\xbe\xde\xde)e\u043a\x89l\x81\u01f3\x11\x95\xe0\x00\x00\u07d4m\xa0\xed\x8f\x1di3\x9f\x05\x9f*\x0e\x02G\x1c\xb4O\xb8\u00fb\x892\xbc8\xbbc\xa8\x16\x00\x00\u07d4m\xb7+\xfdC\xfe\xf4e\xcaV2\xb4Z\xabra@N\x13\xbf\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94m\xbe\x8a\xbf\xa1t(\x06&9\x817\x1b\xf3\xd3U\x90\x80kn\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4m\xc3\xf9+\xaa\x1d!\u06b78+\x892a\xa05o\xa7\xc1\x87\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4m\xc7\x05:q\x86\x16\xcf\u01cb\xeec\x82\xeeQ\xad\xd0\xc7\x030\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94m\xcc~d\xfc\xaf\xcb\xc2\xdcl\x0e^f,\xb3G\xbf\xfc\xd7\x02\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4m\xda_x\x8alh\x8d\u07d2\x1f\xa3\x85.\xb6\xd6\xc6\xc6)f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4m\xdb`\x92w\x9dXB\xea\xd3x\xe2\x1e\x81 \xfdLk\xc12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4m\xdf\xefc\x91U\u06ab\n\\\xb4\x95:\xa8\u016f\xaa\x88\x04S\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4m\xe0/-\xd6~\xfd\xb794\x02\xfa\x9e\xaa\xcb\xcfX\x9d.V\x89@\x13\x8b\x91~\u07f8\x00\x00\u07d4m\u4d418\\\xf7\xfc\x9f\xe8\xc7}\x13\x1f\xe2\xeew$\xc7j\x89})\x97s=\xcc\xe4\x00\x00\u07d4m\xe4\xd1R\x19\x18/\xaf:\xa2\xc5\xd4\xd2Y_\xf20\x91\xa7'\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4m\xed\xf6.t?M,*K\x87\xa7\x87\xf5BJz\xeb9<\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4m\xf2Of\x85\xa6/y\x1b\xa37\xbf?\xf6~\x91\xf3\u053c:\x89ukI\xd4\nH\x18\x00\x00\u07d4m\xf5\xc8O{\x90\x9a\xab>a\xfe\x0e\xcb\x1b;\xf2`\"*\u0489\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4m\xff\x90\xe6\xdc5\x9d%\x90\x88+\x14\x83\xed\xbc\xf8\x87\xc0\xe4#\x8965\u026d\xc5\u07a0\x00\x00\u07d4n\x01\xe4\xadV\x9c\x95\xd0\a\xad\xa3\r^-\xb1(\x88I\"\x94\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\a;f\u0478\xc6gD\u0600\x96\xa8\u0759\xec~\x02(\u0689\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\x0e\xe7\x06\x12\xc9v(}I\x9d\u07e6\xc0\xdc\xc1,\x06\xde\xea\x89\a\v\u0579V!F\x00\x00\xe0\x94n\x12\xb5\x1e\"[JCr\xe5\x9a\u05e2\xa1\xa1>\xa3\u04e17\x8a\x03\x00F\xc8\xccw_\x04\x00\x00\u07d4n\x1a\x04l\xaf[JW\xf4\xfdK\xc1sb!&\xb4\xe2\xfd\x86\x89a\t=|,m8\x00\x00\u07d4n\x1e\xa4\xb1\x83\xe2R\u027bwg\xa0\x06\u05346\x96\u02ca\xe9\x89\x0f\xf3x<\x85\xee\u0400\x00\u07d4n%[p\n\xe7\x13\x8aK\xac\xf2(\x88\xa9\xe2\xc0\n(^\xec\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n'\n\xd5)\xf1\xf0\xb8\xd9\xcbm$'\xec\x1b~-\xc6Jt\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4n.\xab\x85\u0709\xfe)\xdc\n\xa1\x852G\u06b4:R=V\x89\x04V9\x18$O@\x00\x00\u07d4n:Q\xdbt=3M/\xe8\x82$\xb5\xfe|\x00\x8e\x80\xe6$\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4nL*\xb7\xdb\x02i9\xdb\u04fch8J\xf6`\xa6\x18\x16\xb2\x89\t\r\x97/22<\x00\x00\u07d4nM.9\u0203f)\u5d07\xb1\x91\x8af\x9a\xeb\u07556\x8965\u026d\xc5\u07a0\x00\x00\u07d4n\\-\x9b\x1cTj\x86\xee\xfd]\nQ \xc9\xe4\xe70\x19\x0e\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4n`\xae\u19cf\x8e\u068bBLs\xe3S5J\xe6|0B\x89\xbd5\xa4\x8d\x99\x19\xe6\x00\x00\u07d4nd\xe6\x12\x9f\"N7\x8c\x0ensj~z\x06\xc2\x11\xe9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4nm[\xbb\xb9\x05;\x89\xd7D\xa2s\x16\u00a7\xb8\xc0\x9bT}\x891Rq\n\x02>m\x80\x00\u07d4nr\xb2\xa1\x18j\x8e)\x16T;\x1c\xb3jh\x87\x0e\xa5\u0457\x89\n\x15D\xbe\x87\x9e\xa8\x00\x00\u07d4nv\x1e\xaa\x0f4_w{TA\xb7:\x0f\xa5\xb5k\x85\xf2-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4ny\xed\u0504[\anL\u060d\x18\x8bnC-\xd9?5\xaa\x893\xc5I\x901r\f\x00\x00\u07d4n\x82\x12\xb7\"\xaf\xd4\b\xa7\xa7>\xd3\xe29^\xe6EJ\x030\x89\b\x9e\x91y\x94\xf7\x1c\x00\x00\u07d4n\x84\x87m\xbb\x95\xc4\vfV\xe4+\xa9\xae\xa0\x8a\x99;T\u0709;\xbc`\xe3\xb6\u02fe\x00\x00\u07d4n\x84\xc2\xfd\x18\xd8\tW\x14\xa9h\x17\x18\x9c\xa2\x1c\xcab\xba\xb1\x89\x12{lp&!\u0340\x00\u07d4n\x86m\x03-@Z\xbd\xd6\\\xf6QA\x1d\x807\x96\xc2#\x11\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94n\x89\x9eY\xa9\xb4\x1a\xb7\xeaA\xdfu\x17\x86\x0f*\xcbY\xf4\xfd\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4n\x89\xc5\x1e\xa6\xde\x13\xe0l\xdct\x8bg\xc4A\x0f\u9f2b\x03\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\x8a&h\x9fz/\xde\xfd\x00\x9c\xba\xaaS\x10%4P\u06ba\x89o!7\x17\xba\xd8\xd3\x00\x00\u07d4n\x96\xfa\xed\xa3\x05C\x02\xc4_X\xf1a2L\x99\xa3\xee\xbbb\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4n\xb0\xa5\xa9\xae\x96\xd2,\xf0\x1d\x8f\xd6H;\x9f8\xf0\x8c,\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xb3\x81\x96\x17@@X&\x8f\f<\xff5\x96\xbf\xe9\x14\x8c\x1c\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94n\xb5W\x8ak\xb7\xc3!S\x19[\r\x80 \xa6\x91HR\xc0Y\x8a\x8b\u00ab\xf4\x02!\xf4\x80\x00\x00\u07d4n\xbb^iW\xaa\x82\x1e\xf6Y\xb6\x01\x8a9:PL\xaeDP\x89lk\x93[\x8b\xbd@\x00\x00\u07d4n\xbc\xf9\x95\u007f_\xc5\u916d\xd4u\";\x04\xb8\xc1Jz\xed\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4n\xc3e\x95q\xb1\x1f\x88\x9d\xd49\xbc\xd4\xd6u\x10\xa2[\xe5~\x89\x06\xaa\xf7\xc8Qm\f\x00\x00\u07d4n\u021b9\xf9\xf5'jU>\x8d\xa3\x0en\xc1z\xa4~\xef\u01c9\x18BO_\v\x1bN\x00\x00\u07d4n\xc9m\x13\xbd\xb2M\u01e5W)?\x02\x9e\x02\xddt\xb9zU\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xca\xef\xa6\xfc>\xe54bm\xb0,o\x85\xa0\u00d5W\x1ew\x89 \x86\xac5\x10R`\x00\x00\u07d4n\u04a1+\x02\xf8\u0188\u01f5\u04e6\xea\x14\xd66\x87\u06b3\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4n\u0604E\x9f\x80\x9d\xfa\x10\x16\xe7p\xed\xaf>\x9f\xefF\xfa0\x89\xb8R\xd6x \x93\xf1\x00\x00\xe0\x94n\xdf\u007fR\x83r\\\x95>\xe6C\x17\xf6a\x88\xaf\x11\x84\xb03\x8a\x01\xb4d1\x1dE\xa6\x88\x00\x00\u07d4n\xe8\xaa\xd7\xe0\xa0e\u0605-|;\x9an_\xdcK\xf5\f\x00\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4n\xef\u0705\x0e\x87\xb7\x15\xc7'\x91w<\x03\x16\xc3U\x9bX\xa4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xf9\xe8\u0276!}Vv\x9a\xf9}\xbb\x1c\x8e\x1b\x8b\xe7\x99\u0489\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4n\xfb\xa8\xfb*\u0176s\a)\xa9r\xec\"D&\xa2\x87\u00ed\x89\x0fY\x85\xfb\xcb\xe1h\x00\x00\xe0\x94n\xfd\x90\xb55\xe0\v\xbd\x88\x9f\xda~\x9c1\x84\xf8y\xa1Q\u06ca\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4o\x05\x16f\xcbO{\u04b1\x90r!\xb8)\xb5U\u05e3\xdbt\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4o\x0e\xdd#\xbc\xd8_`\x15\xf9(\x9c(\x84\x1f\xe0L\x83\xef\xeb\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4o\x13zq\xa6\xf1\x97\xdf,\xbb\xf0\x10\u073d\x89a\t=|,m8\x00\x00\u07d4p\x10\xbe-\xf5{\u042b\x9a\xe8\x19l\xd5\n\xb0\xc5!\xab\xa9\xf9\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4p#\xc7\tV\xe0J\x92\xd7\x00%\xaa\u0497\xb59\xaf5Xi\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p%\x96]+\x88\xda\x19}DY\xbe=\xc98cD\xcc\x1f1\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d4p(\x02\xf3m\x00%\x0f\xabS\xad\xbc\u0596\xf0\x17oc\x8aI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4pH\x19\xd2\xe4Mn\xd1\xda%\xbf\u0384\u011f\u0322V\x13\xe5\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4pJn\xb4\x1b\xa3O\x13\xad\xdd\xe7\xd2\xdb}\xf0I\x15\u01e2!\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4pJ\xb1\x15\r^\x10\xf5\xe3I\x95\b\xf0\xbfpe\x0f\x02\x8dK\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4pJ\xe2\x1dv-n\x1d\xde(\xc25\xd11\x04Yr6\xdb\x1a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4pM$<)x\xe4l,\x86\xad\xbe\xcd$n;)_\xf63\x89m\x12\x1b\xeb\xf7\x95\xf0\x00\x00\u07d4pM]\xe4\x84m9\xb5<\xd2\x1d\x1cI\xf0\x96\xdb\\\x19\xba)\x89\b=lz\xabc`\x00\x00\u07d4p]\xdd85T\x82\xb8\xc7\u04f5\x15\xbd\xa1P\r\xd7\u05e8\x17\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94pan(\x92\xfa&\x97\x05\xb2\x04k\x8f\xe3\xe7/\xa5X\x16\u04ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4pg\x0f\xbb\x05\xd30\x14DK\x8d\x1e\x8ew\x00%\x8b\x8c\xaam\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\x81\xfak\xaa\xd6\u03f7\xf5\x1b,\xca\x16\xfb\x89p\x99\x1ad\xba\x89\f\xae\xc0\x05\xf6\xc0\xf6\x80\x00\xe0\x94p\x85\xae~~M\x93!\x97\xb5\u01c5\x8c\x00\xa3gF&\xb7\xa5\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4p\x86\xb4\xbd\xe3\xe3]J\xeb$\xb8%\xf1\xa2\x15\xf9\x9d\x85\xf7E\x89lh\xcc\u041b\x02,\x00\x00\u07d4p\x8a*\xf4%\u03b0\x1e\x87\xff\xc1\xbeT\xc0\xf52\xb2\x0e\xac\u0589\aE\u0503\xb1\xf5\xa1\x80\x00\u07d4p\x8e\xa7\a\xba\xe45\u007f\x1e\xbe\xa9Y\u00e2P\xac\u05aa!\xb3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794p\x8f\xa1\x1f\xe3=\x85\xad\x1b\xef\u02ee8\x18\xac\xb7\x1fj}~\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4p\x9101\x16\xd5\xf28\x9b##\x8bMej\x85\x96\u0644\u04c9;N~\x80\xaaX3\x00\x00\u07d4p\x99\xd1/n\xc6V\x89\x9b\x04\x9avW\x06]b\x99h\x92\u0209\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4p\x9f\xe9\xd2\xc1\xf1\xceB |\x95\x85\x04J`\x89\x9f5\x94/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\xa05I\xaaah\xe9~\x88\xa5\b3\nZ\v\xeatq\x1a\x89Hz\x9a0E9D\x00\x00\u07d4p\xa4\x06}D\x8c\xc2]\xc8\xe7\x0ee\x1c\xea|\xf8N\x92\x10\x9e\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4p\xab4\xbc\x17\xb6o\x9c;c\xf1Q'O*r|S\x92c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\xc2\x13H\x8a\x02\f<\xfb9\x01N\xf5\xbad\x04rK\u02a3\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4p\xd2^\xd2\u022d\xa5\x9c\b\x8c\xf7\r\xd2+\xf2\u06d3\xac\xc1\x8a\x899GEE\u4b7c\x00\x00\u07d4p\xe5\xe9\xdas_\xf0w$\x9d\u02da\xaf=\xb2\xa4\x8d\x94\x98\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4p\xfe\xe0\x8b\x00\xc6\xc2\xc0Jp\xc0\xce=\x92\u03ca\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u0794q\v\xe8\xfd^)\x18F\x8b\u2abe\xa8\r\x82\x845\u05d6\x12\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4q\x13]\x8f\x05\x96<\x90ZJ\a\x92)\t#Z\x89jR\ua262\xa1]\tQ\x9b\xe0\x00\x00\u07d4q\x1e\xcfw\xd7\x1b=\x0e\xa9\\\xe4u\x8a\xfe\u0379\xc11\a\x9d\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4q!?\xca14\x04 N\u02e8q\x97t\x1a\xa9\xdf\xe9c8\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94q+vQ\x02\x14\xdcb\x0fl:\x1d\u049a\xa2+\xf6\xd2\x14\xfb\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4q/\xf77\n\x13\xed6\ts\xfe\u071f\xf5\xd2\xc9:P^\x9e\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4q3\x84:x\xd99\u019dD\x86\xe1\x0e\xbc{`*4\x9f\xf7\x89\x11\xd5\xca\xcc\xe2\x1f\x84\x00\x00\u07d4qH\xae\xf32a\xd8\x03\x1f\xac?q\x82\xff5\x92\x8d\xafT\u0649\xdeB\xee\x15D\u0750\x00\x00\u07d4qcu\x8c\xbblLR^\x04\x14\xa4\n\x04\x9d\xcc\xcc\xe9\x19\xbb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4qh\xb3\xbb\x8c\x16s!\u067d\xb0#\xa6\xe9\xfd\x11\xaf\u026f\u0649a\t=|,m8\x00\x00\u07d4qirN\xe7\"q\xc54\xca\xd6B\x0f\xb0N\xe6D\u02c6\xfe\x89\x16<+@\u06e5R\x00\x00\u07d4qj\xd3\xc3:\x9b\x9a\n\x18\x96sW\x96\x9b\x94\xee}*\xbc\x10\x89\x1a!\x17\xfeA*H\x00\x00\xe0\x94qk\xa0\x1e\xad*\x91'\x065\xf9_%\xbf\xaf-\xd6\x10\xca#\x8a\ty\xe7\x01 V\xaax\x00\x00\u07d4qmP\u0320\x1e\x93\x85\x00\xe6B\x1c\xc0p\xc3P|g\u04c7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4qv,cg\x8c\x18\xd1\xc67\x8c\xe0h\xe6f8\x13\x15\x14~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4qxL\x10Q\x17\xc1\xf6\x895y\u007f\xe1Y\xab\xc7NC\xd1j\x89l\x81\u01f3\x11\x95\xe0\x00\x00\xe0\x94qyro\\q\xae\x1bm\x16\xa6\x84(\x17Nk4\xb26F\x8a\x01\x8e\xa2P\t|\xba\xf6\x00\x00\xe0\x94q|\xf9\xbe\xab680\x8d\xed~\x19^\f\x86\x13-\x16?\xed\x8a\x032n\xe6\xf8e\xf4\"\x00\x00\u07d4q\x80\xb8>\xe5WC\x17\xf2\x1c\x80r\xb1\x91\u0615\xd4aS\u00c9\x18\xef\xc8J\xd0\u01f0\x00\x00\u07d4q\x94kq\x17\xfc\x91^\xd1\a8_B\u065d\xda\xc62I\u0089lk\x93[\x8b\xbd@\x00\x00\xe0\x94q\x9e\x89\x1f\xbc\xc0\xa3>\x19\xc1-\xc0\xf0 9\xca\x05\xb8\x01\u07ca\x01OU8F:\x1bT\x00\x00\u07d4q\xc7#\n\x1d5\xbd\u0581\x9e\u0539\xa8\x8e\x94\xa0\xeb\a\x86\u0749\uc80b5=$\x14\x00\x00\u07d4q\xd2\xccm\x02W\x8ce\xf7\r\xf1\x1bH\xbe\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4r\x83\xcdFu\xdaX\u0116UaQ\xda\xfd\x80\xc7\xf9\x95\xd3\x18\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4r\x86\xe8\x9c\xd9\u078fz\x8a\x00\xc8o\xfd\xb59\x92\u0752Q\u0449i*\xe8\x89p\x81\xd0\x00\x00\u07d4r\x8f\x9a\xb0\x80\x15}\xb3\a1V\xdb\xca\x1a\x16\x9e\xf3\x17\x94\a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4r\x94\xc9\x18\xb1\xae\xfbM%\x92~\xf9\u05d9\xe7\x1f\x93\xa2\x8e\x85\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94r\x94\uc763\x10\xbckK\xbd\xf5C\xb0\xefE\xab\xfc>\x1bM\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4r\x9a\xadF'tNS\xf5\xd6c\t\xaatD\x8b:\xcd\xf4o\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xa2\xfc\x86u\xfe\xb9r\xfaA\xb5\r\xff\u06fa\xe7\xfa*\u07f7\x89\x9a\xb4\xfcg\xb5(\xc8\x00\x00\u07d4r\xa8&\b&)G&\xa7[\xf3\x9c\u066a\x9e\a\xa3\xea\x14\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4r\xb0Yb\xfb*\u0549\xd6Z\xd1j\"U\x9e\xba\x14X\xf3\x87\x89\a?u\u0460\x85\xba\x00\x00\u07d4r\xb5c?\xe4w\xfeT.t/\xac\xfdi\f\x13xT\xf2\x16\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4r\xb7\xa0=\xda\x14\u029cf\x1a\x1dF\x9f\xd376\xf6s\xc8\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xb9\x04D\x0e\x90\xe7 \u05ac\x1c*\u05dc2\x1d\xcc\x1c\x1a\x86\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94r\xb9\nM\xc0\x97#\x94\x92\u0179w}\xcd\x1eR\xba+\xe2\u008a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4r\xbb'\u02d9\xf3\xe2\xc2\u03d0\xa9\x8fp}0\xe4\xa2\x01\xa0q\x89X\xe7\x92n\xe8X\xa0\x00\x00\xe0\x94r\xc0\x83\xbe\xad\xbd\xc2'\xc5\xfbC\x88\x15\x97\xe3.\x83\xc2`V\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4r\xcd\x04\x8a\x11\x05tH)\x83I-\xfb\x1b\xd2yB\xa6\x96\xba\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xd0=M\xfa\xb3P\f\xf8\x9b\x86\x86o\x15\xd4R\x8e\x14\xa1\x95\x89\xf3K\x82\xfd\x8e\x91 \x00\x00\u07d4r\u06bb[n\ud799\xbe\x91X\x88\xf6V\x80V8\x16\b\xf8\x89\vL\x96\xc5,\xb4\xfe\x80\x00\u07d4r\xfbI\u009d#\xa1\x89P\u0132\xdc\r\xdfA\x0fS-oS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xfe\xaf\x12EyR9Td[\u007f\xaf\xff\x03x\xd1\xc8$.\x8965\u026d\xc5\u07a0\x00\x00\u07d4s\x01\xdcL\xf2mq\x86\xf2\xa1\x1b\xf8\xb0\x8b\xf2)F?d\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s\x04G\xf9|\xe9\xb2_\"\xba\x1a\xfb6\xdf'\xf9Xk\ub6c9,s\xc97t,P\x00\x00\u07d4s\x06\xde\x0e(\x8bV\xcf\u07d8~\xf0\xd3\xcc)f\a\x93\xf6\u0749\x1b\x8a\xbf\xb6.\xc8\xf6\x00\x00\xe0\x94s\r\x87c\u01a4\xfd\x82J\xb8\xb8Y\x16\x1e\xf7\xe3\xa9j\x12\x00\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4s\x12\x81sH\x95(\x01.v\xb4\x1a^(\u018b\xa4\xe3\xa9\u050965\u026d\xc5\u07a0\x00\x00\u07d4s\x13F\x12\bETUFTE\xa4Y\xb0l7s\xb0\xeb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s/\xea\xd6\x0f{\xfd\u05a9\xde\u0101%\xe3s]\xb1\xb6eO\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4sB#\xd2\u007f\xf2>Y\x06\xca\xed\"YW\x01\xbb4\x83\f\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4sG>r\x11Q\x10\xd0\xc3\xf1\x17\b\xf8nw\xbe+\xb0\x98<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4sRXm\x02\x1a\xd0\xcfw\xe0\xe9(@JY\xf3t\xffE\x82\x89\xb8Pz\x82\a( \x00\x00\u07d4sU\v\xebs+\xa9\u076f\xdaz\xe4\x06\xe1\x8f\u007f\xeb\x0f\x8b\xb2\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4s[\x97\xf2\xfc\x1b\xd2K\x12\an\xfa\xf3\xd1(\x80s\xd2\f\x8c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4s^2\x86f\xedV7\x14+3\x06\xb7|\xccT`\xe7,=\x89j\xb8\xf3xy\u0251\x00\x00\u07d4sc\u0350\xfb\xab[\xb8\u011a\xc2\x0f\xc6,9\x8f\xe6\xfbtL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4skDP=\xd2\xf6\xddTi\xffL[-\xb8\xeaO\xece\u0409\x11\x04\xeeu\x9f!\xe3\x00\x00\xe0\x94sk\xf1@,\x83\x80\x0f\x89>X1\x92X*\x13N\xb52\xe9\x8a\x02\x1e\x19\u0493\xc0\x1f&\x00\x00\xe0\x94s\x8c\xa9M\xb7\u038b\xe1\xc3\x05l\u0598\x8e\xb3v5\x9f3S\x8a\x05f[\x96\xcf5\xac\xf0\x00\x00\u07d4s\x91K\"\xfc/\x13\x15\x84$}\x82\xbeO\ucfd7\x8a\u053a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s\x93'\t\xa9\u007f\x02\u024eQ\xb0\x911(e\x12#\x85\xae\x8e\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4s\x93\xcb\xe7\xf9\xba!e\xe5\xa7U5\x00\xb6\xe7]\xa3\xc3:\xbf\x89\x05k\xc7^-c\x10\x00\x00\u07d4s\xb4\u0519\xde?8\xbf5\xaa\xf7i\xa6\xe3\x18\xbcm\x126\x92\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94s\xbe\xddo\xda{\xa3'!\x85\b{cQ\xfc\x13=HN7\x8a\x01\x12&\xbf\x9d\xceYx\x00\x00\u07d4s\xbf\xe7q\x0f1\u02b9I\xb7\xa2`O\xbfR9\xce\xe7\x90\x15\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94s\u03c0\xae\x96\x88\xe1X\x0eh\xe7\x82\xcd\b\x11\xf7\xaaIM,\x8a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\xe0\x94s\xd7&\x9f\xf0l\x9f\xfd3uL\xe5\x88\xf7J\x96j\xbb\xbb\xba\x8a\x01e\xc9fG\xb3\x8a \x00\x00\u07d4s\xd8\xfe\xe3\u02c6M\xce\"\xbb&\u029c/\bm^\x95\xe6;\x8965\u026d\xc5\u07a0\x00\x00\u07d4s\xdf<>yU\xf4\xf2\xd8Y\x83\x1b\xe3\x80\x00\xb1\ak8\x84\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4s\u48b6\f\U0010e2ef+w~\x17Z[\x1eM\f-\x8f\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94t\n\xf1\xee\xfd3e\u05cb\xa7\xb1,\xb1\xa6s\xe0j\arF\x8a\x04+\xf0kx\xed;P\x00\x00\xe0\x94t\v\xfdR\xe0\x16g\xa3A\x9b\x02\x9a\x1b\x8eEWj\x86\xa2\u06ca\x03\x8e\xba\xd5\xcd\xc9\x02\x80\x00\x00\u07d4t\x0fd\x16\x14w\x9d\u03e8\x8e\xd1\xd4%\xd6\r\xb4*\x06\f\xa6\x896\"\xc6v\b\x10W\x00\x00\u07d4t\x12\u027c0\xb4\xdfC\x9f\x021\x00\xe69$\x06j\xfdS\xaf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4t\x16\x93\xc3\x03vP\x85\x13\b \xcc+c\xe9\xfa\x92\x13\x1b\x89A\rXj \xa4\xc0\x00\x00\u07d4t!\xce[\xe3\x81s\x8d\u0703\xf0&!\x97O\xf0hly\xb8\x89Xx\x8c\xb9K\x1d\x80\x00\x00\u07d4t1j\xdf%7\x8c\x10\xf5v\u0574\x1aoG\xfa\x98\xfc\xe3=\x89\x128\x13\x1e\\z\xd5\x00\x00\u07d4t6Q\xb5^\xf8B\x9d\xf5\f\xf8\x198\xc2P\x8d\xe5\u0207\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t=\xe5\x00&\xcag\xc9M\xf5O\x06b`\xe1\xd1J\xcc\x11\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tE /\ft)z\x00N\xb3rj\xa6\xa8-\xd7\xc0/\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tK\x03\xbb\xa8X*\xe5I\x8e-\xc2-\x19\x94\x94g\xabS\xfc\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4tL\fw\xba\u007f#i \xd1\xe44\xde]\xa3>H\xeb\xf0,\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4tP\xff\u007f\x99\xea\xa9\x11bu\u07ach\xe4(\xdf[\xbc\u0639\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tV\u0172\xc5Cn>W\x10\b\x93?\x18\x05\xcc\xfe4\xe9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4tZ\u04eb\xc6\xee\xeb$qh\x9bS\x9ex\x9c\xe2\xb8&\x83\x06\x89=A\x94\xbe\xa0\x11\x92\x80\x00\xe0\x94tZ\xec\xba\xf9\xbb9\xb7Jg\xea\x1c\xe6#\xde6\x84\x81\xba\xa6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4t\\\xcf-\x81\x9e\u06fd\u07a8\x11{\\I\xed<*\x06n\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4tb\u021c\xaa\x9d\x8dx\x91\xb2T]\xef!otd\u057b!\x89\x05\xea\xedT\xa2\x8b1\x00\x00\u07d4td\x8c\xaa\xc7H\xdd\x13\\\xd9\x1e\xa1L(\xe1\xbdM\u007f\xf6\xae\x89\xa8\r$g~\xfe\xf0\x00\x00\xe0\x94tq\xf7.\xeb0\x06$\xeb(.\xabM\x03r\x00\x00\x00\xe0\x94t\x84\xd2k\xec\xc1\xee\xa8\xc61^\xc3\xee\nE\x01\x17\u0706\xa0\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4t\x86:\xce\xc7]\x03\xd5>\x86\x0ed\x00/,\x16^S\x83w\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\x89\u030a\xbeu\u0364\xef\r\x01\xce\xf2`^G\xed\xa6z\xb1\x89\a?u\u0460\x85\xba\x00\x00\u07d4t\x8c(^\xf1#?\xe4\xd3\x1c\x8f\xb17\x833r\x1c\x12\xe2z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t\x90\x87\xac\x0fZ\x97\xc6\xfa\xd0!S\x8b\xf1\xd6\u0361\x8e\r\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\x95\xaex\xc0\xd9\x02a\xe2\x14\x0e\xf2\x061\x04s\x1a`\xd1\xed\x89\x01\xdbPq\x89%!\x00\x00\u07d4t\x9aJv\x8b_#rH\x93\x8a\x12\xc6#\x84{\xd4\xe6\x88\u0709\x03\xe73b\x87\x14 \x00\x00\u07d4t\x9a\xd6\xf2\xb5pk\xbe/h\x9aD\u0136@\xb5\x8e\x96\xb9\x92\x89\x05k\xc7^-c\x10\x00\x00\u07d4t\xa1\u007f\x06K4N\x84\xdbce\u0695\x91\xff\x16(%vC\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4t\xae\xec\x91]\xe0\x1c\u019b,\xb5\xa65o\xee\xa1FX\xc6\u0149\f\x9a\x95\xee)\x86R\x00\x00\u07d4t\xaf\xe5I\x02\xd6\x15x%v\xf8\xba\xac\x13\xac\x97\f\x05\x0fn\x89\t\xa1\xaa\xa3\xa9\xfb\xa7\x00\x00\u07d4t\xb7\xe0\"\x8b\xae\xd6YW\xae\xbbM\x91m3:\xae\x16O\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t\xbcJ^ E\xf4\xff\x8d\xb1\x84\xcf:\x9b\f\x06Z\xd8\a\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4t\xbc\xe9\xec86-l\x94\u032c&\xd5\xc0\xe1:\x8b;\x1d@\x8965&A\x04B\xf5\x00\x00\u07d4t\xbfzZ\xb5\x92\x93\x14\x9b\\`\xcf6Bc\xe5\xeb\xf1\xaa\r\x89\x06G\f>w\x1e<\x00\x00\xe0\x94t\xc7<\x90R\x8a\x15s6\xf1\xe7\xea b\n\xe5?\xd2G(\x8a\x01\xe6:.S\x8f\x16\xe3\x00\x00\u07d4t\u0464\xd0\xc7RN\x01\x8dN\x06\xed;d\x80\x92\xb5\xb6\xaf,\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94t\xd3f\xb0{/VG}|pw\xaco\xe4\x97\xe0\xebeY\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4t\xd3zQt{\xf8\xb7q\xbf\xbfC\x9493\xd1\x00\xd2\x14\x83\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\xd6q\u065c\xbe\xa1\xabW\x90cu\xb6?\xf4+PE\x1d\x17\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\xeb\xf4BVF\xe6\u03c1\xb1\t\xce{\xf4\xa2\xa6=\x84\x81_\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4t\xed3\xac\xf4?5\xb9\x8c\x920\xb9\xe6d.\xcbS0\x83\x9e\x89$\xf6\xdf\xfbI\x8d(\x00\x00\u07d4t\xef(i\xcb\xe6\b\x85`E\xd8\xc2\x04\x11\x18W\x9f\"6\xea\x89\x03<\xd6E\x91\x95n\x00\x00\u07d4t\xfcZ\x99\xc0\xc5F\x05\x03\xa1;\x05\tE\x9d\xa1\x9c\xe7\u0350\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4u\v\xbb\x8c\x06\xbb\xbf$\bC\xccux.\xe0/\b\xa9tS\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4u\x14\xad\xbd\xc6?H?0M\x8e\x94\xb6\u007f\xf30\x9f\x18\v\x82\x89!\u0120n-\x13Y\x80\x00\u0794u\x17\xf1l(\xd12\xbb@\xe3\xba6\u01ae\xf11\xc4b\xda\x17\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4u\x1a,\xa3Nq\x87\xc1c\u048e6\x18\xdb(\xb1<\x19m&\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94u\x1a\xbc\xb6\xcc\x030Y\x91\x18\x15\xc9o\u04516\n\xb0D-\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4u&\xe4\x82R\x9f\n\x14\xee\u0248q\xdd\xdd\x0er\x1b\f\u0662\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4u)\xf3y{\xb6\xa2\x0f~\xa6I$\x19\xc8L\x86vA\xd8\x1c\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94u*^\xe22a,\xd3\x00_\xb2n[Y}\xe1\x9fwk\xe6\x8a\x01'\xfc\xb8\xaf\xae \xd0\x00\x00\u07d4u,\x9f\xeb\xf4/f\xc4x{\xfa~\xb1|\xf53;\xbaPp\x89j\x99\xf2\xb5O\xddX\x00\x00\u07d4u930F\u07b1\xef\x8e\u07b9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94u\xc1\xad#\xd2?$\xb3\x84\xd0\xc3\x14\x91w\xe8f\x97a\r!\x8a\x01\\[\xcdl(\x8b\xbd\x00\x00\u07d4u\xc2\xff\xa1\xbe\xf5I\x19\xd2\t\u007fz\x14-.\x14\xf9\xb0JX\x89\x90\xf3XP@2\xa1\x00\x00\u07d4u\xd6|\xe1N\x8d)\xe8\xc2\xff\u3051{\x93\v\x1a\xff\x1a\x87\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4u\xde~\x93R\xe9\v\x13\xa5\x9aXx\xff\xec\u01c3\x1c\xacM\x82\x89\x94\x89#z\u06daP\x00\x00\u07d4u\xf7S\x9d0\x9e\x909\x98\x9e\xfe.\x8b-\xbd\x86Z\r\xf0\x88\x89\x85[[\xa6\\\x84\xf0\x00\x00\u07d4v\b\xf47\xb3\x1f\x18\xbc\vd\u04c1\xae\x86\xfd\x97\x8e\u05f3\x1f\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94v\x0f\xf35N\x0f\u0793\x8d\x0f\xb5\xb8,\xef[\xa1\\=)\x16\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4v\x1an6,\x97\xfb\xbd|Yw\xac\xba-\xa7F\x876_I\x89\t\xf7J\xe1\xf9S\xd0\x00\x00\u07d4v\x1el\xae\xc1\x89\xc20\xa1b\xec\x00e0\x19>g\u03dd\x19\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94v\x1f\x8a:*\U00028f7e\x1d\xa0\t2\x1f\xb2\x97d\xebb\xa1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4v)\x98\xe1\xd7R'\xfc\xedzp\xbe\x10\x9aL\vN\xd8d\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4v-o0\u06b9\x915\xe4\xec\xa5\x1dRC\xd6\xc8b\x11\x02\u0549\x0fI\x89A\xe6d(\x00\x00\u07d4v3\x1e0yl\xe6d\xb2p\x0e\rASp\x0e\u0706\x97w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v8\x86\xe33\xc5o\xef\xf8[\xe3\x95\x1a\xb0\xb8\x89\xce&.\x95\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v:|\xba\xb7\rzd\u0427\xe5)\x80\xf6\x81G%\x93I\f\x89 \x86\xac5\x10R`\x00\x00\u07d4v>\xec\u0c0a\u021e2\xbf\xa4\xbe\xcev\x95\x14\xd8\xcb[\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4v@\xa3\u007f\x80R\x98\x15\x15\xbc\xe0x\u0693\xaf\xa4x\x9bW4\x89lk\x93[\x8b\xbd@\x00\x00\u0794vA\xf7\xd2j\x86\xcd\xdb+\xe10\x81\x81\x0e\x01\xc9\xc8E\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94vO\xc4mB\x8bm\xbc\"\x8a\x0f_U\xc9P\x8cw.\xab\x9f\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4vPn\xb4\xa7\x80\xc9Q\xc7J\x06\xb0=;\x83b\xf0\x99\x9dq\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94v[\xe2\xe1/b\x9ecI\xb9}!\xb6*\x17\xb7\xc80\xed\xab\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94vb\x81P\xe2\x99[['\x9f\xc8>\r\xd5\xf1\x02\xa6q\xdd\x1c\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4vk7Y\xe8yN\x92m\xacG=\x91:\x8f\xb6\x1a\xd0\xc2\u0249\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4vp\xb0/,<\xf8\xfdOG0\xf38\x1aq\xeaC\x1c3\u01c9\x0e~\xeb\xa3A\vt\x00\x00\u07d4vz\x03eZ\xf3`\x84\x1e\x81\r\x83\xf5\xe6\x1f\xb4\x0fL\xd1\x13\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4vz\u0190y\x1c.#E\x10\x89\xfelp\x83\xfeU\u07b6+\x89,s\xc97t,P\x00\x00\u07d4v\u007f\xd7y}Qi\xa0_sd2\x1c\x19\x84:\x8c4\x8e\x1e\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u0794v\x84o\r\xe0;Zv\x97\x1e\xad)\x8c\xdd\b\x84:K\xc6\u0188\xd7\x1b\x0f\u088e\x00\x00\xe0\x94v\x84\x98\x93N7\xe9\x05\xf1\xd0\xe7{D\xb5t\xbc\xf3\xecJ\xe8\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4v\x8c\xe0\u06a0)\xb7\xde\xd0\"\xe5\xfcWM\x11\xcd\xe3\xec\xb5\x17\x89\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\xe0\x94v\x93\xbd\xebo\xc8+[\xcar\x13U\"1u\xd4z\bKM\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4v\xaa\xf8\xc1\xac\x01/\x87R\xd4\xc0\x9b\xb4f\a\xb6e\x1d\\\xa8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4v\xab\x87\xddZ\x05\xad\x83\x9aN/\xc8\xc8Z\xa6\xba\x05d\x170\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v\xaf\xc2%\xf4\xfa0}\xe4\x84U+\xbe\x1d\x9d?\x15\aLJ\x89\xa2\x90\xb5\u01ed9h\x00\x00\xe0\x94v\xbe\xca\xe4\xa3\x1d6\xf3\xcbW\u007f*CYO\xb1\xab\xc1\xbb\x96\x8a\x05C\xa9\xce\x0e\x132\xf0\x00\x00\u07d4v\xc2u5\xbc\xb5\x9c\xe1\xfa-\x8c\x91\x9c\xab\xebJk\xba\x01\u0449lk\x93[\x8b\xbd@\x00\x00\u07d4v\xca\"\xbc\xb8y\x9eS'\u012a*}\tI\xa1\xfc\xce_)\x89R\xa0?\"\x8cZ\xe2\x00\x00\u07d4v\xca\u0108\x11\x1aO\u0555\xf5h\xae:\x85\x87p\xfc\x91]_\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94v\u02dc\x8bi\xf48vu\u0102S\xe24\xcb~\rt\xa4&\x8a\x01\x90\xf4H.\xb9\x1d\xae\x00\x00\u07d4v\xf8:\xc3\xda0\xf7\t&(\xc73\x9f \x8b\xfc\x14,\xb1\ue25a\x18\xff\xe7B}d\x00\x00\xe0\x94v\xf9\xad=\x9b\xbd\x04\xae\x05\\\x14w\xc0\xc3^u\x92\xcb* \x8a\b\x83?\x11\xe3E\x8f \x00\x00\u07d4v\xff\xc1W\xadk\xf8\xd5m\x9a\x1a\u007f\u077c\x0f\xea\x01\n\xab\xf4\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\x02\x8e@\x9c\xc4:;\xd3=!\xa9\xfcS\xec`n\x94\x91\x0e\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4w\f/\xb2\u0128\x17S\xac\x01\x82\xeaF\x0e\xc0\x9c\x90\xa5\x16\xf8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4w\r\x98\xd3\x1bCS\xfc\xee\xe8V\fL\u03c0>\x88\xc0\xc4\xe0\x89 \x86\xac5\x10R`\x00\x00\xe0\x94w\x13\xab\x807A\x1c\t\xbah\u007fo\x93d\xf0\xd3#\x9f\xac(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4w\x15\a\xae\xeej%]\xc2\u035d\xf5QT\x06-\b\x97\xb2\x97\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4w\x19\x88\x87\x95\xadtY$\xc7W`\u0771\x82}\xff\xd8\u0368\x89lkLM\xa6\u077e\x00\x00\u07d4w'\xaf\x10\x1f\n\xab\xa4\xd2:\x1c\xaf\xe1|n\xb5\u06b1\xc6\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4w,)\u007f\n\u0454H.\xe8\xc3\xf06\xbd\xeb\x01\xc2\x01\xd5\u0309\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94w0o\xfe.J\x8f<\xa8&\xc1\xa2I\xf7!-\xa4:\xef\xfd\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4w1A\x12}\x8c\xf3\x18\xae\xbf\x886Z\xdd=U'\xd8[j\x8966\u05ef^\u024e\x00\x00\u07d4wF\xb6\xc6i\x9c\x8f4\xca'h\xa8 \xf1\xff\xa4\xc2\a\xfe\x05\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4wQ\xf3c\xa0\xa7\xfd\x053\x19\b\t\u076f\x93@\xd8\xd1\x12\x91\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4wW\xa4\xb9\xcc=\x02G\u032a\xeb\x99\t\xa0\xe5n\x1d\xd6\xdc\u0089\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4w\\\x10\xc9>\r\xb7 [&CE\x823\xc6O\xc3?\xd7[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4wa~\xbcK\xeb\xc5\xf5\xdd\xeb\x1bzp\xcd\xebj\xe2\xff\xa0$\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4wiC\xff\xb2\xef\\\xdd5\xb8<(\xbc\x04k\xd4\xf4gp\x98\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94wp\x1e,I=\xa4|\x1bX\xf4!\xb5I]\xeeE\xbe\xa3\x9b\x8a\x01H\xf6I\xcfaB\xa5\x80\x00\u07d4wy\x8f \x12W\xb9\xc3R\x04\x95pW\xb5Ft\xae\xfaQ\u07c9\b\x13\xcaV\x90m4\x00\x00\u07d4w\x8cC\xd1\x1a\xfe;Xo\xf3t\x19-\x96\xa7\xf2=+\x9b\u007f\x89\x8b\xb4\xfc\xfa;}k\x80\x00\u07d4w\x8cy\xf4\xde\x19S\xeb\u0398\xfe\x80\x06\xd5:\x81\xfbQ@\x12\x8963\x03\"\xd5#\x8c\x00\x00\u07d4w\x92t\xbf\x18\x03\xa36\xe4\u04f0\r\u0753\xf2\xd4\xf5\xf4\xa6.\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\xa1q\"\xfa1\xb9\x8f\x17\x11\xd3*\x99\xf0>\xc3&\xf3=\b\x89\\(=A\x03\x94\x10\x00\x00\u07d4w\xa3I\a\xf3\x05\xa5L\x85\xdb\t\xc3c\xfd\xe3\xc4~j\xe2\x1f\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4w\xa7i\xfa\xfd\xec\xf4\xa68v-[\xa3\x96\x9d\xf61 \xa4\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4w\xbekd\xd7\xc73\xa46\xad\xec^\x14\xbf\x9a\xd7@+\x1bF\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\xbf\xe9<\u0367P\x84~A\xa1\xaf\xfe\xe6\xb2\u0696\xe7!N\x89\x10CV\x1a\x88)0\x00\x00\u07d4w\u0126\x97\xe6\x03\xd4+\x12\x05l\xbb\xa7a\xe7\xf5\x1d\x04C\xf5\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4w\xcc\x02\xf6#\xa9\u03d8S\t\x97\xeag\xd9\\;I\x18Y\xae\x89Is\x03\xc3n\xa0\xc2\x00\x00\u07d4w\xd4?\xa7\xb4\x81\xdb\xf3\xdbS\f\xfb\xf5\xfd\xce\xd0\xe6W\x181\x89lk\x93[\x8b\xbd@\x00\x00\u07d4w\xda^lr\xfb6\xbc\xe1\xd9y\x8f{\xcd\xf1\u044fE\x9c.\x89\x016\x95\xbbl\xf9>\x00\x00\u07d4w\xf4\xe3\xbd\xf0V\x88<\xc8r\x80\xdb\xe6@\xa1\x8a\r\x02\xa2\a\x89\n\x81\x99:+\xfb[\x00\x00\u0794w\xf6\t\u0287 \xa0#&,U\xc4o-&\xfb90\xaci\x88\xf0\x15\xf2W6B\x00\x00\u07d4w\xf8\x1b\x1b&\xfc\x84\xd6\u0797\uf2df\xbdr\xa310\xccJ\x8965\u026d\xc5\u07a0\x00\x00\u07d4x\x19\xb0E\x8e1N+S\xbf\xe0\f8I_\u0539\xfd\xf8\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4x\x1b\x15\x01dz.\x06\xc0\xedC\xff\x19\u007f\xcc\xec5\xe1p\v\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4x/R\xf0\xa6v\xc7w\x16\xd5t\xc8\x1e\xc4hO\x9a\x02\n\x97\x89.\x14\xe2\x06\xb70\xad\x80\x00\u07d4x5]\xf0\xa20\xf8=\x03,p1TAM\xe3\xee\u06b5W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4x6\xf7\xefk\u01fd\x0f\xf3\xac\xafD\x9c\x84\xddk\x1e,\x93\x9f\x89\xe0\x8d\xe7\xa9,\xd9|\x00\x00\u07d4x7\xfc\xb8v\xda\x00\xd1\xeb;\x88\xfe\xb3\xdf?\xa4\x04/\xac\x82\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4x>\uc2a5\xda\xc7{.f#\xedQ\x98\xa41\xab\xba\xee\a\x89\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4x\\\x8e\xa7t\xd70D\xa74\xfay\n\x1b\x1et>w\xed|\x89\f\xf1Rd\f\\\x83\x00\x00\u07d4x`\xa3\xde8\xdf8*\xe4\xa4\xdc\xe1\x8c\f\a\xb9\x8b\xce=\xfa\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94xcCq\xe1s\x04\xcb\xf39\xb1E*L\xe48\xdcvL\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4xd\u0719\x9f\xe4\xf8\xe0\x03\xc0\xf4=\xec\u00da\xae\x15\"\xdc\x0f\x89\x05\x1e\x10+\xd8\xec\xe0\x00\x00\u07d4xtj\x95\x8d\xce\xd4\xc7d\xf8vP\x8cAJh4,\uce49\x02\xbe7O\xe8\xe2\xc4\x00\x00\xe0\x94x}1?\xd3k\x05>\xee\xae\xdb\xcet\xb9\xfb\x06x32\x89\x8a\x05\xc0X\xb7\x84'\x19`\x00\x00\u07d4x\x85\x9c[T\x8bp\r\x92\x84\xce\xe4\xb6c=GJ\x8a\x04{\x92\xc4\x15B$-\n\b\xc7\x0f\x99\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4x\u03c36\xb3(\xdb=\x87\x81:G+\x9e\x89\xb7^\f\xf3\xbc\x8965\u026d\xc5\u07a0\x00\x00\u07d4x\xd4\xf8\xc7\x1c\x1eh\xa6\x9a\x98\xf5/\xcbE\u068a\xf5n\xa1\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4x\xdf&\x81\xd6\xd6\x02\xe2!B\xd5A\x16\u07a1]EIW\xaa\x89\x10'\x94\xad \xdah\x00\x00\u07d4x\xe0\x8b\xc53A<&\u2473\x14?\xfa|\u026f\xb9{x\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4x\xe8?\x80\xb3g\x8cz\nN>\x8c\x84\xdc\xcd\xe0dBbw\x89a\t=|,m8\x00\x00\u07d4x\xf5\xc7G\x85\xc5f\x8a\x83\x80r\x04\x8b\xf8\xb4SYM\u06ab\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4y\x0f\x91\xbd]\x1c\\\xc4s\x9a\xe9\x13\x00\u06c9\xe1\xc10<\x93\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\x17\u5f42\xa9y\x0f\xd6P\xd0C\xcd\xd90\xf7y\x963\u06c9\xd8\xd4`,&\xbfl\x00\x00\u07d4y\x19\xe7b\u007f\x9b}T\xea;\x14\xbbM\xd4d\x9fO9\xde\xe0\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4y\x1f`@\xb4\xe3\xe5\r\xcf5S\xf1\x82\u0357\xa9\x060\xb7]\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4y0\xc2\xd9\xcb\xfa\x87\xf5\x10\xf8\xf9\x87w\xff\x8a\x84H\xcaV)\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4yE)\u041d\x01rq5\x970\x02pu\xb8z\xd8=\xaen\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4yKQ\u00deS\xd9\xe7b\xb0a;\x82\x9aD\xb4r\xf4\xff\xf3\x89$5\xe0dxA\u0300\x00\xe0\x94yU\x1c\xed\xe3v\xf7G\xe3ql\x8dy@\rvm.\x01\x95\x8a\t\xcb7\xaf\xa4\xffxh\x00\x00\u07d4y^\xbc&&\xfc9\xb0\xc8b\x94\xe0\xe87\xdc\xf5#U0\x90\x8965\u026d\xc5\u07a0\x00\x00\u07d4yn\xbb\xf4\x9b>6\xd6v\x94\xady\xf8\xff6vz\xc6\xfa\xb0\x89\x03K\xc4\xfd\xde'\xc0\x00\x00\u07d4yo\x87\xbaaz)0\xb1g\v\xe9.\xd1(\x1f\xb0\xb3F\xe1\x89\x06\xf5\xe8o\xb5((\x00\x00\u07d4yt'\xe3\xdb\xf0\xfe\xaez%\x06\xf1-\xf1\xdc@2n\x85\x05\x8965\u026d\xc5\u07a0\x00\x00\u07d4yu\x10\xe3\x86\xf5c\x93\xce\xd8\xf4w7\x8aDLHO}\xad\x8965\u026d\xc5\u07a0\x00\x00\u07d4y{\xb7\xf1W\xd9\xfe\xaa\x17\xf7m\xa4\xf7\x04\xb7M\xc1\x03\x83A\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4y\x88\x90\x131\xe3\x87\xf7\x13\xfa\u03b9\x00\\\xb9\xb6Q6\xeb\x14\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4y\x89\u041f8&\xc3\u5bccu*\x81\x15r:\x84\xd8\tp\x89\x16\x86\xf8aL\xf0\xad\x00\x00\xe0\x94y\x95\xbd\x8c\xe2\xe0\xc6{\xf1\u01e51\xd4w\xbc\xa1\xb2\xb9ua\x8a\x01BH\xd6\x17\x82\x9e\xce\x00\x00\u07d4y\xae\xb3Ef\xb9t\xc3ZX\x81\xde\xc0 \x92}\xa7\xdf]%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\xb1 \xeb\x88\x06s#!(\x8fgZ'\xa9\"_\x1c\xd2\ub245\xa0\xbf7\xde\xc9\xe4\x00\x00\u07d4y\xb4\x8d-a7\u00c5Ma\x1c\x01\xeaBBz\x0fY{\xb7\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4y\xb8\xaa\xd8y\xdd0V~\x87x\xd2\xd21\xc8\xf3z\xb8sN\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\xbf/{n2\x8a\xaf&\xe0\xbb\t?\xa2-\xa2\x9e\xf2\xf4q\x89a\t=|,m8\x00\x00\u07d4y\xc10\xc7b\xb8v[\x19\u04ab\u0260\x83\xab\x8f:\xady@\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4y\xc1\xbe\x19q\x1fs\xbe\xe4\xe61j\xe7T\x94Y\xaa\u03a2\xe0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4y\xc6\x00/\x84R\xca\x15\u007f\x13\x17\xe8\n/\xaf$GUY\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4y\xca\xc6IO\x11\xef'\x98t\x8c\xb52\x85\xbd\x8e\"\xf9|\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4y\u03e9x\n\xe6\xd8{,1\x88?\t'i\x86\u021ag5\x8965\u026d\xc5\u07a0\x00\x00\u07d4y\u06e2VG-\xb4\xe0X\xf2\xe4\xcd\xc3\xeaN\x8aBw83\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4y\xed\x10\xcf\x1fm\xb4\x82\x06\xb5\t\x19\xb9\xb6\x97\b\x1f\xbd\xaa\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u0794y\xf0\x8e\x01\xce\t\x88\xe6<\u007f\x8f)\b\xfa\xdeC\xc7\xf9\xf5\u0248\xfc\x93c\x92\x80\x1c\x00\x00\u07d4y\xfdmH1Pf\xc2\x04\xf9e\x18i\xc1\tl\x14\xfc\x97\x81\x89lk\x93[\x8b\xbd@\x00\x00\u0794y\xff\xb4\xac\x13\x81*\vx\u0123{\x82u\">\x17k\xfd\xa5\x88\xf0\x15\xf2W6B\x00\x00\u07d4z\x05\x89\xb1C\xa8\xe5\xe1\a\u026cf\xa9\xf9\xf8Yz\xb3\u7ac9Q\xe92\xd7n\x8f{\x00\x00\u07d4z\nx\xa9\xcc9?\x91\xc3\xd9\xe3\x9ak\x8c\x06\x9f\a^k\xf5\x89Hz\x9a0E9D\x00\x00\u07d4z\x13p\xa7B\xec&\x87\xe7a\xa1\x9a\u0167\x942\x9e\xe6t\x04\x89\xa2\xa12ga\xe2\x92\x00\x00\xe0\x94z-\xfcw\x0e$6\x811\xb7\x84w\x95\xf2\x03\xf3\xd5\r[V\x8a\x02i\xfe\xc7\xf06\x1d \x00\x00\u07d4z3\x83N\x85\x83s>-R\xae\xadX\x9b\u046f\xfb\x1d\xd2V\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94z6\xab\xa5\xc3\x1e\xa0\xca~'{\xaa2\xecF\u0393\xcfu\x06\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94z8\x11\"\xba\xday\x1az\xb1\xf6\x03}\xac\x80C'S\xba\xad\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94zH\xd8w\xb6:\x8f\x8f\x93\x83\xe9\xd0\x1eS\xe8\fR\x8e\x95_\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4zO\x9b\x85\x06\x90\xc7\xc9F\x00\xdb\xee\f\xa4\xb0\xa4\x11\xe9\xc2!\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4zc\x86\x9f\xc7g\xa4\u01b1\xcd\x0e\x06I\xf3cL\xb1!\xd2K\x89\x043\x87Oc,\xc6\x00\x00\u07d4zg\xdd\x04:PO\xc2\xf2\xfcq\x94\xe9\xbe\xcfHL\xec\xb1\xfb\x89\r\x8drkqw\xa8\x00\x00\xe0\x94zk&\xf48\u0663RD\x91U\xb8\x87l\xbd\x17\xc9\u065bd\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4zmx\x1cw\u013a\x1f\xca\xdfhsA\xc1\xe3\x17\x99\xe9='\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4zph\xe1\xc37\\\x0eY\x9d\xb1\xfb\xe6\xb2\xea#\xb8\xf4\a\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4zt\xce\xe4\xfa\x0fcp\xa7\x89O\x11l\xd0\f\x11G\xb8>Y\x89+^:\xf1k\x18\x80\x00\x00\u07d4zy\xe3\x0f\xf0W\xf7\n=\x01\x91\xf7\xf5?v\x157\xaf}\xff\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94zzO\x80sW\xa4\xbb\xe6\x8e\x1a\xa8\x0692\x10\xc4\x11\u0333\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4z\x85c\x86y\x01 o?+\xf0\xfa>\x1c\x81\t\u02bc\u0345\x89\amA\xc6$\x94\x84\x00\x00\xe0\x94z\x87\x97i\n\xb7{Tp\xbf|\f\x1b\xbaa%\b\xe1\xac}\x8a\x01\xe0\x92\x96\xc37\x8d\xe4\x00\x00\u07d4z\x8c\x89\xc0\x14P\x9dV\u05f6\x810f\x8f\xf6\xa3\xec\xecsp\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94z\x94\xb1\x99\x92\u03b8\xcec\xbc\x92\xeeKZ\xde\xd1\fM\x97%\x8a\x03\x8d\x1a\x80d\xbbd\xc8\x00\x00\u07d4z\xa7\x9a\xc0C\x16\u030d\b\xf2\x00e\xba\xa6\xd4\x14(\x97\xd5N\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4z\xadM\xbc\u04ec\xf9\x97\u07d3XiV\xf7+d\u062d\x94\xee\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94z\xb2V\xb2\x04\x80\n\xf2\x017\xfa\xbc\xc9\x16\xa22Xu%\x01\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4z\xbaV\xf6:H\xbc\b\x17\u05b9p9\x03\x9az\xd6/\xae.\x89 \x86\xac5\x10R`\x00\x00\xe0\x94z\xbb\x10\xf5\xbd\x9b\xc3;\x8e\xc1\xa8-d\xb5[k\x18wuA\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4z\u010d@\xc6d\u031am\x89\xf1\xc5\xf5\xc8\n\x1cp\xe7D\u6263\x10b\xbe\xee\xd7\x00\x00\x00\u07d4z\u014fo\xfcO\x81\a\xaen07\x8eN\x9f\x99\xc5\u007f\xbb$\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4z\xd3\xf3\aao\x19\u0731C\xe6DM\xab\x9c<3a\x1fR\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4z\xd8,\xae\xa1\xa8\xb4\xed\x051\x9b\x9c\x98p\x17<\x81N\x06\xee\x89!d\xb7\xa0J\u0220\x00\x00\u07d4z\xde]f\xb9D\xbb\x86\f\x0e\xfd\xc8bv\u054fFS\xf7\x11\x89lk\x93[\x8b\xbd@\x00\x00\u07d4z\xdf\xed\xb0m\x91\xf3\xccs\x90E\v\x85U\x02p\x88<{\xb7\x89\x11x\xfa@Q]\xb4\x00\x00\u07d4z\xe1\xc1\x9eS\xc7\x1c\xeeLs\xfa\xe2\xd7\xfcs\xbf\x9a\xb5\u348965\u026d\xc5\u07a0\x00\x00\u07d4z\xe6Y\xeb;\xc4hR\xfa\x86\xfa\xc4\xe2\x1cv\x8dP8\x89E\x89\x0f\x81\f\x1c\xb5\x01\xb8\x00\x00\u07d4z\xea%\xd4+&\x12(n\x99\xc56\x97\u01bcA\x00\xe2\u06ff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4z\xef{U\x1f\v\x9cF\xe7U\xc0\xf3\x8e[:s\xfe\x11\x99\xf5\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4{\v1\xffn$t^\xad\x8e\u067b\x85\xfc\v\xf2\xfe\x1dU\u0509+^:\xf1k\x18\x80\x00\x00\xe0\x94{\x0f\xea\x11v\xd5!Y3:\x14<)IC\xda6\xbb\u0774\x8a\x01\xfc}\xa6N\xa1L\x10\x00\x00\u07d4{\x11g<\xc0\x19bk)\f\xbd\xce&\x04o~m\x14\x1e!\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4{\x12!b\xc9\x13\xe7\x14l\xad\v~\xd3z\xff\xc9*\v\xf2\u007f\x89Q\xaf\tk#\x01\u0440\x00\u07d4{\x1b\xf5:\x9c\xbe\x83\xa7\u07a44W\x9f\xe7*\xac\x8d*\f\u0409\n\xd4\xc81j\v\f\x00\x00\u07d4{\x1d\xaf\x14\x89\x1b\x8a\x1e\x1b\xd4)\u0633k\x9aJ\xa1\u066f\xbf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4{\x1f\xe1\xabM\xfd\x00\x88\xcd\xd7\xf6\x01c\xefY\xec*\xee\x06\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4{%\xbb\x9c\xa8\xe7\x02!~\x933\"RP\xe5<6\x80MH\x89e\xea=\xb7UF`\x00\x00\u07d4{'\xd0\xd1\xf3\xdd<\x14\x02\x94\xd0H\x8bx>\xbf@\x15'}\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94{@\a\xc4^ZW?\u06f6\xf8\xbdtk\xf9J\xd0J<&\x8a\x038!\xf5\x13]%\x9a\x00\x00\u07d4{C\xc7\xee\xa8\xd6#U\xb0\xa8\xa8\x1d\xa0\x81\xc6Dk3\xe9\xe0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4{M*8&\x90i\xc1\x85Ww\rY\x1d$\xc5\x12\x1f^\x83\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94{au\xec\x9b\xef\xc78$\x955\xdd\xde4h\x8c\xd3n\xdf%\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94{f\x12hy\x84M\xfa4\xfee\xc9\xf2\x88\x11\u007f\xef\xb4I\xad\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4{j\x84q\x8d\xd8nc3\x84)\xac\x81\x1d|\x8a\x86\x0f!\xf1\x89a\t=|,m8\x00\x00\xe0\x94{q,z\xf1\x16v\x00jf\xd2\xfc\\\x1a\xb4\xc4y\xce`7\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4{s$-u\u029a\xd5X\xd6P)\r\xf1v\x92\xd5L\u0638\x89lnY\xe6|xT\x00\x00\u07d4{v\x1f\xeb\u007f\u03e7\xde\xd1\xf0\xeb\x05\x8fJ`\v\xf3\xa7\b\u02c9\xf9]\xd2\xec'\xcc\xe0\x00\x00\xe0\x94{\x82|\xae\u007f\xf4t\t\x18\xf2\xe00\xab&\u02d8\xc4\xf4l\xf5\x8a\x01\x94hL\v9\xde\x10\x00\x00\xe0\x94{\x892\x86B~r\xdb!\x9a!\xfcM\xcd_\xbfY(<1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4{\x92&\xd4o\xe7Q\x94\v\xc4\x16\xa7\x98\xb6\x9c\xcf\r\xfa\xb6g\x89\u3bb5sr@\xa0\x00\x00\u07d4{\x98\xe2<\xb9k\xee\xe8\n\x16\x80i\ube8f \xed\xd5\\\u03c9\v\xa0\xc9\x15\x87\xc1J\x00\x00\u07d4{\xb0\xfd\xf5\xa6c\xb5\xfb\xa2\x8d\x9c\x90*\xf0\xc8\x11\xe2R\xf2\x98\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4{\xb9W\x1f9K\v\x1a\x8e\xbaVd\xe9\u0635\xe8@g{\xea\x89\x01\x11du\x9f\xfb2\x00\x00\xe0\x94{\xb9\x84\xc6\u06f9\xe2y\x96j\xfa\xfd\xa5\x9c\x01\xd0&'\xc8\x04\x8a\x01\xb4d1\x1dE\xa6\x88\x00\x00\u07d4{\xbb\xec^p\xbd\xea\u063b2\xb4(\x05\x98\x8e\x96H\xc0\xaa\x97\x8966\u05ef^\u024e\x00\x00\u07d4{\xca\x1d\xa6\xc8\nf\xba\xa5\xdbZ\u0245A\u013e'kD}\x89$\xcf\x04\x96\x80\xfa<\x00\x00\u07d4{\u0772\xee\x98\xde\x19\xeeL\x91\xf6a\xee\x8eg\xa9\x1d\x05K\x97\x8965\u026d\xc5\u07a0\x00\x00\u0794{\xe2\xf7h\f\x80-\xa6\x15L\x92\xc0\x19J\xe72Qzqi\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4{\xe7\xf2Eiq\x88;\x9a\x8d\xbeL\x91\xde\xc0\x8a\xc3N\x88b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4{\xe8\u0334\xf1\x1bf\xcan\x1dW\xc0\xb59b!\xa3\x1b\xa5:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94{\xeb\x81\xfb/^\x91Rk*\xc9y^v\u019b\xcf\xf0K\xc0\x8a\x0e\xb2.yO\n\x8d`\x00\x00\u07d4|\b\x83\x05L-\x02\xbcz\x85+\x1f\x86\xc4'w\xd0\xd5\xc8V\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4|\x0f^\a C\xc9\xeet\x02B\x19~x\xccK\x98\xcd\xf9`\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|\x1d\xf2JO\u007f\xb2\u01f4r\xe0\xbb\x00l\xb2}\xcd\x16AV\x8965\u026d\xc5\u07a0\x00\x00\u07d4|)\xd4}W\xa73\xf5k\x9b!pc\xb5\x13\xdc;1Y#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4|+\x96\x03\x88JO.FN\u03b9}\x17\x93\x8d\x82\x8b\xc0,\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4|8,\x02\x96a.N\x97\xe4@\xe0-8q';U\xf5;\x89\n\xb6@9\x12\x010\x00\x00\u07d4|>\xb7\x13\xc4\xc9\xe08\x1c\xd8\x15L|\x9a}\xb8d\\\xde\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|D\x01\xae\x98\xf1.\xf6\xde9\xae$\u03df\xc5\x1f\x80\xeb\xa1k\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|E\xf0\xf8D*V\xdb\u04dd\xbf\x15\x99\x95A\\R\xedG\x9b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94|S-\xb9\xe0\xc0l&\xfd@\xac\xc5j\xc5\\\x1e\xe9-<:\x8a?\x87\bW\xa3\xe0\xe3\x80\x00\x00\u07d4|`\xa0_zJ_\x8c\xf2xC\x916.uZ\x83A\xefY\x89f\x94\xf0\x18*7\xae\x00\x00\u07d4|`\xe5\x1f\v\xe2(\xe4\xd5o\xdd)\x92\xc8\x14\xdaw@\u01bc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|i$\xd0|>\xf5\x89\x19f\xfe\nxV\xc8{\xef\x9d 4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94|\x8b\xb6Zo\xbbI\xbdA3\x96\xa9\xd7\xe3\x10S\xbb\xb3z\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94|\x9a\x11\f\xb1\x1f%\x98\xb2\xb2\x0e,\xa4\x002^A\xe9\xdb3\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4|\xbc\xa8\x8f\xcaj\x00`\xb9`\x98\\\x9a\xa1\xb0%4\xdc\"\b\x89\x19\x12z\x13\x91\xea*\x00\x00\u07d4|\xbe\xb9\x992\xe9~n\x02\x05\x8c\xfcb\u0432k\xc7\u0325+\x89lk\x93[\x8b\xbd@\x00\x00\u07d4|\xc2Jj\x95\x8c \xc7\xd1$\x96`\xf7Xb&\x95\v\r\x9a\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4|\xd2\x0e\u0335\x18\xb6\f\xab\t[r\x0fW\x15p\u02aaD~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4|\xd5\xd8\x1e\xab7\xe1\x1ebv\xa3\xa1\t\x12Q`~\r~8\x89\x03hM^\xf9\x81\xf4\x00\x00\u07d4|\xdft!9E\x95=\xb3\x9a\xd0\xe8\xa9x\x1a\xddy.M\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4|\xe4hdF\U000547be\xd6r\x15\xeb\rZ\x1d\xd7,\x11\xb8\x89x9\xd3!\xb8\x1a\xb8\x00\x00\u07d4|\xefMC\xaaA\u007f\x9e\xf8\xb7\x87\xf8\xb9\x9dS\xf1\xfe\xa1\ue209g\x8a\x93 b\xe4\x18\x00\x00\u07d4}\x03P\xe4\v3\x8d\xdasfa\x87+\xe3?\x1f\x97R\xd7U\x89\x02\xb4\xf5\xa6\U00051500\x00\xe0\x94}\x04\xd2\xed\xc0X\xa1\xaf\xc7a\xd9\u025a\xe4\xfc\\\x85\xd4\u0226\x8aB\xa9\xc4g\\\x94g\xd0\x00\x00\u07d4}\v%^\xfbW\xe1\x0fp\b\xaa\"\xd4\x0e\x97R\xdf\xcf\x03x\x89\x01\x9f\x8euY\x92L\x00\x00\xe0\x94}\x13\xd6pX\x84\xab!W\u074d\xccpF\xca\xf5\x8e\xe9K\xe4\x8a\x1d\r\xa0|\xbb>\xe9\xc0\x00\x00\u07d4}'>c~\xf1\xea\u0101\x11\x94\x13\xb9\x1c\x98\x9d\xc5\xea\xc1\"\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4}*R\xa7\xcf\f\x846\xa8\xe0\a\x97kl&\xb7\"\x9d\x1e\x15\x89\x17\xbf\x06\xb3*$\x1c\x00\x00\u07d4}4\x805i\xe0\v\u05b5\x9f\xff\b\x1d\xfa\\\n\xb4\x19zb\x89\\\xd8|\xb7\xb9\xfb\x86\x00\x00\u07d4}4\xffY\xae\x84\nt\x13\u01baL[\xb2\xba,u\xea\xb0\x18\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4}9(R\xf3\xab\xd9/\xf4\xbb[\xb2l\xb6\bt\xf2\xbeg\x95\x8966\xc2^f\xec\xe7\x00\x00\u07d4}DRg\u015a\xb8\u04a2\xd9\xe7\t\x99\x0e\th%\x80\u011f\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94}U\x13\x97\xf7\x9a)\x88\xb0d\xaf\xd0\xef\xeb\xee\x80,w!\xbc\x8a\bW\xe0\xd6\xf1\xdav\xa0\x00\x00\u07d4}Z\xa3?\xc1KQ\x84\x1a\x06\x90n\xdb+\xb4\x9c*\x11ri\x89\x10D\x00\xa2G\x0eh\x00\x00\xe0\x94}]/s\x94\x9d\xad\xda\bV\xb2\x06\x98\x9d\xf0\a\x8dQ\xa1\xe5\x8a\x02\xc4:H\x1d\xf0M\x01wb\xed\xcb\\\xaab\x9bZ\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4~\x8f\x96\xcc)\xf5{\tu\x12\f\xb5\x93\xb7\u0743=`kS\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4~\x97*\x8a|*D\xc9;!Cl8\xd2\x1b\x92R\xc3E\xfe\x89a\t=|,m8\x00\x00\u07d4~\x99\u07fe\x98\x9d;\xa5)\u0457Q\xb7\xf41\u007f\x89S\xa3\xe2\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4~\xa0\xf9n\xe0\xa5s\xa30\xb5h\x97v\x1f=L\x010\xa8\xe3\x89Hz\x9a0E9D\x00\x00\u0794~\xa7\x91\xeb\xab\x04E\xa0\x0e\xfd\xfcNJ\x8e\x9a~ue\x13m\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4~\xab\xa05\xe2\xaf7\x93\xfdtgK\x10%@\xcf\x19\n\u0779\x89E\x02l\x83[`D\x00\x00\xe0\x94~\xb4\xb0\x18\\\x92\xb6C\x9a\b\xe72!h\xcb5<\x8awJ\x8a\x02'\x19l\xa0I\x83\xca\x00\x00\xe0\x94~\xbd\x95\xe9\xc4p\xf7(5\x83\xdcn\x9d,M\xce\v\ua3c4\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4~\u0425\xa8G\xbe\xf9\xa9\xda|\xba\x1dd\x11\xf5\xc3\x161&\x19\x89\x02(\xeb7\xe8u\x1d\x00\x00\u07d4~\xda\xfb\xa8\x98K\xafc\x1a\x82\vk\x92\xbb\xc2\xc56U\xf6\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4~\xdb\x02\xc6\x1a\"r\x87a\x1a\xd9Pici\xccNdzh\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4~\xe5\u0280]\xce#\xaf\x89\xc2\xd4D\xe7\xe4\af\xc5Lt\x04\x89\r\v\xd4\x12\xed\xbd\x82\x00\x00\xe0\x94~\xe6\x04\u01e9\xdc)\t\xce2\x1d\u6e72OWgWuU\x8a\x01+\xf9\u01d8\\\xf6-\x80\x00\u07d4~\xf1o\xd8\xd1[7\x8a\x0f\xba0k\x8d\x03\u0758\xfc\x92a\x9f\x89%\xf2s\x93=\xb5p\x00\x00\u07d4~\xf9\x8bR\xbe\xe9S\xbe\xf9\x92\xf3\x05\xfd\xa0'\xf8\x91\x1cXQ\x89\x1b\xe7\" i\x96\xbc\x80\x00\u07d4~\xfc\x90vj\x00\xbcR7,\xac\x97\xfa\xbd\x8a<\x83\x1f\x8e\u0349\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4~\xfe\xc0\xc6%<\xaf9\u007fq(|\x1c\a\xf6\xc9X+[\x86\x89\x1a,\xbc\xb8O0\u0540\x00\u07d4\u007f\x01\xdc|7G\xca`\x8f\x98=\xfc\x8c\x9b9\xe7U\xa3\xb9\x14\x89\v8l\xad_zZ\x00\x00\u07d4\u007f\x06b\xb4\x10)\x8c\x99\xf3\x11\u04e1EJ\x1e\xed\xba/\xeav\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\u007f\x06\u021dY\x80\u007f\xa6\v\xc6\x016\xfc\xf8\x14\u02ef%C\xbd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u007f\v\x90\xa1\xfd\u050f'\xb2h\xfe\xb3\x83\x82\xe5]\xdbP\xef\x0f\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\u007f\x0e\xc3\u06c0F\x92\xd4\xd1\xea2E6Z\xab\x05\x90\a[\u0109\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u007f\x0f\x04\xfc\xf3zS\xa4\xe2N\xden\x93\x10Nx\xbe\x1d<\x9e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u007f\x13\xd7`I\x8dq\x93\xcahY\xbc\x95\xc9\x018d#\xd7l\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\u007f\x15\n\xfb\x1aw\u00b4Y(\xc2h\xc1\u9f74d\x1dG\u0609lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\x16\x19\x98\x8f7\x15\xe9O\xf1\xd2S&-\xc5X\x1d\xb3\xde\x1c\x890\xca\x02O\x98{\x90\x00\x00\u07d4\u007f\x1c\x81\xee\x16\x97\xfc\x14K|\v\xe5I;V\x15\xae\u007f\xdd\u0289\x1b\x1d\xaba\u04ead\x00\x00\u07d4\u007f#\x82\xff\xd8\xf89VFy7\xf9\xbar7F#\xf1\x1b8\x89 \x86\xac5\x10R`\x00\x00\u07d4\u007f7\t9\x1f?\xbe\xba5\x92\xd1u\xc7@\xe8z\tT\x1d\x02\x89\x1a\x05V\x90\xd9\u06c0\x00\x00\u07d4\u007f8\x9c\x12\xf3\xc6\x16OdFVlwf\x95\x03\xc2y%'\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\xe0\x94\u007f:\x1eE\xf6~\x92\u0200\xe5s\xb43y\xd7\x1e\xe0\x89\xdbT\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\u007f=r\x03\u0224G\xf7\xbf6\u060a\xe9\xb6\x06*^\xeex\xae\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\u007fF\xbb%F\r\xd7\xda\xe4!\x1c\xa7\xf1Z\xd3\x12\xfc}\xc7\\\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\u007fI\xe7\xa4&\x98\x82\xbd\x87\"\u0526\xf5f4v)b@y\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u007fI\xf2\a&G\x1a\xc1\u01e8>\xf1\x06\xe9w\\\xebf%f\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\u007fK^'\x85x\xc0F\xcc\xea\xf6W0\xa0\xe0h2\x9e\u0576\x89e\xea=\xb7UF`\x00\x00\u07d4\u007fOY;a\x8c3\v\xa2\xc3\xd5\xf4\x1e\xce\xeb\x92\xe2~Bl\x89\x96n\xdcuk|\xfc\x00\x00\u07d4\u007fT\x14\x91\u04ac\x00\xd2a/\x94\xaa\u007f\v\xcb\x01FQ\xfb\u0509\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\u007fZ\xe0Z\xe0\xf8\xcb\xe5\xdf\xe7!\xf0D\u05e7\xbe\xf4\xc2y\x97\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\u007f`:\xec\x17Y\xea_\a\xc7\xf8\xd4\x1a\x14(\xfb\xba\xf9\xe7b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u007falo\x00\x8a\u07e0\x82\xf3M\xa7\xd0e\x04`6\x80u\xfb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u007fa\xfal\xf5\xf8\x98\xb4@\xda\u016b\xd8`\rmi\x1f\xde\xf9\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94\u007fe\\g\x89\xed\xdfE\\\xb4\xb8\x80\x99r\x0698\x9e\ubb0a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u007fk(\u0204!\xe4\x85~E\x92\x81\u05c4ai$\x89\xd3\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007fn\xfboC\x18\x87m.\xe6$\xe2u\x95\xf4DF\xf6\x8e\x93\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u007fq\x92\xc0\xdf\x1c}\xb6\xd9\xede\xd7\x11\x84\xd8\xe4\x15Z\x17\xba\x89\x04Sr\x8d3\x94,\x00\x00\u07d4\u007fz:!\xb3\xf5\xa6]\x81\xe0\xfc\xb7\xd5-\xd0\n\x1a\xa3m\xba\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u007f\x8d\xbc\xe1\x80\xed\x9cV65\xaa\xd2\xd9{L\xbcB\x89\x06\u0649\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\u007f\x99=\xdb~\x02\u0082\xb8\x98\xf6\x15_h\x0e\xf5\xb9\xaf\xf9\a\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\u007f\x9f\x9bV\xe4(\x9d\xfbX\xe7\x0f\xd5\xf1*\x97\xb5m5\u01a5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u007f\xa3~\xd6x\x87u\x1aG\x1f\x0e\xb3\x06\xbeD\xe0\xdb\xcd`\x89\x899vt\u007f\xe1\x1a\x10\x00\x00\u07d4\u007f\xaa0\xc3\x15\x19\xb5\x84\xe9rP\xed*<\xf38^\xd5\xfdP\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xcf[\xa6fo\x96lTH\xc1{\xf1\xcb\v\xbc\xd8\x01\x9b\x06\x89\x05k\xc3\u042e\xbeI\x80\x00\xe0\x94\u007f\xd6y\xe5\xfb\r\xa2\xa5\xd1\x16\x19M\xcbP\x83\x18\xed\u0140\xf3\x8a\x01c\x9eI\xbb\xa1b\x80\x00\x00\u07d4\u007f\u06e01\u01cf\x9c\tmb\xd0Z6\x9e\uac3c\xccU\u5257\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\u007f\xdb\u00e8D\xe4\r\x96\xb2\xf3\xa652.`e\xf4\xca\x0e\x84\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xdf\u020dx\xbf\x1b(Z\xc6O\x1a\xdb5\xdc\x11\xfc\xb09Q\x89|\x06\xfd\xa0/\xb06\x00\x00\u07d4\u007f\xea\x19b\xe3]b\x05\x97h\xc7I\xbe\u0756\u02b90\xd3x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xef\x8c8w\x9f\xb3\a\xeco\x04K\xeb\xe4\u007f<\xfa\xe7\x96\xf1\x89\t#@\xf8l\xf0\x9e\x80\x00\u07d4\u007f\xf0\xc6?p$\x1b\xec\xe1\x9bs~SA\xb1+\x10\x901\u0609\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\u007f\xfa\xbf\xbc9\f\xbeC\u0389\x18\x8f\bh\xb2}\xcb\x0f\f\xad\x8a\x01YQ\x82\"K&H\x00\x00\xe0\x94\u007f\xfd\x02\xed7\fp`\xb2\xaeS\xc0x\xc8\x01!\x90\u07fbu\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794\x80\x02*\x12\a\xe9\x10\x91\x1f\xc9(I\xb0i\xab\f\xda\xd0C\u04c8\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x80\t\xa7\xcb\u0452\xb3\xae\u052d\xb9\x83\xd5(ER\xc1ltQ\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80\x0e}c\x1cnW:\x903/\x17\xf7\x1f_\u045bR\x8c\xb9\x89\b=lz\xabc`\x00\x00\u07d4\x80\x15m\x10\ufa320\u0254\x10c\r7\xe2i\xd4\t<\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\x172\xa4\x81\u00c0\xe5~\xd6-l)\u0799\x8a\xf3\xfa;\x13\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x1de\xc5\x18\xb1\x1d\x0e?OG\x02!Ap\x13\xc8\xe5>\u0149\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80&CZ\xacr\x8dI{\x19\xb3\xe7\xe5|(\xc5c\x95O+\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x80-\xc3\xc4\xff-}\x92^\u215fJ\x06\u05fa`\xf10\x8c\x89\x05P\x94\f\x8f\xd3L\x00\x00\u07d4\x800\xb1\x11\u0198?\x04\x85\u076c\xa7b$\xc6\x18\x064x\x9f\x89\x04V9\x18$O@\x00\x00\u07d4\x805\xbc\xff\xae\xfd\xee\xea5\x83\fI}\x14(\x9d6 #\u0789\x10CV\x1a\x88)0\x00\x00\u07d4\x805\xfeNkj\xf2z\u44a5xQ^\x9d9\xfao\xa6[\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80C\xed\"\xf9\x97\u58a4\xc1n6D\x86\xaed\x97V\x92\u0109=I\x04\xff\xc9\x11.\x80\x00\u07d4\x80C\xfd\u043cL\x97=\x16c\xd5_\xc15P\x8e\xc5\xd4\xf4\xfa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x80L\xa9IrcOc:Q\xf3V\v\x1d\x06\xc0\xb2\x93\xb3\xb1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x80R-\u07d4N\xc5.'\xd7$\xedL\x93\xe1\xf7\xbe`\x83\u0589\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x80Y\x1aB\x17\x9f4\xe6M\x9d\xf7]\xcdF;(hoUt\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x80\\\xe5\x12\x97\xa0y;\x81 g\xf0\x17\xb3\xe7\xb2\u07db\xb1\xf9\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x80]\x84o\xb0\xbc\x02\xa73r&\u0585\xbe\x9e\xe7s\xb9\x19\x8a\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x80c7\x9a{\xf2\u02d2:\x84\xc5\t>h\xda\xc7\xf7T\x81\u0149\x11v\x10.n2\xdf\x00\x00\u07d4\x80hTX\x8e\xcc\xe5AI_\x81\u008a)\x03s\xdf\x02t\xb2\x89\x1f\x8c\xdf\\n\x8dX\x00\x00\u07d4\x80oD\xbd\xebh\x807\x01^\x84\xff!\x80I\xe3\x823*3\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\x80tF\x18\xde9jT1\x97\xeeH\x94\xab\xd0c\x98\xdd|'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80w\xc3\xe4\xc4EXn\tL\xe1\x02\x93\u007f\xa0[s{V\x8c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x90\u007fY1H\xb5|F\xc1w\xe2=%\xab\u012a\xe1\x83a\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x97s\x16\x94NYB\xe7\x9b\x0e:\xba\u04cd\xa7F\be\x19\x89\x02\x1auJm\xc5(\x00\x00\xe0\x94\x80\xa0\xf6\xcc\x18l\xf6 \x14\x00sn\x06Z9\x1fR\xa9\xdfJ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x80\xab\xecZ\xa3n\\\x9d\t\x8f\x1b\x94(\x81\xbdZ\xca\u0196=\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xb2=8\v\x82\\F\xe098\x99\xa8UVF-\xa0\u1309lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xb4-\xe1p\xdb\xd7#\xf4T\xe8\x8fw\x16E-\x92\x98P\x92\x89\x10F#\xc0v-\xd1\x00\x00\u07d4\x80\xb7\x9f3\x83\x90\u047a\x1b77\xa2\x9a\x02W\xe5\xd9\x1e\a1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x80\xbf\x99^\u063a\x92p\x1d\x10\xfe\u011f\x9e}\x01M\xbe\xe0&\x89\x1f\x047\xca\x1a~\x12\x80\x00\u07d4\x80\xc0N\xfd1\x0fD\x04\x83\xc7?tK[\x9edY\x9c\xe3\xec\x89A\rXj \xa4\xc0\x00\x00\u07d4\x80\u00e9\xf6\x95\xb1m\xb1Yr\x86\u0473\xa8\xb7il9\xfa'\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\xc5>\xe7\xe35\u007f\x94\xce\rxh\x00\x9c \x8bJ\x13\x01%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xcc!\xbd\x99\xf3\x90\x05\u014f\xe4\xa4H\x90\x92 !\x8ff\u02c966\xc9yd6t\x00\x00\u07d4\x80\xd5\xc4\fY\xc7\xf5N\xa3\xa5_\xcf\xd1uG\x1e\xa3P\x99\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x80\xda/\u0762\x9a\x9e'\xf9\xe1\x15\x97^i\xae\x9c\xfb\xf3\xf2~\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x80\xe7\xb3 R0\xa5f\xa1\xf0a\xd9\"\x81\x9b\xb4\xd4\u04a0\xe1\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x80\xea\x1a\xcc\x13n\xcaKh\xc8B\xa9Z\xdfk\u007f\xee~\xb8\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80\xf0z\xc0\x9e{,<\n=\x1e\x94\x13\xa5D\xc7:A\xbe\u02c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x81\r\xb2Vu\xf4^\xa4\xc7\xf3\x17\u007f7\xce)\xe2-g\x99\x9c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81\x13\x9b\xfd\u0326V\xc40 ?r\x95\x8cT;e\x80\xd4\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x81\x14a\xa2\xb0\u0290\xba\xda\xc0j\x9e\xa1nx{3\xb1\x96\u0309\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\x81\x16M\xeb\x10\x81J\xe0\x83\x91\xf3,\bf{bH\xc2}z\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\x81\x18i1\x18A7\xd1\x19*\u020c\xd3\xe1\xe5\xd0\xfd\xb8jt\x89\x9d5\x95\xab$8\xd0\x00\x00\u0794\x81*U\xc4<\xae\xdcYr\x187\x90\x00\xceQ\rT\x886\xfd\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x81.\xa7\xa3\xb2\xc8n\xed2\xffO,sQL\xc6;\xac\xfb\u038965\u026d\xc5\u07a0\x00\x00\u07d4\x814\xdd\x1c\x9d\xf0\xd6\u0225\x81$&\xbbU\xc7a\u0283\x1f\b\x89\x06\xa2\x16\v\xb5|\xcc\x00\x00\u07d4\x81A5\u068f\x98\x11\aW\x83\xbf\x1a\xb6pb\xaf\x8d>\x9f@\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x81I\x8c\xa0{\x0f/\x17\xe8\xbb\xc7\xe6\x1a\u007fJ\xe7\xbef\xb7\x8b\x89\x05\x81\xfb\xb5\xb3;\xb0\x00\x00\u07d4\x81Um\xb2sI\xab\x8b'\x00ID\xedP\xa4n\x94\x1a\x0f_\x89\u063beI\xb0+\xb8\x00\x00\u07d4\x81U\xfalQ\xeb1\xd8\bA-t\x8a\xa0\x86\x10P\x18\x12/\x89e\xea=\xb7UF`\x00\x00\xe0\x94\x81V6\v\xbd7\ta\xce\xcakf\x91\xd7P\x06\xad L\xf2\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x81a\xd9@\xc3v\x01\x00\xb9\b\x05)\xf8\xa6\x03%\x03\x0fn\u0709\x10CV\x1a\x88)0\x00\x00\xe0\x94\x81d\xe7\x83\x14\xae\x16\xb2\x89&\xccU=,\xcb\x16\xf3V'\r\x8a\x01\xca\x13N\x95\xfb2\xc8\x00\x00\u07d4\x81e\u02b0\xea\xfbZ2\x8f\xc4\x1a\xc6M\xaeq[.\xef,e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81h\xed\xce\u007f)a\xcf)[\x9f\xcdZE\xc0l\xde\xdan\xf5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81m\x97r\xcf\x119\x91\x16\xcc\x1er\xc2lgt\xc9\xed\xd79\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81s\xc85djg.\x01R\xbe\x10\xff\xe8Ab\xdd%nL\x89\x1a\xab\xdf!E\xb40\x00\x00\u07d4\x81t\x93\u035b\xc6#p*$\xa5o\x9f\x82\xe3\xfdH\xf3\xcd1\x89\x9eK#\xf1-L\xa0\x00\x00\u07d4\x81y\xc8\tp\x18,\u0177\xd8*M\xf0n\xa9M\xb6:%\xf3\x89'o%\x9d\xe6k\xf4\x00\x00\u07d4\x81z\xc3;\xd8\xf8GVsr\x95\x1fJ\x10\u05e9\x1c\xe3\xf40\x89\n\xd7\xc4\x06\xc6m\xc1\x80\x00\xe0\x94\x81\x8f\xfe'\x1f\u00d75e\xc3\x03\xf2\x13\xf6\xd2\u0689\x89~\xbd\x8a\x016\xe0SB\xfe\u1e40\x00\u07d4\x81\x97\x94\x81!s.c\xd9\xc1H\x19N\xca\xd4n0\xb7I\u0209\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x81\x9a\xf9\xa1\xc2s2\xb1\xc3i\xbb\xda\x1b=\xe1\xc6\xe93\xd6@\x89\x11\t\xe6T\xb9\x8fz\x00\x00\xe0\x94\x81\x9c\u06a506x\xef|\xecY\u050c\x82\x16:\xcc`\xb9R\x8a\x03\x13QT_y\x81l\x00\x00\u07d4\x81\x9e\xb4\x99\vZ\xbaUG\t=\xa1+k<\x10\x93\xdfmF\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81\xa8\x81\x96\xfa\xc5\xf2<>\x12\xa6\x9d\xecK\x88\x0e\xb7\xd9s\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x81\xbc\xcb\xff\x8fD4~\xb7\xfc\xa9['\xce|\x95$\x92\xaa\xad\x89\b@\xc1!e\xddx\x00\x00\u07d4\x81\xbdu\xab\xd8e\xe0\xc3\xf0J\vO\xdb\xcbt\xd3@\x82\xfb\xb7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x81\xc1\x8c*#\x8d\xdcL\xba#\n\a-\xd7\xdc\x10\x1eb\x02s\x89Hz\x9a0E9D\x00\x00\u07d4\x81\xc9\xe1\xae\xe2\xd36]S\xbc\xfd\u0356\xc7\xc58\xb0\xfd~\xec\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\x81\u03edv\t\x13\xd3\xc3\"\xfc\xc7{I\u00ae9\a\xe7On\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x81\xd6\x19\xffW&\xf2@_\x12\x90Lr\xeb\x1e$\xa0\xaa\xeeO\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x81\xef\u25aev\xc8`\xd1\xc5\xfb\xd3=G\xe8\u0399\x96\xd1W\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81\xf8\xde,(=_\u052f\xbd\xa8]\xed\xf9v\x0e\xab\xbb\xb5r\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x82\f\x19)\x11\x96P[e\x05\x9d\x99\x14\xb7\t\v\xe1\u06c7\u0789\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x82\x1c\xb5\xcd\x05\xc7\uf41f\xe1\xbe`s=\x89c\xd7`\xdcA\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x82\x1dy\x8a\xf1\x99\x89\u00ee[\x84\xa7\xa7(<\xd7\xfd\xa1\xfa\xbe\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x82\x1e\xb9\t\x94\xa2\xfb\xf9K\xdc23\x91\x02\x96\xf7o\x9b\xf6\xe7\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x82$\x9f\xe7\x0fa\u01b1o\x19\xa3$\x84\x0f\xdc\x02\x021\xbb\x02\x8a\x02\x036\xb0\x8a\x93c[\x00\x00\u07d4\x82(\xeb\xc0\x87H\x0f\xd6EG\xca(\x1f^\xac\xe3\x04\x14S\xb9\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\x82)\u03b9\xf0\xd7\b9I\x8dD\xe6\xab\xed\x93\xc5\xca\x05\x9f]\x8a\x1a\x1c\x1b<\x98\x9a \x10\x00\x00\u07d4\x82.\xdf\xf66V:a\x06\xe5.\x9a%\x98\xf7\xe6\xd0\xef'\x82\x89\x01\xf4\xf9i=B\u04c0\x00\u07d4\x822\x19\xa2Yv\xbb*\xa4\xaf\x8b\xadA\xac5&\xb4\x936\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x822\xd1\xf9t.\u07cd\xd9'\xda5;*\xe7\xb4\xcb\xceu\x92\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\x824\xf4c\u0444\x85P\x1f\x8f\x85\xac\xe4\x97,\x9bc-\xbc\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\x827htg7\xcem\xa3\x12\xd5>TSN\x10o\x96|\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82;\xa7dr8\xd1\x13\xbc\xe9\x96JC\u0420\x98\x11\x8b\xfeM\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x82@t1(\x06\xdaGHCBf\xee\x00!@\u305a\u0089Q\xb1\u04c3\x92a\xac\x00\x00\u07d4\x82C\x8f\u04b3*\x9b\xddgKI\xd8\xcc_\xa2\xef\xf9x\x18G\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82HW(\xd0\xe2\x81V7X\xc7Z\xb2~\xd9\u80a0\x00-\x89\a\xf8\b\xe9)\x1el\x00\x00\u07d4\x82K<\x19)]~\xf6\xfa\xa7\xf3t\xa4y\x84\x86\xa8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82Q5\x8c\xa4\xe0`\u0775Y\xcaX\xbc\v\u077e\xb4\a\x02\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82Q5\xb1\xa7\xfc\x16\x05aL\x8a\xa4\u042cm\xba\u040fH\x0e\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\x82S\t\xa7\xd4]\x18\x12\xf5\x1en\x8d\xf5\xa7\xb9ol\x90\x88\x87\x89\x804\xf7\u0671f\xd4\x00\x00\u07d4\x82Z\u007fN\x10\x94\x9c\xb6\xf8\x96Bh\xf1\xfa_W\xe7\x12\xb4\u0109\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82a\xfa#\f\x90\x1dC\xffW\x9fG\x80\u04d9\xf3\x1e`v\xbc\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82b\x16\x9baXp\x13N\xb4\xacl_G\x1ck\xf2\xf7\x89\xfc\x89\x19\x12z\x13\x91\xea*\x00\x00\u07d4\x82c\xec\xe5\xd7\t\xe0\u05eeq\u0328h\xed7\xcd/\xef\x80{\x895\xab\x02\x8a\xc1T\xb8\x00\x00\xe0\x94\x82l\xe5y\x052\xe0T\x8ca\x02\xa3\r>\xac\x83k\xd68\x8f\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4\x82n\xb7\xcds\x19\xb8-\xd0z\x1f;@\x90q\xd9n9g\u007f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x82u1\xa6\u0141z\xe3_\x82\xb0\v\x97T\xfc\xf7LU\xe22\x89\xc3(\t>a\xee@\x00\x00\u0794\x82u\xcdhL6y\u0548}\x03fN3\x83E\xdc<\xdd\xe1\x88\xdbD\xe0I\xbb,\x00\x00\u07d4\x82\x84\x92;b\u62ff|+\x9f4\x14\xd1>\xf6\xc8\x12\xa9\x04\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\x82\x8b\xa6Q\u02d3\x0e\xd9xqV)\x9a=\xe4L\u040br\x12\x89Hz\x9a0E9D\x00\x00\u07d4\x82\xa1\\\xef\x1dl\x82`\xea\xf1Y\xea?\x01\x80\xd8g}\xce\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82\xa8\xb9kl\x9e\x13\xeb\xec\x1e\x9f\x18\xac\x02\xa6\x0e\xa8\x8aH\xff\x89lk\x8c@\x8es\xb3\x00\x00\u07d4\x82\xa8\u02ff\xdf\xf0+.8\xaeK\xbf\xca\x15\xf1\xf0\xe8;\x1a\xea\x89\x04\x9b\x99\x1c'\xefm\x80\x00\u07d4\x82\xe4F\x1e\xb9\xd8I\xf0\x04\x1c\x14\x04!\x9eBr\u0110\n\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82\xe5w\xb5\x15\xcb+\b`\xaa\xfe\x1c\xe0\x9aY\xe0\x9f\xe7\xd0@\x89 \x86\xac5\x10R`\x00\x00\u07d4\x82\xea\x01\xe3\xbf.\x83\x83nqpN\"\xa2q\x93w\xef\xd9\u00c9\xa4\xccy\x95c\u00c0\x00\x00\u07d4\x82\xf2\xe9\x91\xfd2L_]\x17v\x8e\x9fa3]\xb61\x9dl\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x82\xf3\x9b'X\xaeB'{\x86\u059fu\xe6(\xd9X\xeb\u02b0\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94\x82\xf8T\xc9\xc2\xf0\x87\xdf\xfa\x98Z\xc8 \x1ebl\xa5Fv\x86\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x82\xffqo\xdf\x03>\xc7\xe9B\xc9\t\u0643\x18g\xb8\xb6\xe2\xef\x89a\t=|,m8\x00\x00\u07d4\x83\b\xed\n\xf7\xf8\xa3\xc1u\x1f\xaf\xc8w\xb5\xa4*\xf7\xd3X\x82\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x83\x1cD\xb3\b@G\x18K*\xd2\x18h\x06@\x907P\xc4]\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x83!\x05\x83\xc1jN\x1e\x1d\xac\x84\xeb\xd3~=\x0f|W\ub909lk\x93[\x8b\xbd@\x00\x00\u07d4\x83,T\x17k\xdfC\xd2\u027c\u05f8\b\xb8\x95V\xb8\x9c\xbf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x833\x16\x98]Gt+\xfe\xd4\x10`J\x91\x95<\x05\xfb\x12\xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x834vK{9zNW\x8fP6M`\xceD\x89\x9b\xff\x94\x89\x05\x03\xb2\x03\xe9\xfb\xa2\x00\x00\xe0\x94\x83;j\x8e\xc8\xda@\x81\x86\xac\x8a}*m\xd6\x15#\xe7\u0384\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x83=?\xaeT*\xd5\xf8\xb5\f\xe1\x9b\xde+\xecW\x91\x80\u020c\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\x83=\xb4,\x14\x16<{\xe4\u02b8j\u0153\xe0bf\u0599\u054a$\xe4\r+iC\xef\x90\x00\x00\xe0\x94\x83V;\xc3d\ud060\xc6\xda;V\xffI\xbb\xf2g\x82z\x9c\x8a\x03\xab\x91\xd1{ \xdeP\x00\x00\u07d4\x83zd]\xc9\\IT\x9f\x89\x9cN\x8b\u03c7S$\xb2\xf5|\x89 \x8c9J\xf1\u0208\x00\x00\u07d4\x83\x8b\xd5e\xf9\x9f\xdeH\x05?y\x17\xfe3<\xf8J\xd5H\xab\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x83\x90\x8a\xa7G\x8am\x1c\x9b\x9b\x02\x81\x14\x8f\x8f\x9f$+\x9f\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\x83\x92\xe57vq5x\x01[\xffI@\xcfC\x84\x9d}\u02e1\x89\bM\xf05]V\x17\x00\x00\xe0\x94\x83\x97\xa1\xbcG\xac\xd6GA\x81Y\xb9\x9c\xeaW\xe1\xe6S-n\x8a\x01\xf1\x0f\xa8'\xb5P\xb4\x00\x00\u07d4\x83\x98\xe0~\xbc\xb4\xf7_\xf2\x11m\xe7|\x1c*\x99\xf3\x03\xa4\u03c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xa3\x14\x883\xd9dI\x84\xf7\xc4u\xa7\x85\a\x16\ufd00\xff\x89\xb8Pz\x82\a( \x00\x00\u07d4\x83\xa4\x02C\x8e\x05\x19w=TH2k\xfba\xf8\xb2\f\xf5-\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\x83\xa9;[\xa4\x1b\xf8\x87 \xe4\x15y\f\xdc\vg\xb4\xaf4\u0109\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x83\xc2=\x8aP!$\xee\x15\x0f\b\xd7\x1d\xc6rt\x10\xa0\xf9\x01\x8a\a3\x1f;\xfef\x1b\x18\x00\x00\u07d4\x83\u0217\xa8Ki^\xeb\xe4fy\xf7\xda\x19\xd7vb\x1c&\x94\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xd52\u04cdm\xee?`\xad\u018b\x93a3\u01e2\xa1\xb0\u0749\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xdb\xf8\xa1(S\xb4\n\xc6\x19\x96\xf8\xbf\x1d\xc8\xfd\xba\xdd\xd3)\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x83\xdb\xfd\x8e\xda\x01\xd0\u078e\x15\x8b\x16\u0413_\xc28\n]\u01c9 \x86\xac5\x10R`\x00\x00\u07d4\x83\xe4\x80U2|(\xb5\x93o\xd9\xf4D~s\xbd\xb2\xdd3v\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\x83\xfeZ\x1b2\x8b\xaeD\a\x11\xbe\xafj\xad`&\xed\xa6\xd2 \x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x84\x00\x8ar\xf8\x03o?\xeb\xa5B\xe3Px\xc0W\xf3*\x88%\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x84\x0e\xc8>\xa96!\xf04\xe7\xbb7b\xbb\x8e)\xde\xd4\xc4y\x89\x87\x86x2n\xac\x90\x00\x00\xe0\x94\x84\x11E\xb4H@\xc9F\xe2\x1d\xbc\x19\x02d\xb8\xe0\xd5\x02\x93i\x8a?\x87\bW\xa3\xe0\xe3\x80\x00\x00\u07d4\x84#!\a\x93+\x12\xe01\x86X5%\xce\x02:p>\xf8\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4\x84$O\xc9ZiW\xed|\x15\x04\xe4\x9f0\xb8\xc3^\xcaKy\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x841'}{\xdd\x10E}\xc0\x17@\x8c\x8d\xbb\xbdAJ\x8d\xf3\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4\x847Z\xfb\xf5\x9b:\x1da\xa1\xbe2\xd0u\xe0\xe1ZO\xbc\xa5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x84;\xd3P/E\xf8\xbcM\xa3p\xb3#\xbd\xac?\xcf_\x19\xa6\x89P\x03\x9dc\xd1\x1c\x90\x00\x00\u07d4\x84P34c\rw\xf7AG\xf6\x8b.\bf\x13\xc8\xf1\xad\xe9\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x84R\x03u\x0fqH\xa9\xaa&)!\xe8mC\xbfd\x19t\xfd\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x84a\xec\u0126\xa4^\xb1\xa5\xb9G\xfb\x86\xb8\x80i\xb9\x1f\xcdo\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84g^\x91wrmE\xea\xa4k9\x92\xa3@\xba\u007fq\f\x95\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x84hl{\xadv,T\xb6g\u055f\x90\x94<\xd1M\x11z&\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\x89\xf6\xad\x1d\x9a\x94\xa2\x97x\x91V\x89\x9d\xb6AT\xf1\u06f5\x89\x13t\a\xc0<\x8c&\x80\x00\u07d4\x84\x8c\x99Jy\x00?\xe7\xb7\xc2l\xc62\x12\xe1\xfc/\x9c\x19\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\x8f\xbd)\xd6|\xf4\xa0\x13\xcb\x02\xa4\xb1v\xef$N\x9e\u6349\x01\x17*ck\xbd\xc2\x00\x00\u07d4\x84\x94\x9d\xbaU\x9ac\xbf\xc8E\xde\xd0n\x9f-\x9b\u007f\x11\xef$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\x9a\xb8\a\x90\xb2\x8f\xf1\xff\u05ba9N\xfctc\x10\\6\xf7\x89\x01\xe0+\xe4\xael\x84\x00\x00\u07d4\x84\x9b\x11oYc\x01\xc5\u063bb\xe0\xe9z\x82H\x12n9\xf3\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x84\xa7L\xee\xcf\xf6\\\xb9;/\x94\x9dw>\xf1\xad\u007f\xb4\xa2E\x89\x05\n\x9bDF\x85\xc7\x00\x00\u07d4\x84\xaa\xc7\xfa\x19\u007f\xf8\\0\xe0;zS\x82\xb9W\xf4\x1f:\xfb\x89\b\x8b#\xac\xff\u0650\x00\x00\u07d4\x84\xaf\x1b\x15sB\xd5Ch&\r\x17\x87b0\xa54\xb5K\x0e\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x84\xb0\xeek\xb87\u04e4\xc4\xc5\x01\x1c:\"\x8c\x0e\u06b4cJ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\xb4\xb7Nf#\xba\x9d\x15\x83\xe0\u03feId?\x168AI\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\xb6\xb6\xad\xbe/[>-h,f\xaf\x1b\u0110S@\xc3\xed\x89!\x92\xf8\xd2\"\x15\x00\x80\x00\xe0\x94\x84\xb9\x1e.)\x02\xd0^+Y\x1bA\b;\u05fe\xb2\xd5,t\x8a\x02\x15\xe5\x12\x8bE\x04d\x80\x00\u07d4\x84\xbc\xbf\"\xc0\x96\a\xac\x844\x1d.\xdb\xc0;\xfb\x179\xd7D\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x84\xbf\xce\xf0I\x1a\n\xe0iK7\u03ac\x02E\x84\xf2\xaa\x04g\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x84\xcb}\xa0P-\xf4\\\xf5a\x81{\xbd#b\xf4Q\xbe\x02\u0689Hz\x9a0E9D\x00\x00\u07d4\x84\xccxx\xda`_\xdb\x01\x9f\xab\x9bL\xcf\xc1Wp\x9c\u0765\x89Hy\x85\x13\xaf\x04\xc9\x00\x00\u07d4\x84\xdb\x14Y\xbb\x00\x81.\xa6~\xcb=\xc1\x89\xb7!\x87\xd9\xc5\x01\x89\b\x11\xb8\xfb\u0685\xab\x80\x00\u07d4\x84\u9516\x80\xbe\xcehA\xb9\xa7\xe5%\r\b\xac\xd8}\x16\u0349\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x84\xe9\u03c1f\xc3j\xbf\xa4\x90S\xb7\xa1\xad@6 &\x81\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\xec\x06\xf2G\x00\xfeBAL\xb9\x89|\x15L\x88\xde/a2\x89Hz\x9a0E9D\x00\x00\xe0\x94\x84\xf5\"\xf0R\x0e\xbaR\xdd\x18\xad!\xfaK\x82\x9f+\x89\u02d7\x8a\x01\fQ\x06\xd5\x13O\x13\x00\x00\u07d4\x85\v\x9d\xb1\x8f\xf8K\xf0\xc7\xdaI\xea7\x81\xd9 \x90\xad~d\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\x85\x10\xee\x93O\f\xbc\x90\x0e\x10\a\xeb8\xa2\x1e*Q\x01\xb8\xb2\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4\x85\x16\xfc\xafw\u0213\x97\x0f\xcd\x1a\x95\x8b\xa9\xa0\x0eI\x04@\x19\x89\n\xa3\xeb\x16\x91\xbc\xe5\x80\x00\u07d4\x85\x1a\xa9\x1c\x82\xf4/\xad]\xd8\xe8\xbb^\xa6\x9c\x8f:Yw\u0449\b\x0eV\x1f%xy\x80\x00\u07d4\x85\x1c\rb\xbeF5\xd4w~\x805\xe3~K\xa8Q|a2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x85\x1d\u00ca\xdbE\x93r\x9av\xf3:\x86\x16\u06b6\xf5\xf5\x9aw\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x852I\b\x97\xbb\xb4\u038b\u007fk\x83~L\xba\x84\x8f\xbe\x99v\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x85>j\xba\xf4Di\xc7/\x15\x1dN\"8\x19\xac\xedN7(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x85F\x91\xceqO2\\\xedU\xceY(\u039b\xa1/\xac\u0478\x89\xedp\xb5\xe9\xc3\xf2\xf0\x00\x00\u07d4\x85L\fF\x9c$k\x83\xb5\u0473\xec\xa4C\xb3\x9a\xf5\xee\x12\x8a\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x85]\x9a\xef,9\xc6#\r\t\u025e\xf6II\x89\xab\u61c5\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\x85c\u0113a\xb6%\xe7hw\x1c\x96\x15\x1d\xbf\xbd\x1c\x90iv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x85fa\t\x01\xaa\xce8\xb82D\xf3\xa9\xc810jg\xb9\u0709\xb0\x82\x13\xbc\xf8\xff\xe0\x00\x00\xe0\x94\x85j\xa2<\x82\xd7![\xec\x8dW\xf6\n\xd7^\xf1O\xa3_D\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x85nZ\xb3\xf6L\x9a\xb5k\x00\x93\x93\xb0\x16d\xfc\x03$\x05\x0e\x89a\t=|,m8\x00\x00\u07d4\x85n\xb2\x04$\x1a\x87\x83\x0f\xb2)\x03\x13C\xdc0\x85OX\x1a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x85s,\x06\\\xbdd\x11\x99A\xae\xd40\xacYg\vlQ\u0109'\xa5sb\xab\n\x0e\x80\x00\xe0\x94\x85x\xe1\x02\x12\xca\x14\xff\a2\xa8$\x1e7F}\xb8V2\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x85y\xda\xdf\x1a9Z4q\xe2\vov=\x9a\x0f\xf1\x9a?o\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x85\u007f\x10\v\x1aY0\"^\xfc~\x90 \u05c3'\xb4\x1c\x02\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x85\x94mV\xa4\xd3q\xa93hS\x96\x90\xb6\x0e\xc8%\x10tT\x89]\u0212\xaa\x111\xc8\x00\x00\xe0\x94\x85\x99\xcb\u0566\xa9\xdc\u0539f\xbe8}iw]\xa5\xe3C'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86$_Yf\x91\t>\xce?=<\xa2&>\xac\xe8\x19A\u0649\n1\x06+\xee\xedp\x00\x00\u07d4\x86%i!\x1e\x8cc'\xb5A^:g\xe5s\x8b\x15\xba\xafn\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x86)}s\x0f\xe0\xf7\xa9\xee$\xe0\x8f\xb1\b{1\xad\xb3\x06\xa7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86D\xcc(\x1b\xe32\xcc\xce\xd3m\xa4\x83\xfb*\aF\u067a.\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x86I\x9a\x12(\xff-~\xe3\au\x93dPo\x8e\x8c\x83\a\xa5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x86K\xecPi\xf8U\xa4\xfdX\x92\xa6\xc4I\x1d\xb0|\x88\xff|\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86W\n\xb2Y\u0271\xc3,\x97) /w\xf5\x90\xc0}\xd6\x12\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86c\xa2A\xa0\xa8\x9ep\xe1\x82\xc8E\xe2\x10\\\x8a\xd7&K\u03ca\x03#\xb1=\x83\x98\xf3#\x80\x00\u07d4\x86g\xfa\x11U\xfe\xd72\u03f8\u0725\xa0\xd7e\xce\r\a\x05\xed\x89\x04n\xc9e\u00d3\xb1\x00\x00\u07d4\x86h\xaf\x86\x8a\x1e\x98\x88_\x93\u007f&\x15\xde\xd6u\x18\x04\xeb-\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x86t\nFd\x8e\x84Z]\x96F\x1b\x18\t\x1f\xf5{\xe8\xa1o\x8a\x14\xc0\x974\x85\xbf9@\x00\x00\xe0\x94\x86~\xbaVt\x8aY\x045\r,\xa2\xa5\u039c\xa0\vg\n\x9b\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x86\x80dt\xc3X\x04}\x94\x06\xe6\xa0\u007f@\x94[\xc82\x8eg\x8a\x01u.\xb0\xf7\x01=\x10\x00\x00\u07d4\x86\x88=T\xcd9\x15\xe5I\tU0\xf9\xab\x18\x05\xe8\xc5C-\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x86\x8c#\xbe\x874f\xd4\xc7L\"\n\x19\xb2E\xd1x~\x80\u007f\x89J\x13\xbb\xbd\x92\u020e\x80\x00\xe0\x94\x86\x92O\xb2\x11\xaa\xd2<\xf5\xce`\x0e\n\xae\x80c\x96D@\x87\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x86\x93\u9e3e\x94B^\xefyi\xbci\xf9\xd4/|\xadg\x1e\x8967\tlK\xcci\x00\x00\xe0\x94\x86\x9f\x1a\xa3\x0eDU\xbe\xb1\x82 \x91\xde\\\xad\xecy\xa8\xf9F\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x86\xa1\xea\xde\xeb0F\x13E\xd9\xefk\xd0R\x16\xfa$|\r\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86\xa5\xf8%\x9e\u0570\x9e\x18\x8c\xe3F\xee\x92\xd3J\xa5\u0753\xfa\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xb7\xbdV<\uad86\xf9bD\xf9\xdd\xc0*\u05f0\xb1K\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x86\u008bVx\xaf7\xd7'\xec\x05\xe4Dw\x90\xf1_q\xf2\xea\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xc4\xce\x06\u066c\x18[\xb1H\xd9o{z\xbes\xf4A\x00m\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x86\xc8\xd0\u0642\xb59\xf4\x8f\x980\xf9\x89\x1f\x9d`z\x94&Y\x8a\x02\xce\xd3wa\x82O\xb0\x00\x00\u07d4\x86\xc94\xe3\x8eS\xbe;3\xf2t\xd0S\x9c\xfc\xa1Y\xa4\xd0\u04494\x95tD\xb8@\xe8\x00\x00\xe0\x94\x86\xca\x01E\x95~k\r\xfe6\x87_\xbez\r\xecU\xe1z(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x86\u02af\xac\xf3*\xa01|\x03*\xc3k\xab\xed\x97G\x91\xdc\x03\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x86\u0377\xe5\x1a\xc4Gr\xbe6\x90\xf6\x1d\x0eYvn\x8b\xfc\x18\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x86\xdfs\xbd7\u007f,\t\xdec\xc4]g\xf2\x83\xea\xef\xa0\xf4\xab\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86\xe3\xfe\x86\xe9=\xa4\x86\xb1Bf\xea\xdf\x05l\xbf\xa4\xd9\x14C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86\xe8g\x0e'Y\x8e\xa0\x9c8\x99\xabw\x11\u04f9\xfe\x90\x1c\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xefd&!\x19I\xcc7\xf4\xc7^xP6\x9d\f\xf5\xf4y\x8a\x02\xd6_2\xea\x04Z\xf6\x00\x00\u07d4\x86\xf0]\x19\x06>\x93i\xc6\x00N\xb3\xf1#\x94:|\xffN\xab\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x86\xf2>\x9c\n\xaf\u01cb\x9c@M\xcd`3\x9a\x92[\xff\xa2f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x86\xf4\xf4\n\u0644\xfb\xb8\t3\xaebn\x0eB\xf93?\xddA\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x86\xf9\\[\x11\xa2\x93\x94\x0e5\xc0\xb8\x98\u0637_\b\xaa\xb0m\x8a\x06D\xe3\xe8u\xfc\xcft\x00\x00\u07d4\x86\xff\xf2 \xe5\x93\x05\xc0\x9fH8`\xd6\xf9N\x96\xfb\xe3/W\x89\x02S[j\xb4\xc0B\x00\x00\u07d4\x87\a\x96\xab\xc0\u06c4\xaf\x82\xdaR\xa0\xedhsM\xe7\xe66\xf5\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x87\x0f\x15\xe5\u07cb\x0e\xab\xd0%iSz\x8e\xf9;Vx\\B\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\x87\x181`\xd1r\xd2\xe0\x84\xd3'\xb8k\xcb|\x1d\x8eg\x84\xef\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94\x87\x1b\x8a\x8bQ\u07a1\x98\x9aY!\xf1>\xc1\xa9U\xa5\x15\xadG\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x87%\xe8\xc7S\xb3\xac\xbf\u0725_I\x13\\3\x91\x99\x10`)\n\xa7\xf6\u0338\xf8Zx\u06c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x87Pa\xee\x12\xe8 \x04\x1a\x01\x94,\xb0\xe6[\xb4'\xb0\x00`\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\x87XJ?a;\xd4\xfa\xc7L\x1ex\v\x86\xd6\xca\xeb\x89\f\xb2\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x87d\xd0'\"\x00\t\x96\xec\xd4u\xb43)\x8e\x9fT\v\x05\xbf\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x87l?!\x8bGv\xdf<\xa9\xdb\xfb'\r\xe1R\xd9N\xd2R\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x87u\xa6\x10\xc5\x02\xb9\xf1\xe6\xadL\xda\u06cc\xe2\x9b\xffu\xf6\xe4\x89 \x86\xac5\x10R`\x00\x00\u07d4\x87vN6w\xee\xf6\x04\xcb\u015a\xed$\xab\xdcVk\t\xfc%\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\x87\x87\xd1&w\xa5\xec)\x1eW\xe3\x1f\xfb\xfa\xd1\x05\xc32K\x87\x8a\x02\xa2N\xb52\b\xf3\x12\x80\x00\u07d4\x87\x94\xbfG\xd5E@\xec\xe5\xc7\"7\xa1\xff\xb5\x11\u0777Gb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x87\xa5>\xa3\x9fY\xa3[\xad\xa85%!dU\x94\xa1\xa7\x14\u02c9g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x87\xa7\xc5\b\xefqX-\u0665Cr\xf8\x9c\xb0\x1f%/\xb1\x80\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x87\xaf%\xd3\xf6\xf8\xee\xa1S\x13\xd5\xfeEW\xe8\x10\xc5$\xc0\x83\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x87\xb1\x0f\x9c(\x00\x98\x17\x9a+v\xe9\u0390\xbea\xfc\x84M\r\x89Hz\x9a0E9D\x00\x00\u07d4\x87\xbf|\xd5\u0629)\xe1\u01c5\xf9\xe5D\x91\x06\xac#$c\u0249\x047\xb1\x1f\xccEd\x00\x00\u07d4\x87\u0118\x17\t4\xb8#=\x1a\xd1\xe7i1}\\G_/@\x897\b\xba\xed=h\x90\x00\x00\u07d4\x87\xcf6\xad\x03\xc9\xea\xe9\x05:\xbbRB\u0791\x17\xbb\x0f*\v\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x87\u05ec\x06S\xcc\xc6z\xa9\xc3F\x9e\xefCR\x19?}\xbb\x86\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94\x87\xe3\x06+#!\xe9\u07f0\x87\\\u311c\x9b.5\"\xd5\n\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x87\xe6\x03N\xcf#\xf8\xb5c\x9d_\x0e\xa7\n\"S\x8a\x92\x04#\x89\x11\xc7\xea\x16.x \x00\x00\u07d4\x87\xefm\x8bj|\xbf\x9b\\\x8c\x97\xf6~\xe2\xad\u00a7;?w\x89\n\xdd\x1b\xd2<\x00L\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x88F\x92\x8dh2\x89\xa2\xd1\x1d\xf8\xdbz\x94t\x98\x8e\xf0\x13H\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x88I\x80\xebEe\xc1\x04\x83\x17\xa8\xf4\u007f\u06f4a\x96[\u4049\xd8\xd6\x11\x9a\x81F\x05\x00\x00\xe0\x94\x88Jz9\u0411n\x05\xf1\xc2B\xdfU`\u007f7\u07cc_\u068a\x04\xf4\x84<\x15|\x8c\xa0\x00\x00\u07d4\x88T\x93\xbd\xa3j\x042\x97eF\xc1\xdd\xceq\xc3\xf4W\x00!\x89\v\xbfQ\r\xdf\xcb&\x00\x00\xe0\x94\x88`\x9e\nF[n\x99\xfc\xe9\a\x16mW\xe9\xda\b\x14\xf5\u020a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x88m\n\x9e\x17\xc9\xc0\x95\xaf.\xa25\x8b\x89\xecpR\x12\ue509\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\x88y~Xg^\xd5\xccL\x19\x98\a\x83\xdb\xd0\xc9V\bQS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x88|\xacA\xcdpo3E\xf2\xd3J\xc3N\x01u*nY\t\x89 F\\\ue7617\x00\x00\u07d4\x88\x88\x8aW\xbd\x96\x87\xcb\xf9P\xae\xea\u03d7@\xdc\xc4\xd1\xefY\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u0794\x88\x89D\x83\x16\xcc\xf1N\xd8m\xf8\xe2\xf4x\xdcc\xc43\x83@\x88\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x88\x8c\x16\x14I3\x19|\xac&PM\xd7n\x06\xfdf\x00\u01c9\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x88\x8e\x94\x91p\x83\xd1R +S\x1699\x86\x9d'\x11u\xb4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x88\x90\x87\xf6o\xf2\x84\xf8\xb5\xef\xbd)I;pg3\xab\x14G\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\x88\x95\xebrb&\xed\xc3\xf7\x8c\u01a5\x15\a{2\x96\xfd\xb9^\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x88\x97Z_\x1e\xf2R\x8c0\v\x83\xc0\xc6\a\xb8\xe8}\u0593\x15\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\u07d4\x88\x9d\xa4\x0f\xb1\xb6\x0f\x9e\xa9\xbdzE>XL\xf7\xb1\xb4\xd9\xf7\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x88\x9d\xa6b\xebJ\n*\x06\x9d+\xc2K\x05\xb4\xee.\x92\xc4\x1b\x89Z,\x8cTV\xc9\xf2\x80\x00\u07d4\x88\xa1\"\xa28,R91\xfbQ\xa0\u032d;\xeb[rY\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x88\xa2\x15D0\xc0\xe4\x11G\xd3\xc1\xfe\u3cf0\x06\xf8Q\xed\xbd\x8965f3\xeb\xd8\xea\x00\x00\u07d4\x88\xb2\x17\u0337\x86\xa2T\xcfM\xc5\u007f]\x9a\xc3\xc4U\xa3\x04\x83\x892$\xf4'#\xd4T\x00\x00\xe0\x94\x88\xbcC\x01.\xdb\x0e\xa9\xf0b\xacCxC%\n9\xb7\x8f\xbb\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x88\xc2Qj|\xdb\t\xa6'mr\x97\xd3\x0fZM\xb1\xe8K\x86\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x88\xc3ad\rki7;\b\x1c\xe0\xc43\xbdY\x02\x87\xd5\xec\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x88\xd5A\xc8@\xceC\xce\xfb\xafm\x19\xafk\x98Y\xb5s\xc1E\x89\t79SM(h\x00\x00\u07d4\x88\xde\x13\xb0\x991\x87|\x91\rY1e\xc3d\u0221d\x1b\u04c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x88\xde\u017d?N\xba-\x18\xb8\xaa\xce\xfa{r\x15H\xc3\x19\xba\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\x88\xe6\xf9\xb2G\xf9\x88\xf6\xc0\xfc\x14\xc5o\x1d\xe5>\u019dC\u0309\x05k\xc7^-c\x10\x00\x00\u07d4\x88\xee\u007f\x0e\xfc\x8fw\x8ckh~\xc3+\xe9\xe7\xd6\xf0 \xb6t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x88\xf1\x04_\x19\xf2\xd3\x19\x18\x16\xb1\xdf\x18\xbbn\x145\xad\x1b8\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x89\x00\x9e\a\xe3\xfahc\xa7x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x89Wr~r\xcfb\x90 \xf4\xe0^\xdfy\x9a\xa7E\x80b\u0409wC\"\x17\xe6\x83`\x00\x00\u07d4\x89]iN\x88\v\x13\xcc\u0404\x8a\x86\xc5\xceA\x1f\x88Gk\xbf\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x89^\xc5TVD\u0dc30\xff\xfa\xb8\xdd\xea\xc9\xe83\x15l\x89 \x86\xac5\x10R`\x00\x00\u07d4\x89`\tRj,{\f\t\xa6\xf6:\x80\xbd\U0009d707\u079c\x89\xbb\xb8k\x82#\xed\xeb\x00\x00\u07d4\x89g\u05f9\xbd\xb7\xb4\xae\xd2.e\xa1]\xc8\x03\xcbz!?\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x89n3\\\xa4z\xf5yb\xfa\x0fM\xbf>E\xe6\x88\u02e5\x84\x89J/\xc0\xab`R\x12\x00\x00\u07d4\x89s\xae\xfd^\xfa\xee\x96\t]\x9e(\x8fj\x04l\x977KC\x89\a\xa4\u0120\xf32\x14\x00\x00\u07d4\x89\x8cr\xddseX\xef\x9eK\xe9\xfd\xc3O\xefT\xd7\xfc~\b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x89\x9b<$\x9f\fK\x81\xdfu\xd2\x12\x00M=m\x95/\xd2#\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x89\xab\x13\xee&mw\x9c5\xe8\xbb\x04\u034a\x90\xcc!\x03\xa9[\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\x89\xc43\xd6\x01\xfa\xd7\x14\xdaci0\x8f\xd2l\x1d\u0254+\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x89\xd7[\x8e\b1\xe4o\x80\xbc\x17A\x88\x18N\x00o\xde\x0e\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x89\u3d5a\x15\x86G7\u0513\xc1\xd2<\xc5=\xbf\x8d\xcb\x13b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x89\xfc\x8eM8k\r\v\xb4\xa7\a\xed\xf3\xbdV\r\xf1\xad\x8fN\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\x89\xfe\xe3\r\x17(\xd9l\xec\xc1\u06b3\xda.w\x1a\xfb\u03eaA\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x8a\x1c\u016c\x11\x1cI\xbf\xcf\xd8H\xf3}\xd7h\xaae\u0208\x02\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x8a \xe5\xb5\xce\xe7\xcd\x1fU\x15\xba\xce;\xf4\xf7\u007f\xfd\xe5\xcc\a\x89\x04V9\x18$O@\x00\x00\xe0\x94\x8a!}\xb3\x8b\xc3_!_\xd9)\x06\xbeBCo\xe7\xe6\xed\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x8a$:\n\x9f\xeaI\xb89TwE\xff-\x11\xaf?K\x05\"\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x8a$}\x18e\x10\x80\x9fq\xcf\xfcEYG\x1c9\x10\x85\x81!\x89a\t=|,m8\x00\x00\u07d4\x8a4p(-^**\xef\u05e7P\x94\xc8\"\xc4\xf5\xae\uf289\r(\xbc`dx\xa5\x80\x00\u07d4\x8a6\x86\x9a\xd4x\x99|\xbfm\x89$\xd2\n<\x80\x18\xe9\x85[\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8aC\x14\xfba\u0353\x8f\xc3>\x15\xe8\x16\xb1\x13\U000ac267\xfb\x89\x17vNz\xede\x10\x00\x00\u07d4\x8aOJ\u007fR\xa3U\xba\x10_\xca r\xd3\x06_\xc8\xf7\x94K\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8aX1(,\xe1Jezs\r\xc1\x88&\xf7\xf9\xb9\x9d\xb9h\x89\uaf8a[A\xc16\x00\x00\u07d4\x8a_\xb7W\x93\xd0C\xf1\xbc\xd48\x85\xe07\xbd0\xa5(\xc9'\x89\x13Snm.\x9a\xc2\x00\x00\u07d4\x8af\xab\xbc-0\xce!\xa83\xb0\u06ceV\x1dQ\x05\xe0\xa7,\x89%\xf1\xde\\v\xac\xdf\x00\x00\u07d4\x8atl]g\x06G\x11\xbf\xcah[\x95\xa4\xfe)\x1a'\x02\x8e\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8ax\n\xb8z\x91E\xfe\x10\xed`\xfaGjt\n\xf4\u02b1\u0489\x12\x1b.^ddx\x00\x00\u07d4\x8az\x06\xbe\x19\x9a:X\x01\x9d\x84j\xc9\xcb\xd4\xd9]\xd7W\u0789\xa2\xa4#\x94BV\xf4\x00\x00\u07d4\x8a\x81\x01\x14\xb2\x02]\xb9\xfb\xb5\x00\x99\xa6\xe0\u02de.\xfak\u0709g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x8a\x86\xe4\xa5\x1c\x01;\x1f\xb4\xc7k\xcf0f|x\xd5.\xed\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\x9e\u029cZ\xba\x8e\x13\x9f\x80\x03\xed\xf1\x16:\xfbp\xaa:\xa9\x89#\xc7W\a+\x8d\xd0\x00\x00\u07d4\x8a\xb89\xae\xaf*\xd3|\xb7\x8b\xac\xbb\xb63\xbc\xc5\xc0\x99\xdcF\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\u021b\u06780\x1ek\x06w\xfa%\xfc\xf0\xf5\x8f\f\u01f6\x11\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x8a\xdcS\xef\x8c\x18\xed0Qx]\x88\xe9\x96\xf3\xe4\xb2\x0e\xcdQ\x8a\b\xe4\xd3\x16\x82v\x86@\x00\x00\u07d4\x8a\xe6\xf8\vp\xe1\xf2<\x91\xfb\u0569f\xb0\xe4\x99\xd9]\xf82\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x8a\xe9\uf30a\x8a\u07e6\xaby\x8a\xb2\xcd\xc4\x05\b*\x1b\xbbp\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\xf6&\xa5\xf3'\xd7Pe\x89\xee\xb7\x01\x0f\xf9\xc9D` \u0489K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\x8b\x01\xda4\xd4p\xc1\xd1\x15\xac\xf4\xd8\x11\xe1\x01\xdb\x1e\x14\xec\xc7\xd3\"\xc7+\x8c\x04s\x89\x18\xb2j1>\x8a\xe9\x00\x00\xe0\x94\x8bH\xe1\x9d9\xdd5\xb6nn\x1b\xb6\xb9\xc6W\xcb,\xf5\x9d\x04\x8a\x03\xc7U\xac\x9c\x02J\x01\x80\x00\xe0\x94\x8bP^(q\xf7\u07b7\xa68\x95 \x8e\x82'\u072a\x1b\xff\x05\x8a\f\xf6\x8e\xfc0\x8dy\xbc\x00\x00\u07d4\x8bW\xb2\xbc\x83\u030dM\xe31 N\x89?/;\x1d\xb1\a\x9a\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8b\\\x91K\x12\x8b\xf1i\\\b\x89#\xfaF~y\x11\xf3Q\xfa\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\xe0\x94\x8b_)\xcc/\xaa&,\xde\xf3\x0e\xf5T\xf5\x0e\xb4\x88\x14n\xac\x8a\x01;hp\\\x97 \x81\x00\x00\u07d4\x8bpV\xf6\xab\xf3\xb1\x18\xd0&\xe9D\xd5\xc0sC<\xa4Q\u05c965\xc6 G9\u0640\x00\u07d4\x8bqE\"\xfa(9b\x04p\xed\xcf\fD\x01\xb7\x13f=\xf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x8bt\xa7\xcb\x1b\xb8\u014f\xce&tf\xa3\x03X\xad\xafR\u007fa\x8a\x02\xe2WxN%\xb4P\x00\x00\u07d4\x8b~\x9fo\x05\xf7\xe3dv\xa1n>q\x00\xc9\x03\x1c\xf4\x04\xaf\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8b\x81\x15ni\x869\x94<\x01\xa7Rr\xad=5\x85\x1a\xb2\x82\x89\x12\xb3\x16_e\xd3\xe5\x00\x00\u07d4\x8b\x95w\x92\x00S\xb1\xa0\x01\x890M\x88\x80\x10\xd9\xef,\xb4\xbf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8b\x98A\x86.w\xfb\xbe\x91\x94p\x93U\x83\xa9<\xf0'\xe4P\x89llS4B\u007f\x1f\x00\x00\u07d4\x8b\x99}\xbc\a\x8a\xd0)a5]\xa0\xa1Y\xf2\x92~\xd4=d\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x8b\x9f\xda}\x98\x1f\xe9\xd6B\x87\xf8\\\x94\xd8?\x90t\x84\x9f\u030a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x8b\xb0!/2\x95\xe0)\u02b1\xd9a\xb0A3\xa1\x80\x9e{\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8b\xbe\xac\xfc)\xcf\xe94\x02\xdb\xd6j\x1a\xcbvv\x85c7\xb9;\xf0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8b\xf3s\xd0v\x81L\xbcW\xe1\xc6\xd1j\x82\u017e\x13\xc7=7\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x8c\x10#\xfd\xe1WM\xb8\xbbT\xf1s\x96p\x15|\xa4}\xa6R\x8a\x01y\u03da\u00e1\xb1w\x00\x00\u07d4\x8c\x1f\xbe_\n\xea5\x9cZ\xa1\xfa\b\u0209T\x12\u028e\x05\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\"B`U\xb7o\x11\xf0\xa2\xde\x1a\u007f\x81\x9aa\x96\x85\xfe`\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4\x8c+}\x8b`\x8d(\xb7\u007f\\\xaa\x9c\xd6E$*\x82>L\u0649b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\x8c/\xbe\ue3ac\xc5\xc5\xd7|\x16\xab\xd4b\ue701E\xf3K\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x8c:\x9e\xe7\x1fr\x9f#l\xba8g\xb4\u05dd\x8c\xee\xe2]\xbc\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8cP\xaa*\x92\x12\xbc\xdeVA\x8a\xe2a\xf0\xb3^z\x9d\xbb\x82\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8cT\xc7\xf8\xb9\x89nu\xd7\xd5\xf5\xc7`%\x86\x99\x95qB\xad\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8c]\x16\xede\xe3\xed~\x8b\x96\u0297+\xc8as\xe3P\v\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8cj\xa8\x82\xee2,\xa8HW\x8c\x06\xcb\x0f\xa9\x11\xd3`\x83\x05\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\x8cj\xe7\xa0Z\x1d\xe5u\x82\xae'h Bv\xc0\xffG\xed\x03\x8a,\v\xb3\xdd0\xc4\xe2\x00\x00\x00\u07d4\x8co\x9fN[z\xe2v\xbfXI{\u05ff*}%$_d\x89\x93\xfe\\W\xd7\x10h\x00\x00\u07d4\x8cu\x95n\x8f\xedP\xf5\xa7\xdd|\xfd'\xda \x0fgF\xae\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c|\xb4\xe4\x8b%\x03\x1a\xa1\xc4\xf9)%\xd61\xa8\xc3\xed\xc7a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\u007f\xa5\xca\xe8/\xed\xb6\x9a\xb1\x89\xd3\xff'\xae \x92\x93\xfb\x93\x89\x15\xaf\x88\r\x8c\u06c3\x00\x00\xe0\x94\x8c\x81A\x0e\xa85L\xc5\xc6\\A\xbe\x8b\xd5\xdes<\v\x11\x1d\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\x8c\x83\xd4$\xa3\xcf$\xd5\x1f\x01\x92=\xd5J\x18\u05b6\xfe\xde{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8c\x90\n\x826\xb0\x8c+e@]9\xd7_ \x06*ua\xfd\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\x8c\x93\xc3\xc6\u06dd7q}\xe1e\u00e1\xb4\xfeQ\x95,\b\u0789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8c\x99\x95\x91\xfdr\xefq\x11\xef\xcaz\x9e\x97\xa25k;\x00\n\x89\xddd\xe2\xaa\ngP\x00\x00\u07d4\x8c\xa6\x98\x97F\xb0n2\xe2Hta\xb1\u0399j':\xcf\u05c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8c\xb3\xaa?\xcd!(T\xd7W\x8f\xcc0\xfd\xed\xe6t*1*\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x8c\xc0\xd7\xc0\x16\xfaz\xa9P\x11J\xa1\xdb\tH\x82\xed\xa2t\xea\x89\b\xa9\xab\xa5W\xe3l\x00\x00\u07d4\x8c\xc6R\xdd\x13\xe7\xfe\x14\u06bb\xb3m]2\r\xb9\xff\xee\x8aT\x89a\t=|,m8\x00\x00\u07d4\x8c\u02bf%\a\u007f:\xa4\x15E4MS\xbe\x1b+\x9c3\x90\x00\x89[\xe8f\xc5b\xc5D\x00\x00\u07d4\x8c\xcf:\xa2\x1a\xb7BWj\xd8\xc4\"\xf7\x1b\xb1\x88Y\x1d\ua28965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\xd0\xcd\"\xe6 \xed\xa7\x9c\x04a\xe8\x96\xc9\xd1b)\x12K_z\xfb\xec\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x8c\xe2/\x9f\xa3rD\x9aB\x06\x10\xb4z\xe0\xc8\xd5eH\x122\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x8c\u451d\x8a\x16T-B<\x17\x98Ng9\xfar\u03b1w\x8a\x05K@Y&\xf4\xa6=\x80\x00\u07d4\x8c\xe5\xe3\xb5\xf5\x91\xd5\uc8ca\xbf\"\x8f.<5\x13K\xda\xc0\x89}\xc3[\x84\x89|8\x00\x00\xe0\x94\x8c\xee8\xd6YW\x88\xa5n?\xb9F4\xb3\xff\xe1\xfb\xdb&\u058a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x8c\xee\xa1^\xec;\xda\xd8\x02?\x98\xec\xf2[+\x8f\xef'\xdb)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8c\xf3To\xd1\u0363=X\x84_\xc8\xfc\xfe\u02bc\xa7\xc5d*\x89\x1f\x1e9\x93,\xb3'\x80\x00\u07d4\x8c\xf6\xda\x02\x04\xdb\u0106\vF\xad\x97?\xc1\x11\x00\x8d\x9e\fF\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x8c\xfe\xde\xf1\x98\xdb\n\x91C\xf0\x91)\xb3\xfdd\u073b\x9bIV\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\x04\xa5\xeb\xfb]\xb4\t\xdb\x06\x17\xc9\xfaV1\xc1\x92\x86\x1fJ\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x8d\x06\xe4d$\\\xadaI9\xe0\xaf\bE\xe6\xd70\xe2\x03t\x89\n\u070a(\xf3\xd8}\x80\x00\u07d4\x8d\a\xd4-\x83\x1c-|\x83\x8a\xa1\x87+:\xd5\xd2w\x17h#\x89\x12\xee\x1f\x9d\xdb\xeeh\x00\x00\u07d4\x8d\v\x9e\xa5?\xd2cA^\xac\x119\x1f|\xe9\x12V\xb9\xfb\x06`\xf6\xf0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8dy\\_JV\x89\xadb\u0696\x16q\xf0(\x06R\x86\xd5T\x89o\x05\xb5\x9d; \x00\x00\x00\u07d4\x8d\u007f>a)\x9c-\xb9\xb9\xc0H|\xf6'Q\x9e\xd0\n\x91#\x89^t\xa8P^\x80\xa0\x00\x00\xe0\x94\x8d\x89\x17\v\x92\xb2\xbe,\b\xd5|H\xa7\xb1\x90\xa2\xf1Fr\x0f\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x8d\x93\xda\u01c5\xf8\x8f\x1a\x84\xbf\x92}Se+E\xa1T\xcc\u0749\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x8d\x99R\u043bN\xbf\xa0\xef\xd0\x1a:\xa9\xe8\xe8\u007f\x05%t.\x89\xbb\x91%T\"c\x90\x00\x00\u07d4\x8d\x9a\fp\xd2& B\xdf\x10\x17\xd6\xc3\x03\x13 $w'\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\x9e\xd7\xf4U0X\xc2ox6\xa3\x80-0d\xeb\x1b6=\x89\x04\xe1\x00;(\xd9(\x00\x00\u07d4\x8d\xa1\x17\x8fU\xd9wr\xbb\x1d$\x11\x1a@JO\x87\x15\xb9]\x89/\x9a\xc3\xf6\xde\x00\x80\x80\x00\u07d4\x8d\xa1\xd3Y\xbal\xb4\xbc\xc5}zCw \xd5]\xb2\xf0\x1cr\x89\x04V9\x18$O@\x00\x00\u07d4\x8d\xab\x94\x8a\xe8\x1d\xa3\x01\xd9r\xe3\xf6\x17\xa9\x12\xe5\xa7Sq.\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8d\xad\xdfR\xef\xbdt\u0695\xb9i\xa5GoO\xbb\xb5c\xbf\u0489-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4\x8d\xb1\x85\xfe\x1bp\xa9Jj\b\x0e~#\xa8\xbe\xdcJ\xcb\xf3K\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x8d\xb5\x8e@n -\xf9\xbcpl\xb43\xe1\x94\xf4\x0f\x82\xb4\x0f\xaa\xdb\x1f\x8b\x85a\x16\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x8d\xc1\xd5\x11\x1d\t\xaf%\xfd\xfc\xacE\\|\xec(>mgu\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\u0504\xff\x8a0sd\xebf\xc5%\xa5q\xaa\xc7\x01\xc5\xc3\x18\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\u05a9\xba\xe5\u007fQ\x85I\xad\xa6wFo\ua2b0O\u0674\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\xde<\xb8\x11\x85h\xefE\x03\xfe\x99\x8c\xcd\xf56\xbf\x19\xa0\x98\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\xde`\xeb\b\xa0\x99\xd7\u06a3V\u06aa\xb2G\r{\x02Zk\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x8d\xf39!Kj\u0472Fc\xceq`4t\x9dn\xf88\u064a\x02TO\xaaw\x80\x90\xe0\x00\x00\xe0\x94\x8d\xf5=\x96\x19\x14q\xe0Y\xdeQ\xc7\x18\xb9\x83\xe4\xa5\x1d*\xfd\x8a\x06\u01b95\xb8\xbb\xd4\x00\x00\x00\u07d4\x8d\xfb\xaf\xbc\x0e[\\\x86\xcd\x1a\u0597\xfe\xea\x04\xf41\x88\u0796\x89\x15%+\u007f_\xa0\xde\x00\x00\u07d4\x8e\a;\xad%\xe4\"\x18a_J\x0ek.\xa8\xf8\xde\"0\xc0\x89\x82=b\x9d\x02k\xfa\x00\x00\u07d4\x8e\x0f\xee8hZ\x94\xaa\xbc\xd7\u0385{k\x14\t\x82Ou\xb8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e#\xfa\xcd\x12\xc7e\xc3j\xb8\x1am\xd3M\x8a\xa9\xe6\x89\x18\xae\x89\t\x11\u418d\xba\x9b\x00\x00\xe0\x94\x8e/\x904\xc9%G\x19\u00ceP\u026ad0^\u0596\xdf\x1e\x8a\x01\x00N.E\xfb~\xe0\x00\x00\u07d4\x8e2@\xb0\x81\x0e\x1c\xf4\a\xa5\x00\x80G@\u03cdad2\xa4\x89\x02/fU\xef\v8\x80\x00\u07d4\x8eHj\x04B\xd1q\xc8`[\xe3H\xfe\xe5~\xb5\b^\xff\r\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8eaV3k\xe2\u037e2\x14\r\xf0\x8a+\xa5_\u0425\x84c\x89\x04\t\x9e\x1dcW\x18\x00\x00\u07d4\x8eg\b\x15\xfbg\xae\xae\xa5{\x86SN\xdc\x00\xcd\xf5d\xfe\u5272\xe4\xb3#\xd9\xc5\x10\x00\x00\u07d4\x8emt\x85\xcb\u942c\xc1\xad\x0e\xe9\xe8\xcc\xf3\x9c\f\x93D\x0e\x893\xc5I\x901r\f\x00\x00\xe0\x94\x8et\xe0\u0477~\xbc\x82:\xca\x03\xf1\x19\x85L\xb1 '\xf6\u05ca\x16\xb3R\xda^\x0e\xd3\x00\x00\x00\u07d4\x8ex\xf3QE}\x01oJ\xd2u^\xc7BN\\!\xbamQ\x89\a\xea(2uw\b\x00\x00\u07d4\x8ey6\u0552\x00\x8f\xdcz\xa0N\xde\xebuZ\xb5\x13\u06f8\x9d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8e\u007f\xd28H\xf4\xdb\a\x90j}\x10\xc0K!\x80;\xb0\x82'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8e\x92\xab\xa3\x8er\xa0\x98\x17\v\x92\x95\x92FSz.UV\xc0\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x8e\x98ve$\xb0\xcf'G\xc5\r\xd4;\x95gYM\x971\u0789lD\xb7\xc2a\x82(\x00\x00\u07d4\x8e\x9b5\xadJ\n\x86\xf7XDo\xff\xde4&\x9d\x94\f\xea\u0349\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8e\x9c\b\xf78f\x1f\x96v#n\xff\x82\xbaba\xdd?H\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8e\x9cB\x92f\xdf\x05~\xfax\xdd\x1d_w\xfc@t*\xd4f\x89\x10D.\u0475l|\x80\x00\u07d4\x8e\xa6V\xe7\x1e\xc6Q\xbf\xa1|ZWY\xd8`1\xcc5\x99w\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8e\xae)CU\x98\xba\x8f\x1c\x93B\x8c\xdb>+M1\a\x8e\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\xb1\xfb\xe4\xe5\xd3\x01\x9c\xd7\xd3\r\xae\x9c\r[Lv\xfbc1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\xb5\x17t\xaf k\x96k\x89\t\xc4Z\xa6r'H\x80,\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e\xb8\xc7\x19\x82\xa0\x0f\xb8Bu)2S\xf8\x04ED\xb6kI\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\x8e\xcb\u03ec\xbf\xaf\xe9\xf0\f9\"\xa2N,\xf0\x02gV\xca \x8a\x011\xbe\xb9%\xff\xd3 \x00\x00\u07d4\x8e\u03b2\xe1$Sl[_\xfcd\x0e\xd1O\xf1^\u0668\xcbq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\u042f\x11\xff(p\xda\x06\x81\x00J\xfe\x18\xb0\x13\xf7\xbd8\x82\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\x8e\xd1Cp\x1f/r(\x0f\xd0J{Ad(\x19y\xea\x87\u0248\xc2I\xfd\xd3'x\x00\x00\u07d4\x8e\xd1R\x8bD~\xd4)y\x02\xf69\xc5\x14\u0414J\x88\xf8\u0209\n\xc6\xe7z\xb6c\xa8\x00\x00\u07d4\x8e\xd4(L\x0fGD\x9c\x15\xb8\u0673$]\u8fb6\u0380\xbf\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\x8e\xde~=\xc5\aI\xc6\xc5\x0e.(\x16\x84x\xc3M\xb8\x19F\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x8e\xe5\x843}\xdb\xc8\x0f\x9e4\x98\xdfU\xf0\xa2\x1e\xac\xb5\u007f\xb1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8e\xeb\xec\x1ab\xc0\x8b\x05\xa7\xd1\u0551\x80\xaf\x9f\xf0\u044e?6\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e\xf4\u0622\xc2o\xf7\x14\xb6u\x89\x19\x80\x1c\x83\xb6\xc7\xc0\x00\x00\u07d4\x8fM\x1dAi>F,\xf9\x82\xfd\x81\u042ap\x1d:St\u0249\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8fM\x1e~Ea(J4\xfe\xf9g<\r4\xe1*\xf4\xaa\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8fO\xb1\xae\xa7\xcd\x0fW\x0e\xa5\xe6\x1b@\xa4\xf4Q\vbd\xe4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8fV\x1bA\xb2\t\xf2H\u0229\x9f\x85\x87\x887bP`\x9c\xf3\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94\x8fX\xd84\x8f\xc1\xdcN\r\xd84;eC\xc8W\x04^\xe9@\x8a\x02\xe3\x03\x8d\xf4s\x03(\x00\x00\u07d4\x8f`\x89_\xbe\xbb\xb5\x01\u007f\xcb\xff<\u0763\x97)+\xf2[\xa6\x89\x17D\x06\xff\x9fo\u0480\x00\u07d4\x8fd\xb9\xc1$m\x85x1d1\a\xd3U\xb5\xc7_\xef]O\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x8ff\x0f\x8b.L|\u00b4\xac\x9cG\xed(P\x8d_\x8f\x86P\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x8fi\xea\xfd\x023\xca\xdb@Y\xabw\x9cF\xed\xf2\xa0PnH\x89`\xf0f \xa8IE\x00\x00\xe0\x94\x8fq~\xc1U/LD\x00\x84\xfb\xa1\x15J\x81\xdc\x00>\xbd\xc0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x8f\x8a\xcb\x10v\a8\x84y\xf6K\xaa\xab\xea\x8f\xf0\a\xad\xa9}\x8a\x05\xc6\xf3\b\n\xd4#\xf4\x00\x00\u07d4\x8f\x8c\xd2n\x82\xe7\xc6\xde\xfd\x02\u07ed\a\x97\x90!\xcb\xf7\x15\f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x8f\x8f7\u042d\x8f3]*q\x01\xb4\x11V\xb6\x88\xa8\x1a\x9c\xbe\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\x8f\x92\x84O(*\x92\x99\x9e\u5d28\xd7s\xd0kiM\xbd\x9f\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x8f\xact\x8fxJ\x0f\xedh\u06e43\x19\xb4*u\xb4d\x9cn\x891T\xc9r\x9d\x05x\x00\x00\u07d4\x8f\u0665\xc3:}\x9e\xdc\xe0\x99{\xdfw\xab0d$\xa1\x1e\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8f\xef\xfa\xdb8z\x15G\xfb(M\xa9\xb8\x14\u007f>|m\xc6\u0689-b{\xe4S\x05\b\x00\x00\u07d4\x8f\xf4`Ehw#\xdc3\xe4\u0419\xa0i\x04\xf1\ubd44\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\x8f\xfa\x06!\"\xac0t\x18\x82\x1a\u06d3\x11\aZ7\x03\xbf\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8f\xfe2)\x97\xb8\xe4\x04B-\x19\xc5J\xad\xb1\x8f[\xc8\u9dc9\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x90\x01\x94\u0131\aC\x05\u045d\xe4\x05\xb0\xacx(\x0e\xca\xf9g\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x90\x03\xd2p\x89\x1b\xa2\xdfd=\xa84\x15\x83\x195E\xe3\xe0\x00\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x90\x05z\xf9\xaaf0~\xc9\xf03\xb2\x97$\u04f2\xf4\x1e\xb6\xf9\x8a\x19\xd1\u05aa\xdb,R\xe8\x00\x00\u07d4\x90\x0f\v\x8e5\xb6h\xf8\x1e\xf2R\xb18U\xaaP\a\xd0\x12\xe7\x89\x17\n\x0fP@\xe5\x04\x00\x00\u07d4\x90\x18\xcc\x1fH\xd20\x8e%*\xb6\b\x9f\xb9\x9a|\x1dV\x94\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x90\x1d\x99\xb6\x99\xe5\u0191\x15\x19\xcb v\xb4\xc7c0\xc5M\"\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x90-t\xa1W\xf7\u04b9\xa37\x8b\x1fVp70\xe0:\x17\x19\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x904\x13\x87\x8a\xea;\xc1\bc\t\xa3\xfev\x8beU\x9e\x8c\xab\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x90If\xcc\"\x13\xb5\xb8\xcb[\xd6\b\x9e\xf9\xcd\xdb\xef~\xdf\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\x90L\xaaB\x9ca\x9d\x94\x0f\x8egA\x82j\r\xb6\x92\xb1\x97(\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x90R\xf2\xe4\xa3\xe3\xc1-\xd1\xc7\x1b\xf7\x8aN\xc3\x04=\u020b~\x89\x0e~\xeb\xa3A\vt\x00\x00\u0794\x90U&V\x8a\xc1#\xaf\xc0\xe8J\xa7\x15\x12O\xeb\xe8=\xc8|\x88\xf8i\x93)g~\x00\x00\u07d4\x90\x92\x91\x87\a\xc6!\xfd\xbd\x1d\x90\xfb\x80\xebx\u007f\xd2osP\x89\x85[[\xa6\\\x84\xf0\x00\x00\u07d4\x90\x9b^v:9\xdc\u01d5\"=s\xa1\u06f7\xd9L\xa7Z\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\x90\xac\xce\xd7\xe4\x8c\b\u01b94dm\xfa\n\xdf)\u0714\aO\x89\x03\vK\x15{\xbdI\x00\x00\u07d4\x90\xb1\xf3p\xf9\xc1\xeb\v\xe0\xfb\x8e+\x8a\xd9jAcq\u074a\x890\xca\x02O\x98{\x90\x00\x00\u07d4\x90\xb6/\x13\x1a_)\xb4UqQ>\xe7\xa7J\x8f\v#\"\x02\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x90\xbdb\xa0P\x84Ra\xfaJ\x9f|\xf2A\xeac\v\x05\ufe09\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x90\xc4\x1e\xba\x00\x8e \xcb\xe9'\xf3F`?\u0206\x98\x12Yi\x89\x02F\xdd\xf9yvh\x00\x00\u07d4\x90\u0480\x9a\xe1\xd1\xff\xd8\xf6>\xda\x01\xdeI\xddU-\xf3\u047c\x89\u063beI\xb0+\xb8\x00\x00\u07d4\x90\xdc\t\xf7\x17\xfc*[i\xfd`\xba\b\xeb\xf4\v\xf4\xe8$l\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x90\xe3\x00\xacqE\x1e@\x1f\x88\u007fnw(\x85\x16G\xa8\x0e\a\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x90\xe3Z\xab\xb2\xde\xef@\x8b\xb9\xb5\xac\xefqDW\xdf\xdebr\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\x90\xe7\a\x0fM\x03?\xe6\x91\f\x9e\xfeZ'\x8e\x1f\xc6#M\xef\x89\x05q8\b\x19\xb3\x04\x00\x00\u07d4\x90\xe9>M\xc1q!HyR36\x14\x00+\xe4#VI\x8e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x90\u9a68.\u06a8\x14\u0084\xd22\xb6\u9e90p\x1dIR\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\x90\xf7t\xc9\x14}\u0790\x85=\xdcC\xf0\x8f\x16\xd4U\x17\x8b\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x90\xfcS{!\x06Xf\n\x83\xba\xa9\xacJ\x84\x02\xf6WF\xa8\x89e\xea=\xb7UF`\x00\x00\u07d4\x91\x05\n\\\xff\xad\xed\xb4\xbbn\xaa\xfb\xc9\xe5\x014(\xe9l\x80\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x91\x05\x17d\xafk\x80\x8eB\x12\xc7~0\xa5W.\xaa1pp\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x91\v}Wz~9\xaa#\xac\xf6*\xd7\xf1\xef4)4\xb9h\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x91\x0e\x99eC4Lh\x15\xfb\x97\u0367\xafK\x86\x98vZ[\x89\x05\x9a\xf6\x98)\xcfd\x00\x00\u07d4\x91\x1f\xee\xa6\x1f\xe0\xedP\u0179\xe5\xa0\xd6`q9\x9d(\xbd\u0189\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x91\x1f\xf23\xe1\xa2\x11\xc0\x17,\x92\xb4l\xf9\x97\x03\x05\x82\xc8:\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x91 \xe7\x11s\xe1\xba\x19\xba\x8f\x9fO\xdb\u072a4\xe1\u05bbx\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91!\x17\x12q\x9f+\bM;8u\xa8Pi\xf4f61A\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x91#\x04\x11\x8b\x80G=\x9e\x9f\xe3\xeeE\x8f\xbea\x0f\xfd\xa2\xbb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x91Tky\xec\xf6\x9f\x93kZV\x15\b\xb0\xd7\xe5\f\u0159/\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x91V\u0440)5\x0eG\x04\b\xf1_\x1a\xa3\xbe\x9f\x04\ng\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x91b\x0f>\xb3\x04\xe8\x13\u048b\x02\x97Ume\xdcN]\u5a89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\x91k\xf7\xe3\xc5E\x92\x1d2\x06\xd9\x00\xc2O\x14\x12|\xbd^p\x8a\x03\xd0\u077c}\xf2\xbb\x10\x00\x00\u0794\x91l\xf1}qA(\x05\xf4\xaf\xc3DJ\v\x8d\xd1\xd93\x9d\x16\x88\xc6s\xce<@\x16\x00\x00\u07d4\x91{\x8f\x9f:\x8d\t\xe9 ,R\u009erA\x96\xb8\x97\xd3^\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\x91\x89g\x91\x8c\u0617\xdd\x00\x05\xe3m\xc6\u0203\xefC\x8f\xc8\u01c9\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x91\x89\x8e\xab\x8c\x05\xc0\"(\x83\xcdM\xb2;w\x95\xe1\xa2J\u05c9lk\x93[\x8b\xbd@\x00\x00\u0794\x91\x91\xf9F\x98!\x05\x16\xcfc!\xa1B\a\x0e Yvt\xed\x88\xee\x9d[\xe6\xfc\x11\x00\x00\u07d4\x91\xa4\x14\x9a,{\x1b:g\xea(\xaf\xf3G%\u0fcdu$\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x91\xa7\x87\xbcQ\x96\xf3HW\xfe\f7/M\xf3v\xaa\xa7f\x13\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91\xa8\xba\xae\xd0\x12\xea.c\x80;Y=\r\f*\xabL[\n\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x91\xac\\\xfeg\xc5J\xa7\xeb\xfb\xa4HflF\x1a;\x1f\xe2\xe1\x89\x15\xc94\x92\xbf\x9d\xfc\x00\x00\u07d4\x91\xbb?y\x02+\xf3\xc4S\xf4\xff%n&\x9b\x15\xcf,\x9c\xbd\x89RX\\\x13\xfe:\\\x00\x00\u07d4\x91\xc7^<\xb4\xaa\x89\xf3F\x19\xa1d\xe2\xa4x\x98\xf5gM\x9c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91\xc8\f\xaa\b\x1b85\x1d*\x0e\x0e\x00\xf8\n4\xe5dt\xc1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x91\xccF\xaa7\x9f\x85jf@\xdc\xcdZd\x8ay\x02\xf8I\u0649\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x91\u04a9\xee\x1am\xb2\x0fS\x17\u0327\xfb\xe218\x95\u06ce\xf8\x8a\x01\xcc\u00e5/0n(\x00\x00\u07d4\x91\xd6n\xa6(\x8f\xaaK=`l*\xa4\\{k\x8a%'9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x91\u06f6\xaa\xad\x14\x95\x85\xbeG7\\]m\xe5\xff\t\x19\x15\x18\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x91\xe8\x81\x06R\xe8\xe6\x16\x15%\xd6;\xb7u\x1d\xc2\x0fg`v\x89'Mej\xc9\x0e4\x00\x00\u07d4\x91\xf5\x16\x14l\xda (\x17\x19\x97\x80`\u01beAI\x06|\x88\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x91\xf6$\xb2J\x1f\xa5\xa0V\xfeW\x12)\xe77\x9d\xb1K\x9a\x1e\x8a\x02\x8a\x85\x17\xc6i\xb3W\x00\x00\xe0\x94\x91\xfe\x8aLad\u07cf\xa6\x06\x99]k\xa7\xad\xca\xf1\u0213\u038a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4\x92\x1fRa\xf4\xf6\x12v\a\x06\x89&%\xc7^{\u0396\xb7\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92!\xc9\xce\x01#&et\x10\x96\xac\a#Y\x03\xad\x1f\xe2\xfc\x89\x06\xdbc3U\"b\x80\x00\u07d4\x92%\x988`\xa1\xcbF#\xc7$\x80\xac\x16'+\f\x95\xe5\xf5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x92%\xd4jZ\x80\x949$\xa3\x9e[\x84\xb9m\xa0\xacE\x05\x81\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x92* \u01da\x1d:&\xdd8)g{\xf1\xd4\\\x8fg+\xb6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x92C\x8eR\x03\xb64o\xf8\x86\xd7\xc3b\x88\xaa\xcc\xccx\xce\u028965\u026d\xc5\u07a0\x00\x00\u07d4\x92C\xd7v-w({\x12c\x86\x88\xb9\x85N\x88\xa7i\xb2q\x8965\u026d\xc5\u07a0\x00\x00\u0794\x92K\xcez\x85<\x97\v\xb5\xec{\xb7Y\xba\xeb\x9ct\x10\x85{\x88\xbe -j\x0e\xda\x00\x00\u07d4\x92N\xfam\xb5\x95\xb7\x93\x13'~\x881\x96%\akX\n\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92U\x82&\xb3\x84bl\xadH\xe0\x9d\x96k\xf19^\xe7\xea]\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4\x92`\x82\xcb~\xedK\x19\x93\xad$ZGrg\xe1\xc3<\xd5h\x89\x14Jt\xba\u07e4\xb6\x00\x00\u07d4\x92b\t\xb7\xfd\xa5N\x8d\u06dd\x9eM=\x19\xeb\u070e\x88\u009f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92h\xd6&FV6\x11\xdc;\x83*0\xaa#\x94\xc6F\x13\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92i\x8e4Sx\xc6-\x8e\xda\x18M\x946j\x14K\f\x10[\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x92y:\u0173rhwJq0\xde+\xbd3\x04\x05f\x17s\x89\x02,\xa3X|\xf4\xeb\x00\x00\xe0\x94\x92y\xb2\"\x8c\xec\x8f{M\xda?2\x0e\x9a\x04f\xc2\xf5\x85\u028a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x92|\xb7\xdc\x18p6\xb5B{\xc7\xe2\x00\xc5\xecE\f\x1d'\u0509\v\xb5\x9a'\x95<`\x00\x00\u07d4\x92|\u00bf\xda\x0e\b\x8d\x02\xef\xf7\v8\xb0\x8a\xa5<\xc3\tA\x89do`\xa1\xf9\x866\x00\x00\xe0\x94\x92\x84\xf9m\xdbG\xb5\x18n\xe5X\xaa12M\xf56\x1c\x0fs\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x92\x9d6\x8e\xb4j-\x1f\xbd\xc8\xff\xa0`~\xdeK\xa8\x8fY\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xa7\u0166Cb\xe9\xf8B\xa2=\xec\xa2\x105\x85\u007f\x88\x98\x00\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x92\xa8\x98\xd4o\x19q\x9c8\x12j\x8a<'\x86z\xe2\xce\u5589lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xa9q\xa79y\x9f\x8c\xb4\x8e\xa8G]r\xb2\xd2GAr\xe6\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x92\xaa\xe5\x97h\xed\xdf\xf8<\xfe`\xbbQ.s\n\x05\xa1a\u05c9\\\x97xA\fv\u0440\x00\u07d4\x92\xad\x1b=u\xfb\xa6}Tf=\xa9\xfc\x84\x8a\x8a\xde\x10\xfag\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xae[|~\xb4\x92\xff\x1f\xfa\x16\xddB\xad\x9c\xad@\xb7\xf8\u0709.\xe4IU\b\x98\xe4\x00\x00\u07d4\x92\xc0\xf5s\xec\xcfb\xc5H\x10\xeek\xa8\xd1\xf1\x13T+0\x1b\x89\xb7ro\x16\u0331\xe0\x00\x00\u07d4\x92\xc1?\xe0\xd6\u0387\xfdP\xe0=\uf7e6@\x05\t\xbdps\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x92\xc9L( \xdf\xcfqV\xe6\xf10\x88\xec\u754b6v\xfd\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4\x92\xcf\xd6\x01\x88\xef\u07f2\xf8\xc2\xe7\xb1i\x8a\xbb\x95&\xc1Q\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\u062d\x9aMah;\x80\u0526g.\x84\xc2\rbB\x1e\x80\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x92\u0725\xe1\x02\xb3\xb8\x1b`\xf1\xa5\x04cIG\xc3t\xa8\x8c\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xe454\x0e\x9d%<\x00%c\x89\xf5+\x06}U\x97Nv\x89\x0e\x87?D\x13<\xb0\x00\x00\xe0\x94\x92\xe49(\x16\xe5\xf2\xef_\xb6X7\xce\xc2\xc22\\\xc6I\"\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x92\xe6X\x1e\x1d\xa1\xf9\xb8F\xe0\x93G3=\xc8\x18\xe2\u04acf\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\x93\x1d\xf3M\x12%\xbc\xd4\"Nch\r\\L\t\xbc\xe75\xa6\x89\x03\xaf\xb0\x87\xb8v\x90\x00\x00\u07d4\x93\x1f\xe7\x12\xf6B\a\xa2\xfdP\"r\x88CT\x8b\xfb\x8c\xbb\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x93#_4\r(c\xe1\x8d/LR\x99e\x16\x13\x8d\"\x02g\x89\x04\x00.D\xfd\xa7\xd4\x00\x00\u07d4\x93%\x82U\xb3|\u007fX\xf4\xb1\x06s\xa92\xdd:\xfd\x90\xf4\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x93(\xd5\\\xcb?\xceS\x1f\x19\x93\x823\x9f\x0eWn\xe8@\xa3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x93)\xff\xdc&\x8b\xab\u0788t\xb3f@l\x81D[\x9b-5\x89\x16\xe6/\x8cs\f\xa1\x80\x00\u07d4\x93+\x9c\x04\xd4\r*\xc80\x83\xd9B\x98\x16\x9d\xae\x81\xab.\u0409lk\x93[\x8b\xbd@\x00\x00\u07d4\x9346\xc8G&U\xf6L:\xfa\xaf|Lb\x1c\x83\xa6+8\x8965\u026d\xc5\u07a0\x00\x00\u0794\x93;\xf3?\x82\x99p+:\x90&B\xc3>\v\xfa\xea\\\x1c\xa3\x88\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x93@4\\\xa6\xa3\uaf77sc\xf2X`C\xf2\x948\xce\v\x89\x1c\xc8\x05\xda\r\xff\xf1\x00\x00\xe0\x94\x93@\xb5\xf6x\xe4^\xe0^\xb7\b\xbbz\xbbn\xc8\xf0\x8f\x1bk\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x93J\xf2\x1b~\xbf\xa4g\xe2\xce\xd6Z\xa3N\xdd:\x0e\xc7\x132\x8a\a\x80\x1f>\x80\xcc\x0f\xf0\x00\x00\xe0\x94\x93PiDJj\x98M\xe2\bNFi*\xb9\x9fg\x1f\xc7'\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\xe0\x94\x93P~\x9e\x81\x19\xcb\xce\u068a\xb0\x87\xe7\xec\xb0q8=i\x81\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x93g\x8a\x00\x00\xe0\x94\x93m\xcf\x00\x01\x94\xe3\xbf\xf5\n\u0174$:;\xa0\x14\xd6a\u060a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x93o8\x13\xf5\xf6\xa1;\x8eO\xfe\xc8?\xe7\xf8&\x18jq\u0349\x1c0s\x1c\xec\x03 \x00\x00\u07d4\x93t\x86\x9dJ\x99\x11\xee\x1e\xafU\x8b\xc4\u00b6>\xc6:\xcf\u074965\u026d\xc5\u07a0\x00\x00\u07d4\x93uc\u0628\x0f\u05657\xb0\xe6m \xa0%%\xd5\u0606`\x89\x87\x86x2n\xac\x90\x00\x00\u07d4\x93v\xdc\xe2\xaf.\xc8\xdc\xdat\x1b~sEfF\x81\xd96h\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x93\x86\x8d\xdb*yM\x02\xeb\xda/\xa4\x80|v\xe3`\x98X\u0709m\xee\x15\xfc|$\xa7\x80\x00\xe0\x94\x93\x9cC\x13\xd2(\x0e\xdf^\a\x1b\xce\xd8F\x06?\n\x97]T\x8a\x19i6\x89t\xc0[\x00\x00\x00\xe0\x94\x93\xa6\xb3\xabB0\x10\xf9\x81\xa7H\x9dJ\xad%\xe2b\\WA\x8a\x04F\x80\xfej\x1e\xdeN\x80\x00\u07d4\x93\xaa\x8f\x92\xeb\xff\xf9\x91\xfc\x05^\x90ne\x1a\xc7h\xd3+\u02092\xf5\x1e\u06ea\xa30\x00\x00\u07d4\x93\xb4\xbf?\xdf\xf6\xde?NV\xbamw\x99\xdcK\x93\xa6T\x8f\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4\x93\xbc}\x9aJ\xbdD\u023b\xb8\xfe\x8b\xa8\x04\xc6\x1a\xd8\xd6Wl\x89\xd8\xd6\x11\x9a\x81F\x05\x00\x00\u07d4\x93\xc2\xe6N]\xe5X\x9e\xd2P\x06\xe8C\x19n\xe9\xb1\xcf\v>\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\x93\u020e-\x88b\x1e0\xf5\x8a\x95\x86\xbe\xd4\t\x89\x99\xebg\u074a\x06\x9bZ\xfa\xc7P\xbb\x80\x00\x00\u07d4\x93\xe0\xf3~\xcd\xfb\x00\x86\xe3\xe8b\xa9p4D{\x1eM\xec\x1a\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\xe0\x94\x93\xe3\x03A\x1a\xfa\xf6\xc1\a\xa4A\x01\u026c[6\xe9\xd6S\x8b\x8a\r\xf9\xdd\xfe\xcd\x03e@\x00\x00\u07d4\x93\xf1\x8c\xd2R`@v\x14\x88\xc5\x13\x17M\x1eycv\x8b,\x89\x82\xff\xac\x9a\u0553r\x00\x00\u07d4\x94\x0fqQ@P\x9f\xfa\xbf\x97EF\xfa\xb3\x90\"\xa4\x19R\u0489K\xe4\xe7&{j\xe0\x00\x00\u07d4\x94,k\x8c\x95[\xc0\u0608\x12g\x8a#g%\xb3'9\xd9G\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\x94=7\x86JJS}5\xc8\u0657#\xcdd\x06\xce%b\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94C\x9c\xa9\xcc\x16\x9ay\u0520\x9c\xae^gvJo\x87\x1a!\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x94D\x9c\x01\xb3*\u007f\xa5Z\xf8\x10OB\xcd\xd8D\xaa\x8c\xbc@\x8a\x03\x81\x11\xa1\xf4\xf0<\x10\x00\x00\xe0\x94\x94E\xba\\0\xe9\x89a\xb8`$a\xd08]@\xfb\xd8\x03\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x94O\a\xb9o\x90\xc5\xf0\xd7\xc0\u0140S1I\xf3\xf5\x85\xa0x\x89\x04\x02\xf4\xcf\xeeb\xe8\x00\x00\u07d4\x94T\xb3\xa8\xbf\xf9p\x9f\xd0\u1407~l\xb6\u0219t\xdb\u0589\x90\xf54`\x8ar\x88\x00\x00\u07d4\x94]\x96\xeaW>\x8d\xf7&+\xbf\xa5r\"\x9bK\x16\x01k\x0f\x89\vX\x9e\xf9\x14\xc1B\x00\x00\u07d4\x94^\x18v\x9d~\xe7'\xc7\x01?\x92\xde$\xd1\x17\x96\u007f\xf3\x17\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94a'\x81\x03;W\xb1F\xeet\xe7S\xc6r\x01\u007fS\x85\xe4\x89\xc3(\t>a\xee@\x00\x00\xe0\x94\x94dJ\xd1\x16\xa4\x1c\xe2\xca\u007f\xbe\xc6\t\xbd\xefs\x8a*\xc7\u01ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x94p\xcc6YE\x86\x82\x18!\xc5\u0256\xb6\xed\xc8;mZ2\x89\x01M\x11 \u05f1`\x00\x00\xe0\x94\x94u\xc5\x10\xec\x9a&\x97\x92GtL=\x8c;\x0e\v_D\u04ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x94~\x11\xe5\xea)\ro\u00f3\x80H\x97\x9e\f\xd4N\xc7\xc1\u007f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94\x83\u064f\x14\xa3?\xdc\x11\x8d@9U\u00995\xed\xfc_p\x89\x18\xea;4\xefQ\x88\x00\x00\u07d4\x94\x911\xf2\x89C\x92\\\xfc\x97\xd4\x1e\f\xea\v&)s\xa70\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\x94\x9f\x84\xf0\xb1\xd7\u0127\xcfI\xee\u007f\x8b,J\x13M\xe3(x\x89%\"H\u07b6\xe6\x94\x00\x00\u07d4\x94\x9f\x8c\x10{\xc7\xf0\xac\xea\xa0\xf1pR\xaa\xdb\xd2\xf9s+.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94\xa7\u0368\xf4\x81\xf9\u061dB\xc3\x03\xae\x162\xb3\xb7\t\xdb\x1d\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x94\xa9\xa7\x16\x911| d'\x1bQ\xc95?\xbd\xed5\x01\xa8\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x94\xadK\xad\x82K\xd0\ub7a4\x9cX\u03bc\xc0\xff^\b4k\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x94\xbb\xc6}\x13\xf8\x9e\xbc\xa5\x94\xbe\x94\xbcQp\x92\f0\xd9\xf3\x89\x04X\xff\xa3\x15\nT\x00\x00\u07d4\x94\xbe:\xe5Ob\xd6c\xb0\xd4\u031e\x1e\xa8\xfe\x95V\ua7bf\x89\x01C\x13,\xa8C\x18\x00\x00\xe0\x94\x94\xc0U\xe8X5z\xaa0\xcf A\xfa\x90Y\xce\x16J\x1f\x91\x8a\x04<%\xe0\xdc\xc1\xbd\x1c\x00\x00\xe0\x94\x94\xc7B\xfdz\x8by\x06\xb3\xbf\xe4\xf8\x90O\xc0\xbe\\v\x803\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x94\xcaV\xdew\u007f\xd4S\x17\u007f^\x06\x94\xc4x\xe6j\xff\x8a\x84\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x94\xd8\x10t\xdbZ\xe1\x97\u04bb\x13s\xab\x80\xa8}\x12\x1cK\u04ca\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x94\u06c0xs\x86\n\xac=Z\xea\x1e\x88^R\xbf\xf2\x86\x99T\x89\xae\x8ez\v\xb5u\xd0\x00\x00\u07d4\x94\xe1\xf5\u02db\x8a\xba\xce\x03\xa1\xa6B\x82VU;i\f#U\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x94\xef\x8b\xe4Pw\xc7\xd4\xc5e'@\u0794jbbOq?\x89\x05l\xf5Y:\x18\xf8\x80\x00\u07d4\x94\xf1?\x9f\b6\xa3\xee$7\xa8I\"\u0498M\xc0\xf7\xd5;\x89\xa2\xa02\x9b\u00ca\xbe\x00\x00\u07d4\x94\xf8\xf0W\xdb~`\xe6u\xad\x94\x0f\x15X\x85\u0464w4\x8e\x89\x15\xbeat\xe1\x91.\x00\x00\xe0\x94\x94\xfc\u03ad\xfe\\\x10\x9c^\xae\xafF-C\x871B\u020e\"\x8a\x01\x045a\xa8\x82\x93\x00\x00\x00\u07d4\x95\x03N\x16!\x86Q7\xcdG9\xb3F\xdc\x17\xda:'\xc3N\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\x95\fh\xa4\t\x88\x15M#\x93\xff\xf8\xda|\u0369\x96\x14\xf7,\x89\xf9AF\xfd\x8d\xcd\xe5\x80\x00\xe0\x94\x95\x0f\xe9\xc6\xca\xd5\f\x18\xf1\x1a\x9e\xd9\xc4W@\xa6\x18\x06\x12\u040a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x95!\x83\xcf\u04ce5.W\x9d6\xde\xce\u0171\x84P\xf7\xfb\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95'\x8b\b\xde\xe7\xc0\xf2\xc8\xc0\xf7\"\xf9\xfc\xbb\xb9\xa5$\x1f\u0689\x82\x93\t\xf6O\r\xb0\x00\x00\u07d4\x95,W\xd2\xfb\x19Q\a\xd4\xcd\\\xa3\x00wA\x19\u07ed/x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x955r\xf0\xeam\xf9\xb1\x97\xca\xe4\x0eK\x8e\xcc\x05lCq\u014965\u026d\xc5\u07a0\x00\x00\u07d4\x95>\xf6R\xe7\xb7i\xf5=nxjX\x95/\xa9>\xe6\xab\u725b\ny\x1f\x12\x110\x00\x00\u07d4\x95DpF1;/:^\x19\xb9H\xfd;\x8b\xed\xc8,q|\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x95]\xb3\xb7C`\xb9\xa2hg~s\u03a8!f\x8a\xf6\xfa\u038a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\x95`\xe8\xacg\x18\xa6\xa1\xcd\xcf\xf1\x89\xd6\x03\xc9\x06>A=\xa6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x95g\xa0\u0781\x1d\xe6\xff\t[~\xe6N\u007f\x1b\x83\xc2a[\x80\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x95h\x1c\xda\xe6\x9b I\xce\x10\x1e2\\u\x98\x92\xca\xc3\xf8\x11\x89\x9a\xe9*\x9b\xc9L@\x00\x00\xe0\x94\x95h\xb7\xdeuV(\xaf5\x9a\x84T=\xe25\x04\xe1^A\xe6\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x95i\xc6:\x92\x84\xa8\x05bm\xb3\xa3.\x9d#c\x93GaQ\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x95\x80\x9e\x8d\xa3\xfb\xe4\xb7\xf2\x81\xf0\xb8\xb1q_B\x0f}}c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95\x9fW\xfd\xedj\xe3y\x13\xd9\x00\xb8\x1e_H\xa7\x93\"\xc6'\x89\r\xdb&\x10GI\x11\x80\x00\u07d4\x95\x9f\xf1\u007f\x1dQ\xb4s\xb4@\x10\x05'U\xa7\xfa\x8cu\xbdT\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x95\xa5w\xdc.\xb3\xael\xb9\xdf\xc7z\xf6\x97\xd7\xef\xdf\xe8\x9a\x01\x89\a_a\x0fp\xed \x00\x00\u07d4\x95\xcbm\x8acy\xf9J\xba\x8b\x88ViV,MD\x8eV\xa7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95\xd5PB{ZQLu\x1ds\xa0\xf6\u049f\xb6]\"\xed\x10\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x95\u064d\f\x10i\x90\x8f\x06zR\xac\xac+\x8bSM\xa3z\xfd\x89oY\xb60\xa9)p\x80\x00\xe0\x94\x95\xdfN4E\xd7f&$\u010e\xbat\u03de\nS\xe9\xf72\x8a\v\xdb\xc4\x1e\x03H\xb3\x00\x00\x00\u07d4\x95\xe6\xa5K-_g\xa2JHu\xafu\x10|\xa7\xea\x9f\xd2\xfa\x89Hz\x9a0E9D\x00\x00\xe0\x94\x95\xe6\xf9=\xac\"\x8b\xc7XZ%sZ\xc2\xd0v\xcc:@\x17\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x95\xe7ad$\xcd\ta\xa7\x17'$t7\xf0\x06\x92r(\x0e\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x95\xe8\n\x82\xc2\f\xbe= `$,\xb9-sX\x10\xd04\xa2\x89\x01\xc3.F?\u0539\x80\x00\u07d4\x95\xf6-\x02C\xed\xe6\x1d\xad\x9a1e\xf59\x05'\rT\xe2B\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\x95\xfbZ\xfb\x14\xc1\uf6b7\xd1y\xc5\xc3\x00P?\xd6j^\xe2\x89\x01\xda\xf7\xa0+\r\xbe\x80\x00\u07d4\x96\x10Y\"\x02\u0082\xab\x9b\u0628\x84Q\x8b>\v\xd4u\x817\x89\x0e\x87?D\x13<\xb0\x00\x00\xe0\x94\x96\x1cY\xad\xc7E\x05\u0446M\x1e\xcf\u02ca\xfa\x04\x12Y<\x93\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x96,\r\xec\x8a=FK\xf3\x9b\x12\x15\xea\xfd&H\n\xe4\x90\u0349l\x82\xe3\xea\xa5\x13\xe8\x00\x00\u07d4\x96,\xd2*\x8e\xdf\x1eONU\xb4\xb1]\xdb\xfb]\x9dT\x19q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x963K\xfe\x04\xff\xfaY\x02\x13\xea\xb3e\x14\xf38\xb8d\xb76\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x967\xdc\x12r=\x9cxX\x85B\uac02fO?\x03\x8d\x9d\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x96N\xabK'kL\u0618>\x15\xcar\xb1\x06\x90\x0f\xe4\x1f\u0389\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x96b\xee\x02\x19&h+1\xc5\xf2\x00\xceEz\xbe\xa7ll\xe9\x89$Y\x0e\x85\x89\xebj\x00\x00\xe0\x94\x96l\x04x\x1c\xb5\xe6}\xde25\xd7\xf8b\x0e\x1a\xb6c\xa9\xa5\x8a\x10\r P\xdacQ`\x00\x00\u07d4\x96pv\xa8w\xb1\x8e\xc1ZA[\xb1\x16\xf0n\xf3&E\u06e3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96{\xfa\xf7bC\u0379@\t\xae<\x8d5\x05\xe9\xc0\x80EK\xe0\xe8\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x96\x92A\x91\xb7\xdfe[3\x19\xdcma7\xf4\x81\xa7:\x0f\xf3\x89\xd9\xec\xb4\xfd \x8eP\x00\x00\u07d4\x96\x96\x05!83\x8cr/\x11@\x81\\\xf7t\x9d\r;:t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x96\xa5_\x00\xdf\xf4\x05\xdcM\xe5\xe5\x8cW\xf6\xf6\xf0\xca\xc5]/\x89jf\x167\x9c\x87\xb5\x80\x00\u07d4\x96\xaaW?\xed/#4\x10\u06eeQ\x80\x14[#\xc3\x1a\x02\xf0\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x96\xadW\x9b\xbf\xa8\u06ce\xbe\xc9\u0486\xa7.Fa\xee\xd8\xe3V\x89:\v\xa4+\xeca\x83\x00\x00\u07d4\x96\xb44\xfe\x06W\xe4*\u0302\x12\xb6\x86Q9\xde\xde\x15\x97\x9c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x96\xb9\x06\xear\x9fFU\xaf\xe3\xe5}5'|\x96}\xfa\x15w\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x96\xd6-\xfdF\b\u007fb@\x9d\x93\xdd`a\x88\xe7\x0e8\x12W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96\xd9\u0328\xf5^\xea\x00@\xecn\xb3H\xa1wK\x95\xd9>\xf4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x96\xe7\xc0\xc9\u057f\x10\x82\x1b\xf1@\xc5X\xa1E\xb7\xca\xc2\x13\x97\x899>\xf1\xa5\x12|\x80\x00\x00\u07d4\x96\xeaj\u021a+\xac\x954{Q\u06e6=\x8b\xd5\xeb\xde\xdc\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96\xea\xfb\xf2\xfboM\xb9\xa46\xa7LE\xb5eDR\xe28\x19\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x96\xebR>\x83/P\n\x01}\xe1>\xc2\u007f]6lV\x0e\xff\x89\x10\xac\u03baC\xee(\x00\x00\u07d4\x96\xf0F*\xe6\xf8\xb9`\x88\xf7\xe9\u018ct\xb9\u062d4\xb3G\x89a\t=|,m8\x00\x00\u07d4\x96\xf8 P\vp\xf4\xa3\xe3#\x9da\x9c\xff\x8f\" u\xb15\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x96\xfeY\xc3\u06f3\xaa|\xc8\xcbbH\fe\xe5nb\x04\xa7\xe2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x96\xffoP\x99h\xf3l\xb4,\xbaH\xdb2\xf2\x1fVv\xab\xf8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x97\t8R*\xfb^\x8f\x99Hs\xc9\xfb\xdc&\xe3\xb3~1L\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x97\n\xbdS\xa5O\xcaJd) |\x18-MW\xbb9\u0520\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97\r\x8b\x8a\x00\x16\xd1C\x05O\x14\x9f\xb3\xb8\xe5P\xdc\a\x97\u01c965\u026d\xc5\u07a0\x00\x00\u07d4\x97,/\x96\xaa\x00\u03ca/ Z\xbc\xf8\x93|\fu\xf5\xd8\u0649\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97?N6\x1f\xe5\xde\u0358\x9dL\x8f}|\xc9y\x908]\xaf\x89\x15\x0f\x85C\xa3\x87B\x00\x00\u07d4\x97M\x05A\xabJG\xec\u007fu6\x9c\x00i\xb6J\x1b\x81w\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u0794\x97M/\x17\x89_)\x02\x04\x9d\xea\xae\xcf\t\xc3\x04e\a@-\x88\xcc\x19\u00947\xab\x80\x00\u07d4\x97R\xd1O^\x10\x93\xf0qq\x1c\x1a\xdb\xc4\xe3\xeb\x1e\\W\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97V\xe1v\xc9\xefi>\xe1\xee\u01b9\xf8\xb1Q\xd3\x13\xbe\xb0\x99\x89A\rXj \xa4\xc0\x00\x00\u07d4\x97_7d\xe9{\xbc\xcfv|\xbd;y[\xa8m\x8b\xa9\x84\x0e\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\x97j\x18Sj\xf4\x18tBc\b\x87\x1b\xcd\x15\x12\xa7u\xc9\xf8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x97n<\xea\xf3\xf1\xafQ\xf8\u009a\xff]\u007f\xa2\x1f\x03\x86\xd8\xee\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x97w\xcca\xcfuk\xe3\xb3\xc2\f\xd4I\x1ci\xd2u\xe7\xa1 \x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x97\x81\v\xaf\xc3~\x840c2\xaa\xcb5\xe9*\xd9\x11\xd2=$\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x97\x8cC\f\xe45\x9b\x06\xbc,\xdf\\)\x85\xfc\x95\x0eP\xd5\u0209\x1a\x05V\x90\xd9\u06c0\x00\x00\u07d4\x97\x95\xf6C\x19\xfc\x17\xdd\x0f\x82a\xf9\xd2\x06\xfbf\xb6L\xd0\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97\x99\xca!\xdb\xcfi\xbf\xa1\xb3\xf7+\xacQ\xb9\xe3\xcaX|\xf9\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x97\x9c\xbf!\xdf\xec\x8a\xce?\x1c\x19m\x82\u07d6%4\xdf9O\x89\x99\x91\xd4x\xddM\x16\x00\x00\u07d4\x97\x9dh\x1ca}\xa1o!\xbc\xac\xa1\x01\xed\x16\xed\x01Z\xb6\x96\x89e\xea=\xb7UF`\x00\x00\u07d4\x97\x9f0\x15\x8bWK\x99\x9a\xab4\x81\a\xb9\xee\xd8[\x1f\xf8\xc1\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x97\xa8o\x01\xce?|\xfdDA3\x0e\x1c\x9b\x19\xe1\xb1\x06\x06\xef\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x97\xb9\x1e\xfesP\xc2\xd5~~@k\xab\x18\xf3a{\xcd\xe1J\x8a\x02\x1e\x19\x99\xbb\xd5\u04be\x00\x00\u07d4\x97\xd0\xd9r^;p\xe6u\x841s\x93\x8e\xd3q\xb6,\u007f\xac\x89\t79SM(h\x00\x00\u07d4\x97\xd9\xe4jv\x04\u05f5\xa4\xeaN\xe6\x1aB\xb3\xd25\x0f\xc3\xed\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97\xdc&\xecg\n1\xe0\"\x1d*u\xbc]\xc9\xf9\f\x1fo\u0509\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x97\xde!\xe4!\xc3\u007f\xe4\xb8\x02_\x9aQ\xb7\xb3\x90\xb5\xdfx\x04\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\x97\xe2\x89s\xb8`\xc5g@(\x00\xfb\xb6<\xe3\x9a\x04\x8a=y\x89\x05B%:\x12l\xe4\x00\x00\u07d4\x97\xe5\xcca'\xc4\xf8\x85\xbe\x02\xf4KB\xd1\u0230\xac\x91\u44c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97\xf1\xfeL\x80\x83\xe5\x96!*\x18w(\xdd\\\xf8\n1\xbe\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x97\xf7v\x06W\xc1\xe2\x02u\x90\x86\x96>\xb4!\x1c_\x819\xb9\x8a\n\x8a\t\u007f\xcb=\x17h\x00\x00\xe0\x94\x97\xf9\x9bk\xa3\x13F\u0358\xa9\xfeL0\x8f\x87\u0165\x8cQQ\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x98\n\x84\xb6\x86\xfc1\xbd\xc8<\"\x10XTjq\xb1\x1f\x83\x8a\x89*AUH\xaf\x86\x81\x80\x00\u07d4\x98\x10\xe3J\x94\xdbn\xd1V\xd08\x9a\x0e+\x80\xf4\xfdk\n\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\x1d\xdf\x04\x04\xe4\xd2-\xdaUj\a&\xf0\v-\x98\xab\x95i\x8965f3\xeb\xd8\xea\x00\x00\xe0\x94\x98\x1fq'u\xc0\xda\xd9u\x18\xff\xed\xcbG\xb9\xad\x1dl'b\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\x984h!\x80\xb9\x82\xd1f\xba\u06dd\x9d\x1d\x9b\xbf\x01m\x87\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x986\xb4\xd3\x04sd\x1a\xb5j\xee\xe1\x92Bv\x1drrQx\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x989sB\xec_=L\xb8w\xe5N\xf5\xd6\xf1\xd3fs\x1b\u050a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\x98Fd\x886\xa3\a\xa0W\x18O\xd5\x1fb\x8a_\x8c\x12B|\x8a\x04\vi\xbfC\xdc\xe8\xf0\x00\x00\xe0\x94\x98Jy\x85\xe3\xcc~\xb5\xc96\x91\xf6\xf8\xcc{\x8f$]\x01\xb2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x98]p\xd2\a\x89+\xed9\x85\x90\x02N$!\xb1\xcc\x11\x93Y\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x98m\xf4~v\xe4\u05e7\x89\xcd\xee\x91<\u0243\x16P\x93l\x9d\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x98t\x80?\xe1\xf3\xa06^y\"\xb1Bp\xea\xeb\x03,\xc1\xb5\x89<\xf5\x92\x88$\xc6\xc2\x00\x00\u07d4\x98ub4\x95\xa4l\xdb\xf2YS\x0f\xf88\xa1y\x9e\u00c9\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98v\x18\xc8VV |{\xac\x15\a\xc0\xff\xef\xa2\xfbd\xb0\x92\x89\x03}\xfeC1\x89\xe3\x80\x00\u07d4\x98|\x9b\xcdn?9\x90\xa5+\xe3\xed\xa4q\f'Q\x8fOr\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x98\x82\x96|\xeeh\u04a89\xfa\u062bJ|=\xdd\xf6\xc0\xad\u0209Hx\xbe\x1f\xfa\xf9]\x00\x00\u07d4\x98\x85\\}\xfb\xee3SD\x90J\x12\xc4\fs\x17\x95\xb1:T\x899\xfb\xae\x8d\x04-\xd0\x00\x00\u07d4\x98\x9c\f\xcf\xf6T\xda\x03\xae\xb1\x1a\xf7\x01\x05Ea\xd6)~\x1d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x98\xa0\xe5Lm\x9d\u023e\x96'l\xeb\xf4\xfe\xc4`\xf6#]\x85\x89j\u0202\x10\tR\u01c0\x00\u07d4\x98\xb7i\xcc0\\\xec\xfbb\x9a\x00\xc9\a\x06\x9d~\xf9\xbc:\x12\x89\x01h\u048e?\x00(\x00\x00\xe0\x94\x98\xbaN\x9c\xa7/\xdd\xc2\fi\xb49ov\xf8\x18?z*N\x8a\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\x00\u07d4\x98\xbeimQ\xe3\x90\xff\x1cP\x1b\x8a\x0fc1\xb6(\xdd\u016d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\xbe\u04e7.\xcc\xfb\xaf\xb9#H\x92\x93\xe4)\xe7\x03\xc7\xe2[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\xbfJ\xf3\x81\v\x84#\x87\xdbp\xc1MF\t\x96&\x00=\x10\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x98\xc1\x0e\xbf,O\x97\u02e5\xa1\xab?*\xaf\xe1\xca\xc4#\xf8\u02c9\x10CV\x1a\x88)0\x00\x00\u07d4\x98\xc1\x9d\xba\x81\v\xa6\x11\xe6\x8f/\x83\xee\x16\xf6\xe7tO\f\x1f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x98\xc5IJ\x03\xac\x91\xa7h\xdf\xfc\x0e\xa1\xdd\u0b3f\x88\x90\x19\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\x98\xd2\x04\xf9\b_\x8c\x8e}\xe2>X\x9bd\xc6\xef\xf6\x92\xccc\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x98\xd3s\x19\x92\xd1\xd4\x0e\x12\x11\xc7\xf75\xf2\x18\x9a\xfa\a\x02\xe0\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x98\xe2\xb6\xd6\x06\xfd-i\x91\xc9\xd6\xd4\a\u007f\xdf?\xddE\x85\u06890\xdf\x1ao\x8a\xd6(\x00\x00\u07d4\x98\xe3\xe9\v(\xfc\xca\ue087y\xb8\xd4\nUh\xc4\x11n!\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x98\xe6\xf5G\u06c8\xe7_\x1f\x9c\x8a\xc2\xc5\xcf\x16'\xbaX\v>\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x98\xf4\xaf:\xf0\xae\xde_\xaf\xdcB\xa0\x81\xec\xc1\xf8\x9e<\xcf \x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x98\xf6\xb8\xe6!=\xbc\x9aU\x81\xf4\xcc\xe6e_\x95%+\xdb\a\x89\x11Xr\xb0\xbc\xa40\x00\x00\u07d4\x99\te\r\u05719{\x8b\x8b\x0e\xb6\x94\x99\xb2\x91\xb0\xad\x12\x13\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x99\x11s`\x19G\xc2\bJb\xd69R~\x96\x15\x12W\x9a\xf9\x89 \x86\xac5\x10R`\x00\x00\u07d4\x99\x12\x9d[<\f\xdeG\xea\r\xefM\xfc\a\r\x1fJY\x95'\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x99\x17\u058dJ\xf3A\xd6Q\xe7\xf0\a\\m\xe6\xd7\x14Nt\t\x8a\x012\xd4Gl\b\xe6\xf0\x00\x00\u07d4\x99\x1a\xc7\xcap\x97\x11_& ^\xee\x0e\xf7\xd4\x1e\xb4\xe3\x11\xae\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\x99#e\xd7d\xc5\xce5@9\xdd\xfc\x91.\x02:u\xb8\xe1h\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x99&F\xac\x1a\u02ab\xf5\u076b\xa8\xf9B\x9a\xa6\xa9Nt\x96\xa7\x8967Pz0\xab\xeb\x00\x00\u07d4\x99&\x83'\xc3s3.\x06\xc3\xf6\x16B\x87\xd4U\xb9\xd5\xfaK\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99(\xffqZ\xfc:+`\xf8\xebL\u013aN\xe8\u06b6\u5749\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4\x992\xef\x1c\x85\xb7Z\x9b*\x80\x05}P\x874\xc5\x10\x85\xbe\u0309\x02\xb8?\xa50\x1dY\x00\x00\xe0\x94\x99?\x14ax`^f\xd5\x17\xbex.\xf0\xb3\xc6\x1aN\x19%\x8a\x01|\x1f\x055\u05e5\x83\x00\x00\xe0\x94\x99A7\x04\xb1\xa3.p\xf3\xbc\ri\u0748\x1c8VkT\u02ca\x05\xcckiF1\xf7\x12\x00\x00\u07d4\x99AR\xfc\x95\xd5\xc1\u028b\x88\x11:\xbb\xadMq\x0e@\xde\xf6\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x99D\xfe\xe9\xd3JJ\x88\x00#\u01c92\xc0\vY\xd5\xc8*\x82\x89(\xa8\xa5k6\x90\a\x00\x00\u07d4\x99L\u00b5\"~\xc3\xcf\x04\x85\x12F|A\xb7\xb7\xb7H\x90\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99q\xdf`\xf0\xaef\xdc\xe9\xe8\xc8N\x17\x14\x9f\t\xf9\xc5/d\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x99v\x94~\xff_j\xe5\xda\b\xddT\x11\x92\xf3x\xb4(\xff\x94\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x99}e\x92\xa3\x15\x89\xac\xc3\x1b\x99\x01\xfb\xeb<\xc3\xd6[2\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\x82\xa5\x89\x0f\xfbT\x06\u04ec\xa8\u04bf\xc1\xddp\xaa\xa8\n\xe0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\x87\x8f\x9dn\n~\u066e\u01c2\x97\xb78y\xa8\x01\x95\xaf\xe0\x89\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d4\x99\x8c\x1f\x93\xbc\xdbo\xf2<\x10\xd0\u0712G(\xb7;\xe2\xff\x9f\x896[\xf3\xa43\xea\xf3\x00\x00\u07d4\x99\x91aL[\xaaG\xddl\x96\x87FE\xf9z\xdd,=\x83\x80\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x99\x92J\x98\x16\xbb}\xdf?\xec\x18D\x82\x8e\x9a\xd7\xd0k\xf4\xe6\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x99\x99vh\xf7\xc1\xa4\xff\x9e1\xf9\x97z\xe3\"K\u02c8z\x85\x89\x0f\xc969(\x01\xc0\x00\x00\u07d4\x99\x9cI\xc1t\xca\x13\xbc\x83l\x1e\n\x92\xbf\xf4\x8b'\x15C\u0289\xb1\xcf$\xdd\u0431@\x00\x00\u07d4\x99\xa4\xde\x19\xde\u05d0\b\xcf\xdc\xd4]\x01M.XK\x89\x14\xa8\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x99\xa9k\xf2$.\xa1\xb3\x9e\xceo\xcc\r\x18\xae\xd0\f\x01y\xf3\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x99\xb0\x18\x93+\xca\xd3U\xb6y+%]\xb6p-\xec\x8c\xe5\u0749\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x99\xb7C\xd1\xd9\xef\xf9\r\x9a\x194\xb4\xdb!\xd5\x19\u061bJ8\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x99\xb8\xc8$\x86\x9d\xe9\xed$\xf3\xbf\xf6\x85L\xb6\xddE\xcc?\x9f\x89e\xea=\xb7UF`\x00\x00\u07d4\x99\xc0\x17L\xf8N\a\x83\xc2 \xb4\xebj\xe1\x8f\xe7\x03\x85J\u04c9py\xa2W=\fx\x00\x00\u07d4\x99\xc1\xd9\xf4\fj\xb7\xf8\xa9/\xce/\xdc\xe4zT\xa5\x86\xc5?\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x99\xc26\x14\x1d\xae\xc87\xec\xe0O\xda\xee\x1d\x90\u03cb\xbd\xc1\x04\x89ve\x16\xac\xac\r \x00\x00\u07d4\x99\xc3\x1f\xe7HX7\x87\xcd\xd3\xe5%\xb2\x81\xb2\x18\x96\x179\xe3\x897\b\xba\xed=h\x90\x00\x00\u07d4\x99\xc4u\xbf\x02\xe8\xb9!J\xda_\xad\x02\xfd\xfd\x15\xba6\\\f\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\x99\u0203%\x85F\xcc~N\x97\x1fR.8\x99\x18\xda^\xa6:\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x99\xc9\xf9>E\xfe<\x14\x18\xc3S\xe4\u016c8\x94\xee\xf8\x12\x1e\x89\x05\x85\xba\xf1E\x05\v\x00\x00\xe0\x94\x99\xd1W\x9c\xd4&\x82\xb7dN\x1dOq(D\x1e\xef\xfe3\x9d\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x99\u0475\x85\x96_@jB\xa4\x9a\x1c\xa7\x0fv\x9evZ?\x98\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\x99\xdf\xd0PL\x06\xc7C\xe4e4\xfd{U\xf1\xf9\xc7\xec3)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\xf4\x14|\xcck\u02c0\u0304.i\xf6\xd0\x0e0\xfaA3\u0649\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x99\xf7\u007f\x99\x8b \xe0\xbc\xdc\xd9\xfc\x83\x86ARl\xf2Y\x18\xef\x89a\t=|,m8\x00\x00\u07d4\x99\xfa\xd5\x008\xd0\xd9\xd4\xc3\xfb\xb4\xbc\xe0V\x06\xec\xad\xcdQ!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\xfe\r \x12(\xa7S\x14VU\xd4(\xeb\x9f\xd9I\x85\xd3m\x89i \xbf\xf3QZ:\x00\x00\u07d4\x9a\a\x9c\x92\xa6)\xca\x15\xc8\xca\xfa.\xb2\x8d[\xc1z\xf8(\x11\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9a\r<\xee=\x98\x92\xea;7\x00\xa2\u007f\xf8A@\xd9\x02T\x93\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x9a$\u038dH\\\xc4\xc8nI\u07b3\x90\"\xf9,t0\xe6~\x89Fy\x1f\xc8N\a\xd0\x00\x00\u07d4\x9a,\xe4;]\x89\u0593k\x8e\x8c5G\x91\xb8\xaf\xff\x96$%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9a9\x01bS^9\x88w\xe4\x16x}b9\xe0uN\x93|\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9a=\xa6P#\xa10 \xd2!E\xcf\xc1\x8b\xab\x10\xbd\x19\xceN\x89\x18\xbfn\xa3FJ:\x00\x00\xe0\x94\x9a>+\x1b\xf3F\xdd\a\v\x02sW\xfe\xacD\xa4\xb2\xc9}\xb8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9aL\xa8\xb8!\x17\x89NC\xdbr\xb9\xfax\xf0\xb9\xb9:\xce\t\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x9aR.R\xc1\x95\xbf\xb7\xcf_\xfa\xae\u06d1\xa3\xbath\x16\x1d\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9aZ\xf3\x1c~\x063\x9a\u0234b\x8d|M\xb0\xce\x0fE\u0224\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\x9ac?\xcd\x11,\xce\xebv_\xe0A\x81ps*\x97\x05\u7708\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x9ac\u0445\xa7\x91)\xfd\xab\x19\xb5\x8b\xb61\xea6\xa4 TN\x89\x02F\xdd\xf9yvh\x00\x00\u07d4\x9ag\b\u0778\x90<(\x9f\x83\xfe\x88\x9c\x1e\xdc\xd6\x1f\x85D#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9ao\xf5\xf6\xa7\xaf{z\xe0\xed\x9c \xec\xecP#\u0481\xb7\x86\x89\x8a\x12\xb9\xbdjg\xec\x00\x00\xe0\x94\x9a\x82\x82m<)H\x1d\xcc+\u0495\x00G\xe8\xb6\x04\x86\xc38\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9a\x8e\xcaA\x89\xffJ\xa8\xff~\u0536\xb7\x03\x9f\t\x02!\x9b\x15\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9a\x95;[\xccp\x93y\xfc\xb5Y\u05f9\x16\xaf\u06a5\f\xad\u0309\x05k\xc7^-c\x10\x00\x00\u07d4\x9a\x99\v\x8a\xebX\x8d~\xe7\xec.\xd8\xc2\xe6Os\x82\xa9\xfe\xe2\x89\x01\xd1'\xdbi\xfd\x8b\x00\x00\u07d4\x9a\x9d\x1d\xc0\xba\xa7}n \xc3\xd8I\u01c8b\xdd\x1c\x05L\x87\x89/\xb4t\t\x8fg\xc0\x00\x00\xe0\x94\x9a\xa4\x8cf\xe4\xfbJ\u0419\x93N2\x02.\x82t'\xf2w\xba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9a\xa80\x8fB\x91\x0eZ\xde\t\xc1\xa5\xe2\x82\xd6\xd9\x17\x10\xbd\xbf\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9a\xaa\xfa\x00gd~\u0659\x06kzL\xa5\xb4\xb3\xf3\xfe\xaao\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9a\xb9\x88\xb5\x05\xcf\xee\x1d\xbe\x9c\u044e\x9bTs\xb9\xa2\xd4\xf56\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\x9a\xb9\x8dm\xbb\x1e\xaa\xe1mE\xa0EhT\x1a\xd3\xd8\xfe\x06\u0309\x0e\xc5\x04d\xfe#\xf3\x80\x00\xe0\x94\x9a\xba+^'\xffx\xba\xaa\xb5\xcd\u0248\xb7\xbe\x85\\\xeb\xbd\u038a\x02\x1e\f\x00\x13\a\n\xdc\x00\x00\u07d4\x9a\xc4\xdaQ\xd2x\"\xd1\xe2\b\xc9n\xa6J\x1e[U)\x97#\x89\x05lUy\xf7\"\x14\x00\x00\u0794\x9a\xc8S\x97y*i\u05cf(k\x86C*\a\xae\u03b6\x0ed\x88\xc6s\xce<@\x16\x00\x00\xe0\x94\x9a\xc9\a\xee\x85\xe6\xf3\xe2#E\x99\x92\xe2V\xa4?\xa0\x8f\xa8\xb2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9a\xd4\u007f\xdc\xf9\u0354-(\xef\xfd[\x84\x11[1\xa6X\xa1>\x89\xb2Y\xec\x00\xd5;(\x00\x00\u07d4\x9a\xdb\u04fc{\n\xfc\x05\xd1\xd2\xed\xa4\x9f\xf8c\x93\x9cH\xdbF\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x9a\xdfE\x8b\xff5\x99\xee\xe1\xa2c\x98\x85\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x9a\xf9\xdb\xe4t\"\xd1w\xf9E\xbd\xea\xd7\xe6\xd8)05b0\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x9a\xfaSkLf\xbc8\xd8u\u0133\x00\x99\xd9&\x1f\xdb8\xeb\x89\v*\x8f\x84*w\xbc\x80\x00\u07d4\x9b\x06\xad\x84\x1d\xff\xbeL\xcfF\xf1\x03\x9f\u00c6\xf3\xc3!Dn\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\x11h\u078a\xb6KGU/3\x89\x80\n\x9c\xc0\x8bFf\u03c9]\u0212\xaa\x111\xc8\x00\x00\u07d4\x9b\x18\x11\xc3\x05\x1fF\xe6d\xaeK\xc9\xc8$\u0445\x92\xc4WJ\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x9b\x18G\x86U\xa4\x85\x1c\xc9\x06\xe6`\xfe\xaca\xf7\xf4\u023f\xfc\x89\xe2G\x8d8\x90}\x84\x00\x00\u07d4\x9b\"\xa8\r\\{3t\xa0[D`\x81\xf9}\n4\a\x9e\u007f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x9b+\xe7\xf5gT\xf5\x05\xe3D\x1a\x10\xf7\xf0\xe2\x0f\xd3\xdd\xf8I\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\x9b2\xcfOQ\x15\xf4\xb3J\x00\xa6La}\xe0c\x875C#\x89\x05\xb8\x1e\u0608 |\x80\x00\u07d4\x9bC\u0739_\xde1\x80u\xa5g\xf1\xe6\xb5v\x17\x05^\xf9\xe8\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x9bDO\xd37\xe5\xd7R\x93\xad\xcf\xffp\xe1\xea\x01\xdb\x022\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9bH$\xff\x9f\xb2\xab\xdaUM\xeeO\xb8\xcfT\x91eW\x061\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9bL'\x15x\f\xa4\xe9\x9e`\xeb\xf2\x19\xf1Y\f\x8c\xadP\n\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x9bY\xeb!;\x1eue\xe4PG\xe0N\xa07O\x10v-\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\\9\xf7\xe0\xac\x16\x8c\x8e\xd0\xed4\x04w\x11}\x1bh.\xe9\x89\x05P\x05\xf0\xc6\x14H\x00\x00\u07d4\x9b^\xc1\x8e\x83\x13\x88}\xf4a\u0490.\x81\xe6z\x8f\x11;\xb1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9bd\xd3\u034d+s\xf6hA\xb5\xc4k\xb6\x95\xb8\x8a\x9a\xb7]\x89\x01 :Ov\f\x16\x80\x00\u07d4\x9be\x8f\xb3a\xe0F\xd4\xfc\xaa\x8a\xefm\x02\xa9\x91\x11\"6%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9bfA\xb1>\x17/\xc0r\xcaK\x83'\xa3\xbc(\xa1[f\xa9\x89\x06\x81U\xa46v\xe0\x00\x00\xe0\x94\x9bh\xf6t\x16\xa6;\xf4E\x1a1\x16L\x92\xf6r\xa6\x87Y\xe9\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\x9bw6i\xe8}v\x01\x8c\t\x0f\x82U\xe5D\t\xb9\u0728\xb2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9bw\xeb\xce\xd7\xe2\x15\xf0\x92\x0e\x8c+\x87\x00$\xf6\xec\xb2\xff1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9b|\x88\x10\xcc|\u021e\x80Nm>8\x12\x18PG(w\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\xa5=\xc8\xc9^\x9aG/\xeb\xa2\xc4\xe3,\x1d\xc4\xdd{\xabF\x89Hz\x9a0E9D\x00\x00\xe0\x94\x9b\xac\xd3\xd4\x0f;\x82\xac\x91\xa2d\xd9\u060d\x90\x8e\xac\x86d\xb9\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9b\xb7`\xd5\u0089\xa3\xe1\xdb\x18\xdb\tSE\xcaA;\x9aC\u0089\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x9b\xb7b\x04\x18j\xf2\xf6;\xe7\x91h`\x16\x87\xfc\x9b\xadf\x1f\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9b\xb9\xb0*&\xbf\xe1\xcc\xc3\xf0\xc6!\x9e&\x1c9\u007f\xc5\xcax\x89Hz\x9a0E9D\x00\x00\u07d4\x9b\xc5s\xbc\xda#\xb8\xb2o\x90s\xd9\f#\x0e\x8eq\xe0'\v\x896/u\xa40]\f\x00\x00\u07d4\x9b\xd7\u00caB\x100JMe>\xde\xff\x1b<\xe4_\xcexC\x89\x0fI\x89A\xe6d(\x00\x00\xe0\x94\x9b\u0600h\xe10u\xf3\xa8\xca\xc4d\xa5\xf9I\xd6\xd8\x18\xc0\xf6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9b\xd9\x05\xf1q\x9f\u01ec\xd0\x15\x9dM\xc1\xf8\xdb/!G#8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9b\xdb\u071b\x9741\xd1<\x89\xa3\xf9u~\x9b;bu\xbf\u01c9\x1b\x1a}\u03caD\u04c0\x00\u07d4\x9b\xe3\xc3)\xb6*(\xb8\xb0\x88l\xbd\x8b\x99\xf8\xbc\x93\f\xe3\xe6\x89\x04\t\xe5+H6\x9a\x00\x00\xe0\x94\x9b\xf5\x8e\xfb\xea\a\x84\xeb\x06\x8a\xde\u03e0\xbb!P\x84\xc7:5\x8a\x01:k+VHq\xa0\x00\x00\u07d4\x9b\xf6r\xd9y\xb3fR\xfcR\x82Tzjk\xc2\x12\xaeCh\x89#\x8f\xd4,\\\xf0@\x00\x00\xe0\x94\x9b\xf7\x03\xb4\x1c6$\xe1_@T\x96#\x90\xbc\xba0R\xf0\xfd\x8a\x01H>\x01S<.<\x00\x00\u07d4\x9b\xf7\x1f\u007f\xb57\xacT\xf4\xe5\x14\x94\u007f\xa7\xffg(\xf1m/\x89\x01\u03c4\xa3\n\n\f\x00\x00\u07d4\x9b\xf9\xb3\xb2\xf2<\xf4a\xebY\x1f(4\v\xc7\x19\x93\x1c\x83d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x9b\xfce\x9c\x9c`\x1e\xa4*k!\xb8\xf1p\x84\xec\x87\xd7\x02\x12\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9b\xff\xf5\r\xb3jxUU\xf0vR\xa1S\xb0\xc4+\x1b\x8bv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\x05\xe9\xd0\xf0u\x8eyS\x03q~1\xda!<\xa1W\u618965\u026d\xc5\u07a0\x00\x00\u07d4\x9c\x1bw\x1f\t\xaf\x88*\xf0d0\x83\xde*\xa7\x9d\xc0\x97\xc4\x0e\x89\x86p\xe9\xece\x98\xc0\x00\x00\u07d4\x9c(\xa2\xc4\b`\x91\xcb]\xa2&\xa6W\xce2H\xe8\xea{o\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\x9c/\xd5@\x89\xaff]\xf5\x97\x1ds\xb8\x04a`9dsu\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c4@\x98\xbaaZ9\x8f\x11\xd0\t\x90[\x17|D\xa7\xb6\x02\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c=\x06\x92\xce\xee\xf8\n\xa4\x96\\\xee\xd2b\xff\xc7\xf0i\xf2\u0709\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9c@\\\xf6\x97\x95a8\x06^\x11\xc5\xf7U\x9eg$[\u0465\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9cE *%\xf6\xad\x00\x11\xf1\x15\xa5\xa7\"\x04\xf2\xf2\x19\x88f\x8a\x01\x0f\xcf:b\xb0\x80\x98\x00\x00\xe0\x94\x9cI\xde\xffG\b_\xc0\x97\x04\u02a2\u0728\u0087\xa9\xa17\u068a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x9cK\xbc\xd5\xf1dJo\aX$\xdd\xfe\x85\xc5q\u05ab\xf6\x9c\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4\x9cRj\x14\x06\x83\xed\xf1C\x1c\xfa\xa1(\xa95\xe2\xb6\x14\u060b\x89\x06\x04o7\xe5\x94\\\x00\x00\xe0\x94\x9cT\xe4\xedG\x9a\x85h)\u01bbB\u069f\vi*u\xf7(\x8a\x01\x97\xa8\xf6\xddU\x19\x80\x00\x00\xe0\x94\x9cX\x1a`\xb6\x10(\xd94\x16y)\xb2-p\xb3\x13\xc3O\u040a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x9c\\\xc1\x11\t,\x12!\x16\xf1\xa8_N\xe3\x14\bt\x1a}/\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\x9ck\u0264k\x03\xaeT\x04\xf0C\xdf\xcf!\x88>A\x10\xcc3\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9cx\x96?\xbc&<\t\xbdr\xe4\xf8\xde\xf7J\x94u\xf7\x05\\\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4\x9cx\xfb\xb4\xdfv\x9c\xe2\xc1V\x92\f\xfe\xdf\xda\x03:\x0e%J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x9c{m\xc5\x19\x0f\xe2\x91)c\xfc\xd5yh>\xc79Q\x16\xb0\x89*\x11)\u0413g \x00\x00\u07d4\x9c\x80\xbc\x18\xe9\xf8\u0516\x8b\x18]\xa8\u01df\xa6\xe1\x1f\xfc>#\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x9c\x98\xfd\xf1\xfd\u034b\xa8\xf4\u0170L:\xe8X~\xfd\xf0\xf6\xe6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x9c\x99\xa1\u0691\u0552\v\xc1N\f\xb9\x14\xfd\xf6+\x94\u02c3X\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9c\x99\xb6&\x06(\x1b\\\xef\xab\xf3aV\xc8\xfeb\x83\x9e\xf5\xf3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x9c\x9a\a\xa8\xe5|1r\xa9\x19\xefdx\x94tI\x0f\r\x9fQ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9c\x9d\xe4G$\xa4\x05M\xa0\xea\xa6\x05\xab\u0300&hw\x8b\xea\x89\n\xd7\xd5\xca?\xa5\xa2\x00\x00\u07d4\x9c\x9f;\x8a\x81\x1b!\xf3\xff?\xe2\x0f\xe9p\x05\x1c\xe6j\x82O\x89>\xc2\u07bc\a\u053e\x00\x00\xe0\x94\x9c\x9f\x89\xa3\x91\x0fj*\xe8\xa9\x10G\xa1z\xb7\x88\xbd\xde\xc1p\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9c\xa0B\x9f\x87O\x8d\xce\xe2\xe9\xc0b\xa9\x02\n\x84*Xz\xb9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\xa4.\u7838\x98\xf6\xa5\xcc`\xb5\xa5\u05f1\xbf\xa3\xc321\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\xb2\x8a\xc1\xa2\n\x10o\u007f76\x92\xc5\xceLs\xf172\xa1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c\xcd\u0732\xcf\u00b2[\br\x9a\n\x98\xd9\xe6\xf0 .\xa2\xc1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9c\xe2\u007f$^\x02\xd1\xc3\x12\xc1\xd5\x00x\x8c\x9d\xefv\x90E;\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9c\xe56;\x13\xe8#\x8a\xa4\xdd\x15\xac\u0432\xe8\xaf\xe0\x872G\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9c\xf2\x92\x8b\xee\xf0\x9a@\xf9\xbf\xc9S\xbe\x06\xa2Q\x11a\x82\xfb\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9d\x06\x91\x97\xd1\xdeP\x04Z\x18o^\xc7D\xac@\u8bd1\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\x9d\x0e}\x92\xfb0XS\u05d8&;\xf1^\x97\xc7+\xf9\xd7\xe0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9d\x0f4~\x82k}\u03aa\xd2y\x06\n5\xc0\x06\x1e\xcf3K\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d u\x17B,\xc0\xd6\r\xe7\xc27\tzMO\xce \x94\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x9d%\n\xe4\xf1\x10\xd7\x1c\xaf\u01f0\xad\xb5.\x8d\x9a\xcbfy\xb8\x8a\x02\x15mn\x99r\x13\xc0\x00\x00\xe0\x94\x9d+\xfc6\x10o\x03\x82P\xc0\x18\x01hW\x85\xb1l\x86\xc6\r\x8aPw\xd7]\xf1\xb6u\x80\x00\x00\xe0\x94\x9d0\xcb#{\xc0\x96\xf1p6\xfc\x80\xdd!\xcah\x99,\xa2\u064a\x06n\xe71\x8f\u070f0\x00\x00\u07d4\x9d2\x96.\xa9\x97\x00\xd92(\xe9\xdb\xda\xd2\xcc7\xbb\x99\xf0~\x89\xb4c+\xed\xd4\xde\xd4\x00\x00\u07d4\x9d4\xda\xc2[\xd1X(\xfa\xef\xaa\xf2\x8fq\aS\xb3\x9e\x89\u0709;\x1cV\xfe\xd0-\xf0\x00\x00\u07d4\x9d6\x91e\xfbp\xb8\x1a:v_\x18\x8f\xd6\f\xbe^{\th\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9d@\xe0\x12\xf6\x04%\xa3@\xd8-\x03\xa1\xc7W\xbf\xab\xc7\x06\xfb\x89\t4o:\xdd\u020d\x80\x00\u07d4\x9dAt\xaaj\xf2\x84v\xe2)\xda\xdbF\x18\b\b\xc6u\x05\xc1\x89B\x1a\xfd\xa4.\u0597\x00\x00\u07d4\x9dB\x133\x9a\x01U\x18avL\x87\xa9<\xe8\xf8_\x87\x95\x9a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9dF\f\x1b7\x9d\xdb\x19\xa8\xc8[LgG\x05\r\xdf\x17\xa8u\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x9dG\xba[L\x85\x05\xad\x8d\xa4)4(\va\xa0\xe1\xe8\xb9q\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9dM2\x11w%n\xbd\x9a\xfb\xda0A5\xd5\x17\xc3\xdcV\x93\x89!d\xb7\xa0J\u0220\x00\x00\u07d4\x9dO\xf9\x89\xb7\xbe\u066b\x10\x9d\x10\xc8\xc7\xe5_\x02\xd7g4\xad\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x9dQ\x15C\xb3\xd9\xdc`\xd4\u007f\t\u051d\x01\xb6\u0118\xd8 x\x8a\x02a\x97\xb9Qo\u00d4\x00\x00\u07d4\x9dn\u03e0:\xf2\xc6\xe1D\xb7\xc4i*\x86\x95\x1e\x90.\x9e\x1f\x89\xa2\xa5\xaa`\xad$?\x00\x00\u07d4\x9dvU\xe9\xf3\xe5\xba]n\x87\xe4\x12\xae\xbe\x9e\xe0\u0512G\ue24e\t1\x1c\x1d\x80\xfa\x00\x00\u07d4\x9dx1\xe84\xc2\v\x1b\xaaiz\xf1\xd8\xe0\xc6!\u016f\xff\x9a\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4\x9dx\xa9u\xb7\xdb^M\x8e(\x84\\\xfb\xe7\xe3\x14\x01\xbe\r\u0649H\xa40k\xa2\u5e5c\x8ahX\u02f5,\f\xf75\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\x9d\u007f\xdapp\xbf>\xe9\xbb\u0664\x1fU\xca\u0505J\xe6\xc2,\x8a\x02U\u02e3\xc4o\xcf\x12\x00\x00\u07d4\x9d\x81\xae\xa6\x9a\xedj\xd0p\x89\xd6\x14E4\x8c\x17\xf3K\xfc[\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9d\x91\x1f6\x82\xf3/\xe0y.\x9f\xb6\xff<\xfcG\xf5\x89\xfc\xa5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\x91;]3\x9c\x95\xd8wEV%c\xfe\xa9\x8b#\xc6\f\u0109\tA0,\u007fM#\x00\x00\u07d4\x9d\x93\xfa\xb6\xe2(E\xf8\xf4Z\aIo\x11\xdeqS\r\xeb\u01c9lO\xd1\xee$nx\x00\x00\u07d4\x9d\x99\xb1\x89\xbb\u0664\x8f\xc2\xe1n\x8f\u0363;\xb9\x9a1{\xbb\x89=\x16\xe1\vm\x8b\xb2\x00\x00\u07d4\x9d\x9cN\xfe\x9fC9\x89\xe2;\xe9@I!S)\xfaU\xb4\u02c9\r\u3c89\x03\u01b5\x80\x00\u07d4\x9d\x9eW\xfd\xe3\x0ePh\xc0>I\x84\x8e\xdc\xe3C\xb7\x02\x83X\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x9d\xa30\"@\xaf\x05\x11\xc6\xfd\x18W\xe6\u07779Ow\xabk\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\x9d\xa4\xec@pw\xf4\xb9p{-\x9d.\xde^\xa5(+\xf1\u07c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\xa6\t\xfa:~l\xf2\xcc\x0ep\u036b\xe7\x8d\xc4\xe3\x82\xe1\x1e\x89A\rXj \xa4\xc0\x00\x00\xe0\x94\x9d\xa6\x1c\xcdb\xbf\x86\x06V\xe02]qW\xe2\xf1`\xd9;\xb5\x8a\x01\x0f\f\xa9V\xf8y\x9e\x00\x00\xe0\x94\x9d\xa6\xe0u\x98\x9ct\x19\tL\xc9\xf6\xd2\u44d3\xbb\x19\x96\x88\x8a\x02Y\xbbq\u056d\xf3\xf0\x00\x00\u07d4\x9d\xa8\xe2,\xa1\x0eg\xfe\xa4NR^GQ\xee\xac6\xa3\x11\x94\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\x9d\xb2\xe1\\\xa6\x81\xf4\xc6`H\xf6\xf9\xb7\x94\x1e\u040b\x1f\xf5\x06\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\xc1\x0f\xa3\x8f\x9f\xb0h\x10\xe1\x1f`\x17>\xc3\xd2\xfdju\x1e\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x9d\xd2\x19f$\xa1\xdd\xf1J\x9d7^_\a\x15+\xaf\"\xaf\xa2\x89A\xb0^$c\xa5C\x80\x00\u07d4\x9d\xd4k\x1cm?\x05\u279co\x03~\xed\x9aYZ\xf4\xa9\xaa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9d\xdd5^cN\xe9\x92~K\u007fl\x97\xe7\xbf:/\x1ehz\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x9d\xe2\n\xe7j\xa0\x82c\xb2\x05\xd5\x14$a\x96\x1e$\b\xd2f\x89\r\xa93\xd8\xd8\xc6p\x00\x00\u07d4\x9d\xe2\v\xc3~\u007fH\xa8\x0f\xfdz\xd8O\xfb\xf1\xa1\xab\xe1s\x8c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9d\xe78m\xde@\x1c\xe4\xc6{q\xb6U?\x8a\xa3N\xa5\xa1}\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x9d\xeb9\x02z\xf8w\x99+\x89\xf2\xecJ\x1f\x82.\xcd\xf1&\x93\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9d\xef\xe5j\x0f\xf1\xa1\x94}\xba\t#\xf7\xdd%\x8d\x8f\x12\xfaE\x8a\x05\xb1*\ufbe8\x04\x00\x00\x00\u07d4\x9d\xf0W\xcd\x03\xa4\xe2~\x8e\x03/\x85y\x85\xfd\u007f\x01\xad\xc8\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9d\xf3*P\x1c\vx\x1c\x02\x81\x02/B\xa1)?\xfd{\x89*\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4\x9e\x01vZ\xff\b\xbc\"\x05P\xac\xa5\xea.\x1c\xe8\u5c19#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9e \xe5\xfd6\x1e\xab\xcfc\x89\x1f[\x87\xb0\x92h\xb8\xeb7\x93\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9e#,\b\xc1M\xc1\xa6\xed\v\x8a;(h\x97{\xa5\xc1}\x10\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e#\xc5\u4dc2\xb0\n_\xad\U0006eb47\xda\xcf[\x03g\xa1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e59\x90q\xa4\xa1\x01\xe9\x19M\xaa?\t\xf0J\v_\x98p\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9e>\xb5\t'\x8f\xe0\xdc\xd8\xe0\xbb\xe7\x8a\x19N\x06\xb6\x809C\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\x9eBrrQk>g\xd4\xfc\xbf\x82\xf5\x93\x90\xd0L\x8e(\xe5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x9eL\xec5:\xc3\u3043^<\t\x91\xf8\xfa\xa5\xb7\u0428\xe6\x8a\x02\x1e\x18\xb9\xe9\xabE\xe4\x80\x00\u07d4\x9eX\x11\xb4\v\xe1\xe2\xa1\xe1\u048c;\at\xac\xde\n\t`=\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x9eZ1\x1d\x9fi\x89\x8a|j\x9dc`h\x048\xe6z{/\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x9e| P\xa2'\xbb\xfd`\x93~&\x8c\xea>h\xfe\xa8\xd1\xfe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9e\u007fe\xa9\x0e\x85\b\x86{\xcc\xc9\x14%j\x1e\xa5t\xcf\a\xe3\x89C8t\xf62\xcc`\x00\x00\xe0\x94\x9e\x81D\xe0\x8e\x89dx\x11\xfekr\xd4E\u05a5\xf8\n\xd2D\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9e\x8fd\xdd\xcd\u9e34Q\xba\xfa\xa25\xa9\xbfQ\x1a%\xac\x91\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\x9e\x95\x1fm\xc5\xe3R\xaf\xb8\xd0B\x99\xd2G\x8aE\x12Y\xbfV\x89\x03\xe7A\x98\x81\xa7:\x00\x00\u07d4\x9e\x96\r\xcd\x03\u057a\x99\xcb\x11]\x17\xffL\t$\x8a\xd4\u043e\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9e\xafj2\x8a@v\x02N\xfakg\xb4\x8b!\xee\xdc\xc0\xf0\xb8\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x9e\xb1\xffqy\x8f(\xd6\xe9\x89\xfa\x1e\xa0X\x8e'\xba\x86\xcb}\x89\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\x9e\xb2\x81\xc3'\x19\xc4\x0f\xdb>!m\xb0\xf3\u007f\xbcs\xa0&\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e\xb3\xa7\xcb^g&Bz:6\x1c\xfa\x8dad\xdb\u043a\x16\x89+\x95\xbd\xcc9\xb6\x10\x00\x00\u07d4\x9e\xb7\x83N\x17\x1dA\xe0i\xa7yG\xfc\xa8v\"\xf0\xbaNH\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x9e\xc0>\x02\u51f7v\x9d\xefS\x84\x13\xe9\u007f~U\xbeq\u060a\x04+\xf0kx\xed;P\x00\x00\u07d4\x9e\u02eb\xb0\xb2'\x82\xb3uD)\xe1uz\xab\xa0K\x81\x18\x9f\x89,\xa7\xbb\x06\x1f^\x99\x80\x00\u07d4\x9e\xce\x14\x00\x80\t6\xc7\xc6H_\xcd\xd3b`\x17\u041a\xfb\xf6\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4\x9e\xd4\xe6?ReB\xd4O\xdd\xd3MY\xcd%8\x8f\xfdk\u0689\u049b4\xa4cH\x94\x00\x00\u07d4\x9e\xd8\x0e\xda\u007fU\x05M\xb9\xfbR\x82E\x16\x88\xf2k\xb3t\xc1\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9e\u0710\xf4\xbe!\be!J\xb5\xb3^Z\x8d\xd7t\x15'\x9d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9e\u07acL\x02k\x93\x05M\u0171\xd6a\fo9`\xf2\xads\x89A\rXj \xa4\xc0\x00\x00\u07d4\x9e\xe9?3\x9eg&\xece\xee\xa4O\x8aK\xfe\x10\xda=2\x82\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9e\xe9v\f\xc2s\xd4pj\xa0\x83u\xc3\xe4o\xa20\xaf\xf3\u054a\x01\xe5.3l\xde\"\x18\x00\x00\u07d4\x9e\xeb\a\xbd+x\x90\x19^}F\xbd\xf2\a\x1bf\x17QM\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x9e\xefD-)\x1aD}t\xc5\xd2S\u011e\xf3$\xea\xc1\xd8\xf0\x89\xb9f\b\xc8\x10;\xf0\x00\x00\u07d4\x9e\xf1\x89k\x00|2\xa1Q\x14\xfb\x89\xd7=\xbdG\xf9\x12+i\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9f\x01w\x06\xb80\xfb\x9c0\ufc20\x9fPk\x91WEu4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x10\xf2\xa0F;e\xae0\xb0p\xb3\xdf\x18\xcfF\xf5\x1e\x89\xbd\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x9f\x19\xfa\u0223$7\xd8\n\u0183z\v\xb7\x84\x17)\xf4\x97.\x89#=\xf3)\x9far\x00\x00\u07d4\x9f\x1a\xa8\xfc\xfc\x89\xa1\xa52\x8c\xbdcD\xb7\x1f'\x8a,\xa4\xa0\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9f!0,\xa5\tk\xeat\x02\xb9\x1b\x0f\xd5\x06%O\x99\x9a=\x89C\x97E\x1a\x00=\xd8\x00\x00\u07d4\x9f'\x1d(U\x00\xd78F\xb1\x8fs>%\u074bO]J\x8b\x89'#\xc3F\xae\x18\b\x00\x00\u07d4\x9f4\x97\xf5\xef_\xe60\x95\x83l\x00N\xb9\xce\x02\xe9\x01;K\x89\"V\x86\x1b\xf9\xcf\b\x00\x00\xe0\x94\x9f:t\xfd^~\xdc\xc1\x16)\x93\x17\x13\x81\u02f62\xb7\xcf\xf0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9fF\xe7\xc1\xe9\a\x8c\xae\x860Z\xc7\x06\v\x01F}f\x85\xee\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\x9fIl\xb2\x06\x95c\x14M\b\x11g{\xa0\xe4q:\nAC\x89<\xd2\xe0\xbfc\xa4H\x00\x00\u07d4\x9fJq\x95\xac|\x15\x1c\xa2X\xca\xfd\xa0\u02b0\x83\xe0I\xc6\x02\x89SS\x8c2\x18\\\xee\x00\x00\u07d4\x9fJ\xc9\xc9\xe7\xe2L\xb2DJ\x04T\xfa[\x9a\xd9\xd9-8S\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4\x9f_D\x02kWjJ\xdbA\xe9YaV\x1dA\x03\x9c\xa3\x91\x89\r\x8drkqw\xa8\x00\x00\u07d4\x9f`{?\x12F\x9fDa!\u03bf4u5kq\xb42\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9fa\xbe\xb4o^\x85=\n\x85!\xc7Dnh\xe3L}\ts\x89\x1e[\x8f\xa8\xfe*\xc0\x00\x00\u07d4\x9fd\xa8\xe8\xda\xcfJ\xde0\xd1\x0fMY\xb0\xa3\u056b\xfd\xbft\x8966\x9e\xd7t}&\x00\x00\u07d4\x9ff.\x95'A!\xf1wVncm#\x96L\xf1\xfdho\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9fj2*mF\x99\x81Bj\xe8D\x86]~\xe0\xbb\x15\u01f3\x89\x02\xb5\xeeW\x92\x9f\u06c0\x00\u07d4\x9fy\x86\x92J\xeb\x02h|\xd6A\x89\x18\x9f\xb1g\xde\xd2\xdd\\\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x9fz\x03\x92\xf8Ws.0\x04\xa3u\xe6\xb1\x06\x8dI\xd801\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x82E\u00eb}\x171d\x86\x1c\u04d9\x1b\x94\xf1\xba@\xa9:\x89\x9b\ny\x1f\x12\x110\x00\x00\u07d4\x9f\x83\xa2\x93\xc3$\xd4\x10l\x18\xfa\xa8\x88\x8fd\u0499\x05L\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9f\x86\xa0f\xed\xb6\x1f\xcbXV\u0793\xb7\\\x8cy\x18d\xb9{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x98\xeb4\xd4iy\xb0\xa6\u078b\x05\xaaS:\x89\xb8%\xdc\xf1\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\xe0\x94\x9f\x9f\xe0\xc9_\x10\xfe\xe8z\xf1\xaf r6\xc8\xf3aN\xf0/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9f\xae\xa1\xc5\xe8\x1ez\xcb?\x17\xf1\xc3Q\xee.\u0649\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa0\b\x01\x98c\xc1\xa7|\x14\x99\xeb9\xbb\u05ff-\u05e3\x1c\xb9\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xa0\t\xbf\ao\x1b\xa3\xfaW\u04a7!r\x18\xbe\xd5VZzz\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa0\x1e\x94v\u07c4C\x18%\xc86\xe8\x80:\x97\xe2/\xa5\xa0\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\xa0\x1f\x12\xd7\x0fD\xaa{\x11;(\\\"\xdc\xdbE\x874T\xa7\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xa0\x1f\u0450j\x90\x85\x06\xde\xda\xe1\xe2\b\x12\x88r\xb5n\u7489\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa0\"\x82@\xf9\x9e\x1d\xe9\xcb2\xd8,\x0f/\xa9\xa3\xd4K\v\xf3\x89V\xbcu\xe2\xd61\x00\x00\x00\xe0\x94\xa0+\xdedahn\x19\xace\f\x97\r\x06r\xe7m\xcbO\u008a\x01\xe0\x92\x96\xc37\x8d\xe4\x00\x00\u07d4\xa0,\x1e4\x06O\x04u\xf7\xfa\x83\x1c\xcb%\x01L:\xa3\x1c\xa2\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xa0-\u01aa2\x8b\x88\r\u97acTh#\xfc\xcfw@G\xfb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa0.?\x8fYY\xa7\xaa\xb7A\x86\x12\x12\x9bp\x1c\xa1\xb8\x00\x10\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa04\u007f\n\x98wc\x90\x16\\\x16m2\x96;\xf7M\xcd\n/\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa05\xa3e$x\xf8-\xbdm\x11_\xaa\x8c\xa9F\xec\x9eh\x1d\x89\x05\xf4\xe4-\u052f\xec\x00\x00\u07d4\xa0:=\xc7\xc53\xd1tB\x95\xbe\x95]a\xaf?R\xb5\x1a\xf5\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa0E\x9e\xf3i:\xac\xd1d|\xd5\u0612\x989 L\xefS\xbe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa0O*\xe0*\xdd\x14\xc1/\xafe\xcb%\x90\"\u0403\n\x8e&\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xa0l\xd1\xf3\x969l\ndFFQ\xd7\xc2\x05\xef\xaf8|\xa3\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xa0ri\x1c\x8d\xd7\xcdB7\xffr\xa7\\\x1a\x95\x06\xd0\xce[\x9e\x89\x14\x0e\xc8\x0f\xa7\xee\x88\x00\x00\u07d4\xa0r\u03beb\xa9\xe9\xf6\x1c\xc3\xfb\xf8\x8a\x9e\xfb\xfe>\x9a\x8dp\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa0v\x82\x00\v\x1b\xcf0\x02\xf8\\\x80\xc0\xfa)I\xbd\x1e\x82\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa0z\xa1mt\xae\u8a63(\x8dR\xdb\x15Q\u0553\x882\x97\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa0\x8d![[j\xacHa\xa2\x81\xac~@\vx\xfe\xf0L\xbf\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa0\x95\x19p\xdf\u0403/\xb8;\xda\x12\xc25E\xe7\x90Aul\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa0\x9fM^\xaae\xa2\xf4\xcbu\nI\x924\x01\xda\u5410\xaf\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\xe0\x94\xa0\xa0\xe6R\x04T\x1f\u029b/\xb2\x82\u0355\x13\x8f\xae\x16\xf8\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa0\xaa_\x02\x01\xf0M;\xbe\xb8\x98\x13/|\x11g\x94f\xd9\x01\x89\x01\xfb\xedR\x15\xbbL\x00\x00\u07d4\xa0\xaa\xdb\xd9P\x97\"p_m#X\xa5\u01df7\x97\x0f\x00\xf6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa0\xb7q\x95\x1c\xe1\xde\xee6:\xe2\xb7q\xb7>\a\u0135\xe8\x00\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa0\xde\\`\x1eif5\u0198\xb7\xae\x9c\xa4S\x9f\u01f9A\xec\x89\x12\xc3\xcb\xd7\x04\xc9w\x00\x00\u07d4\xa0\xe8\xbaf\x1bH\x15L\xf8C\xd4\u00a5\xc0\xf7\x92\xd5(\xee)\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa0\xfc~S\xc5\xeb\xd2z*\xbd\xacE&\x1f\x84\xab;Q\xae\xfb\x89\xa3\x13\xda\xec\x9b\xc0\xd9\x00\x00\xe0\x94\xa0\xff[L\xf0\x16\x02~\x83#I}D(\xd3\xe5\xa8;\x87\x95\x8a\x01e\x98\xd3\xc8>\xc0B\x00\x00\u07d4\xa1\x06F[\xbd\x19\u1dbc\xe5\r\x1b\x11W\xdcY\tZ60\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa1\x06\xe6\x92>\xddS\u028e\xd6P\x96\x8a\x91\b\xd6\xcc\xfd\x96p\x8a\x02\x02\xfe\x15\x05\xaf\uc240\x00\u07d4\xa1\t\u12f0\xa3\x9c\x9e\xf8/\xa1\x95\x97\xfc^\xd8\xe9\xebmX\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\xa1\x1a\x03\u013b&\xd2\x1e\xffg}]U\\\x80\xb2TS\xeez\x89\x03\xcb'Y\xbcA\x0f\x80\x00\u07d4\xa1\x1e\xff\xabl\xf0\xf5\x97,\xff\xe4\xd5e\x96\xe9\x89h\x14J\x8f\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xa1 M\xad_V\a(\xa3\\\r\x8f\u01d4\x81\x05{\xf7s\x86\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa1&#\xe6)\u07d3\tg\x04\xb1`\x84\xbe,\u061dV-\xa4\x8a\x01\xcc\xc92E\x11\xe4P\x00\x00\xe0\x94\xa1*l-\x98]\xaf\x0eO_ z\xe8Q\xaa\xf7)\xb32\u034a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xa13m\xfb\x96\xb6\xbc\xbeK>\xdf2\x05\xbeW#\xc9\x0f\xadR\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa1;\x9d\x82\xa9\x9b<\x9b\xbaZ\xe7.\xf2\x19\x9e\xdc};\xb3l\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xa1<\xfe\x82mm\x18A\u072eD;\xe8\u00c7Q\x816\xb5\xe8\x8a\x1d\xa5jK\b5\xbf\x80\x00\x00\xe0\x94\xa1C.\xd2\u01b7wz\x88\xe8\xd4m8\x8epG\u007f \x8c\xa5\x8a\x01\xb1\xa7\xe4\x13\xa1\x96\xc5\x00\x00\u07d4\xa1D\xf6\xb6\x0fr\xd6J!\xe30\xda\xdbb\u0619\n\xde+\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa1P%\xf5\x95\xac\xdb\xf3\x11\x0fw\u017f$G~eH\xf9\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1X\x14\x8a.\x0f>\x92\xdc,\xe3\x8f\xeb\xc2\x01\a\xe3%<\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1a`\x85\x1d+\x9c4\x9b\x92\xe4o\x82\x9a\xbf\xb2\x10\x945\x95\x89a\t=|,m8\x00\x00\u07d4\xa1f\xf9\x11\xc6D\xac2\x13\u049e\x0e\x1a\xe0\x10\xf7\x94\u056d&\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1m\x9e=c\x98aY\xa8\x00\xb4h7\xf4^\x8b\xb9\x80\xee\v\x89n\x11u\xdaz\xd1 \x00\x00\u07d4\xa1pp\xc2\xe9\u0169@\xa4\xec\x0eIT\xc4\xd7\xd6C\xbe\x8fI\x89lk\x17\x03;6\x1c\x80\x00\u07d4\xa1|\x9eC#\x06\x95\x18\x18\x9dR\a\xa0r\x8d\u02d20j?\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa1\x83`\xe9\x85\xf2\x06.\x8f\x8e\xfe\x02\xad,\xbc\x91\xad\x9aZ\xad\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa1\x91\x14\x05\xcfn\x99\x9e\xd0\x11\xf0\xdd\xcd*O\xf7\u008f%&\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa1\x92i\x80\a\xcc\x11\xaa`=\"\x1d_\xee\xa0v\xbc\xf7\xc3\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\x92\xf0j\xb0R\xd5\xfd\u007f\x94\xee\xa81\x8e\x82x\x15\xfegz\x89\a\x1f\x8a\x93\xd0\x1eT\x00\x00\u07d4\xa1\x99\x81D\x96\x8a\\p\xa6AUT\xce\xfe\u0082F\x90\u0125\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa1\xa1\xf0\xfam \xb5\nyO\x02\xefR\b\\\x9d\x03j\xa6\u028965\u026d\xc5\u07a0\x00\x00\u07d4\xa1\xae\x8dE@\xd4\xdbo\xdd\xe7\x14oA[C\x1e\xb5\\y\x83\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xa1\xb4|M\x0e\xd6\x01\x88B\xe6\xcf\xc8c\n\u00e3\x14.^k\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xa1\xc4\xf4Z\x82\xe1\xc4x\xd8E\b.\xb1\x88u\xc4\xeae9\xab\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xa1\xdc\xd0\xe5\xb0Z\x97|\x96#\xe5\xae/Y\xb9\xad\xa2\xf3>1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa1\xe48\n;\x1ft\x96s\xe2p\"\x99\x93\xeeU\xf3Vc\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\xf1\x93\xa0Y/\x1f\xeb\x9f\xdf\xc9\n\xa8\x13xN\xb8\x04q\u0249K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa1\xf2\x85@P\xf8re\x8e\xd8.R\xb0\xad{\xbc\x1c\xb9!\xf6\x89m\x03\x17\xe2\xb3&\xf7\x00\x00\u07d4\xa1\xf5\xb8@\x14\rZ\x9a\xce\xf4\x02\xac<\u00c8jh\xca\xd2H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\xf7e\xc4O\xe4_y\x06w\x94HD\xbeO-B\x16_\xbd\x89\xc7\xe9\xcf\xdev\x8e\xc7\x00\x00\u07d4\xa1\xf7\xdd\xe1\xd78\xd8\xcdg\x9e\xa1\xee\x96[\xee\"K\xe7\xd0M\x89=\x18DP\xe5\xe9<\x00\x00\u07d4\xa1\xf8\u063c\xf9\x0ew\u007f\x19\xb3\xa6Iu\x9a\xd9P'\xab\xdf\u00c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\x02TrB\x80onp\xe7@X\xd6\xe5)-\xef\xc8\xc8\u0509l\x87T\xc8\xf3\f\b\x00\x00\u07d4\xa2\r\a\x1b\x1b\x000cI}y\x90\xe1$\x9d\xab\xf3l5\xf7\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa2\r\x8f\xf6\f\xaa\xe3\x1d\x02\xe0\xb6e\xfaC]v\xf7|\x94B\x89\x1a\x8a\x90\x9d\xfc\xef@\x00\x00\u07d4\xa2\x11\xda\x03\xcc\x0e1\xec\xceS\t\x99\x87\x18QU(\xa0\x90\u07c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\x14B\xab\x054\n\xdeh\xc9\x15\xf3\xc39\x9b\x99U\xf3\xf7\xeb\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xa2\"\"Y\u075c>=\xed\x12p\x84\xf8\b\xe9*\x18\x870,\x89\b\xc83\x9d\xaf\xedH\x00\x00\u07d4\xa2*\xde\r\xdb\\n\xf8\xd0\u034d\xe9M\x82\xb1\x10\x82\xcb.\x91\x897KW\xf3\xce\xf2p\x00\x00\u07d4\xa2L:\xb6!\x81\xe9\xa1[x\xc4b\x1eL|X\x81'\xbe&\x89\b\xcd\xe4:\x83\xd31\x00\x00\u07d4\xa2W\xadYK\u0603(\xa7\xd9\x0f\xc0\xa9\a\u07d5\xee\xca\xe3\x16\x89\x1c7\x86\xff8F\x93\x00\x00\u07d4\xa2[\bd7\xfd!\x92\u0420\xf6On\xd0D\xf3\x8e\xf3\xda2\x89\x12)\x0f\x15\x18\v\xdc\x00\x00\u07d4\xa2v\xb0X\u02d8\u060b\xee\xdbg\xe5CPl\x9a\r\x94p\u0609\x90\xaa\xfcv\xe0/\xbe\x00\x00\u07d4\xa2\x82\xe9i\xca\xc9\xf7\xa0\xe1\xc0\u0350\xf5\xd0\xc48\xacW\r\xa3\x89\"\a\xeb\x89\xfc'8\x00\x00\xe0\x94\xa2\x91\xe9\u01d9\rU-\u046e\x16\u03bc?\xca4,\xba\xf1\u044a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa2\x93\x19\xe8\x10i\xe5\xd6\r\xf0\x0f=\xe5\xad\xee5\x05\xec\xd5\xfb\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa2\x96\x8f\xc1\xc6K\xac\vz\xe0\u058b\xa9I\x87Mm\xb2S\xf4\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xa2\x9d[\xdat\xe0\x03GHr\xbdX\x94\xb8\x853\xffd\u00b5\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa2\x9df\x1acv\xf6m\vt\xe2\xfe\x9d\x8f&\xc0$~\xc8L\x89\xdf3\x04\a\x9c\x13\xd2\x00\x00\u07d4\xa2\xa45\xdeD\xa0\x1b\xd0\ucc9eD\xe4vD\xe4j\f\xdf\xfb\x89\x1b\x1dDZz\xff\xe7\x80\x00\u07d4\xa2\xac\xe4\u0253\xbb\x1eS\x83\xf8\xact\xe1y\x06n\x81O\x05\x91\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa2\xb7\x01\xf9\xf5\xcd\u041eK\xa6+\xae\xba\u3a02W\x10X\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa2\u0145O\xf1Y\x9f\x98\x89,W%\xd2b\xbe\x1d\xa9\x8a\xad\xac\x89\x11\t\xff30\x10\xe7\x80\x00\u07d4\xa2\xc7\xea\xff\xdc,\x9d\x93sE l\x90\x9aR\u07f1LG\x8f\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\xa2\u04aabk\t\xd6\xd4\xe4\xb1?\u007f\xfcZ\x88\xbdz\xd3gB\x89\xfb\x80xPuS\x83\x00\x00\u07d4\xa2\u04cd\xe1\xc79\x06\xf6\xa7\xcan\xfe\xb9|\xf6\xf6\x9c\xc4!\xbe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa2\xdce\xee%kY\xa5\xbdy)wO\x90K5\x8d\U000ed84a\x04\x83\xbc\xe2\x8b\xeb\t\xf8\x00\x00\u07d4\xa2\xe0h:\x80]\xe6\xa0^\xdb/\xfb\xb5\xe9o\x05p\xb67\u00c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa2\u1e2a\x90\x0e\x9c\x13\x9b?\xa1\"5OaV\xd9*\x18\xb1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa2\u2d54\x1e\f\x01\x94K\xfe\x1d_\xb4\xe8\xa3K\x92,\u03f1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\xe4`\xa9\x89\xcb\x15V_\x9e\u0327\xd1!\xa1\x8eN\xb4\x05\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa2\xec\xce,I\xf7*\t\x95\xa0\xbd\xa5z\xac\xf1\xe9\xf0\x01\xe2*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xa2\xf4r\xfeO\"\xb7}\xb4\x89!\x9e\xa4\x02=\x11X*\x93)\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xa2\xf7\x98\xe0w\xb0}\x86\x12N\x14\a\xdf2\x89\r\xbbKcy\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\xf8k\xc0a\x88N\x9e\xef\x05d\x0e\xddQ\xa2\xf7\xc0Yli\x89llD\xfeG\xec\x05\x00\x00\u07d4\xa2\xfa\x17\xc0\xfbPl\xe4\x94\x00\x8b\x95W\x84\x1c?d\x1b\x8c\xae\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa3\x04X\x8f\r\x85\f\xd8\u04cfv\xe9\xe8<\x1b\xf6>3>\u0789\x02(V\x01!l\x8c\x00\x00\u07d4\xa3\x05\x8cQszN\x96\xc5_.\xf6\xbd{\xb3X\x16~\u00a7\x89 \xdb:\xe4H\x1a\u0500\x00\u07d4\xa3\t\xdfT\u02bc\xe7\f\x95\xec03\x14\x9c\xd6g\x8ao\xd4\u03c9\f\x1f\x12\xc7Q\x01X\x00\x00\u07d4\xa3\nER\x0eR\x06\xd9\x00@p\xe6\xaf>{\xb2\xe8\xddS\x13\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa3\x0e\n\xcbSL\x9b0\x84\xe8P\x1d\xa0\x90\xb4\xeb\x16\xa2\xc0\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3 0\x95\xed\xb7\x02\x8ehq\xce\n\x84\xf5HE\x9f\x830\n\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa3!\t\x1d0\x18\x06By\xdb9\x9d+*\x88\xa6\xf4@\xae$\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4\xa3#-\x06\x8dP\x06I\x03\xc9\xeb\xc5c\xb5\x15\xac\u0237\xb0\x97\x89l\x87T\xc8\xf3\f\b\x00\x00\xe0\x94\xa3$\x1d\x89\n\x92\xba\xf5)\b\xdcJ\xa0Irk\xe4&\xeb\u04ca\x04<-\xa6a\xca/T\x00\x00\u07d4\xa3)F&\xec)\x84\xc4;C\xdaM]\x8eFi\xb1\x1dKY\x896\xa4\xcfcc\x19\xc0\x00\x00\u07d4\xa3,\xf7\xdd\xe2\f=\xd5g\x9f\xf5\xe3%\x84\\p\u0156&b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa39\xa3\xd8\xca(\x0e'\xd2A[&\xd1\xfcy2(\xb6`C\x896\U00086577\x8f\xf0\x00\x00\u07d4\xa3<\xb4P\xf9[\xb4n%\xaf\xb5\x0f\xe0_\xee\xe6\xfb\x8c\xc8\xea\x89*\x11)\u0413g \x00\x00\u07d4\xa3?p\xdaru\xef\x05q\x04\u07e7\xdbd\xf4r\xe9\xf5\xd5S\x89\x04YF\xb0\xf9\xe9\xd6\x00\x00\u07d4\xa3@v\xf8K\xd9\x17\xf2\x0f\x83B\u024b\xa7\x9eo\xb0\x8e\xcd1\x89\u3bb5sr@\xa0\x00\x00\u07d4\xa3C\x0e\x1fd\u007f2\x1e\xd3G9V##\xc7\xd6#A\vV\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xa3O\x9dV\x8b\xf7\xaf\xd9L*[\x8a_\xf5\\f\xc4\by\x99\x89\x84}P;\"\x0e\xb0\x00\x00\u07d4\xa3V\x06\xd5\x12 \xee\u007f!F\xd4\x11X.\xe4\xeeJEYn\x89\u062a\xbe\b\v\xc9@\x00\x00\u07d4\xa3VU\x1b\xb7}OE\xa6\xd7\xe0\x9f\n\b\x9ey\u0322I\u02c9\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xa3\\\x19\x13,\xac\x195Wj\xbf\xedl\x04\x95\xfb\a\x88\x1b\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3e\x91\x8b\xfe?&'\xb9\xf3\xa8gu\xd8un\x0f\u0629K\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa3n\r\x94\xb9Sd\xa8&q\xb6\b\xcb-72Ea)\t\x89\b!\xd2!\xb5)\x1f\x80\x00\u07d4\xa3u\xb4\xbc$\xa2N\x1fyu\x93\xcc0+/3\x10c\xfa\\\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa3v\"\xac\x9b\xbd\xc4\xd8+u\x01]t[\x9f\x8d\xe6Z(\uc25d\xc0\\\xce(\u00b8\x00\x00\xe0\x94\xa3y\xa5\a\fP=/\xac\x89\xb8\xb3\xaf\xa0\x80\xfdE\xedK\xec\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xa3\x80-\x8ae\x9e\x89\xa2\xc4~\x90T0\xb2\xa8'\x97\x89P\xa7\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa3\x83\x06\xcbp\xba\xa8\u4446\xbdh\xaap\xa8=$/)\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\x84vi\x1d4\x94.\xeak/v\x88\x92#\x04}\xb4az\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\x87\xceN\x96\x1axG\xf5`\a\\d\xe1YkVA\xd2\x1c\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xa3\x87\xec\xde\x0e\xe4\xc8\a\x94\x99\xfd\x8e\x03G;\u060a\xd7R*\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa3\x88:$\xf7\xf1f _\x1aj\x99I\al&\xa7nqx\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xa3\x8b[\xd8\x1a\x9d\xb9\u04b2\x1d^\xc7\xc6\x05R\xcd\x02\xedV\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa3\x90\xca\x12+\x85\x01\xee>^\a\xa8\xcaKA\x9f~M\xae\x15\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xa3\x93*1\xd6\xffu\xfb;\x12q\xac\xe7\u02a7\xd5\xe1\xff\x10Q\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xa3\x94\xadO\xd9\xe6S\x0eo\\S\xfa\xec\xbe\u0781\xcb\x17-\xa1\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xa3\x97\x9a\x92v\n\x13Z\xdfi\xd7/u\xe1gu_\x1c\xb8\u00c9\x05k\xc7^-c\x10\x00\x00\xe0\x94\xa3\x9b\xfe\xe4\xae\u027du\xbd\"\u01b6r\x89\x8c\xa9\xa1\xe9]2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa3\xa2b\xaf\u0493h\x19#\b\x92\xfd\xe8O-ZYJ\xb2\x83\x89e\xea=\xb7UF`\x00\x00\u07d4\xa3\xa2\xe3\x19\xe7\u04e1D\x8bZ\xa2F\x89S\x16\f-\xbc\xbaq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\xa5{\a\x16\x13(\x04\xd6\n\xac(\x11\x97\xff+=#{\x01\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa3\xa9>\xf9\xdb\xea&6&=\x06\xd8I/jA\u0790|\"\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\xa3\xae\x18y\x00}\x80\x1c\xb5\xf3RqjM\u063a'!\xde=\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xa3\xba\r:6\x17\xb1\xe3\x1bNB,\xe2i\xe8s\x82\x8d]i\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\xa3\xbc\x97\x9bp\x80\t/\xa1\xf9/n\x0f\xb3G\u2359PE\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xa3\xbf\xf1\u07e9\x97\x16h6\f\r\x82\x82\x842\xe2{\xf5Ng\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa3\xc1J\xce(\xb1\x92\u02f0b\x14_\u02fdXi\xc6rq\xf6\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa3\xc3:\xfc\x8c\xb4pN#\x15=\xe2\x04\x9d5\xaeq3$r\x89+X\xad\u06c9\xa2X\x00\x00\u07d4\xa3\u0430<\xff\xbb&\x9fyj\u009d\x80\xbf\xb0}\xc7\u01ad\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\u0543\xa7\xb6[#\xf6\vy\x05\xf3\xe4\xaab\xaa\xc8\u007fB'\x898\xbe\xfa\x12mZ\x9f\x80\x00\u07d4\xa3\xdb6J3-\x88K\xa9;&\x17\xaeM\x85\xa1H\x9b\xeaG\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xa3\xe0Q\xfbtJ\xa3A\f;\x88\xf8\x99\xf5\xd5\u007f\x16\x8d\xf1-\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\xa3\xe3\xa6\xeaP\x95s\xe2\x1b\xd0#\x9e\xce\x05#\xa7\xb7\u061b/\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa3\xf4\xad\x14\xe0\xbbD\xe2\xce,\x145\x9cu\xb8\xe72\xd3pT\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa3\xfa\xccP\x19\\\vI3\xc8X\x97\xfe\xcc[\xbd\x99\\4\xb8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\x03Z\xb1\xe5\x18\b!\xf0\xf3\x80\xf1\x13\x1bs\x87\xc8\u0641\u0349\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\n\xa2\xbb\xce\fr\xb4\xd0\xdf\xff\xccBq[+T\xb0\x1b\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4\x19\xa9\x84\x14#c&uuV`\x894\x0e\xea\x0e\xa2\b\x19\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xa4!\u06f8\x9b:\aA\x90\x84\xad\x10\xc3\xc1]\xfe\x9b2\xd0\u008a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa4\"\xe4\xbf\v\xf7AG\u0309[\xed\x8f\x16\xd3\xce\xf3BaT\x89\x12\xef?b\xee\x116\x80\x00\u07d4\xa4%\x9f\x83E\xf7\u3a37+\x0f\xec,\xf7^2\x1f\xdaM\u0089g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xa4)\b\xe7\xfeS\x98\n\x9a\xbf@D\xe9W\xa5Kp\u973e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4)\xfa\x88s\x1f\xdd5\x0e\x8e\xcdn\xa5B\x96\xb6HO\u6549j\xc5\xc6-\x94\x86\a\x00\x00\xe0\x94\xa40\x99]\xdb\x18[\x98e\xdb\xe6%9\xad\x90\xd2.Ks\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa46\xc7TS\xcc\xcaJ\x1f\x1bb\xe5\u0123\r\x86\xdd\xe4\xbeh\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa47\xfen\xc1\x03\u028d\x15\x8fc\xb34\"N\u032c[>\xa3\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4;m\xa6\xcbz\xacW\x1d\xff'\xf0\x9d9\xf8F\xf57i\xb1\x89\x14\x99\x8f2\xacxp\x00\x00\u07d4\xa4;\x81\xf9\x93V\xc0\xaf\x14\x1a\x03\x01\rw\xbd\x04,q\xc1\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4>\x19G\xa9$+5Ua\xc3\n\x82\x9d\xfe\uc881Z\xf8\x89\xd2=\x99\x96\x9f\u0591\x80\x00\u07d4\xa4H\x9aP\xea\xd5\xd5DZ{\xeeM-U6\u00a7lA\xf8\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa4O\xe8\x00\xd9o\xca\xd7;qp\xd0\xf6\x10\u02cc\x06\x82\xd6\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa4T2\xa6\xf2\xac\x9dVW{\x93\x8a7\xfa\xba\xc8\xcc|F\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4f\xd7p\u0618\xd8\xc9\xd4\x05\xe4\xa0\xe5Q\xef\xaf\xcd\xe5<\xf9\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\xe0\x94\xa4g\a1\x17X\x93\xbb\xcf\xf4\xfa\x85\u0397\xd9O\xc5\x1cK\xa8\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4kC\x87\xfbM\xcc\xe0\x11\xe7nMsT}D\x81\xe0\x9b\xe5\x89Hz\x9a0E9D\x00\x00\u07d4\xa4l\xd27\xb6>\xeaC\x8c\x8e;e\x85\xf6y\xe4\x86\b2\xac\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4wy\u063c\x1c{\xce\x0f\x01\x1c\xcb9\xefh\xb8T\xf8\u078f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4\x82kl8\x82\xfa\xd0\xed\\\x8f\xbb%\xcc@\xccO3u\x9f\x89p\x1bC\xe3D3\xd0\x00\x00\u07d4\xa4\x87Y(E\x8e\xc2\x00]\xbbW\x8c\\\xd35\x80\xf0\xcf\x14R\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4\x9fR:\xa5\x13d\xcb\xc7\u0655\x16=4\xebY\r\xed/\b\x89\x90'B\x1b*\x9f\xbc\x00\x00\u07d4\xa4\xa4\x9f\v\xc8h\x8c\xc9\xe6\xdc\x04\xe1\xe0\x8dR\x10&\xe6Ut\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa4\xa7\xd3\x06\xf5\x10\xcdX5\x94(\xc0\xd2\xf7\xc3`\x9dVt\u05c9\xb5\x8c\xb6\x1c<\xcf4\x00\x00\u07d4\xa4\xa8:\a8y\x9b\x97\x1b\xf2\xdep\x8c.\xbf\x91\x1c\xa7\x9e\xb2\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa4\xb0\x9d\xe6\xe7\x13\xdciTnv\xef\n\xcf@\xb9O\x02A\xe6\x89\x11}\xc0b~\xc8p\x00\x00\xe0\x94\xa4\u04b4)\xf1\xadSI\xe3\x17\x04\x96\x9e\xdc_%\xee\x8a\xca\x10\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xa4\xd6\xc8.\u076eYG\xfb\xe9\xcd\xfb\xd5H\xae3\xd9\x1aq\x91\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4\xda4E\r\"\xec\x0f\xfc\xed\xe0\x00K\x02\xf7\x87.\xe0\xb7:\x89\x05\x0fafs\xf0\x83\x00\x00\xe0\x94\xa4\xddY\xab^Q}9\x8eI\xfaS\u007f\x89\x9f\xedL\x15\xe9]\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa4\xe6#E\x1e~\x94\xe7\u86e5\xed\x95\u0228:b\xff\xc4\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\xed\x11\xb0r\u061f\xb16u\x9f\u019bB\x8cH\xaa]L\xed\x89\x0e?\x15'\xa0<\xa8\x00\x00\u07d4\xa4\xfb\x14@\x9ag\xb4V\x88\xa8Y>\\\xc2\xcfYl\xedo\x11\x89a\t=|,m8\x00\x00\xe0\x94\xa5\x14\xd0\x0e\xddq\b\xa6\xbe\x83\x9ac\x8d\xb2AT\x18\x17A\x96\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x94\xa5\"\xde~\xb6\xae\x12PR*Q13\xa9;\xd4(IG\\\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa5$\xa8\xcc\xccIQ\x8d\x17\n2\x82p\xa2\xf8\x813\xfb\xaf]\x89\x0f\xf7\x02-\xac\x10\x8a\x00\x00\u07d4\xa59\xb4\xa4\x01\xb5\x84\xdf\xe0\xf3D\xb1\xb4\"\xc6UC\x16~.\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa5>\xadT\xf7\x85\n\xf2\x148\xcb\xe0z\xf6\x86'\x9a1[\x86\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa5C\xa0f\xfb2\xa8f\x8a\xa0sj\f\x9c\xd4\rx\t\x87'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa5gw\vj\xe3 \xbd\xdeP\xf9\x04\xd6c\xe7F\xa6\x1d\xac\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5h\xdbMW\xe4\xd6tb\xd73\u019a\x9e\x0f\xe2n!\x83'\x89;k\xff\x92f\xc0\xae\x00\x00\u07d4\xa5i\x8059\x1eg\xa4\x90\x13\xc0\x00 yY1\x14\xfe\xb3S\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4\xa5p\":\xe3\u02a8QA\x8a\x98C\xa1\xacU\xdbH$\xf4\xfd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa5s`\xf0\x02\xe0\xd6M-tE}\x8c\xa4\x85~\xe0\v\xcd\u07c9\x123\xe22\xf6\x18\xaa\x00\x00\u07d4\xa5u\xf2\x89\x1d\xcf\u0368<\\\xf0\x14t\xaf\x11\xee\x01\xb7-\u0089\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\xa5x;\xf342\xff\x82\xacI\x89\x85\xd7\xd4`\xaeg\xec6s\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xa5\x87MuF5\xa7b\xb3\x81\xa5\xc4\u01d2H:\xf8\xf2=\x1d\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\xa5\xa4\"\u007fl\xf9\x88%\xc0\u057a\xffS\x15u,\xcc\x1a\x13\x91\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa5\xabK\xd3X\x8fF\xcb'.V\xe9=\xee\u04c6\xba\x8bu=\x89HB\xf0A\x05\x87,\x80\x00\xe0\x94\xa5\xba\xd8e\t\xfb\xe0\xe0\xe3\xc0\xe9?m8\x1f\x1a\xf6\xe9\u0501\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa5\xc36\b;\x04\xf9G\x1b\x8cn\xd76y\xb7Mf\xc3c\uc263e\nL\x9d \xe2\x00\x00\u07d4\xa5\xcd\x129\x92\x19K4\xc4x\x13\x140;\x03\xc5IH\xf4\xb9\x89l\xfc\xc3\xd9\x1d\xa5c\x00\x00\u07d4\xa5\u0578\xb6-\x00-\xef\x92A7\x10\xd1;o\xf8\xd4\xfc}\u04c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xa5\xd9ni}F5\x8d\x11\x9a\xf7\x81\x9d\xc7\b\u007fj\xe4\u007f\xef\x8a\x03\x17\xbe\xe8\xaf3\x15\xa7\x80\x00\u07d4\xa5\xde^CO\xdc\xddh\x8f\x1c1\xb6\xfbQ,\xb1\x96rG\x01\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xa5\xe0\xfc<:\xff\xed=\xb6q\tG\xd1\xd6\xfb\x01\u007f>'m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5\xe9;I\xea|P\x9d\xe7\xc4Ml\xfe\xdd\xefY\x10\u07aa\xf2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5\xe9\xcdKt%]\"\xb7\u0672z\xe8\xddC\xedn\xd0%+\x89)\x8d\xb2\xf5D\x11\u0640\x00\xe0\x94\xa5\xf0\a{5\x1flP\\\xd5\x15\u07e6\xd2\xfa\u007f\\L\u0487\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xa5\xf0u\xfd@\x135W{f\x83\u0081\xe6\xd1\x01C-\xc6\xe0\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xa5\xfe,\xe9\u007f\x0e\x8c8V\xbe\r\xe5\xf4\u0732\xce]8\x9a\x16\x89\x01=\xb0\xb8\xb6\x86>\x00\x00\u07d4\xa5\xffb\"-\x80\xc0\x13\xce\xc1\xa0\xe8\x85\x0e\xd4\xd3T\xda\xc1m\x89\vA\a\\\x16\x8b\x18\x00\x00\u07d4\xa6\t\xc2m\xd3P\xc25\xe4K+\x9c\x1d\xdd\xcc\u0429\xd9\xf87\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa6\f\x12\tuO]\x87\xb1\x81\xdaO\b\x17\xa8\x18Y\xef\x9f\u0609\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\xa6\x10\x1c\x96\x1e\x8e\x1c\x15y\x8f\xfc\xd0\xe3 \x1dw\x86\xec7:\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xa6\x13Ei\x96@\x8a\xf1\xc2\xe9>\x17w\x88\xabU\x89^+2\x8a\x01Y\x19\xffG|\x88\xb8\x00\x00\u07d4\xa6\x18\x87\x81\x8f\x91J \xe3\x10w)\v\x83qZk-n\xf9\x89e\xea=\xb7UF`\x00\x00\u07d4\xa6\x1aT\xdfxJD\xd7\x1bw\x1b\x871u\t!\x13\x81\xf2\x00\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa6\x1c\u06ed\xf0K\x1eT\u0203\xde`\x05\xfc\xdf\x16\xbe\xb8\xeb/\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa69\xac\xd9k1\xbaS\xb0\u0407c\"\x9e\x1f\x06\xfd\x10^\x9d\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa6BP\x10\x04\xc9\x0e\xa9\xc9\xed\x19\x98\xba\x14\nL\xd6,o_\x89\r\x94\xfb\x8b\x10\xf8\xb1\x80\x00\u07d4\xa6D\xed\x92,\xc27\xa3\xe5\u0117\x9a\x99Tw\xf3nP\xbcb\x89\x1f\xa7=\x84]~\x96\x00\x00\u07d4\xa6F\xa9\\moY\xf1\x04\xc6T\x1dw`uz\xb3\x92\xb0\x8c\x89\u3bb5sr@\xa0\x00\x00\xe0\x94\xa6HL\u0184\xc4\xc9\x1d\xb5>\xb6\x8aM\xa4Zjk\xda0g\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa6N_\xfbpL,\x919\xd7~\xf6\x1d\x8c\u07e3\x1dz\x88\xe9\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\xe0\x94\xa6T&\xcf\xf3x\xed#%5\x13\xb1\x9fIm\xe4_\xa7\u13ca\x01\x86P\x12|\xc3\u0700\x00\x00\u07d4\xa6jIc\xb2\u007f\x1e\xe1\x93+\x17+\xe5\x96N\r:\xe5KQ\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4\xa6\u007f8\x81\x95eB:\xa8_>:\xb6\x1b\xc7c\u02eb\x89\u0749sw\xb0\"\u01be\b\x00\x00\u07d4\xa6\x8c14E\xc2-\x91\x9e\xe4l\xc2\xd0\xcd\xff\x04:uX%\x89\x04\x13t\xfd!\xb0\u0600\x00\u07d4\xa6\x8e\f0\u02e3\xbcZ\x88>T\x03 \xf9\x99\xc7\xcdU\x8e\\\x89a\x9237b\xa5\x8c\x80\x00\u07d4\xa6\x90\xf1\xa4\xb2\n\xb7\xba4b\x86 \u079c\xa0@\xc4<\x19c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa6\x9d|\xd1}HB\xfe\x03\xf6*\x90\xb2\xfb\xf8\xf6\xaf{\xb3\x80\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa6\xa0\x82R\xc8YQw\xcc.`\xfc'Y>#y\xc8\x1f\xb1\x89\x01\x16Q\xac>zu\x80\x00\u07d4\xa6\xa0\xdeB\x1a\xe5Om\x17(\x13\b\xf5dm/9\xf7w]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa6\xb2\xd5s)s`\x10,\a\xa1\x8f\xc2\x1d\xf2\xe7I\x9f\xf4\xeb\x89\xd9o\u0390\u03eb\xcc\x00\x00\xe0\x94\xa6\xc9\x10\xceMIJ\x91\x9c\u036a\xa1\xfc;\x82\xaat\xba\x06\u03ca\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa6\u3ea3\x8e\x10J\x1e'\xa4\xd8(i\xaf\xb1\xc0\xaen\xff\x8d\x89\x01\x11@\ueb4bq\x00\x00\u07d4\xa6\xee\xbb\xe4d\u04d1\x87\xbf\x80\u029c\x13\xd7 '\xec[\xa8\xbe\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa6\xf6+\x8a=\u007f\x11\"\a\x01\xab\x9f\xff\xfc\xb3'\x95\x9a'\x85\x89\x1bn)\x1f\x18\u06e8\x00\x00\u07d4\xa6\xf93\a\xf8\xbc\xe01\x95\xfe\u0387 C\xe8\xa0?{\xd1\x1a\x89\x9csK\xadQ\x11X\x00\x00\u07d4\xa7\x01\xdfy\xf5\x94\x90\x1a\xfe\x14DH^k \u00fd\xa2\xb9\xb3\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa7\x02L\xfdt,\x1e\xc1<\x01\xfe\xa1\x8d0B\xe6_\x1d]\xee\x8a\x02c\x11\x9a(\xab\u0430\x80\x00\u07d4\xa7\x18\xaa\xadY\xbf9\\\xba+#\xe0\x9b\x02\xfe\f\x89\x81bG\x8960<\x97\xe4hx\x00\x00\u07d4\xa7$|S\xd0Y\xeb|\x93\x10\xf6(\xd7\xfclj\nw?\b\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xa7%7c\xcfJu\u07d2\xca\x1evm\xc4\xee\x8a'E\x14{\x8a\x02F7p\xe9\n\x8fP\x00\x00\u07d4\xa7.\xe6f\u0133^\x82\xa5\x06\x80\x8bD<\xeb\xd5\xc62\xc7\u0749+^:\xf1k\x18\x80\x00\x00\u07d4\xa7DD\xf9\x0f\xbbT\xe5o:\u0276\xcf\u032aH\x19\xe4aJ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa7GC\x9a\xd0\u04d3\xb5\xa08a\xd7r\x962m\u8edd\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa7`{BW;\xb6\xf6\xb4\xd4\xf2<~*&\xb3\xa0\xf6\xb6\xf0\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\xa7i)\x89\n{G\xfb\x85\x91\x96\x01lo\u0742\x89\u03b7U\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u0794\xa7kt?\x98\x1bi0r\xa11\xb2+\xa5\x10\x96\\/\xef\u05c8\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xa7m?\x15bQ\xb7,\f\xcfKG\xa39<\xbdoI\xa9\u0149Hz\x9a0E9D\x00\x00\u07d4\xa7t(\xbc\xb2\xa0\xdbv\xfc\x8e\xf1\xe2\x0eF\x1a\n2\u016c\x15\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xa7u\x8c\xec\xb6\x0e\x8faL\u0396\x13~\xf7+O\xbd\awJ\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa7w^J\xf6\xa2:\xfa \x1f\xb7\x8b\x91^Q\xa5\x15\xb7\xa7(\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\xa7\u007f>\u1793\x88\xbb\xbb\"\x15\xc6#\x97\xb9e`\x13#`\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa7\x85\x9f\xc0\u007fun\xa7\xdc\xeb\xbc\xcdB\xf0X\x17X-\x97?\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa7\x96lH\x9fLt\x8az\u902a'\xa5t%\x17g\xca\xf9\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa7\xa3\xbba9\xb0\xad\xa0\f\x1f\u007f\x1f\x9fV\u0654\xbaM\x1f\xa8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa7\xa3\xf1S\xcd\u00c8!\xc2\f]\x8c\x82A\xb2\x94\xa3\xf8+$\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa7\xa5\x17\u05ed5\x82\v\t\u0517\xfa~U@\xcd\xe9IXS\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa7\xc9\u04c8\xeb\xd8s\xe6k\x17\x13D\x83\x97\xd0\xf3\u007f\x8b\u04e8\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa7\u073b\xa9\xb9\xbfgb\xc1EAlPjq\u3d17 \x9c\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xa7\xe7O\v\xdb'\x8f\xf0\xa8\x05\xa6Ha\x8e\xc5+\x16o\xf1\xbe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa7\xe87r\xbc \x0f\x90\x06\xaa*&\r\xba\xa8H=\xc5+0\x89\vB\xd56f7\xe5\x00\x00\xe0\x94\xa7\xef5\u0387\xed\xa6\u008d\xf2HxX\x15\x05>\xc9zPE\x8a\x01\x0f\f\xe9I\xe0\x0f\x93\x00\x00\u07d4\xa7\xf9\"\f\x80G\x82k\xd5\xd5\x18?Ngjmw\xbf\xed6\x89\bPh\x97k\xe8\x1c\x00\x00\u07d4\xa8\a\x10O'\x03\xd6y\xf8\u07af\xc4B\xbe\xfe\x84\x9eB\x95\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\f\xb1s\x8b\xac\b\xd4\xf9\xc0\x8bM\xef\xf5\x15T_\xa8XO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa8\x19\xd2\xec\xe1\"\xe0(\xc8\xe8\xa0J\x06M\x02\xb9\x02\x9b\b\xb9\x8965\u026d\xc5\u07a0\x00\x00\u0794\xa8%\xfdZ\xbby&\xa6|\xf3k\xa2F\xa2K\xd2{\xe6\xf6\xed\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4\xa8(U9\x86\x9d\x88\xf8\xa9aS7Uq}~\xb6Uv\xae\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa83\x82\xb6\xe1Rg\x97J\x85P\xb9\x8fqv\xc1\xa3S\xf9\xbe\x89\xbf\xfd\xaf/\xc1\xb1\xa4\x00\x00\xe0\x94\xa8DlG\x81\xa77\xacC(\xb1\xe1[\x8a\v?\xbb\x0f\xd6h\x8a\x04\x87\x94\xd1\xf2F\x19*\x00\x00\u07d4\xa8E[A\x17e\u0590\x1e1\x1erd\x03\t\x1eB\xc5f\x83\x89\xb7:\xec;\xfe\x14P\x00\x00\xe0\x94\xa8f\x13\xe6\u0124\xc9\xc5_\\\x10\xbc\xda2\x17]\u02f4\xaf`\x8a\x02C\xd6\xc2\xe3k\xe6\xae\x00\x00\u07d4\xa8m\xb0}\x9f\x81/G\x96b-@\xe0=\x13Xt\xa8\x8at\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa8\u007fz\xbdo\xa3\x11\x94(\x96x\xef\xb6<\xf5\x84\xee^*a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xa8\x80\u2a3f\x88\xa1\xa8&H\xb4\x01W\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa8\x9d\xf3HY\xed\xd7\xc8 \u06c8w@\xd8\xff\x9e\x15\x15|{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\xa4<\x00\x91\x00al\xb4\xaeN\x03?\x1f\xc5\xd7\xe0\xb6\xf1R\x89\u0548\xd0x\xb4?M\x80\x00\u07d4\xa8\xa7\b\xe8O\x82\u06c6\xa3U\x02\x19;Ln\xe9\xa7n\xbe\x8f\x897\b\xba\xed=h\x90\x00\x00\xe0\x94\xa8\xa7\xb6\x8a\u06b4\xe3\xea\xdf\xf1\x9f\xfaX\xe3J?\xce\xc0\xd9j\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xa8\xa8\xdb\xdd\x1a\x85\u047e\xee%i\xe9\x1c\xccM\t\xae\u007fn\xa1\x8a\x01:k+VHq\xa0\x00\x00\u07d4\xa8\xac\xa7H\xf9\xd3\x12\xect\u007f\x8bex\x14&\x94\xc7\xe9\xf3\x99\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\xb6[\xa3\x17\x1a?w\xa65\v\x9d\xaf\x1f\x8dU\xb4\xd2\x01\xeb\x89(b\xf3\xb0\xd2\"\x04\x00\x00\u07d4\xa8\xbe\xb9\x1c+\x99\u0216J\xa9[kJ\x18K\x12i\xfc4\x83\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u0794\xa8\xc0\xb0/\xaf\x02\xcbU\x19\u0768\x84\xde{\xbc\x8c\x88\xa2\u0681\x88\xe7\xc2Q\x85\x05\x06\x00\x00\u07d4\xa8\xc1\u05aaA\xfe=e\xf6{\xd0\x1d\xe2\xa8f\xed\x1e\u066eR\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\xa8\xca\xfa\xc3\"\x80\xd0!\x02\v\xf6\xf2\xa9x(\x83\u05ea\xbe\x12\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa8\xdb\v\x9b \x14S3A<;\fb\xf5\xf5.\u0544\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa8\xf3\u007f\n\xb3\xa1\xd4H\xa9\xe3\xce@\x96_\x97\xa6F\b:4\x89\x11\xe0\xe4\xf8\xa5\v\xd4\x00\x00\u07d4\xa8\xf8\x9d\xd5\xccnd\u05f1\xee\xac\xe0\a\x02\x02,\xd7\xd2\xf0=\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\xa9\x04v\xe2\xef\xdf\xeeO8{\x0f2\xa5\x06x\xb0\xef\xb5s\xb5\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa9\x14PF\xfa6(\xcf_\xd4\xc6\x13\x92{\xe51\xe6\xdb\x1f\u0749\x06\x12O\xee\x99;\xc0\x00\x00\u07d4\xa9\x14\u0375q\xbf\xd9=d\xdaf\xa4\xe1\b\xea\x13NP\xd0\x00\x89M\x878\x99G\x13y\x80\x00\xe0\x94\xa9\x1aZ{4\x1f\x99\xc55\x14N \xbe\x9ck;\xb4\u008eM\x8a\x01&u:\xa2$\xa7\v\x00\x00\u07d4\xa9%%Q\xa6$\xaeQ7\x19\u06beR\a\xfb\xef\xb2\xfdwI\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa9'\u050b\xb6\u02c1K\xc6\t\xcb\u02a9\x15\x1f]E\x9a'\xe1\x89\x0e\xb95\t\x00d\x18\x00\x00\u07d4\xa9)\u023dq\xdb\f0\x8d\xac\x06\b\n\x17G\xf2\x1b\x14e\xaa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa9K\xbb\x82\x14\u03cd\xa0\xc2\xf6h\xa2\xacs\xe8bHR\x8dK\x894\n\xad!\xb3\xb7\x00\x00\x00\u07d4\xa9Q\xb2D\xffP\u03eeY\x1d^\x1a\x14\x8d\xf6\xa98\xef*\x1a\x89^\x00\x15\x84\xdf\xcfX\x00\x00\u07d4\xa9`\xb1\xca\xdd;\\\x1a\x8el\xb3\xab\xca\xf5.\xe7\xc3\xd9\xfa\x88\x89R\x8b\xc3T^Rh\x00\x00\u07d4\xa9a\x17\x1fSB\xb1s\xddp\xe7\xbf\xe5\xb5\xca#\x8b\x13\xbc\u0749\xb8'\x94\xa9$O\f\x80\x00\u07d4\xa9u\xb0w\xfc\xb4\u030e\xfc\xbf\x83\x84Y\xb6\xfa$:AY\u0589\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xa9{\xeb:H\xc4_\x15((L\xb6\xa9_}\xe4S5\x8e\u018a\x06\x90\x83l\n\xf5\xf5`\x00\x00\u07d4\xa9~\a!DI\x9f\xe5\xeb\xbd5J\xcc~~\xfbX\x98]\b\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xa9\x86v/zO)O.\v\x172y\xad,\x81\xa2\"4X\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa9\x8f\x10\x985\xf5\xea\xcd\x05Cd|4\xa6\xb2i\xe3\x80/\xac\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa9\x97\xdf\u01d8j'\x05\bH\xfa\x1cd\u05e7\xd6\xe0z\u0322\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\xa9\x99\x91\u03bd\x98\xd9\xc88\xc2_zt\x16\xd9\xe2D\xca%\r\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa9\xa1\xcd\xc3;\xfd7o\x1c\rv\xfbl\x84\xb6\xb4\xac'Mh\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa9\xa8\xec\xa1\x1a#\xd6F\x89\xa2\xaa>A}\xbb=3k\xb5\x9a\x89\x0e4S\xcd;g\xba\x80\x00\u07d4\xa9\xac\xf6\x00\b\x1b\xb5[\xb6\xbf\xba\xb1\x81_\xfcN\x17\xe8Z\x95\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa9\xad\x19&\xbcf\xbd\xb31X\x8e\xa8\x197\x88SM\x98,\x98\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xa9\xaf!\xac\xbeH/\x811\x89j\"\x806\xbaQ\xb1\x94S\u00c9\x02\xb5\xe0!\x98\f\xc1\x80\x00\xe0\x94\xa9\xb2\xd2\xe0IN\xab\x18\xe0}7\xbb\xb8V\xd8\x0e\x80\xf8L\u04ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa9\xbaoA;\x82\xfc\xdd\xf3\xaf\xfb\xbd\u0412\x87\xdc\xf5\x04\x15\u0289\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa9\xbe\x88\xad\x1eQ\x8b\v\xbb\x02J\xb1\xd8\xf0\xe7?y\x0e\fv\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xa9\xbf\xc4\x10\xdd\xdb q\x1eE\xc0s\x87\xea\xb3\n\x05N\x19\xac\x89>\x99`\x1e\xdfNS\x00\x00\u07d4\xa9\u0522\xbc\xbe[\x9e\bi\xd7\x0f\x0f\xe2\xe1\u05aa\xcdE\xed\u0149\n\xc6\xe7z\xb6c\xa8\x00\x00\xe0\x94\xa9\xd6KO;\xb7\x85\a\"\xb5\x8bG\x8b\xa6\x917^\"NB\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa9\xd6\xf8q\xcax\x1au\x9a \xac:\u06d7,\xf1()\xa2\b\x892$\xf4'#\xd4T\x00\x00\xe0\x94\xa9\xdc\x04$\u0196\x9dy\x83X\xb3\x93\xb1\x93:\x1fQ\xbe\xe0\n\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa9\xe1\x94f\x1a\xacpN\xe9\u07a0C\x97N\x96\x92\xde\xd8J]\x89\x1a&\xa5\x14\"\xa0p\x00\x00\u07d4\xa9\xe2\x837\xe65q\x93\xd9\xe2\xcb#k\x01\xbeD\xb8\x14'\u07c9wC\"\x17\xe6\x83`\x00\x00\u07d4\xa9\xe6\xe2^ekv%Xa\x9f\x14z!\x98[\x88t\xed\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa9\xe9\xdb\xcez,\xb06\x94y\x98\x97\xbe\xd7\xc5M\x15_\u06a8\x89\n\xb5\xae\x8f\u025de\x80\x00\u07d4\xa9\xed7{}n\xc2Yq\xc1\xa5\x97\xa3\xb0\xf3\xbe\xadW\u024f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xaa\x02\x00\xf1\xd1~\x9cT\xda\x06G\xbb\x969]W\xa7\x858\u06099>\xf1\xa5\x12|\x80\x00\x00\u07d4\xaa\f\xa3ss7\x17\x8a\f\xaa\xc3\t\x9cXK\x05lV0\x1c\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xaa\x13kG\x96+\xb8\xb4\xfbT\r\xb4\xcc\xf5\xfd\xd0B\xff\xb8\u03c9\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\xaa\x14B-o\n\xe5\xa7X\x19N\xd1W\x80\xc88\xd6\u007f\x1e\xe1\x8a\x06\t2\x05lD\x9d\xe8\x00\x00\u07d4\xaa\x16&\x9a\xac\x9c\r\x800h\xd8/\u01d1Q\xda\xdd3Kf\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xaa\x16p&\u04da\xb7\xa8V5\x94N\xd9\xed\xb2\xbf\xeb\xa1\x18P\x8a\x01\xc1\xd5\xe2\x1bO\xcfh\x00\x00\u07d4\xaa\x1b7h\xc1m\x82\x1fX\x0ev\xc8\xe4\xc8\xe8m}\u01c8S\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xaa\x1d\xf9.Q\xdf\xf7\v\x19s\xe0\xe9$\xc6b\x87\xb4\x94\xa1x\x89\x1c\xf8J0\xa0\xa0\xc0\x00\x00\u07d4\xaa,g\x00\x96\xd3\xf990S%B~\xb9U\xa8\xa6\r\xb3\u0149l\x95Y\x06\x99#-\x00\x00\u07d4\xaa15\xcbT\xf1\x02\xcb\xef\xe0\x9e\x96\x10:\x1ayg\x18\xffT\x89\x03\"\"\xd9\xc31\x94\x00\x00\u07d4\xaa2\x1f\xdb\xd4I\x18\r\xb8\xdd\xd3O\x0f\xe9\x06\xec\x18\xee\t\x14\x89%\"H\u07b6\xe6\x94\x00\x00\xe0\x94\xaa9%\xdc\"\v\xb4\xae!w\xb2\x880x\xb6\xdc4l\xa1\xb2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xaa?)`\x1a\x131t^\x05\xc4(0\xa1^q\x93\x8ab7\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94\xaaG\xa4\xff\xc9y622\u025b\x99\xfa\xda\x0f'4\xb0\xae\xee\x8a\x01\xb8H\x9d\xf4\xdb\xff\x94\x00\x00\u07d4\xaaI=?O\xb8fI\x1c\xf8\xf8\x00\xef\xb7\xe22N\xd7\xcf\xe5\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xaaV\xa6]\u012b\xb7/\x11\xba\xe3+o\xbb\aDG\x91\xd5\u0249(\x94\xe9u\xbfIl\x00\x00\xe0\x94\xaaZ\xfc\xfd\x83\t\xc2\u07dd\x15\xbe^jPN}pf$\u014a\x01<\xf4\"\xe3\x05\xa17\x80\x00\u07d4\xaa\x8e\xb0\x82;\a\xb0\xe6\xd2\n\xad\xda\x0e\x95\xcf85\xbe\x19.\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xaa\x91#~t\r%\xa9/\u007f\xa1F\xfa\xa1\x8c\xe5m\xc6\xe1\xf3\x892$\xf4'#\xd4T\x00\x00\u07d4\xaa\x96\x0e\x10\xc5#\x91\xc5N\x158|\xc6z\xf8'\xb51m\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\x9b\xd4X\x955\xdb'\xfa+\xc9\x03\xca\x17\xd6y\xddeH\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\xa8\xde\xfe\x11\xe3a?\x11\x06\u007f\xb9\x83bZ\b\x99Z\x8d\xfc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaa\xaa\xe6\x8b2\x14\x02\xc8\xeb\xc14h\xf3A\xc6<\f\xf0?\u0389Rf<\u02b1\xe1\xc0\x00\x00\u07d4\xaa\xad\x1b\xaa\xdeZ\xf0N+\x17C\x9e\x93Y\x87\xbf\x8c+\xb4\xb9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xaa\xb0\n\xbfX(\xd7\xeb\xf2kG\u03ac\u0378\xba\x032Qf\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xaa\xbd\xb3\\\x15\x14\x98J\x03\x92\x13y?3E\xa1h\xe8\x1f\xf1\x89\x10\xca\u0216\xd29\x00\x00\x00\u07d4\xaa\xca`\xd9\xd7\x00\u7156\xbb\xbb\xb1\xf1\xe2\xf7\x0fF'\xf9\u060965\xbbw\xcbK\x86\x00\x00\u07d4\xaa\xce\u0629V;\x1b\xc3\x11\xdb\xdf\xfc\x1a\xe7\xf5u\x19\xc4D\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\u04b7\xf8\x10f\x95\a\x8el\x13\x8e\xc8\x1at\x86\xaa\xca\x1e\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaa\xe6\x1eC\xcb\r\f\x96\xb3\x06\x99\xf7~\x00\xd7\x11\u0423\x97\x9b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaa\xe72\xed\xa6Y\x88\u00e0\f\u007fG/5\x1cF;\x1c\x96\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\xf0#\xfe\U0009091b\xb7\x8b\xb7\xab\xc9]f\x9cP\xd5(\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xaa\xf5\xb2\a\xb8\x8b\r\xe4\xac@\xd7G\xce\xe0n\x17-\xf6\xe7E\x8a\x06\xa7\xb7\x1d\u007fQ\u0410\x00\x00\u07d4\xaa\xf9\xeeK\x88lm\x1e\x95Io\xd2t#[\xf4\xec\xfc\xb0}\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xaa\xfb{\x01:\xa1\xf8T\x1c~2{\xf6P\xad\xbd\x19L \x8f\x89I\x9e\t-\x01\xf4x\x00\x00\xe0\x94\xab\t\x863\xee\xee\f\xce\xfd\xf62\xf9WTV\xf6\u0740\xfc\x86\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xab\f\xedv.\x16a\xfa\xe1\xa9*\xfb\x14\b\x88\x94\x13yH%\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xab\x14\xd2!\xe3=TF)\x19\x8c\u0416\xedc\u07e2\x8d\x9fG\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xab \x9f\u0729y\u0426G\x01\n\xf9\xa8\xb5/\xc7\xd2\r\x8c\u044a\x01\xee\xe2S,|-\x04\x00\x00\u07d4\xab'\xbax\xc8\xe5\xe3\xda\xef1\xad\x05\xae\xf0\xff\x03%r\x1e\b\x89\x19^\xce\x00n\x02\xd0\x00\x00\u07d4\xab(q\xe5\a\u01fe9eI\x8e\x8f\xb4b\x02Z\x1a\x1cBd\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xab8a\"o\xfe\xc1(\x91\x87\xfb\x84\xa0\x8e\xc3\xed\x042d\xe8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab=\x86\xbc\x82\x92~\f\xd4!\xd1F\xe0\u007f\x91\x93'\xcd\xf6\xf9\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xab>b\xe7z\x8b\"^A\x15\x92\xb1\xaf0\aR\xfeA$c\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xab>x)K\xa8\x86\xa0\xcf\xd5\xd3H\u007f\xb3\xa3\a\x8d3\x8dn\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xab@\x04\xc0@?~\xab\xb0\xeaXo!!V\xc4 =g\xf1\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xabAo\xe3\rX\xaf\xe5\xd9EL\u007f\xce\u007f\x83\v\xccu\x03V\x89\x0657\x01\xc6\x05\u06c0\x00\u07d4\xabEr\xfb\xb1\xd7+W]i\xecj\xd1s3\x87>\x85R\xfc\x89lj\xc5L\xdahG\x00\x00\u07d4\xabZy\x01av2\ts\xe8\xcd8\xf67U0\x02%1\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab]\xfc\x1e\xa2\x1a\xdcB\u03cc?n6\x1e$?\xd0\xdaa\xe5\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xabke\xea\xb8\xdf\xc9\x17\xec\x02Q\xb9\xdb\x0e\u03e0\xfa\x03(I\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xabp\x91\x93.K\u00dd\xbbU#\x80\u0293O\xd7\x16m\x1en\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\xabt\x16\xff2%IQ\u02fcbN\xc7\xfbE\xfc~\u02a8r\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xab|B\xc5\xe5-d\x1a\a\xadu\t\x9cb\x92\x8b\u007f\x86b/\x89\x126\x1a\xa2\x1d\x14\xba\x00\x00\u07d4\xab}T\xc7\xc6W\x0e\xfc\xa5\xb4\xb8\xcep\xf5*Ws\xe5\xd5;\x89\x0f(:\xbe\x9d\x9f8\x00\x00\u07d4\xab~\v\x83\xed\x9aBLm\x1ejo\x87\xa4\xdb\xf0d\t\xc7\u0589\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xab\x84\xa0\xf1G\xad&T\x00\x00+\x85\x02\x9aA\xfc\x9c\xe5\u007f\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab\x93\xb2n\xce\n\n\xa2\x13e\xaf\xed\x1f\xa9\xae\xa3\x1c\xd5Dh\x89W+{\x98sl \x00\x00\u07d4\xab\x94\x8aJ\xe3y\\\xbc\xa11&\xe1\x92S\xbd\xc2\x1d:\x85\x14\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xab\x9a\xd3n\\t\xce.\x969\x9fW\x83\x941\xd0\u77d6\xab\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\xab\xb2\xe6\xa7*@\xban\xd9\b\u037c\xec\x91\xac\xfdwx0\xd1dcG\x8a\xe0\xfcw \x89\a?u\u0460\x85\xba\x00\x00\xe0\x94\xab\u071f\x1b\xcfM\x19\xee\x96Y\x100\xe7r\xc340/}\x83\x8a\b~^\x11\xa8\x1c\xb5\xf8\x00\x00\u07d4\xab\xde\x14{*\xf7\x89\ua946T~f\xc4\xfa&d\xd3(\xa4\x89\rk`\x81\xf3L\x12\x80\x00\xe0\x94\xab\xe0|\xedj\xc5\xdd\xf9\x91\xef\xf6\xc3\xda\"jt\x1b\xd2C\xfe\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xab\xf1/\xa1\x9e\x82\xf7lq\x8f\x01\xbd\xca\x00\x03gE#\xef0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xab\xf7(\u03d3\x12\xf2!(\x02NpF\xc2Q\xf5\xdcY\x01\xed\x8a\x06A\xe8\xa15c\xd8\xf8\x00\x00\u07d4\xab\xf8\xff\xe0p\x8a\x99\xb5(\xcc\x1e\xd4\xe9\xceK\r\x060\xbe\x8c\x89z\xb5\u00ae\xee\xe68\x00\x00\u07d4\xab\xfc\xf5\xf2P\x91\xceW\x87_\xc6t\xdc\xf1\x04\xe2\xa7=\xd2\xf2\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xab\xfe\x93d%\xdc\u01f7K\x95P\x82\xbb\xaa\xf2\xa1\x1dx\xbc\x05\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xac\x02OYO\x95X\xf0ICa\x8e\xb0\xe6\xb2\xeeP\x1d\xc2r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\x12*\x03\xcd\x05\x8c\x12._\xe1{\x87/Hw\xf9\u07d5r\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xac\x14.\xda\x11W\xb9\xa9\xa6C\x90\xdf~j\xe6\x94\xfa\u0249\x05\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\x1d\xfc\x98Kq\xa1\x99)\xa8\x1d\x81\xf0J|\xbb\x14\a7\x03\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xac!\xc1\xe5\xa3\xd7\xe0\xb5\x06\x81g\x9d\xd6\u01d2\xdb\u0287\xde\u02ca\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xac(\x89\xb5\x96o\f\u007f\x9e\xdbB\x89\\\xb6\x9d\x1c\x04\xf9#\xa2\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xac(\xb5\xed\xea\x05\xb7o\x8c_\x97\bEA'|\x96ijL\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xac,\x8e\t\xd0d\x93\xa68XC{\xd2\v\xe0\x19bE\x03e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xac.vm\xac?d\x8fcz\xc6q?\u0770h\xe4\xa4\xf0M\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\xac9\x00)\x8d\xd1M|\xc9mJ\xbbB\x8d\xa1\xba\xe2\x13\xff\xed\x8a\x05<\xa1)t\x85\x1c\x01\x00\x00\u07d4\xac=\xa5&\xcf\u0388)s\x02\xf3LI\xcaR\r\xc2q\xf9\xb2\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xacD`\xa7nm\xb2\xb9\xfc\xd1R\xd9\xc7q\x8d\x9a\xc6\xed\x8co\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xacJ\xcf\xc3n\xd6\tJ'\xe1\x18\xec\xc9\x11\xcdG>\x8f\xb9\x1f\x89a\x91>\x14@<\f\x00\x00\u07d4\xacL\xc2V\xaet\xd6$\xac\xe8\r\xb0x\xb2 \u007fW\x19\x8fk\x89lyt\x12?d\xa4\x00\x00\u07d4\xacN\xe9\xd5\x02\xe7\xd2\xd2\xe9\x9eY\xd8\xca}_\x00\xc9KM\u058965\u026d\xc5\u07a0\x00\x00\u07d4\xacR\xb7~\x15fH\x14\xf3\x9eO'\x1b\xe6A0\x8d\x91\xd6\u0309\v\xed\x1d\x02c\xd9\xf0\x00\x00\u07d4\xacY\x99\xa8\x9d-\u0486\u0568\fm\xee~\x86\xaa\xd4\x0f\x9e\x12\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xac_br1H\r\r\x950.m\x89\xfc2\xcb\x1dO\xe7\xe3\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xac`\x8e+\xac\x9d\xd2\a(\u0494~\xff\xbb\xbf\x90\n\x9c\xe9K\x8a\x01EK\r\xb3uh\xfc\x00\x00\u07d4\xacm\x02\xe9\xa4k7\x9f\xacJ\u0271\u05f5\xd4{\xc8P\xce\x16\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xacoh\xe87\xcf\x19a\xcb\x14\xabGDm\xa1h\xa1m\u0789\x89Hz\x9a0E9D\x00\x00\u07d4\xacw\xbd\xf0\x0f\u0558[]\xb1+\xbe\xf8\x008\n\xbc*\x06w\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xac~\x03p'#\xcb\x16\xee'\xe2-\u0438\x15\xdc-\\\xae\x9f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xac\x8bP\x9a\xef\xea\x1d\xbf\xaf+\xb35\x00\xd6W\vo\xd9mQ\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xac\x8e\x87\xdd\xda^x\xfc\xbc\xb9\xfa\u007f\xc3\xce\x03\x8f\x9f}.4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xac\x9f\xffh\xc6\x1b\x01\x1e\xfb\xec\xf08\xedr\u06d7\xbb\x9er\x81\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xac\xa1\xe6\xbcd\xcc1\x80\xf6 \xe9M\u0171\xbc\xfd\x81X\xe4]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\xa2\xa883\v\x170-\xa71\xd3\r\xb4\x8a\x04\xf0\xf2\a\xc1\x89Hz\x9a0E9D\x00\x00\u07d4\xac\xaa\xdd\xcb\xf2\x86\xcb\x0e!]\xdaUY\x8f\u007f\xf0\xf4\xad\xa5\u018965\u026d\xc5\u07a0\x00\x00\u07d4\xac\xb9C8UK\u0108\u0308\xae-\x9d\x94\b\rk\u07c4\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xac\xbc-\x19\xe0l;\xab\xbb[o\x05+k\xf7\xfc7\xe0r)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\xbd\x18U\x89\xf7\xa6\x8ag\xaaK\x1b\xd6Pw\xf8\xc6NN!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\xc0bp,Ya]4D\xefb\x14\xb8\x86+\x00\x9a\x02\xed\x89QO\xcb$\xff\x9cP\x00\x00\u07d4\xac\xc0\x90\x9f\xda.\xa6\xb7\xb7\xa8\x8d\xb7\xa0\xaa\xc8h\t\x1d\xdb\xf6\x89\x013v_\x1e&\u01c0\x00\u07d4\xac\xc1\u01c7\x86\xabM+;'q5\xb5\xba\x12>\x04\x00Hk\x89\x04E\x91\xd6\u007f\xec\xc8\x00\x00\u07d4\xac\xc4j*U\\t\xde\u0522\xbd\tN\x82\x1b\x97\x84;@\xc0\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xac\u015f;0\xce\xff\xc5da\xcc[\x8d\xf4\x89\x02$\x0e\x0e{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\xce\x01\xe0\xa7\x06\x10\xdcp\xbb\x91\xe9\x92o\xa9\x95\u007f7/\xba\x89\x1d\x1c_>\xda \xc4\x00\x00\u07d4\xac\xd8\u0751\xf7\x14vLEg|c\xd8R\xe5n\xb9\xee\xce.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\u2af6;\x06\x04@\x9f\xbd\xe3\xe7\x16\u0487mD\xe8\xe5\u0749\b=lz\xabc`\x00\x00\xe0\x94\xac\xec\x91\xefiA\xcfc\v\xa9\xa3\u71e0\x12\xf4\xa2\xd9\x1d\u050a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xad\nJ\xe4x\xe9cn\x88\xc6\x04\xf2B\xcfT9\xc6\xd4V9\x89\xbe\xd1\xd0&=\x9f\x00\x00\x00\u07d4\xad\x17\x99\xaa\xd7`+E@\u0343/\x9d\xb5\xf1\x11P\xf1hz\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xad\x1dh\xa08\xfd%\x86\x06~\xf6\xd15\xd9b\x8ey\xc2\xc9$\x89\xfe\t\xa5'\x9e*\xbc\x00\x00\u07d4\xad*\\\x00\xf9#\xaa\xf2\x1a\xb9\xf3\xfb\x06n\xfa\n\x03\xde/\xb2\x8965\xbbw\xcbK\x86\x00\x00\u07d4\xad5e\xd5+h\x8a\xdd\xed\b\x16\x8b-8r\xd1}\n&\xae\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xad7|\xd2^\xb5>\x83\xae\t\x1a\n\x1d+E\x16\xf4\x84\xaf\u0789i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xadAM)\xcb~\xe9s\xfe\xc5N\"\xa3\x88I\x17\x86\xcfT\x02\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xadD5~\x01~$OGi1\u01f8\x18\x9e\xfe\xe8\n]\n\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xadW\xaa\x9d\x00\xd1\fC\x9b5\xef\xcc\v\xec\xac.9U\xc3\x13\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xadY\xa7\x8e\xb9\xa7J\u007f\xbd\xae\xfa\xfa\x82\xea\u0684u\xf0\u007f\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xadZ\x8dV\x01L\xfc\xb3`\xf4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xadr\x81!\x87?\x04V\xd0Q\x8b\x80\xabe\x80\xa2\x03pe\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xads,\x97e\x93\xee\xc4x;N.\xcdy9yx\v\xfe\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xad}\xd0S\x85\x9e\xdf\xf1\xcbo\x9d*\xcb\xedm\xd5\xe32Bo\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xad\x80\xd8e\xb8\\4\xd2\xe6IK.z\xef\xeak\x9a\xf1\x84\u06c9\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xad\x8b\xfe\xf8\u018aH\x16\xb3\x91o5\xcb{\xfc\xd7\xd3\x04\tv\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xad\x8eH\xa3wi]\xe0\x146:R:(\xb1\xa4\fx\xf2\b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xad\x91\n#\u0585\x06\x13eJ\xf7\x863z\u04a7\bh\xacm\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xad\x92~\x03\xd1Y\x9ax\xca+\xf0\xca\u04a1\x83\xdc\xebq\xea\xc0\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xad\x92\xca\x06n\xdb|q\x1d\xfc[\x16a\x92\xd1\xed\xf8\xe7q\x85\x8a\a\x9f\x90\\o\xd3N\x80\x00\x00\u07d4\xad\x94#_\u00f3\xf4z$\x13\xaf1\u8111I\b\xef\fE\x89\x1b\x1b\x01B\xd8\x15\x84\x00\x00\u07d4\xad\x9e\x97\xa0H/5:\x05\xc0\xf7\x92\xb9w\xb6\xc7\xe8\x11\xfa_\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xad\x9fL\x89\n;Q\x1c\xeeQ\xdf\xe6\xcf\xd7\xf1\t;vA,\x89\x1bv|\xbf\xeb\f\xe4\x00\x00\u07d4\xad\xaa\x0eT\x8c\x03Z\xff\xedd\xcag\x8a\x96?\xab\xe9\xa2k\xfd\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\xad\xb9H\xb1\xb6\xfe\xfe }\xe6^\x9b\xbc-\xe9\x8e`]\vW\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xad\xc1\x9e\xc85\xaf\xe3\u5347\u0713\xa8\xa9!<\x90E\x13&\x89j\xdb\xe54\"\x82\x00\x00\x00\u07d4\xad\xc8\"\x8e\xf9(\xe1\x8b*\x80}\x00\xfb1\xfcgX\x15\xaa\x00\x00\u07d4\xad\xff\r\x1d\v\x97G\x1ev\u05c9\xd2\u470at\xf9\xbdT\xff\x89e\xea=\xb7UF`\x00\x00\u07d4\xae\x06,D\x86\x18d0u\xdez\x0004-\xce\xd6=\xba\u05c9,\xc6\u034c\u0082\xb3\x00\x00\xe0\x94\xae\x10\xe2z\x01O\r0k\xaf&mH\x97\u021a\xee\xe2\xe9t\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xae\x12k8,\xf2W\xfa\xd7\xf0\xbc}\x16)~T\xccrg\u0689\x10CV\x1a\x88)0\x00\x00\u07d4\xae\x13\xa0\x85\x11\x11\x0f2\xe5;\xe4\x12xE\xc8C\xa1\xa5|{\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xae\x17\x9aF\r\xb6c&t=$\xe6u#\xa5{$m\xaf\u007f\x8a\x01\x00\a\xae|\xe5\xbb\xe4\x00\x00\u07d4\xae\"(ey\x90y\xaa\xf4\xf0gJ\f\u06ab\x02\xa6\xd5p\xff\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xae#\x9a\xcf\xfdN\xbe.\x1b\xa5\xb4\x17\x05r\xdcy\xcce3\xec\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xae/\x9c\x19\xacv\x13e\x94C#\x93\xb0G\x1d\b\x90!d\u04c9%\xdf\x05\u01a8\x97\xe4\x00\x00\u07d4\xae4\x86\x1d4\"S\x19O\xfcfR\xdf\xdeQ\xabD\xca\xd3\xfe\x89\x19F\bhc\x16\xbd\x80\x00\u07d4\xae6\xf7E!!\x91>\x80\x0e\x0f\xcd\x1ae\xa5G\x1c#\x84o\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\xae?\x98\xa4C\xef\xe0\x0f>q\x1dR]\x98\x94\u071aa\x15{\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\xe0\x94\xaeG\xe2`\x9c\xfa\xfe6\x9df\xd4\x15\xd99\xde\x05\b\x1a\x98r\x8a\x05\xba\xec\xf0%\xf9\xb6P\x00\x00\u07d4\xaeO\x12.5\xc0\xb1\xd1\xe4\x06\x92\x91E|\x83\xc0\u007f\x96_\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaePU\x81L\xb8\xbe\f\x11{\xb8\xb1\xc8\u04b6;F\x98\xb7(\x89\x01\xbc\x93.\xc5s\xa3\x80\x00\u07d4\xaeS\x8cs\u0173\x8d\x8dXM~\xbd\xad\xef\xb1\\\xab\xe4\x83W\x896'\xe8\xf7\x127<\x00\x00\u07d4\xaeW\xcc\x12\x9a\x96\xa8\x99\x81\xda\xc6\r/\xfb\x87}]\xc5\xe42\x89<:#\x94\xb3\x96U\x00\x00\u07d4\xaeZ\xa1\xe6\u00b6\x0fo\xd3\xef\xe7!\xbbJq\x9c\xbe=o]\x89+$\u01b5Z^b\x00\x00\u07d4\xae\\\x9b\xda\xd3\xc5\u0221\"\x04D\xae\xa5\xc2)\xc1\x83\x9f\x1dd\x89\x19\xe2\xa4\xc8\x18\xb9\x06\x00\x00\u07d4\xae\\\xe35Z{\xa9\xb32v\f\tP\u00bcE\xa8_\xa9\xa0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xae]\"\x1a\xfc\xd3\u0493U\xf5\b\xea\xdf\xca@\x8c\xe3<\xa9\x03\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xaec[\xf781\x11\x9d-)\xc0\xd0O\xf8\xf8\xd8\u0425zF\x89Hz\x9a0E9D\x00\x00\xe0\x94\xaed\x81U\xa6X7\x0f\x92\x9b\xe3\x84\xf7\xe0\x01\x04~I\xddF\x8a\x02\xdf$\xae2\xbe D\x00\x00\xe0\x94\xaeo\fs\xfd\xd7|H\x97'Q!t\u0675\x02\x96a\x1cL\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xaep\xe6\x9d,J\n\xf8\x18\x80{\x1a'\x05\xf7\x9f\u0435\xdb\u01095e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xaew9\x12N\xd1S\x05%\x03\xfc\x10\x14\x10\xd1\xff\xd8\xcd\x13\xb7\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xaex\xbb\x84\x919\xa6\xba8\xae\x92\xa0\x9ai`\x1c\xc4\xcbb\u0449\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xae\x84\"\x10\xf4M\x14\u0124\u06d1\xfc\x9d;;P\x01O{\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xae\x84.\x81\x85\x8e\xcf\xed\xf6Plhm\xc2\x04\xac\x15\xbf\x8b$\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xae\x89T\xf8\xd6\x16m\xe5\a\xcfa)}\x0f\xc7\xcak\x9eq(\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xae\x9e\xcdk\u0755.\xf4\x97\xc0\x05\n\u0aca\x82\xa9\x18\x98\u0389\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\xae\x9f\\?\xbb\xe0\u027c\xbf\x1a\xf8\xfft\xea(\v:]\x8b\b\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xae\xad\x88\u0589Ak\x1c\x91\xf26D!7[}\x82\xd0RR\n\xfb\\Wm\x9f~\xb9>\u048a\r\xd0A \xba\t\xcf\xe6\x00\x00\u07d4\xae\xc2\u007f\xf5\xd7\xf9\xdd\u0691\x18?F\xf9\xd5%C\xb6\xcd+/\x89\x18e\x01'\xcc=\xc8\x00\x00\u07d4\xae\xe4\x9dh\xad\xed\xb0\x81\xfdCpZ_x\xc7x\xfb\x90\xdeH\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xae\xf5\xb1\"X\xa1\x8d\xec\a\xd5\xec.1et\x91\x9dy\xd6\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\xae\xfc\xfe\x88\xc8&\xcc\xf11\xd5N\xb4\ua7b8\x0ea\xe1\xee%\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xaf\x06\xf5\xfam\x12\x14\xecC\x96}\x1b\xd4\xdd\xe7J\xb8\x14\xa98\x89\x04\xc5>\xcd\xc1\x8a`\x00\x00\u07d4\xaf\x11H\xefl\x8e\x10=u0\xef\xc9\x16y\u026c'\x00\t\x93\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaf >\"\x9d~mA\x9d\xf47\x8e\xa9\x87\x15Q_c\x14\x85\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xaf X\xc7(,\xf6|\x8c<\xf90\x13<\x89a|\xe7])\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4\xaf&\xf7\u01bfE> x\xf0\x89S\u4c80\x04\xa2\xc1\xe2\t\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xaf0\x87\xe6.\x04\xbf\x90\rZT\xdc>\x94bt\u0692B;\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf6\x14\u0736\x8a6\xe4ZN\x91\x1ebybG\"-Y[\x89z\x81\x06_\x11\x03\xbc\x00\x00\u07d4\xaf6\x15\u01c9\u0431\x15*\xd4\xdb%\xfe]\xcf\"(\x04\xcfb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaf<\xb5\x96Y3\xe7\xda\u0603i;\x9c>\x15\xbe\xb6\x8aHs\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xafD\x93\xe8R\x1c\xa8\x9d\x95\xf5&|\x1a\xb6?\x9fEA\x1e\x1b\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xafL\xf4\x17\x85\x16\x1fW\x1d\f\xa6\x9c\x94\xf8\x02\x1fA)N\u028a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xafR\x9b\xdbE\x9c\xc1\x85\xbe\xe5\xa1\u014b\xf7\xe8\xcc\xe2\\\x15\r\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xafg\xfd>\x12\u007f\xd9\xdc6\xeb?\xcdj\x80\u01feOu2\xb2\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xafw\x1094Z40\x01\xbc\x0f\x8aY#\xb1&\xb6\rP\x9c\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\xaf\u007fy\xcbAZ\x1f\xb8\u06fd\tF\a\xee\x8dA\xfb|Z;\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xaf\x87\xd27\x1e\xf3x\x95\u007f\xbd\x05\xba/\x1df\x93\x1b\x01\u2e09%\xf2s\x93=\xb5p\x00\x00\u07d4\xaf\x88\x0f\xc7V}U\x95\xca\xcc\xe1\\?\xc1L\x87B\xc2l\x9e\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xaf\x8e\x1d\xcb1L\x95\r6\x87CM0\x98X\xe1\xa8s\x9c\u0509\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xaf\x99-\xd6i\xc0\x88>U\x15\xd3\xf3\x11*\x13\xf6\x17\xa4\xc3g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaf\xa1\u056d8\xfe\xd4GY\xc0[\x89\x93\xc1\xaa\r\xac\xe1\x9f@\x89\x04V9\x18$O@\x00\x00\xe0\x94\xaf\xa59XnG\x19\x17J;F\xb9\xb3\xe6c\xa7\u0475\xb9\x87\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xaf\xa6\x94n\xff\xd5\xffS\x15O\x82\x01\x02S\xdfG\xae(\f\u0309j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xaf\xc8\xeb\u860b\xd4\x10Z\xccL\x01\x8eTj\x1e\x8f\x9cx\x88\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xaf\xcc}\xbb\x83V\xd8B\xd4:\xe7\xe2<\x84\"\xb0\"\xa3\b\x03\x8a\x06o\xfc\xbf\xd5\xe5\xa3\x00\x00\x00\u07d4\xaf\xd0\x19\xff6\xa0\x91U4ki\x97H\x15\xa1\xc9\x12\xc9\n\xa4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaf\xda\xc5\xc1\xcbV\xe2E\xbfp3\x00f\xa8\x17\uabecL\u0449\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf\xdd\x1bxab\xb81~ \xf0\xe9y\xf4\xb2\xceHmv]\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf\xf1\x04Z\xdf'\xa1\xaa2\x94a\xb2M\xe1\xba\u950ai\x8b\x89\x01\u03c4\xa3\n\n\f\x00\x00\u07d4\xaf\xf1\a\x96\v~\xc3N\u0590\xb6e\x02M`\x83\x8c\x19\x0fp\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xaf\xf1\x1c\xcfi\x93\x04\xd5\xf5\x86*\xf8`\x83E\x1c&\xe7\x9a\xe5\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xaf\xf1at\nm\x90\x9f\xe9\x9cY\xa9\xb7yE\xc9\x1c\xc9\x14H\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\xaf\xfc\x99\xd5\ubd28O\xe7x\x8d\x97\xdc\xe2t\xb08$\x048\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xaf\xfe\xa0G7\"\xcb\u007f\x0e\x0e\x86\xb9\xe1\x18\x83\xbfB\x8d\x8dT\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xb0\t\x96\xb0Vn\xcb>rC\xb8\"y\x88\u0733R\xc2\x18\x99\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xb0\x1e8\x9b(\xa3\x1d\x8eI\x95\xbd\xd7\xd7\xc8\x1b\xee\xab\x1eA\x19\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb0-\x06(s3EE\u03a2\x92\x18\xe4\x05w`Y\x0ft#\x89\xac\xb6\xa1\xc7\xd9:\x88\x00\x00\u07d4\xb0/\xa2\x93\x87\xec\x12\xe3\u007fi\"\xacL\xe9\x8c[\t\xe0\xb0\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb06\x91k\xda\u03d4\xb6\x9eZ\x8ae`)u\xeb\x02a\x04\u0749\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb0A1\x0f\xe9\xee\u0586L\xed\u053e\xe5\x8d\xf8\x8e\xb4\xed<\xac\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb0U\xafL\xad\xfc\xfd\xb4%\xcfe\xbad1\a\x8f\a\xec\u056b\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xb0W\x11S\xdb\x1cN\u05ec\xae\xfe\x13\xec\xdf\xdbr\xe7\xe4\xf0j\x8a\x11\f\xffyj\xc1\x95 \x00\x00\u07d4\xb0n\xab\t\xa6\x10\u01a5=V\xa9F\xb2\xc44\x87\xac\x1d[-\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb0rI\xe0U\x04J\x91U5\x9a@)7\xbb\xd9T\xfeH\xb6\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xb0v\x182\x8a\x90\x13\a\xa1\xb7\xa0\xd0X\xfc\xd5xn\x9er\xfe\x8a\x06gI]JC0\xce\x00\x00\u07d4\xb0y\xbbM\x98f\x14:m\xa7*\xe7\xac\x00\"\x06)\x811\\\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4\xb0{\xcc\bZ\xb3\xf7)\xf2D\x00Ah7\xb6\x996\xba\x88s\x89lm\x84\xbc\xcd\xd9\xce\x00\x00\u07d4\xb0{\xcf\x1c\xc5\xd4F.Q$\xc9e\xec\xf0\xd7\r\xc2z\xcau\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\xb0|\xb9\xc1$\x05\xb7\x11\x80uC\u0113De\xf8\u007f\x98\xbd-\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb0\u007f\u07af\xf9\x1dD`\xfel\xd0\u8870\xbd\x8d\"\xa6.\x87\x8a\x01\x1d%)\xf3SZ\xb0\x00\x00\xe0\x94\xb0\x9f\xe6\xd44\x9b\x99\xbc7\x93\x80T\x02-T\xfc\xa3f\xf7\xaf\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94\xb0\xaa\x00\x95\f\x0e\x81\xfa2\x10\x17>r\x9a\xaf\x16:'\xcdq\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xb0\xacN\xfff\x80\xee\x14\x16\x9c\xda\xdb\xff\xdb0\x80Om%\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb0\xb3j\xf9\xae\xee\u07d7\xb6\xb0\"\x80\xf1\x14\xf19\x84\xea2`\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xb0\xb7y\xb9K\xfa<.\x1fX{\u031c~!x\x92\"7\x8f\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xb0\xba\xeb0\xe3\x13wlLm$t\x02\xbaAg\xaf\u0361\u0309j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb0\xbb)\xa8a\xea\x1dBME\xac\u053f\u0112\xfb\x8e\xd8\t\xb7\x89\x04V9\x18$O@\x00\x00\xe0\x94\xb0\xc1\xb1w\xa2 \xe4\x1f|t\xd0|\u0785i\xc2\x1cu\xc2\xf9\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xb0\xc7\xceL\r\xc3\u00bb\xb9\x9c\xc1\x85{\x8aE_a\x17\x11\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb0\xce\xf8\xe8\xfb\x89\x84\xa6\x01\x9f\x01\xc6y\xf2r\xbb\xe6\x8f\\w\x89\b=lz\xabc`\x00\x00\xe0\x94\xb0\xd3+\xd7\xe4\u6577\xb0\x1a\xa3\xd0Ao\x80U}\xba\x99\x03\x8a\x03s\x9f\xf0\xf6\xe6\x130\x00\x00\xe0\x94\xb0\xd3\u0247+\x85\x05n\xa0\xc0\xe6\xd1\xec\xf7\xa7~<\u6ac5\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\xb0\xe4i\u0206Y8\x15\xb3IV8Y]\xae\xf0f_\xaeb\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xb0\xe7`\xbb\a\xc0\x81wsE\xe0W\x8e\x8b\u0218\"mN;\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1\x040\x04\xec\x19A\xa8\xcfO+\x00\xb1W\x00\u076co\xf1~\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb1\x05\xdd=\x98|\xff\xd8\x13\xe9\xc8P\n\x80\xa1\xad%}V\u0189lj\xccg\u05f1\xd4\x00\x00\u07d4\xb1\x0f\u04a6G\x10/\x88\x1ft\xc9\xfb\xc3}\xa62\x94\x9f#u\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xb1\x15\xee:\xb7d\x1e\x1a\xa6\xd0\x00\xe4\x1b\xfc\x1e\xc7!\f/2\x8a\x02\xc0\xbb=\xd3\fN \x00\x00\u07d4\xb1\x17\x8a\xd4s\x83\xc3\x1c\x814\xa1\x94\x1c\xbc\xd4t\xd0bD\xe2\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xb1\x17\x95\x89\u1779\xd4\x15W\xbb\xec\x1c\xb2L\xcc-\xec\x1c\u007f\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xb1\x19\u76a9\xb9\x16Re\x81\xcb\xf5!\xefGJ\xe8M\xcf\xf4\x89O\xba\x10\x01\xe5\xbe\xfe\x00\x00\u07d4\xb1\x1f\xa7\xfb'\n\xbc\xdfZ.\xab\x95\xaa0\u013566\uffc9+^:\xf1k\x18\x80\x00\x00\u07d4\xb1$\xbc\xb6\xff\xa40\xfc\xae.\x86\xb4_'\xe3\xf2\x1e\x81\xee\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1)\xa5\xcbq\x05\xfe\x81\v\u0615\xdcr\x06\xa9\x91\xa4TT\x88\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\xe0\x94\xb1.\xd0{\x8a8\xadU\x066?\xc0z\vmy\x996\xbd\xaf\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb14\xc0\x049\x1a\xb4\x99(x3zQ\xec$/B(WB\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1?\x93\xaf0\xe8\xd7fs\x81\xb2\xb9[\xc1\xa6\x99\xd5\xe3\xe1)\x89\x16\u012b\xbe\xbe\xa0\x10\x00\x00\u07d4\xb1E\x92\x85\x86>\xa2\xdb7Y\xe5F\u03b3\xfb7a\xf5\x90\x9c\x89<\xd7*\x89@\x87\xe0\x80\x00\u07d4\xb1F\xa0\xb9%U<\xf0o\xca\xf5J\x1bM\xfe\xa6!)\aW\x89lnY\xe6|xT\x00\x00\xe0\x94\xb1Jz\xaa\x8fI\xf2\xfb\x9a\x81\x02\u05bb\xe4\u010a\xe7\xc0o\xb2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb1K\xbe\xffpr\tu\xdca\x91\xb2\xa4O\xf4\x9f&r\x87<\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\xe0\x94\xb1L\xc8\xde3\xd63\x826S\x9aH\x90 \xceFU\xa3+\u018a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb1M\xdb\x03\x86\xfb`c\x98\xb8\xccGVZ\xfa\xe0\x0f\xf1\xd6j\x89\xa1*\xff\b>f\xf0\x00\x00\u07d4\xb1S\xf8(\xdd\amJ|\x1c%t\xbb-\xee\x1aD\xa3\x18\xa8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb1T\x0e\x94\xcf\xf3F\\\xc3\u0447\xe7\xc8\u3f6f\x98FY\u2262\x15\xe4C\x90\xe33\x00\x00\u07d4\xb1X\xdbC\xfab\xd3\x0ee\xf3\u041b\xf7\x81\u01f6sr\uba89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xb1ar_\xdc\xed\xd1yR\xd5{#\xef([~K\x11i\xe8\x89\x02\xb6\xdf\xed6d\x95\x80\x00\u07d4\xb1dy\xba\x8e}\xf8\xf6>\x1b\x95\xd1I\u0345)\xd75\xc2\u0689-\xe3:j\xac2T\x80\x00\u07d4\xb1f\xe3}.P\x1a\xe7<\x84\x14+_\xfbZ\xa6U\xddZ\x99\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xb1\x83\xeb\xeeO\xcbB\xc2 \xe4wt\xf5\x9dlT\xd5\xe3*\xb1\x89V\xf7\xa9\xc3<\x04\xd1\x00\x00\u07d4\xb1\x88\a\x84D\x02~8g\x98\xa8\xaehi\x89\x19\xd5\xcc#\r\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xb1\x89j7\xe5\u0602Z-\x01vZ\xe5\xdeb\x99w\u0783R\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb1\x8eg\xa5\x05\n\x1d\xc9\xfb\x19\t\x19\xa3=\xa88\xefDP\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb1\xa2\xb4:t3\xdd\x15\v\xb8\"'\xedQ\x9c\u05b1B\u04c2\x89\x94mb\rtK\x88\x00\x00\u07d4\xb1\xc0\u040b6\xe1\x84\xf9\x95*@7\xe3\xe5:f}\a\nN\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb1\xc3(\xfb\x98\xf2\xf1\x9a\xb6do\n|\x8cVo\xdaZ\x85@\x89\x87\x86x2n\xac\x90\x00\x00\xe0\x94\xb1\xc7Qxi9\xbb\xa0\xd6q\xa6w\xa1X\u01ab\xe7&^F\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xb1\xcdK\xdf\xd1\x04H\x9a\x02n\u025dYs\a\xa0By\xf1s\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb1\u03d4\xf8\t\x15\x05\x05_\x01\n\xb4\xba\u0196\xe0\xca\x0fg\xa1\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xb1\u05b0\x1b\x94\xd8T\xfe\x8b7J\xa6^\x89\\\xf2*\xa2V\x0e\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94\xb1\u06e5%\v\xa9bWU$n\x06yg\xf2\xad/\a\x91\u078a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xb1\xe2\u0755\xe3\x9a\xe9w\\U\xae\xb1?\x12\xc2\xfa#0S\xba\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1\xe6\xe8\x10\xc2J\xb0H\x8d\xe9\xe0\x1eWH7\x82\x9f|w\u0409\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb1\xe9\xc5\xf1\xd2\x1eauzk.\xe7Y\x13\xfcZ\x1aA\x01\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\x03\u049elV\xb9&\x99\u0139-\x1fo\x84d\x8d\xc4\u03fc\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\x16\xdcY\xe2|=ry\xf5\xcd[\xb2\xbe\u03f2`n\x14\u0649\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\x1byy\xbf|\\\xa0\x1f\xa8-\xd6@\xb4\x1c9\xe6\u01bcu\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xb2#\xbf\x1f\xbf\x80H\\\xa2\xb5V}\x98\xdb{\xc3SM\xd6i\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb2-PU\xd9b15\x96\x1ej\xbd'<\x90\xde\xea\x16\xa3\xe7\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xb2-\xad\xd7\xe1\xe0R2\xa927\xba\xed\x98\xe0\u07d2\xb1\x86\x9e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb24\x03_uDF<\xe1\xe2+\xc5S\x06F\x84\xc5\x13\xcdQ\x89\r\x89\xfa=\u010d\xcf\x00\x00\u07d4\xb2G\u03dcr\xecH*\xf3\xea\xa7Ye\x8fy=g\nW\f\x891p\x8a\xe0\x04T@\x00\x00\u07d4\xb2ghA\xee\x9f-1\xc1r\xe8#\x03\xb0\xfe\x9b\xbf\x9f\x1e\t\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb2y\xc7\xd3U\u0088\x03\x92\xaa\u046a!\xee\x86|;5\a\u07c9D[\xe3\xf2\uf1d4\x00\x00\u07d4\xb2|\x1a$ L\x1e\x11\x8du\x14\x9d\xd1\t1\x1e\a\xc0s\xab\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb2\x81\x81\xa4X\xa4@\xf1\u01bb\x1d\xe8@\x02\x81\xa3\x14\x8fL5\x89\x14b\fW\xdd\xda\xe0\x00\x00\xe0\x94\xb2\x82E\x03|\xb1\x92\xf7W\x85\u02c6\xcb\xfe|\x93\r\xa2X\xb0\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xb2\x87\xf7\xf8\xd8\u00c7,\x1bXk\xcd}\n\xed\xbf~s'2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb2\x8b\xb3\x9f4fQ|\xd4o\x97\x9c\xf5\x96S\xee}\x8f\x15.\x89\x18e\x01'\xcc=\xc8\x00\x00\u07d4\xb2\x8d\xbf\xc6I\x98\x94\xf7:q\xfa\xa0\n\xbe\x0fK\xc9\u045f*\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xb2\x96\x8f}5\xf2\b\x87\x161\xc6h{?=\xae\xab\xc6al\x89\bu\xc4\u007f(\x9fv\x00\x00\u07d4\xb2\x9f[|\x190\xd9\xf9z\x11^\x06pf\xf0\xb5M\xb4K;\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb2\xa1D\xb1\xeag\xb9Q\x0f\"g\xf9\xda9\xd3\xf9=\xe2fB\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xa2\xc2\x11\x16\x12\xfb\x8b\xbb\x8e}\xd97\x8dg\xf1\xa3\x84\xf0P\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb2\xa4\x98\xf0;\xd7\x17\x8b\u0627\x89\xa0\x0fR7\xafy\xa3\xe3\xf8\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\xb2\xaa/\x1f\x8e\x93\xe7\x97\x13\xd9,\xea\x9f\xfc\xe9\xa4\n\xf9\xc8-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xb5\x16\xfd\u045e\u007f8d\xb6\xd2\xcf\x1b%*AV\xf1\xb0;\x89\x02\xe9\x83\xc7a\x15\xfc\x00\x00\u07d4\xb2\xb7\u0374\xffKa\u0577\xce\v\"p\xbb\xb5&\x97C\xec\x04\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xbd\xbe\u07d5\x90\x84v\xd7\x14\x8a7\f\u0193t6(\x05\u007f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb2\xbf\xaaX\xb5\x19l\\\xb7\xf8\x9d\xe1_G\x9d\x188\xdeq=\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4\xb2\xc5>\xfa3\xfeJ:\x1a\x80 \\s\xec;\x1d\xbc\xad\x06\x02\x89h\x01\u06b3Y\x18\x93\x80\x00\xe0\x94\xb2\xd06\x05\x15\xf1}\xab\xa9\x0f\u02ec\x82\x05\xd5i\xb9\x15\u05ac\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xb2\xd1\xe9\x9a\xf9\x121\x85\x8epe\xdd\x19\x183\r\xc4\xc7G\u054a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\xb2\u066b\x96d\xbc\xf6\xdf <4o\u0192\xfd\x9c\xba\xb9 ^\x89\x17\xbex\x97`e\x18\x00\x00\u07d4\xb2\u0777\x86\xd3yN'\x01\x87\xd0E\x1a\xd6\u0237\x9e\x0e\x87E\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\xe0\x85\xfd\xdd\x14h\xba\aA['NsN\x11#\u007f\xb2\xa9\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xb2\xe9\xd7k\xf5\x0f\xc3k\xf7\u04d4Kc\xe9\u0288\x9bi\x99h\x89\x902\xeab\xb7K\x10\x00\x00\xe0\x94\xb2\xf9\xc9r\xc1\xe9swU\xb3\xff\x1b0\x88s\x83\x969[&\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xb2\xfc\x84\xa3\xe5\nP\xaf\x02\xf9M\xa08>\u055fq\xff\x01\u05ca\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xb3\x05\v\xef\xf9\xde3\xc8\x0e\x1f\xa1R%\xe2\x8f,A:\xe3\x13\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\xb3\x11\x96qJH\xdf\xf7&\xea\x943\xcd)\x12\xf1\xa4\x14\xb3\xb3\x89\x91Hx\xa8\xc0^\xe0\x00\x00\xe0\x94\xb3\x14[tPm\x1a\x8d\x04|\xdc\xdcU9*{SPy\x9a\x8a\x1bb)t\x1c\r=]\x80\x00\u07d4\xb3 \x83H6\xd1\xdb\xfd\xa9\xe7\xa3\x18M\x1a\xd1\xfdC \xcc\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3#\u073f.\xdd\xc58.\u4efb \x1c\xa3\x93\x1b\xe8\xb48\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb3$\x00\xfd\x13\xc5P\t\x17\xcb\x03{)\xfe\"\xe7\xd5\"\x8f-\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xb3%gL\x01\xe3\xf7)\rR&3\x9f\xbe\xacg\xd2!'\x9f\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xb3(%\xd5\xf3\xdb$\x9e\xf4\xe8\\\xc4\xf31S\x95\x89v\u8f09\x1b-\xf9\xd2\x19\xf5y\x80\x00\u07d4\xb3*\xf3\xd3\xe8\xd0u4I&To.2\x88{\xf9;\x16\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb3/\x1c&\x89\xa5\xcey\xf1\xbc\x97\v1XO\x1b\xcf\"\x83\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb3<\x03#\xfb\xf9\xc2l\x1d\x8a\xc4N\xf7C\x91\u0400F\x96\u0689\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb3O\x04\xb8\xdbe\xbb\xa9\xc2n\xfcL\xe6\xef\xc5\x04\x81\xf3\xd6]\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb3U}9\xb5A\x1b\x84D__T\xf3\x8fb\xd2qM\x00\x87\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xb3X\xe9|p\xb6\x05\xb1\xd7\xd7)\u07f6@\xb4<^\xaf\xd1\xe7\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xb3^\x8a\x1c\r\xac~\x0ef\u06ecsjY*\xbdD\x01%a\x88\xcf\xceU\xaa\x12\xb3\x00\x00\xe0\x94\xb3fx\x94\xb7\x86<\x06\x8a\xd3D\x87?\xcf\xf4\xb5g\x1e\x06\x89\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb3qw1\xda\xd6Q2\xday-\x87`0\xe4j\xc2'\xbb\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3s\x1b\x04l\x8a\u0195\xa1'\xfdy\u0425\xd5\xfaj\xe6\xd1.\x89lO\xd1\xee$nx\x00\x00\u07d4\xb3|+\x9fPc{\xec\xe0\u0295\x92\b\xae\xfe\xe6F;\xa7 \x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb3\x88\xb5\xdf\xec\xd2\xc5\u4d56W|d%V\xdb\xfe'xU\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb3\x8cNS{]\xf90\xd6Zt\xd0C\x83\x1dkH[\xbd\xe4\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xb3\x919Wa\x94\xa0\x86a\x95\x15\x1f3\xf2\x14\n\xd1\u0306\u03ca\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xb3\x9fL\x00\xb2c\f\xab}\xb7)^\xf4=G\xd5\x01\xe1\u007f\u05c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb3\xa6K\x11vrOT\t\xe1AJ5#f\x1b\xae\xe7KJ\x89\x01ch\xffO\xf9\xc1\x00\x00\u07d4\xb3\xa6\xbdA\xf9\xd9\xc3 \x1e\x05\v\x87\x19\x8f\xbd\xa3\x994\"\x10\x89\xc4a\xe1\xdd\x10)\xb5\x80\x00\u07d4\xb3\xa8\xc2\xcb}5\x8eW9\x94\x1d\x94[\xa9\x04Z\x02:\x8b\xbb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3\xaeT\xfb\xa0\x9d>\xe1\u05bd\xd1\xe9W\x929\x19\x02L5\xfa\x89\x03\x8d,\xeee\xb2*\x80\x00\u07d4\xb3\xb7\xf4\x93\xb4J,\x8d\x80\xecx\xb1\xcd\xc7Ze+s\xb0l\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb3\xc2(s\x1d\x18m-\xed[_\xbe\x00Lfl\x8eF\x9b\x86\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4\xb3\xc2``\x9b\x9d\xf4\t^l]\xff9\x8e\xeb^-\xf4\x99\x85\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xb3\xc6[\x84Z\xbal\xd8\x16\xfb\xaa\xe9\x83\xe0\xe4l\x82\xaa\x86\"\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3\xc9H\x11\xe7\x17[\x14\x8b(\x1c\x1a\x84[\xfc\x9b\xb6\xfb\xc1\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb3\xe2\x0e\xb4\xde\x18\xbd\x06\x02!h\x98\x94\xbe\u5bb2SQ\xee\x89\x03\xfc\x80\xcc\xe5\x16Y\x80\x00\u07d4\xb3\xe3\xc49\x06\x98\x80\x15f\x00\u0089.D\x8dA6\xc9-\x9b\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\xe0\x94\xb3\xf8*\x87\xe5\x9a9\xd0\u0480\x8f\aQ\xebr\xc22\x9c\xdc\u014a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb3\xfc\x1dh\x81\xab\xfc\xb8\xbe\xcc\v\xb0!\xb8\xb7;r3\u0751\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xb4\x05\x94\xc4\xf3fN\xf8I\u0326\"{\x8a%\xaai\t%\xee\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb4\x1e\xaf]Q\xa5\xba\x1b\xa3\x9b\xb4\x18\u06f5O\xabu\x0e\xfb\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4$\u058d\x9d\r\x00\xce\xc1\x93\x8c\x85N\x15\xff\xb8\x80\xba\x01p\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb4%bs\x96+\xf61\xd0\x14U\\\xc1\xda\r\xcc1akI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb40g\xfep\u0675Ys\xbaX\xdcd\xdd\u007f1\x1eUBY\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb46W\xa5\x0e\xec\xbc0w\xe0\x05\xd8\xf8\xd9O7xv\xba\u0509\x01\xec\x1b:\x1f\xf7Z\x00\x00\u07d4\xb4<'\xf7\xa0\xa1\"\bK\x98\xf4\x83\x92%A\u0203l\xee,\x89&\u009eG\u0104L\x00\x00\xe0\x94\xb4A5v\x86\x9c\b\xf9Q*\xd3\x11\xfe\x92Y\x88\xa5-4\x14\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb4F\x05U$q\xa6\xee\xe4\u06abq\xff;\xb4\x13&\xd4s\xe0\x89-~=Q\xbaS\xd0\x00\x00\u07d4\xb4GW\x1d\xac\xbb>\u02f6\xd1\xcf\v\f\x8f88\xe5#$\xe2\x89\x01\xa3\x18f\u007f\xb4\x05\x80\x00\u07d4\xb4G\x83\xc8\xe5{H\a\x93\xcb\u059aE\xd9\f{O\fH\xac\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb4H\x15\xa0\xf2\x8eV\x9d\x0e\x92\x1aJ\u078f\xb2d%&Iz\x89\x03\x027\x9b\xf2\xca.\x00\x00\u07d4\xb4Im\xdb'y\x9a\"$W\xd79y\x11g(\u8844[\x89\x8d\x81\x9e\xa6_\xa6/\x80\x00\xe0\x94\xb4RL\x95\xa7\x86\x0e!\x84\x02\x96\xa6\x16$@\x19B\x1cJ\xba\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb4\\\xca\r6\x82fbh<\xf7\u0432\xfd\xach\u007f\x02\xd0\u010965\u026d\xc5\u07a0\x00\x00\u0794\xb4d@\u01d7\xa5V\xe0L}\x91\x04f\x04\x91\xf9k\xb0v\xbf\x88\xce\xc7o\x0eqR\x00\x00\u07d4\xb4j\u0386^,P\xeaF\x98\xd2\x16\xabE]\xffZ\x11\xcdr\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4m\x11\x82\xe5\xaa\xca\xff\r&\xb2\xfc\xf7/<\x9f\xfb\xcd\xd9}\x89\xaa*`<\xdd\u007f,\x00\x00\u07d4\xb4\x89!\xc9h}U\x10tE\x84\x93n\x88\x86\xbd\xbf-\xf6\x9b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\x98\xbb\x0fR\x00\x05\xb6!jD%\xb7Z\xa9\xad\xc5-b+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xb4\xb1\x1d\x10\x9f`\x8f\xa8\xed\xd3\xfe\xa9\xf8\xc3\x15d\x9a\xeb=\x11\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb4\xb1K\xf4TU\u042b\b\x035\x8bu$\xa7+\xe1\xa2\x04[\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb4\xb1\x85\xd9C\xee+Xc\x1e3\xdf\xf5\xafhT\xc1y\x93\xac\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\xbf$\u02c3hk\xc4i\x86\x9f\xef\xb0D\xb9\tqi\x93\xe2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb4\xc2\x00@\xcc\u0661\xa3(=\xa4\u0522\xf3e\x82\bC\xd7\xe2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\xc8\x17\x0f{*\xb56\xd1\u0662[\xdd :\xe1(\x8d\xc3\u0549\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb4\xd8/.i\x94?}\xe0\xf5\xf7t8y@o\xac.\x9c\xec\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xb4\xddF\f\xd0\x16rZd\xb2.\xa4\xf8\xe0n\x06gN\x03>\x8a\x01#\x1b\xb8t\x85G\xa8\x00\x00\u07d4\xb4\xddT\x99\xda\xeb%\a\xfb-\xe1\"\x97s\x1dLr\xb1k\xb0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb5\x04l\xb3\xdc\x1d\xed\xbd6E\x14\xa2\x84\x8eD\xc1\xdeN\xd1G\x8a\x03{}\x9b\xb8 @^\x00\x00\xe0\x94\xb5\b\xf9\x87\xb2\xde4\xaeL\xf1\x93\u0785\xbf\xf6\x13\x89b\x1f\x88\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xb5\tU\xaan4\x15q\x98f\b\xbd\u0211\xc2\x13\x9fT\f\u07c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb5\f\x14\x9a\x19\x06\xfa\xd2xo\xfb\x13Z\xabP\x177\xe9\xe5o\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\xb5\f\x9fW\x89\xaeD\xe2\xdc\xe0\x17\xc7\x14\xca\xf0\f\x83\x00\x84\u0089\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xb5\x14\x88,\x97\x9b\xb6B\xa8\r\u04c7T\u0578\xc8)m\x9a\a\x893\xc5I\x901r\f\x00\x00\u07d4\xb5\x1d\u0734\xddN\x8a\xe6\xbe3m\xd9eIq\xd9\xfe\xc8kA\x89\x16\xd4d\xf8=\u2500\x00\u07d4\xb5\x1eU\x8e\xb5Q/\xbc\xfa\x81\xf8\u043d\x93\x8cy\xeb\xb5$+\x89&\u009eG\u0104L\x00\x00\u07d4\xb5#\xff\xf9t\x98q\xb3S\x88C\x887\xf7\xe6\xe0\u07a9\xcbk\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb5-\xfbE\xde]t\xe3\xdf \x832\xbcW\x1c\x80\x9b\x8d\xcf2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xb55\xf8\u06c7\x9f\xc6\u007f\xecX\x82J\\\xbenT\x98\xab\xa6\x92\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xb57\xd3jp\xee\xb8\xd3\xe5\xc8\r\xe8\x15\"\\\x11X\u02d2\u0109QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xb5;\xcb\x17L%\x184\x8b\x81\x8a\xec\xe0 6E\x96Fk\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5I>\xf1srDE\xcf4\\\x03]'\x9b\xa7Y\xf2\x8dQ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb5S\xd2]kT!\xe8\x1c*\xd0^\v\x8b\xa7Q\xf8\xf0\x10\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5Tt\xbaX\xf0\xf2\xf4\x0el\xba\xbe\xd4\xea\x17n\x01\x1f\xca\u0589j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb5U\xd0\x0f\x91\x90\xcc6w\xae\xf3\x14\xac\xd7?\xdc99\x92Y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5W\xab\x949\xefP\xd27\xb5S\xf0%\b6JFj\\\x03\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5jx\x00(\x03\x9c\x81\xca\xf3{gu\xc6 \u7195Gd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5j\u04ae\xc6\xc8\xc3\xf1\x9e\x15\x15\xbb\xb7\u0751(RV\xb69\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5t\x13\x06\n\xf3\xf1N\xb4y\x06_\x1e\x9d\x19\xb3uz\xe8\u0309\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xb5uI\xbf\xbc\x9b\xdd\x18\xf76\xb2&P\xe4\x8as`\x1f\xa6\\\x89\x18-~L\xfd\xa08\x00\x00\xe0\x94\xb5w\xb6\xbe\xfa\x05N\x9c\x04\x04a\x85P\x94\xb0\x02\xd7\xf5{\u05ca\x18#\xf3\xcfb\x1d#@\x00\x00\u07d4\xb5{\x04\xfa#\xd1 ?\xae\x06\x1e\xacEB\xcb`\xf3\xa5v7\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xb5\x87\f\xe3B\xd43C36s\x03\x8bGd\xa4n\x92_>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5\x87\xb4J,\xa7\x9eK\xc1\u074b\xfd\xd4: qP\xf2\xe7\xe0\x89\",\x8e\xb3\xfff@\x00\x00\u07d4\xb5\x89gm\x15\xa0DH4B0\xd4\xff'\xc9^\xdf\x12,I\x8965\u026d\xc5\u07a0\x00\x00\u0794\xb5\x8bR\x86^\xa5]\x806\xf2\xfa\xb2`\x98\xb3R\u0283~\x18\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xb5\x90k\n\u9881X\xe8\xacU\x0e9\xda\bn\xe3\x15v#\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5\xa4g\x96\x85\xfa\x14\x19l.\x920\xc8\xc4\xe3;\xff\xbc\x10\xe2\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xb5\xa5\x89\u075f@q\u06f6\xfb\xa8\x9b?]]\xae}\x96\xc1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5\xa6\x06\xf4\xdd\u02f9G\x1e\xc6\u007fe\x8c\xaf+\x00\xees\x02^\x89\xeaun\xa9*\xfct\x00\x00\u07d4\xb5\xadQW\u0769!\xe6\xba\xfa\u0350\x86\xaes\xae\x1fa\x1d?\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5\xad\xd1\u701f}\x03\x06\x9b\xfe\x88;\n\x93\"\x10\xbe\x87\x12\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5\xba)\x91|x\xa1\xd9\xe5\xc5\xc7\x13fl\x1eA\x1d\u007fi:\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb5\xc8\x16\xa8(<\xa4\xdfh\xa1\xa7=c\xbd\x80&\x04\x88\xdf\b\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5\xca\xc5\xed\x03G}9\v\xb2g\xd4\xeb\xd4a\x01\xfb\xc2\xc3\u0689\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xb5\u037cA\x15@oR\u5a85\xd0\xfe\xa1p\u0497\x9c\u01fa\x89Hz\x9a0E9D\x00\x00\u0794\xb5\u0653M{)+\xcf`;(\x80t\x1e\xb7`(\x83\x83\xa0\x88\xe7\xc2Q\x85\x05\x06\x00\x00\u07d4\xb5\xddP\xa1]\xa3Ih\x89\nS\xb4\xf1?\xe1\xaf\b\x1b\xaa\xaa\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb5\xfa\x81\x84\xe4>\xd3\u0e2b\x91!da\xb3R\x8d\x84\xfd\t\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xb5\xfb~\xa2\xdd\xc1Y\x8bfz\x9dW\xdd9\xe8Z8\xf3]V\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb6\x00B\x97R\xf3\x99\xc8\r\a4tK\xae\n\x02.\xcag\u0189\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb6\x00\xfe\xabJ\xa9lSu\x04\xd9`W\"1Ai,\x19:\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb6\x04|\u07d3-\xb3\xe4\x04_Iv\x12#AS~\u0556\x1e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb6\x15\xe9@\x14>\xb5\u007f\x87X\x93\xbc\x98\xa6\x1b=a\x8c\x1e\x8c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb6\x1c4\xfc\xac\xdap\x1aZ\xa8p$Y\u07b0\u4b83\x8d\xf8\x8a\aiZ\x92\xc2\ro\xe0\x00\x00\xe0\x94\xb60d\xbd3U\xe6\xe0~-7p$\x12Z3wlJ\xfa\x8a\b7Z*\xbc\xca$@\x00\x00\u07d4\xb65\xa4\xbcq\xfb(\xfd\xd5\xd2\xc3\"\x98:V\u0084Bni\x89\t79SM(h\x00\x00\u07d4\xb6F\u07d8\xb4\x94BtkaR\\\x81\xa3\xb0K\xa3\x10bP\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb6YA\xd4LP\xd2Ffg\r6Gf\xe9\x91\xc0.\x11\u0089 \x86\xac5\x10R`\x00\x00\xe0\x94\xb6[\u05c0\xc7CA\x15\x16 'VR#\xf4NT\x98\xff\x8c\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\xb6d\x11\xe3\xa0-\xed\xb7&\xfay\x10}\xc9\v\xc1\xca\xe6MH\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb6fu\x14.1\x11\xa1\xc2\xea\x1e\xb2A\x9c\xfaB\xaa\xf7\xa24\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xb6o\x92\x12K^c\x03XY\xe3\x90b\x88i\xdb\u07a9H^\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xb6rsJ\xfc\xc2$\xe2\xe6\t\xfcQ\xd4\xf0Ys'D\xc9H\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\xe0\x94\xb6w\x1b\v\xf3B\u007f\x9a\xe7\xa9>|.a\xeec\x94\x1f\xdb\b\x8a\x03\xfb&i)T\xbf\xc0\x00\x00\u07d4\xb6z\x80\xf1p\x19}\x96\xcd\xccJ\xb6\u02e6'\xb4\xaf\xa6\xe1,\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xb6\x88\x99\xe7a\rL\x93\xa255\xbc\xc4H\x94[\xa1fo\x1c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb6\xa8)3\xc9\xea\u06bd\x98\x1e]m`\xa6\x81\x8f\xf8\x06\xe3k\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xb6\xaa\u02cc\xb3\v\xab*\xe4\xa2BF&\xe6\xe1+\x02\xd0F\x05\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb6\xb3J&?\x10\xc3\xd2\xec\xeb\n\xccU\x9a{*\xb8\\\xe5e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb6\xbf\xe1\xc3\xef\x94\xe1\x84o\xb9\xe3\xac\xfe\x9bP\xc3\xe9\x06\x923\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xb6\xcdt2\xd5\x16\x1b\xe7\x97h\xadE\xde>Dz\a\x98 c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb6\xceM\xc5`\xfcs\xdci\xfbzb\xe3\x88\xdb~r\xeavO\x894]\xf1i\xe9\xa3X\x00\x00\u07d4\xb6\xde\u03c2\x96\x98\x19\xba\x02\xde)\xb9\xb5\x93\xf2\x1bd\xee\xda\x0f\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\xe0\x94\xb6\xe6\xc3\"+ko\x9b\xe2\x87]*\x89\xf1'\xfbd\x10\x0f\xe2\x8a\x01\xb2\x1dS#\xcc0 \x00\x00\u07d4\xb6\xe8\xaf\xd9=\xfa\x9a\xf2\u007f9\xb4\xdf\x06\ag\x10\xbe\xe3\u07eb\x89\x01Z\xf1\u05cbX\xc4\x00\x00\xe0\x94\xb6\xf7\x8d\xa4\xf4\xd0A\xb3\xbc\x14\xbc[\xa5\x19\xa5\xba\f2\xf1(\x8a$}\xd3,?\xe1\x95\x04\x80\x00\xe0\x94\xb6\xfb9xbP\b\x14&\xa3B\xc7\rG\xeeR\x1e[\xc5c\x8a\x03-&\xd1.\x98\v`\x00\x00\u07d4\xb7\r\xba\x93\x91h+J6Nw\xfe\x99%c\x01\xa6\xc0\xbf\x1f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\x16#\xf3Q\a\xcft1\xa8?\xb3\xd2\x04\xb2\x9e\u0c67\xf4\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xb7\x1a\x13\xba\x8e\x95\x16{\x803\x1bR\u059e7\x05O\xe7\xa8&\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\x1bb\xf4\xb4H\xc0+\x12\x01\xcb^9J\xe6'\xb0\xa5`\xee\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb7\" \xad\xe3d\xd06\x9f--\xa7\x83\xcaGM{\x9b4\u0389\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\xe0\x94\xb7#\r\x1d\x1f\xf2\xac\xa3f\x969\x14\xa7\x9d\xf9\xf7\xc5\xea,\x98\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\xb7$\n\U000af433<\b\xae\x97d\x10>5\xdc\xe3c\x84(\x8a\x01\xca\xdd/\xe9hnc\x80\x00\u07d4\xb7'\xa9\xfc\x82\xe1\xcf\xfc\\\x17_\xa1HZ\x9b\xef\xa2\u037d\u04496'\xe8\xf7\x127<\x00\x00\u07d4\xb7,*\x01\x1c\r\xf5\x0f\xbbn(\xb2\n\xe1\xaa\xd2\x17\x88g\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb78-7\xdb\x03\x98\xacrA\f\xf9\x81=\xe9\xf8\xe1\uc36d\x8966\xc2^f\xec\xe7\x00\x00\u07d4\xb7;O\xf9\x9e\xb8\x8f\u061b\vmW\xa9\xbc3\x8e\x88o\xa0j\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xb7=jwU\x9c\x86\xcfet$)\x039K\xac\xf9n5p\x89\x04\xf1\xa7|\xcd;\xa0\x00\x00\u07d4\xb7Cr\xdb\xfa\x18\x1d\xc9$/9\xbf\x1d71\xdf\xfe+\xda\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7G\x9d\xabP\"\xc4\xd5\u06ea\xf8\xde\x17\x1bN\x95\x1d\u0464W\x89\x04V9\x18$O@\x00\x00\u07d4\xb7I\xb5N\x04\u0571\x9b\xdc\xed\xfb\x84\xdaw\x01\xabG\x8c'\xae\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xb7N\xd2f`\x01\xc1c3\xcfz\xf5\x9eJ=H`6;\x9c\x89\n~\xbd^Cc\xa0\x00\x00\u07d4\xb7QI\xe1\x85\xf6\xe3\x92pWs\x90s\xa1\x82*\xe1\xcf\r\xf2\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xb7S\xa7_\x9e\xd1\v!d:\n=\xc0Qz\xc9k\x1a@h\x89\x15\xc8\x18[,\x1f\xf4\x00\x00\xe0\x94\xb7V\xadR\xf3\xbft\xa7\xd2LgG\x1e\b\x87Ci6PL\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb7Wn\x9d1M\xf4\x1e\xc5Pd\x94):\xfb\x1b\xd5\xd3\xf6]\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb7X\x89o\x1b\xaa\x86O\x17\xeb\xed\x16\xd9S\x88o\xeeh\xaa\xe6\x8965\u026d\xc5\u07a0\x00\x00\u0794\xb7h\xb5#N\xba:\x99h\xb3Mm\xdbH\x1c\x84\x19\xb3e]\x88\xcf\xceU\xaa\x12\xb3\x00\x00\u07d4\xb7\x82\xbf\xd1\xe2\xdep\xf4gdo\x9b\xc0\x9e\xa5\xb1\xfc\xf4P\xaf\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xb7\xa2\xc1\x03r\x8bs\x05\xb5\xaen\x96\x1c\x94\xee\x99\xc9\xfe\x8e+\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xb7\xa3\x1a|8\xf3\xdb\t2.\xae\x11\xd2'!A\xea\"\x99\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7\xa6y\x1c\x16\xebN!b\xf1Ke7\xa0+=c\xbf\xc6\x02\x89*Rc\x91\xac\x93v\x00\x00\u07d4\xb7\xa7\xf7|4\x8f\x92\xa9\xf1\x10\fk\xd8)\xa8\xacm\u007f\u03d1\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xb7\xc0w\x94ft\xba\x93A\xfbLtz]P\xf5\xd2\xdad\x15\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb7\xc0\xd0\xcc\vM4-@b\xba\xc6$\xcc\xc3\xc7\f\xc6\xda?\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb7\xc9\xf1+\x03\x8esCm\x17\xe1\xc1/\xfe\x1a\xec\u0373\xf5\x8c\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\xb7\xcck\x1a\xcc2\u0632\x95\xdfh\xed\x9d^`\xb8\xf6L\xb6{\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xb7\xcehK\t\xab\xdaS8\x9a\x87Si\xf7\x19X\xae\xac;\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7\xd1.\x84\xa2\xe4\u01264Z\xf1\xdd\x1d\xa9\xf2PJ*\x99n\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\xd2R\xee\x94\x02\xb0\xee\xf1D)_\x0ei\xf0\xdbXl\bq\x89#\xc7W\a+\x8d\xd0\x00\x00\xe0\x94\xb7\u0541\xfe\n\xf1\xec8?;\xce\x00\xaf\x91\x99\xf3\xcf_\xe0\xcc\xe2\x8c\xd1J\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\xb8R\x18\xf3B\xf8\x01.\u069f'Nc\xce!R\xb2\xdc\xfd\xab\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb8UP\x10wn<\\\xb3\x11\xa5\xad\xee\xfe\x9e\x92\xbb\x9ad\xb9\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xb8_&\xdd\x0er\xd9\u009e\xba\xf6\x97\xa8\xafwG,+X\xb5\x8a\x02\x85\x19\xac\xc7\x19\fp\x00\x00\u07d4\xb8_\xf0>{_\xc4\"\x98\x1f\xae^\x99A\xda\xcb\u06bau\x84\x89Hz\x9a0E9D\x00\x00\xe0\x94\xb8f\a\x02\x1bb\xd3@\xcf&R\xf3\xf9_\xd2\xdcgi\x8b\u07ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb8}\xe1\xbc\u0492i\xd5!\xb8v\x1c\u00dc\xfbC\x19\xd2\xea\u054965\u026d\xc5\u07a0\x00\x00\u07d4\xb8\u007fSv\xc2\xde\vl\xc3\xc1y\xc0`\x87\xaaG=kFt\x89Hz\x9a0E9D\x00\x00\u07d4\xb8\x84\xad\u060d\x83\xdcVJ\xb8\xe0\xe0,\xbd\xb69\x19\xae\xa8D\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\x8a7\xc2\u007fx\xa6\x17\xd5\xc0\x91\xb7\u0577:7a\xe6_*\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\x94x\"\u056c\u79ad\x83&\xe9T\x96\"\x1e\v\xe6\xb7=\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb8\x9c\x03n\xd7\u0112\x87\x99!\xbeA\xe1\f\xa1i\x81\x98\xa7L\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xb8\x9fF2\xdfY\t\xe5\x8b*\x99d\xf7O\xeb\x9a;\x01\xe0\u014a\x04\x88u\xbc\xc6\xe7\xcb\xeb\x80\x00\u07d4\xb8\xa7\x9c\x84\x94^G\xa9\xc3C\x86\x83\u05b5\x84,\xffv\x84\xb1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\xa9y5'Y\xba\t\xe3Z\xa5\x93]\xf1u\xbf\xf6x\xa1\b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb8\xab9\x80[\xd8!\x18Ol\xbd=$s4{\x12\xbf\x17\\\x89\x06hZ\xc1\xbf\xe3,\x00\x00\xe0\x94\xb8\xac\x11}\x9f\r\xba\x80\x90\x14E\x82:\x92\x11\x03\xa51o\x85Zew\x9d\x1b\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4\xb9\xe9\f\x11\x92\xb3\xd5\xd3\xe3\xab\a\x00\xf1\xbfe_]\xd44z\x89\x1b\x19\xe5\vD\x97|\x00\x00\u07d4\xb9\xfd83\xe8\x8e|\xf1\xfa\x98y\xbd\xf5Z\xf4\xb9\x9c\xd5\xce?\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xba\x02I\xe0\x1d\x94[\xef\x93\xee^\xc6\x19%\xe0<\\\xa5\t\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xba\x0f9\x02;\xdb)\xeb\x18b\xa9\xf9\x05\x9c\xab]0nf/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\x10\xf2vB\x90\xf8uCCr\xf7\x9d\xbfq8\x01\u02ac\x01\x893\xc5I\x901r\f\x00\x00\u07d4\xba\x151\xfb\x9ey\x18\x96\xbc\xf3\xa8\x05X\xa3Y\xf6\xe7\xc1D\xbd\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xba\x17m\xbe2I\xe3E\xcdO\xa9g\xc0\xed\x13\xb2LG\u5189\x15\xae\xf9\xf1\xc3\x1c\u007f\x00\x00\xe0\x94\xba\x1f\x0e\x03\u02da\xa0!\xf4\xdc\xeb\xfa\x94\xe5\u0209\xc9\u01fc\x9e\x8a\x06\u0450\xc4u\x16\x9a \x00\x00\u07d4\xba\x1f\xca\xf2#\x93~\xf8\x9e\x85gU\x03\xbd\xb7\xcaj\x92\x8bx\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\xe0\x94\xba$\xfcCgS\xa79\xdb,\x8d@\xe6\xd4\xd0LR\x8e\x86\xfa\x8a\x02\xc0\xbb=\xd3\fN \x00\x00\u07d4\xbaB\xf9\xaa\xceL\x18E\x04\xab\xf5BWb\xac\xa2oq\xfb\u0709\x02\a\a}\u0627\x9c\x00\x00\u07d4\xbaF\x9a\xa5\u00c6\xb1\x92\x95\u0521\xb5G;T\x03S9\f\x85\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbad@\xae\xb3s{\x8e\xf0\xf1\xaf\x9b\f\x15\xf4\xc2\x14\xff\xc7\u03c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbam1\xb9\xa2a\xd6@\xb5\u07a5\x1e\xf2\x16,1\t\xf1\uba0a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xbap\xe8\xb4u\x9c\f<\x82\xcc\x00\xacN\x9a\x94\xdd[\xaf\xb2\xb8\x890C\xfa3\xc4\x12\xd7\x00\x00\u07d4\xba\x8ac\xf3\xf4\r\u4a03\x88\xbcP!/\xea\x8e\x06O\xbb\x86\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\x8eF\u059d.#C\xd8l`\xd8,\xf4, A\xa0\xc1\u0089\x05k\xc7^-c\x10\x00\x00\u07d4\xba\xa4\xb6L+\x15\xb7\x9f_ BF\xfdp\xbc\xbd\x86\xe4\xa9*\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xba\u0212,J\xcc},\xb6\xfdY\xa1N\xb4\\\xf3\xe7\x02!K\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xba\xd25\xd5\b]\u01f0h\xa6|A&w\xb0>\x186\x88L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\xd4B^\x17\x1c>r\x97^\xb4j\xc0\xa0\x15\xdb1Z]\x8f\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xba\xdc*\xef\x9fYQ\xa8\u05cak5\xc3\u0433\xa4\xe6\xe2\xe79\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xba\xdeCY\x9e\x02\xf8OL0\x14W\x1c\x97k\x13\xa3le\xab\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xba\xe9\xb8/r\x99c\x14\be\x9d\xd7N\x89\x1c\xb8\xf3\x86\x0f\xe5\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xbb\x03f\xa7\u03fd4E\xa7\r\xb7\xfeZ\xe3H\x85uO\xd4h\x8a\x01M\xef,B\xeb\xd6@\x00\x00\u07d4\xbb\aj\xac\x92 \x80i\xea1\x8a1\xff\x8e\xeb\x14\xb7\xe9\x96\xe3\x89\b\x13\xcaV\x90m4\x00\x00\u07d4\xbb\bW\xf1\xc9\x11\xb2K\x86\u0227\x06\x81G?\u6aa1\xcc\xe2\x89\x05k\xc7^-c\x10\x00\x00\u0794\xbb\x19\xbf\x91\u02edt\xcc\xeb_\x81\x1d\xb2~A\x1b\xc2\xea\x06V\x88\xf4?\xc2\xc0N\xe0\x00\x00\xe0\x94\xbb'\u01a7\xf9\x10uGZ\xb2)a\x90@\xf8\x04\xc8\xeczj\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xbb7\x1cr\xc9\xf01l\xea+\xd9\xc6\xfb\xb4\a\x9ewT)\xef\x89_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94\xbb;\x01\v\x18\xe6\xe2\xbe\x115\x87\x10&\xb7\xba\x15\xea\x0f\xde$\x8a\x02 |\x800\x9bwp\x00\x00\xe0\x94\xbb;\x90\x05\xf4o\xd2\xca;0\x16%\x99\x92\x8cw\xd9\xf6\xb6\x01\x8a\x01\xb1\xae\u007f+\x1b\xf7\xdb\x00\x00\u07d4\xbb?\xc0\xa2\x9c\x03Mq\b\x12\xdc\xc7u\xc8\u02b9\u048diu\x899\xd4\xe8D\xd1\xcf_\x00\x00\u07d4\xbbH\xea\xf5\x16\xce-\xec>A\xfe\xb4\xc6y\xe4\x95vA\x16O\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\xbbKJKT\x80p\xffAC,\x9e\b\xa0\xcao\xa7\xbc\x9fv\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\xbbV\xa4\x04r<\xff \xd0hT\x88\xb0Z\x02\xcd\xc3Z\xac\xaa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbba\x8e%\"\x1a\u0667@\xb2\x99\xed\x14\x06\xbc94\xb0\xb1m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbba\xa0K\xff\xd5|\x10G\rE\u00d1\x03\xf6FP4v\x16\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbbh#\xa1\xbd\x81\x9f\x13QU8&J-\xe0R\xb4D\"\b\x89\x01ch\xffO\xf9\xc1\x00\x00\u07d4\xbbl(J\xac\x8ai\xb7\\\u0770\x0f(\xe1EX;V\xbe\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xbbu\xcbPQ\xa0\xb0\x94KFs\xcau*\x97\x03\u007f|\x8c\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbb\x99;\x96\xee\x92Z\xda}\x99\u05c6W=?\x89\x18\f\u3a89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xa3\u0180\x04$\x8eH\x95s\xab\xb2t6w\x06k$\u0227\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xa4\xfa\xc3\xc4 9\xd8(\xe7B\xcd\xe0\xef\xff\xe7t\x94\x1b9\x89lj\u04c2\xd4\xfba\x00\x00\u07d4\xbb\xa8\xab\"\xd2\xfe\xdb\xcf\xc6?hL\b\xaf\xdf\x1c\x17P\x90\xb5\x89\x05_)\xf3~N;\x80\x00\u07d4\xbb\xa9v\xf1\xa1!_u\x12\x87\x18\x92\xd4_pH\xac\xd3V\u0209lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbb\xab\x00\v\x04\b\xed\x01Z7\xc0GG\xbcF\x1a\xb1N\x15\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xbb\xab\xf6d;\xebK\xd0\x1c\x12\v\xd0Y\x8a\t\x87\xd8)g\u0449\xb52\x81x\xad\x0f*\x00\x00\u07d4\xbb\xb4\xee\x1d\x82\xf2\xe1VD,\xc938\xa2\xfc(o\xa2\x88d\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\xbb\xb5\xa0\xf4\x80,\x86H\x00\x9e\x8ai\x98\xaf5,\u0787TO\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4\xbb\xb6C\xd2\x18{6J\xfc\x10\xa6\xfd6\x8d}U\xf5\r\x1a<\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbb\xb8\xff\xe4?\x98\u078e\xae\x18F#\xaeRd\xe4$\u0438\u05c9\x05\xd5?\xfd\xe9(\b\x00\x00\u07d4\xbb\xbdn\u02f5u(\x91\xb4\u03b3\xcc\xe7:\x8fGpY7o\x89\x01\xf3\x99\xb1C\x8a\x10\x00\x00\u07d4\xbb\xbf9\xb1\xb6y\x95\xa4\"APO\x97\x03\u04a1JQV\x96\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xbb\xc8\xea\xffc~\x94\xfc\u014d\x91\xdb\\\x89\x12\x1d\x06\xe1/\xff\x98\x80\x00\u07d4\xbc\u065e\xdc!`\xf2\x10\xa0^:\x1f\xa0\xb0CL\xed\x00C\x9b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbc\u07ec\xb9\xd9\x02<4\x17\x18.\x91\x00\xe8\xea\x1d73\x93\xa3\x89\x034-`\xdf\xf1\x96\x00\x00\u07d4\xbc\xe1>\"2*\u03f3U\xcd!\xfd\r\xf6\f\xf9:\xdd&\u0189\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xbc\xe4\x04u\xd3E\xb0q-\xeep=\x87\xcdvW\xfc\u007f;b\x8a\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\xbc\xed\xc4&|\u02c9\xb3\x1b\xb7d\xd7!\x11q\x00\x8d\x94\xd4M\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbc\xfc\x98\xe5\xc8+j\xdb\x18\n?\xcb\x12\v\x9av\x90\xc8j?\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbd\x04;g\xc6>`\xf8A\xcc\xca\x15\xb1)\xcd\xfee\x90\xc8\xe3\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbd\x04\u007f\xf1\xe6\x9c\u01b2\x9a\xd2d\x97\xa9\xa6\xf2z\x90?\xc4\u0749.\xe4IU\b\x98\xe4\x00\x00\u07d4\xbd\b\xe0\xcd\xde\xc0\x97\xdby\x01\ua05a=\x1f\xd9\u0789Q\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbd\t\x12l\x89\x1cJ\x83\x06\x80Y\xfe\x0e\x15ylFa\xa9\xf4\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xbd\f\\\u05d9\xeb\u0106B\xef\x97\xd7N\x8eB\x90d\xfe\u4489\x11\xac(\xa8\xc7)X\x00\x00\u07d4\xbd\x17\xee\xd8+\x9a%\x92\x01\x9a\x1b\x1b<\x0f\xba\xd4\\@\x8d\"\x89\r\x8drkqw\xa8\x00\x00\u07d4\xbd\x18\x037\v\u0771)\xd29\xfd\x16\xea\x85&\xa6\x18\x8a\u5389\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbd+p\xfe\xcc7d\x0fiQO\xc7\xf3@IF\xaa\xd8k\x11\x89A\rXj \xa4\xc0\x00\x00\u07d4\xbd0\x97\xa7\x9b<\r.\xbf\xf0\xe6\xe8j\xb0\xed\xad\xbe\xd4p\x96\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xbd2]@)\xe0\xd8r\x9fm9\x9cG\x82$\xae\x9ez\xe4\x1e\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xbdC*9\x16$\x9bG$):\xf9\x14nI\xb8(\n\u007f*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbdG\xf5\xf7n;\x93\x0f\xd9HR\t\xef\xa0\xd4v=\xa0uh\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbdK`\xfa\xect\n!\xe3\a\x13\x91\xf9j\xa54\xf7\xc1\xf4N\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xbdK\u0571\"\xd8\xef{|\x8f\x06gE\x03 \xdb!\x16\x14.\x89 \x86\xac5\x10R`\x00\x00\u07d4\xbdQ\xee.\xa1C\u05f1\u05b7~~D\xbd\xd7\xda\x12\U00105b09G~\x06\u0332\xb9(\x00\x00\u07d4\xbdY\tN\aO\x8dy\x14*\xb1H\x9f\x14\x8e2\x15\x1f \x89\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbdZ\x8c\x94\xbd\x8b\xe6G\x06D\xf7\f\x8f\x8a3\xa8\xa5\\cA\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbd^G:\xbc\xe8\xf9zi2\xf7|/\xac\xaf\x9c\xc0\xa0\x05\x14\x89<\x92X\xa1\x06\xa6\xb7\x00\x00\u07d4\xbd_F\u02ab,=K(\x93\x96\xbb\xb0\u007f *\x06\x11>\xd4\xc3\xfb\xa1\xa8\x91;\x19@~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\x9eV\xe9\x02\xf4\xbe\x1f\xc8v\x8d\x808\xba\xc6>*\u02ff\x8e\x8965f3\xeb\xd8\xea\x00\x00\u07d4\xbd\xa4\xbe1~~K\xed\x84\xc0I^\xee2\xd6\a\xec8\xcaR\x89}2'yx\xefN\x80\x00\u07d4\xbd\xb6\v\x82:\x11s\xd4Z\a\x92$_\xb4\x96\xf1\xfd3\x01\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xba\xf6CM@\xd65[\x1e\x80\xe4\f\u012b\x9ch\xd9a\x16\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbd\xc0,\xd43\f\x93\xd6\xfb\xdaOm\xb2\xa8]\xf2/C\xc23\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xc4aF+c\"\xb4b\xbd\xb3?\"y\x9e\x81\b\xe2A}\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xbd\xc79\xa6\x99p\v.\x8e,JL{\x05\x8a\x0eQ=\u07be\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xc7Hs\xaf\x92+\x9d\xf4t\x85;\x0f\xa7\xff\v\xf8\xc8&\x95\x89\xd8\xc9F\x00c\xd3\x1c\x00\x00\u07d4\xbd\xca*\x0f\xf3E\x88\xafb_\xa8\xe2\x8f\xc3\x01Z\xb5\xa3\xaa\x00\x89~\xd7?w5R\xfc\x00\x00\u07d4\xbd\xd3%N\x1b:m\xc6\xcc,i}Eq\x1a\xca!\xd5\x16\xb2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbd\u07e3M\x0e\xbf\x1b\x04\xafS\xb9\x9b\x82IJ\x9e=\x8a\xa1\x00\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xbd\xe4\xc7?\x96\x9b\x89\xe9\u03aef\xa2\xb5\x18DH\x0e\x03\x8e\x9a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbd\xe9xj\x84\xe7[H\xf1\x8erm\u05cdp\xe4\xaf>\xd8\x02\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4\xbd\xed\x11a/\xb5\xc6\u0699\xd1\xe3\x0e2\v\xc0\x99Tf\x14\x1e\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xbd\xed~\a\xd0q\x1ehM\xe6Z\u0232\xabW\xc5\\\x1a\x86E\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\xbd\xf6\x93\xf83\xc3\xfeG\x17S\x18G\x88\xebK\xfeJ\xdc?\x96\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbd\xf6\xe6\x8c\f\xd7X@\x80\xe8G\xd7,\xbb#\xaa\xd4j\xeb\x1d\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbe\n/8_\t\xdb\xfc\xe9g2\xe1+\xb4\n\xc3I\x87\x1b\xa8\x89WL\x11^\x02\xb8\xbe\x00\x00\u07d4\xbe\f*\x80\xb9\xde\bK\x17(\x94\xa7l\xf4szOR\x9e\x1a\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xbe\x1c\xd7\xf4\xc4r\a\th\xf3\xbd\xe2h6k!\xee\xea\x83!\x89\xe9\x1a|\u045f\xa3\xb0\x00\x00\u07d4\xbe#F\xa2\u007f\xf9\xb7\x02\x04OP\r\xef\xf2\xe7\xff\xe6\x82EA\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbe$q\xa6\u007f`G\x91\x87r\xd0\xe3h9%^\xd9\u0591\xae\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbe+\"\x80R7h\xea\x8a\xc3\\\xd9\xe8\x88\xd6\nq\x93\x00\u0509lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbe+2nx\xed\x10\xe5P\xfe\xe8\xef\xa8\xf8\a\x03\x96R/Z\x8a\bW\xe0\xd6\xf1\xdav\xa0\x00\x00\xe0\x94\xbe0Zyn3\xbb\xf7\xf9\xae\xaee\x12\x95\x90f\xef\xda\x10\x10\x8a\x02M\xceT\xd3J\x1a\x00\x00\x00\u07d4\xbeG\x8e\x8e=\xdek\xd4\x03\xbb-\x1ce|C\x10\xee\x19'#\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\xbeN}\x98?.*ck\x11\x02\xecp9\xef\xeb\xc8B\u9349\x03\x93\xef\x1aQ'\xc8\x00\x00\u07d4\xbeO\xd0sap\"\xb6\u007f\\\x13I\x9b\x82\u007fv69\xe4\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbeRZ3\xea\x91aw\xf1r\x83\xfc\xa2\x9e\x8b5\v\u007fS\v\x89\x8f\x01\x9a\xafF\xe8x\x00\x00\u07d4\xbeS2/C\xfb\xb5\x84\x94\xd7\xcc\xe1\x9d\xda'+$P\xe8'\x89\n\xd7\u03afB\\\x15\x00\x00\u07d4\xbeS\x82F\xddNo\f \xbfZ\xd17<;F:\x13\x1e\x86\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbeZ`h\x99\x98c\x9a\xd7[\xc1\x05\xa3qt>\xef\x0fy@\x89\x1b2|s\xe1%z\x00\x00\u07d4\xbe\\\xba\x8d7By\x86\xe8\xca&\x00\xe8X\xbb\x03\xc3YR\x0f\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\xbe`\x03~\x90qJK\x91~a\xf1\x93\xd84\x90g\x03\xb1:\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xbec:77\xf6\x849\xba\xc7\xc9\nR\x14 X\ue38ao\x894\n\xad!\xb3\xb7\x00\x00\x00\xe0\x94\xbee\x9d\x85\xe7\xc3O\x883\xea\u007fH\x8d\xe1\xfb\xb5\xd4\x14\x9b\xef\x8a\x01\xeb\xd2:\xd9\u057br\x00\x00\u07d4\xbes'M\x8cZ\xa4J<\xbe\xfc\x82c\xc3{\xa1!\xb2\n\u04c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbe\x86\u0430C\x84\x19\u03b1\xa081\x927\xbaR\x06\xd7.F\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xbe\x8d\u007f\x18\xad\xfe]l\xc7u9I\x89\xe1\x93\f\x97\x9d\x00}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbe\x91\x86\xc3JRQJ\xbb\x91\a\x86\x0fgO\x97\xb8!\xbd[\x89\x1b\xa0\x1e\xe4\x06\x03\x10\x00\x00\u07d4\xbe\x93W\x93\xf4[p\xd8\x04]&T\xd8\xdd:\xd2K[a7\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xbe\x98\xa7\u007f\xd4\x10\x97\xb3OY\xd7X\x9b\xaa\xd0!e\x9f\xf7\x12\x890\xca\x02O\x98{\x90\x00\x00\u07d4\xbe\x9b\x8c4\xb7\x8e\xe9G\xff\x81G.\xdaz\xf9\xd2\x04\xbc\x84f\x89\b!\xab\rD\x14\x98\x00\x00\u07d4\xbe\xa0\r\xf1pg\xa4:\x82\xbc\x1d\xae\xca\xfbl\x140\x0e\x89\xe6\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xbe\xa0\xaf\xc9:\xae!\b\xa3\xfa\xc0Yb;\xf8o\xa5\x82\xa7^\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xbe\xb35\x8cP\u03dfu\xff\xc7mD<,\u007fU\aZ\x05\x89\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xbe\xb4\xfd1UYC`E\u0739\x9dI\xdc\xec\x03\xf4\fB\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\xbe\xc2\xe6\xde9\xc0|+\xaeUj\u03fe\xe2\xc4r\x8b\x99\x82\xe3\x89\x1f\x0f\xf8\xf0\x1d\xaa\xd4\x00\x00\u07d4\xbe\xc6d\x0fI\t\xb5\x8c\xbf\x1e\x80cB\x96\x1d`u\x95\tl\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xbe\xc8\xca\xf7\xeeIF\x8f\xeeU.\xff:\xc5#N\xb9\xb1}B\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbe\xce\xf6\x1c\x1cD+\xef|\xe0Ks\xad\xb2I\xa8\xba\x04~\x00\x896;V\u00e7T\xc8\x00\x00\u0794\xbe\xd4d\x9d\xf6F\u2052)\x03-\x88hUo\xe1\xe0S\u04c8\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\xbe\xd4\xc8\xf0\x06\xa2|\x1e_|\xe2\x05\xdeu\xf5\x16\xbf\xb9\xf7d\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xbe\xe8\u0430\bB\x19T\xf9-\x00\r9\x0f\xb8\xf8\xe6X\xea\xee\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbe\xec\u05af\x90\f\x8b\x06J\xfc\xc6\a?-\x85\u055a\xf1\x19V\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbe\xef\x94!8y\xe0&\"\x14+\xeaa)\tx\x93\x9a`\u05ca\x016\x85{2\xad\x86\x04\x80\x00\xe0\x94\xbe\xf0}\x97\xc3H\x1f\x9dj\xee\x1c\x98\xf9\xd9\x1a\x18\n2D+\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xbe\xfbD\x8c\f_h?\xb6~\xe5p\xba\xf0\xdbV\x86Y\x97Q\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbf\x05\a\f,4!\x93\x11\xc4T\x8b&\x14\xa48\x81\r\xedm\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbf\x05\xff^\xcf\r\xf2\u07c8wY\xfb\x82t\xd928\xac&}\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\xbf\t\xd7pH\xe2p\xb6b3\x0e\x94\x86\xb3\x8bC\xcdx\x14\x95\x8a\\S\x9b{\xf4\xff(\x80\x00\x00\u07d4\xbf\x17\xf3\x97\xf8\xf4o\x1b\xaeE\u0447\x14\x8c\x06\xee\xb9Y\xfaM\x896I\u0156$\xbb0\x00\x00\u07d4\xbf\x186A\xed\xb8\x86\xce`\xb8\x19\x02a\xe1OB\xd9<\xce\x01\x89\x01[5W\xf1\x93\u007f\x80\x00\u07d4\xbf*\xeaZ\x1d\xcfn\u04f5\xe829D\xe9\x83\xfe\xdf\u046c\xfb\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xbf@\x96\xbcT}\xbf\xc4\xe7H\t\xa3\x1c\x03\x9e{8\x9d^\x17\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xbfI\xc1H\x981eg\u0637\t\xc2\xe5\x05\x94\xb3f\xc6\u04cc\x89'\xbf8\xc6TM\xf5\x00\x00\u07d4\xbfLs\xa7\xed\xe7\xb1d\xfe\a!\x14\x846T\xe4\xd8x\x1d\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xbfP\xce.&K\x9f\xe2\xb0h0az\xed\xf5\x02\xb25\x1bE\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbfY\xae\xe2\x81\xfaC\xfe\x97\x19CQ\xa9\x85~\x01\xa3\xb8\x97\xb2\x89 \x86\xac5\x10R`\x00\x00\u07d4\xbfh\u048a\xaf\x1e\xee\xfe\xf6F\xb6^\x8c\xc8\u0450\xf6\xc6\u069c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbfi%\xc0\aQ\x00\x84@\xa6s\x9a\x02\xbf+l\u06ab^:\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbfw\x01\xfcb%\u0561x\x15C\x8a\x89A\xd2\x1e\xbc]\x05\x9d\x89e\xea=\xb7UF`\x00\x00\u07d4\xbf\x8b\x80\x05\xd66\xa4\x96d\xf7Bu\xefBC\x8a\xcde\xac\x91\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbf\x92A\x8a\fl1$M\"\x02`\xcb>\x86}\u05f4\xefI\x89\x05i\x00\xd3<\xa7\xfc\x00\x00\u07d4\xbf\x9a\xcdDE\xd9\xc9UF\x89\u02bb\xba\xb1\x88\x00\xff\x17A\u008965\u026d\xc5\u07a0\x00\x00\u07d4\xbf\x9f'\x1fz~\x12\xe3m\xd2\xfe\x9f\xac\xeb\xf3\x85\xfeaB\xbd\x89\x03f\xf8O{\xb7\x84\x00\x00\u07d4\xbf\xa8\xc8X\xdf\x10,\xb1$!\x00\x8b\n1\xc4\xc7\x19\n\xd5`\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbf\xae\xb9\x10ga}\u03cbD\x17+\x02\xafaVt\x83]\xba\x89\b\xb5\x9e\x88H\x13\b\x80\x00\xe0\x94\xbf\xb0\xea\x02\xfe\xb6\x1d\xec\x9e\"\xa5\a\tY3\x02\x99\xc40r\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xbf\xbc\xa4\x18\xd3R\x9c\xb3\x93\b\x10b\x03*n\x11\x83\u01b2\u070a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xbf\xbe\x05\u831c\xbb\xcc\x0e\x92\xa4\x05\xfa\xc1\xd8]\xe2H\xee$\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xbf\xbf\xbc\xb6V\u0099+\xe8\xfc\u0782\x19\xfb\xc5J\xad\u055f)\x8a\x02\x1e\x18\xd2\xc8!\xc7R\x00\x00\u07d4\xbf\xc5z\xa6f\xfa\u239f\x10zI\xcbP\x89\xa4\xe2!Q\u074965\u026d\xc5\u07a0\x00\x00\u07d4\xbf\u02d70$c\x04p\r\xa9\vAS\xe7\x11Ab.\x1cA\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbf\xd9<\x90\u009c\a\xbc_\xb5\xfcI\xae\xeaU\xa4\x0e\x13O5\x8a\x05\xed\xe2\x0f\x01\xa4Y\x80\x00\x00\xe0\x94\xbf\xe3\xa1\xfcn$\xc8\xf7\xb3%\x05`\x99\x1f\x93\u02e2\u03c0G\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xbf\u6f30\xf0\xc0xRd3$\xaa]\xf5\xfdb%\xab\xc3\u0289\x04\t\xe5+H6\x9a\x00\x00\u07d4\xbf\xf5\xdfv\x994\xb8\x94<\xa9\x13}\x0e\xfe\xf2\xfen\xbb\xb3N\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbf\xfbi)$\x1fx\x86\x93'>p\"\xe6\x0e>\xab\x1f\xe8O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\x06O\x1d\x94t\xab\x91]V\x90l\x9f\xb3 \xa2\xc7\t\x8c\x9b\x89\x13h?\u007f<\x15\xd8\x00\x00\u07d4\xc0\a\xf0\xbd\xb6\xe7\x00\x92\x02\xb7\xaf>\xa9\t\x02i|r\x14\x13\x89\xa2\xa0\xe4>\u007f\xb9\x83\x00\x00\u07d4\xc0\n\xb0\x80\xb6C\xe1\u00ba\xe3c\xe0\u0455\xde.\xff\xfc\x1cD\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\xc0 wD\x9a\x13Jz\xd1\xef~M\x92z\xff\xec\ueb75\xae\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xc0$q\xe3\xfc.\xa0S&\x15\xa7W\x1dI2\x89\xc1<6\xef\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0-n\xad\xea\xcf\x1bx\xb3\u0285\x03\\c{\xb1\xce\x01\xf4\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc03\xb12Z\n\xf4Tr\xc2U'\x85;\x1f\x1c!\xfa5\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xc03\xbe\x10\xcbHa;\xd5\xeb\xcb3\xedI\x02\xf3\x8bX0\x03\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc04[3\xf4\x9c\xe2\u007f\xe8,\xf7\xc8M\x14\x1ch\xf5\x90\xcev\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc0=\xe4*\x10\x9bezd\xe9\"$\xc0\x8d\xc1'^\x80\u0672\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0@i\u07f1\x8b\tlxg\xf8\xbe\xe7zm\xc7Gz\xd0b\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xc0A?Z|-\x9aK\x81\b(\x9e\xf6\xec\xd2qx\x15$\xf4\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xc0C\xf2E-\u02d6\x02\xefb\xbd6\x0e\x03=\xd29q\xfe\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc0OK\xd4\x04\x9f\x04F\x85\xb8\x83\xb6)Y\xaec\x1df~5\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\xc0V\u053dk\xf3\u02ec\xace\xf8\xf5\xa0\xe3\x98\v\x85'@\xae\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xc0[t\x06 \xf1s\xf1nRG\x1d\u00cb\x9cQJ\v\x15&\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\xc0i\xef\x0e\xb3B\x99\xab\xd2\xe3-\xab\xc4yD\xb2r3H$\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\xc0l\xeb\xbb\xf7\xf5\x14\x9af\xf7\xeb\x97k>G\xd5e\x16\xda/\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc0r^\u00bd\xc3:\x1d\x82`q\u07a2\x9db\xd48Z\x8c%\x8a\b\xa0\x85\x13F:\xa6\x10\x00\x00\u07d4\xc0~8g\xad\xa0\x96\x80z\x05\x1al\x9c4\xcc;?J\xd3J\x89`\xf0f \xa8IE\x00\x00\u07d4\xc0\x89^\xfd\x05m\x9a:\x81\xc3\xdaW\x8a\xda1\x1b\xfb\x93V\u03c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc0\x90\xfe#\xdc\xd8k5\x8c2\xe4\x8d*\xf9\x10$%\x9fef\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc0\x9af\x17*\xea7\r\x9ac\xda\x04\xffq\xff\xbb\xfc\xff\u007f\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\x9e<\xfc\x19\xf6\x05\xff>\xc9\xc9\xc7\x0e%@\xd7\xee\x97Cf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc0\xa0*\xb9N\xbeV\xd0E\xb4\x1bb\x9b\x98F.:\x02J\x93\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xc0\xa3\x93\b\xa8\x0e\x9e\x84\xaa\xaf\x16\xac\x01\xe3\xb0\x1dt\xbdk-\x89\afM\xddL\x1c\v\x80\x00\u07d4\xc0\xa6\u02edwi*=\x88\xd1A\xefv\x9a\x99\xbb\x9e<\x99Q\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xc0\xa7\xe8C]\xff\x14\xc2Uws\x9d\xb5\\$\u057fW\xa3\u064a\nm\xd9\f\xaeQ\x14H\x00\x00\u07d4\xc0\xae\x14\xd7$\x83./\xce'x\xde\u007f{\x8d\xaf{\x12\xa9>\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0\xaf\xb7\u0637\x93p\xcf\xd6c\u018c\u01b9p*7\u035e\xff\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc0\xb0\xb7\xa8\xa6\xe1\xac\xdd\x05\xe4\u007f\x94\xc0\x96\x88\xaa\x16\u01ed\x8d\x89\x03{m\x02\xacvq\x00\x00\xe0\x94\xc0\xb3\xf2D\xbc\xa7\xb7\xde[H\xa5>\u06dc\xbe\xab\vm\x88\xc0\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\xc0\xc0M\x01\x06\x81\x0e>\xc0\xe5J\x19\U000ab157\xe6\x9aW=\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xc0\xca2w\x94.tE\x87K\xe3\x1c\xeb\x90)rqO\x18#\x89\r\x8drkqw\xa8\x00\x00\u07d4\xc0\u02ed<\xcd\xf6T\xda\"\xcb\xcf\\xe\x97\xca\x19U\xc1\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\xcb\xf6\x03/\xa3\x9e|F\xffw\x8a\x94\xf7\xd4E\xfe\"\xcf0\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4\xc0\xe0\xb9\x03\b\x8e\fc\xf5=\xd0iWTR\xaf\xf5$\x10\u00c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc0\xe4W\xbdV\xec6\xa1$k\xfa20\xff\xf3\x8eY&\xef\"\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xc0\xed\rJ\xd1\r\xe045\xb1S\xa0\xfc%\xde;\x93\xf4R\x04\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\xc0\xf2\x9e\xd0\af\x11\xb5\xe5^\x13\x05G\xe6\x8aH\xe2m\xf5\u4262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc1\x13(x#\\]\u06e5\xd9\xf3\"\x8bR6\xe4p \xdco\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc1\x17\r\xba\xad\xb3\xde\xe6\x19\x8e\xa5D\xba\xec\x93%\x18`\xfd\xa5\x89A\rXj \xa4\xc0\x00\x00\xe0\x94\xc1&W=\x87\xb0\x17ZR\x95\xf1\xdd\a\xc5u\u03cc\xfa\x15\xf2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc1'\xaa\xb5\x90e\xa2\x86D\xa5k\xa3\xf1^.\xac\x13\xda)\x95\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xc1+\u007f@\u07da/{\xf9\x83f\x14\"\xab\x84\xc9\xc1\xf5\bX\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xc1,\xfb{=\xf7\x0f\xce\xca\x0e\xde&5\x00\xe2xs\xf8\xed\x16\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc1/\x88\x1f\xa1\x12\xb8\x19\x9e\xcb\xc7>\xc4\x18W\x90\xe6\x14\xa2\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc18Lnq~\xbeK#\x01NQ\xf3\x1c\x9d\xf7\xe4\xe2[1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc1C\x8c\x99\xddQ\xef\x1c\xa88j\xf0\xa3\x17\xe9\xb0AEx\x88\x89\f\x1d\xaf\x81\u0623\xce\x00\x00\u07d4\xc1c\x12(\xef\xbf*.:@\x92\xee\x89\x00\xc69\xed4\xfb\u02093\xc5I\x901r\f\x00\x00\u07d4\xc1u\xbe1\x94\xe6iB-\x15\xfe\xe8\x1e\xb9\xf2\xc5lg\xd9\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc1\x82v\x86\xc0\x16\x94\x85\xec\x15\xb3\xa7\xc8\xc0\x15\x17\xa2\x87M\xe1\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xc1\x8a\xb4g\xfe\xb5\xa0\xaa\xdf\xff\x91#\x0f\xf0VFMx\xd8\x00\x89lk\x93[\x8b\xbd@\x00\x00\u0794\xc1\x95\x05CUM\x8aq0\x03\xf6b\xbba,\x10\xadL\xdf!\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xc1\xa4\x1aZ'\x19\x92&\xe4\xc7\xeb\x19\x8b\x03\x1bY\x19o\x98B\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xc1\xb2\xa0\xfb\x9c\xadE\xcdi\x91\x92\xcd'T\v\x88\xd38By\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc1\xb2\xaa\x8c\xb2\xbfb\xcd\xc1:G\xec\xc4e\u007f\xac\xaa\x99_\x98\x8967\x93\xfa\x96\u6980\x00\u07d4\xc1\xb5\x00\x01\x1c\xfb\xa9]|\xd66\xe9^l\xbfagFK%\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc1\xb9\xa5pM5\x1c\xfe\x98?y\xab\xee\xc3\u06fb\xae;\xb6)\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc1\xcb\xd2\xe23*RL\xf2\x19\xb1\r\x87\x1c\xcc \xaf\x1f\xb0\xfa\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xc1\xcd\xc6\x01\xf8\x9c\x04(\xb3\x13\x02\u0447\xe0\xdc\b\xad}\x1cW\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xc1\u052f8\xe9\xbay\x90@\x89HI\xb8\xa8!\x93u\xf1\xacx\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc1\xe1@\x9c\xa5,%CQ4\xd0\x06\u00a6\xa8T-\xfbrs\x89\x01\xdd\x1eK\xd8\xd1\xee\x00\x00\u07d4\xc1\xeb\xa5hJ\xa1\xb2L\xbac\x15\x02c\xb7\xa9\x13\x1a\xee\u008d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc1\xec\x81\xdd\x12=K|-\u0674\xd48\xa7\a,\x11\u0707L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc1\xf3\x9b\xd3]\xd9\xce\xc37\xb9oG\xc6w\x81\x81`\xdf7\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\xc1\xff\xad\a\u06d6\x13\x8cK*S\x0e\xc1\xc7\xde)\xb8\xa0Y,\x88\xf4?\xc2\xc0N\xe0\x00\x00\xe0\x94\xc2\x1f\xa6d:\x1f\x14\xc0)\x96\xadqD\xb7Y&\xe8~\xcbK\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc24\nL\xa9L\x96x\xb7IL<\x85%(\xed\xe5\xeeR\x9f\x89\x02\xa3k\x05\xa3\xfd|\x80\x00\u07d4\xc29\xab\u07ee>\x9a\xf5E\u007fR\xed+\x91\xfd\n\xb4\xd9\xc7\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2;/\x92\x1c\xe4\xa3z%\x9e\u4b4b!X\xd1]fOY\x89\x01`\x89\x95\xe8\xbd?\x80\x00\u07d4\xc2C\x99\xb4\xbf\x86\xf73\x8f\xbfd^;\"\xb0\u0dd79\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2L\u03bc#D\xcc\xe5d\x17\xfbhL\xf8\x16\x13\xf0\xf4\xb9\xbd\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xc2Rf\xc7gf2\xf1>\xf2\x9b\xe4U\ud50a\xddVw\x92\x89Hz\x9a0E9D\x00\x00\u07d4\xc2\\\xf8&U\f\x8e\xaf\x10\xaf\"4\xfe\xf9\x04\u0779R\x13\xbe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc2f?\x81E\xdb\xfe\xc6\xc6F\xfc\\I\x96\x13E\xde\x1c\x9f\x11\x89%g\xacp9+\x88\x00\x00\u07d4\xc2pEh\x854+d\vL\xfc\x1bR\x0e\x1aTN\xe0\xd5q\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xc2sv\xf4]!\xe1^\xde;&\xf2e_\xce\xe0,\xcc\x0f*\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xc2w\x97q\xf0Smy\xa8p\x8fi1\xab\xc4K05\u964a\x047\u04ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xc2\xc1>r\xd2h\xe7\x15\r\u01d9\xe7\xc6\xcf\x03\u0209T\xce\u05c9%\xf2s\x93=\xb5p\x00\x00\u07d4\xc2\xcb\x1a\xda]\xa9\xa0B8s\x81G\x93\xf1aD\xef6\xb2\xf3\x89HU~;p\x17\xdf\x00\x00\u07d4\xc2\xd1w\x8e\xf6\xee_\xe4\x88\xc1E\xf3Xkn\xbb\xe3\xfb\xb4E\x89>\x1f\xf1\xe0;U\xa8\x00\x00\xe0\x94\xc2\xd9\xee\xdb\xc9\x01\x92c\xd9\xd1l\u016e\a-\x1d=\xd9\xdb\x03\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc2\xe0XJq4\x8c\xc3\x14\xb7; )\xb6#\v\x92\u06f1\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2\xe2\u0518\xf7\r\xcd\bY\xe5\v\x02:q\nmK!3\xbd\x8989\x11\xf0\f\xbc\xe1\x00\x00\u07d4\xc2\xed_\xfd\u046d\xd8U\xa2i/\xe0b\xb5\xd6\x18t#`\u0509A\rXj \xa4\xc0\x00\x00\u07d4\xc2\xee\x91\xd3\xefX\xc9\u0465\x89\x84N\xa1\xae1%\xd6\u017ai\x894\x95tD\xb8@\xe8\x00\x00\u07d4\xc2\xfa\xfd\xd3\n\xcbmg\x06\xe9)<\xb0&A\xf9\xed\xbe\a\xb5\x89Q\x00\x86\vC\x0fH\x00\x00\u07d4\xc2\xfd\v\xf7\xc7%\xef>\x04~Z\xe1\u009f\xe1\x8f\x12\xa7)\x9c\x89Hz\x9a0E9D\x00\x00\u07d4\xc2\xfe}us\x1fcm\xcd\t\xdb\xda\x06q9;\xa0\xc8*}\x89wC\"\x17\xe6\x83`\x00\x00\u07d4\xc3\x10z\x9a\xf32-R8\xdf\x012A\x911b\x959W}\x89\x1a\xb4\xe4d\xd4\x141\x00\x00\xe0\x94\xc3\x11\v\xe0\x1d\xc9sL\xfcn\x1c\xe0\u007f\x87\xd7}\x13E\xb7\xe1\x8a\x01\x0f\f\xe9I\xe0\x0f\x93\x00\x00\u07d4\xc3 8\xcaR\xae\xe1\x97E\xbe\\1\xfc\xdcT\x14\x8b\xb2\xc4\u0409\x02\xb5\xaa\xd7,e \x00\x00\u07d4\xc3%\xc3R\x80\x1b\xa8\x83\xb3\"l_\xeb\r\xf9\xea\xe2\xd6\xe6S\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xc3.\xc7\xe4*\xd1l\xe3\xe2UZ\xd4\xc5C\x06\xed\xa0\xb2gX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc32\xdfP\xb1<\x014\x90\xa5\xd7\xc7]\xbf\xa3f\u0687\xb6\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc3:\u0373\xba\x1a\xab'P{\x86\xb1]g\xfa\xf9\x1e\xcfb\x93\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3>\u0393Z\x8fN\xf98\xea~\x1b\xac\x87\u02d2]\x84\x90\u028a\a\x03\x8c\x16x\x1fxH\x00\x00\u07d4\xc3@\xf9\xb9\x1c&r\x8c1\xd1!\xd5\xd6\xfc;\xb5m=\x86$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3F\xcb\x1f\xbc\xe2\xab(]\x8eT\x01\xf4-\xd7#M7\xe8m\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\xe0\x94\xc3H=n\x88\xac\x1fJ\xe7<\xc4@\x8dl\x03\xab\xe0\xe4\x9d\u028a\x03\x99\x92d\x8a#\u0220\x00\x00\xe0\x94\xc3H\xfcZF\x13#\xb5{\xe3\x03\u02c96\x1b\x99\x19\x13\xdf(\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xc3N;\xa12.\xd0W\x11\x83\xa2O\x94 N\xe4\x9c\x18fA\x89\x03'\xaf\uf927\xbc\x00\x00\xe0\x94\xc3[\x95\xa2\xa3s|\xb8\xf0\xf5\x96\xb3E$\x87+\xd3\r\xa24\x8a\x01\x98\xbe\x85#^-P\x00\x00\xe0\x94\xc3c\x1cv\x98\xb6\xc5\x11\x19\x89\xbfE''\xb3\xf99Zm\xea\x8a\x02C'X\x96d\x1d\xbe\x00\x00\u07d4\xc3l\vc\xbf\xd7\\/\x8e\xfb\x06\b\x83\xd8h\xcc\xcdl\xbd\xb4\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xc3uk\xcd\xcc~\xect\xed\x89j\xdf\xc35'Y0&n\b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u00c4\xacn\xe2|9\xe2\xf2x\xc2 \xbd\xfa[\xae\xd6&\xd9\u04c9 \x86\xac5\x10R`\x00\x00\u07d4\u00e0F\xe3\u04b2\xbfh\x14\x88\x82n2\xd9\xc0aQ\x8c\xfe\x8c\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\u00e9\"j\xe2u\xdf,\xab1+\x91\x10@cJ\x9c\x9c\x9e\xf6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u00f9(\xa7o\xadex\xf0O\x05U\xe69R\xcd!\xd1R\n\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3\xc2)s)\xa6\xfd\x99\x11~T\xfcj\xf3y\xb4\xd5VT~\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc3\xc3\xc2Q\rg\x80 HZcs]\x13\a\xecL\xa60+\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc3\xcbk6\xafD?,n%\x8bJ9U:\x81\x87G\x81\x1f\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\xc3\xdbVW\xbbr\xf1\rX\xf21\xfd\xdf\x11\x98\n\xffg\x86\x93\x8a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\xc3\u06df\xb6\xf4lH\n\xf3De\u05d7S\xb4\xe2\xb7Jg\u038a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc3\xddX\x908\x860;\x92\x86%%z\xe1\xa0\x13\xd7\x1a\xe2\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\xe0G\x1cd\xff5\xfaR2\xcc1!\xd1\u04cd\x1a\x0f\xb7\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\xe2\f\x96\u07cdN8\xf5\v&Z\x98\xa9\x06\xd6\x1b\xc5\x1aq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\u31f0<\xe9\\\xcf\xd7\xfaQ\u0744\x01\x83\xbcCS(\t\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3\xf8\xf6r\x95\xa5\xcd\x04\x93d\xd0]#P&#\xa3\xe5.\x84\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc4\x01\xc4'\xcc\xcf\xf1\r\xec\xb8d /6\xf5\x80\x83\"\xa0\xa8\x89\xb4{Q\xa6\x9c\xd4\x02\x00\x00\u07d4\xc4\b\x8c\x02_>\x85\x01?T9\xfb4@\xa1s\x01\xe5D\xfe\x89~\t\xdbM\x9f?4\x00\x00\u07d4\xc4\x14a\xa3\u03fd2\u0246UU\xa4\x8117\xc0v1#`\x8965\xc6 G9\u0640\x00\u07d4\xc4 8\x8f\xbe\xe8J\xd6V\xddh\xcd\xc1\xfb\xaa\x93\x92x\v4\x89\n-\xcac\xaa\xf4\u0140\x00\u07d4\xc4\"P\xb0\xfeB\xe6\xb7\xdc\xd5\u0210\xa6\xf0\u020f__\xb5t\x89\b\x1e\xe4\x82SY\x84\x00\x00\u07d4\xc4-j\xebq\x0e:P\xbf\xb4Ml1\t)i\xa1\x1a\xa7\xf3\x89\b\"c\xca\xfd\x8c\xea\x00\x00\xe0\x94\xc4@\xc7\xca/\x96Kir\xeffJ\"a\xdd\xe8\x92a\x9d\x9c\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xc4K\xde\xc8\xc3l\\h\xba\xa2\xdd\xf1\xd41i2)rlC\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xc4OJ\xb5\xbc`9|s~\xb0h3\x91\xb63\xf8\xa2G\x1b\x12\x1c\xa4\x89 .h\xf2\u00ae\xe4\x00\x00\u07d4\xc4h\x1es\xbb\x0e2\xf6\xb7& H1\xffi\xba\xa4\x87~2\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xc4k\xbd\xefv\xd4\xca`\xd3\x16\xc0\u007f]\x1ax\x0e;\x16_~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc4}a\v9\x92P\xf7\x0e\xcf\x13\x89\xba\xb6),\x91&O#\x89\x0f\xa7\xe7\xb5\xdf<\xd0\x00\x00\u07d4\u0100;\xb4\a\xc7b\xf9\vu\x96\xe6\xfd\u1513\x1ev\x95\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0106Q\xc1\xd9\xc1k\xffL\x95T\x88l??&C\x1foh\x89#\xab\x95\x99\xc4?\b\x00\x00\u07d4\u0109\xc8?\xfb\xb0%*\xc0\xdb\xe3R\x12\x17c\x0e\x0fI\x1f\x14\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u010bi<\xac\xef\xdb\xd6\xcb]x\x95\xa4.1\x962~&\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0113H\x9eV\u00fd\xd8)\x00}\xc2\xf9VA)\x06\xf7k\xfa\x89\x02\xa7\x91H\x8eqT\x00\x00\u07d4\u0116\u02f0E\x9aj\x01`\x0f\u0149\xa5Z2\xb4T!\u007f\x9d\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4\u011c\xfa\xa9g\xf3\xaf\xbfU\x03\x10a\xfcL\xef\x88\xf8]\xa5\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0136\xe5\xf0\x9c\xc1\xb9\r\xf0x\x03\xce=M\x13vj\x9cF\xf4\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\u013e\xc9c\b\xa2\x0f\x90\u02b1\x83\x99\u0113\xfd=\x06Z\xbfE\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xc4\xc0\x1a\xfc>\x0f\x04R!\xda\x12\x84\u05c7\x85tD/\xb9\xac\x8a\x01\x92\xb5\u0249\x02J\x19\xc1\xbdo\x12\x80\x00\xe0\x94\xc5\x00\xb7 sN\xd2)8\u05cc^H\xb2\xba\x93g\xa5u\xba\x8a\a\x12\x9e\x1c\xdf7>\xe0\x00\x00\u07d4\xc5\x0f\xe4\x15\xa6A\xb0\x85lNu\xbf\x96\x05\x15D\x1a\xfa5\x8d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5\x13L\xfb\xb1\xdfz \xb0\xedpWb.\xee\u0480\x94}\xad\x89\xcd\xff\x97\xfa\xbc\xb4`\x00\x00\xe0\x94\xc5\x17\xd01\\\x87\x88\x13\xc7\x17\u132f\xa1\xea\xb2eN\x01\u068a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc5\x18y\x9aY%Wb\x13\xe2\x18\x96\xe0S\x9a\xbb\x85\xb0Z\xe3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc5\"\xe2\x0f\xbf\x04\xed\u007fk\x05\xa3{G\x18\xd6\xfc\xe0\x14.\x1a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc5$\bmF\xc8\x11+\x12\x8b/\xafo|}\x81`\xa88l\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc5-\x1a\fs\u00a1\xbe\x84\x91Q\x85\xf8\xb3O\xaa\n\xdf\x1d\xe3\x89K\xe4\xea\xb3\xfa\x0f\xa6\x80\x00\xe0\x94\xc55\x94\xc7\u03f2\xa0\x8f(L\xc9\u05e6;\xbd\xfc\v1\x972\x8a\nk#(\xff:b\xc0\x00\x00\u07d4\xc57I(\xcd\xf1\x93pTC\xb1L\xc2\r\xa4#G<\xd9\u03c9\a}\x10P\x9b\xb3\xaf\x80\x00\u07d4\xc58\xa0\xff(*\xaa_Ku\u03f6,p\x03~\xe6}O\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5;P\xfd;+r\xbclC\v\xaf\x19JQU\x85\u04d8m\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xc5=y\xf7\u02dbp\x95/\xd3\x0f\xceX\xd5K\x9f\vY\xf6G\x8a\x01\x13\xe2\xd6tCE\xf8\x00\x00\u07d4\xc5I\u07c3\xc6\xf6^\xec\x0f\x1d\u0260\x93J\\_:P\xfd\x88\x89\x9d\xc0\\\xce(\u00b8\x00\x00\u07d4\xc5P\x05\xa6\xc3~\x8c\xa7\xe5C\xce%\x99s\xa3\xca\u0396\x1aJ\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5U\xb91V\xf0\x91\x01#\x80\x00\xe0\x94\u0166)\xa3\x96%R\u02ce\xde\u0609cj\xaf\xbd\f\x18\xcee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u016e\x86\xb0\xc6\xc7\xe3\x90\x0f\x13h\x10\\VS\u007f\xaf\x8dt>\x89\n1\x06+\xee\xedp\x00\x00\u07d4\u0170\t\xba\xea\xf7\x88\xa2v\xbd5\x81:\xd6[@\v\x84\x9f;\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0175l\xd24&|(\xe8\x9cok\"f\xb0\x86\xa1/\x97\f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xc5\u01a4\x99\x8a3\xfe\xb7dCz\x8b\xe9)\xa7;\xa3J\ad\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\xe0\x94\xc5\xc7=a\xcc\xe7\xc8\xfeL\x8f\xce)\xf3\x90\x92\xcd\x19>\x0f\xff\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xc5\xc7Y\vV!\xec\xf85\x85\x88\u079bh\x90\xf2baC\U000498a1]\tQ\x9b\xe0\x00\x00\u07d4\xc5\xcd\xce\xe0\xe8]\x11}\xab\xbfSj?@i\xbfD?T\xe7\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xc5\u050c\xa2\xdb/\x85\xd8\xc5U\xcb\x0e\x9c\xfe\x82i6x?\x9e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc5\xde\x12\x03\xd3\xcc,\xea1\xc8.\xe2\xdeY\x16\x88\a\x99\xea\xfd\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xc5\xe4\x88\xcf+Vw\x939q\xf6L\xb8 -\xd0WR\xa2\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc5\xe8\x12\xf7o\x15\xf2\xe1\xf2\xf9\xbcH#H<\x88\x04cog\x89\x03\xf5\x14\x19:\xbb\x84\x00\x00\u07d4\xc5\u94d34\xf1%.\u04ba&\x81D\x87\xdf\u0498+1(\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\xc5\xebB)^\x9c\xad\xea\xf2\xaf\x12\xde\u078a\x8dS\xc5y\xc4i\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\xc5\xed\xbb\xd2\xca\x03WeJ\xd0\xeaG\x93\xf8\xc5\xce\xcd0\xe2T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc5\xf6K\xab\xb7\x031B\xf2\x0eF\u05eab\x01\xed\x86\xf6q\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5\xf6\x87qrF\u068a \r \xe5\u9f2c`\xb6\u007f8a\x89\x01\x8d\x99?4\xae\xf1\x00\x00\u07d4\xc6\x04[<5\vL\xe9\xca\fkuO\xb4\x1ai\xb9~\x99\x00\x892$\xf4'#\xd4T\x00\x00\u07d4\xc6\v\x04eN\x00;F\x83\x04\x1f\x1c\xbdk\u00cf\xda|\xdb\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\x14F\xb7T\xc2N;\x16B\xd9\xe5\x17e\xb4\xd3\xe4k4\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\x18R\x13!\xab\xaf[&Q:J\x95(\bo\"\n\xdco\x89\x01v\xb3D\xf2\xa7\x8c\x00\x00\u07d4\xc6#FW\xa8\a8A&\xf8\x96\x8c\xa1p\x8b\xb0{\xaaI<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc6%\xf8\u024d'\xa0\x9a\x1b\u02bdQ(\xb1\u00a9HV\xaf0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc65^\xc4v\x8cp\xa4\x9a\xf6\x95\x13\u0343\xa5\xbc\xa7\xe3\xb9\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc6:\xc4\x17\x99.\x9f\x9b`8n\xd9S\xe6\xd7\xdf\xf2\xb0\x90\xe8\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xc6<\u05c8!\x18\xb8\xa9\x1e\aML\x8fK\xa9\x18Q0;\x9a\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xc6R\x87\x1d\x19$\"\u01bc#_\xa0c\xb4J~\x1dC\u3149\bg\x0e\x9e\xc6Y\x8c\x00\x00\xe0\x94\xc6gD\x1e\u007f)y\x9a\xbaadQ\xd5;?H\x9f\x9e\x0fH\x8a\x02\xf2\x9a\xceh\xad\u0740\x00\x00\u07d4\xc6j\xe4\xce\xe8\u007f\xb352\x19\xf7\u007f\x1dd\x86\u0140(\x032\x89\x01\x9a\x16\xb0o\xf8\xcb\x00\x00\u07d4\xc6t\xf2\x8c\x8a\xfd\a?\x8by\x96\x91\xb2\xf0XM\xf9B\xe8D\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0197\xb7\x04w\u02b4.+\x8b&f\x81\xf4\xaesu\xbb%A\x8a\x01.W2\xba\xba\\\x98\x00\x00\u07d4\u019b\x85U9\xce\x1b\x04qG(\xee\xc2Z7\xf3g\x95\x1d\xe7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u019b\xe4@\x13Mb\x80\x98\x01D\xa9\xf6M\x84t\x8a7\xf3I\x89&\u009eG\u0104L\x00\x00\u07d4\u019df<\x8d`\x90\x83\x91\xc8\xd26\x19\x153\xfd\xf7wV\x13\x89\x1aJ\xba\"\\ t\x00\x00\u0794\u01a2\x86\xe0e\xc8_:\xf7H\x12\xed\x8b\u04e8\xce]%\xe2\x1d\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\u01a3\x0e\xf5\xbb3 \xf4\r\xc5\xe9\x81#\rR\xae:\xc1\x93\"\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\u01ae(}\xdb\xe1\x14\x9b\xa1m\xdc\xcaO\xe0j\xa2\uaa48\xa9\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc6\xc7\xc1\x917\x98\x97\u075c\x9d\x9a3\x83\x9cJ_b\xc0\x89\r\x89\xd8\xd8T\xb2$0h\x80\x00\xe0\x94\xc6\xcdh\xec56,Z\xd8L\x82\xadN\xdc#!%\x91-\x99\x8a\x05\xe0T\x9c\x962\xe1\xd8\x00\x00\u07d4\xc6\u0615N\x8f?\xc53\xd2\xd20\xff\x02\\\xb4\xdc\xe1O4&\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc6\xdb\u06de\xfd^\xc1\xb3xn\x06q\xeb\"y\xb2S\xf2\x15\xed\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc6\xdf u\xeb\xd2@\xd4Hi\u00bek\u07c2\xe6=N\xf1\xf5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc6\xe2\xf5\xaf\x97\x9a\x03\xfdr:\x1bn\xfar\x83\x18\u03dc\x18\x00\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xc6\xe3$\xbe\xeb[6v^\xcdFB`\xf7\xf2`\x06\xc5\xc6.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\xe4\xcc\fr\x83\xfc\x1c\x85\xbcH\x13\xef\xfa\xafr\xb4\x98#\xc0\x89\x0f\x03\x1e\xc9\xc8}\xd3\x00\x00\xe0\x94\xc6\xee5\x93B)i5)\xdcA\u067bq\xa2IfX\xb8\x8e\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xc6\xfb\x1e\xe3t\x17\u0400\xa0\xd0H\x92;\u06ba\xb0\x95\xd0w\u0189\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc7\x05'\xd4D\u0110\xe9\xfc?\\\xc4Nf\xebO0k8\x0f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7\r\x85mb\x1e\xc1E0<\nd\x00\xcd\x17\xbb\xd6\xf5\xea\xf7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc7\x0f\xa4Uv\xbf\x9c\x86_\x988\x93\x00,AI&\xf6\x10)\x89\x15\xb4\xaa\x8e\x97\x02h\x00\x00\u07d4\xc7\x11E\xe5)\u01e7\x14\xe6y\x03\xeeb\x06\xe4\xc3\x04+g'\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xc7\x1b*=q5\u04a8_\xb5\xa5q\u073ei^\x13\xfcC\u034965\u026d\xc5\u07a0\x00\x00\u07d4\xc7\x1f\x1du\x87?3\u0732\xddK9\x87\xa1-\a\x91\xa5\xce'\x897\b\xba\xed=h\x90\x00\x00\u07d4\xc7\x1f\x92\xa3\xa5J{\x8c/^\xa4C\x05\xfc\u02c4\xee\xe21H\x89\x02\xb5\x9c\xa11\xd2\x06\x00\x00\u07d4\xc7!\xb2\xa7\xaaD\xc2\x12\x98\xe8P9\xd0\x0e.F\x0eg\v\x9c\x89\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\xc7,\xb3\x01%\x8e\x91\xbc\b\x99\x8a\x80]\u0452\xf2\\/\x9a5\x89 \t\xc5\u023fo\xdc\x00\x00\xe0\x94\xc76\x8b\x97\t\xa5\xc1\xb5\x1c\n\xdf\x18ze\xdf\x14\xe1+}\xba\x8a\x02\x02o\xc7\u007f\x03\u5b80\x00\u07d4\xc79%\x9e\u007f\x85\xf2e\x9b\xef_`\x9e\xd8k=Yl \x1e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc7>!\x12(\"\x15\xdc\ab\xf3+~\x80}\xcd\x1az\xae>\x8a\x01v\f\xbcb;\xb3P\x00\x00\xe0\x94\xc7If\x80B\xe7\x11#\xa6H\x97^\b\xedc\x82\xf8>\x05\xe2\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xc7J9\x95\xf8\a\xde\x1d\xb0\x1a.\xb9\xc6.\x97\xd0T\x8fio\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc7Pl\x10\x19\x12\x1f\xf0\x8a,\x8c\x15\x91\xa6^\xb4\xbd\xfbJ?\x89 \x86\xac5\x10R`\x00\x00\u07d4\xc7\\7\xce-\xa0k\xbc@\b\x11Y\u01ba\x0f\x97n9\x93\xb1\x89:y#\x15\x1e\xcfX\x00\x00\u07d4\xc7]\"Y0j\xec}\xf0\"v\x8ci\x89\x9ae!\x85\xdb\u0109\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7`\x97\x1b\xbc\x18\x1cj|\xf7tA\xf2BG\u045c\xe9\xb4\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xc7a0\xc7<\xb9!\x028\x02\\\x9d\xf9]\v\xe5J\xc6\u007f\xbe\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xc7e\xe0\x04v\x81\tG\x81j\xf1B\xd4m.\u7f28\xccO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc7g^VG\xb9\xd8\xda\xf4\xd3\xdf\xf1\xe5R\xf6\xb0qT\xac8\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xc7{\x01\xa6\xe9\x11\xfa\x98\x8d\x01\xa3\xab3dk\xee\xf9\xc18\xf3\x89'\x1bo\xa5\xdb\xe6\xcc\x00\x00\u07d4\u01c3z\u0420\xbf\x14\x18i7\xac\xe0lUF\xa3j\xa5OF\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u01d8\x06\x03+\xc7\xd8(\xf1\x9a\u01a6@\u018e=\x82\x0f\xa4B\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u01d9\xe3N\x88\xff\x88\xbe}\xe2\x8e\x15\xe4\xf2\xa6=\v3\xc4\u02c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\u01ddPb\u01d6\xddwa\xf1\xf1>U\x8ds\xa5\x9f\x82\xf3\x8b\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\u01e0\x18\xf0\x96\x8aQ\xd1\xf6`<\\I\xdcT[\xcb\x0f\xf2\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u01ef\xf9\x19)yt\x89UZ/\xf1\xd1M\\iZ\x10\x83U\x8965\u026d\xc5\u07a0\x00\x00\u0794\u01f1\xc8>c ?\x95G&>\xf6(.}\xa3;n\xd6Y\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\u01f3\x9b\x06\x04Q\x00\f\xa1\x04\x9b\xa1T\xbc\xfa\x00\xff\x8a\xf2b\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\u01ff\x17\xc4\xc1\x1f\x98\x94\x1fP~w\bO\xff\xbd-\xbd=\xb5\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u01ff.\xd1\xed1)@\xeej\xde\xd1Qn&\x8eJ`HV\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc7\xd4O\xe3,\u007f\x8c\xd5\xf1\xa9t'\xb6\xcd:\xfc\x9eE\x02>\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xc7\xd5\xc7\x05@\x81\xe9\x18\xech{Z\xb3n\x97=\x18\x13)5\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xc7\xde^\x8e\xaf\xb5\xf6+\x1a\n\xf2\x19\\\xf7\x93\u01c9L\x92h\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc7\xe30\xcd\f\x89\n\u025f\xe7q\xfc\xc7\xe7\xb0\t\xb7A=\x8a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7\xea\xc3\x1a\xbc\xe6\xd5\xf1\u07a4\"\x02\xb6\xa6t\x15=\xb4z)\x89 \t\xc5\u023fo\xdc\x00\x00\xe0\x94\xc7\xecb\xb8\x04\xb1\xf6\x9b\x1e0p\xb5\xd3b\xc6/\xb3\t\xb0p\x8a\x02\xc4k\xf5A`f\x11\x00\x00\u07d4\xc7\xf7+\xb7X\x01k7G\x14\u0509\x9b\xce\"\xb4\xae\xc7\n1\x89:&\xc9G\x8f^-\x00\x00\u0794\xc8\v6\u047e\xaf\xba_\xccdM`\xacnF\xed)'\xe7\u0708\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xc8\x11\xc2\xe9\xaa\x1a\xc3F.\xba^\x88\xfc\xb5\x12\x0e\x9fn,\xa2\x89K\xe6\u0607\xbd\x87n\x00\x00\u07d4\xc8\x17\xdf\x1b\x91\xfa\xf3\x0f\xe3%\x15qr|\x97\x11\xb4]\x8f\x06\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xc8\x1f\xb7\xd2\x0f\u0480\x01\x92\xf0\xaa\xc1\x98\xd6\u05a3}?\xcb}\x89\x0e\x11I3\x1c-\xde\x00\x00\u07d4\xc8 \xc7\x11\xf0w\x05'8\a\xaa\xaam\xe4M\x0eKH\xbe.\x89\bg\x0e\x9e\xc6Y\x8c\x00\x00\u07d4\xc8#\x1b\xa5\xa4\x11\xa1>\"+)\xbf\xc1\b?v1X\xf2&\x8967\tlK\xcci\x00\x00\u07d4\xc86\xe2Jo\xcf)\x94;6\b\xe6b)\n!_e)\xea\x89\x0f\xd4Pd\xea\xee\x10\x00\x00\xe0\x94\xc8;\xa6\u0755I\xbe\x1d2\x87\xa5\xa6T\xd1\x06\xc3Lk]\xa2\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xc8>\x9djX%;\uefb7\x93\xe6\xf2\x8b\x05JXI\x1bt\x89\x0fF\u00b6\xf5\xa9\x14\x00\x00\u07d4\xc8A\x88O\xa4x_\xb7s\xb2\x8e\x97\x15\xfa\xe9\x9aQ40]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc8M\x9b\xea\n{\x9f\x14\x02 \xfd\x8b\x90\x97\u03ff\xd5\xed\xf5d\x89\x06\xab\x9e\u0091\xad}\x80\x00\u07d4\xc8RB\x8d+Xd\x97\xac\xd3\fV\xaa\x13\xfbU\x82\xf8D\x02\x893B\xd6\r\xff\x19`\x00\x00\u07d4\xc8S![\x9b\x9f-,\xd0t\x1eX^\x98{_\xb8\f!.\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xc8S%\uaca5\x9b>\xd8c\xc8j_)\x06\xa0B)\xff\xa9\x89\x19=\u007f}%=\xe0\x00\x00\u07d4\xc8^\xf2}\x82\x04\x03\x80_\xc9\xed%\x9f\xffd\xac\xb8\xd64j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc8akN\xc0\x91(\xcd\xff9\xd6\u4e6c\x86\xee\xc4q\xd5\xf2\x89\x01\r:\xa56\xe2\x94\x00\x00\xe0\x94\xc8a\x90\x90K\x8d\a\x9e\xc0\x10\xe4b\xcb\xff\xc9\b4\xff\xaa\\\x8a\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4\xc8q\r~\x8bZ;\u059aB\xfe\x0f\xa8\xb8|5\u007f\xdd\xcd\u0209\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc8sR\u06e5\x82\xee f\xb9\xc0\x02\xa9b\xe0\x03\x13Ox\xb1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xc8|w\xe3\xc2J\xde\xcd\xcd\x108\xa3\x8bV\xe1\x8d\xea\u04f7\x02\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\xc8}:\xe3\u0607\x04\u066b\x00\t\xdc\xc1\xa0\x06q1\xf8\xba<\x89j\xc5\xc6-\x94\x86\a\x00\x00\xe0\x94\u0201N4R>8\xe1\xf9'\xa7\xdc\xe8FjDz\t6\x03\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0202U\xed\xdc\xf5!\xc6\xf8\x1d\x97\xf5\xa4!\x81\xc9\a=N\xf1\x89\x0f\u00d0D\xd0\n*\x80\x00\u07d4\u0205\xa1\x8a\xab\xf4T\x1b{{~\xcd0\xf6\xfa\u619d\x95i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u020c\xa1\xe6\xe5\xf4\xd5X\xd17\x80\xf4\x88\xf1\rJ\xd3\x13\r4\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u020e\xecT\xd3\x05\xc9(\xcc(H\xc2\xfe\xe251\xac\xb9mI\x89lj\u04c2\xd4\xfba\x00\x00\xe0\x94\u021c\xf5\x04\xb9\xf3\xf85\x18\x1f\xd8BO\\\xcb\xc8\xe1\xbd\xdf}\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0222\xc4\xe5\x9e\x1c\u007f\xc5H\x05X\x048\xae\xd3\xe4J\xfd\xf0\x0e\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4\u022aI\u301f\b\x99\xf2\x8a\xb5~gCp\x9dXA\x903\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\u022b\x1a<\xf4l\xb8\xb0d\xdf.\"-9`s\x94 2w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0231\x85\x05%\xd9F\xf2\xae\x84\xf3\x17\xb1Q\x88\xc56\xa5\u0706\x89\x91\x8d\xdc:B\xa3\xd4\x00\x00\u07d4\xc8\xd4\xe1Y\x9d\x03\xb7\x98\t\xe0\x13\n\x8d\u00c4\b\xf0^\x8c\u04c9\x9f\xad\x06$\x12y\x16\x00\x00\u07d4\xc8\xdd'\xf1k\xf2$P\xf5w\x1b\x9f\xe4\xedO\xfc\xb3\t6\xf4\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xc8\xdezVL\u007f@\x12\xa6\xf6\xd1\x0f\u040fG\x89\x0f\xbf\a\u0509\x10CV\x1a\x88)0\x00\x00\u07d4\xc8\xe2\xad\xebT^I\x9d\x98,\f\x11sc\u03b4\x89\u0171\x1f\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\xc8\xe5X\xa3\xc5i~o\xb2:%\x94\u0200\xb7\xa1\xb6\x8f\x98`\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc8\xf2\xb3 \xe6\xdf\xd7\t\x06\u0157\xba\xd2\xf9P\x13\x12\u01c2Y\x89Q\x93K\x8b:W\xd0\x00\x00\u07d4\xc9\x03\x00\xcb\x1d@w\xe6\xa6\xd7\xe1i\xa4`F\x8c\xf4\xa4\x92\u05c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9\f7e\x15k\u028eH\x97\xab\x80$\x19\x15<\xbeR%\xa9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc9\x10\xa9pUl\x97\x16\xeaS\xaff\xdd\xef\x93\x141$\x91=\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\xe0\x94\xc9\x12{\u007ff)\xee\x13\xfc?`\xbc/Dg\xa2\aE\xa7b\x8a\x03|\x9a\xa4\xe7\xceB\x1d\x80\x00\u07d4\xc9\x1b\xb5b\xe4+\xd4a0\xe2\u04eeFR\xb6\xa4\ub1bc\x0f\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\xe0\x94\xc90\x88y\x05m\xfe\x13\x8e\xf8 \x8fy\xa9\x15\u01bc~p\xa8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xc94\xbe\xca\xf7\x1f\"_\x8bJK\xf7\xb1\x97\xf4\xac\x9604\\\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc9?\xbd\xe8\xd4m+\xcc\x0f\xa9\xb3;\u063a\u007f\x80B\x12Ue\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xc9@\x89U:\xe4\xc2,\xa0\x9f\xbc\x98\xf5pu\xcf.\u0155\x04\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc9A\x10\xe7\x1a\xfeW\x8a\xa2\x18\xe4\xfc(d\x03\xb03\n\u038d\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc9F\u056c\xc14n\xba\nry\xa0\xac\x1dF\\\x99m\x82~\x8a\x03x=T_\xdf\n\xa4\x00\x00\u07d4\xc9J(\xfb20\xa9\xdd\xfa\x96Nw\x0f,\xe3\xc2S\xa7\xbeO\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc9JXR\x03\xda{\xba\xfd\x93\xe1X\x84\xe6`\u0531\xea\xd8T\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xc9O|5\xc0'\xd4}\xf8\xefO\x9d\xf8Z\x92H\xa1}\xd2;\x89\x01\x9f\x8euY\x92L\x00\x00\u07d4\xc9Q\x90\f4\x1a\xbb\xb3\xba\xfb\xf7\xee )7pq\xdb\xc3j\x89\x11\xc2]\x00M\x01\xf8\x00\x00\u07d4\xc9S\xf94\xc0\xeb-\x0f\x14K\u06b0\x04\x83\xfd\x81\x94\x86\\\xe7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9f&r\x8a\xaaLO\xb3\xd3\x1c&\xdf:\xf3\x10\b\x17\x10\u0449\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\xc9gQel\n\x8e\xf45{sD2!4\xb9\x83PJ\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\u0240Hh\u007f+\xfc\u027d\x90\xed\x18slW\xed\xd3R\xb6]\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0241\xd3\x12\u0487\xd5X\x87\x1e\u0757:\xbbv\xb9y\xe5\xc3^\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0242Xmc\xb0\xd7L \x1b\x1a\xf8A\x83r\xe3\fv\x16\xbe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u0249CO\x82Z\xaf\x9cU/h^\xba|\x11\xdbJ_\xc7:\x89\x1b(\u014d\x96\x96\xb4\x00\x00\u07d4\u0249\xee\xc3\a\u80db\x9dr7\xcf\xda\b\x82)b\xab\u41c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u0252\xbeY\xc6r\x1c\xafN\x02\x8f\x9e\x8f\x05\xc2\\UQ[\u0509\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0255{\xa9L\x1b)\xe5'~\xc3f\"pI\x04\xc6=\xc0#\x89h>\xfcg\x82d,\x00\x00\xe0\x94\u025a\x9c\xd6\xc9\xc1\xbe54\xee\u0352\xec\xc2/\\8\xe9Q[\x8a\x01\x05Y;:\x16\x9dw\x00\x00\xe0\x94\u026c\x01\xc3\xfb\t)\x03?\f\xcc~\x1a\xcf\uaae7\x94]G\x8a\x02\xa3j\x9e\x9c\xa4\xd2\x03\x80\x00\u07d4\u0276\x98\xe8\x98\xd2\rMO@\x8eNM\x06\x19\"\xaa\x85c\a\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u0276\xb6\x86\x11\x16\x91\xeej\xa1\x97\xc7#\x1a\x88\xdc`\xbd)]\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc9\u01ec\v\u0753B\xb5\xea\xd46\t#\xf6\x8cr\xa6\xbac:\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc9\xc8\r\xc1.{\xab\x86\xe9I\xd0\x1eL>\xd3_+\x9b\xba_\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9\xd7dF\u056a\xdf\xf8\vh\xb9\x1b\b\u035b\xc8\xf5U\x1a\xc1\x89&\xb4\xbd\x91\x10\xdc\xe8\x00\x00\xe0\x94\xc9\u073b\x05oM\xb7\xd9\xda9\x93b\x02\u017d\x820\xb3\xb4w\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc9\xe0&\b\x06h(\x84\x8a\xeb(\xc76r\xa1)%\x18\x1fM\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xca\x042\xcb\x15{Qy\xf0.\xbb\xa5\xc9\u0475O\xecM\x88\u028965\u026d\xc5\u07a0\x00\x00\u07d4\xca\x12,\xf0\U00094216\xb7HC\xf4\x9a\xfe\u043a\x16\x18\xee\u05c9\x1e[\x8f\xa8\xfe*\xc0\x00\x00\xe0\x94\xca\"\u0363`m\xa5\xca\xd0\x13\xb8\aG\x06\xd7\xe9\xe7!\xa5\f\x8a\x01q\x81\xc6\xfa9\x81\x94\x00\x00\u07d4\xca#\xf6-\xff\rd`\x03lb\xe8@\xae\xc5W~\v\xef\u0489\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\xca%\xff4\x93L\x19B\xe2*N{\xd5o\x14\x02\x1a\x1a\xf0\x88\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xca7?\xe3\xc9\x06\xb8\xc6U\x9e\xe4\x9c\xcd\a\xf3|\xd4\xfbRf\x89a\t=|,m8\x00\x00\u07d4\xcaA\u032c0\x17 R\xd5\"\xcd//\x95}$\x81S@\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xcaB\x88\x01N\xdd\xc5c/_\xac\xb5\xe3\x85\x17\xa8\xf8\xbc]\x98\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xcaB\x88c\xa5\xca06\x98\x92\xd6\x12\x18>\xf9\xfb\x1a\x04\xbc\xea\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\xcaI\xa5\xf5\x8a\xdb\xef\xae#\xeeY\xee\xa2A\xcf\x04\x82b.\xaa\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xcaL\xa9\xe4w\x9dS\x0e\u02ec\xd4~j\x80X\xcf\xdee\u064f\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xcae~\xc0o\xe5\xbc\t\xcf#\xe5*\xf7\xf8\f\xc3h\x9en\u07890\xca\x02O\x98{\x90\x00\x00\u07d4\xcaf\xb2(\x0f\xa2\x82\u0176v1\xceU+b\xeeU\xad\x84t\x89j\xc4\"\xf54\x92\x88\x00\x00\xe0\x94\xcal\x81\x8b\xef\xd2Q6\x1e\x02t@h\xbe\x99\u062a`\xb8J\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcap\xf4\u077f\x06\x9d!C\xbdk\xbc\u007fikRx\x9b2\u7262\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xcatuvDjL\x8f0\xb0\x83@\xfe\xe1\x98\xdec\xec\x92\u03ca\x01|\x8e\x12\x06r*0\x00\x00\u07d4\xca{\xa3\xffSl~_\x0e\x158\x00\xbd8=\xb81)\x98\xe0\x89\t1\xac=k\xb2@\x00\x00\xe0\x94\u0282v\xc4w\xb4\xa0{\x80\x10{\x845\x94\x18\x96\a\xb5;\xec\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u0284\t\b>\x01\xb3\x97\xcf\x12\x92\x8a\x05\xb6\x84U\xceb\x01\u07c9V\xbcu\xe2\xd61\x00\x00\x00\u07d4\u0298\u01d8\x8e\xfa\b\xe9%\uf719ER\x03&\xe9\xf4;\x99\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u029a\x04*j\x80o\xfc\x92\x17\x95\x00\xd2D)\xe8\xabR\x81\x17\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\u029d\xec\x02\x84\x1a\xdf\\\xc9 WjQ\x87\xed\u04bdCJ\x18\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u029f\xaa\x17T/\xaf\xbb8\x8e\xab!\xbcL\x94\u89f3G\x88\x89lk\x8f\xce\r\x18y\x80\x00\xe0\x94\u02aah\xeel\xdf\r4EJv\x9b\r\xa1H\xa1\xfa\xaa\x18e\x8a\x01\x87.\x1d\xe7\xfeR\xc0\x00\x00\u07d4\u02ad\x9d\xc2\rX\x9c\xe4(\xd8\xfd\xa3\xa9\xd5:`{y\x88\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u02b0\xd3,\xf3v\u007f\xa6\xb3S|\x842\x8b\xaa\x9fPE\x816\x8a\x01\xe5\xb8\xfa\x8f\xe2\xac\x00\x00\x00\u07d4\u02b9\xa3\x01\xe6\xbdF\xe9@5P(\xec\xcd@\xceMZ\x1a\u00c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u02b9\xa9z\xda\x06\\\x87\x81nh`\xa8\xf1Bo\xe6\xb3\xd7u\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u02ba\xb6'N\xd1P\x89s~({\xe8x\xb7W\x93Hd\xe2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\u02bd\xaf5OG \xa4f\xa7d\xa5(\xd6\x0e:H*9<\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xca\xcbg^\t\x96#T\x04\ufbfb.\u02c1R'\x1bU\xe0\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\xca\xd1O\x9e\xbb\xa7f\x80\xeb\x83k\a\x9c\u007f{\xaa\xf4\x81\xedm\x89\f\xef={\xd7\xd04\x00\x00\xe0\x94\xca\xe3\xa2S\xbc\xb2\xcfN\x13\xba\x80\u0098\xab\x04\x02\xda|*\xa0\x8a\x01$\xbc\r\u0752\xe5`\x00\x00\u07d4\xca\xef\x02{\x1a\xb5\x04\xc7?A\xf2\xa1\ty\xb4t\xf9~0\x9f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xca\xf4H\x1d\x9d\xb7\x8d\xc4\xf2_{J\u023d;\x1c\xa0\x10k1\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xca\xfd\xe8U\x86L%\x98\xda<\xaf\xc0Z\u064d\U00089380H\x8a\x03\x00\xa8\xed\x96\xffJ\x94\x00\x00\xe0\x94\xcb\r\xd7\xcfN]\x86a\xf6\x02\x89C\xa4\xb9\xb7\\\x91D6\xa7\x8a\x19i6\x89t\xc0[\x00\x00\x00\u07d4\xcb\x1b\xb6\xf1\xda^\xb1\rH\x99\xf7\xe6\x1d\x06\xc1\xb0\x0f\u07f5-\x898E$\xccp\xb7x\x00\x00\u07d4\xcb=vl\x98?\x19+\xce\xca\xc7\x0fN\xe0=\xd9\xffqMQ\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xcbB\xb4N\xb5\xfd`\xb5\x83~O\x9e\xb4rgR=\x1a\"\x9c\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xcbG\xbd0\u03e8\xecTh\xaa\xa6\xa9FB\xce\xd9\xc8\x19\xc8\u0509\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcbH\xfe\x82e\u066fU\xebp\x06\xbc3VE\xb0\xa3\xa1\x83\xbe\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xcbJ\x91M+\xb0)\xf3._\xef\\#LO\xec--\xd5w\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xcbJ\xbf\u0082\xae\xd7n]W\xaf\xfd\xa5B\xc1\xf3\x82\xfc\xac\xf4\x8a\x01\xb9\x0f\x11\xc3\x18?\xaa\x00\x00\u07d4\xcbJ\xd0\xc7#\xdaF\xabV\xd5&\xda\f\x1d%\xc7=\xaf\xf1\n\x89\x1b\xa5\xab\xf9\xe7y8\x00\x00\u07d4\xcbK\xb1\xc6#\xba(\xdcB\xbd\xaa\xa6\xe7N\x1d*\xa1%l*\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xcbPXt\x12\x82#\x04\xeb\u02e0}\xab:\x0f\t\xff\xfe\u4189JD\x91\xbdm\xcd(\x00\x00\u07d4\xcbX\x99\v\u0350\u03ffm\x8f\t\x86\xf6\xfa`\x02v\xb9N-\x8964\xbf9\xab\x98x\x80\x00\u07d4\xcbh\xaeZ\xbe\x02\xdc\xf8\xcb\u016aq\x9c%\x81FQ\xaf\x8b\x85\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xcbty\x10\x9bC\xb2fW\xf4F_M\x18\xc6\xf9t\xbe_B\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xcb}+\x80\x89\xe91,\u026e\xaa's\xf3S\b\xecl*{\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u02c6\xed\xbc\x8b\xbb\x1f\x911\x02+\xe6IV^\xbd\xb0\x9e2\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u02d3\x19\x9b\x9c\x90\xbcI\x15\xbd\x85\x9e=B\x86m\xc8\xc1\x87I\x89\f\x90\xdf\a\xde\xf7\x8c\x00\x00\u07d4\u02d4\xe7o\xeb\xe2\b\x11g3\xe7n\x80]H\xd1\x12\xec\x9f\u028965\u026d\xc5\u07a0\x00\x00\u07d4\u02dbQ\x03\xe4\u0389\xafOd\x91aP\xbf\xf9\xee\u02df\xaa\\\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u02e2\\zP<\xc8\xe0\xd0Iq\xca\x05\xc7b\xf9\xb7b\xb4\x8b\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u02e2\x88\xcd<\x1e\xb4\u055d\xdb\x06\xa6B\x1c\x14\xc3E\xa4{$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u02f3\x18\x9eK\xd7\xf4_\x17\x8b\x1c0\xc7n&1MJK\n\x89\x0f\xfe\vg|e\xa9\x80\x00\xe0\x94\u02f7\xbe\x17\x95?,\u0313\u1f19\x80[\xf4U\x11CNL\x8a\n\xae[\x9d\xf5m/ \x00\x00\xe0\x94\xcb\xc0KM\x8b\x82\xca\xf6p\x99o\x16\f6)@\xd6o\xcf\x1a\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcb\u07974\xb8\xe6\xaaS\x8c)\x1dm\u007f\xac\xed\xb0\xf38\xf8W\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xcb\xe1\xb9H\x86M\x84t\xe7e\x14XX\xfc\xa4U\x0fxK\x92\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xe5/\xc53\xd7\xdd`\x8c\x92\xa2`\xb3|?E\u07b4\xeb3\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcb\xe8\x10\xfe\x0f\xec\xc9dGJ\x1d\xb9w(\xbc\x87\xe9s\xfc\xbd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xf1j\x0f\xe2tRX\xcdR\xdb+\xf2\x19T\xc9u\xfcj\x15\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\xcb\xf3\u007f\xf8T\xa2\xf1\xceS\x93D\x94wx\x92\xd3\xeceW\x82\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xfaj\xf6\u0083\xb0F\xe2w,`c\xb0\xb2\x15S\xc4\x01\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcb\xfav\xdb\x04\xce8\xfb ]7\xb8\xd3w\xcf\x13\x80\xda\x03\x17\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xcc\x03I\x85\xd3\xf2\x8c-9\xb1\xa3K\xce\xd4\u04f2\xb6\xca#N\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xcc\x04\x8d\u01f9]\xca%\xdf&\xee\xfac\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcc+_D\x8f5(\xd3\xfeA\xcc}\x1f\xa9\xc0\xdcv\xf1\xb7v\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xcc-\x04\xf0\xa4\x01q\x89\xb3@\xcaw\x19\x86A\xdc\xf6Ek\x91\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xccA\x9f\u0651+\x85\x13VY\xe7z\x93\xbc=\xf1\x82\xd4Q\x15\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xccE\xfb:U[\xad\x80{8\x8a\x03W\xc8U _|u\xe8\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xccHAM*\xc4\xd4*Yb\xf2\x9e\xeeD\x97\t/C\x13R\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\xccJ/,\xf8l\xf3\xe43u\xf3`\xa4sF\x91\x19_\x14\x90\x89I\x15\x05;\xd1)\t\x80\x00\u07d4\xccO\x0f\xf2\xae\xb6}T\xce;\xc8\xc6Q\v\x9a\xe8>\x9d2\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xccO\xaa\xc0\v\xe6b\x8f\x92\xefk\x8c\xb1\xb1\xe7j\xac\x81\xfa\x18\x89\v\"\xa2\xea\xb0\xf0\xfd\x00\x00\xe0\x94\xccO\xebr\u07d8\xff5\xa18\xe0\x17a\xd1 ?\x9b~\xdf\n\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xcc`oQ\x13\x97\xa3\x8f\u01c7+\u04f0\xbd\x03\xc7\x1b\xbdv\x8b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xcc`\xf86\xac\xde\xf3T\x8a\x1f\xef\u0321>\u01a97\xdbD\xa0\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4\xccl\x03\xbd`>\t\xdeT\xe9\xc4\u056cmA\xcb\xceqW$\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xccl-\xf0\x0e\x86\xec\xa4\x0f!\xff\xda\x1ag\xa1i\x0fG|e\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94\xccm{\x12\x06\x1b\xc9m\x10M`me\xff\xa3+\x006\xeb\a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xccs\xdd5kIy\xb5y\xb4\x01\xd4\xccz1\xa2h\xdd\xceZ\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xccu\x8d\a\x1d%\xa62\n\xf6\x8c]\xc9\xc4\xf6\x95[\xa9E \x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcc{\x04\x81\xcc2\xe6\xfa\xef#\x86\xa0p\"\xbc\xb6\xd2\u00f4\xfc\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94\u0314;\xe1\",\xd1@\n#\x99\xdd\x1bE\x94E\xcfmT\xa9\x8a\x02\xa7@\xaee6\xfc\x88\x00\x00\u07d4\u0315\x19\xd1\xf3\x98_k%^\xad\xed\x12\xd5bJ\x97'!\xe1\x8965\u026d\xc5\u07a0\x00\x00\u0794\u031a\xc7\x15\xcdo&\x10\xc5+XgdV\x88B\x97\x01\x8b)\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\u0320{\xb7\x94W\x1dJ\xcf\x04\x1d\xad\x87\xf0\xd1\xef1\x85\xb3\x19\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u032b\xc6\x04\x8aSFD$\xfc\xf7n\xeb\x9en\x18\x01\xfa#\u0509\x02\xab{&\x0f\xf3\xfd\x00\x00\u07d4\u032e\r=\x85*}\xa3\x86\x0f\x066\x15L\nl\xa3\x16(\u0509\x05\xc6\xd1+k\xc1\xa0\x00\x00\u07d4\xcc\xca$\xd8\xc5mn,\a\xdb\bn\xc0~X[\xe2g\xac\x8d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xcc\xd5!\x13-\x98l\xb9hi\x84&\"\xa7\u0762l>\xd0W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcc\xf49u\xb7k\xfes_\xec<\xb7\xd4\xdd$\xf8\x05\xba\tb\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xcc\xf6*f?\x13S\xba.\xf8\xe6R\x1d\xc1\xec\xb6s\xec\x8e\xf7\x89\b=lz\xabc`\x00\x00\u07d4\xcc\xf7\x11\r\x1b\u0667K\xfd\x1d}}-\x9dU`~{\x83}\x890\xca\x02O\x98{\x90\x00\x00\u07d4\xcc\xfdrW`\xa6\x88#\xff\x1e\x06/L\xc9~\x13`\xe8\u0657\x89\x15\xacV\xed\xc4\xd1,\x00\x00\u07d4\xcd\x02\x0f\x8e\xdf\xcfRG\x98\xa9\xb7:d\x034\xbb\xf7/\x80\xa5\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xcd\x06\xf8\xc1\xb5\u037d(\xe2\xd9kcF\xc3\xe8Z\x04\x83\xba$\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcd\a.n\x183\x13y\x95\x19m{\xb1r_\xef\x87a\xf6U\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcd\n\x16\x1b\xc3g\xae\t'\xa9*\xac\x9c\xf6\xe5\bg\x14\xef\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\xcd\n\xf3GN\"\xf0i\xec4\a\x87\r\xd7pD=[\x12\xb0\x89\x8e^\xb4\xeew\xb2\xef\x00\x00\u07d4\xcd\v\x02W\u70e3\xd2\xc2\u3e9dny\xb7^\xf9\x80$\u0509\x9f\xad\x06$\x12y\x16\x00\x00\u07d4\xcd\x10,\xd6\xdb=\xf1J\u05af\x0f\x87\xc7$y\x86\x1b\xfc=$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcd\x1ef\xedS\x9d\xd9/\xc4\v\xba\xa1\xfa\x16\u078c\x02\xc1ME\x89\fw\xe4%hc\xd8\x00\x00\u07d4\xcd\x1e\xd2c\xfb\xf6\xf6\xf7\xb4\x8a\xef\x8fs=2\x9dC\x82\xc7\u01c9\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4\xcd*6\xd7S\xe9\xe0\xed\x01*XMqh\aX{A\xd5j\x89\x0e+\xa7[\v\x1f\x1c\x00\x00\u07d4\xcd2\xa4\xa8\xa2\u007f\x1c\xc69T\xaacOxW\x05s4\u01e3\x89:\xd1fWlr\xd4\x00\x00\u07d4\xcd5\xff\x01\x0e\xc5\x01\xa7!\xa1\xb2\xf0z\x9c\xa5\x87}\xfc\xf9Z\x89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4\xcdC\x06\xd7\xf6\x94z\xc1tMN\x13\xb8\xef2\xcbe~\x1c\x00\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4\xcdC%\x8bs\x92\xa90\x83\x9aQ\xb2\xef\x8a\xd24\x12\xf7Z\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcdI\xbf\x18^p\xd0E\a\x99\x9f\x92\xa4\xdeDU1('\u040965\u026d\xc5\u07a0\x00\x00\u07d4\xcdU\x10\xa2B\u07f0\x18=\xe9%\xfb\xa8f\xe3\x12\xfa\xbc\x16W\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xcdVj\u05f8\x83\xf0\x1f\u04d9\x8a\x9aX\xa9\xde\xe4rM\u0725\x89\x030\xae\x185\xbe0\x00\x00\xe0\x94\xcdY\xf3\xdd\xe7~\t\x94\v\xef\xb6\xeeX\x03\x19e\xca\xe7\xa36\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcdr]p\xbe\x97\xe6w\xe3\xc8\xe8\\\v&\xef1\xe9\x95PE\x89Hz\x9a0E9D\x00\x00\xe0\x94\xcd~G\x90\x94d\xd8q\xb9\xa6\xdcv\xa8\xe9\x19]\xb3H^z\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xcd~\xce\bkKa\x9b;6\x93R\xee8\xb7\x1d\xdb\x06C\x9a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xcd\u007f\t\xd7\xedf\xd0\u00cb\u016dN2\xb7\xf2\xb0\x8d\xc1\xb3\r\x89>;\xb3M\xa2\xa4p\x00\x00\u07d4\u0355)I+\\)\xe4u\xac\xb9A@+=;\xa5\x06\x86\xb0\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0355\xfaB=o\xc1 'J\xac\xde\x19\xf4\xee\xb7f\xf1\x04 \x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u035bL\xefs9\f\x83\xa8\xfdq\u05f5@\xa7\xf9\u03cb\x8c\x92\x89\x04\xe1\x00;(\xd9(\x00\x00\u07d4\u0361t\x11\t\xc0&[?\xb2\xbf\x8d^\xc9\u00b8\xa34kc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0361\xb8\x86\u39d5\u027aw\x91N\n/\xe5go\x0f\\\u03c9\x05\xbf`\xeaB\xc2\x04\x00\x00\u07d4\u0364S\x0fK\x9b\xc5\t\x05\xb7\x9d\x17\u008f\xc4o\x954\x9b\u07c93\x10\xe0I\x11\xf1\xf8\x00\x00\u07d4\u036bF\xa5\x90 \x80do\xbf\x95B\x04 J\xe8\x84\x04\x82+\x89\x1d\x8a\x96\xe5\xc6\x06\xeb\x00\x00\u07d4\u0375\x97)\x900\x18?n-#\x853\xf4d*\xa5\x87T\xb6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xcd\xd5\u0601\xa76,\x90p\a;\u07fcu\xe7$S\xacQ\x0e\x89-\xa5\x18\xea\xe4\x8e\xe8\x00\x00\u07d4\xcd\xd6\rs\xef\xaa\xd8s\u027b\xfb\x17\x8c\xa1\xb7\x10Z\x81\xa6\x81\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xcd\xd9\xef\xacMm`\xbdq\xd9U\x85\xdc\xe5\u0557\x05\xc15d\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xcd\xe3m\x81\xd1(\u015d\xa1Ee!\x93\xee\u00bf\xd9e\x86\xef\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcd\xea8o\x9d\x0f\xd8\x04\xd0(\x18\xf27\xb7\xd9\xfavF\xd3^\x89\xa3I\xd3m\x80\xecW\x80\x00\u07d4\xcd\xec\xf5gT3\u0370\xc2\xe5Zh\xdb]\x8b\xbexA\x9d\u0489\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xcd\xfd\x82\x173\x97%\xd7\xeb\xac\x11\xa66U\xf2e\xef\xf1\xcc=\x8a\x01\x0f\fid\x10\xe3\xa9\x00\x00\u07d4\xce\a\x9fQ\x88wt\xd8\x02\x1c\xb3\xb5u\xf5\x8f\x18\xe9\xac\xf9\x84\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xce\x18\x84\u077b\xb8\xe1\x0eM\xbanD\xfe\xee\u00a7\xe5\xf9/\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xce\x1b\f\xb4j\xae\xcf\u05db\x88\f\xad\x0f-\u068a\x8d\xed\u0431\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xce&\xf9\xa50_\x83\x81\tCT\xdb\xfc\x92fN\x84\xf9\x02\xb5\x89\fz\xaa\xb0Y\x1e\xec\x00\x00\u07d4\xce-\xea\xb5\x1c\n\x9a\xe0\x9c\xd2\x12\xc4\xfaL\xc5+S\xcc\r\xec\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xce.\r\xa8\x93F\x99\xbb\x1aU>U\xa0\xb8\\\x16\x945\xbe\xa3\x8a\x01\x0f\fid\x10\xe3\xa9\x00\x00\u07d4\xce:a\xf0F\x1b\x00\x93^\x85\xfa\x1e\xad\x82\xc4^Zd\u0508\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xceK\x06]\xbc\xb20G 2b\xfbH\xc1\x18\x83d\x97tp\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xceS\xc8\xcd\xd7B\x96\xac\xa9\x87\xb2\xbc\x19\u00b8u\xa4\x87I\u0409\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xce^\x04\xf0\x18Ci\xbc\xfa\x06\xac\xa6o\xfa\x91\xbfY\xfa\x0f\xb9\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xce^\xb6:{\xf4\xfb\xc2\xf6\u4ea0\u018a\xb1\xcbL\xf9\x8f\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xceb\x12Z\xde\xc37\n\xc5!\x10\x95:Nv\v\xe9E\x1e;\x89\b=lz\xabc`\x00\x00\xe0\x94\xceq\bmL`%T\xb8-\xcb\xfc\xe8\x8d cMS\xccM\x8a\t(\x96R\x9b\xad\u0708\x00\x00\u07d4\u038akmP3\xb1I\x8b\x1f\xfe\xb4\x1aAU\x04\x05\xfa\x03\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0397\x86\xd3q/\xa2\x00\xe9\xf6\x857\xee\xaa\x1a\x06\xa6\xf4ZK\x89a\t=|,m8\x00\x00\u07d4\u039d!\u0192\xcd<\x01\xf2\x01\x1fP_\x87\x006\xfa\x8fl\u0489\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\u03a2\x89f#\xf4\x91\x02\x87\xa2\xbd\u017e\x83\xae\xa3\xf2\xe6\xde\b\x8a\x01\xfbZ7Q\xe4\x90\xdc\x00\x00\u07d4\u03a3JM\xd9=\u066e\xfd9\x90\x02\xa9}\x99z\x1bK\x89\u0349QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\u03a4?pu\x81k`\xbb\xfc\u62d9:\xf0\x88\x12p\xf6\u0109lk\x93[\x8b\xbd@\x00\x00\u07d4\u03a8t3AS<\xb2\xf0\xb9\xc6\xef\xb8\xfd\xa8\rw\x16(%\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u03b0\x89\xec\x8ax3~\x8e\xf8\x8d\xe1\x1bI\xe3\u0751\x0ft\x8f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u03b3=x\xe7Tz\x9d\xa2\xe8}Q\xae\xc5\xf3D\x1c\x87\x92:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u03b3\x898\x1dH\xa8\xaeO\xfcH:\u043b^ L\xfd\xb1\xec\x89('\xe6\xe4\xddb\xba\x80\x00\u07d4\xce\xc6\xfce\x85?\x9c\xce_\x8e\x84Fv6.\x15y\x01_\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xce\xd3\u01fe\x8d\xe7XQ@\x95*\xebP\x1d\xc1\xf8v\ucbf0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xce\xd8\x1e\xc3S?\xf1\xbf\xeb\xf3\xe3\x84>\xe7@\xad\x11u\x8d>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xce\u0733\xa1\u0584?\xb6\xbe\xf6Ca}\xea\U000cf398\xdd_\x89\x19\xe2\xa4\xc8\x18\xb9\x06\x00\x00\u07d4\xce\xe6\x99\xc0pzx6%+)/\x04|\xe8\xad(\x9b/U\x89\x11\x9a\x1e!\xaaiV\x00\x00\u07d4\xce\xedG\xca[\x89\x9f\xd1b?!\xe9\xbdM\xb6Z\x10\u5c1d\x89\a8w@L\x1e\xee\x00\x00\u07d4\xce\xf7tQ\u07e2\xc6C\xe0\v\x15mlo\xf8N#s\xebf\x89\n1\x06+\xee\xedp\x00\x00\u07d4\xcf\x11i\x04\x1c\x17E\xe4[\x17$5\xa2\xfc\x99\xb4\x9a\xce+\x00\x89\x01\xbb\x88\xba\xab-|\x00\x00\xe0\x94\xcf\x15v\x12vN\x0f\u0596\xc8\xcb_\xba\x85\xdfL\r\xdc<\xb0\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u0794\xcf\x1b\xdby\x9b.\xa6<\xe14f\x8b\xdc\x19\x8bT\x84\x0f\x18\v\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xcf\"\x88\xefN\xbf\x88\xe8m\xb1=\x8a\x0e\v\xf5*\x05e\x82\u00c9\x89Po\xbf\x97@t\x00\x00\u07d4\xcf&Ni%\x13\t\x06\xc4\xd7\xc1\x85\x91\xaaA\xb2\xa6\u007foX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf&\xb4{\xd04\xbcP\x8elK\xcf\xd6\xc7\xd3\x004\x92Wa\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xcf.*\xd65\xe9\x86\x1a\xe9\\\xb9\xba\xfc\xca\x03kR\x81\xf5\u038a\at2!~h6\x00\x00\x00\u07d4\xcf.s@B\xa3U\xd0_\xfb.9\x15\xb1h\x11\xf4Zi^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf4\x8f/\xe4{~A<\az{\xaf:u\xfb\xf8B\x86\x92\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xcf?\x91(\xb0r\x03\xa3\xe1\r}WU\xc0\u012b\xc6\xe2\xca\u008a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xcf?\xbf\xa1\xfd2\u05e6\xe0\xe6\xf8\xefN\xabW\xbe4\x02\\L\x899\xa1\xc0\xf7YMH\x00\x00\u07d4\xcfAftn\x1d;\xc1\xf8\xd0qK\x01\xf1~\x8ab\xdf\x14d\x896w\x03n\xdf\n\xf6\x00\x00\u07d4\xcfO\x118\xf1\xbdk\xf5\xb6\u0505\xcc\xe4\xc1\x01\u007f\u02c5\xf0}\x89/\u043cw\xc3+\xff\x00\x00\u07d4\xcfZo\x9d\xf7Uy\xc6D\xf7\x94q\x12\x15\xb3\rw\xa0\xce@\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf^\x0e\xac\u0473\x9d\x06U\xf2\xf7u5\xeff\b\xeb\x95\v\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcfhM\xfb\x83\x04r\x93U\xb5\x83\x15\xe8\x01\x9b\x1a\xa2\xad\x1b\xac\x89\x17r$\xaa\x84Lr\x00\x00\u07d4\xcfi@\x81\xc7m\x18\xc6L\xa7\x13\x82\xbe\\\xd6;<\xb4v\xf8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xcfnR\xe6\xb7t\x80\xb1\x86~\xfe\xc6Dm\x9f\xc3\xcc5w\xe8\x89\f\t\x01\xf6\xbd\x98y\x00\x00\u07d4\u03c8: 2\x96g\xea\"j\x1e\x9a\x92*\x12\xf2\x1f\xaa\x03\x81V\x91\x8cO\u02dc\x89\x04E\x91\xd6\u007f\xec\xc8\x00\x00\u07d4\xcf\xf7\xf8\x9aMB\x19\xa3\x82\x95%\x131V\x82\x10\xff\xc1\xc14\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xcf\xf8\xd0k\x00\xe3\xf5\f\x19\x10\x99\xadV\xbaj\xe2eq\u0348\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcf\xfcI\xc1x~\ubcb5l\xab\xe9$\x04\xb66\x14}EX\x8a\x013\xe00\x8f@\xa3\u0680\x00\u07d4\xd0\bQ;'`J\x89\xba\x17c\xb6\xf8L\u6233F\x94[\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\x0f\x06r\x86\xc0\xfb\u0402\xf9\xf4\xa6\x10\x83\xecv\u07b3\xce\xe6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\x15\xf6\xfc\xb8M\xf7\xbbA\x0e\x8c\x8f\x04\x89J\x88\x1d\xca\xc27\x898E$\xccp\xb7x\x00\x00\u07d4\xd0\x1a\xf9\x13O\xafRW\x17N\x8by\x18oB\xee5Nd-\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0!\b\u04ae<\xab\x10\xcb\xcf\x16W\xaf\">\x02|\x82\x10\xf6\x89lm\x84\xbc\xcd\xd9\xce\x00\x00\u07d4\xd0*\xfe\u03ce.\u00b6*\u022d Aa\xfd\x1f\xaew\x1d\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd01\x919\xfb\xab.\x8e*\xcc\xc1\xd9$\u0531\x1d\xf6ilZ\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd07\xd2\x15\xd1\x1d\x1d\xf3\xd5O\xbd2\x1c\u0495\xc5F^';\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xd0:-\xa4\x1e\x86\x8e\xd3\xfe\xf5t[\x96\xf5\xec\xa4b\xffo\u0689\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xd0?\xc1eWj\xae\xd5%\xe5P,\x8e\x14\x0f\x8b.\x86\x969\x8a\x01sV\u0633%\x01\xc8\x00\x00\u07d4\xd0C\xa0\x11\xecBp\xee~\u0239hsu\x15\xe5\x03\xf80(\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd0K\x86\x1b=\x9a\xccV:\x90\x16\x89\x94\x1a\xb1\xe1\x86\x11a\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd0ZD|\x91\x1d\xbb'[\xfb.Z7\xe5\xa7\x03\xa5o\x99\x97\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd0_\xfb+t\xf8g O\xe51e;\x02H\xe2\x1c\x13TN\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0bX\x81q\u03d9\xbb\xebX\xf1&\xb8p\xf9\xa3r\x8da\xec\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\u07d4\xd0c\x8e\xa5q\x89\xa6\xa6\x99\x02J\u05ccq\xd99\xc1\xc2\xff\x8c\x89\x8e\xaeVg\x10\xfc \x00\x00\xe0\x94\xd0d\x8aX\x1b5\b\xe15\xa2\x93]\x12\xc9epE\xd8q\u028a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4\xd0q\x19)f\xebi\xc3R\x0f\xca:\xa4\xdd\x04)~\xa0KN\x89\x05\xf6\x8e\x811\xec\xf8\x00\x00\u07d4\xd0q\x85 \xea\xe0\xa4\xd6-p\xde\x1b\xe0\xcaC\x1c^\xea$\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd0w]\xba*\xf4\xc3\n:x6Y9\xcdq\xc2\xf9\u0795\u0489i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xd0{\xe0\xf9\t\x97\xca\xf9\x03\u022c\x1dS\xcd\xe9\x04\xfb\x19\aA\x8968\x908\xb6\x99\xb4\x00\x00\u07d4\xd0~Q\x18d\xb1\u03d9i\xe3V\x06\x02\x82\x9e2\xfcNq\xf5\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\u0400\x94\x98\xc5H\x04z\x1e**\xa6\xa2\x9c\xd6\x1a\x0e\xe2h\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0402'_tZ,\xac\x02v\xfb\xdb\x02\u0532\xa3\xab\x17\x11\xfe\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\u040f\xc0\x9a\x000\xfd\t(\xcd2\x11\x98X\x01\x82\xa7j\xae\x9f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0413\xe8)\x81\x9f\xd2\xe2[\x978\x00\xbb=XA\xdd\x15-\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0414J\xa1\x85\xa13pa\xae \u071d\xd9l\x83\xb2\xbaF\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u0416V[|t\a\xd0e6X\x03U\xfd\xd6\xd29\x14J\xa1\x89\r\x8drkqw\xa8\x00\x00\u07d4\u041c\xb2\xe6\b-i:\x13\xe8\xd2\xf6\x8d\xd1\u0744a\xf5X@\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0426\xc6\xf9\xe9\u0133\x83\xd7\x16\xb3\x1d\xe7\x8dVAM\xe8\xfa\x91\x89\x10CV\x1a\x88)0\x00\x00\u07d4\u0427 \x9b\x80\xcf`\xdbb\xf5}\n]}R\x1ai`fU\x89\b\xacr0H\x9e\x80\x00\x00\xe0\x94\u0428\xab\xd8\n\x19\x9bT\xb0\x8be\xf0\x1d \x9c'\xfe\xf0\x11[\x8a\x01a\xc6&\xdca\xa2\xef\x80\x00\xe0\x94\u042b\xccp\xc0B\x0e\x0e\x17/\x97\xd4;\x87\xd5\xe8\f3n\xa9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u042es]\x91^\x94hf\xe1\xfe\xa7~^\xa4f\xb5\xca\xdd\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0431\x1do+\u0394^\fjP \u00f5'S\xf8\x03\xf9\u0449\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xd0\xc1\x01\xfd\x1f\x01\xc6?k\x1d\x19\xbc\x92\r\x9f\x93#\x14\xb16\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd0\xc5Z\xbf\x97o\xdc=\xb2\xaf\u9f99\u0519HMWl\x02\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\u0422\xadE\xf5\x9a\x9d\xcc\u0195\xd8_%\xcaF\xed1\xa5\xa3\x89-\x89W}}@ \x00\x00\u07d4\xd0\xd6,G\xea`\xfb\x90\xa3c\x92\t\xbb\xfd\xd4\xd93\x99\x1c\u0189\n\x84Jt$\xd9\xc8\x00\x00\u07d4\xd0\xdbEax o\\D0\xfe\x00Pc\x90<=zI\xa7\x89&I\x1eE\xa7S\xc0\x80\x00\u07d4\xd0\xe1\x94\xf3K\x1d\xb6\t(\x85\t\xcc\xd2\xe7;a1\xa2S\x8b\x8965f3\xeb\xd8\xea\x00\x00\u07d4\xd0\xe3^\x04vF\xe7Y\xf4Qp\x93\xd6@\x86BQ\u007f\bM\x89\u054f\xa4h\x18\xec\u02c0\x00\u07d4\xd0\xeeM\x02\xcf$8,0\x90\xd3\xe9\x95`\xde6xs\\\u07c9\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xd0\xf0OR\x10\x9a\xeb\xec\x9a{\x1e\x932v\x1e\x9f\xe2\xb9{\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd0\xf9Yx\x11\xb0\xb9\x92\xbb}7W\xaa%\xb4\xc2V\x1d2\xe2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\x03\x02\xfa\xa1\x92\x9a2i\x04\xd3v\xbf\v\x8d\xc9:\xd0LL\x89a\t=|,m8\x00\x00\xe0\x94\xd1\x10\r\xd0\x0f\xe2\xdd\xf1\x81c\xad\x96M\vi\xf1\xf2\xe9e\x8a\x8a\x01C\x12\tU\xb2Pk\x00\x00\u07d4\xd1\x16\xf3\xdc\xd5\xdbtK\xd0\b\x88v\x87\xaa\x0e\xc9\xfdr\x92\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd1\x19A|Fs,\xf3M\x1a\x1a\xfby\xc3\xe7\xe2\u034e\xec\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1-w\xae\x01\xa9-5\x11{\xacpZ\xac\u0642\xd0.t\xc1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd15yK\x14\x9a\x18\xe1G\xd1nb\x1ai1\xf0\xa4\n\x96\x9a\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xd1C%8\xe3[vd\x95j\u4563*\xbd\xf0A\xa7\xa2\x1c\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xd1C\x82g#\x17\x04\xfcr\x80\xd5c\xad\xf4v8D\xa8\a\"\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd1S\x8e\x9a\x87\u5729\xec\x8eX&\xa5\xb7\x93\xf9\x9f\x96\xc4\u00c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd1d\x85\x03\xb1\xcc\u0178\xbe\x03\xfa\x1e\xc4\xf3\xee&~j\xdf{\x8a\x01;\xef\xbfQ\xee\xc0\x90\x00\x00\xe0\x94\xd1h,!Y\x01\x8d\xc3\xd0\u007f\b$\n\x8c`m\xafe\xf8\xe1\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xd1q\xc3\xf2%\x8a\xef5\xe5\x99\xc7\xda\x1a\xa0s\x00#M\xa9\xa6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1w\x8c\x13\xfb\xd9h\xbc\b<\xb7\xd1\x02O\xfe\x1fI\xd0,\xaa\x89\xd9\xec\xb4\xfd \x8eP\x00\x00\u07d4\xd1\u007f\xbe\"\xd9\x04b\xed7(\x06p\xa2\xea\v0\x86\xa0\xd6\u0589\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\u0441\x1cU\x97i\x80\xf0\x83\x90\x1d\x8a\r\xb2i\"-\xfb\\\xfe\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u044e\xb9\xe1\u0485\u06be\x93\xe5\u053a\xe7k\xee\xfeC\xb5!\xe8\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\u0453\xe5\x83\xd6\a\x05c\xe7\xb8b\xb9aJG\u9509\xf3\xe5\x8965f3\xeb\xd8\xea\x00\x00\u07d4\u0457\x8f.4@\u007f\xab\x1d\xc2\x18=\x95\xcf\xdab`\xb3Y\x82\x89*\xb7\xb2`\xff?\xd0\x00\x00\u07d4\u045c\xaf9\xbb7\u007f\xdf,\xf1\x9b\xd4\xfbRY\x1c&1\xa6<\x8965\u026d\xc5\u07a0\x00\x00\u0794\u0463\x96\xdc\u06b2\xc7IA0\xb3\xfd0x 4\r\xfd\x8c\x1f\x88\xf9\"P\xe2\xdf\xd0\x00\x00\xe0\x94\u0467\x1b-\bX\xe82p\b]\x95\xa3\xb1T\x96P\x03^#\x8a\x03'\xbb\t\xd0j\xa8P\x00\x00\u07d4\u046c\xb5\xad\xc1\x189s%\x8dk\x85$\xff\xa2\x8f\xfe\xb2=\xe3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0473\u007f\x03\xcb\x10t$\xe9\xc4\xddW\\\xcdOL\xeeW\xe6\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4\u0475\xa4T\xac4\x05\xbbAy \x8cl\x84\xde\x00k\u02db\xe9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\xc4YT\xa6+\x91\x1a\xd7\x01\xff.\x90\x13\x1e\x8c\xeb\x89\xc9\\\x89K\x91\xa2\xdeE~\x88\x00\x00\u07d4\xd1\xc9np\xf0Z\xe0\xe6\xcd`!\xb2\b7P\xa7q|\xdeV\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\u0571\u007f\xfe-{\xbby\xcc}y0\xbc\xb2\xe5\x18\xfb\x1b\xbf\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd1\xda\f\x8f\xb7\xc2\x10\xe0\xf2\xeca\x8f\x85\xbd\xae}>sK\x1c\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd1\xddy\xfb\x15\x81`\xe5\xb4\xe8\xe2?1.j\x90\u007f\xbcMN\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\xdeZ\xad:_\xd8\x03\U00071bb6\x10<\xb8\xe1O\xe7#\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd1\xe1\xf2\xb9\xc1l0\x98t\xde\xe7\xfa\xc3&u\xaf\xf1)\u00d8\x89\x03\xf2M\x8eJ\x00p\x00\x00\xe0\x94\xd1\xe5\xe24\xa9\xf4Bf\xa4\xa6$\x1a\x84\u05e1\xa5Z\u0567\xfe\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd1\xeaMr\xa6{[>\x0f1UY\xf5+\xd0aMq0i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1\xee\x90YW\xfe|\xc7\x0e\xc8\xf2\x86\x8bC\xfeG\xb1?\xeb\xff\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4\xd1\xf1iM\"g\x1bZ\xadj\x94\x99\\6\x9f\xbea3go\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd1\xf4\xdc\x1d\u06ca\xbb\x88H\xa8\xb1N%\xf3\xb5Z\x85\x91\xc2f\x89\r\x8drkqw\xa8\x00\x00\u07d4\xd1\xfe\u042e\xe6\xf5\xdf\xd7\xe2Wi%L<\xfa\xd1Z\xde\u032a\x89'\x92\xc8\xfcKS(\x00\x00\u07d4\xd2\x05\x1c\xb3\xcbg\x04\xf0T\x8c\u0210\xab\n\x19\xdb4\x15\xb4*\x89\x12\x1b.^ddx\x00\x00\u07d4\xd2\x06\xaa\u07736\xd4^yr\xe9<\xb0uG\x1d\x15\x89{]\x89 \x86\xac5\x10R`\x00\x00\u07d4\xd2\tH+\xb5I\xab\xc4w{\xeam\u007fe\x00b\xc9\xc5z\x1c\x89\x11e\x1a\xc3\xe7\xa7X\x00\x00\u07d4\xd2\r\xcb\vxh+\x94\xbc0\x00(\x14H\xd5W\xa2\v\xfc\x83\x890\x84\x9e\xbe\x166\x9c\x00\x00\u07d4\xd2\x10{57&\u00e2\xb4ef\xea\xa7\xd9\xf8\v]!\xdb\xe3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd2\x11\xb2\x1f\x1b\x12\xb5\ta\x81Y\r\xe0~\xf8\x1a\x89S~\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd2\x18\xef\xb4\u06d8\x1c\xddjy\u007fK\u050c|&)<\xeb@\x89\xa1Fk1\xc6C\x1c\x00\x00\xe0\x94\xd2\x1asA\xeb\x84\xfd\x15\x10T\xe5\u31fb%\xd3nI\x9c\t\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xd2$\xf8\x80\xf9G\x9a\x89\xd3/\t\xe5+\u9432\x88\x13\\\xef\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xd2/\f\xa4\xcdG\x9ef\x17u\x05;\xccI\xe3\x90\xf6p\u074a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd21\x92\x975\x13!\x02G\x1b\xa5\x90\a\xb6dL\xc0\xc1\xde>\x8967\tlK\xcci\x00\x00\u07d4\xd25\xd1\\\xb5\xec\xee\xbba)\x9e\x0e\x82\u007f\xa8'H\x91\x1d\x89\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd2:$\xd7\xf9F\x83C\xc1C\xa4\x1ds\xb8\x8f|\xbec\xbe^\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd2=z\xff\xac\xdc>\x9f=\xaez\xfc\xb4\x00oX\xf8\xa4F\x00\x89\xc3(\t>a\xee@\x00\x00\u07d4\xd2C\x18L\x80\x1e]y\xd2\x06?5x\u06ee\x81\u7ce9\u02c9k\u0722h\x1e\x1a\xba\x00\x00\u07d4\xd2KfD\xf49\xc8\x05\x1d\xfcd\u04c1\xb8\xc8lu\xc1u8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd2K\xf1--\xdfE}\xec\xb1xt\xef\xde R\xb6\\\xbbI\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xd2Q\xf9\x03\xae\x18rrY\xee\xe8A\xa1\x89\xa1\xf5i\xa5\xfdv\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd2R\x96\v\v\xf6\xb2\x84\x8f\u07ad\x80\x13m\xb5\xf5\a\xf8\xbe\x02\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd2X\x1aU\xce#\xab\x10\u062d\x8cD7\x8fY\a\x9b\xd6\xf6X\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\xd2Z\xec\xd7\xeb\x8b\xd64[\x06;]\xbd'\x1cw\xd3QD\x94\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xd2|#O\xf7\xac\xca\xce=\x99g\b\xf8\xf9\xb0Ip\xf9}6\x89Hz\x9a0E9D\x00\x00\u07d4\u0482\x98RM\xf5\xecK$\xb0\xff\xb9\u07c5\x17\n\x14Z\x9e\xb5\x89\x0f\x98\xa3\xb9\xb37\xe2\x00\x00\xe0\x94\u0483\xb8\xed\xb1\n%R\x8aD\x04\xde\x1ce\xe7A\r\xbc\xaag\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\u0484\xa5\x03\x82\xf8:am9\xb8\xa9\xc0\xf3\x96\xe0\ubfe9]\x8966\xc2^f\xec\xe7\x00\x00\u07d4\u0488\xe7\xcb{\xa9\xf6 \xab\x0ftR\xe5\bc=\x1cZ\xa2v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u049d\xc0\x8e\xfb\xb3\xd7.&?x\xabv\x10\xd0\"m\xe7k\x00\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\u04a00\xac\x89R2_\x9e\x1d\xb3x\xa7\x14\x85\xa2N\x1b\a\xb2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u04a4y@CG\xc5T:\xab)*\xe1\xbbJo\x15\x83W\xfa\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u04a5\xa0$#\nW\xcc\xc6fv\v\x89\xb0\xe2l\xaf\u0449\u01ca\n\x96YZ\\n\x8a?\x80\x00\u07d4\u04a8\x03'\xcb\xe5\\L{\xd5\x1f\xf9\xdd\xe4\xcad\x8f\x9e\xb3\xf8\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\u04a8Oug\\b\xd8\f\x88ulB\x8e\xee+\xcb\x18T!\x89A\rXj \xa4\xc0\x00\x00\u07d4\u04ab\xd8J\x18\x10\x93\xe5\xe2)\x13oB\xd85\xe8#]\xe1\t\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\u04ac\r:X`^\x1d\x0f\x0e\xb3\xde%\xb2\xca\xd1)\xed`X\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u04bfg\xa7\xf3\xc6\xceV\xb7\xbeAg]\xbb\xad\xfe~\xa9:3\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd2\xdb\xeb\xe8\x9b\x03W\xae\xa9\x8b\xbe\x8eIc8\u07bb(\xe8\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd2\xe2\x1e\xd5hh\xfa\xb2\x8e\tG\x92z\xda\xf2\x9f#\xeb\xadl\x89l\x18O\x13U\xd0\xe8\x00\x00\u07d4\xd2\xe8\x17s\x8a\xbf\x1f\xb4\x86X?\x80\xc3P1\x8b\xed\x86\f\x80\x89\r\x02\xce\xcf_]\x81\x00\x00\u07d4\xd2\xed\xd1\xdd\xd6\xd8m\xc0\x05\xba\xebT\x1d\"\xb6@\xd5\xc7\xca\xe5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd2\xf1\x99\x8e\x1c\xb1X\f\xecOl\x04}\xcd=\xce\xc5L\xf7<\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd2\xf2A%]\xd7\xc3\xf7<\a\x040q\xec\b\xdd\xd9\xc5\xcd\xe5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd2\xffg \x16\xf6;/\x859\x8fJo\xed\xbb`\xa5\r<\u0389\x12\x91$o[sJ\x00\x00\u07d4\xd3\rLC\xad\xcfU\xb2\xcbS\u0583#&A4I\x8d\x89\u038965\u026d\xc5\u07a0\x00\x00\u07d4\xd3\x0e\xe9\xa1+Mh\xab\xac\xe6\xba\u029a\u05ff\\\xd1\xfa\xf9\x1c\x89QO\xcb$\xff\x9cP\x00\x00\u07d4\xd3\x11\x8e\xa3\xc85\x05\xa9\u0613\xbbg\xe2\xde\x14-Sz>\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd3\x11\xbc\u05eaN\x9bO8?\xf3\xd0\u05b6\xe0~!\xe3p]\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd3\x15\xde\xea\x1d\x8c\x12q\xf9\xd11\x12c\xabG\xc0\a\xaf\xb6\xf5\x89\x03\xc8\x1dNeK@\x00\x00\u07d4\xd3+,y\xc3dx\xc5C\x19\x01\xf6\xd7\x00\xb0M\xbe\x9b\x88\x10\x89\x15w\x9a\x9d\xe6\xee\xb0\x00\x00\u07d4\xd3+EVF\x14Ql\x91\xb0\u007f\xa9\xf7-\xcfx|\xceN\x1c\x89\x0f\xc6o\xae7F\xac\x00\x00\u07d4\xd30r\x811\xfe\x8e:\x15Hz4W<\x93E~*\xfe\x95\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd31\xc8#\x82Z\x9eRc\xd0R\u0611]M\xcd\xe0z\\7\x89\x1e\x93\x12\x83\xcc\xc8P\x00\x00\u07d4\xd33btE\xf2\u05c7\x90\x1e\xf3;\xb2\xa8\xa3g^'\xff\xec\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd3<\xf8+\xf1LY&@\xa0\x86\b\x91L#py\u057e4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd3Mp\x8ds\x98\x02E3\xa5\xa2\xb20\x9b\x19\xd3\xc5Qq\xbb\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd3N\x03\xd3j+\xd4\u045a_\xa1b\x18\xd1\xd6\x1e?\xfa\v\x15\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\xd3Pu\xcaa\xfeY\xd1#\x96\x9c6\xa8-\x1a\xb2\xd9\x18\xaa8\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xd3g\x00\x9a\xb6X&;b\xc23:\x1c\x9eA@I\x8e\x13\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd3g\x9aG\xdf-\x99\xa4\x9b\x01\u024d\x1c>\f\x98|\xe1\xe1X\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94\u04cf\xa2\xc4\xcc\x14z\xd0j\u0562\xf7Uy(\x1f\"\xa7\xcc\x1f\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\u04da]\xa4`9+\x94\v\u01ee8\xf1e\u007f\x8a\x01f\xc5H\b\x89\xdbw\x00\x00\xe0\x94\xd3\xd6\xe9\xfb\x82T/\u049e\xd9\xea6\t\x89\x1e\x15\x13\x96\xb6\xf7\x8a\voX\x8a\xa7\xbc\xf5\xc0\x00\x00\xe0\x94\xd3\xda\u0476\u040dE\x81\u032ee\xa8s-\xb6\xaci\xf0\u019e\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xd3\xdf;S\xcb;GU\xdeT\xe1\x80E\x1c\xc4L\x9e\x8a\u0a89#\u0114\t\xb9w\x82\x80\x00\u07d4\xd3\xf8s\xbd\x99V\x13W\x89\xab\x00\xeb\xc1\x95\xb9\"\xe9K%\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\x02\xb4\xf6\xa0\x99\xeb\xe7\x16\xcb\x14\xdfOy\xc0\xcd\x01\xc6\a\x1b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd4\r\x00U\xfd\x9a8H\x8a\xff\x92?\xd0=5\xecF\xd7\x11\xb3\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\xd4\x0e\xd6j\xb3\xce\xff$\xca\x05\xec\xd4q\ufd12\xc1__\xfa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd4\x18\x87\v\xc2\xe4\xfa{\x8aa!\xae\br\xd5RG\xb6%\x01\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xd4\x1d\u007f\xb4\x9f\xe7\x01\xba\xac%qpBl\u0273\x8c\xa3\xa9\xb2\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4\xd4 U\x92\x84@U\xb3\u01e1\xf8\f\xef\xe3\xb8\xebP\x9b\xcd\xe7\x89\t\xb3\xbf\xd3B\xa9\xfc\x80\x00\u07d4\xd4+ \xbd\x03\x11`\x8bf\xf8\xa6\xd1[*\x95\xe6\xde'\u017f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd44O}\\\xade\xd1~\\-\x0es#\x94=ob\xfe\x92\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xd4>\xe48\xd8=\xe9\xa3ub\xbbN(l\xb1\xbd\x19\xf4\x96M\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd4C4\xb4\xe2:\x16\x9a\f\x16\xbd!\xe8f\xbb\xa5-\x97\x05\x87\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\xe0\x94\xd4M\x81\xe1\x8fF\xe2\u03f5\xc1\xfc\xf5\x04\x1b\xc8V\x97g\xd1\x00\x8a\a\xb4B\xe6\x84\xf6Z\xa4\x00\x00\u07d4\xd4OJ\xc5\xfa\xd7k\xdc\x157\xa3\xb3\xafdr1\x9bA\r\x9d\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\xd4O^\xdf+\xcf$3\xf2\x11\xda\xdd\f\xc4P\xdb\x1b\x00\x8e\x14\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xd4Oj\u00d2;_\xd71\xa4\xc4YD\xecO~\xc5*j\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd4[3A\xe8\xf1\\\x802\x93 \u00d7~;\x90\xe7\x82j~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd4]]\xaa\x13\x8d\xd1\xd3t\xc7\x1b\x90\x19\x91h\x11\xf4\xb2\nN\x89\x1f9\x9b\x148\xa1\x00\x00\x00\u07d4\xd4`\xa4\xb9\b\xdd+\x05gY\xb4\x88\x85\vf\xa88\xfcw\xa8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd4g\xcf\x06L\bq\x98\x9b\x90\u0632\xeb\x14\xcc\xc6;6\b#\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd4k\xaea\xb0'\xe5\xbbB.\x83\xa3\xf9\xc9?<\x8f\xc7}'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4o\x82#E)\x82\xa1\xee\xa0\x19\xa8\x81n\xfc-o\xc0\ah\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xd4uG\u007f\xa5c\x90\xd30\x17Q\x8dg\x11\x02\u007f\x05\U0008dfc9k\x11\x133\xd4\xfdL\x00\x00\u07d4\xd4|$.\xdf\xfe\xa0\x91\xbcT\xd5}\xf5\xd1\xfd\xb91\x01Gl\x89\x9d\xf7\u07e8\xf7`H\x00\x00\u07d4\xd4}\x86\x85\xfa\xee\x14|R\x0f\u0646p\x91u\xbf/\x88k\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\u007fP\u07c9\xa1\xcf\xf9e\x13\xbe\xf1\xb2\xae:)q\xac\xcf,\x89-\x89W}}@ \x00\x00\u07d4\u0502\xe7\xf6\x8eA\xf28\xfeQx)\xde\x15G\u007f\xe0\xf6\xdd\x1d\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u0507\x9f\xd1+\x1f:'\xf7\xe1\tv\x1b#\xca4\xfa#\x06K\x1c\xaf\x00Qn(pJ\x82\xa4\xf8\x89Hz\x9a0E9D\x00\x00\u07d4\xd5\x00\xe4\xd1\u0242K\xa9\xf5\xb65\u03e3\xa8\xc2\u00cb\xbdL\xed\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd5\b\u04dcp\x91oj\xbcL\xc7\xf9\x99\xf0\x11\xf0w\x10X\x02\x89\x05rM$\xaf\xe7\u007f\x00\x00\u07d4\xd5\x0f\u007f\xa0>8\x98v\u04d0\x8b`\xa57\xa6pc\x04\xfbV\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd5\x13\xa4P\x80\xff/\xeb\xe6,\u0545J\xbe)\xeeDg\xf9\x96\x89\bN\x13\xbcO\xc5\xd8\x00\x00\u07d4\xd5'o\f\xd5\xff\xd5\xff\xb6?\x98\xb5p=U\x94\xed\xe0\x83\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd5)KfbB0;m\xf0\xb1\u020d7B\x9b\xc8\xc9e\xaa\x89\x10M\r\x00\u04b7\xf6\x00\x00\u07d4\xd5*\xec\xc6I98\xa2\x8c\xa1\xc3g\xb7\x01\xc2\x15\x98\xb6\xa0.\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\xd5\x99x\xee \xa3\x8c?I\x8dc\xd5\u007f1\xa3\x9fj\x06\x8a\x022\xb3o\xfcg*\xb0\x00\x00\u07d4\u05568\xd3\xc5\xfa\xa7q\x1b\xf0\x85t_\x9d[\xdc#\u0518\u0609lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u055d\x92\xd2\xc8p\x19\x80\xcc\a<7]r\n\xf0dt<\f\x8a\x04\x05\xfd\xf7\u5bc5\xe0\x00\x00\u07d4\u0567\xbe\xc32\xad\xde\x18\xb3\x10KW\x92Tj\xa5\x9b\x87\x9bR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0571\x17\xec\x11n\xb8FA\x89a\xeb~\xdbb\x9c\xd0\xddi\u007f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\u0572\x84\x04\x010\xab\xf7\xc1\xd1cq#q\xcc~(\xadf\u0689j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0579\xd2w\u062a\xd2\x06\x97\xa5\x1fv\xe2\tx\x99k\xff\xe0U\x89\a\xc3\xfe<\aj\xb5\x00\x00\u07d4\u057d^\x84U\xc10\x16\x93W\xc4q\xe3\u6077\x99jrv\x89-\x9e(\x8f\x8a\xbb6\x00\x00\u07d4\xd5\u02e5\xb2k\xea]s\xfa\xbb\x1a\xba\xfa\xcd\xef\x85\xde\xf3h\u0309\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd5\xceU\u0476/YC\xc0?\x89\b\xe3\x1f\xe1h\x9d\x8a\x00\x00\u07d4\xd6\x06Q\xe3\x93x4#\xe5\xcc\x1b\xc5\xf8\x89\xe4N\xf7\xea$>\x89\x15\x9ev7\x11)\xc8\x00\x00\u07d4\xd6\t\xbfO\x14n\xeak\r\xc8\xe0m\xdc\xf4D\x8a\x1f\xcc\xc9\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\t\xec\v\xe7\r\n\xd2ong\xc9\xd4v+R\xeeQ\x12,\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd6\nRX\a(R\r\xf7Tk\xc1\xe2\x83)\x17\x88\u06ee\f\x8964\x89\xef?\xf0\xd7\x00\x00\u07d4\xd6\v$s!\xa3*Z\xff\xb9k\x1e'\x99'\xccXM\xe9C\x89z\xd0 \xd6\xdd\xd7v\x00\x00\u07d4\xd6\x11\x02v\xcf\xe3\x1eB\x82ZW\u007fkC]\xbc\xc1\f\xf7d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd6\x12Y{\xc3\x17C\u01c63\xf63\xf29\xb1\xe9Bk\xd9%\x8a\x10\x17\xf7\u07d6\xbe\x17\x80\x00\x00\u07d4\xd6#J\xafE\xc6\xf2.f\xa2%\xff\xb9:\xddb\x9bN\xf8\x0f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd6.\u06d6\xfc\u259a\xaflT^\x96|\xf1\xc0\xbc\x80R\x05\x89\x04\xa5eSjZ\u0680\x00\u07d4\xd60\v2\x15\xb1\x1d\xe7b\xec\xdeKp\xb7\x92}\x01)\x15\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd69]\xb5\xa4\xbbf\xe6\x0fL\xfb\xcd\xf0\x05{\xb4\xd9xb\xe2\x891T\xc9r\x9d\x05x\x00\x00\xe0\x94\xd6J-P\xf8\x85\x857\x18\x8a$\xe0\xf5\r\xf1h\x1a\xb0~\u05ca\b7Z*\xbc\xca$@\x00\x00\u07d4\xd6X\n\xb5\xedL}\xfaPo\xa6\xfed\xad\\\xe1)pw2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd6Y\x8b\x13\x86\xe9<\\\u02d6\x02\xffK\xbb\xec\xdb\xd3p\x1d\u0109\f%\xf4\xec\xb0A\xf0\x00\x00\u07d4\xd6dM@\xe9\v\xc9\u007f\xe7\xdf\xe7\u02bd2i\xfdW\x9b\xa4\xb3\x89\b\x9e\x91y\x94\xf7\x1c\x00\x00\xe0\x94\xd6g\f\x03m\xf7T\xbeC\xda\u074fP\xfe\xea(\x9d\x06\x1f\u058a\x01D\xa2\x904H\xce\xf7\x80\x00\u07d4\xd6hR:\x90\xf0)=e\xc58\xd2\xddlWg7\x10\x19n\x89\x02$,0\xb8S\xee\x00\x00\u07d4\xd6j\xb7\x92\x94\aL\x8bb}\x84-\xabA\xe1}\xd7\f]\xe5\x8965\u026d\xc5\u07a0\x00\x00\u0794\xd6j\xcc\r\x11\xb6\x89\u03a6\xd9\xea_\xf4\x01L\"J]\xc7\u0108\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xd6m\xdf\x11Y\xcf\"\xfd\x8czK\xc8\u0540wV\xd43\xc4>\x89wC\"\x17\xe6\x83`\x00\x00\u07d4\u0587\xce\xc0\x05\x90\x87\xfd\xc7\x13\xd4\xd2\xd6^w\xda\xef\xed\xc1_\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\u0588\xe7\x85\u024f\x00\xf8K:\xa1S3U\u01e2X\xe8yH\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u05a2.Y\x8d\xab\u04ce\xa6\xe9X\xbdy\u050d\u0756\x04\xf4\u07c965\u026d\xc5\u07a0\x00\x00\u07d4\u05a7\xacM\xe7\xb5\x10\xf0\xe8\xdeQ\x9d\x97?\xa4\xc0\x1b\xa84\x00\x89e\xea=\xb7UF`\x00\x00\u07d4\u05ac\xc2 \xba.Q\xdf\xcf!\xd4C6\x1e\xeav\\\xbd5\u0609\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u05ac\xff\u043f\u065c8.{\xd5o\xf0\xe6\x14J\x9eR\xb0\x8e\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xd6\xc0\u043c\x93\xa6.%qtp\x0e\x10\xf0$\u0232?\x1f\x87\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd6\xcf\\\x1b\u03dd\xa6b\xbc\xea\"U\x90P\x99\xf9\xd6\xe8M\u030a\x01\u011eB\x01W\xd9\xc2\x00\x00\u07d4\xd6\xd05r\xa4RE\xdb\xd46\x8cO\x82\xc9W\x14\xbd!g\xe2\x89?\x00\xc3\xd6f\x86\xfc\x00\x00\u07d4\xd6\xd6wiX\xee#\x14:\x81\xad\xad\xeb\b8 \t\xe9\x96\u0089\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd6\xd9\xe3\x0f\bB\x01*qv\xa9\x17\xd9\xd2\x04\x8c\xa0s\x87Y\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd6\xe0\x9e\x98\xfe\x13\x003!\x04\xc1\xca4\xfb\xfa\xc5T6N\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\xe8\xe9z\u90db\x9e\xe5\a\xee\xdb(\xed\xfbtw\x03\x149\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\uea18\u052e+q\x80'\xa1\x9c\xe9\xa5\xebs\x00\xab\xe3\u0289\x01}J\xce\xeec\u06c0\x00\xe0\x94\xd6\xf1\xe5[\x16\x94\b\x9e\xbc\xb4\xfe}x\x82\xaaf\u0217av\x8a\x04<#\xbd\xbe\x92\x9d\xb3\x00\x00\u07d4\xd6\xf4\xa7\xd0N\x8f\xaf \xe8\xc6\ub15c\xf7\xf7\x8d\xd2=z\x15\x89\a$\xde\xd1\xc7H\x14\x00\x00\u07d4\xd6\xfc\x04F\u01a8\xd4\n\xe3U\x1d\xb7\xe7\x01\xd1\xfa\x87nJI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\x03\u01a4\xf1\x1d`\x19Ey\u054c'f\xa7\xef\x16\xc3\n)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\x05%\x19uj\xf4%\x90\xf1S\x91\xb7#\xa0?\xa5d\xa9Q\x89\xfa61H\r\x01\xfd\x80\x00\u07d4\xd7\na+\xd6\u0769\xea\xb0\xdd\xdc\xffJ\xafA\"\u04cf\xea\xe4\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\xd7\n\xd2\xc4\xe9\uefe67\xefV\xbdHj\u04a1\xe5\xbc\xe0\x93\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\x14\f\x8eZC\a\xfa\xb0\xcc'\xba\u0752\x95\x01\x8b\xf8yp\x89\x05\xf1\x01kPv\xd0\x00\x00\u07d4\xd7\x16J\xa2a\xc0\x9a\u0672\xb5\x06\x8dE>\xd8\xebj\xa10\x83\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd7\x1eC\xa4Qw\xadQ\xcb\xe0\xf7!\x84\xa5\xcbP9\x17(Z\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\x1f\xb10\xf0\x15\fVRi\xe0\x0e\xfbC\x90+R\xa4U\xa6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\"W8\xdc\xf3W\x848\xf8\xe7\u0233\x83~B\xe0J&/\x89\x18+\x8c\ubec3\xaa\x00\x00\u07d4\xd7'MP\x80M\x9cw\u0693\xfaH\x01V\xef\xe5{\xa5\x01\u0789i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xd71\xbbk_<79^\t\u03ac\xcd\x14\xa9\x18\xa6\x06\a\x89\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xd7>\xd2\u0645\xb5\xf2\x1bU\xb2td;\xc6\xda\x03\x1d\x8e\u074d\x8a\nm\xd9\f\xaeQ\x14H\x00\x00\u07d4\xd7D\xac~S\x10\xbeijc\xb0\x03\xc4\v\xd097\x05a\u0189Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\xd7Jn\x8dj\xab4\u0385\x97h\x14\xc12{\xd6\xea\a\x84\u048a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xd7ZP*[gr\x87G\x0fe\u016aQ\xb8|\x10\x15\x05r\x8910\xb4dc\x85t\x00\x00\u07d4\xd7m\xba\xeb\xc3\rN\xf6{\x03\xe6\xe6\xec\xc6\xd8N\x00MP-\x89mv\xb9\x18\x8e\x13\x85\x00\x00\u07d4\xd7q\xd9\xe0\u028a\b\xa1\x13wW1CN\xb3'\x05\x99\xc4\r\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd7x\x8e\xf2\x86X\xaa\x06\xccS\xe1\xf3\xf0\xdeX\xe5\xc3q\xbex\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xd7x\x92\xe2';#]v\x89\xe40\xe7\xae\ud73c\xe8\xa1\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u05c1\xf7\xfc\t\x18F\x11V\x85p\xb4\x98n,r\x87+~\u0409\x01\x15\x95a\x06]]\x00\x00\u07d4\u05c5\xa8\xf1\x8c8\xb9\xbcO\xfb\x9b\x8f\xa8\xc7r{\xd6B\xee\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u05ce\xcd%\xad\xc8k\xc2\x05\x1d\x96\xf6Sd\x86kB\xa4&\xb7\x89\xd20X\xbf/&\x12\x00\x00\xe0\x94\u05cf\x84\xe3\x89D\xa0\xe0%_\xae\xceH\xbaIP\u053d9\u048a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\u05d4\x83\xf6\xa8DO%I\xd6\x11\xaf\xe0,C-\x15\xe1\x10Q\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u05d85\xe4\x04\xfb\x86\xbf\x84_\xba\t\rk\xa2^\f\x88f\xa6\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\u05da\xff\x13\xba-\xa7]F$\f\xac\n$g\xc6V\x94\x98#\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\u05dd\xb5\xabCb\x1az=\xa7\x95\xe5\x89)\xf3\xdd%\xafg\u0649lj\xccg\u05f1\xd4\x00\x00\u07d4\u05e1C\x1e\xe4S\xd1\xe4\x9a\x05P\xd1%hy\xb4\xf5\xd1\x02\x01\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\u05ed\t\xc6\xd3&WhSU\xb5\xc6\uc39fW\xb4\ube42\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u05f7@\xdf\xf8\xc4Wf\x8f\xdft\xf6\xa2f\xbf\xc1\u0737#\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd7\u0080>\u05f0\xe0\x83sQA\x1a\x8ef7\xd1h\xbc[\x05\x8a\x06A\xda\xf5\xc9\x1b\xd95\x80\x00\u07d4\xd7\xc6&]\xea\x11\x87l\x90;q\x8eL\u062b$\xfe&[\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xca\u007f\xdc\xfe\xbeE\x88\xef\xf5B\x1d\x15\"\xb6\x13(\xdf{\xf3\x89\xd8\xe6\x00\x1el0+\x00\x00\u07d4\xd7\u037dA\xff\xf2\r\xf7'\xc7\vbU\xc1\xbav\x06\x05Th\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\xd1W\xe4\xc0\xa9d7\xa6\u0485t\x1d\xd2>\xc46\x1f\xa3k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xd2\xc6\xfc\xa8\xad\x1fu9R\x10\xb5}\xe5\xdf\xd6s\x939\t\x89\x12nr\xa6\x9aP\xd0\x00\x00\xe0\x94\xd7\xd3\xc7Y Y\x048\xb8,>\x95\x15\xbe.\xb6\xedz\x8b\x1a\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\xd7\xd7\xf2\u02a4b\xa4\x1b;0\xa3J\xeb;\xa6\x10\x10\xe2bo\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xe7J\xfd\xba\xd5^\x96\u03bcZ7O,\x8bv\x86\x80\xf2\xb0\x89\x05]\xe6\xa7y\xbb\xac\x00\x00\xe0\x94\xd7\xeb\x901b'\x1c\x1a\xfa5\xfei\xe3s\"\u0224\u049b\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd7\xeb\u0779\xf99\x87w\x9bh\x01U7T8\xdbe\xaf\xcbj\x89\x05t\x1a\xfe\xff\x94L\x00\x00\u07d4\xd7\xef4\x0ef\xb0\u05ef\xcc\xe2\n\x19\xcb{\xfc\x81\xda3\xd9N\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd7\xf3p\u053e\xd9\xd5|oI\u0259\xder\x9e\xe5i\xd3\xf4\xe4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\xfa_\xfb`H\xf9o\xb1\xab\xa0\x9e\xf8{\x1c\x11\xddp\x05\xe4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd8\x06\x9f\x84\xb5!I?G\x15\x03\u007f2&\xb2_3\xb6\x05\x86\x89g\x8a\x93 b\xe4\x18\x00\x00\u0794\xd8\x15\xe1\xd9\xf4\xe2\xb5\xe5~4\x82k|\xfd\x88\x81\xb8Th\x90\x88\xf0\x15\xf2W6B\x00\x00\u07d4\xd8\x1b\xd5K\xa2\xc4Jok\xeb\x15a\u058b\x80\xb5DNm\u0189?\x17\r~\xe4\"\xf8\x9c\x80-1({\x96q\xe8\x1c\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xd8K\x92/xA\xfcWt\xf0\x0e\x14`J\xe0\xdfB\xc8U\x1e\x89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4\xd8U\xb0<\xcb\x02\x9awG\xb1\xf0s\x03\xe0\xa6dy59\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\xd8_\u07af*a\xf9]\xb9\x02\xf9\xb5\xa5<\x9b\x8f\x92f\u00ec\x89l\xf6Z~\x90G(\x00\x00\u07d4\xd8q^\xf9\x17o\x85\v.0\xeb\x8e8'\a\xf7w\xa6\xfb\xe9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd8t\xb9\u07eeEj\x92\x9b\xa3\xb1\xa2~W,\x9b,\xec\u07f3\x89\t79SM(h\x00\x00\u07d4\u0613\n9\xc7sW\xc3\n\u04e0`\xf0\v\x06\x04c1\xfdb\x89,s\xc97t,P\x00\x00\u07d4\u061b\xc2q\xb2{\xa3\xabib\xc9JU\x90\x06\xae8\xd5\xf5j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0637}\xb9\xb8\x1b\xbe\x90B{b\xf7\x02\xb2\x01\xff\u009f\xf6\x18\x892m\x1eC\x96\xd4\\\x00\x00\u07d4\xd8\xcdd\xe0(N\xecS\xaaF9\xaf\xc4u\b\x10\xb9\u007f\xabV\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd8\xd6C\x84$\x9bwg\x94\x06;V\x98x\xd5\xe3\xb50\xa4\xb2\x89\t\xa0C\u0432\xf9V\x80\x00\u07d4\xd8\xd6T \xc1\x8c#'\xccZ\xf9t%\xf8W\xe4\xa9\xfdQ\xb3\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xd8\xe5\xc9g^\xf4\xde\xed&k\x86\x95o\xc4Y\x0e\xa7\u0522}\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd8\xe8GB\x92\xe7\xa0Q`L\xa1d\xc0pw\x83\xbb(\x85\xe8\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\u07d4\xd8\xebxP>\xc3\x1aT\xa9\x016x\x1a\xe1\t\x00Lt2W\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd8\xee\xf4\xcfK\xeb\x01\xee \xd1\x11t\x8ba\xcbM?d\x1a\x01\x89\x94\x89#z\u06daP\x00\x00\u07d4\xd8\xf4\xba\xe6\xf8M\x91\rm}Z\xc9\x14\xb1\xe6\x83r\xf9A5\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd8\xf6 6\xf0;v5\xb8X\xf1\x10?\x8a\x1d\x90\x19\xa8\x92\xb6\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xd8\xf6e\xfd\x8c\xd5\u00bc\xc6\xdd\xc0\xa8\xaeR\x1eM\u01aa``\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xd8\xf9$\fU\xcf\xf05RB\x80\xc0\x9e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd8\xfe\b\x8f\xff\u0394\x8fQ7\xee#\xb0\x1d\x95\x9e\x84\xacB#\x89\f[T\xa9O\xc0\x17\x00\x00\u07d4\xd9\x0f0\t\xdbC~N\x11\u01c0\xbe\u0209os\x8de\xef\r\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd9\x10;\xb6\xb6zU\xa7\xfe\xce-\x1a\xf6-E|!x\x94m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd9\x13\xf0w\x19Iu\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd9D\u0226\x9f\xf2\xca\x12Ii\f\x12)\xc7\x19/6%\x10b\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd9JW\x88*Rs\x9b\xbe*\x06G\xc8\f$\xf5\x8a+O\x1c\x89H\xb5N*\xdb\xe1+\x00\x00\xe0\x94\xd9SB\x95<\x8a!\xe8\xb65\xee\xfa\u01c1\x9b\xea0\xf1pG\x8a\x13\xf0l\u007f\xfe\xf0]@\x00\x00\u07d4\xd9\\\x90\xff\xbeT\x84\x86G\x80\xb8gIJ\x83\u0212V\xd6\xe4\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\xd9g\x11T\x0e.\x99\x83C\xd4\xf5\x90\xb6\xfc\x8f\xac;\xb8\xb3\x1d\x89_Z@h\xb7\x1c\xb0\x00\x00\u07d4\xd9j\xc2Pt\t\u01e3\x83\xab.\xee\x18\"\xa5\xd78\xb3kV\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd9m\xb3;{Z\x95\f>\xfa-\xc3\x1b\x10\xba\x10\xa52\uf1c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd9wYe\xb7\x16Gfu\xa8\xd5\x13\xeb\x14\xbb\xf7\xb0|\xd1J\x8a\x01\x13.m-#\xc5\xe4\x00\x00\u07d4\xd9{\xc8J\xbdG\xc0[\xbfE{.\xf6Y\xd6\x1c\xa5\xe5\u43c9\x06\x9d\x17\x11\x9d\u0168\x00\x00\u07d4\xd9\u007fE&\u07a9\xb1c\xf8\xe8\xe3:k\u03d2\xfb\x90}\xe6\xec\x89\x0feJ\xafM\xb2\xf0\x00\x00\u07d4\xd9\u007f\xe6\xf5?*X\xf6\xd7mu*\xdft\xa8\xa2\xc1\x8e\x90t\x89\x10\xcd\xf9\xb6\x9aCW\x00\x00\u07d4\u0659\x99\xa2I\r\x94\x94\xa50\xca\xe4\xda\xf3\x85T\xf4\xddc>\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\u065d\xf7B\x1b\x93\x82\xe4,\x89\xb0\x06\xc7\xf0\x87p*\aW\xc0\x89\x1a\x05V\x90\xd9\u06c0\x00\x00\xe0\x94\u0677\x83\xd3\x1d2\xad\xc5\x0f\xa3\xea\u02a1]\x92\xb5h\xea\xebG\x8a\a3\xaf\x907L\x1b(\x00\x00\u07d4\xd9\xd3p\xfe\xc65v\xab\x15\xb3\x18\xbf\x9eX6M\u00a3U*\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xd9\xd4/\xd1>\xbdK\xf6\x9c\xac^\x9c~\x82H:\xb4m\xd7\xe9\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xd9\xe2~\xb0}\xfcq\xa7\x06\x06\f\u007f\a\x928\u0293\xe8\x859\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd9\xe3\x85~\xfd\x1e *D\x17p\xa7w\xa4\x9d\xccE\xe2\xe0\u04c9\f\x1d\xaf\x81\u0623\xce\x00\x00\u07d4\xd9\xec.\xfe\x99\xff\\\xf0\r\x03\xa81{\x92\xa2J\xefD\x1f~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd9\xec\x8f\xe6\x9bw\x16\xc0\x86Z\xf8\x88\xa1\x1b+\x12\xf7 \xed3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd9\xf1\xb2d\b\xf0\xecg\xad\x1d\ro\xe2.\x85\x15\xe1t\x06$\x89\x01M\x11 \u05f1`\x00\x00\u07d4\xd9\xf5G\xf2\xc1\xde\x0e\u064aS\xd1a\xdfWc]\xd2\x1a\x00\xbd\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xd9\xff\x11]\x01&l\x9fs\xb0c\xc1\xc28\xef5e\xe6;6\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\xda\x06\x04N)#&\xffil\x0091h\xceF\xff\xac9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda*\x14\xf9r@\x15\u05d0\x14\xed\x8eY\th\x1dYaH\xf1\x89\x02\xa1\x0f\x0f\x8a\x91\xab\x80\x00\u07d4\xda*\u054ew\xde\xdd\xed\xe2\x18vF\xc4e\x94Z\x8d\xc3\xf6A\x89#\xc7W\a+\x8d\xd0\x00\x00\u07d4\xda0\x17\xc1P\xdd\r\xce\u007f\u03c8\x1b\nH\xd0\xd1\xc7V\xc4\u01c9\x05k\xf9\x1b\x1ae\xeb\x00\x00\u07d4\xda4\xb2\xea\xe3\v\xaf\xe8\xda\xec\xcd\xe8\x19\xa7\x94\u0349\xe0\x95I\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdaJ_U\u007f;\xab9\n\x92\xf4\x9b\x9b\x90\n\xf3\fF\xae\x80\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdaPU7S\u007f\xfb3\xc4\x15\xfe\xc6Ni\xba\xe0\x90\xc5\xf6\x0f\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xdai\x8dd\xc6\\\u007f+,rS\x05\x9c\xd3\u0441\u0619\xb6\xb7\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\u07d4\xdaw2\xf0/.'.\xaf(\u07d7.\xcc\r\xde\xed\x9c\xf4\x98\x89\v \xbf\xbfig\x89\x00\x00\u07d4\xdaz\xd0%\xeb\xde%\xd2\"C\u02c3\x0e\xa1\xd3\xf6JVc#\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\u0685]SG\u007fP^\xc4\xc8\xd5\u8ed1\x80\u04c6\x81\x11\x9c\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\u0687^N/<\xab\xe4\xf3~\x0e\xae\xd7\xd1\xf6\xdc\xc6\xff\xefC\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u068b\xbe\xe1\x82\xe4U\xd2\t\x8a\xcb3\x8amE\xb4\xb1~\u0636\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0698.\x96C\xff\xec\xe7#\aZ@\xfewnZ\xce\x04\xb2\x9b\x89\b\xb8\xb6\u0259\x9b\xf2\x00\x00\u07d4\u069fUF\tF\u05ff\xb5p\xdd\xecu|\xa5w;XB\x9a\x89\x1b\x84]v\x9e\xb4H\x00\x00\u07d4\u06a1\xbdz\x91H\xfb\x86\\\xd6\x12\xdd5\xf1b\x86\x1d\x0f;\u0709\xa68\xabr\xd9,\x13\x80\x00\xe0\x94\u06a6<\xbd\xa4]\u0507\xa3\xf1\xcdJtj\x01\xbb^\x06\v\x90\x8a\x01\x04\x16\u0670*\x89$\x00\x00\u07d4\u06a7v\xa6uDi\u05f9&z\x89\xb8g%\xe7@\xda\x0f\xa0\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u06ac\x91\xc1\xe8Y\xd5\xe5~\xd3\bKP \x0f\x97f\xe2\xc5+\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u06ac\xda\xf4\"&\xd1\\\xb1\u03d8\xfa\x15\x04\x8c\u007fL\xee\xfei\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\u06b6\xbc\u06c3\xcf$\xa0\xae\x1c\xb2\x1b;[\x83\xc2\xf3\x82I'\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\u06bb\b\x89\xfc\x04)&\xb0^\xf5{% \x91\n\xbcKAI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06bc\"PB\xa6Y,\xfa\x13\xeb\xe5N\xfaA\x04\bx\xa5\xa2\x89\x0e\x11\xfa\xd5\xd8\\\xa3\x00\x00\u07d4\xda\xc0\xc1w\xf1\x1c\\>>x\xf2\xef\xd6c\xd12!H\x85t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\xd16\xb8\x81x\xb4\x83zlx\x0f\xeb\xa2&\xb9\x85i\xa9L\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xda\xdb\xfa\xfd\x8bb\xb9*$\xef\xd7RV\u0743\xab\xdb\u05fb\u06c9\x01\x11du\x9f\xfb2\x00\x00\u07d4\xda\xdc\x00\xaby'`\xaa4\x15i\xfa\x9f\xf5\x98&\x84\x85JJ2\x8an\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\xe7 \x1e\xab\x8c\x063\x02\x93\ri9)\xd0\u007f\x95\xe7\x19b\x89\x91\xae\xc0(\xb4\x19\x81\x00\x00\u07d4\xda\xed\u052d\x10{'\x1e\x89Hl\xbf\x80\xeb\xd6!\u0757Ex\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x04\xfa\xd9\u011f\x9e\x88\v\xeb\x8f\xcf\x1d:8\x90\u4cc4o\x89CZ\xe6\xcc\fX\xe5\x00\x00\u07d4\xdb\f\u01cft\u0642{\u070ads'n\xb8O\u0717b\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x12\x93\xa5\x06\xe9\f\xad*Y\xe1\xb8V\x1f^f\x96\x1ag\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x19\xa3\x98\"06\x8f\x01w!\x9c\xb1\f\xb2Y\u0372%|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb#\xa6\xfe\xf1\xaf{X\x1ew,\xf9\x18\x82\u07b2Qo\xc0\xa7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb$O\x97\xd9\xc4K\x15\x8a@\xed\x96\x06\xd9\xf7\xbd8\x9131\x89\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xdb(\x8f\x80\xff\xe22\u00baG\u0314\xc7c\xcfo\u0278+\r\x89\x04\x9b\x9c\xa9\xa6\x944\x00\x00\u07d4\xdb*\f\x9a\xb6M\xf5\x8d\u07f1\u06ec\xf8\xba\r\x89\xc8[1\xb4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdb4t^\u0785v\xb4\x99\xdb\x01\xbe\xb7\xc1\xec\u0685\xcfJ\xbe\x89\x04V9\x18$O@\x00\x00\u07d4\xdb?%\x8a\xb2\xa3\xc2\xcf3\x9cD\x99\xf7ZK\xd1\xd3G.\x9e\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xdbK\xc8;\x0ek\xaa\xdb\x11V\xc5\xcf\x06\xe0\xf7!\x80\x8cR\u01c9/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xdbc\x12-\xe7\x03}\xa4\x97\x151\xfa\u9bc5\x86x\x86\u0192\x89\x0f\x04%\xb0d\x1f4\x00\x00\u07d4\xdbl*s\xda\xc7BJ\xb0\xd01\xb6ga\x12%f\xc0\x10C\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xdbnV\f\x9b\xc6 \u053e\xa3\xa9MG\xf7\x88\v\xf4\u007f-_\x89\x04\xda\x0f\xdf\xcf\x05v\x00\x00\u07d4\xdbo\xf7\x1b=\xb0\x92\x8f\x83\x9e\x05\xa72;\xfbW\u049c\x87\xaa\x891T\xc9r\x9d\x05x\x00\x00\u07d4\xdbsF\vY\xd8\xe8PE\xd5\xe7R\xe6%Y\x87^BP.\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\xdbw\xb8\x8d\xcbq/\xd1~\xe9\x1a[\x94t\x8dr\f\x90\xa9\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb}@7\b\x1fle\xf9Gk\x06\x87\xd9\u007f\x1e\x04M\n\x1d\x89#\xc7W\a+\x8d\xd0\x00\x00\xe0\x94\u06c8.\xac\xed\xd0\xef\xf2cQ\x1b1*\u06fcY\u01b8\xb2[\x8a\x01\xedO\xdez\"6\xb0\x00\x00\u07d4\u06d3q\xb3\fL\x84NY\xe0>\x92K\xe6\x06\xa98\xd1\xd3\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06e4ym\f\xebM:\x83k\x84\xc9o\x91\n\xfc\x10?[\xa0\x89\t\b\xf4\x93\xf77A\x00\x00\u07d4\u06ed\xc6\x1e\xd5\xf0F\n\u007f\x18\xe5\x1b/\xb2aM\x92d\xa0\xe0\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u06f6\xacH@'\x04\x16B\xbb\xfd\x8d\x80\xf9\xd0\xc1\xcf3\xc1\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06fc\xbby\xbfG\x9aB\xadq\xdb\u02b7{Z\u07ea\x87,X\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xdb\xc1\xce\x0eI\xb1\xa7\x05\xd2. 7\xae\xc8x\xee\ru\xc7\x03\x89\r\x8drkqw\xa8\x00\x00\u07d4\xdb\xc1\xd0\xee+\xabS\x11@\xde\x13w\"\xcd6\xbd\xb4\xe4q\x94\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb\u015e\u0609s\u07ad1\b\x84\":\xf4\x97c\xc0P0\xf1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\xdb\xc6ie\xe4&\xff\x1a\xc8z\xd6\xebx\xc1\xd9Rq\x15\x8f\x9f\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xdb\xcb\xcdzW\ua7724\x9b\x87\x8a\xf3K\x1a\xd6B\xa7\xf1\u0449\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb\xd5\x1c\xdf,;\xfa\xcd\xff\x10b!\xde.\x19\xadmB\x04\x14\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xdb\xd7\x1e\xfaK\x93\u0209\xe7e\x93\xde`\x9c;\x04\u02ef\xbe\b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xdb\xf5\xf0a\xa0\xf4\x8e^ia\x879\xa7}.\xc1\x97h\xd2\x01\x89\b=lz\xabc`\x00\x00\u07d4\xdb\xf8\xb19g\xf5Q%'-\xe0V%6\xc4P\xbaVU\xa0\x89n\xf5x\xf0n\f\xcb\x00\x00\u07d4\xdb\xfb\x1b\xb4d\xb8\xa5\x8eP\r.\xd8\u0797,E\xf5\xf1\xc0\xfb\x89V\xbcu\xe2\xd61\x00\x00\x00\xe0\x94\xdc\x06~\xd3\xe1-q\x1e\xd4u\xf5\x15n\xf7\xe7\x1a\x80\xd94\xb9\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xdc\b\u007f\x93\x90\xfb\x9e\x97j\xc2:\xb6\x89TJ\tB\xec !\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xdc\x1e\xb9\xb6\xe6CQ\xf5d$P\x96E\xf8>y\xee\xe7l\xf4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdc\x1f\x19ya_\b!@\xb8\xbbx\xc6{'\xa1\x94'\x13\xb1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xdc#\xb2`\xfc\xc2n}\x10\xf4\xbd\x04J\xf7\x94W\x94`\xd9\u0689\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xdc)\x11\x97E\xd23s \xdaQ\xe1\x91\x00\xc9H\u0640\xb9\x15\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xdc-\x15\xa6\x9fk\xb3;$j\xef@E\aQ\xc2\xf6uj\u0489l4\x10\x80\xbd\x1f\xb0\x00\x00\u07d4\xdc=\xaeY\xed\x0f\xe1\x8bXQ\x1eo\xe2\xfbi\xb2\x19h\x94#\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xdc?\x0evr\xf7\x1f\xe7R[\xa3\v\x97U\x18: \xb9\x16j\x8a\x02\b\x9c\xf5{[>\x96\x80\x00\xe0\x94\xdcCE\u0581.\x87\n\xe9\fV\x8cg\xd2\xc5g\u03f4\xf0<\x8a\x01k5-\xa5\xe0\xed0\x00\x00\u07d4\xdcD'[\x17\x15\xba\xea\x1b\x03EsZ)\xacB\xc9\xf5\x1bO\x89?\x19\xbe\xb8\xdd\x1a\xb0\x00\x00\u07d4\xdcF\xc13%\u034e\xdf\x020\xd0h\x89d\x86\xf0\a\xbfN\xf1\x89Hz\x9a0E9D\x00\x00\u07d4\xdcQ\xb2\u071d$z\x1d\x0e[\xc3l\xa3\x15oz\xf2\x1f\xf9\xf6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xdcS\x05\xb4\x02\n\x06\xb4\x9de||\xa3L5\xc9\x1c_,V\x8a\x01}\xf6\xc1\r\xbe\xba\x97\x00\x00\u07d4\xdcW4[8\xe0\xf0g\u0263\x1d\x9d\xea\xc5'Z\x10\x94\x93!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdcWG}\xaf\xa4/p\\\u007f\xe4\x0e\xae\x9c\x81un\x02%\xf1\x89\x1b\x1b\x81(\xa7An\x00\x00\u07d4\xdc_Z\xd6c\xa6\xf2c2}d\xca\xc9\xcb\x13=,\x96\x05\x97\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdcp:_7\x94\xc8Ml\xb3TI\x18\xca\xe1J5\u00fdO\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94\xdcs\x8f\xb2\x17\u03ad/iYL\b\x17\r\xe1\xaf\x10\xc4\x19\xe3\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xdcv\xe8[\xa5\v\x9b1\xec\x1e& \xbc\xe6\xe7\xc8\x05\x8c\x0e\xaf\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0703\xb6\xfd\rQ!1 G\a\xea\xf7.\xa0\xc8\u027e\xf9v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u070c)\x12\xf0\x84\xa6\u0444\xaasc\x85\x13\u033c2n\x01\x02\x89F3\xbc6\xcb\xc2\xdc\x00\x00\u07d4\u0711\x1c\xf7\xdc]\u04016Vg\x05(\xe93\x8eg\x03G\x86\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0730;\xfal\x111#NV\xb7\xea|Or\x14\x87Tkz\x89Hz\x9a0E9D\x00\x00\xe0\x94\u0736M\xf47X\xc7\u03d7O\xa6`HO\xbbq\x8f\x8cg\xc1\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xdc\xc5-\x8f\x8d\x9f\xc7B\xa8\xb8'g\xf0US\x87\xc5c\xef\xff\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xdc\xcb7\x0e\u058a\xa9\"(0C\xef|\xad\x1b\x9d@?\xc3J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdc\u0324 E\xec>\x16P\x8b`?\xd96\xe7\xfd}\xe5\xf3j\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xdc\xd1\fU\xbb\x85OuD4\xf1!\x9c,\x9a\x98\xac\xe7\x9f\x03\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xdc\u057c\xa2\x00S\x95\xb6u\xfd\xe5\x03VY\xb2k\xfe\xfcI\xee\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xdc\u06fdN&\x04\xe4\x0e\x17\x10\xccg0(\x9d\xcc\xfa\u04c9-\x89\xf9]\xd2\xec'\xcc\xe0\x00\x00\u07d4\xdc\xe3\f1\xf3\xcafr\x1e\xcb!<\x80\x9a\xabV\x1d\x9bR\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdc\xf39eS\x13\x80\x161h\xfc\x11\xf6~\x89\xc6\xf1\xbc\x17\x8a\x89\x12'v\x854\x06\xb0\x80\x00\u07d4\xdc\xf6\xb6W&n\x91\xa4\xda\xe6\x03=\xda\xc1S2\u074d+4\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xdc\xf9q\x9b\xe8|oFum\xb4\x89\x1d\xb9\xb6\x11\xd2F\x9cP\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xdc\xff\xf3\xe8\xd2<*4\xb5k\u0473\xbdE\u01d3tC\"9\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xdd\x04\xee\xe7N\v\xf3\f?\x8dl,\u007fR\xe0Q\x92\x10\u07d3\x89\x04V9\x18$O@\x00\x00\xe0\x94\xdd&\xb4)\xfdC\xd8N\xc1y\x82S$\xba\u057f\xb9\x16\xb3`\x8a\x01\x16\xbf\x95\xbc\x842\x98\x00\x00\u07d4\xdd*#:\xde\xdef\xfe\x11&\xd6\xc1h#\xb6*\x02\x1f\xed\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xdd+\u07e9\x17\xc1\xf3\x10\xe6\xfa5\xaa\x8a\xf1i9\xc23\xcd}\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xdd5\xcf\xdb\u02d93\x95Sz\xec\xc9\xf5\x90\x85\xa8\xd5\u0776\xf5\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xddG\x18\x9a>d9qg\xf0b\x0eHEe\xb7b\xbf\xbb\xf4\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xddM\xd6\xd3`3\xb0co\u030d\t8`\x9fM\xd6OJ\x86\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xddO_\xa2\x11\x1d\xb6\x8fk\xde5\x89\xb60)9[i\xa9-\x89\b\x96=\xd8\xc2\xc5\xe0\x00\x00\xe0\x94\xddc\x04/%\xed2\x88J\xd2n:\xd9Y\xeb\x94\xea6\xbfg\x8a\x04\x84\xd7\xfd\xe7\u0553\xf0\x00\x00\u07d4\xdde\xf6\xe1qc\xb5\xd2\x03d\x1fQ\xcc{$\xb0\x0f\x02\xc8\xfb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xddl\x06!\x93\xea\xc2=/\xdb\xf9\x97\xd5\x06:4k\xb3\xb4p\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdd{\u0366Y$\xaa\xa4\x9b\x80\x98J\xe1su\x02X\xb9(G\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdd\u007f\xf4A\xbao\xfe6q\xf3\xc0\u06bb\xff\x18#\xa5\x043p\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0742T\x12\x1an\x94/\xc9\b(\xf2C\x1fQ\x1d\xad\u007f2\u6263\x9b)\xe1\xf3`\xe8\x00\x00\xe0\x94\u074a\xf9\xe7vR#\xf4DoD\xd3\xd5\t\x81\x9a==\xb4\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\u0755\xdb\xe3\x0f\x1f\x18w\xc5\xddv\x84\xae\xef0*\xb6\x88Q\x92\x8a\x01\xc5\xd8\xd6\xeb>2P\x00\x00\xe0\x94\u0756|L_\x8a\xe4~&o\xb4\x16\xaa\u0456N\xe3\xe7\xe8\u00ca\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\u075bHZ;\x1c\xd3:j\x9cb\xf1\xe5\xbe\xe9'\x01\x85m%\x89\f3\x83\xed\x03\x1b~\x80\x00\xe0\x94\u0763q\xe6\x00\xd3\x06\x88\xd4q\x0e\b\x8e\x02\xfd\xf2\xb9RM_\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4\u0764\xed*X\xa8\xdd \xa72u4{X\rq\xb9[\xf9\x9a\x89\x15\xa1<\xc2\x01\xe4\xdc\x00\x00\xe0\x94\u0764\xff}\xe4\x91\u0187\xdfEt\xdd\x1b\x17\xff\x8f$k\xa3\u044a\x04&\x84\xa4\x1a\xbf\xd8@\x00\x00\u07d4\u076bkQ\xa9\x03\v@\xfb\x95\xcf\vt\x8a\x05\x9c$\x17\xbe\u01c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u076bu\xfb/\xf9\xfe\u02c8\xf8\x94vh\x8e+\x00\xe3g\xeb\xf9\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\xe0\x94\u076b\xf1<<\x8e\xa4\xe3\xd7=x\xecqz\xfa\xfaC\x0eTy\x8a\b\xcf#\xf9\t\xc0\xfa\x00\x00\x00\u07d4\u076c1*\x96UBj\x9c\f\x9e\xfa?\xd8%Y\xefE\x05\xbf\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\u076ck\xf4\xbb\xdd}Y}\x9chm\x06\x95Y;\xed\xcc\xc7\xfa\x89.\xe4IU\b\x98\xe4\x00\x00\xe0\x94\u077d+\x93,v;\xa5\xb1\xb7\xae;6.\xac>\x8d@\x12\x1a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u077d\xdd\x1b\xbd8\xff\xad\xe00]0\xf0 (\xd9.\x9f:\xa8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u077e\xe6\xf0\x94\xea\xe64 \xb0\x03\xfbGW\x14*\xeal\xd0\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdd\u059c[\x9b\xf5\xebZ9\xce\xe7\xc34\x1a\x12\r\x97?\xdb4\x89k\xc1K\x8f\x8e\x1b5\x00\x00\xe0\x94\xdd\xdd{\x9en\xab@\x9b\x92&:\xc2r\u0680\x1bfO\x8aW\x8ai\xe1\r\xe7fv\u0400\x00\x00\u07d4\xdd\xe6p\xd0\x169fuv\xa2-\xd0]2F\xd6\x1f\x06\xe0\x83\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94\xdd\xe7zG@\xba\b\xe7\xf7?\xbe:\x16t\x91)1t.\xeb\x8a\x044\xfeMC\x82\xf1\u0500\x00\u07d4\xdd\xe8\xf0\xc3\x1bt\x15Q\x1d\xce\xd1\xcd}F2>K\xd1\"2\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\xdd\xe9i\xae\xf3N\xa8z\u0099\xb7Y~)+J\x01U\u030a\x89\x102\xf2YJ\x01s\x80\x00\u07d4\xdd\xf0\xcc\xe1\xfe\x99m\x91v5\xf0\a\x12\xf4\x05 \x91\xdf\xf9\xea\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdd\xf3\xadv58\x10\xbej\x89\xd71\xb7\x87\xf6\xf1q\x88a+\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xdd\xf5\x81\n\x0e\xb2\xfb.22;\xb2\u0255\t\xab2\x0f$\xac\x8a\x03\xca\\f\u067cD0\x00\x00\xe0\x94\xdd\xf9\\\x1e\x99\xce/\x9fV\x98\x05|\x19\xd5\xc9@'\xeeJn\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\xdd\xfa\xfd\xbc|\x90\xf12\x0eT\xb9\x8f7F\x17\xfb\xd0\x1d\x10\x9f\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xdd\xfc\xca\x13\xf94\xf0\u03fe#\x1d\xa109\xd7\x04u\xe6\xa1\u040968\"\x16`\xa5\xaa\x80\x00\u07d4\xde\x02~\xfb\xb3\x85\x03\"n\xd8q\t\x9c\xb3\v\xdb\x02\xaf\x135\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xde\x06\xd5\xeawzN\xb1G^`]\xbc\xbfCDN\x807\xea\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xde\a\xfb[zFN;\xa7\xfb\xe0\x9e\x9a\xcb'\x1a\xf53\x8cX\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xde\x11!\x82\x9c\x9a\b(@\x87\xa4?\xbd/\xc1\x14*23\xb4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xde\x17kR\x84\xbc\xee:\x83\x8b\xa2Og\xfc|\xbfg\u05ce\xf6\x89\x02\t\xce\b\xc9b\xb0\x00\x00\u07d4\xde!\"\x93\xf8\xf1\xd21\xfa\x10\xe6\tG\rQ,\xb8\xff\xc5\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde0\xe4\x9eZ\xb3\x13!M/\x01\u072b\u0389@\xb8\x1b\x1cv\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xde3\xd7\b\xa3\xb8\x9e\x90\x9e\xafe;0\xfd\u00e5\xd5\u0334\xb3\x89\t\x9c\x88\"\x9f\xd4\xc2\x00\x00\u07d4\xde7B\x99\xc1\xd0}ySs\x85\x19\x0fD.\xf9\xca$\x06\x1f\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xdeB\xfc\xd2L\xe4#\x93\x830CgY_\x06\x8f\fa\a@\x89\x02r*p\xf1\xa9\xa0\x00\x00\u07d4\xdeP\x86\x8e\xb7\xe3\xc7\x197\xecs\xfa\x89\u074b\x9e\xe1\rE\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xdeU\xde\x04X\xf8P\xb3~Mx\xa6A\xdd.\xb2\u074f8\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xde[\x00_\xe8\u06ae\x8d\x1f\x05\xde>\xda\x04 f\xc6\xc4i\x1c\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\xdea-\a$\xe8N\xa4\xa7\xfe\xaa=!B\xbd^\xe8-2\x01\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdem61\x06\xccb8\xd2\xf0\x92\xf0\xf07!6\xd1\xcdP\u018a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xde}\xee\"\x0f\x04W\xa7\x18}V\xc1\xc4\x1f.\xb0\n\xc5`!\x89\"%\xf3\x9c\x85\x05*\x00\x00\u07d4\u0782\u030dJ\x1b\xb1\xd9CC\x92\x96[>\x80\xba\xd3\xc0=O\x89P\x18nu\u0797\xa6\x00\x00\u07d4\u0797\xf43\a\x00\xb4\x8cImC|\x91\xca\x1d\xe9\u0130\x1b\xa4\x89\x9d\xcc\x05\x15\xb5n\f\x00\x00\u07d4\u079e\xffLy\x88\x11\xd9h\xdc\xcbF\r\x9b\x06\x9c\xf3\x02x\xe0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u07b1\xbc4\xd8mJM\xde%\x80\u063e\xaf\aN\xb0\xe1\xa2D\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\u07b2I]j\xca{*j-\x13\x8bn\x1aB\xe2\xdc1\x1f\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\u07b9rTGL\r/Zyp\xdc\xdb/R\xfb\x10\x98\xb8\x96\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u07b9\xa4\x9aC\x870 \xf0u\x91\x85\xe2\v\xbbL\U000c1ecf\x89\vx\xed\xb0\xbf.^\x00\x00\u07d4\u07bb\u0743\x1e\x0f \xaen7\x82R\xde\xcd\xf9/|\xf0\xc6X\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde\xc3\xee\xc2d\nu,Fn+~~\u616f\xe9\xacA\xf4\x89G\u0257SYk(\x80\x00\u07d4\xde\xc8#s\xad\xe8\xeb\xcf*\xcbo\x8b\xc2AM\u05eb\xb7\rw\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xde\u0221\xa8\x98\xf1\xb8\x95\xd80\x1f\xe6J\xb3\xad]\xe9A\xf6\x89\x89*\xb4\xf6~\x8as\x0f\x80\x00\u07d4\xde\u025e\x97/\xcaqwP\x8c\x8e\x1aG\xac\"\xd7h\xac\xab|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde\xd8w7\x84\a\xb9Nx\x1cN\xf4\xaf|\xfc[\xc2 \xb5\x16\x89\x141y\xd8i\x11\x02\x00\x00\u07d4\xde\xe9B\xd5\xca\xf5\xfa\xc1\x14!\xd8k\x01\vE\x8e\\9)\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xde\xee&\x89\xfa\x90\x06\xb5\x9c\xf2\x85#}\xe5;:\u007f\xd0\x148\x89\x18ey\xf2\x9e %\x00\x00\u07d4\xde\xfd\xdf\u055b\x8d,\x15N\xec\xf5\xc7\xc1g\xbf\v\xa2\x90]>\x89\x05\x12\xcb^&GB\x00\x00\u07d4\xde\xfe\x91A\xf4pE\x99\x15\x9d{\"=\xe4+\xff\xd8\x04\x96\xb3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xdf\t\x8f^N=\xff\xa5\x1a\xf27\xbd\xa8e,Os\ud726\x89\x1b6\xa6DJ>\x18\x00\x00\xe0\x94\xdf\r\ba{\xd2R\xa9\x11\u07cb\xd4\x1a9\xb8=\u07c0\x96s\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdf\x0f\xf1\xf3\xd2z\x8e\xc9\xfb\x8fk\f\xb2T\xa6;\xba\x82$\xa5\x89\xec\xc5 )E\xd0\x02\x00\x00\u07d4\xdf\x1f\xa2\xe2\x0e1\x98^\xbe,\x0f\f\x93\xb5L\x0f\xb6z&K\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdf!\x1c\xd2\x12\x88\xd6\xc5o\xaef\xc3\xffTb]\u0531T'\x89\x87\x86\xcdvN\x1f,\x00\x00\u07d4\xdf#k\xf6\xab\xf4\xf3)7\x95\xbf\f(q\x8f\x93\u3c73k\x89Hz\x9a0E9D\x00\x00\u07d4\xdf1\x02_VI\xd2\xc6\xee\xa4\x1e\u04fd\xd3G\x1ay\x0fu\x9a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdf7\xc2.`:\xed\xb6\nbrS\xc4}\x8b\xa8f\xf6\xd9r\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4\xdf;r\u017dq\u0501N\x88\xa6#!\xa9=@\x11\xe3W\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdf?W\xb8\xeed4\xd0G\"=\xeft\xb2\x0fc\xf9\xe4\xf9U\x89\r\x94b\xc6\xcbKZ\x00\x00\u07d4\xdfD\xc4\u007f\xc3\x03\xacv\xe7O\x97\x19L\xcag\xb5\xbb<\x02?\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\xdfG\xa6\x1brSQ\x93\xc5a\xcc\xccu\xc3\xf3\xce\b\x04\xa2\x0e\x89\x15\x93\\\vN=x\x00\x00\u07d4\xdfG\xa8\xef\x95\xf2\xf4\x9f\x8eoX\x18AT\x14]\x11\xf7'\x97\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xdfS\x003F\xd6\\^zdk\xc04\xf2\xb7\xd3/\xcb\xe5j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdfW5:\xaf\xf2\xaa\xdb\n\x04\xf9\x01N\x8d\xa7\x88N\x86X\x9c\x89\bH\x86\xa6nO\xb0\x00\x00\u07d4\xdf`\xf1\x8c\x81*\x11\xedN'v\xe7\xa8\x0e\xcf^S\x05\xb3\u05890\xca\x02O\x98{\x90\x00\x00\u07d4\xdfd\x85\xc4)z\xc1R\xb2\x89\xb1\x9d\xde2\xc7~\xc4\x17\xf4}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xdff\n\x91\u06b9\xf70\xf6\x19\rP\xc89\x05aP\aV\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\xdfn\xd6\x00jj\xbe\x88n\xd3=\x95\xa4\xde(\xfc\x12\x189'\x891T\xc9r\x9d\x05x\x00\x00\u07d4\u07c5\x10y>\xee\x81\x1c-\xab\x1c\x93\xc6\xf4G?0\xfb\xef[\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u07cdH\xb1\xeb\a\xb3\xc2\x17y\x0el-\xf0M\xc3\x19\xe7\xe8H\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u07e6\xb8\xb8\xad1\x84\xe3W\xda()Q\u05d1a\u03f0\x89\xbc\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\u07ef1\xe6\"\xc0=\x9e\x18\xa0\u0778\xbe`\xfb\xe3\xe6a\xbe\n\x8a\x02\x1e\x17\x1a>\xc9\xf7,\x00\x00\u07d4\u07f1bn\xf4\x8a\x1d}uR\xa5\xe0)\x8f\x1f\xc2:;H-\x89\\\xe8\x95\u0754\x9e\xfa\x00\x00\xe0\x94\u07f4\u052d\xe5/\u0301\x8a\xccz,k\xb2\xb0\x02$e\x8fx\x8a\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\u07fdB2\xc1|@z\x98\r\xb8\u007f\xfb\u036060\xe5\xc4Y\x89\x1d\xfc\u007f\x92I#S\x00\x00\u07d4\xdf\xcb\xdf\tEN\x1a^J@\xd3\xee\xf7\xc5\xcf\x1c\xd3\u0794\x86\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdf\xdb\xce\xc1\x01K\x96\xda!X\xcaQ>\x9c\x8d;\x9a\xf1\xc3\u0409lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdf\xde\xd2WK'\xd1a:}\x98\xb7\x15\x15\x9b\r\x00\xba\xab(\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xdf\xdfC9P\x8b\x0fnZ\xb1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe0\x06\x04b\xc4\u007f\xf9g\x9b\xae\xf0qY\xca\xe0\x8c)\xf2t\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\r\x15;\x106\x91C\xf9\u007fT\xb8\xd4\xca\"\x9e\xb3\xe8\xf3$\x89\b=lz\xabc`\x00\x00\u07d4\xe0\x12\xdbE8'\xa5\x8e\x16\xc16V\b\xd3n\xd6Xr\x05\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x15G\xbaB\xfc\xaf\xaf\x93\x93\x8b\xec\xf7i\x9ft)\n\xf7O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x16\xdc\x13\x8e%\x81[\x90\xbe?\xe9\xee\xe8\xff\xb2\xe1\x05bO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0\x18Y\xf2B\xf1\xa0\xec`/\xa8\xa3\xb0\xb5v@\xec\x89\a^\x89\x1e\x16,\x17{\xe5\xcc\x00\x00\xe0\x94\xe0 \xe8cb\xb4\x87u(6\xa6\xde\v\xc0,\xd8\u061a\x8bj\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xe0#\xf0\x9b(\x87a,|\x9c\xf1\x98\x8e::`+3\x94\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0'\"\x13\xe8\xd2\xfd>\x96\xbdb\x17\xb2KK\xa0\x1bapy\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe0+t\xa4v(\xbe1[\x1fv\xb3\x15\x05J\xd4J\xe9qo\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xe02 \u0197\xbc\u048f&\xef\vt@J\x8b\xeb\x06\xb2\xba{\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe05/\u07c1\x9b\xa2e\xf1L\x06\xa61\\J\xc1\xfe\x13\x1b.\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe08\x8a\xed\xdd?\xe2\xadV\xf8WH\xe8\x0eq\n4\xb7\xc9.\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0<\x00\xd0\x03\x88\xec\xbfO&=\n\xc7x\xbbA\xa5z@\u064966\xc9yd6t\x00\x00\u07d4\xe0I \xdcn\xcc\x1dn\xcc\bO\x88\xaa\n\xf5\u06d7\xbf\x89:\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xe0Ir\xa8<\xa4\x11+\xc8q\xc7-J\xe1al/\a(\u06c9\x0e\x81\xc7\u007f)\xa3/\x00\x00\u07d4\xe0O\xf5\xe5\xa7\u2bd9]\x88W\xce\x02\x90\xb5:+\x0e\xda]\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xe0P)\xac\xeb\axg[\xef\x17A\xab,\u0493\x1e\xf7\xc8K\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4\xe0V\xbf?\xf4\x1c&%o\xefQqf\x12\xb9\u04da\u0799\x9c\x89\x05k\xe7W\xa1.\n\x80\x00\u07d4\xe0a\xa4\xf2\xfcw\xb2\x96\u045a\xda#\x8eI\xa5\u02ce\xcb\xfap\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe0f>\x8c\xd6g\x92\xa6A\xf5nP\x03f\x01G\x88\x0f\x01\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0f\x8f\xa8,\x14\xd6\xe8\xd9:S\x11>\xf2\x86/\xa8\x15\x81\xbc\x89//9\xfclT\x00\x00\x00\u07d4\xe0i\xc0\x173R\xb1\v\xf6\x83G\x19\xdb[\xed\x01\xad\xf9{\xbc\x89\x01\x064\xf8\xe52;\x00\x00\u07d4\xe0l)\xa8\x15\x17\xe0\u0507\xb6\u007f\xb0\xb6\xaa\xbcOW6\x83\x88\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xe0l\xb6)G\x04\xee\xa7C|/\xc3\xd3\as\xb7\xbf8\x88\x9a\x89\x01\x16\xdc:\x89\x94\xb3\x00\x00\u07d4\xe0q7\xae\r\x11m\x0353\xc4\uad16\xf8\xa9\xfb\tV\x9c\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\xe0v\xdb0\xabHoy\x19N\xbb\xc4]\x8f\xab\x9a\x92B\xf6T\x8a\x01\x06`~4\x94\xba\xa0\x00\x00\u07d4\xe0~\xbb\xc7\xf4\xdaAnB\xc8\xd4\xf8B\xab\xa1b3\xc1%\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x81\xca\x1fH\x82\xdb`C\u0569\x19\a\x03\xfd\xe0\xab;\xf5m\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\x83\xd3Hc\xe0\xe1\u007f\x92ky(\xed\xff1~\x99\x8e\x9cK\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\x8b\x9a\xbak\xd9\u048b\xc2\x05gy\xd2\xfb\xf0\xf2\x85Z=\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x8b\u009c+H\xb1i\xff+\xdc\x16qLXnl\xb8\\\u03c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe0\x8c`11\x06\xe3\xf93O\xe6\xf7\xe7bM!\x110\xc0w\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xe0\x9ch\xe6\x19\x98\xd9\xc8\x1b\x14\xe4\xee\x80+\xa7\xad\xf6\xd7L\u06c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe0\x9f\xeauZ\xee\x1aD\xc0\xa8\x9f\x03\xb5\u07b7b\xba3\x00o\x89;\xa2\x89\xbc\x94O\xf7\x00\x00\xe0\x94\xe0\xa2T\xac\t\xb9r[\xeb\xc8\xe4`C\x1d\xd0s.\xbc\xab\xbf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xe0\xaai6UU\xb7?(#3\xd1\xe3\f\x1b\xbd\a(T\xe8\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xe0\xba\u064e\ue598\xdb\xf6\xd7`\x85\xb7\x92=\xe5uN\x90m\x89\t\r\x97/22<\x00\x00\u07d4\xe0\u012b\x90r\xb4\xe6\xe3eJI\xf8\xa8\xdb\x02jK3\x86\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\u0380\xa4a\xb6H\xa5\x01\xfd\v\x82F\x90\u0206\x8b\x0eM\xe8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0\xcfi\x8a\x053'\xeb\xd1k}w\x00\t/\xe2\xe8T$F\x89\x05*4\u02f6\x1fW\x80\x00\xe0\x94\xe0\xd21\xe1D\xec\x91\a8l|\x9b\x02\xf1p,\xea\xa4\xf7\x00\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4\xe0\xd7kqf\xb1\xf3\xa1+@\x91\xee+)\u078c\xaa}\a\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\xe0\xb2\xe2\x9d\xdes\xafu\x98~\xe4Dl\x82\x9a\x18\x9c\x95\xbc\x89\b\x13\xcaV\x90m4\x00\x00\xe0\x94\xe0\xe9xu=\x98/\u007f\x9d\x1d#\x8a\x18\xbdH\x89\xae\xfeE\x1b\x8a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4\xe0\xf3r4|\x96\xb5_}C\x06\x03K\xeb\x83&o\xd9\tf\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\xf9\x03\xc1\xe4\x8a\xc4!\xabHR\x8f=J&H\b\x0f\xe0C\x897\b\xba\xed=h\x90\x00\x00\u07d4\xe0\xff\v\xd9\x15D9\u0125\xb7#>)\x1d}\x86\x8a\xf5?3\x89\x15y!jQ\xbb\xfb\x00\x00\xe0\x94\xe1\n\xc1\x9cTo\xc2T|a\xc19\xf5\xd1\xf4Zff\u0570\x8a\x01\x02\xdao\xd0\xf7:<\x00\x00\xe0\x94\xe1\fT\x00\x88\x11?\xa6\xec\x00\xb4\xb2\u0202O\x87\x96\xe9n\u010a2\x0fE\t\xab\x1e\xc7\xc0\x00\x00\xe0\x94\xe1\x17:$})\xd8#\x8d\xf0\x92/M\xf2Z\x05\xf2\xafw\u00ca\bx\xc9]V\x0f0G\x80\x00\xe0\x94\xe1 >\xb3\xa7#\xe9\x9c\" \x11|\xa6\xaf\xebf\xfaBOa\x8a\x02\x00\uf49e2V\xfe\x00\x00\xe0\x94\xe11\xf8~\xfc^\xf0~C\xf0\xf2\xf4\xa7G\xb5Q\xd7P\xd9\xe6\x8a\x04<%\xe0\xdc\xc1\xbd\x1c\x00\x00\u07d4\xe13N\x99\x83y\xdf\xe9\x83\x17pby\x1b\x90\xf8\x0e\xe2-\x8d\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe15@\xec\xee\x11\xb2\x12\xe8\xb7u\u070eq\xf3t\xaa\xe9\xb3\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1;=+\xbf\u073c\x87r\xa23\x15rL\x14%\x16|V\x88\x897\xf3y\x14\x1e\xd0K\x80\x00\u07d4\xe1D=\xbd\x95\xccA#\u007fa:HEi\x88\xa0Oh2\x82\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xe1F\x17\xf6\x02%\x01\xe9~{>-\x886\xaaa\xf0\xff-\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe1I\xb5rl\xafm^\xb5\xbf*\xccA\xd4\xe2\xdc2\x8d\u1089i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xe1T\xda\xea\xdbTX8\xcb\u01aa\fUu\x19\x02\xf5(h*\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4\xe1l\xe3Ya\xcdt\xbdY\r\x04\u012dJ\x19\x89\xe0V\x91\u0189\a\xea(2uw\b\x00\x00\u07d4\xe1r\xdf\xc8\xf8\f\xd1\xf8\u03459\xdc&\b \x14\xf5\xa8\xe3\u8262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xe1w\xe0\xc2\x01\xd35\xba9V\x92\x9cW\x15\x88\xb5\x1cR#\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1x\x12\xf6l^e\x94\x1e\x18lF\x92+n{/\x0e\xebF\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xe1\x80\u079e\x86\xf5{\xaf\xac\u05d0O\x98&\xb6\xb4\xb2c7\xa3\x89-\x04\x1dpZ,`\x00\x00\xe0\x94\xe1\x92H\x9b\x85\xa9\x82\xc1\x882F\xd9\x15\xb2)\xcb\x13 \u007f8\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xe1\x95\xbb\xc6,{tD\x04\x0e\xb9\x96#\x96Ovg\xb3v\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe2\x06\xfbs$\xe9\u07b7\x9e\x19\x904\x96\u0596\x1b\x9b\xe5f\x03\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe2\aW\x8e\x1fM\u06cf\xf6\u0546{9X-q\xb9\x81*\u0149\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xe2\b\x81*h@\x98\xf3\xdaN\xfej\xba%bV\xad\xfe?\xe6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe2\tT\xd0\xf4\x10\x8c\x82\xd4\u0732\x14\x8d&\xbb\xd9$\xf6\xdd$\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe2\v\xb9\xf3\x96d\x19\xe1K\xbb\xaa\xaag\x89\xe9$\x96\u03e4y\x89\xbb\xd8%\x03\aRv\x00\x00\u07d4\xe2\r\x1b\xcbq(m\xc7\x12\x8a\x9f\xc7\xc6\xed\u007fs8\x92\xee\xf5\x896d\xf8\xe7\xc2J\xf4\x00\x00\u0794\xe2\x19\x12\x15\x98?3\xfd3\xe2,\u0522I\x00T\xdaS\xfd\u0708\xdbD\xe0I\xbb,\x00\x00\u07d4\xe2\x19\x8c\x8c\xa1\xb3\x99\xf7R\x15a\xfdS\x84\xa7\x13/\xbaHk\x897\b\xba\xed=h\x90\x00\x00\xe0\x94\xe2\x1cw\x8e\xf2\xa0\xd7\xf7Q\xea\x8c\aM\x1f\x81\"C\x86>N\x8a\x01\x1f\xc7\x0e,\x8c\x8a\xe1\x80\x00\xe0\x94\xe2)\xe7F\xa8?,\xe2S\xb0\xb0>\xb1G$\x11\xb5~W\x00\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4\xe2+ \xc7x\x94F;\xafwL\xc2V\u057d\u06ff}\xdd\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe20\xfe\x1b\xff\x03\x18m\x02\x19\xf1]LH\x1b}Y\xbe(j\x89\x01\xfdt\x1e\x80\x88\x97\x00\x00\u07d4\xe27\xba\xa4\xdb\u0252n2\xa3\xd8]\x12d@-T\xdb\x01/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2A\t\xbe/Q=\x87I\x8e\x92j(d\x99uO\x9e\u051e\x890\x0e\xa8\xad\x1f'\xca\x00\x00\u07d4\xe2Fh<\u025d\xb7\u0125+\u02ec\xaa\xb0\xb3/k\xfc\x93\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe2Z\x16{\x03\x1e\x84am\x0f\x01?1\xbd\xa9]\xcccP\xb9\x8a\x02\x8c*\xaa\u0243\xd0]\u0187st\xa8\xf4F\xee\xe9\x89\n\xb6@9\x12\x010\x00\x00\u07d4\xe2\x8b\x06\"Y\xe9n\xeb<\x8dA\x04\x94?\x9e\xb3%\x89<\xf5\x89Hz\x9a0E9D\x00\x00\xe0\x94\u237c\x8e\xfd^Ajv.\xc0\xe0\x18\x86K\xb9\xaa\x83({\x8a\x051\xf2\x00\xab>\x03\n\x80\x00\u07d4\xe2\x90K\x1a\xef\xa0V9\x8bb4\xcb5\x81\x12\x88\xd76\xdbg\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u274a\xe4R\xdc\xf3\xb6\xacd^c\x04\t8UQ\xfa\xae\n\x89\x04Z\r\xa4\xad\xf5B\x00\x00\u07d4\xe2\xbb\xf8FA\xe3T\x1fl3\xe6\xedh:cZp\xbd\xe2\xec\x89\x1bA<\xfc\xbfY\xb7\x80\x00\u07d4\xe2\xcf6\n\xa22\x9e\xb7\x9d+\xf7\xca\x04\xa2z\x17\xc52\xe4\u0609\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xe2\xdf#\xf6\xea\x04\xbe\xcfJ\xb7\x01t\x8d\xc0\x961\x84U\\\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2\xe1\\`\xdd8\x1e:K\xe2Pq\xab$\x9aL\\Rd\u0689\u007fk\u011b\x81\xb57\x00\x00\u07d4\xe2\xe2nN\x1d\xcf0\xd0H\xccn\u03ddQ\xec\x12\x05\xa4\xe9&\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xe2\xeei\x1f#~\xe6R\x9beW\xf2\xfc\xdd=\xcf\fY\xecc\x8a\x01'r\x9c\x14h| \x00\x00\u07d4\xe2\xef\xa5\xfc\xa7\x958\xce`h\xbf1\xd2\xc5\x16\xd4\xd5<\b\xe5\x89\a\x1c\xc4\b\xdfc@\x00\x00\xe0\x94\xe2\xef\u0429\xbc@~\xce\x03\xd6~\x8e\xc8\xe9\u0483\xf4\x8d*I\x8a\x02\x99\xb3;\xf9\u0144\xe0\x00\x00\u07d4\xe2\xf4\r5\x8f^?\xe7F>\xc7\x04\x80\xbd.\u04d8\xa7\x06;\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe2\xf98=X\x10\xea{C\x18+\x87\x04\xb6+'\xf5\x92]9\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe2\xff\x9e\xe4\xb6\xec\xc1AA\xcct\xcaR\xa9\xe7\xa2\xee\x14\xd9\b\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\xe3\x02\x12\xb2\x01\x1b\xb5k\xdb\xf1\xbc5i\x0f:N\x0f\xd9\x05\xea\x8a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4\xe3\x03\x16\u007f=I`\xfe\x88\x1b2\x80\n+J\xef\xf1\xb0\x88\u0509lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3\x04\xa3/\x05\xa87btJ\x95B\x97o\xf9\xb7#\xfa1\xea\x89Ur\xf2@\xa3F \x00\x00\u07d4\xe3\bCR\x04y7d\xf5\xfc\xbee\xebQ\x0fZtJeZ\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3\t\x97L\xe3\x9d`\xaa\xdf.ig2Q\xbf\x0e\x04v\n\x10\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xe3\x1bN\xef\x18L$\xab\t\x8e6\xc8\x02qK\xd4t=\xd0\u0509\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3!\xbbJ\x94j\xda\xfd\xad\xe4W\x1f\xb1\\\x00C\u04de\xe3_\x89Udu8+L\x9e\x00\x00\u07d4\xe3&<\xe8\xafm\xb3\xe4gXE\x02\xedq\t\x12^\xae\"\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3+\x1cG%\xa1\x87TI\u93d7\x0e\xb3\xe5@b\xd1X\x00\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3/\x95vmW\xb5\xcdK\x172\x89\u0587o\x9edU\x81\x94\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe38@\u063c\xa7\u0698\xa6\xf3\u0416\xd8=\xe7\x8bp\xb7\x1e\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe38\xe8Y\xfe.\x8c\x15UHH\xb7\\\xae\u0368w\xa0\xe82\x89a\xac\xff\x81\xa7\x8a\xd4\x00\x00\u07d4\xe3=\x98\x02 \xfa\xb2Y\xafj\x1fK8\xcf\x0e\xf3\xc6\xe2\xea\x1a\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe3=\xf4\u0380\u0336*v\xb1+\xcd\xfc\xec\xc4b\x89\x97:\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xe3?\xf9\x87T\x1d\xde\\\xde\u0a29m\xcc?3\xc3\xf2L\u008a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xe3A\v\xb7U|\xf9\x1dy\xfai\xd0\xdf\xea\n\xa0u@&Q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3Ad-@\u04af\xce.\x91\a\xc6py\xacz&`\bl\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe3TS\xee\xf2\xcc2\x89\x10CR\x8d\t\x84i\x80\x00\xe0\x94\xe5\x10\xd6y\u007f\xba=f\x93\x83Z\x84N\xa2\xadT\x06\x91\x97\x1b\x8a\x03\xae9\xd4s\x83\xe8t\x00\x00\u07d4\xe5\x14!\xf8\xee\"\x10\xc7\x1e\xd8p\xfea\x82v\u0215J\xfb\xe9\x89Hz\x9a0E9D\x00\x00\u07d4\xe5\x1e\xb8~\u007f\xb71\x1fR(\xc4y\xb4\x8e\u0247\x881\xacL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5!V1\xb1BH\xd4Z%R\x96\xbe\xd1\xfb\xfa\x030\xff5\x89G\x03\xe6\xebR\x91\xb8\x00\x00\xe0\x94\xe5(\xa0\xe5\xa2g\xd6g\xe99:e\x84\xe1\x9b4\u071b\xe9s\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xe54%\xd8\xdf\x1f\x11\xc3A\xffX\xae_\x148\xab\xf1\xcaS\u03c9\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\u07d4\xe5No\x9c\xffV\xe1\x9cF\x1e\xb4T\xf9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5A\x02SM\xe8\xf2>\xff\xb0\x93\xb3\x12B\xad;#?\xac\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe5E\xee\x84\xeaH\xe5d\x16\x1e\x94\x82\u055b\xcf@j`,\xa2\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xe5H\x1a\u007f\xedB\xb9\x01\xbb\xed x\x9b\u052d\xe5\r_\x83\xb9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5Y\xb5\xfd3{\x9cUr\xa9\xbf\x9e\x0f%!\xf7\xd4F\xdb\xe4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe5\\\x80R\n\x1b\x0fu[\x9a,\xd3\xce!Ov%e>\x8a\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe5mC\x13$\xc9)\x11\xa1t\x9d\xf2\x92p\x9c\x14\xb7ze\u034a\x01\xbc\x85\xdc*\x89\xbb \x00\x00\u07d4\xe5})\x95\xb0\xeb\xdf?<\xa6\xc0\x15\xeb\x04&\r\xbb\x98\xb7\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\u51f1j\xbc\x8at\b\x1e6\x13\xe1CB\xc03u\xbf\bG\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\x89\xfav\x98M\xb5\xec@\x04\xb4n\u8954\x92\xc3\aD\u0389\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xe5\x8d\xd228\xeen\xa7\xc2\x13\x8d8]\xf5\x00\xc3%\xf3v\xbe\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xe5\x95?\xeaIq\x04\xef\x9a\xd2\xd4\xe5\x84\x1c'\x1f\a5\x19\u0089&)\xf6n\fS\x00\x00\x00\xe0\x94\u5587\x97F\x8e\xf7g\x10\x1bv\x1dC\x1f\xce\x14\xab\xff\u06f4\x8a\x01\xb3\xd9i\xfaA\x1c\xa0\x00\x00\u07d4\xe5\x97\xf0\x83\xa4i\xc4Y\x1c=+\x1d,w'\x87\xbe\xfe'\xb2\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xe5\x9b;\xd3\x00\x89?\x97#>\xf9G\xc4or\x17\xe3\x92\xf7\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5\xa3e4<\xc4\xeb\x1ew\x03h\xe1\xf1\x14Jw\xb82\xd7\xe0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe5\xa3\xd7\xeb\x13\xb1\\\x10\x01w#m\x1b\xeb0\xd1~\xe1T \x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xaa\v\x83;\xb9\x16\xdc\x19\xa8\xddh?\x0e\xde$\x1d\x98\x8e\xba\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\u5def\x14i\x86\xc0\xff\x8f\x85\xd2.l\xc34\a}\x84\xe8$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xb8&\x19l\x0e\x1b\xc1\x11\x9b\x02\x1c\xf6\xd2Y\xa6\x10\u0256p\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe5\xb9o\u026c\x03\xd4H\xc1a:\xc9\x1d\x15\x97\x81E\xdb\xdf\u0449\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u5e40\u048e\xec\xe2\xc0o\xcal\x94s\x06\x8b7\u0526\xd6\xe9\x89%\xaf\u058c\xac+\x90\x00\x00\u07d4\u5eb4\xf0\xaf\u0629\u0463\x81\xb4Wa\xaa\x18\xf3\xd3\xcc\xe1\x05\x89Q\xbf\xd7\xc18x\xd1\x00\x00\u07d4\xe5\xbc\u020c;%on\xd5\xfeU\x0eJ\x18\x19\x8b\x943V\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xbd\xf3OL\xccH>L\xa50\xcc|\xf2\xbb\x18\xfe\xbe\x92\xb3\x89\x06\xd85\xa1\v\xbc\xd2\x00\x00\u07d4\xe5\u0713I\xcbR\xe1a\x19a\"\u03c7\xa3\x896\xe2\xc5\u007f4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xe38\x00\xa1\xb2\xe9k\xde\x101c\n\x95\x9a\xa0\a\xf2nQ\x89Hz\x9a0E9D\x00\x00\u07d4\xe5\xe3~\x19@\x8f,\xfb\xec\x834\x9d\u0501S\xa4\xa7\x95\xa0\x8f\x89\u3bb5sr@\xa0\x00\x00\u07d4\xe5\xed\xc7>bo]4A\xa4U9\xb5\xf7\xa3\x98\u0153\xed\xf6\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xe5\xed\xf8\x12?$\x03\xce\x1a\x02\x99\xbe\xcfz\xactM\a_#\x89\n\xdaUGK\x814\x00\x00\u07d4\xe5\xf8\xefm\x97\x066\xb0\u072aO \x0f\xfd\xc9\xe7Z\xf1t\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xfb1\xa5\xca\xeej\x96\xde9;\xdb\xf8\x9f\xbee\xfe\x12[\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5\xfb\xe3I\x84\xb67\x19o3\x1cg\x9d\f\fG\xd84\x10\xe1\x89llD\xfeG\xec\x05\x00\x00\u07d4\xe6\tU\xdc\v\xc1V\xf6\xc4\x18I\xf6\xbdwk\xa4K\x0e\xf0\xa1\x89\x10C\x16'\xa0\x93;\x00\x00\u07d4\xe6\nU\xf2\u07d9m\u00ee\xdbil\b\xdd\xe09\xb2d\x1d\xe8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe6\x11[\x13\xf9y_~\x95e\x02\xd5\aEg\u06b9E\xcek\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xe6\x1f(\t\x15\xc7t\xa3\x1d\"<\xf8\f\x06\x92f\xe5\xad\xf1\x9b\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xe6/\x98e\a\x12\xeb\x15\x87S\xd8)r\xb8\u9723\xf6\x18w\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe6/\x9d|d\xe8\xe2cZ\xeb\x88=\xd7;\xa6\x84\xee|\x10y\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe6>xt\x14\xb9\x04\x84x\xa5\a35\x9e\xcd\xd7\xe3dz\xa6\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xe6FfXr\xe4\v\rz\xa2\xff\x82r\x9c\xaa\xba[\xc3\u8789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe6N\xf0\x12e\x8dT\xf8\xe8`\x9cN\x90#\xc0\x9f\xe8e\xc8;\x89\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\xe6On\x1dd\x01\xb5l\akd\xa1\xb0\x86}\v/1\rN\x89\x02\u02edq\xc5:\xe5\x00\x00\u07d4\xe6g\xf6R\xf9W\u008c\x0ef\u04364\x17\xc8\f\x8c\x9d\xb8x\x89 \x9d\x92/RY\xc5\x00\x00\xe0\x94\xe6w\xc3\x1f\xd9\xcbr\x00u\u0724\x9f\x1a\xbc\xcdY\xec3\xf74\x8a\x01\xa6\u05be\xb1\xd4.\xe0\x00\x00\u07d4\xe6|,\x16e\u02038h\x81\x87b\x9fI\xe9\x9b`\xb2\u04fa\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xe6\x9al\xdb:\x8a}\xb8\xe1\xf3\f\x8b\x84\xcds\xba\xe0+\xc0\xf8\x8a\x03\x94\xfd\xc2\xe4R\xf6q\x80\x00\u07d4\xe6\x9d\x1c7\x8bw\x1e\x0f\xef\xf0Q\xdbi\xd9f\xacgy\xf4\xed\x89\x1d\xfaj\xaa\x14\x97\x04\x00\x00\u07d4\xe6\x9f\xcc&\xed\"_{.7\x984\xc5$\xd7\f\x175\u5f09lk\x93[\x8b\xbd@\x00\x00\u07d4\xe6\xa3\x01\x0f\x02\x01\xbc\x94\xffg\xa2\xf6\x99\xdf\xc2\x06\xf9\xe7gB\x89/\xa7\xcb\xf6dd\x98\x00\x00\u07d4\xe6\xa6\xf6\xddop\xa4V\xf4\xec\x15\xefz\xd5\xe5\u06f6\x8b\xd7\u0709\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe6\xb2\x0f\x98\n\xd8S\xad\x04\xcb\xfc\x88|\xe6`\x1ck\xe0\xb2L\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u6cec?]M\xa5\xa8\x85}\v?0\xfcK+i+w\u05c9O%\x91\xf8\x96\xa6P\x00\x00\u07d4\xe6\xb9T_~\u0406\xe5R\x92F9\xf9\xa9\xed\xbb\xd5T\v>\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\xe0\x94\xe6\xbc\xd3\n\x8f\xa18\xc5\xd9\xe5\xf6\xc7\xd2\u0680i\x92\x81-\u034a7\x0e\xa0\xd4|\xf6\x1a\x80\x00\x00\u07d4\xe6\xc8\x1f\xfc\xec\xb4~\xcd\xc5\\\vq\xe4\x85_>^\x97\xfc\x1e\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4\xe6\xcb&\vqmL\n\xb7&\xee\xeb\a\xc8pr\x04\xe2v\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe6\xcb?1$\xc9\xc9\xcc84\xb1'K\xc33dV\xa3\x8b\xac\x89\x17+\x1d\xe0\xa2\x13\xff\x00\x00\xe0\x94\xe6\xd2\"\t\xff\u0438u\t\xad\xe3\xa8\xe2\xefB\x98y\u02c9\xb5\x8a\x03\xa7\xaa\x9e\x18\x99\xca0\x00\x00\u07d4\xe6\u051f\x86\xc2(\xf4sg\xa3^\x88l\xaa\xcb'\x1eS\x94)\x89\x16^\xc0\x9d\xa7\xa1\x98\x00\x00\u07d4\xe6\xe6!\xea\xab\x01\xf2\x0e\xf0\x83k|\xadGFL\xb5\xfd<\x96\x89\x11!\x93B\xaf\xa2K\x00\x00\u07d4\xe6\xe8\x861{jf\xa5\xb4\xf8\x1b\xf1d\xc58\xc2d5\x17e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe6\u98ddu\x0f\xe9\x949N\xb6\x82\x86\xe5\xeab\xa6\x99x\x82\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xe6\xec\\\xf0\u011b\x9c1~\x1epc\x15\uf7b7\xc0\xbf\x11\xa7\x8a\x03\xa4i\xf3F~\x8e\xc0\x00\x00\u07d4\xe6\xf5\xebd\x9a\xfb\x99Y\x9cAK'\xa9\xc9\xc8U5\u007f\xa8x\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xe6\xfe\n\xfb\x9d\xce\xdd7\xb2\xe2,E\x1b\xa6\xfe\xabg4\x803\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe7\x10\xdc\u041b\x81\x01\xf9C{\xd9}\xb9\ns\xef\x99=\v\xf4\x89\x14\xee6\xc0Z\xc2R\x00\x00\u07d4\xe7'\xe6~\xf9\x11\xb8\x1fl\xf9\xc7?\xcb\xfe\xbc+\x02\xb5\xbf\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7.\x1d3\\\u009a\x96\xb9\xb1\xc0/\x00:\x16\xd9q\xe9\v\x9d\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xe71\x1c\x953\xf0\t,rH\xc9s\x9b[,\x86J4\xb1\u0389\x97\xf9}l\xc2m\xfe\x00\x00\u07d4\xe7;\xfe\xad\xa6\xf0\xfd\x01o\xbc\x84>\xbc\xf6\xe3p\xa6[\xe7\f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xe7<\xcfCg%\xc1Q\xe2U\xcc\xf5!\f\xfc\xe5\xa4?\x13\xe3\x89\x01\x15NS!}\xdb\x00\x00\u07d4\xe7B\xb1\xe6\x06\x9a\x8f\xfc'\f\xc6\x1f\xa1d\xac\x15SE\\\x10]\x04\x88~\x14\x89\x06\x96\xd8Y\x00 \xbb\x00\x00\u07d4\xe7\\\x1f\xb1w\b\x9f>X\xb1\x06y5\xa6Yn\xf1s\u007f\xb5\x89\x05j\x87\x9f\xa7uG\x00\x00\u07d4\xe7\\;8\xa5\x8a?3\xd5V\x90\xa5\xa5\x97f\xbe\x18^\x02\x84\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe7a\xd2\u007f\xa3P,\xc7k\xb1\xa6\bt\x0e\x14\x03\u03dd\xfci\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xe7f\xf3O\xf1o<\xfc\xc9s!r\x1fC\xdd\xf5\xa3\x8b\f\xf4\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xe7m\x94Z\xa8\x9d\xf1\xe4W\xaa4+1\x02\x8a^\x910\xb2\u03897\b\xba\xed=h\x90\x00\x00\u07d4\xe7s^\xc7e\x18\xfcj\xa9-\xa8qZ\x9e\xe3\xf6%x\x8f\x13\x89lM\x16\v\xaf\xa1\xb7\x80\x00\xe0\x94\xe7z\x89\xbdE\xdc\x04\xee\xb4\xe4\x1d{Ykp~nQ\xe7L\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xe7}}\uac96\u0234\xfa\a\xca;\xe1\x84\x16=Zm`l\x89\x05\x049\x04\xb6q\x19\x00\x00\u07d4\xe7\u007f\xeb\xab\xdf\b\x0f\x0f]\xca\x1d?Wf\xf2\xa7\x9c\x0f\xfa|\x89K\"\x9d(\xa8Ch\x00\x00\xe0\x94\u7025c\x06\xba\x1ek\xb31\x95,\"S\x9b\x85\x8a\xf9\xf7}\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xe7\x81\xecs-@\x12\x02\xbb\x9b\xd18`\x91\r\xd6\u009a\xc0\xb6\x89C8t\xf62\xcc`\x00\x00\u07d4\xe7\x84\xdc\xc8s\xaa\x8c\x15\x13\xec&\xff6\xbc\x92\xea\xc6\xd4\xc9h\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe7\x91-L\xf4V,W=\xdc[q\xe3s\x10\xe3x\xef\x86\u0249\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xe7\x91\u0545\xb8\x996\xb2])\x8f\x9d5\xf9\xf9\xed\xc2Z)2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7\x924\x9c\xe9\xf6\xf1O\x81\xd0g@\x96\xbe\xfa\x1f\x92!\xcd\xea\x89[]#J\r\xb48\x80\x00\u07d4\xe7\x96\xfdN\x83\x9bL\x95\xd7Q\x0f\xb7\xc5\xc7+\x83\xc6\xc3\xe3\u01c9\x1b\xc43\xf2?\x83\x14\x00\x00\xe0\x94\xe7\xa4/Y\xfe\xe0t\xe4\xfb\x13\xea\x9eW\xec\xf1\xccH(\"I\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xe7\xa4V\f\x84\xb2\x0e\x0f\xb5LIg\f)\x03\xb0\xa9lB\xa4\x89 j\xea\u01e9\x03\x98\x00\x00\u07d4\xe7\xa8\xe4q\xea\xfby\x8fET\xccnRg0\xfdV\xe6,}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u7f82\xc6Y<\x1e\xed\xdd*\xe0\xb1P\x01\xff \x1a\xb5{/\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4\xe7\u01b5\xfc\x05\xfct\x8e[C\x81rdI\xa1\xc0\xad\x0f\xb0\xf1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7\xd1u$\xd0\v\xad\x82I|\x0f'\x15jd\u007f\xf5\x1d'\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe7\xd2\x13\x94\u007f\u02d0J\xd78H\v\x1e\xed/\\2\x9f'\xe8\x89\x01\x03\u00f1\xd3\xe9\xc3\x00\x00\u07d4\xe7\xd6$\x06 \xf4,^\u06f2\xed\xe6\xae\xc4=\xa4\xed\x9bWW\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe7\xda`\x9d@\xcd\xe8\x0f\x00\xce[O\xfbj\xa9\u04304\x94\xfc\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe7\xf0oi\x9b\xe3\x1cD\vC\xb4\xdb\x05\x01\xec\x0e%&\x16D\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe7\xf4\xd7\xfeoV\x1f\u007f\xa1\xda0\x05\xfd6TQ\xad\x89\u07c9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe7\xfd\x8f\xd9Y\xae\xd2v~\xa7\xfa\x96\f\xe1\xdbS\xaf\x80%s\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe8\x0e\u007f\xef\x18\xa5\xdb\x15\xb0\x14s\xf3\xadkx\xb2\xa2\xf8\xac\u0649\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe8\x13\u007f\xc1\xb2\xec|\xc7\x10:\xf9!\x89\x9bJ9\xe1\xd9Y\xa1\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\xe8\x1c-4l\n\xdfL\xc5g\b\xf69K\xa6\xc8\u0226J\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8,X\xc5yC\x1bg5F\xb5:\x86E\x9a\xca\xf1\u079b\x93\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe84\xc6C\x18 \\\xa7\xddJ!\xab\xcb\b&l\xb2\x1f\xf0,\x8965\xc6 G9\u0640\x00\u07d4\xe86\x04\xe4\xffk\xe7\xf9o`\x18\xd3\xec0r\xecR]\xffk\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\xe0\x94\xe8E\xe3\x87\xc4\xcb\u07d8\"\x80\xf6\xaa\x01\xc4\x0eK\xe9X\u0772\x8a\x05K@\xb1\xf8R\xbd\xa0\x00\x00\u07d4\xe8H\xca~\xbf\xf5\xc2O\x9b\x9c1g\x97\xa4;\xf7\xc3V)-\x89\x06.\x11\\\x00\x8a\x88\x00\x00\u07d4\xe8KU\xb5%\xf1\x03\x9etK\x91\x8c\xb33$\x92\xe4^\xcaz\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe8O\x80v\xa0\xf2\x96\x9e\xcd3>\xef\x8d\xe4\x10B\x98b\x91\xf2\x89\x17k4O*x\xc0\x00\x00\u07d4\xe8d\xfe\xc0~\xd1!Je1\x1e\x11\xe3)\xde\x04\r\x04\xf0\xfd\x89Y\u0283\xf5\xc4\x04\x96\x80\x00\u07d4\xe8}\xba\xc66\xa3w!\xdfT\xb0\x8a2\xefIY\xb5\xe4\xff\x82\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe8~\x9b\xbf\xbb\xb7\x1c\x1at\ft\xc7#Bm\xf5]\x06=\u064a\x01\xb1\x92\x8c\x00\u01e68\x00\x00\u07d4\xe8~\xacm`+A\t\xc9g\x1b\xf5{\x95\f,\xfd\xb9\x9dU\x89\x02\xb4\xf2\x19r\xec\xce\x00\x00\xe0\x94\u807b\xbeir-\x81\xef\xec\xaaH\u0455*\x10\xa2\xbf\xac\x8f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xe8\x92Is\x8b~\xce\xd7\xcbfjf\xe4s\xbcv\x82/U\t\x8d\x89\xb9\x1c\u0149lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\xc3\u04f0\xe1\u007f\x97\xd1\xe7V\xe6\x84\xf9N\x14p\xf9\x9c\x95\xa1\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xe8\xc3\xf0E\xbb}8\xc9\xd2\U000d5c3a\x84\x92\xb2S#\t\x01\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4\xe8\xccC\xbcO\x8a\xcf9\xbf\xf0N\xbf\xbfB\xaa\xc0j2\x84p\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe8\xd9B\xd8/\x17^\xcb\x1c\x16\xa4\x05\xb1\x01C\xb3\xf4k\x96:\x89\x1e\xd2\xe8\xffm\x97\x1c\x00\x00\u07d4\xe8\u077e\xd72\xeb\xfeu@\x96\xfd\xe9\bk\x8e\xa4\xa4\xcd\xc6\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\xder^\xca]\xef\x80_\xf7\x94\x1d1\xac\x1c.4-\xfe\x95\x89\x85~\ro\x1d\xa7j\x00\x00\u07d4\xe8\xe9\x85\x05\x86\xe9OR\x99\xabIK\xb8!\xa5\xf4\f\x00\xbd\x04\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\xe8\xea\u047b\x90\xcc\u00ee\xa2\xb0\xdc\u0175\x80VUFU\xd1\u054a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\u07d4\xe8\xea\xf1)D\t-\xc3Y\x9b9S\xfa|\xb1\xc9v\x1c\xc2F\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xe8\xedQ\xbb\xb3\xac\xe6\x9e\x06\x02K3\xf8hD\xc4sH\u06de\x8a\"\xf9\xea\x89\xf4\xa7\xd6\xc4\x00\x00\u07d4\xe8\xef\x10\r|\xe0\x89X2\xf2g\x8d\xf7-J\u03cc(\xb8\xe3\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xe8\xf2\x99i\xe7\\e\xe0\x1c\xe3\xd8aT }\n\x9e|v\xf2\x89\xa2/\xa9\xa7:'\x19\x80\x00\u07d4\xe8\xfc6\xb0\x13\x1e\xc1 \xac\x9e\x85\xaf\xc1\f\xe7\vV\u0636\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9\n5L\xec\x04\u059e]\x96\xdd\xc0\xc5\x13\x8d=3\x15\n\xa0\x89\x1b\x1a}\u03caD\u04c0\x00\xe0\x94\xe9\x13>}1\x84]_+f\xa2a\x87\x92\xe8i1\x1a\xcff\x8a\x05\x17\xc0\xcb\xf9\xa3\x90\x88\x00\x00\u07d4\xe9\x1d\xac\x01\x95\xb1\x9e7\xb5\x9bS\xf7\xc0\x17\xc0\xb29[\xa4L\x89e\xea=\xb7UF`\x00\x00\u07d4\xe9\x1f\xa0\xba\xda\u0779\xa9~\x88\xd3\xf4\xdb|U\u05bbt0\xfe\x89\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\xe9#\xc0aw\xb3B~\xa4H\xc0\xa6\xff\x01\x9bT\xccT\x8d\x95\x89\x01\xf7\x80\x01Fg\xf2\x80\x00\xe0\x94\xe9=G\xa8\u0288]T\fNRo%\xd5\xc6\xf2\xc1\b\u0138\x8a\x17\xda:\x04\u01f3\xe0\x00\x00\x00\u07d4\xe9E\x8fh\xbb',\xb5g:\x04\xf7\x81\xb4\x03Uo\u04e3\x87\x89\x03N\x8b\x88\xce\xe2\xd4\x00\x00\u07d4\xe9IA\xb6\x03`\x19\xb4\x01j0\xc1\x03}Zi\x03\xba\xba\xad\x89*H\xac\xabb\x04\xb0\x00\x00\u07d4\xe9I[\xa5\x84'(\xc0\ud5fe7\xd0\xe4\"\xb9\x8di ,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe9M\xed\x99\u0735r\xb9\xbb\x1d\u02e3/m\xee\x91\xe0W\x98N\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\xe0\x94\xe9QyR}\uc951l\xa9\xa3\x8f!\\\x1e\x9c\xe77\xb4\u024a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xe9U\x91\x85\xf1f\xfc\x95\x13\xccq\x11aD\xce-\xeb\x0f\x1dK\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xe9^\x92\xbb\xc6\xde\a\xbf:f\x0e\xbf_\xeb\x1c\x8a5'\xe1\u0148\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\xe9e\u06a3@9\xf7\xf0\xdfb7Z7\u5acar\xb3\x01\xe7\x8a\x01\x03\xfd\xde\u0373\xf5p\x00\x00\u07d4\xe9i\xea\x15\x95\xed\xc5\u0127\a\xcf\xde8\t)c2Q\xa2\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9k\x18N\x1f\x0fT\x92J\xc8t\xf6\v\xbfDptF\xb7+\x89\x9d\xcc\x05\x15\xb5n\f\x00\x00\xe0\x94\xe9m}L\xdd\x15U:NM1mmd\x80\xca<\xea\x1e8\x8a\x02\x95]\x02\xe1\xa15\xa0\x00\x00\u07d4\xe9n-8\x13\xef\xd1\x16_\x12\xf6\x02\xf9\u007fJb\x90\x9d\x1b;\xc0\xe9\xaa\"\u007f\x90\x89'\xcaK\xd7\x19\xf0\xb8\x00\x00\u07d4\xea,\x19}&\xe9\x8b\r\xa8>\x1br\u01c7a\x8c\x97\x9d=\xb0\x89\x01\x11du\x9f\xfb2\x00\x00\xe0\x94\xea7y\xd1J\x13\xf6\u01c5f\xbc\xde@5\x91A:b9\u06ca)\xb7d2\xb9DQ \x00\x00\u07d4\xeaN\x80\x9e&j\xe5\xf1<\xdb\u33dd\x04V\xe68m\x12t\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\xe0\x94\xeaS\xc9T\xf4\xed\x97\xfdH\x10\x11\x1b\u06b6\x9e\xf9\x81\xef%\xb9\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xeaS\xd2ed\x85\x9d\x9e\x90\xbb\x0eS\xb7\xab\xf5`\xe0\x16,8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xea`Ci\x12\xdek\xf1\x87\u04e4r\xff\x8fS3\xa0\xf7\xed\x06\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xea`T\x9e\xc7U?Q\x1d!I\xf2\xd4fl\xbd\x92C\xd9<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xeaf\xe7\xb8M\u037f6\xee\xa3\xe7[\x858*u\xf1\xa1]\x96\x89]\xbc\x91\x91&o\x11\x80\x00\u07d4\xeahlPW\t<\x17\x1cf\u06d9\xe0\x1b\x0e\xce\xcb0\x86\x83\x89\x14\u0768],\xe1G\x80\x00\u07d4\xeaj\xfe,\xc9(\xac\x83\x91\xeb\x1e\x16_\xc4\x00@\xe3t!\u7262\u007f\xa0c\xb2\xe2\xe6\x80\x00\u07d4\xeay\x05}\xab\xef^d\xe7\xb4O\u007f\x18d\x8e~S7\x18\u0489\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xea|Mm\xc7)\xcdk\x15|\x03\xad#|\xa1\x9a \x93F\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\x81h\xfb\xf2%\xe7\x86E\x9c\xa6\xbb\x18\xd9c\xd2kPS\t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xea\x81\u02868T\f\xd9\xd4\xd7=\x06\x0f,\xeb\xf2$\x1f\xfc>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xea\x83\x17\x19yYB@A\xd9\xd7\xc6z>\xce\x1d\xbbx\xbbU\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xea\x85'\xfe\xbf\xa1\xad\xe2\x9e&A\x93)\u04d3\xb9@\xbb\xb7\u0709lj\xccg\u05f1\xd4\x00\x00\u07d4\xea\x8f0\xb6\xe4\xc5\xe6R\x90\xfb\x98d%\x9b\u0159\x0f\xa8\ue289\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xea\x94\xf3(\b\xa2\uf29b\xf0\x86\x1d\x1d$\x04\xf7\xb7\xbe%\x8a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xea\xa4\\\xea\x02\xd8},\xc8\xfd\xa9CN-\x98[\xd4\x03\x15\x84\x89h\x1f\xc2\xccn+\x8b\x00\x00\xe0\x94\uac3d\x14\x83\t\x18l\xf8\xcb\xd1;r2\xd8\tZ\u02c3:\x8a\x02C\x9a\x88\x1cjq|\x00\x00\u07d4\uaed0\xd3y\x89\xaa\xb3\x1f\xea\xe5G\xe0\xe6\xf3\x99\x9c\xe6\xa3]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xc0\x82~\xff\fn?\xf2\x8a}JT\xf6\\\xb7h\x9d{\x99\x89\x9a\xd9\u67ddGR\x00\x00\u07d4\xea\xc1H(&\xac\xb6\x11\x1e\x19\xd3@\xa4_\xb8QWk\xed`\x89\x01\xbe\x8b\xab\x04\u067e\x80\x00\xe0\x94\xea\xc1{\x81\xedQ\x91\xfb\b\x02\xaaT3s\x13\x83A\a\xaa\xa4\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xea\u00efW\x84\x92\u007f\u9958\xfcN\xec8\xb8\x10/7\xbcX\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xea\u01b9\x88BT.\xa1\v\xb7O&\xd7\xc7H\x8fi\x8bdR\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xea\xc7h\xbf\x14\xb8\xf9C.i\xea\xa8*\x99\xfb\xeb\x94\xcd\f\x9c\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4\xea\xd2\x1c\x1d\xec\u03ff\x1c\\\xd9f\x88\xa2Gki\xba\a\xceJ\x89\x03\xf2M\x8eJ\x00p\x00\x00\u07d4\xea\xd4\xd2\xee\xfbv\xab\xaeU3\x96\x1e\xdd\x11@\x04\x06\xb2\x98\xfc\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xea\xd6Rb\xed]\x12-\xf2\xb2u\x14\x10\xf9\x8c2\xd1#\x8fQ\x89\x05\x83\x17\xedF\xb9\xb8\x00\x00\u07d4\xea\xd7P\x16\u3801Pr\xb6\xb1\b\xbc\xc1\xb7\x99\xac\xf08>\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xea#\xaa\x05r\x00\xe7\xc9\xc1^\x8f\xf1\x90\xd0\xe6l\f\x0e\x83\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xed\x16\xea\xf5\u06ab[\xf0)^^\a\u007fY\xfb\x82U\x90\v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xea\xed\xcck\x8bib\xd5\xd9(\x8c\x15lW\x9dG\xc0\xa9\xfc\xff\x89\x04\x9b\x9c\xa9\xa6\x944\x00\x00\u07d4\xea\xf5#\x88Tn\xc3Z\xcaolc\x93\xd8\xd6\t\xde:K\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xeb\x10E\x8d\xac\xa7\x9eJk$\xb2\x9a\x8a\x8a\xdaq\x1b\u007f.\xb6\x89\u063beI\xb0+\xb8\x00\x00\u07d4\xeb\x1c\xea{E\u047dM\x0e*\x00{\u04ff\xb3Tu\x9e,\x16\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\xeb%H\x1f\u035c\"\x1f\x1a\xc7\xe5\xfd\x1e\u0353\a\xa1b\x15\xb8\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\xeb.\xf3\u04cf\xe6R@<\xd4\xc9\xd8^\xd7\xf0h,\xd7\xc2\u078a\t\x0fSF\b\xa7(\x80\x00\x00\xe0\x94\xeb;\xddY\xdc\u0765\xa9\xbb*\xc1d\x1f\xd0!\x80\xf5\xf3e`\x8a\x01e\xc9fG\xb3\x8a \x00\x00\u07d4\xeb<\xe7\xfc8\x1cQ\xdb}_\xbdi/\x8f\x9e\x05\x8aLp=\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xebE?Z:\xdd\u074a\xb5gP\xfa\xdb\x0f\xe7\xf9M\x9c\x89\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xebO\x00\xe2\x836\xea\t\x94%\x88\ueb12\x18\x11\xc5\"\x14<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xebR\xab\x10U4\x922\x9c\x1cT\x83:\xe6\x10\xf3\x98\xa6[\x9d\x89\b=lz\xabc`\x00\x00\u07d4\xebW\r\xba\x97R'\xb1\xc4-n\x8d\xea,V\u026d\x96\x06p\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xebc\x94\xa7\xbf\xa4\u0489\x11\u0565\xb2>\x93\xf3^4\f\"\x94\x89\x04:w\xaa\xbd\x00x\x00\x00\u07d4\xebh\x10i\x1d\x1a\xe0\u045eG\xbd\"\u03be\u0cfa'\xf8\x8a\x89\x87\x85c\x15\xd8x\x15\x00\x00\u07d4\xebvBL\x0f\u0557\xd3\xe3A\xa9d*\xd1\xee\x11\x8b+W\x9d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xeb| +F+|\u0145]t\x84u_n&\xefC\xa1\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xeb\x83\\\x1a\x91\x18\x17\x87\x8a3\xd1gV\x9e\xa3\xcd\u04c7\xf3(\x8965\u026d\xc5\u07a0\x00\x00\u07d4\ub268\x82g\t\t\xcf7~\x9ex(n\xe9{\xa7\x8dF\u0089+|\xc2\xe9\xc3\"\\\x00\x00\xe0\x94\xeb\x90\u01d3\xb3S\x97a\xe1\xc8\x14\xa2\x96q\x14\x86\x92\x19>\xb4\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xeb\x9c\xc9\xfe\bi\xd2\u06b5,\u01ea\xe8\xfdW\xad\xb3_\x9f\xeb\x89j\x93\xbb\x17\xaf\x81\xf8\x00\x00\xe0\x94\ub8c8\xb0\xda'\xc8{\x1c\xc0\xea\xc6\xc5{,Z\vE\x9c\x1a\x8a\x01p\xa0\xf5\x04\x0eP@\x00\x00\u07d4\xeb\xaa!m\xe9\xccZC\x03\x17\a\xd3o\xe6\u057e\xdc\x05\xbd\xf0\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xeb\xac+D\b\xefT1\xa1;\x85\b\xe8bP\x98!\x14\xe1E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xeb\xb6,\xf8\xe2,\x88K\x1b(\xc6\xfa\x88\xfb\xbc\x17\x93\x8a\xa7\x87\x89+By\x84\x03\u0278\x00\x00\u07d4\xeb\xb7\xd2\xe1\x1b\u01b5\x8f\n\x8dE\xc2\xf6\xde0\x10W\n\u0211\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4\xeb\xbbO,=\xa8\xbe>\xb6-\x1f\xfb\x1f\x95\x02a\u03d8\xec\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4\xeb\xbdM\xb9\x01\x99R\u058b\x1b\x0fm\x8c\xf0h<\x008{\xb5\x89\x12\x04\x01V=}\x91\x00\x00\u07d4\xeb\xbe\xeb%\x91\x84\xa6\xe0\x1c\xcc\xfc\"\a\xbb\u0603xZ\xc9\n\x89!\x9b\xc1\xb0G\x83\xd3\x00\x00\u07d4\xeb\xd3V\x15j81#4=H\x84;\xff\xeda\x03\xe8f\xb3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xeb\xd3{%ec\xe3\fo\x92\x89\xa8\xe2p/\bR\x88\b3\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xeb\xe4l\xc3\xc3L2\xf5\xad\xd6\xc3\x19[\xb4\x86\xc4q>\xb9\x18\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xeb\xff\x84\xbb\xefB0q\xe6\x04\xc3a\xbb\xa6w\xf5Y=\xefN\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xec\t'\xba\xc7\xdc6f\x9c(5J\xb1\xbe\x83\xd7\xee\xc3\t4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\x0e\x18\xa0\x1d\xc4\xdc]\xaa\xe5g\xc3\xfaL\u007f\x8f\x9bY\x02\x05\x89\x11\x1f\xfe@JA\xe6\x00\x00\xe0\x94\xec\x116,\xec\x81\t\x85\xd0\xeb\xbd{sE\x14D\x98[6\x9f\x8a\x06ZNIWpW1\x80\x00\u07d4\xec,\xb8\xb97\x8d\xff1\xae\xc3\xc2.\x0em\xad\xff1J\xb5\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\xec0\xad\u0749[\x82\xee1\x9eT\xfb\x04\xcb+\xb09q\xf3k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec;\x8bX\xa1'\x03\xe5\x81\xce_\xfd~!\xc5}\x1e\\f?\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xecHg\xd2\x17Z\xb5\xb9F\x93aYUFUF\x84\u0364`\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xecM\b\xaa.GIm\u0287\"]\xe3?+@\xa8\xa5\xb3o\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\xecX\xbc\r\f \xd8\xf4\x94efAS\xc5\xc1\x96\xfeY\u6f89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xec[\x19\x8a\x00\u03f5Z\x97\xb5\xd56D\xcf\xfa\x8a\x04\u04abE\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec]\xf2'\xbf\xa8]z\xd7kBn\x1c\xee\x96;\xc7\xf5\x19\u074965\u026d\xc5\u07a0\x00\x00\xe0\x94\xec_\xea\xfe!\f\x12\xbf\u0265\xd0Y%\xa1#\xf1\xe7?\xbe\xf8\x8a`\x8f\xcf=\x88t\x8d\x00\x00\x00\u07d4\xeci\x04\xba\xe1\xf6\x97\x90Y\x17\t\xb0`\x97\x83s?%s\xe3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xecs\x11L^@o\u06fe\t\xb4\xfab\x1b\xd7\x0e\xd5N\xa1\xef\x8a\x050%\xcd!o\xceP\x00\x00\u07d4\xecs\x83=\xe4\xb8\x10\xbb\x02x\x10\xfc\x8fi\xf5D\xe8<\x12\u044965\u026d\xc5\u07a0\x00\x00\u07d4\xecu\xb4\xa4u\x13\x12\v\xa5\xf8`9\x81O\x19\x98\xe3\x81z\u00c9\t\xb0\xbc\xe2\xe8\xfd\xba\x00\x00\u07d4\xecv\xf1.W\xa6U\x04\x03?,\v\xceo\xc0;\xd7\xfa\n\u0109\xc2\x12z\xf8X\xdap\x00\x00\u0794\xec\x80\x14\xef\xc7\xcb\xe5\xb0\xceP\xf3V,\xf4\xe6\u007f\x85\x93\xcd2\x88\xf0\x15\xf2W6B\x00\x00\u07d4\xec\x82\xf5\r\x06G_hM\xf1\xb3\x92\xe0\r\xa3A\xaa\x14TD\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xec\x83\xe7\x98\u00d6\xb7\xa5^*\"$\xab\u0343K'\xeaE\x9c\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xec\x89\xf2\xb6x\xa1\xa1[\x914\xec^\xb7\fjb\a\x1f\xba\xf9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xec\x8c\x1d{j\xac\xcdB\x9d\xb3\xa9\x1e\xe4\xc9\xeb\x1c\xa4\xf6\xf7<\x89\xe6d\x99\"\x88\xf2(\x00\x00\xe0\x94\xec\x98Q\xbd\x91rpa\x02g\xd6\x05\x18\xb5M<\xa2\xb3[\x17\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xec\x99\xe9]\xec\xe4o\xff\xfb\x17^\xb6@\x0f\xbe\xbb\b\ue6d5\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xec\xa5\xf5\x87\x92\xb8\xc6-*\xf5Vq~\xe3\xee0(\xbeM\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\xabZ\xba[\x82\x8d\xe1pS\x81\xf3\x8b\xc7D\xb3+\xa1\xb47\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\xec\xaf3P\xb7\xce\x14M\x06\x8b\x18`\x10\x85,\x84\xdd\f\xe0\xf0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\xb9LV\x8b\xfeY\xad\xe6Pd_O&0lsl\xac\xe4\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xec\xbeB^g\r9\tN \xfbVC\xa9\xd8\x18\xee\xd26\u078a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xec\xbe^\x1c\x9a\u04b1\xdc\xcf\n0_\xc9R/Fi\xdd:\xe7\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xec\xcfz\x04W\xb5f\xb3F\xcag:\x18\x0fDA0!j\u00c9\x05k\xc7^-c\x10\x00\x00\u07d4\xec\u0466(\x025\x1aAV\x8d#\x030\x04\xac\xc6\xc0\x05\xa5\u04c9\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xec\xd2v\xafd\u01dd\x1b\u0669+\x86\xb5\u835a\x95\xeb\x88\xf8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xec\u0506\xfc\x19g\x91\xb9,\xf6\x12\xd3HaO\x91VH\x8b~\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xec\xda\xf92)\xb4^\xe6r\xf6]\xb5\x06\xfb^\xca\x00\xf7\xfc\xe6\x89W\x01\xf9m\xcc@\xee\x80\x00\u07d4\xec\xe1\x11g\vV<\u037e\xbc\xa5#\x84)\x0e\xcdh\xfe\\\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xec\xe1\x15&\x82\xb7Y\x8f\xe2\xd1\xe2\x1e\xc1U3\x88T5\xac\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xec\xe1)\bw\xb5\x83\xe3a\xa2\xd4\x1b\x00\x93F\xe6'N%8\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xec\xf0]\a\xea\x02n~\xbfIA\x00#5\xba\xf2\xfe\xd0\xf0\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xec\xf2L\xdd|\"\x92\x8cD\x1eiM\xe4\xaa1\xb0\xfa\xb5\x97x\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xec\xfd\x00M\x02\xf3l\xd4\u0634\xa8\xc1\xa9S;j\xf8\\\xd7\x16\x8a\x01\x0fA\xac\xb4\xbb;\x9c\x00\x00\xe0\x94\xed\x02\x06\xcb#1Q(\xf8\xca\xff&\xf6\xa3\v\x98Tg\xd0\"\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xed\x10e\xdb\u03dds\xc0O\xfcy\b\x87\r\x88\x14h\xc1\xe12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xed\x12vQ;o\u0186(\xa7A\x85\xc2\xe2\f\xbb\xcax\x17\xbf\x89\nZ\xa8P\t\xe3\x9c\x00\x00\xe0\x94\xed\x12\xa1\xba\x1f\xb8\xad\xfc\xb2\r\xfa\x19X.RZ\xa3\xb7E$\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xed\x16\xce9\xfe\xef;\xd7\xf5\xd1b\x04^\x0fg\xc0\xf0\x00F\xbb\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xed\x1a\\C\xc5t\xd4\xe94)\x9b$\xf1G,\u071f\xd6\xf0\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xed\x1b$\xb6\x91-Q\xb34\xac\r\xe6\xe7q\xc7\xc0EF\x95\xea\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xed\x1f\x1e\x11Z\r`\xce\x02\xfb%\xdf\x01M(\x9e:\f\xbe}\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xed10\\1\x9f\x92s\u04d3m\x8f[/q\u9c72)c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xed2z\x14\xd5\u03ed\u0641\x03\xfc\t\x99q\x8d~\xd7\x05(\xea\x89N\x10\x03\xb2\x8d\x92\x80\x00\x00\u07d4\xed<\xbc7\x82\u03bdg\x98\x9b0\\A3\xb2\xcd\xe3\"\x11\xeb\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xed@\x14S\x8c\xeefJ/\xbc\xb6\xdcf\x9fz\xb1m\v\xa5|\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xedA\u188f\\\xaa\x848\x80\xefN\x8b\b\xbdl3\x14\x1e\u07c9*\xd5\xdd\xfaz\x8d\x83\x00\x00\xe0\x94\xedK\xe0J\x05-z\u0333\xdc\u03901\x9d\xba@ \xab,h\x8a\a\xf3zp\xea\xf3b\x17\x80\x00\xe0\x94\xedR\xa2\xcc\bi\u071e\x9f\x84+\u0415|G\xa8\xe9\xb0\xc9\xff\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xed[LA\xe7b\xd9B@Cs\xca\xf2\x1e\xd4a]%\xe6\xc1\x89m-O=\x95%\xb4\x00\x00\u07d4\xed`\u012bnT\x02\x061~5\x94zc\xa9\xcak\x03\xe2\u02c9\x03\x1a\u066d\vF\u007f\x80\x00\u07d4\xedd\x1e\x066\x8f\xb0\xef\xaa\x17\x03\xe0\x1f\xe4\x8fJhS\t\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xedfC\xc0\xe8\x88K-2\x11\x857\x85\xa0\x8b\xf8\xf3>\u049f\x89Hz\x9a0E9D\x00\x00\xe0\x94\xedp\xa3|\xdd\x1c\xbd\xa9tm\x93\x96X\xae*a\x81(\x85x\x8a\x02\bj\xc3Q\x05&\x00\x00\x00\u07d4\xedsFvn\x1agm\r\x06\xec\x82\x18g\xa2v\xa0\x83\xbf1\x89\u064a\t1\xcc-I\x00\x00\u07d4\xed\x86&\x16\xfc\xbf\xb3\xbe\xcbt\x06\xf7<\\\xbf\xf0\f\x94\aU\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xed\x9e\x03\f\xa7\\\xb1\u049e\xa0\x1d\rL\xdf\xdc\xcd8D\xb6\xe4\x89\x01\xac\xc1\x16\u03ef\xb1\x80\x00\xe0\x94\ud7bc\u02e4/\x98\x15\xe7\x823&m\xd6\xe85\xb6\xaf\xc3\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\ud7f1\xf5\xaf/\xbf\u007f\xfcP)\xce\xe4+p\xff\\'[\xf5\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xed\xa4\xb2\xfaY\u0584\xb2z\x81\r\xf8\x97\x8as\xdf0\x8ac\u0089\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xed\xb4s59y\xa2\x06\x87\x9d\xe1D\xc1\n:\xcf\x12\xa7'OV9a\xf57R\x9d\x89\xc7\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xeer\x88\xd9\x10\x86\xd9\xe2\xeb\x91\x00\x14\u066b\x90\xa0-x\u00a0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xee|=\xed|(\xf4Y\xc9/\xe1;M\x95\xba\xfb\xab\x026}\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\xee\x86} \x91k\xd2\xe9\xc9\xec\xe0\x8a\xa0C\x85\xdbf|\x91.\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\ue25b\x02\xcb\xcb99\xcda\xde\x13B\xd5\x04\x82\xab\xb6\x852\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xee\x90m}_\x17H%\x81t\xbeL\xbc8\x93\x03\x02\xab{B\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\ue5ea\x8a\u019e\xdfz\x98}mp\x97\x9f\x8e\xc1\xfb\xcaz\x94\x89\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\xee\xa1\xe9y\x88\xdeu\xd8!\xcd(\xadh\"\xb2,\u0398\x8b1\x89\x1c0s\x1c\xec\x03 \x00\x00\xe0\x94\xee\u048c?\x06\x8e\tJ0K\x85<\x95\nh\t\xeb\xcb\x03\xe0\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xee\u04c4\xef-A\xd9\xd2\x03\x97NW\xc1#(\xeav\x0e\b\xea\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xee\xdflB\x80\xe6\xeb\x05\xb94\xac\xe4(\xe1\x1dB1\xb5\x90[\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xee\xe7a\x84~3\xfda\u0653\x87\xee\x14b\x86\x94\u047f\xd5%\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xee\xe9\xd0Rn\xda\x01\xe41\x16\xa3\x952-\u0689pW\x8f9\x8a\x02\x1e\x19\x99\xbb\xd5\u04be\x00\x00\u07d4\xee\xf1\xbb\xb1\xe5\xa8?\u0782H\xf8\x8e\xe3\x01\x8a\xfa-\x132\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xee\xfb\xa1-\xfc\x99gB\xdby\x04d\xca}';\xe6\xe8\x1b>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xee\xfd\x05\xb0\xe3\xc4\x17\xd5[3C\x06\x04\x86\xcd\xd5\xe9*\xa7\xa6\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xef\r\xc7\xddzS\xd6\x12r\x8b\xcb\u04b2|\x19\xddM}fo\x89&A\x1c[5\xf0Z\x00\x00\u07d4\xef\x11RR\xb1\xb8E\u0345\u007f\x00-c\x0f\x1bo\xa3zNP\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xef\x1c\x04w\xf1\x18M`\xac\u02b3t\xd3tUz\n>\x10\xf3\x89\b=lz\xabc`\x00\x00\u07d4\xef,4\xbbH}7b\xc3\u0327\x82\xcc\xddz\x8f\xbb\n\x991\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xef5\xf6\u0531\a^j\xa19\x15\x1c\x97K/FX\xf7\x058\x89<;\xc3?\x94\xe5\r\x80\x00\u07d4\xef9\u0291s\xdf\x15S\x1ds\xe6\xb7*hKQ\xba\x0f+\xb4\x89V\xa0\xb4un\xe28\x00\x00\u07d4\xefF<&y\xfb'\x91d\xe2\f=&\x915\x87s\xa0\xad\x95\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xefG\xcf\a>6\xf2q\xd5\"\xd7\xfaNq \xadP\a\xa0\xbc\x89\x87\x86x2n\xac\x90\x00\x00\u07d4\xefa\x15[\xa0\t\xdc\u07be\xf1\v(\xd9\xda=\x1b\xc6\xc9\xce\u0509\x034-`\xdf\xf1\x96\x00\x00\u0794\xefix\x1f2\xff\xce34o,\x9a\xe3\xf0\x84\x93\xf3\xe8/\x89\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xefv\xa4\u034f\xeb\xcb\u0278\x18\xf1x(\xf8\xd94s\xf3\xf3\u02c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\uf4c1\x8fhM\xb0\xc3g^\xc8\x132\xb3\x18>\xcc(\xa4\x95\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94\xef\x9fY\xae\xdaA\x8c\x14\x94h-\x94\x1a\xabI$\xb5\xf4\x92\x9a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\uf9b1\xf0\xdb`57\x82h\x91\xb8\xb4\xbc\x169\x84\xbb@\u03495e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xef\xbdR\xf9}\xa5\xfd:g:F\xcb\xf30D{~\x8a\xad\\\x89\x05l<\x9b\x80\xa0\xa6\x80\x00\xe0\x94\xef\xc8\xcf\x19c\u0269Rg\xb2(\xc0\x86#\x98\x89\xf4\xdf\xd4g\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xef\u02ae\x9f\xf6M,\xd9[RI\xdc\xff\xe7\xfa\xa0\xa0\xc0\xe4M\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xef\xcc\xe0k\xd6\b\x9d\x0eE\x8e\xf5a\xf5\xa6\x89H\n\xfep\x00\x89 \x86\xac5\x10R`\x00\x00\u07d4\xef\xe0g]\xa9\x8a]\xdap\u0356\x19k\x87\xf4\xe7&\xb43H\x89?\x19\xbe\xb8\xdd\x1a\xb0\x00\x00\u07d4\xef\xe8\xff\x87\xfc&\x0e\agc\x8d\xd5\xd0/\xc4g.\x0e\xc0m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xef\xeb\x19\x97\xaa\xd2w\xcc3C\x0ea\x11\xed\tCY@H\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xef\xee\xa0\x10uo\x81\xdaK\xa2[r\x17\x87\xf0X\x17\v\uff49\x01\u009c\x9c\xf7p\xef\x00\x00\u07d4\xef\xf5\x1dr\xad\xfa\xe1C\xed\xf3\xa4+\x1a\xecU\xa2\xcc\xdd\v\x90\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xef\xf8kQ#\xbc\xdc\x17\xedL\xe8\xe0[~\x12\xe5\x13\x93\xa1\xf7\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xef\xfc\x15\u41f1\xbe\xda\n\x8d\x13%\xbd\xb4\x17\"@\xdcT\n\x89\x03\x8599\xee\xe1\xde\x00\x00\xe0\x94\xf0\x11\x95\xd6W\xef<\x94.l\xb89I\xe5\xa2\v\\\xfa\x8b\x1e\x8a\x05ts\xd0]\xab\xae\x80\x00\x00\u07d4\xf0'\x96)Q\x01gB\x88\xc1\xd94g\x05=\x04\"\x19\xb7\x94\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\u07d4\xf09h={=\"[\xc7\xd8\u07ed\xefc\x164A\xbeA\xe2\x89\x01\xdd\x1eK\xd8\xd1\xee\x00\x00\u07d4\xf0Jj7\x97\b\xb9B\x8dr*\xa2\xb0kw\xe8\x895\u03c9\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xf0M,\x91\xef\xb6\xe9\xc4_\xfb\xe7KCL\x8c_+\x02\x8f\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf0W\xaaf\xcav~\xde\x12J\x1c[\x9c\xc5\xfc\x94\xef\v\x017\x89p\xa2K\u02b6\xf4]\x00\x00\u07d4\xf0[\xa8\u05f6\x859\xd930\v\xc9(\x9c=\x94t\xd0A\x9e\x89\x06\xda'\x02M\xd9`\x00\x00\u07d4\xf0\\\xee\xabeA\x05dp\x99Qw<\x84E\xad\x9fN\u01d7\x89\x10C\x16'\xa0\x93;\x00\x00\xe0\x94\xf0_\xcdL\rs\xaa\x16~US\xc8\xc0\xd6\xd4\xf2\xfa\xa3\x97W\x8a\x02\xd2\xd6l1p\xb2\x98\x00\x00\u07d4\xf0g\xe1\xf1\u0583UjL\xc4\xfd\f\x03\x13#\x9f2\xc4\xcf\u060965\u026d\xc5\u07a0\x00\x00\u07d4\xf0g\xfb\x10\u07f2\x93\u962b\xe5d\xc0U\xe34\x8f\x9f\xbf\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0h\xdf\xe9]\x15\xcd:\u007f\x98\xff\xa6\x88\xb44hB\xbe&\x90\x89D\n\xd8\x19\xe0\x97L\x00\x00\xe0\x94\xf0j\x85J<]\xc3m\x1cI\xf4\xc8}m\xb33\xb5~J\u074a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf0y\xe1\xb1&_P\xe8\u0229\x8e\xc0\u01c1^\xb3\xae\xac\x9e\xb4\x89\x01\x16\xdc:\x89\x94\xb3\x00\x00\xe0\x94\xf0{\xd0\xe5\xc2\xcei\xc7\u0127$\xbd&\xbb\xfa\x9d*\x17\xca\x03\x8a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\xf0\x83*k\xb2U\x03\xee\xcaC[\xe3\x1b\v\xf9\x05\xca\x1f\xcfW\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf0\x9b>\x87\xf9\x13\xdd\xfdW\xae\x80I\xc71\u06e9\xb66\xdf\u00c9 \xf5\xb1\uab4d\x80\x00\x00\u07d4\xf0\xb14\v\x99oo\v\xf0\xd9V\x1c\x84\x9c\xaf\u007fD0\xbe\xfa\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf0\xb1\xf9\xe2x2\xc6\xdei\x14\xd7\n\xfc#\x8ct\x99\x95\xac\xe4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf0\xb4i\xea\xe8\x9d@\f\xe7\xd5\xd6j\x96\x95\x03p6\xb8\x89\x03\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf0\xb9\u0583\u03a1+\xa6\x00\xba\xac\xe2\x19\xb0\xb3\xc9~\x8c\x00\xe4\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf0\xbe\x0f\xafMy#\xfcDF\"\u0458\f\xf2\u0650\xaa\xb3\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0\xc0\x81\xdaR\xa9\xae6d*\xdf^\b _\x05\xc5Ah\xa6\x89\x06\x04o7\xe5\x94\\\x00\x00\u07d4\xf0\xc7\r\rm\xabvc\xaa\x9e\xd9\xce\xeaV~\xe2\u01b0'e\x89qC\x8a\u0167\x91\xa0\x80\x00\u07d4\xf0\xcb\xef\x84\xe1ic\x00\x98\xd4\xe3\x01\xb2\x02\b\xef\x05\x84j\u0249\x0e\v\x83EPkN\x00\x00\u07d4\xf0\xd2\x16c\u0630\x17n\x05\xfd\xe1\xb9\x0e\xf3\x1f\x850\xfd\xa9_\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xf0\xd5\xc3\x1c\xcbl\xbe0\xc7\xc9\xea\x19\xf2h\xd1Y\x85\x1f\x8c\x9c\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\xf0\xd6L\xf9\xdf\tt\x113\xd1pH_\xd2K\x00P\x11\xd5 \x89\x1b\b\x93A\xe1O\xcc\x00\x00\u07d4\xf0\xd8X\x10^\x1bd\x81\x01\xac?\x85\xa0\xf8\"+\xf4\xf8\x1dj\x89 \x86\xac5\x10R`\x00\x00\u07d4\xf0\xdcC\xf2\x05a\x91'P{+\x1c\x1c\xfd\xf3-(1\t \x89\x10^\xb7\x9b\x94\x17\b\x80\x00\u07d4\xf0\xe1\u07e4*\u07ac/\x17\xf6\xfd\xf5\x84\xc9Hb\xfdV3\x93\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf0\xe2d\x9c~j?,]\xfe3\xbb\xfb\xd9'\xca<5\nX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0\xe7\xfb\x9eB\nS@\xd56\xf4\x04\b4O\xea\xef\xc0j\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf1\x04b\xe5\x8f\xcc\a\U000d5121\x87c\x94Q\x16~\x85\x92\x01\x89\t4\xdd]3\xbc\x97\x00\x00\xe0\x94\xf1\x06a\xff\x94\x14\x0f >zH%rCy8\xbe\xc9\xc3\xf7\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xf1\x14\xff\r\x0f$\xef\xf8\x96\xed\xdeTq\u07a4\x84\x82J\x99\xb3\x88\xbe -j\x0e\xda\x00\x00\u07d4\xf1\x16\xb0\xb4h\x0fS\xabr\xc9h\xba\x80.\x10\xaa\x1b\xe1\x1d\u0209\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xf1\x1c\xf5\xd3cto\xeehd\xd3\xca3m\xd8\x06y\xbb\x87\xae\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xf1\x1e\x01\u01e9\xd1$\x99\x00_M\xaew\x16\tZ4\x17bw\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf1;\b0\x93\xbaVN-\xc61V\x8c\xf7T\r\x9a\x0e\xc7\x19\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf1O\x0e\xb8m\xb0\xebhu?\x16\x91\x8e]K\x80t7\xbd>\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf1Qx\xff\xc4:\xa8\a\x0e\xce2~\x93\x0f\x80\x9a\xb1\xa5O\x9d\x89\n\xb6@9\x12\x010\x00\x00\u07d4\xf1V\xdc\v*\x98\x1e[U\xd3\xf2\xf0;\x814\xe31\u06ed\xb7\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf1]\x9dZ!\xb1\x92\x9ey\x03q\xa1\u007f\x16\xd9_\fie\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1^\x18,O\xbb\xady\xbd\x934\"B\xd4\xdc\xcf+\xe5\x89%\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf1bM\x98\ve3o\xea\u0166\xd5A%\x00\\\xfc\xf2\xaa\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1g\xf5\x86\x8d\xcfB3\xa7\x83\x06\th,\xaf-\xf4\xb1\xb8\a\x89\x81\xe5B\xe1\xa78?\x00\x00\u07d4\xf1m\xe1\x89\x1d\x81\x96F\x13\x95\xf9\xb16&[;\x95F\xf6\xef\x89\x01\xb2\x8e\x1f\x98\xbb\u0380\x00\u07d4\xf1z\x92\xe06\x1d\xba\xce\xcd\xc5\xde\r\x18\x94\x95Z\xf6\xa9\xb6\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1z\xdbt\x0fE\u02fd\xe3\tN~\x13qo\x81\x03\xf5c\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1\x8b\x14\xcb\xf6iC6\xd0\xfe\x12\xac\x1f%\xdf-\xa0\xc0]\xbb\x89\xd8\xd4`,&\xbfl\x00\x00\u07d4\xf1\x9b98\x9dG\xb1\x1b\x8a,?\x1d\xa9\x12M\xec\xff\xbe\xfa\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1\x9f\x195\b9>M*\x12{ \xb2\x03\x1f9\xc8%\x81\u0189\xbd\xbdz\x83\xbd/l\x00\x00\u07d4\xf1\xa1\xf3 @yd\xfd<\x8f.,\u0224X\r\xa9O\x01\xea\x89ll!wU|D\x00\x00\u07d4\xf1\xb4\xec\xc65%\xf7C,=\x83O\xfe+\x97\x0f\xbe\xb8r\x12\x89\xa2\xa2@h\xfa\u0340\x00\x00\u07d4\U000753ef\xfa\x87\x94\xf5\n\xf8\xe8\x83\t\u01e6&TU\xd5\x1a\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\xf1\xc8\u0129A\xb4b\x8c\rl0\xfd\xa5dR\u065c~\x1bd\x89N\x8c\xea\x1e\xdeu\x04\x00\x00\u07d4\xf1\xda@so\x99\xd5\xdf;\x06\x8a]t_\xaf\xc6F?\u0271\x89\x06\x96\xca#\x05\x8d\xa1\x00\x00\u07d4\xf1\u070a\xc8\x10B\xc6z\x9c\\c2!\xa8\xf76>e\x87\f\x9f(t$\u04a9`\x89J\xcfX\xe0rW\x10\x00\x00\u07d4\xf2B\u0684]B\u053fw\x9a\x00\xf2\x95\xb4\aP\xfeI\xea\x13\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf2RY\xa5\xc99\xcd%\x96l\x9bc\x03\xd3s\x1cS\u077cL\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf2^Lp\xbcFV2\u021eV%\xa82\xa7r/k\xff\xab\x89\xf3K\x82\xfd\x8e\x91 \x00\x00\u07d4\xf2k\xce\xdc\xe3\xfe\xad\u03a3\xbc>\x96\xeb\x10@\xdf\xd8\xff\u1809*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xf2py%v\xf0]QD\x93\xff\xd1\xf5\xe8K\xecK-\xf8\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf2s,\xf2\xc1;\x8b\xb8\xe7I*\x98\x8f_\x89\xe3\x82s\xdd\u0209 \x86\xac5\x10R`\x00\x00\xe0\x94\xf2t.hY\xc5i\xd5\xf2\x10\x83Q\xe0\xbfM\xca5*H\xa8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf2\x81:d\xc5&]\x02\x025\u02dc1\x9bl\x96\xf9\x06\xc4\x1e\x89\x12\xf99\u025e\u06b8\x00\x00\u07d4\xf2\x87\xffR\xf4a\x11z\xdb>\x1d\xaaq\x93-\x14\x93\xc6_.\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\xf2\xab\x11au\x02D\xd0\xec\xd0H\xee\r>Q\xab\xb1C\xa2\xfd\x89B\xfe+\x90ss\xbc\x00\x00\u07d4\xf2\xb4\xab,\x94'\xa9\x01^\xf6\xee\xff\xf5\xed\xb6\x019\xb7\x19\u0449&\u06d9*;\x18\x00\x00\x00\u07d4\xf2\xc0>*8\x99\x8c!d\x87`\xf1\xe5\xae~\xa3\a}\x85\"\x89\x8f?q\x93\xab\a\x9c\x00\x00\u0794\xf2\u0090N\x9f\xa6d\xa1\x1e\xe2VV\xd8\xfd,\xc0\u0665\"\xa0\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xf2\xc3b\xb0\xef\x99\x1b\xc8/\xb3nf\xffu\x93*\xe8\u0742%\x89\x04\x02\xf4\xcf\xeeb\xe8\x00\x00\u07d4\xf2\xd0\xe9\x86\xd8\x14\xea\x13\xc8\xf4f\xa0S\x8cS\u0712&Q\xf0\x89J\xcfX\xe0rW\x10\x00\x00\xe0\x94\xf2\u04775w$\xecL\x03\x18[\x87\x9bc\xf5~&X\x91S\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf2\xd5v<\xe0s\x12~,\xed\xdeo\xab\xa7\x86\xc7<\xa9AA\x8a\x01\xacB\x86\x10\x01\x91\xf0\x00\x00\xe0\x94\xf2\u055c\x89#u\x90s\xd6\xf4\x15\xaa\xf8\xeb\x06_\xf2\U000f614a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4\xf2\xe9\x9f\\\xbb\x83kz\xd3bGW\x1a0,\xbeKH\x1ci\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf2\xed>w%J\u02c3#\x1d\xc0\x86\x0e\x1a\x11$+\xa6'\u06c9kV\x05\x15\x82\xa9p\x00\x00\xe0\x94\xf2\xed\xde7\xf9\xa8\u00dd\u07a2My\xf4\x01WW\xd0k\xf7\x86\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xf2\xef\xe9e`\xc9\xd9{r\xbd6DxC\x88\\\x1d\x90\xc21\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf2\xfb\xb6\u0607\xf8\xb8\xcc:\x86\x9a\xba\x84\u007f=\x1fd\xfcc\x97\xaae\xfbS\xa8\xf0z\x0f\x89:\xae0\xe8\xbc\xee\x89|\xf28\x1fa\x9f\x15\x00\x00\u07d4\xf3@\x83\xec\xea8P\x17\xaa@\xbd\xd3^\xf7\xef\xfbL\xe7v-\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf3F\xd7\u0792t\x1c\b\xfcX\xa6M\xb5[\x06-\xde\x01-\x14\x89\x0f\xffk\x1fv\x1em\x00\x00\xe0\x94\xf3U\xd3\xec\f\xfb\x90}\x8d\xbb\x1b\xf3FNE\x81(\x19\v\xac\x8a\x01\v\x04n\u007f\r\x80\x10\x00\x00\u07d4\xf3m\xf0/\xbd\x89`sG\xaf\xce)i\xb9\xc4#jX\xa5\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3s\xe9\u06ac\f\x86u\xf5;yz\x16\x0fo\xc04\xaek#\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf3{BeG\xa1d-\x8032H\x14\xf0\xed\xe3\x11O\xc2\x12\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xf3{\xf7\x8cXu\x15G\x11\xcbd\r7\xeam(\xcf\xcb\x12Y\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf3\x82\xdfX1U\xd8T\x8f?\x93D\f\xd5\xf6\x8c\xb7\x9d`&\x8a8u}\x02\u007f\xc1\xfd\\\x00\x00\xe0\x94\xf3\x82\xe4\xc2\x04\x10\xb9Q\b\x9e\x19\xba\x96\xa2\xfe\xe3\xd9\x1c\xce~\x8a\x01\x11\xfaV\xee\u00a88\x00\x00\xe0\x94\xf3\x8al\xa8\x01hS~\x97M\x14\xe1\xc3\xd19\x90\xa4L,\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xf3\x9a\x9dz\xa3X\x1d\xf0~\xe4'\x9a\xe6\xc3\x12\xef!\x036X\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf3\xb6h\xb3\xf1M\x92\x0e\xbc7\x90\x92\u06d8\x03\x1bg\xb2\x19\xb3\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\U000fe679\x10<\xe7U\n\xa7O\xf1\xdb\x18\xe0\x9d\xfe2\xe0\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xc1\xab\u049d\xc5{A\xdc\x19-\x0e8M\x02\x1d\xf0\xb4\xf6\u0509\x97\xae\f\u07cf\x86\xf8\x00\x00\u07d4\xf3\xc4qm\x1e\xe5'\x9a\x86\xd0\x16:\x14a\x81\x81\xe1a6\u01c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf3\u030b\xcbU\x94e\xf8\x1b\xfeX;\u05eb\n#\x06E;\x9e\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf3\u0588\xf0k\xbd\xbfP\xf9\x93,AE\xcb\xe4\x8e\xcd\xf6\x89\x04\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf3\xdb\xcf\x13Z\u02dd\xee\x1aH\x9cY<\x02O\x03\u00bb\xae\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xde_&\xefj\xde\xd6\xf0m;\x91\x13F\xeep@\x1d\xa4\xa0\x89\x13:\xb3}\x9f\x9d\x03\x00\x00\u07d4\xf3\xdfc\xa9q\x99\x93308;>\xd7W\v\x96\u0101#4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xe7OG\f}:?\x003x\x0fv\xa8\x9f>\xf6\x91\xe6\u02c9\xa3\xcf\xe61\xd1Cd\x00\x00\u07d4\xf3\xeb\x19H\xb9Q\xe2-\xf1ax)\xbf;\x8d\x86\x80\xeckh\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf3\xf1\xfa9\x18\xca4\xe2\xcf~\x84g\v\x1fM\x8e\xca\x16\r\xb3\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\xf3\xf2O\u009e @?\xc0\xe8\xf5\xeb\xbbU4&\xf7\x82p\xa2\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf3\xfar5R\xa5\xd0Q.+b\xf4\x8d\xca{+\x81\x050[\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xf3\xfeQ\xfd\xe3D\x13\xc73\x18\xb9\xc8T7\xfe~\x82\x0fV\x1a\x896b2\\\u044f\xe0\x00\x00\u07d4\xf4\x00\xf9=_\\~?\xc3\x03\x12\x9a\xc8\xfb\f/xd\a\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\v\x13O\xea\"\u01b2\x9c\x84W\xf4\x9f\x00\x0f\x9c\xdax\x9a\u06c9 \x86\xac5\x10R`\x00\x00\u07d4\xf4\x15W\xdf\u07f1\xa1\xbd\xce\xfe\xfe.\xba\x1e!\xfe\nJ\x99B\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\x17z\r\x85\u050b\x0e&B\x11\xce*\xa2\xef\xd3\xf1\xb4\u007f\b\x89\xc2\xcc\xca&\xb7\xe8\x0e\x80\x00\u07d4\xf4/\x90R1\xc7p\xf0\xa4\x06\xf2\xb7h\x87\u007f\xb4\x9e\xee\x0f!\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xf42\xb9\u06ef\x11\xbd\xbds\xb6Q\x9f\xc0\xa9\x04\x19\x87q\xaa\u0189\b=lz\xabc`\x00\x00\u07d4\xf4=\xa3\xa4\xe3\xf5\xfa\xb1\x04\u029b\xc1\xa0\xf7\xf3\xbbJV\xf3Q\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xf4G\x10\x8b\x98\xdfd\xb5~\x87\x103\x88\\\x1a\xd7\x1d\xb1\xa3\xf9\x8a\x01v\xf4\x9e\xad4\x83P\x80\x00\u07d4\xf4O\x85Q\xac\xe93r\a\x12\xc5\u0111\u0376\xf2\xf9Qsl\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\xf4V\x05Z\x11\xab\x91\xfff\x8e.\xc9\"\x96\x1f*#\xe3\xdb%\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xf4V\xa7[\xb9\x96U\xa7A,\xe9}\xa0\x81\x81m\xfd\xb2\xb1\xf2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf4[\x1d\xcb.A\xdc'\xff\xa0$\u06ad\xf6\x19\xc1\x11u\xc0\x87\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xf4c\xa9\f\xb3\xf1>\x1f\x06CB66\xbe\xab\x84\xc1#\xb0m\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf4h\x90n~\xdffJ\xb0\u063e=\x83\xebz\xb3\xf7\xff\xdcx\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xf4i\x80\u3929\u049ajn\x90`E7\xa3\x11K\xcb(\x97\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf4kk\x9c|\xb5R\x82\x9c\x1d=\xfd\x8f\xfb\x11\xaa\xba\xe7\x82\xf6\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4\xf4v\xe1&\u007f\x86$|\xc9\b\x81o.z\xd58\x8c\x95-\xb0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf4v\xf2\xcbr\b\xa3.\x05\x1f\xd9N\xa8f)\x92c\x82\x87\xa2\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xf4{\xb14\xda0\xa8\x12\xd0\x03\xaf\x8d\u0338\x88\xf4K\xbfW$\x8a\x01\x19Y\xb7\xfe3\x95X\x00\x00\u07d4\xf4\x83\xf6\a\xa2\x1f\xcc(\x10\n\x01\x8cV\x8f\xfb\xe1@8\x04\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf4\x8e\x1f\x13\xf6\xafM\x84\xb3q\xd7\xdeK'=\x03\xa2c'\x8e\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xf4\x9cG\xb3\xef\xd8knj[\xc9A\x8d\x1f\x9f\xec\x81Ki\xef\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xf4\x9fo\x9b\xaa\xbc\x01\x8c\x8f\x8e\x11\x9e\x01\x15\xf4\x91\xfc\x92\xa8\xa4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf4\xa3g\xb1f\u0499\x1a+\xfd\xa9\xf5dc\xa0\x9f%,\x1b\x1d\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\xa5\x1f\xceJ\x1d[\x94\xb0q\x83\x89\xbaNx\x14\x13\x9c\xa78\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xf4\xa9\xd0\f\xef\xa9{zX\xef\x94\x17\xfcbg\xa5\x06\x909\xee\x89\x01.\x89(\u007f\xa7\x84\x00\x00\u07d4\xf4\xaa\xa3\xa6\x16>7\x06W{I\xc0v~\x94\x8ah\x1e\x16\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\xb1bn$\xf3\v\xca\xd9'!\xb2\x93r\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xf5U\xa2{\xb1\xe2\xfdN,\u01c4\xca\ue493\x9f\xc0n/\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\xf5X\xa2\xb2\xdd&\u0755\x93\xaa\xe0E1\xfd<<\u00c5Kg\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\xf5`H\xdd!\x81\u0523od\xfc\xec\xc6!T\x81\xe4*\xbc\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf5dB\xf6\x0e!i\x13\x95\u043f\xfa\xa9\x19M\xca\xff\x12\u2dc9\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xf5yqJE\xeb\x8fR\xc3\xd5{\xbd\xef\xd2\xc1[./\x11\u07c9T\x91YV\xc4\t`\x00\x00\u07d4\xf5\x93\xc6R\x85\xeek\xbdf7\U000fe3c9\xad@\u0509\xf6U\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf5\x98\xdb.\t\xa8\xa5\xee}r\r+\\C\xbb\x12m\x11\xec\u0089\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf5\x9d\xab\x1b\xf8\xdf\x112~a\xf9\xb7\xa1KV:\x96\xec5T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf5\x9f\x9f\x02\xbb\u024e\xfe\t~\xab\xb7\x82\x10\x97\x90!\x89\x8b\xfd\x8a\x02\x1e\x17\x1a>\xc9\xf7,\x00\x00\u07d4\xf5\xa5E\x9f\xcd\xd5\xe5\xb2s\x83\r\xf8\x8e\xeaL\xb7}\xda\u07f9\x89\x04\t\xe5+H6\x9a\x00\x00\u07d4\xf5\xa7gj\xd1H\xae\x9c\x1e\xf8\xb6\xf5\xe5\xa0\xc2\xc4s\xbe\x85\v\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf5\xb0h\x98\x9d\xf2\x9c%5w\xd0@Z\xden\x0eu(\xf8\x9e\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\xf5\xb6\xe9\x06\x1aN\xb0\x96\x16\aw\xe2gb\xcfH\xbd\u0635]\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xf5\xcf\xfb\xbabN~\xb3!\xbc\x83\xc6\f\xa6\x81\x99\xb4\xe3fq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf5\xd1ER\xb1\xdc\xe0\xd6\xdc\x1f2\r\xa6\xff\u02231\xcdo\f\x89Hz\x9a0E9D\x00\x00\xe0\x94\xf5\xd6\x1a\xc4\u0295G^[{\xff\xd5\xf2\xf6\x90\xb3\x16u\x96\x15\x8a\x06\x92\xae\x88\x97\b\x1d\x00\x00\x00\u07d4\xf5\xd9\xcf\x00\xd6X\xddEQzH\xa9\xd3\xf5\xf63T\x1aS=\x89\x06O_\xdfIOx\x00\x00\u07d4\xf5\xea\xdc\xd2\u0478ez\x12\x1f3\xc4X\xa8\xb1>v\xb6U&\x89\r\x8b\x0fZZ\xc2J\x00\x00\u07d4\xf6\a\xc2\x15\r>\x1b\x99\xf2O\xa1\xc7\xd5@\xad\xd3\\N\xbe\x1e\x89\xa7\xf1\xaa\a\xfc\x8f\xaa\x00\x00\u07d4\xf6\v\xd75T>k\xfd.\xa6\xf1\x1b\xffbs@\xbc\x03Z#\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf6\f\x1bE\xf1d\xb9X\x0e 'Z\\9\xe1\xd7\x1e5\xf8\x91\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf6\x0fb\xd797\x95?\xef5\x16\x9e\x11\xd8r\xd2\xea1~\xec\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xf6\x12\x83\xb4\xbd\x85\x04\x05\x8c\xa3`\u94d9\x9bb\xcb\xc8\xcdg\x89\r\xd2\xd5\xfc\xf3\xbc\x9c\x00\x00\u07d4\xf6\x17\xb9g\xb9\xbdH_v\x95\xd2\xefQ\xfbw\x92\u0618\xf5\x00\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf6\x18\u0671\x04A\x14\x80\xa8c\xe6#\xfcU#-\x1aOH\xaa\x89\x0eh\x9emD\xb1f\x80\x00\u07d4\xf6\"\u5126b>\xaa\xf9\x9f+\xe4\x9eS\x80\xc5\xcb\xcf\\\u0609\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf62\xad\xffI\r\xa4\xb7-\x126\xd0KQ\x0ft\xd2\xfa\xa3\u0349K\xe4\xe7&{j\xe0\x00\x00\u07d4\xf69\xac1\u069fg'\x1b\xd1\x04\x02\xb7eN\\\xe7c\xbdG\x89\x15\xaf\x0fB\xba\xf9&\x00\x00\u07d4\xf6:W\x9b\xc3\xea\u00a9I\x04\x10\x12\x8d\xbc\xeb\xe6\xd9\u0782C\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\xf6E\xdd|\x89\x00\x93\xe8\xe4\u022a\x92\xa6\xbb55\"\xd3\u0718\x89\aC\x9f\xa2\t\x9eX\x00\x00\xe0\x94\xf6H\xea\x89\xc2u%q\x01r\x94Ny\xed\xff\x84x\x03\xb7u\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xf6JJ\xc8\xd5@\xa9(\x9ch\xd9`\xd5\xfb|\xc4Zw\x83\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf6N\xcf!\x17\x93\x1cmSZ1\x1eO\xfe\xae\xf9\u0514\x05\xb8\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xf6O\xe0\x93\x9a\x8d\x1e\xea*\x0e\u035a\x970\xfdyX\xe31\t\x89\x01\x1d\xe1\xe6\xdbE\f\x00\x00\u07d4\xf6V\x16\xbe\x9c\x8by~t\x15\"|\x918\xfa\xa0\x89\x17B\u05c9*\xd3s\xcef\x8e\x98\x00\x00\u07d4\xf6W\xfc\xbeh.\xb4\xe8\xdb\x15.\u03c9$V\x00\vQ=\x15\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf6X\x19\xacL\xc1L\x13\u007f\x05\xddyw\xc7\xda\xe0\x8d\x1aJ\xb5\x89\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xf6{\xb8\xe2\x11\x8b\xbc\u0550'fn\xed\xf6\x94>\xc9\xf8\x80\xa5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf6\x84d\xbfd\xf2A\x13V\xe4\xd3%\x0e\xfe\xfe\\P\xa5\xf6[\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf6\x86x[\x89r\va\x14_\ua017\x8dj\u030e\v\xc1\x96\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf6\x8c^3\xfa\x97\x13\x9d\xf5\xb2\xe68\x86\xce4\xeb\xf3\u45dc\x89\xb3\xfaAi\xe2\xd8\xe0\x00\x00\u07d4\xf6\xa8cWW\xc5\xe8\xc14\xd2\r\x02\x8c\xf7x\u03c6\t\xe4j\x89O\x1dw/\xae\xc1|\x00\x00\u07d4\xf6\xb7\x82\xf4\xdc\xd7E\xa6\xc0\xe2\xe00`\x0e\x04\xa2K%\xe5B\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xf6\xbc7\xb1\u04a3x\x8dX\x9bm\xe2\x12\xdc\x17\x13\xb2\xf6\u738a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xf6\xc3\u010a\x1a\xc0\xa3G\x99\xf0M\xb8n\u01e9u\xfewh\xf3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf6\xd2]?=\x84m#\x9fR_\xa8\xca\xc9{\xc45x\u06ec\x890\x92\u007ft\xc9\xde\x00\x00\x00\u07d4\xf6\xea\xacp2\u0512\xef\x17\xfd`\x95\xaf\xc1\x1dcOV\xb3\x82\x89\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\xf6\xea\xd6}\xbf[~\xb13X\xe1\x0f6\x18\x9dS\xe6C\xcf\u03ca\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xf6\xf1\xa4C\t\x05\x1ck%\xe4}\xff\x90\x9b\x17\x9b\xb9\xabY\x1c\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf7\x03(\xef\x97b_\xe7E\xfa\xa4\x9e\xe0\xf9\u052a;\r\xfbi\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf7\n\x99\x8aq{3\x8d\x1d\u0658T@\x9b\x1a3\x8d\ue930\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7\rcz\x84\\\x06\xdbl\u0711\xe67\x1c\xe7\xc48\x8ab\x8e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\x15R\x13D\x98\x92tK\xc6\x0f.\x04@\a\x88\xbd\x04\x1f\u0749\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\xe0\x94\xf7\x1bE4\xf2\x86\xe40\x93\xb1\xe1^\xfe\xa7I\xe7Y{\x8bW\x8a\x16\x1c\x13\xd34\x1c\x87(\x00\x00\u07d4\xf74\xec\x03rM\xde\xe5\xbbRy\xaa\x1a\xfc\xf6\x1b\f\xb4H\xa1\x89\xe5\xbf,\u0270\x97\x80\x00\x00\u07d4\xf76\u0716v\x00\x128\x8f\xe8\x8bf\xc0n\xfeW\xe0\xd7\xcf\n\x89q\xd7Z\xb9\xb9 P\x00\x00\u07d4\xf7:\xc4l ;\xe1S\x81\x11\xb1Q\xec\x82 \u01c6\xd8AD\x89\x0f\xf77x\x17\xb8+\x80\x00\u07d4\xf7=\xd9\xc1B\xb7\x1b\xce\x11\xd0n0\xe7\xe7\xd02\xf2\uc71e\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf7A\x8a\xa0\xe7\x13\xd2H\"\x87v\xb2\xe7CB\"\xaeu\u3949lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7Nn\x14S\x82\xb4\u06c2\x1f\xe0\xf2\u0643\x88\xf4V\t\u019f\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf7P\f\x16o\x8b\xea/\x824v\x06\xe5\x02K\xe9\xe4\xf4\u0399\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7W\xfc\x87 \xd3\xc4\xfaRw\a^`\xbd\\A\x1a\xeb\xd9w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7[\xb3\x9cy\x97y\xeb\xc0J3m&\r\xa61F\xed\x98\u0409\x01Z\xf1\u05cbX\xc4\x00\x00\xe0\x94\xf7h\xf3!\xfdd3\xd9kO5M<\xc1e,\x172\xf5\u007f\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf7oi\xce\xe4\xfa\xa0\xa6;0\xae\x1ex\x81\xf4\xf7\x15ep\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf7w6\x1a=\u062bb\xe5\xf1\xb9\xb0GV\x8c\xc0\xb5UpL\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf7|{\x84QI\xef\xba\x19\xe2a\xbc|u\x15y\b\xaf\xa9\x90\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7\u007f\x95\x87\xffz-r\x95\xf1\xf5q\u0206\xbd3\x92jR|\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xf7\x82X\xc1$\x81\xbc\xdd\u06f7*\x8c\xa0\xc0C\tra\xc6\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\x98\xd1m\xa4\xe4`\xc4`\xcdH_\xae\x0f\xa0Y\x97\b\ub08965\u026d\xc5\u07a0\x00\x00\u07d4\xf7\xa1\xad\xe2\xd0\xf5)\x12=\x10U\xf1\x9b\x17\x91\x9fV!Ng\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf7\xac\xff\x93K\x84\xda\ti\xdc7\xa8\xfc\xf6C\xb7\xd7\xfb\xedA\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf7\xb1Q\xcc^W\x1c\x17\xc7e9\xdb\xe9\x96L\xbbo\xe5\xdey\x89tq|\xfbh\x83\x10\x00\x00\u07d4\xf7\xb2\x9b\x82\x19\\\x88-\xabx\x97\u00ae\x95\xe7w\x10\xf5xu\x89w5Aa2\xdb\xfc\x00\x00\u07d4\xf7\xbcLD\x91\rZ\xed\xd6n\xd25U8\xa6\xb1\x93\xc3a\xec\x89\x05A\xde,-\x8db\x00\x00\u07d4\xf7\xc0\f\xdb\x1f\x02\x03\x10\u056c\xab{Ij\xaaD\xb7y\b^\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xf7\xc1\xb4C\x96\x8b\x11{]\u0677UW/\xcd9\xca^\xc0K\x89\x18\xb9h\u0092\xf1\xb5\x00\x00\xe0\x94\xf7\xc5\x0f\x92*\xd1ka\xc6\u047a\xa0E\xed\x81h\x15\xba\u010f\x8a\x02\xa99j\x97\x84\xad}\x00\x00\u07d4\xf7\xc7\b\x01Pq\xd4\xfb\n:*\t\xa4]\x15c\x96\xe34\x9e\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf7\xcb\u06e6\xbel\xfeh\xdb\xc2<+\x0f\xf50\xee\x05\"o\x84\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\xd0\xd3\x10\xac\xea\x18@a8\xba\xaa\xbb\xfe\x05q\xe8\r\xe8_\x89Hz\x9a0E9D\x00\x00\u07d4\xf7\u05ef LV\xf3\x1f\xd9C\x98\xe4\r\xf1\x96K\u063f\x12<\x89\b!\xd2!\xb5)\x1f\x80\x00\u07d4\xf7\xdc%\x11\x96\xfb\u02f7|\x94}|\x19F\xb0\xffe\x02\x1c\xea\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf7\xe4Z\x12\xaaq\x1cp\x9a\xce\xfe\x95\xf3;xa-*\xd2*\x8a\x0e\x06U\xe2\xf2k\xc9\x18\x00\x00\u07d4\xf7\xf4\x89\x8cLRm\x95_!\xf0U\xcbnG\xb9\x15\xe5\x19d\x89|\b`\xe5\xa8\r\xc0\x00\x00\u07d4\xf7\xf9\x1ez\xcb[\x81)\xa3\x06\x87|\xe3\x16\x8eoC\x8bf\xa1\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4\xf7\xfcE\xab\xf7oP\x88\xe2\u5d68\xd12\xf2\x8aMN\xc1\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\x06:\xf4\xcc\x1d\xd9a\x9a\xb5\u063f\xf3\xfc\xd1\xfa\xa8H\x82!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\bnBf\x1e\xa9)\xd2\u0761\xablt\x8c\xe3\x05]\x11\x1e\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf8\bw\x86\xb4-\xa0N\xd6\xd1\xe0\xfe&\xf6\xc0\xee\xfe\x1e\x9fZ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf8\r6\x19p/\xa5\x83\x8cH9\x18Y\xa89\xfb\x9c\xe7\x16\x0f\x89l\a\xa7\u0471np\x00\x00\u07d4\xf8\x14y\x9fm\xdfM\xcb)\xc7\xee\x87\x0eu\xf9\xcc-52m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf8\x15\xc1\n\x03-\x13\xc3K\x89v\xfan;\xd2\xc9\x13\x1a\x8b\xa9\x89Hz\x9a0E9D\x00\x00\u07d4\xf8\x16\"\xe5WW\xda\xeafu\x97]\xd958\xda}\x16\x99\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8$\xee3\x1eJ\xc3\xccXv\x939[W\xec\xf6%\xa6\xc0\u0089V\xc9]\xe8\xe8\xca\x1d\x00\x00\u07d4\xf8'\xd5n\xd2\xd3' \u052b\xf1\x03\xd6\xd0\xefM;\xcdU\x9b\x89\x01l\x80\x06W\x91\xa2\x80\x00\u07d4\xf8)\x85\x91R>P\xb1\x03\xf0\xb7\x01\xd6#\xcb\xf0\xf7EV\xf6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf8H\xfc\xe9\xaba\x1c}\x99 n#\xfa\u019a\u0508\xb9O\xe1\x89\x02\xa1\x12\x9d\t6r\x00\x00\u07d4\xf8O\t\n\xdf?\x8d\xb7\u1533P\xfb\xb7u\x00i\x9ff\xfd\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf8Q\xb0\x10\xf63\xc4\n\xf1\xa8\xf0js\ubeabe\az\xb5\x89\xee\x86D/\xcd\x06\xc0\x00\x00\u07d4\xf8X\x17\x1a\x04\xd3W\xa1;IA\xc1n~U\xdd\u0514\x13)\x89\x02F\xa5!\x8f*\x00\x00\x00\u07d4\xf8[\xab\x1c\xb3q\x0f\xc0_\xa1\x9f\xfa\xc2.gR\x1a\v\xa2\x1d\x89l\x955\u007f\xa6\xb3l\x00\x00\u07d4\xf8j>\xa8\a\x1fp\x95\xc7\u06ca\x05\xaePz\x89)\u06f8v\x89\x126\xef\xcb\u02f3@\x00\x00\u07d4\xf8pL\x16\xd2\xfd[\xa3\xa2\xc0\x1d\x0e\xb2\x04\x84\xe6\xec\xfa1\t\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf8p\x99_\xe1\xe5\"2\x1duC7\xa4\\\f\x9d{8\x95\x1c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf8s\xe5ze\xc9;n\x18\xcbu\xf0\xdc\a}[\x893\xdc\\\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xf8ua\x9d\x8a#\xe4]\x89\x98\u0444\u0500\xc0t\x89p\x82*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf8{\xb0{(\x9d\xf70\x1eT\xc0\xef\xdaj,\xf2\x91\xe8\x92\x00\x89K\xe4\xe7&{j\xe0\x00\x00\u0794\xf8\x89\x00\xdbsyU\xb1Q\x9b\x1a}\x17\n\x18\x86L\xe5\x90\xeb\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xf8\x8bX\xdb7B\vFL\v\xe8\x8bE\xee+\x95)\x0f\x8c\xfa\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf8\x96+u\xdb]$\xc7\xe8\xb7\xce\xf1\x06\x8c>g\u03bb0\xa5\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xf8\xa0e\xf2\x87\xd9\x1dw\xcdbj\xf3\x8f\xfa\"\r\x9bU*+\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xf8\xa4\x9c\xa29\f\x1fm\\\x0ebQ;\a\x95qt?|\u0189\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf8\xa5\f\xee.h\x8c\xee\u3b24\u0522\x97%\xd4\a,\u0103\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xacJ9\xb5<\x110x \x97;D\x13e\xcf\xfeYof\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xae\x85{g\xa4\xa2\x89:?\xbe|z\x87\xff\x1c\x01\u01a6\xe7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf8\xbf\x9c\x04\x87NZw\xf3\x8fL8R~\x80\xc6v\xf7\xb8\x87\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xc7\xf3J8\xb3\x18\x01\xdaC\x064w\xb1+'\xd0\xf2\x03\xff\x89\x1a\u04ba\xbao\xefH\x00\x00\u07d4\xf8\xca3l\x8e\x91\xbd \xe3\x14\xc2\v-\xd4`\x8b\x9c\x8b\x94Y\x89-\u071b\u0173,x\x00\x00\u07d4\xf8\xd1t$\xc7g\xbe\xa3\x12\x05s\x9a+W\xa7'r\x14\uef89\x02F\xdd\xf9yvh\x00\x00\u07d4\xf8\xd5-\xcc_\x96\xcc(\x00{>\u02f4\t\xf7\xe2*dl\xaa\x89\b\x16\x90\xe1\x81(H\x00\x00\u07d4\xf8\xdc\xe8g\xf0\xa3\x9c[\xef\x9e\xeb\xa6\t\"\x9e\xfa\x02g\x8bl\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xf2&\x14*B\x844\xab\x17\xa1\x86J%\x97\xf6J\xab/\x06\x89\tY\x8b/\xb2\xe9\xf2\x80\x00\u07d4\xf8\xf6d^\r\xeedK=\xad\x81\xd5q\uf6ef\x84\x00!\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\x01\xc0\x0f\xc1\u06c8\xb6\x9cK\xc3%+\\\xa7\x0e\xa6\xee\\\xf6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9=[\xcb\x06D\xb0\xcc\xe5\xfc\u0763C\xf5\x16\x8f\xfa\xb2\x87}\x89\vb\a\xb6}&\xf9\x00\x00\u07d4\xf9W\x0e\x92L\x95\u07bbpa6\x97\x92\xcf.\xfe\u00a8-^\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf9d \x86\xb1\xfb\xaea\xa6\x80M\xbe_\xb1^\xc2\u04b57\xf4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9d\x88i\x85\x90\xdc;,UVB\xb8q4\x8d\xfa\x06z\u0549\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9d\u064d(\x170\xba5\xb2\xe3\xa3\x14yn{B\xfe\xdfg\x89S\xb0\x87`\x98\xd8\f\x00\x00\u07d4\xf9e\ri\x89\xf1\x99\xab\x1c\xc4ycm\xed0\xf2A\x02\x1fe\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\xe0\x94\xf9h\x83X$Y\x90\x8c\x82v'\xe8o(\xe6F\xf9\xc7\xfcz\x8a\x01\u0127\x877\xcd\u03f8\x00\x00\u07d4\xf9kL\x00voSsj\x85t\xf8\"\xe6GL/!\xda-\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9r\x9dH(,\x9e\x87\x16m^\xef-\x01\xed\xa9\xdb\xf7\x88!\x89\x05k\x83\xdd\xc7(T\x80\x00\u07d4\xf9v~N\xcbJY\x80Ru\b\u05fe\xc3\xd4^Ld\x9c\x13\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xf9x\xb0%\xb6B3U\\\xc3\xc1\x9a\xda\u007fA\x99\xc94\x8b\xf7\x8aT\xb4\v\x1f\x85+\xda\x00\x00\x00\u07d4\xf9{V\xeb\u0577z\xbc\x9f\xba\u02eb\u0514\xb9\xd2\xc2!\xcd\x03\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf9\x81\x1f\xa1\x9d\xad\xbf\x02\x9f\x8b\xfeV\x9a\xdb\x18\"\x8c\x80H\x1a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf9\x82Ps\fLa\xc5\u007f\x12\x985\xf2h\b\x94yEB\xf3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xf9\x894gr\x99^\xc1\x90o\xaf\xfe\xba*\u007f\xe7\u079ck\xab\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf9\x98\xca4\x11s\nl\xd1\x0etU\xb0A\x0f\xb0\xf6\xd3\xff\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\x9a\xeeDKW\x83\xc0\x93\xcf\xff\xd1\xc4c,\xf9\x90\x9f\xbb\x91\x1d/\x81\x92\xf8B\t\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xf9\xbf\xb5\x9dS\x8a\xfcHt\xd4\xf5\x94\x1b\b\xc9s\x0e8\xe2K\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf9\xdd#\x90\b\x18/\xb5\x19\xfb0\xee\xdd \x93\xfe\xd1c\x9b\xe8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9\u07ba\xec\xb5\xf39\xbe\xeaH\x94\xe5 K\xfa4\r\x06\u007f%\x89ZB\x84Fs\xb1d\x00\x00\xe0\x94\xf9\xe3tG@lA!\x97\xb2\u2bbc\x00\x1dn0\u024c`\x8a\x01\xc4y\xbbCI\xc0\xee\x00\x00\u07d4\xf9\xe7\"/\xaa\xf0\xf4\xda@\xc1\u0124\x0607:\t\xbe\u05f6\x89\x9bO\u0730\x94V$\x00\x00\u07d4\xf9\xec\xe0\"\xbc\xcd,\x924i\x11\xe7\x9d\xd5\x03\x03\xc0\x1e\x01\x88\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfa\x00\xc3v\xe8\x9c\x05\u81c1z\x9d\xd0t\x8d\x96\xf3A\xaa\x89\x89\x10M\r\x00\u04b7\xf6\x00\x00\u07d4\xfa\f\x1a\x98\x8c\x8a\x17\xad5(\xeb(\xb3@\x9d\xaaX\"_&\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xfa\x10_\x1a\x11\xb6\xe4\xb1\xf5`\x12\xa2y\"\xe2\xac-\xa4\x81/\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xfa\x14/\xe4~\u0697\xe6P;8k\x18\xa2\xbe\xdds\u0335\xb1\x89.\x15:\xd8\x15H\x10\x00\x00\u07d4\xfa\x14\xb5f#J\xbe\xe70B\xc3\x1d!qq\x82\u02e1J\xa1\x89\x11\xc7\xea\x16.x \x00\x00\u07d4\xfa\x19\xd6\xf7\xa5\x0fO\a\x98\x93\xd1g\xbf\x14\xe2\x1d\x00s\u0456\x89\x1c\xbb:?\xf0\x8d\b\x00\x00\u07d4\xfa\x1f\x19q\xa7u\xc3PO\xefPy\xf6@\xc2\u013c\xe7\xac\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfa'\x9b\xfd\x87g\xf9V\xbf\u007f\xa0\xbdV`\x16\x8d\xa7V\x86\xbd\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xfa'\xccI\xd0\vl\x98s6\xa8u\xae9\xdaX\xfb\x04\x1b.\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xfa(2\x99`=\x87X\xe8\u02b0\x82\x12],\x8f}DT)\x8a\x01[\xca\xcb\x1e\x05\x01\xae\x80\x00\u07d4\xfa+\xbc\xa1]?\u37ca2\x8e\x91\xf9\r\xa1Oz\xc6%=\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xfa/\u049d\x03\xfe\xe9\xa0x\x93\xdf:&\x9fV\xb7/.\x1ed\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xfa3U2\x85\xa9sq\x9a\r_\x95o\xf8a\xb2\u061e\xd3\x04\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfa:\fK\x90?n\xa5.\xa7\xab{\x88c\xb6\xa6\x16\xadfP\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfa:\x1a\xa4H\x8b5\x1a\xa7V\f\xf5\xeec\n/\xd4\\2\"\x89/\xa4~j\xa74\r\x00\x00\u07d4\xfaA\tq\xad\"\x9c06\xf4\x1a\u03c5/*\u0259(\x19P\x89\u0633\x11\xa8\xdd\xfa|\x00\x00\u07d4\xfaD\xa8U\xe4\x04\xc8m\f\xa8\xef3$%\x1d\xfb4\x9cS\x9e\x89T\"S\xa1&\xce@\x00\x00\xe0\x94\xfaR\x01\xfe\x13B\xaf\x110{\x91B\xa0A$<\xa9./\t\x8a 8\x11j:\xc0C\x98\x00\x00\xe0\x94\xfa`\x86\x8a\xaf\xd4\xffL\\W\x91K\x8e\u054bBWs\u07e9\x8a\x01\xcf\xe5\xc8\b\xf3\x9f\xbc\x00\x00\u07d4\xfag\xb6{O7\xa0\x15\t\x15\x11\x0e\xde\a;\x05\xb8S\xbd\xa2\x89#\x19\xba\x94sq\xad\x00\x00\u07d4\xfah\xe0\xcb>\xdfQ\xf0\xa6\xf2\x11\u0272\xcb^\a<\x9b\xff\xe6\x89\x0f\xc969(\x01\xc0\x00\x00\xe0\x94\xfaj7\xf0\x18\xe9yg\x93\u007f\xc5\xe8a{\xa1\u05c6\xdd_w\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\xfav\x06C[5l\xee%{\xd2\xfc\xd3\xd9\xea\xcb<\xd1\xc4\xe1\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xfaz\xdff\v\x8d\x99\xce\x15\x93=|_\a/<\xbe\xb9\x9d3\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\xfa\x86\xca'\xbf(T\u0648p\x83\u007f\xb6\xf6\xdf\xe4\xbfdS\xfc\x89\x11u~\x85%\xcf\x14\x80\x00\u07d4\xfa\x8c\xf4\xe6'i\x8c]W\x88\xab\xb7\x88\x04\x17\xe7P#\x13\x99\x89\xe6\x1a6\x96\xee\xf6\x10\x00\x00\u07d4\xfa\x8e;\x1f\x13C9\x00s}\xaa\xf1\xf6)\x9cH\x87\xf8[_\x89&\u009eG\u0104L\x00\x00\u07d4\xfa\x9e\xc8\xef\xe0\x86\x86\xfaX\xc1\x813Xr\xbai\x85`\ucac9lj\xccg\u05f1\xd4\x00\x00\u07d4\xfa\xad\x90]\x84|{#A\x8a\xee\xcb\xe3\xad\u06cd\xd3\xf8\x92J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xfa\xae\xba\x8f\xc0\xbb\xdaU<\xa7.0\xef=s.&\xe8 A\x89H\x8d(*\xaf\xc9\xf6\x80\x00\u07d4\xfa\xb4\x87P\r\xf2\x0f\xb8>\xbe\xd9\x16y\x1dV\x17r\xad\xbe\xbf\x89lkLM\xa6\u077e\x00\x00\u07d4\xfa\xc5\u0294u\x80x\xfb\xfc\xcd\x19\xdb5X\xda~\u8827h\x897(\xa6+\r\xcf\xf6\x00\x00\u07d4\xfa\xd9j\xb6\xacv\x8a\xd5\t\x94R\xacGw\xbd\x1aG\xed\u010f\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xfa\xe7g\x19\xd9~\xacA\x87\x04(\xe9@'\x9d\x97\xddW\xb2\xf6\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4\xfa\u8053pG\x89Zf\f\xf2)v\x0f'\xe6h(\xd6C\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xfa\xe9,\x13p\xe9\u115a]\xf8;V\xd0\xf5\x86\xaa;@L\x89\x05\u0174\xf3\xd8C\x98\x00\x00\xe0\x94\xfa\xf5\xf0\xb7\xb6\xd5X\xf5\t\r\x9e\xa1\xfb-B%\x9cX`x\x8a\x01Z\xff\xb8B\fkd\x00\x00\xe0\x94\xfb\x12o\x0e\xc7i\xf4\x9d\xce\xfc\xa2\xf2\x00(dQX0\x84\xb8\x8a\x01\x0f\xcb\xc25\x03\x96\xbf\x00\x00\xe0\x94\xfb\x13^\xb1Z\x8b\xacr\xb6\x99\x154*`\xbb\xc0k~\a|\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xfb\"<\x1e\"\xea\xc1&\x9b2\xee\x15jS\x85\x92.\xd3o\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb7\xcfkO\x81\xa9\xe2\"\xfb\xa2.\x9b\xd2KP\x98\xb73\u03c9\x02\x1auJm\xc5(\x00\x00\u07d4\xfb8`\xf4\x12\x1cC.\xbd\xc8\xecj\x031\xb1\xb7\ty.\x90\x89 \x8c9J\xf1\u0208\x00\x00\u07d4\xfb9\x18\x9a\xf8v\xe7b\xc7\x1dl>t\x18\x93\xdf\"l\xed\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfb:\v\rkjq\x8fo\xc0)*\x82]\xc9$z\x90\xa5\u0409\n\xd6\xdd\x19\x9e\x97[\x00\x00\xe0\x94\xfb?\xa1\xac\b\xab\xa9\xcc;\xf0\xfe\x9dH8 h\x8fe\xb4\x10\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xfb?\xe0\x9b\xb86\x86\x15)\xd7Q\x8d\xa2v5\xf58PV\x15\x89K\xe3\x92\x16\xfd\xa0p\x00\x00\xe0\x94\xfbQ%\xbf\x0f^\xb0\xb6\xf0 \xe5k\xfc/\xdf=@,\t~\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\xfbU\x18qL\xef\xc3m\x04\x86]\xe5\x91^\xf0\xffG\xdf\xe7C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb_\xfa\xa0\xf7aW&5x\x91GX\x18\x93\x9d 7\u03d6\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfbh\\\x15\xe49\x96^\xf6&\xbf\r\x83L\u0468\x9f+V\x95\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xfbtK\x95\x1d\tK1\x02b\xc8\xf9\x86\xc8`\u07da\xb1\xdee\x89\x02\xd1\xc5\x15\xf1\xcbJ\x80\x00\u07d4\xfby\xab\u06d2\\U\xb9\xf9\x8e\xfe\xefd\xcf\xc9\xeba\xf5\x1b\xb1\x89a@\xc0V\xfb\n\xc8\x00\x00\u07d4\xfb\x81\x13\xf9M\x91s\xee\xfdZ0s\xf5\x16\x80:\x10\xb2\x86\xae\x89\x04V9\x18$O@\x00\x00\u07d4\xfb\x84,\xa2\xc5\xef\x139\x17\xa26\xa0\u052c@i\x01\x10\xb08\x89\x10\x96\x9ab\xbe\x15\x88\x00\x00\u07d4\xfb\x91\xfb\x1aiUS\xf0\u018e!'m\xec\xf0\xb89\t\xb8m\x89\x05l\x006\x17\xafx\x00\x00\u07d4\xfb\x94s\xcfw\x125\n\x1f\xa09Rs\xfc\x80V\aR\xe4\xfb\x89\x06\xaf!\x98\xba\x85\xaa\x00\x00\xe0\x94\xfb\x94\x9cd\u007f\xdc\xfd%\x14\xc7\u054e1\xf2\x8aS-\x8cX3\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xfb\xa5HmS\xc6\xe2@IBA\xab\xf8~C\xc7`\rA:\x89k\xbfaIIH4\x00\x00\u07d4\xfb\xb1a\xfe\x87_\t)\nK&+\xc6\x01\x10\x84\x8f\r\"&\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb\xbb\xeb\u03fe#^W\xdd#\x06\xad\x1a\x9e\u0141\xc7\xf9\xf4\x8f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xfb\xc0\x1d\xb5NG\xcd\xc3\xc48iJ\xb7\x17\xa8V\xc2?\xe6\xe9\x8a\x01\xcaqP\xab\x17OG\x00\x00\xe0\x94\xfb\xcf\xccJ{\x0f&\xcf&\xe9\xf33!2\xe2\xfcj#\af\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfb\xe7\x16\"\xbc\xbd1\xc1\xa3iv\xe7\xe5\xf6p\xc0\u007f\xfe\x16\u0789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfb\xed\xe3,4\x9f3\x00\xefL\xd3;M\xe7\xdc\x18\xe4C\xd3&\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\xfb\xf2\x04\xc8\x13\xf86\xd89b\u01c7\fx\b\xca4\u007f\xd3>\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xfb\xf7Y3\xe0\x1bu\xb1T\xef\x06i\ak\xe8\u007fb\xdf\xfa\xe1\x8a\x10\x84cr\xf2I\xd4\xc0\x00\x00\u07d4\xfc\x00\x96\xb2\x1e\x95\xac\xb8\xd6\x19\xd1v\xa4\xa1\xd8\xd5)\xba\xdb\xef\x89\x14\xd9i;\xcb\xec\x02\x80\x00\xe0\x94\xfc\x00\xa4 \xa3a\a\xdf\xd5\xf4\x95\x12\x8a_\u5af2\xdb\x0f4\x8a\x01C\x17\x9d\x86\x91\x10 \x00\x00\xe0\x94\xfc\x01\x8ai\n\xd6tm\xbe:\u03d7\x12\xdd\xcaR\xb6%\x009\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xfc\x02s@3\xe5\u007fpQ~\n\xfc~\xe6$a\xf0o\xad\x8e\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xfc\x0e\xe6\xf7\u00b3qJ\xe9\x91lEVf\x05\xb6V\xf3$A\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xfc\x10\xb7\xa6{2h\xd53\x1b\xfbj\x14\xde\xf5\xeaJ\x16,\xa3\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfc\x15\u02d9\xa8\xd1\x03\v\x12w\n\xdd\x03:y\xee\r\f\x90\x8c\x89\x12\xfa\x00\xbdR\xe6$\x00\x00\u07d4\xfc)R\xb4\u011f\xed\u043c\x05(\xa3\bI^mj\x1cq\u0589lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xfc,\x1f\x88\x96\x1d\x01\x9c>\x9e\xa30\t\x15.\x06\x93\xfb\xf8\x8a\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\xfc6\x11\x05\u0750\xf9\xed\xe5fI\x9di\xe9\x13\x03\x95\xf1*\u020aS\xa4\xfe/ N\x80\xe0\x00\x00\u07d4\xfc7/\xf6\x92|\xb3\x96\xd9\xcf)\x805\x00\x11\r\xa62\xbcR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc9\xbeA\tK\x19\x97\xd2\x16\x9e\x82d\xc2\u00fa\xa6\u025b\u0109lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc=\"k\xb3jX\xf5&V\x88W\xb0\xbb\x12\xd1\t\xec\x93\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfcC\x82\x9a\u01c7\xff\x88\xaa\xf1\x83\xba5*\xad\xbfZ\x15\xb1\x93\x89\u05ac\n+\x05R\xe0\x00\x00\u07d4\xfcI\xc1C\x9aA\u05b3\xcf&\xbbg\xe06R$\xe5\xe3\x8f_\x8966\u05ef^\u024e\x00\x00\u07d4\xfcU\x00\x82Q\x05\xcfq*1\x8a^\x9c;\xfci\u021d\f\x12\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xfcf\xfa\xba'\u007fK]\xe6J\xd4^\xb1\x9c1\xe0\f\xed>\u054a\x011\xbe\xb9%\xff\xd3 \x00\x00\xe0\x94\xfc~\"\xa5\x03\xecZ\xbe\x9b\b\xc5\v\xd1I\x99\xf5 \xfaH\x84\x8a\x01ZG}\xfb\xe1\xea\x14\x80\x00\u07d4\xfc\x82\x15\xa0\xa6\x99\x13\xf6*C\xbf\x1c\x85\x90\xb9\xdd\xcd\r\x8d\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc\x98\x9c\xb4\x87\xbf\x1a}\x17\xe4\xc1\xb7\u0137\xaa\xfd\xdak\n\x8d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xfc\x9b4td\xb2\xf9\x92\x9d\x80~\x03\x9d\xaeH\xd3\u064d\xe3y\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xfc\xa4;\xbc#\xa0\xd3!\xba\x9eF\xb9)s\\\xe7\xd8\xef\f\x18\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfc\xa7>\xff\x87q\xc0\x10;\xa3\xcc\x1a\x9c%\x94H\xc7*\xbf\v\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfc\xad\xa3\x00(?k\xcc\x13J\x91Eg`\xb0\xd7}\xe4\x10\xe0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xfc\xbc\\q\xac\xe7\x97AE\v\x01,\xf6\xb8\xd3\xf1}\xb6\x8ap\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xfc\xbd\x85\xfe\xeajuO\xcf4ID\x9e7\xff\x97\x84\xf7w<\x89\xa7J\xdai\xab\xd7x\x00\x00\xe0\x94\xfc\xc9\u0524&.z\x02z\xb7Q\x91\x10\xd8\x02\u0115\xce\xea9\x8a\x01YQ\x82\"K&H\x00\x00\xe0\x94\xfc\xcd\r\x1e\xce\xe2z\xdd\xea\x95\xf6\x85z\xee\xc8\u01e0K(\xee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xfc\u0434\x82|\xd2\b\xff\xbf^u\x9d\xba\x8c<\xc6\x1d\x8c,<\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfc\xe0\x89c\\\xe9z\xba\xc0kD\x81\x9b\xe5\xbb\n>.\v7\x89\x05\x03\x92\nv0\xa7\x80\x00\u07d4\xfc\xf1\x99\xf8\xb8T\"/\x18.N\x1d\t\x9dN2>*\xae\x01\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfc\xfc:P\x04\xd6xa?\v6\xa6B&\x9a\u007f7\x1c?j\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfd\x19\x1a5\x15}x\x13s\xfbA\x1b\xf9\xf2R\x90\x04|^\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfd\x1f\xaa4{\x0f\u0300L-\xa8l6\xd5\xf1\u044bp\x87\xbb\x89\x02\xd6\xeb$z\x96\xf6\x00\x00\u07d4\xfd\x1f\xb5\xa8\x9a\x89\xa7!\xb8yph\xfb\xc4\u007f>\x9dR\xe1I\x89\f\u0435\x83\u007f\xc6X\x00\x00\u07d4\xfd OOJ\xba%%\xbar\x8a\xfd\xf7\x87\x92\xcb\u07b75\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd'W\xcc5Q\xa0\x95\x87\x8d\x97\x87V\x15\xfe\fj2\xaa\x8a\x89 m\xb1R\x99\xbe\xac\x00\x00\u07d4\xfd(r\u045eW\x85<\xfa\x16\xef\xfe\x93\u0431\xd4{O\x93\xfb\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfd))'\x1e\x9d \x95\xa2dv~{\r\xf5.\xa0\xd1\xd4\x00\x89\xa2\xa1\xeb%\x1bZ\xe4\x00\x00\u07d4\xfd7z8Rr\x90\f\xb46\xa3\xbbyb\xcd\xff\xe9?]\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd@$+\xb3Jp\x85^\xf0\xfd\x90\xf3\x80-\xec!6\xb3'\x89h\xa8u\a>)$\x00\x00\xe0\x94\xfdE,9i\xec\xe3\x80\x1cT \xf1\xcd\u02a1\xc7\x1e\xd2=\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xfdKU\x1fo\xdb\u0366\xc5\x11\xb5\xbb7\"P\xa6\xb7\x83\xe54\x89\x01\x1d\xe1\xe6\xdbE\f\x00\x00\u07d4\xfdK\x98\x95X\xae\x11\xbe\f;6\xe2\xd6\xf2\xa5J\x93C\xca.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfdM\xe8\xe3t\x8a(\x9c\xf7\xd0`Q}\x9d88\x8d\xb0\x1f\xb8\x89\r\x8drkqw\xa8\x00\x00\u07d4\xfdZc\x15\u007f\x91O\u04d8\uac5c\x13}\xd9U\v\xb7q\\\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xfd`\u04b5\xaf=5\xf7\xaa\xf0\u00d3\x05.y\xc4\xd8#\u0645\x89\x03\x0e\xb5\r.\x14\b\x00\x00\u07d4\xfdhm\xe5?\xa9\u007f\x99c\x9e%hT\x97 \xbcX\x8c\x9e\xfc\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xfd~\u078fR@\xa0eA\xebi\x9dx,/\x9a\xfb!p\xf6\x89Hz\x9a0E9D\x00\x00\u07d4\xfd\x81+\u019f\xb1p\xefW\xe22~\x80\xaf\xfd\x14\xf8\xe4\xb6\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\x88\xd1\x14\"\x0f\b\x1c\xb3\xd5\xe1[\xe8\x15*\xb0sfWj\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xfd\x91\x856\xa8\xef\xa6\xf6\xce\xfe\x1f\xa1\x159\x95\xfe\xf5\xe3=;\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xfd\x92\x0fr&\x82\xaf\xb5\xafE\x1b\x05D\xd4\xf4\x1b;\x9dWB\x89~R\x05j\x12?<\x00\x00\u07d4\xfd\x95y\xf1\x19\xbb\xc8\x19\xa0+a\u3348\x03\xc9B\xf2M2\x89\x05\xb9~\x90\x81\xd9@\x00\x00\u07d4\xfd\xa0\xce\x153\a\a\xf1\v\xce2\x01\x17- \x18\xb9\xdd\xeat\x89\x02\xd0A\xd7\x05\xa2\xc6\x00\x00\xe0\x94\xfd\xa3\x04(\x19\xaf>f)\x00\xe1\xb9+CX\xed\xa6\xe9%\x90\x8a\x19\a\xa2\x84\u054fc\xe0\x00\x00\u07d4\xfd\xa6\x81\x0e\xa5\xac\x98]o\xfb\xf1\xc5\x11\xf1\xc1B\xed\xcf\xdd\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfd\xb39D\xf26\x06\x15\xe5\xbe#\x95w\u0221\x9b\xa5-\x98\x87\x89 \x9d\x92/RY\xc5\x00\x00\u07d4\xfd\xbaSY\xf7\xec;\xc7p\xacI\x97]\x84N\xc9qbV\xf1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xfd\xc4\xd4vZ\x94/[\xf9i1\xa9\xe8\xccz\xb8\xb7W\xffL\x8a\x12lG\x8a\x0e>\xa8`\x00\x00\xe0\x94\xfd\xcd]\x80\xb1\x05\x89zW\xab\xc4xev\x8b)\x00RB\x95\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u0794\xfd\xd1\x19_y}O5q}\x15\xe6\xf9\x81\n\x9a?\xf5T`\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xfd\xd5\x02\xa7N\x81;\u03e3U\xce\xda<\x17ojhq\xaf\u007f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfd\u357c\vm\\\xbbL\x1d\x8f\xea>\vK\xffc^\x9d\xb7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\xea\xac*\xcf\x1d\x13\x8e\x19\xf2\xfc?\x9f\xb7E\x92\xe3\ud04a\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xfd\xec\xc8-\xdf\xc5a\x92\xe2oV<=h\xcbTJ\x96\xbf\xed\x89\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4\xfd\xf4#C\x01\x9b\v\fk\xf2`\xb1s\xaf\xab~E\xb9\xd6!\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xfd\xf4I\xf1\b\xc6\xfbOZ+\b\x1e\xed~E\u645eM%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\xfda4\xc0J\x8a\xb7\xeb\x16\xf0\x06C\xf8\xfe\xd7\u06aa\ucc89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfe\x00\xbfC\x99\x11\xa5S\x98-\xb68\x03\x92E\xbc\xf02\xdb\u0709\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xfe\x01n\xc1~\xc5\xf1\x0e;\xb9\x8f\xf4\xa1\xed\xa0E\x15v\x82\xab\x89\x14_T\x02\xe7\xb2\xe6\x00\x00\u07d4\xfe\x0e0\xe2\x14)\rt=\xd3\x0e\xb0\x82\xf1\xf0\xa5\"Z\xdea\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfe!\v\x8f\x04\xdcmOv!j\xcf\xcb\u055b\xa8;\xe9\xb60\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfe\"\xa0\xb3\x88f\x8d\x1a\xe2d>w\x1d\xac\xf3\x8aCB#\u0309\xd8\xdb^\xbd{&8\x00\x00\u07d4\xfe6&\x88\x84_\xa2D\u0300~K\x110\xeb7A\xa8\x05\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfe8'\xd5v0\u03c7a\xd5\x12y{\v\x85\x8eG\x8b\xbd\x12\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfeA\x8bB\x1a\x9cm76\x02y\x04u\xd20>\x11\xa7Y0\x897\b\xba\xed=h\x90\x00\x00\u07d4\xfeBI\x12yP\xe2\xf8\x96\xec\x0e~.=\x05Z\xab\x10U\x0f\x89$=M\x18\"\x9c\xa2\x00\x00\xe0\x94\xfeM\x84\x03!o\xd5qW+\xf1\xbd\xb0\x1d\x00W\x89x\u0588\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xfeS\xb9I\x89\u0619d\xda aS\x95&\xbb\xe9y\xdd.\xa9\x89h\xa8u\a>)$\x00\x00\u07d4\xfeT\x9b\xbf\xe6G@\x18\x98\x92\x93%8\u06afF\u04b6\x1dO\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xfea]\x97\\\b\x87\xe0\xc9\x11>\xc7)\x84 \xa7\x93\xaf\x8b\x96\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfee\xc4\x18\x8dy\"Wi\td D\xfd\xc5#\x95V\x01e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfei\u007f\xf2,\xa5G\xbf\xc9^3\xd9`\xda`\\gc\xf3[\x89G\xd4\x11\x9f\xd9`\x94\x00\x00\u07d4\xfej\x89[y\\\xb4\xbf\x85\x90=<\xe0\x9cZ\xa49S\u04ff\x89\xb8Pz\x82\a( \x00\x00\u07d4\xfeo_B\xb6\x19;\x1a\xd1b\x06\u4bf5#\x9dM}\xb4^\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xfep\x11\xb6\x98\xbf3q\x13-tE\xb1\x9e\xb5\xb0\x945j\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x80\xe9#-\xea\xff\x19\xba\xf9\x98i\x88:K\xdf\x00\x04\xe5<\x89.b\xf2\ni\xbe@\x00\x00\u07d4\xfe\x8en6eW\r\xffz\x1b\xdaiz\xa5\x89\xc0\xb4\xe9\x02J\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x8f\x1f\u072b\u007f\xbe\u0266\xa3\xfc\xc5\aa\x96\x00P\\6\xa3\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xfe\x91\xec\xcf+\xd5f\xaf\xa1\x16\x96\xc5\x04\x9f\xa8Lic\nR\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xfe\x96\xc4\xcd8\x15b@\x1a\xa3*\x86\xe6[\x9dR\xfa\x8a\xee'\x89\x8f\x1d\\\x1c\xae7@\x00\x00\u07d4\xfe\x98\xc6d\xc3\xe4G\xa9^i\xbdX!q\xb7\x17n\xa2\xa6\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfe\x9a\xd1.\xf0]m\x90&\x1f\x96\xc84\n\x03\x81\x97M\xf4w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x9c\x0f\xff\xef\xb8\x03\b\x12V\xc0\xcfMfY\xe6\xd3>\xb4\xfb\x89R\xd5B\x80O\x1c\xe0\x00\x00\u07d4\xfe\x9c\xfc;\xb2\x93\u0772\x85\xe6%\xf3X/t\xa6\xb0\xa5\xa6\u0349j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xfe\x9e\x11\x97\u05d7JvH\xdc\u01e01\x12\xa8\x8e\xdb\xc9\x04]\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\xe0\x94\xfe\xac\xa2\xactbK\xf3H\xda\u0258QC\xcf\xd6R\xa4\xbeU\x8a\x05\x89\u007f\u02f0)\x14\b\x80\x00\u07d4\xfe\xad\x18\x03\xe5\xe77\xa6\x8e\x18G-\x9a\xc7\x15\xf0\x99L\u00be\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xfe\xb8\xb8\xe2\xafqj\xe4\x1f\xc7\xc0K\xcf)T\x01VF\x1ek\x89TQt\xa5(\xa7z\x00\x00\u07d4\xfe\xb9-0\xbf\x01\xff\x9a\x19\x01flUsS+\xfa\a\xee\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfe\xbc1s\xbc\x90r\x13cT\x00+{O\xb3\xbf\xc5?\"\xf1\x89\x14\x0e\xc8\x0f\xa7\xee\x88\x00\x00\u07d4\xfe\xbdH\xd0\xff\xdb\xd5el\xd5\xe6\x866:a\x14R(\xf2y\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xfe\xbd\x9f\x81\xcfx\xbd_\xb6\u0139\xa2K\xd4\x14\xbb\x9b\xfaLN\x89k\xe1\x0f\xb8\xedn\x13\x80\x00\u07d4\xfe\xc0o\xe2{D\u01c4\xb29n\xc9/{\x92:\xd1~\x90w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\xc1NT\x85\xde+>\xef^t\xc4aF\u06ceEN\x035\x89\t\xb4\x1f\xbf\x9e\n\xec\x00\x00\u07d4\xfe\xd8Gm\x10\u0544\xb3\x8b\xfag7`\x0e\xf1\x9d5\xc4\x1e\u0609b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xfe\xef;n\xab\xc9J\xff\xd31\f\x1cM\x0ee7^\x13\x11\x19\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfe\xf0\x9dp$?9\xed\x8c\xd8\x00\xbf\x96QG\x9e\x8fJ\xca<\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfe\xf3\xb3\u07ad\x1ai&\u051a\xa3+\x12\xc2*\xf5M\x9f\xf9\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xff\v|\xb7\x1d\xa9\xd4\xc1\xean\xcc(\xeb\xdaPLc\xf8/\u04498\x8a\x88]\xf2\xfcl\x00\x00\u07d4\xff\f\xc6\u73c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xff'&)AH\xb8lx\xa97$\x97\xe4Y\x89\x8e\xd3\xfe\xe3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xff=\xedz@\u04ef\xf0\u05e8\xc4_\xa6\x13j\xa0C=\xb4W\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xff>\xeeW\xc3Mm\xae\x97\r\x8b1\x11\x17\xc55\x86\xcd5\x02\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xff>\xf6\xba\x15\x1c!\xb5\x99\x86\xaed\xf6\xe8\"\x8b\u0262\xc73\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xffA\xd9\xe1\xb4\xef\xfe\x18\u0630\xd1\xf6?\xc4%_\xb4\xe0l=\x89Hz\x9a0E9D\x00\x00\u07d4\xffE\xcb4\xc9(6M\x9c\xc9\u063b\x0074ta\x8f\x06\xf3\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xffI\xa7u\x81N\xc0\x00Q\xa7\x95\xa8u\xde$Y.\xa4\x00\u050a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xffJ@\x8fP\xe9\xe7!F\xa2\x8c\xe4\xfc\x8d\x90'\x1f\x11n\x84\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xffM\x9c\x84\x84\xc4\x10T\x89H\xa4\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 RinkebyAllocData = "\xf9\x03\xb7\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x941\xb9\x8d\x14\x00{\xde\xe67)\x80\x86\x98\x8a\v\xbd1\x18E#\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + +const GoerliAllocData = "\xf9\x04\x06\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xe0\x94L*\xe4\x82Y5\x05\xf0\x16<\xde\xfc\a>\x81\xc6<\xdaA\a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xa8\xe8\xf1G2e\x8eKQ\xe8q\x191\x05:\x8ai\xba\xf2\xb1\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe1\x94\u0665\x17\x9f\t\x1d\x85\x05\x1d<\x98'\x85\xef\xd1E\\\uc199\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe1\x94\u08bdBX\xd2v\x887\xba\xa2j(\xfeq\xdc\a\x9f\x84\u01cbJG\xe3\xc1$H\xf4\xad\x00\x00\x00" + +const YoloV2AllocData = "\xf9\x03\xb7\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x94\x8a7\x86o\xd3b|\x92\x05\xa3|\x86\x85fo2\xec\a\xbb\x1b\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +++======= ++ const mainnetAllocData = "\xfa\x04]X\u0793\r\x83b\x011\x8e\u0189\x9agT\x06\x908'\x80t2\x80\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0793\x17bC\x0e\xa9\u00e2nWI\xaf\xdbp\xda_x\u077b\x8c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0793\x1d\x14\x80K9\x9cn\xf8\x0edWoev`\x80O\xec\v\x89\u3bb5sr@\xa0\x00\x00\u07932@5\x87\x94{\x9f\x15b*h\xd1\x04\xd5M3\xdb\xd1\u0349\x043\x87Oc,\xc6\x00\x00\u0793I~\x92\xcd\xc0\xe0\xb9c\xd7R\xb2)j\u02c7\u0682\x8b$\x89\n\x8fd\x9f\xe7\xc6\x18\x00\x00\u0793K\xfb\xe1Tk\xc6\xc6[\\~\xaaU0K8\xbb\xfe\xc6\u04c9lk\x93[\x8b\xbd@\x00\x00\u0793Z\x9c\x03\xf6\x9d\x17\xd6l\xbb\x8a\xd7!\x00\x8a\x9e\xbb\xb86\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u0793]\x0e\xe8\x15^\xc0\xa6\xffh\bU,\xa5\xf1k\xb5\xbe2:\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u0793v\"\xd8J#K\xb8\xb0x#\x0f\u03c4\xb6z\u9a2c\xae\x89%\xe1\xccQ\x99R\xf8\x00\x00\u0793{\x9f\xc3\x19\x05\xb4\x99K\x04\xc9\xe2\xcf\xdc^'pP?B\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u0793\u007fJ#\xca\x00\xcd\x04=%\u0088\x8c\x1a\xa5h\x8f\x81\xa3D\x89)\xf0\xa9[\xfb\xf7)\x00\x00\u0793\x869\u06bb\u3bac\x88{]\xc0\xe4>\x13\xbc\u0487\xd7l\x89\x10\xd0\xe3\xc8}n,\x00\x00\u0793\x89P\x86y\xab\xf8\xc7\x1b\xf6x\x16\x87\x12\x0e>j\x84XM\x89a\x94\x04\x9f0\xf7 \x00\x00\u0793\x8f\xc7\u02ed\xff\xbd\r\u007f\xe4O\x8d\xfd`\xa7\x9dr\x1a\x1c\x9c\x8965\u026d\xc5\u07a0\x00\x00\u0793\x95`\xa3\xdebxh\xf9\x1f\xa8\xbf\xe1\xc1\xb7\xaf\xaf\b\x18k\x89\x1cg\xf5\xf7\xba\xa0\xb0\x00\x00\u0793\x96\x97G\xf7\xa5\xb3\x06E\xfe\x00\xe4I\x01CZ\xce$\xcc7\x89\\(=A\x03\x94\x10\x00\x00\u0793\x9am}\xb3&g\x9bw\xc9\x03\x91\xa7Gm#\x8f;\xa3>\x89\n\xdaUGK\x814\x00\x00\u0793\x9e\xef\n\b\x86\x05n?i!\x18S\xb9\xb7E\u007f7\x82\u4262\xa8x\x06\x9b(\xe0\x00\x00\u0793\x9f\xdb\xf4N\x1fJcb\xb7i\u00daG_\x95\xa9l+\u01c9\x1e\x93\x12\x83\xcc\xc8P\x00\x00\u07d3\xa5y\u007fR\xc9\u054f\x18\x9f6\xb1\xd4]\x1b\xf6\x04\x1f/k\x8a\x01'\u0473F\x1a\xcd\x1a\x00\x00\u0793\xaaS\x81\xb2\x13\x8e\xbe\xff\xc1\x91\xd5\xd8\u00d1u;p\x98\u04895\xab\xb0\x9f\xfe\u07b6\x80\x00\u0793\xaa\xda%\xea\"\x86p\x9a\xbbB-A\x92?\u04c0\xcd\x04\u01c9#=\xf3)\x9far\x00\x00\u0793\xac\xbf\xb2\xf2ZT\x85\xc79\xefp\xa4N\xee\xeb|e\xa6o\x89\x05k\xc7^-c\x10\x00\x00\u07d3\xac\xc6\xf0\x82\xa4B\x82\x87d\xd1\x1fX\u0589J\xe4\b\xf0s\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u0793\xb2w\xb0\x99\xa8\xe8f\xca\x0e\xc6[\u02c7(O\xd1B\xa5\x82\x89j\xcb=\xf2~\x1f\x88\x00\x00\u0753\xbd\xd4\x01:\xa3\x1c\x04al+\xc9x_'\x88\xf9\x15g\x9b\x88\xb9\xf6]\x00\xf6<\x00\x00\u0793\xc2}c\xfd\xe2K\x92\xee\x8a\x1e~\xd5\xd2m\x8d\xc5\xc8;\x03\x89lk\x93[\x8b\xbd@\x00\x00\u0553\xc4\x0f\xe2\tT#P\x9b\x9f\u0677T21X\xaf#\x10\xf3\x80\u0793\xd7^\xd6\fwO\x8b:ZQs\xfb\x183\xadq\x05\xa2\u0649l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d3\u05cd\x89\xb3_G'\x16\xec\xea\xfe\xbf`\x05'\u04e1\xf9i\x8a\x05\xe0T\x9c\x962\xe1\xd8\x00\x00\u0793\xda\xe2{5\v\xae \xc5e!$\xaf]\x8b\\\xba\x00\x1e\xc1\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u0793\xdc\x01\xcb\xf4Ix\xa4.\x8d\xe8\xe46\xed\xf9B\x05\u03f6\xec\x89O\x0f\xeb\xbc\u068c\xb4\x00\x00\u07d3\u607c-\x10\xdbb\u0785\x84\x83$I\"P4\x8e\x90\xbf\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d3\xf4c\xe17\xdc\xf6%\xfb\U000fc8de\u0298\u04b9h\xcf\u007f\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\x01\x00\a9K\x8bue\xa1e\x8a\xf8\x8c\xe4cI\x915\u05b7\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x01\r\xf1\xdfK\xed#v\r-\x1c\x03x\x15\x86\xdd\xf7\x91\x8eT\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\x01\x0fJ\x98\u07e1\xd9y\x9b\xf5\u01d6\xfbU\x0e\xfb\xe7\xec\xd8w\x8a\x01\xb2\xf2\x92#b\x92\xc7\x00\x00\u07d4\x01\x15PW\x00/k\r\x18\xac\xb98\x8d;\xc8\x12\x9f\x8fz \x89H\xa4\xa9\x0f\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01k`\xbbmg\x92\x8c)\xfd\x03\x13\xc6f\u068f\x16\x98\xd9\u0149lk\x93[\x8b\xbd@\x00\x00\u07d4\x01l\x85\xe1a;\x90\x0f\xa3W\xb8(;\x12\x0ee\xae\xfc\xdd\b\x89+]\x97\x84\xa9|\xd5\x00\x00\u07d4\x01\x84\x92H\x8b\xa1\xa2\x924\"G\xb3\x18U\xa5Y\x05\xfe\xf2i\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x01\x8f \xa2{'\xecD\x1a\xf7#\xfd\x90\x99\xf2\u02f7\x9dbc\x89uy*\x8a\xbd\xef|\x00\x00\u07d4\x01\x91\xebT~{\xf6\x97k\x9b\x1bWuFv\x1d\xe6V\"\xe2\x89lkLM\xa6\u077e\x00\x00\u07d4\x01\x9dp\x95y\xffK\xc0\x9f\xdc\xdd\xe41\xdc\x14G\xd2\xc2`\xbc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x01\xa2Z_Z\xf0\x16\x9b0\x86L;\xe4\xd7V<\xcdD\xf0\x9e\x89M\x85<\x8f\x89\b\x98\x00\x00\xe0\x94\x01\xa7\xd9\xfa}\x0e\xb1\x18\\g\xe5M\xa8<.u\xdbi\u37ca\x01\x9dJ\xdd\xd0\u063c\x96\x00\x00\u07d4\x01\xa8\x18\x13ZAB\x10\xc3|b\xb6%\xac\xa1\xa5F\x11\xac6\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\x01\xb1\xca\xe9\x1a;\x95Y\xaf\xb3<\xdcmh\x94B\xfd\xbf\xe07\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01\xb5\xb5\xbcZ\x11\u007f\xa0\x8b4\xed\x1d\xb9D\x06\bYz\xc5H\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01\xbb\xc1Og\xaf\x069\xaa\xb1D\x1ej\b\xd4\xceqb\t\x0f\x89F\xfc\xf6\x8f\xf8\xbe\x06\x00\x00\xe0\x94\x01\xd08\x15\xc6\x1fAkq\xa2a\n-\xab\xa5\x9f\xf6\xa6\xde[\x8a\x02\x05\xdf\xe5\v\x81\xc8.\x00\x00\u07d4\x01\u0559\xee\r_\x8c8\xab-9.,e\xb7L<\xe3\x18 \x89\x1b\xa5\xab\xf9\xe7y8\x00\x00\u07d4\x01\xe4\x05!\x12%0\u066c\x91\x11<\x06\xa0\x19\vmc\x85\v\x89Hz\x9a0E9D\x00\x00\u07d4\x01\xe6A]X{\x06T\x90\xf1\xed\u007f!\xd6\xe0\xf3\x86\xeegG\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x01\xe8d\xd3Tt\x1bB>oB\x85\x17$F\x8ct\xf5\xaa\x9c\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x01\xed_\xba\x8d.\xabg:\xec\x04-0\xe4\xe8\xa6\x11\xd8\xc5Z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01\xfb\x8e\xc1$%\xa0O\x81>F\xc5L\x05t\x8c\xa6\xb2\x9a\xa9\x89\x0e\x15s\x03\x85F|\x00\x00\u07d4\x01\xff\x1e\xb1\u07adP\xa7\xf2\xf9c\x8f\xde\xe6\xec\xcf:{*\u0209 \x86\xac5\x10R`\x00\x00\u07d4\x02\x03b\u00ed\xe8x\u0290\u05b2\u0609\xa4\xccU\x10\xee\xd5\xf3\x898\x88\xe8\xb3\x11\xad\xb3\x80\x00\u07d4\x02\x03\xae\x01\xd4\xc4\x1c\xae\x18e\xe0K\x1f[S\xcd\xfa\xec\xae1\x896\x89\xcd\u03b2\x8c\xd7\x00\x00\u07d4\x02\b\x93a\xa3\xfetQ\xfb\x1f\x87\xf0\x1a-\x86fS\xdc\v\a\x89\x02*\xc7H2\xb5\x04\x00\x00\u07d4\x02\x1fi\x04=\xe8\x8cI\x17\xca\x10\xf1\x84(\x97\xee\xc0X\x9c|\x89kD\u03f8\x14\x87\xf4\x00\x00\u07d4\x02)\x0f\xb5\xf9\xa5\x17\xf8(E\xac\xde\xca\x0f\xc8F\x03\x9b\xe23\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x029\xb4\xf2\x1f\x8e\x05\xcd\x01Q++\xe7\xa0\xe1\x8am\x97F\a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x02Gr\x12\xff\xddu\xe5\x15VQ\xb7e\x06\xb1dfq\xa1\xeb\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x02J\t\x8a\xe7\x02\xbe\xf5@l\x9c\"\xb7\x8b\xd4\xeb,\u01e2\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x02K\xdd,{\xfdP\x0e\xe7@O\u007f\xb3\xe9\xfb1\xdd \xfb\u0449\t\xc2\x00vQ\xb2P\x00\x00\u07d4\x02Sg\x96\x03\x04\xbe\xee4Y\x11\x18\xe9\xac-\x13X\xd8\x02\x1a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x02V\x14\x9f[Pc\xbe\xa1N\x15f\x1f\xfbX\xf9\xb4Y\xa9W\x89&)\xf6n\fS\x00\x00\x00\u07d4\x02`=z;\xb2\x97\xc6|\x87~]4\xfb\u0579\x13\xd4\xc6:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x02a\xad:\x17*\xbf\x13\x15\xf0\xff\xec2p\x98j\x84\t\xcb%\x89\v\b!;\u03cf\xfe\x00\x00\u07d4\x02d2\xaf7\xdcQ\x13\xf1\xf4mH\nM\u0c80R#~\x89\x13I\xb7\x86\xe4\v\xfc\x00\x00\u07d4\x02f\xab\x1ck\x02\x16#\v\x93\x95D=_\xa7^hEh\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x02u\x1d\u018c\xb5\xbdsp'\xab\xf7\u0777s\x90\xcdw\xc1k\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x02w\x8e9\x0f\xa1u\x10\xa3B\x8a\xf2\x87\fBsT}8l\x8a\x03lw\x80\x18\x8b\xf0\xef\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x037|\x0eUkd\x01\x03(\x9aa\x89\u1baecI4g\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03IcM\u00a9\xe8\f?w!\xee+PF\xae\xaa\xed\xfb\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x03U\xbc\xac\xbd!D\x1e\x95\xad\xee\xdc0\xc1r\x18\u0224\b\u0389\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x03n\xef\xf5\xba\x90\xa6\x87\x9a\x14\xdf\xf4\xc5\x04;\x18\xca\x04`\u0249\x05k\xc7^-c\x10\x00\x00\xe0\x94\x03qKA\u04a6\xf7Q\x00\x8e\xf8\xddM+)\xae\u02b8\xf3n\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x03r\xe8RX.\t44J\x0f\xed!x0M\xf2]F(\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03r\xeeU\b\xbf\x81c\xed(N^\xef\x94\xceMsg\xe5\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x03}\xd0V\xe7\xfd\xbdd\x1d\xb5\xb6\xbe\xa2\xa8x\n\x83\xfa\u1009\a\x96\xe3\xea?\x8a\xb0\x00\x00\xe0\x94\x03\x83#\xb1\x84\xcf\xf7\xa8*\xe2\u1f67y?\xe41\x9c\xa0\xbf\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03\x87y\xca-\xbef>c\xdb?\xe7V\x83\xea\x0e\xc6.#\x83\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\x03\x8eE\xea\xdd=\x88\xb8\u007f\xe4\u06b0fh\x05\"\xf0\xdf\xc8\xf9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x03\x92T\x9ar\u007f\x81eT)\u02d2\x8bR\x9f%\xdfM\x13\x85\x89\x01lC\xa0\xee\xa0t\x00\x00\u07d4\x03\x94\xb9\x0f\xad\xb8`O\x86\xf4?\xc1\xe3]1$\xb3*Y\x89\x89)j\xa1@'\x8ep\x00\x00\u0794\x03\x9ezN\xbc(N,\xcdB\xb1\xbd\xd6\v\xd6Q\x1c\x0fw\x06\x88\xf0\x15\xf2W6B\x00\x00\u07d4\x03\x9e\xf1\xceR\xfeyc\xf1f\u0562u\u0131\x06\x9f\xe3\xa82\x89\x15\xaf9\u4ab2t\x00\x00\u07d4\x03\xa2l\xfcL\x181op\u055e\x9e\x1ay\xee>\x8b\x96/L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x03\xaab(\x81#m\xd0\xf4\x94\f$\xc3$\xff\x8b{~!\x86\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4\x03\xafz\xd9\xd5\"<\xf7\xc8\xc1? \xdfg\xeb\xe5\xff\u017bA\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x03\xb0\xf1|\xd4F\x9d\xdc\u03f7\xdai~\x82\xa9\x1a_\x9ewt\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x03\xb4\x1bQ\xf4\x1d\xf2\r\xd2y\xba\xe1\x8c\x12w_w\xadw\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x03\xbe[F)\xae\xfb\xbc\xab\x9d\xe2m9Wl\xb7\xf6\x91\xd7d\x89\n\xdf0\xbap\u0217\x00\x00\u07d4\x03\xc6G\xa9\xf9)\xb0x\x1f\xe9\xae\x01\u02a3\xe1\x83\xe8vw~\x89\x18*\xb7\xc2\f\xe5$\x00\x00\u07d4\x03\xc9\x1d\x92\x946\x03\xe7R >\x054\x0eV`\x13\xb9\x00E\x89+|\xc2\xe9\xc3\"\\\x00\x00\u07d4\x03\xcbLOE\x16\xc4\xffy\xa1\xb6$O\xbfW.\x1c\u007f\xeay\x89\x94\x89#z\u06daP\x00\x00\u07d4\x03\u02d8\u05ec\xd8\x17\u079d\x88m\"\xfa\xb3\xf1\xb5}\x92\xa6\b\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x03\u031d-!\xf8k\x84\xac\x8c\xea\xf9q\u06e7\x8a\x90\xe6%p\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\x03\xd1rO\xd0\x0eT\xaa\xbc\xd2\xde*\x91\xe8F+\x10I\xdd:\x89\x8f\x1d\\\x1c\xae7@\x00\x00\u07d4\x03\xde\xdf\xcd\v<.\x17\xc7\x05\xda$\x87\x90\uf626\xbdWQ\x89Hz\x9a0E9D\x00\x00\u07d4\x03\u8c04SuW\xe7\t\xea\xe2\xe1\u1966\xbc\xe1\xef\x83\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x03\xeam&\u0400\xe5z\xee9&\xb1\x8e\x8e\xd7:N[(&\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x03\xeb<\xb8`\xf6\x02\x8d\xa5T\xd3D\xa2\xbbZP\n\xe8\xb8o\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x03\xeb\xc6?\xdaf`\xa4e\x04^#_\xben\\\xf1\x95s_\x89\a\xb0l\xe8\u007f\xddh\x00\x00\xe0\x94\x03\xefj\xd2\x0f\xf7\xbdO\x00+\xacX\xd4uD\u03c7\x9a\xe7(\x8a\x01u\xc7X\u0439n\\\x00\x00\u07d4\x03\xf7\xb9 \b\x81:\xe0\xa6v\xeb!(\x14\xaf\xab5\"\x10i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\x11p\xf5\x81\u0780\xe5\x8b*\x04\\\x8f|\x14\x93\xb0\x01\xb7\u02c90\xc8\xeca2\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\x04i\xe8\xc4@E\v\x0eQ&&\xfe\x81~gT\xa8\x15(0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04m'K\x1a\xf6\x15\xfbPZvJ\xd8\u0767p\xb1\xdb/=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x04}Z&\u05ed\x8f\x8ep`\x0fp\xa3\x98\u076a\x1c-\xb2o\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x04~\x87\xc8\xf7\xd1\xfc\xe3\xb0\x13S\xa8Xb\xa9H\xac\x04\x9f>\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x04\u007f\x9b\xf1R\x9d\xaf\x87\xd4\a\x17^o\x17\x1b^Y\xe9\xff>\x89#<\x8f\xe4'\x03\xe8\x00\x00\xe0\x94\x04\x85'2\xb4\xc6R\xf6\xc2\u53b3e\x87\xe6\nb\xda\x14\u06ca\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x04\x8a\x89p\xeaAE\xc6MU\x17\xb8\xde[F\xd0YZ\xad\x06\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x04\x9c]K\xc6\xf2]NEli{R\xa0x\x11\xcc\u045f\xb1\x89\x10D\x00\xa2G\x0eh\x00\x00\u07d4\x04\xa1\xca\xda\x1c\xc7Q\b/\xf8\u0692\x8e<\xfa\x00\b \xa9\xe9\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x04\xa8\n\xfa\xd5>\xf1\xf8Ae\xcf\xd8R\xb0\xfd\xf1\xb1\xc2K\xa8\x89\x03$\xe9d\xb3\xec\xa8\x00\x00\u07d4\x04\xaa\xfc\x8a\xe5\xceoI\x03\u021d\u007f\xac\x9c\xb1\x95\x12\"Gw\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x04\xbaK\xb8q@\x02,!Jo\xacB\xdbZ\x16\u0755@E\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x04\xba\x8a?\x03\xf0\x8b\x89P\x95\x99M\xdaa\x9e\u06ac\xee>z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\xc2\xc6K\xb5L>\xcc\xd0U\x85\xe1\x0e\xc6\xf9\x9a\f\xdb\x01\xa3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x04\xceE\xf6\x00\xdb\x18\xa9\u0405\x1b)\xd99>\xbd\xaa\xfe=\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x04\u05b8\xd4\u0686t\a\xbb\x99wI\u07bb\xcd\xc0\xb3XS\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x04\xd78\x96\xcfe\x93\xa6\x91\x97*\x13\xa6\xe4\x87\x1f\xf2\xc4+\x13\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\xd8*\xf9\xe0\x1a\x93m\x97\xf8\xf8Y@\xb9p\xf9\xd4\u06d96\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x04\xe5\xf5\xbc|\x92?\xd1\xe3\x175\xe7.\xf9h\xfdg\x11\fn\x89WU\x1d\xbc\x8ebL\x00\x00\u07d4\x04\xec\xa5\x01c\n\xbc\xe3R\x18\xb1t\x95k\x89\x1b\xa2^\xfb#\x8966\x9e\xd7t}&\x00\x00\u07d4\x05\x05\xa0\x8e\"\xa1\t\x01Z\"\xf6\x850STf*U1\u0549\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\x05\x14\x95L\xe8\x81\xc807\x03d\x00\x89lO\xd1\xee$nx\x00\x00\u07d4\x05\x1dBBv\xb2\x129fQ\x86\x13=e;\xb8\xb1\x86/\x89\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x05!\xbc:\x9f\x87\x11\xfe\xcb\x10\xf5\a\x97\xd7\x10\x83\xe3A\ub749\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x05#mL\x90\xd0e\xf9\u34c3X\xaa\xff\xd7w\xb8j\xecI\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x05*X\xe05\xf1\xfe\x9c\xdd\x16\x9b\xcf \x97\x03E\xd1+\x9cQ\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x05.\xab\x1fa\xb6\xd4U\x17(?A\xd1D\x18$\x87\x87I\u0409\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x053n\x9ar'(\xd9c\xe7\xa1\xcf'Y\xfd\x02tS\x0f\u02891\xa2D?\x88\x8ay\x80\x00\u07d4\x054q\u035aA\x92[9\x04\xa5\xa8\xff\xca6Y\xe04\xbe#\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4\x056\x1d\x8e\xb6\x94\x1dN\x90\xfb~\x14\x18\xa9Z2\xd5%w2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x05B:T\xc8\xd0\xf9p~pAs\xd9#\xb9F\xed\xc8\xe7\x00\x89\x06\xea\x03\u00bf\x8b\xa5\x80\x00\u07d4\x05D\f[\a;R\x9bH) \x9d\xff\x88\t\x0e\a\xc4\xf6\xf5\x89E\u04977\xe2/ \x00\x00\u07d4\x05Z\xb6X\xc6\xf0\xedO\x87^\xd6t.K\xc7)-\x1a\xbb\xf0\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\u07d4\x05[\xd0,\xaf\x19\xd6 +\xbc\u0703m\x18{\xd1\xc0\x1c\xf2a\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x05^\xacO\x1a\xd3\xf5\x8f\v\xd0$\u058e\xa6\r\xbe\x01\u01af\xb3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x05fQU\xccI\xcb\xf6\xaa\xbd\u056e\x92\xcb\xfa\xad\x82\xb8\xc0\xc1\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x05f\x86\a\x8f\xb6\xbc\xf9\xba\n\x8a\x8d\xc6:\x90o_\xea\xc0\xea\x89\x1b\x18\x1eK\xf24<\x00\x00\u07d4\x05iks\x91k\xd3\x03>\x05R\x1e2\x11\xdf\xec\x02n\x98\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x05k\x15F\x89O\x9a\x85\xe2\x03\xfb3m\xb5i\xb1l%\xe0O\x89\t.\xdb\t\xff\b\u0600\x00\u07d4\x05yI\xe1\xca\x05pF\x9eL\xe3\u0190\xaea:k\x01\xc5Y\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x05}\u049f-\x19\xaa=\xa4#'\xeaP\xbc\xe8o\xf5\xc9\x11\u0649\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x05\u007f\u007f\x81\xcdz@o\xc4Y\x94@\x8bPI\x91,Vdc\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x05\x91]N\"Zf\x81b\xae\xe7\xd6\xc2_\xcf\xc6\xed\x18\xdb\x03\x89\x03\x98\xc3ry%\x9e\x00\x00\u07d4\x05\x96\xa2}\xc3\xee\x11_\xce/\x94\xb4\x81\xbc z\x9e&\x15%\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x05\xa80rC\x02\xbc\x0fn\xbd\xaa\x1e\xbe\xee\xb4nl\xe0\v9\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\x05\xae\u007f\u053b\u0300\xca\x11\xa9\n\x1e\u01e3\x01\xf7\xcc\u0303\u06c91T\xc9r\x9d\x05x\x00\x00\u07d4\x05\xbbd\xa9\x16\xbef\xf4`\xf5\xe3\xb6C2\x11\r \x9e\x19\xae\x89\u3bb5sr@\xa0\x00\x00\xe0\x94\x05\xbfO\xcf\xe7r\xe4[\x82dC\x85.l5\x13P\xcer\xa2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\x05\xc6@\x04\xa9\xa8&\xe9N^N\xe2g\xfa*v2\xddNo\x8a\x03m\xc4.\xbf\xf9\v\u007f\x80\x00\xe0\x94\x05\xc76\xd3e\xaa7\xb5\xc0\xbe\x9c\x12\u022d\\\xd9\x03\xc3,\xf9\x8a\x01E^{\x80\n\x86\x88\x00\x00\xe0\x94\x05\xcbl;\x00r\xd3\x11ga\xb52\xb2\x18D;S\xe8\xf6\u014a\x1e\x02\xc3\xd7\xfc\xa9\xb6(\x00\x00\u07d4\x05\xd0\xf4\xd7(\xeb\xe8.\x84\xbfYu\x15\xadA\xb6\v\xf2\x8b9\x89\u3bb5sr@\xa0\x00\x00\u07d4\x05\u058d\xada\u04fb\u07f3\xf7y&\\IGJ\xff?\xcd0\x89\x02\"\xc5]\xc1Q\x9d\x80\x00\u07d4\x05\xe6q\xdeU\xaf\xec\x96K\aM\xe5t\xd5\x15\x8d]!\xb0\xa3\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x05\xe9{\tI,\u058fc\xb1+\x89.\xd1\xd1\x1d\x15,\x0e\u02897\b\xba\xed=h\x90\x00\x00\u07d4\x05\xf3c\x1fVd\xbd\xad]\x012\xc88\x8d6\xd7\u0612\t\x18\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x06\t\xd8:l\xe1\xff\u0276\x90\xf3\xe9\xa8\x1e\x98>\x8b\xdcM\x9d\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\u07d4\x06\x1e\xa4\x87|\u0409D\xebd\u0096n\x9d\xb8\xde\xdc\xfe\xc0k\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x06%\xd0`V\x96\x8b\x00\"\x06\xff\x91\x98\x01@$+\xfa\xa4\x99\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x06(\xbf\xbeU5x/\xb5\x88@k\xc9f`\xa4\x9b\x01\x1a\xf5\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\x061\u044b\xbb\xbd0\xd9\xe1s+\xf3n\xda\xe2\u0389\x01\xab\x80\x89\xa3\xf9\x88U\xec9\x90\x00\x00\u07d4\x061\xdc@\xd7NP\x95\xe3r\x9e\xdd\xf4\x95D\xec\xd49og\x89\b\xacr0H\x9e\x80\x00\x00\xe0\x94\x067Y\xdd\x1cN6.\xb1\x93\x98\x95\x1f\xf9\xf8\xfa\xd1\xd3\x10h\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x06_\xf5u\xfd\x9c\x16\xd3\xcbo\u058f\xfc\x8fH?\xc3.\xc85\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x06a\x8e\x9dWb\xdfb\x02\x86\x01\xa8\x1dD\x87\u05a0\xec\xb8\x0e\x89Hz\x9a0E9D\x00\x00\xe0\x94\x06fG\xcf\xc8]#\xd3v\x05W= \x8c\xa1T\xb2D\xd7l\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x06xeJ\xc6v\x1d\xb9\x04\xa2\xf7\xe8Y^\xc1\xea\xacsC\b\x89/\x98\xb2\x9c(\x18\xf8\x00\x00\u07d4\x06\x86\n\x93RYU\xffbI@\xfa\xdc\xff\xb8\xe1I\xfdY\x9c\x89lh\xcc\u041b\x02,\x00\x00\xe0\x94\x06\x8c\xe8\xbdn\x90*E\u02c3\xb5\x15A\xb4\x0f9\xc4F\x97\x12\x8a\x01\x1c\x0f\x9b\xadJF\xe0\x00\x00\u07d4\x06\x8e)\xb3\xf1\x91\xc8\x12\xa699\x18\xf7\x1a\xb93\xaehG\xf2\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x06\x8eeWf\xb9D\xfb&6\x19e\x87@\xb8P\xc9J\xfa1\x89\x01\xe8\u007f\x85\x80\x9d\xc0\x00\x00\u0794\x06\x96N-\x17\xe9\x18\x9f\x88\xa8 96\xb4\n\xc9nS<\x06\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x06\x99L\xd8:\xa2d\n\x97\xb2`\vA3\x9d\x1e\r>\xdel\x89\r\x8drkqw\xa8\x00\x00\u07d4\x06\x9e\u042bz\xa7}\xe5q\xf1a\x06\x05\x1d\x92\xaf\xe1\x95\xf2\u0409\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x06\xac&\xad\x92\u02c5\x9b\u0550]\xdc\xe4&j\xa0\xecP\xa9\u0149*\x03I\x19\u07ff\xbc\x00\x00\u07d4\x06\xb0\xc1\xe3\u007fZ^\u013b\xf5\b@T\x8f\x9d:\xc0(\x88\x97\x89\xd8\u0602\u148e}\x00\x00\u07d4\x06\xb0\xff\x83@s\xcc\xe1\xcb\xc9\xeaU~\xa8{`Yc\u8d09\x10CV\x1a\x88)0\x00\x00\xe0\x94\x06\xb1\x06d\x9a\xa8\xc4!\xdd\xcd\x1b\x8c2\xcd\x04\x18\xcf0\xda\x1f\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x06\xb5\xed\xe6\xfd\xf1\xd6\xe9\xa3G!7\x9a\xea\xa1|q=\xd8*\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x06\xcb\xfa\b\xcd\xd4\xfb\xa77\xba\xc4\a\xbe\x82$\xf4\xee\xf3X(\x89 +\xe5\xe88.\x8b\x80\x00\u07d4\x06\xd6\xcb0\x84\x81\xc36\xa6\xe1\xa2%\xa9\x12\xf6\xe65Y@\xa1\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x06\xdc\u007f\x18\xce\xe7\xed\xab[yS7\xb1\xdfj\x9e\x8b\u062eY\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x06\xf6\x8d\xe3\xd79\xdbA\x12\x1e\xac\xf7y\xaa\xda=\xe8v!\a\x89\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\x06\xf7\u070d\x1b\x94b\xce\xf6\xfe\xb13h\xa7\xe3\x97K\t\u007f\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\a\x01\xf9\xf1G\xecHhV\xf5\xe1\xb7\x1d\xe9\xf1\x17\xe9\x9e!\x05\x89\te\xdaq\u007f\u0578\x00\x00\u07d4\a\r]6L\xb7\xbb\xf8\"\xfc,\xa9\x1a5\xbd\xd4A\xb2\x15\u0549lk\x93[\x8b\xbd@\x00\x00\xe0\x94\a\x1d\xd9\r\x14\xd4\x1fO\xf7\xc4\x13\xc2B8\xd35\x9c\xd6\x1a\a\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\u07d4\a&\xc4.\x00\xf4T\x04\x83n\xb1\xe2\x80\xd0s\xe7\x05\x96\x87\xf5\x89X\x00>?\xb9G\xa3\x80\x00\xe0\x94\a'\xbe\n*\x00! H\xb5R\x0f\xbe\xfb\x95>\xbc\x9dT\xa0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\a)\xa8\xa4\xa5\xba#\xf5y\xd0\x02[\x1a\xd0\xf8\xa0\xd3\\\xdf\u048a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4\a)\xb4\xb4|\t\xeb\x16\x15\x84d\u022a\u007f\xd9i\vC\x889\x89lh\xcc\u041b\x02,\x00\x00\u0794\a4\xa0\xa8\x1c\x95b\xf4\xd9\xe9\xe1\n\x85\x03\xda\x15\xdbF\xd7n\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\a\xa7\xef[G\x00\x00\xe0\x94\ap\xc6\x1b\xe7\x87r#\f\xb5\xa3\xbb$)\xa7&\x14\xa0\xb36\x8a\x01n\u0899\xb7\x13A\x80\x00\u07d4\ar><0\xe8\xb71\xeeEj)\x1e\xe0\u7630 Jw\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\as\xee\xac\xc0P\xf7G \xb4\xa1\xbdW\x89[\x1c\xce\xebI]\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\a\x80\r/\x80h\xe4H\u01daOi\xb1\xf1^\xf6\x82\xaa\xe5\xf6\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\a\xa8\xda\xde\xc1BW\x1a}S\xa4)pQxm\a,\xbaU\x89\x01;m\xa1\x13\x9b\u0680\x00\u07d4\a\xaf\x93\x8c\x127\xa2|\x900\tM\xcf$\aP$n=,\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\a\xb1\xa3\x06\xcbC\x12\xdffH,,\xaer\xd1\xe0a@\x0f\u034a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\a\xb7\xa5p3\xf8\xf1\x130\xe4f^\x18]#N\x83\xec\x14\v\x89\xea~\xe9*\f\x9a\v\x80\x00\u07d4\a\xbc,\xc8\xee\xdc\x01\x97\a\x00\xef\xc9\xc4\xfb6s^\x98\xcdq\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\a\xd4\x12\x17\xba\u0725\xe0\xe6\x03'\xd8E\xa3FO\x0f'\xf8J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\a\xd43N\u00c5\xe8\xaaT\xee\xda\xea\xdb0\x02/\f\u07e4\xab\x89\x8e\x91\xd5 \xf2\xeby\x00\x00\u07d4\a\xda\xe6\"c\r\x1168\x193\u04adk\"\xb89\xd8!\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\a\xdc+\xf8;\u01af\x19\xa8B\xff\xeaf\x1a\xf5\xb4\x1bg\xfd\xa1\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\a\u070c\x8b\x92z\xdb\xed\xfa\x8f]c\x9bCR5\x1f/6\u0489\x11\n\xed;U0\xdb\x00\x00\u07d4\a\xdd\xd0B,\x86\xefe\xbf\f\u007f\xc3E(b\xb1\"\x8b\b\xb8\x89o\xf5\u04aa\x8f\x9f\xcf\x00\x00\u07d4\a\xe1\x16,\xea\xe3\xcf!\xa3\xf6-\x10Y\x900.0\u007fN;\x89R\xf1\x03\xed\xb6k\xa8\x00\x00\u07d4\a\xe2\xb4\xcd\xee\xd9\u0407\xb1.Um\x9ew\f\x13\xc0\x99a_\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\a\xfe\xefT\xc16\x85\b)\xba\xdcKI\xc3\xf2\xa7<\x89\xfb\x9e\x89\x06hZ\xc1\xbf\xe3,\x00\x00\u07d4\b\x05FP\x8a=&\x82\u0239\x88O\x13c{\x88G\xb4M\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\t\bv\xba\xad\xfe\xe6\\=6;\xa5S\x12t\x8c\xfa\x87=\x89\\*\x997\x1c\xff\xe1\x00\x00\u07d4\b\x16o\x021?\xea\u12f0D\xe7\x87|\x80\x8bU\xb5\xbfX\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\b)\xd0\xf7\xbb|Dl\xfb\xb0\u07ad\xb29M\x9d\xb7$\x9a\x87\x89\x02,\xa3X|\xf4\xeb\x00\x00\u07d4\b0m\xe5\x19\x81\u7b21\x85hY\xb7\xc7xijki\xf9\x89\xadx\xeb\u016cb\x00\x00\x00\xe0\x94\b7S\x9b_jR*H,\xdc\u04e9\xbbpC\xaf9\xbd\u048a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\b8\xa7v\x8d\x9c*\u028b\xa2y\xad\xfe\xe4\xb1\xf4\x91\xe3&\xf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\bA\x16R\xc8qq6\t\xaf\x00b\xa8\xa1(\x1b\xf1\xbb\xcf\u0649K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\bM\x102Tu\x9b4<\xb2\xb9\xc2\xd8\xff\x9e\x1a\xc5\xf1E\x96\x8a\x01\x9b\xff/\xf5yh\xc0\x00\x00\u07d4\bPO\x05d?\xabY\x19\xf5\xee\xa5Y%\u05e3\xed}\x80z\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\b[J\xb7]\x83b\xd9\x14C\\\xed\xee\x1d\xaa+\x1e\xe1\xa2;\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\b[\xa6_\xeb\xe2>\xef\xc2\xc8\x02fj\xb1&#\x82\xcf\u0114\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\bt\x98\xc0FFh\xf3\x11P\xf4\xd3\u013c\u0765\"\x1b\xa1\x02\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\bw\uebabx\xd5\xc0\x0e\x83\xc3+-\x98\xfay\xadQH/\x89\x17\xd2-q\xdab&\x00\x00\u0794\b\x93j7\u07c5\xb3\xa1X\xca\xfd\x9d\xe0!\xf5\x817h\x13G\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\b\xa9\xa4N\x1fA\xde=\xbb\xa7\xa3c\xa3\xabA,\x12L\xd1^\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\b\xb7\xbd\u03d4MUp\x83\x8b\xe7\x04`$:\x86\x94HXX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xb8E6\xb7L\x8c\x01T=\xa8\x8b\x84\u05cb\xb9WG\xd8\"\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\b\xc2\xf26\xacJ\xdc\xd3\xfd\xa9\xfb\xc6\xe4S\"S\xf9\xda;\xec\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\b\xc8\x02\xf8wX4\x9f\xa0>k\xc2\xe2\xfd\a\x91\x19~\ua689lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xc9\U0007fd89\xfd\xf8\x04\xd7i\xf8!#6\x02\x15\xaf\xf9;\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\b\xca\u0215&A\xd8\xfcRn\xc1\xabO-\xf8&\xa5\xe7q\x0f\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\b\xcc\xdaP\xe4\xb2j\x0f\xfc\x0e\xf9.\x92\x051\a\x06\xbe\xc2\u01ca\x01Iul8W\xc6\x00\x00\x00\u07d4\b\u0406M\xc3/\x9a\xcb6\xbfN\xa4G\xe8\xddg&\x90j\x15\x89lnY\xe6|xT\x00\x00\u07d4\b\xd4&\u007f\xeb\x15\u0697\x00\xf7\xcc\xc3\xc8J\x89\x18\xbf\x17\xcf\u0789a\t=|,m8\x00\x00\xe0\x94\b\xd41\x1c\x9c\x1b\xba\xf8\u007f\xab\xe1\xa1\xd0\x14c\x82\x8d]\x98\u038a\x13\x0e\xe8\xe7\x17\x90D@\x00\x00\u07d4\b\xd5N\x83\xadHj\x93L\xfa\xea\u20e3>\xfd\"|\x0e\x99\x898S\x05\x83$^\xdc\x00\x00\u07d4\b\xd9~\xad\xfc\xb7\xb0d\xe1\xcc\xd9\u0217\x9f\xbe\xe5\xe7z\x97\x19\x89\x0el]\xa8\xd6z\xc1\x80\x00\u07d4\b\xda:z\x0fE!a\u03fc\xec1\x1b\xb6\x8e\xbf\xde\xe1~\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xe3\x8e\xe0\xceH\xc9\xcad\\\x10\x19\xf7;SUX\x1cV\xe6\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\b\xef?\xa4\xc4<\xcd\xc5{\"\xa4\xb9\xb23\x1a\x82\xe58\x18\xf2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\t\td\x8c\x18\xa3\xce[\xaez\x04~\xc2\xf8h\xd2L\u0768\x1d\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\t\f\xd6{`\xe8\x1dT\xe7\xb5\xf6\a\x8f>\x02\x1b\xa6[\x9a\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\f\xeb\xef),>\xb0\x81\xa0_\u062a\xf7\u04db\xf0{\x89\u0509\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\t\x0f\xa96{\xdaW\xd0\xd3%:\n\x8f\xf7l\xe0\xb8\xe1\x9as\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\x14n\xa3\x88Qv\xf0w\x82\xe1\xfe0\xdc\xe3\xce$\u011e\x1f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\t!`_\x99\x16N;\xcc(\xf3\x1c\xae\xcex\x971\x82V\x1d\x89+\ai*\x90e\xa8\x00\x00\xe0\x94\t&\x1f\x9a\xcbE\x1c7\x88\x84O\f\x14Q\xa3[\xadP\x98\xe3\x8a\x01\u056d'P) `\x00\x00\xe0\x94\t'\"\x04\x92\x19K.\u069f\u013b\xe3\x8f%\u0581\xdf\xd3l\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\t*\xcbbK\b\xc0U\x10\x18\x9b\xbb\xe2\x1ee$\xd6D\u032d\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\t.\x81UX@-g\xf9\rk\xfem\xa0\xb2\xff\xfa\x91EZ\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\tP0\xe4\xb8&\x92\xdc\xf8\xb8\u0411$\x94\xb9\xb3x\xec\x93(\x89H\xa4zu\x80\x00\u07d4\t\x89\xc2\x00D\v\x87\x89\x91\xb6\x9d`\x95\xdf\xe6\x9e3\xa2.p\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\t\x90\xe8\x1c\u05c5Y\x9e\xa26\xbd\x19f\xcfRc\x02\xc3[\x9c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\x98\xd8'1\x15\xb5j\xf4%\xff\xc8>!\x8c\x1e\n\xfe\x89(\u01c8\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\t\xaeI\xe3\u007f\x12\x1d\xf5\xdc\x15\x8c\xfd\xe8\x06\xf1s\xa0k\f\u007f\x89\xd80\x9e&\xab\xa1\xd0\x00\x00\u07d4\t\xaf\xa7;\xc0G\xefF\xb9w\xfd\x97c\xf8r\x86\xa6\xbeh\u0189\x1b/\xb5\xe8\xf0jf\x00\x00\u07d4\t\xb4f\x86\x96\xf8j\b\x0f\x8b\xeb\xb9\x1d\xb8\xe6\xf8p\x15\x91Z\x89#\x8f\xf7\xb3O`\x01\x00\x00\xe0\x94\t\xb5\x9b\x86\x98\xa7\xfb\xd3\xd2\xf8\xc7:\x00\x89\x88\xde>@k+\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94\t\xb7\xa9\x88\xd1?\xf8\x91\x86so\x03\xfd\xf4au\xb5=\x16\xe0\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\t\xc1w\xf1\xaeD$\x11\u076c\xf1\x87\xd4m\xb9V\x14\x83`\xe7\x8a\x01\xe5.3l\xde\"\x18\x00\x00\xe0\x94\t\u020f\x91~Mj\xd4s\xfa\x12\u93a3\xc4G*^\xd6\u068a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\t\u0438\xcd\a|i\xd9\xf3-\x9c\xcaC\xb3\xc2\b\xa2\x1e\u050b\x89\b!\xd2!\xb5)\x1f\x80\x00\xe0\x94\t\xd6\xce\xfdu\xb0\u0133\xf8\xf1\u0587\xa5\"\xc9a#\xf1\xf59\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\t\xe47\xd4H\x86\x12(\xa22\xb6.\xe8\xd3ye\xa9\x04\ud70a\x04\x98\xcf@\x1d\xf8\x84.\x80\x00\u07d4\t\xee\x12\xb1\xb4+\x05\xaf\x9c\xf2\a\xd5\xfc\xac%[.\xc4\x11\xf2\x89\x031\xcd\xddG\xe0\xfe\x80\x00\u07d4\t\xf3\xf6\x01\xf6\x05D\x11@Xl\xe0eo\xa2J\xa5\xb1\u066e\x89Sswo\xe8\xc4T\x00\x00\u07d4\t\xf9W[\xe5}\x00G\x93\u01e4\ub137\x15\x87\xf9|\xbbj\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n\x06P\x86\x1fx^\xd8\xe4\xbf\x10\x05\xc4P\xbb\xd0n\xb4\x8f\xb6\x89\xa6A;y\x14N~\x00\x00\u07d4\n\x06\xfa\xd7\xdc\u05e4\x92\xcb\xc0S\xee\xab\xdei4\xb3\x9d\x867\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\n\a}\xb1?\xfe\xb0\x94\x84\xc2\x17p\x9dX\x86\xb8\xbf\x9cZ\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\n\x0e\u0366cow\x16\xef\x19saF\x87\xfd\x89\xa8 \xa7\x06\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\n)\xa8\xa4\xd5\xfd\x95\x00u\xff\xb3Mw\xaf\xeb-\x82;\u0589\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n*\u0795\xb2\xe8\xc6m\x8a\xe6\xf0\xbad\xcaW\u05c3\xbemD\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\n+O\xc5\xd8\x1a\xceg\xdcK\xba\x03\xf7\xb4UA=F\xfe=\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\n-\xcbzg\x17\x01\u06f8\xf4\x95r\x80\x88&Xs5l\x8e\x89\b?\x16\xce\b\xa0l\x00\x00\u07d4\n=\xe1U\xd5\xec\xd8\xe8\x1c\x1f\xf9\xbb\xf07\x83\x01\xf8\xd4\xc6#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\nG\xad\x90Y\xa2I\xfc\x93k&b5=\xa6\x90_u\u00b9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\nH)ov1p\x8c\x95\u04b7Iu\xbcJ\xb8\x8a\xc19*\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\nJ\x01\x19\x95\u0181\xbc\x99\x9f\xddyuN\x9a2J\xe3\xb3y\x8a\b\xc1\x9a\xb0n\xb8\x9a\xf6\x00\x00\u07d4\nX\xfd\xddq\x89\x8d\xe7s\xa7O\xda\xe4^{\xd8N\xf46F\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\n[y\xd8\xf2;d\x83\xdb\u2f6ab\xb1\x06L\xc7cf\xae\x89j\u0202\x10\tR\u01c0\x00\u07d4\ne.*\x8bw\xbd\x97\xa7\x90\xd0\xe9\x13a\u0248\x90\u06f0N\x8965\u026d\xc5\u07a0\x00\x00\u07d4\nn\xber;n\xd1\xf9\xa8ji\xdd\xdah\xdcGF\\+\x1b\x89@=-\xb5\x99\xd5\xe4\x00\x00\u07d4\nw\xe7\xf7+C{WO\x00\x12\x8b!\xf2\xac&Q3R\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\n\x91\u007f;\\\xb0\xb8\x83\x04\u007f\u0676Y=\xbc\xd5W\xf4S\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\n\x93\x1bD\x9e\xa8\xf1,\xdb\xd5\xe2\xc8\xccv\xba\xd2\xc2|\x069\x89\x01?\x9e\x8cy\xfe\x05\x80\x00\u0794\n\x98\x04\x13x\x03\xbahh\xd9:U\xf9\x98_\xcdT\x04Q\u4239\x8b\xc8)\xa6\xf9\x00\x00\u07d4\n\x9a\xb2c\x8b\x1c\xfdeM%\u06b0\x18\xa0\xae\xbd\u07c5\xfdU\x89\x01.\x8c\xb5\xfeLJ\x80\x00\u07d4\n\xb3f\xe6\xe7\u056b\xbc\xe6\xb4JC\x8di\xa1\u02bb\x90\xd13\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\n\xb4(\x1e\xbb1\x85\x90\xab\xb8\x9a\x81\xdf\a\xfa:\xf9\x04%\x8a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\n\xb5\x9d9\a\x02\xc9\xc0Y\xdb\x14\x8e\xb4\xf3\xfc\xfa}\x04\xc7\xe7\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\n\xbf\xb3\x9b\x11HmyW(f\x19[\xa2lc\vg\x84\u06ca\x19\xba\x877\xf9i(\xf0\x00\x00\u07d4\n\u029aV&\x91;\b\xcf\u0266m@P\x8d\xceR\xb6\x0f\x87\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\n\xd3\xe4M<\x00\x1f\xa2\x90\xb3\x93ap0TA\b\xacn\xb9\x89j\xbd\xa0\xbc0\xb2\u07c0\x00\u07d4\n\xec.Bn\xd6\xcc\f\xf3\xc2I\xc1\x89~\xacG\xa7\xfa\xa9\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n\xf6_\x14xNU\xa6\xf9Vg\xfds%*\x1c\x94\a-*\x89\nv;\x8e\x02\xd4O\x80\x00\u07d4\n\xf6\xc8\xd59\xc9mP%\x9e\x1b\xa6q\x9e\x9c\x80`\xf3\x88\u008965\u026d\xc5\u07a0\x00\x00\u07d4\v\x069\x0f$7\xb2\x0e\u0123\xd3C\x1b2y\xc6X>^\u05c9\n\x84Jt$\xd9\xc8\x00\x00\u07d4\v\v8b\x11*\xee\u00e04\x92\xb1\xb0_D\x0e\xcaT%n\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\v\x0e\x05[(\xcb\xd0=\xc5\xffD\xaad\xf3\xdc\xe0O^c\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\x11\x9d\xf9\x9ck\x8d\xe5\x8a\x1e,?)zgD\xbfU\"w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\x14\x89\x19\x99\xa6\\\x9e\xf73\b\xef\xe3\x10\f\xa1\xb2\x0e\x81\x92\x89+^:\xf1k\x18\x80\x00\x00\u07d4\v!\x13PE4d*\x1d\xaf\x10.\xee\x10\xb9\xeb\xdev\xe2a\x89\x94,\xdd|\x95\xf2\xbd\x80\x00\xe0\x94\v(\x8aZ\x8bu\xf3\xdcA\x91\xeb\x04W\xe1\xc8=\xbd M%\x8a\x01\a\x14\xe7{\xb4:\xb4\x00\x00\u07d4\v6\x9e\x00.\x1bLy\x13\xfc\xf0\x0f-^\x19\u0141eG\x8f\x89\x03\u007fe\x16(\x8c4\x00\x00\u07d4\vC\xbd#\x91\x02U\x81\u0615l\xe4*\a%y\u02ff\xcb\x14\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u07d4\vP|\xf5SV\x8d\xaa\xf6U\x04\xaeN\xaa\x17\xa8\xea<\xdb\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v]f\xb1<\x87\xb3\x92\xe9M\x91\xd5\xf7l\rE\nU(C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v^ \x11\xeb\xc2Z\x00\u007f!6)`I\x8a\xfb\x8a\xf2\x80\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vd\x9d\xa3\xb9j\x10,\xdcm\xb6R\xa0\xc0}e\xb1\xe4C\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vi \xa6K6;\x8d]\x90\x80$\x94\xcfVKT|C\r\x89A\rXj \xa4\xc0\x00\x00\u07d4\vp\x11\x01\xa4\x10\x9f\x9c\xb3`\xdcW\xb7tBg=^Y\x83\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vq\xf5T\x12$i\uf5ce/\x1f\xef\xd7\u02f4\x10\x98'r\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\xe0\x94\v{\xb3B\xf0\x1b\u0248\x8ej\x9a\xf4\xa8\x87\xcb\xf4\xc2\xdd,\xaf\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\v}3\x93q\xe5\xbeg'\xe6\xe31\xb5\x82\x1f\xa2K\u06ddZ\x89.\u007f\x81\x86\x82b\x01\x00\x00\u07d4\v\u007f\xc9\xdd\xf7\x05v\xf63\x06i\xea\xaaq\xb6\xa81\xe9\x95(\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\v\x80\xfcp(,\xbd\xd5\xfd\xe3[\xf7\x89\x84\xdb;\xdb\x12\x01\x88\x8968\x02\x1c\xec\u06b0\x00\x00\u07d4\v\x92M\xf0\a\xe9\xc0\x87\x84\x17\xcf\xe6;\x97n\xa1\xa3\x82\xa8\x97\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\v\x93\xfc\xa4\xa4\xf0\x9c\xac \xdb`\xe0e\xed\xcc\xcc\x11\u0976\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\v\x9d\xf8\x0f\xbe# \t\xda\xcf\n\xa8\xca\u0153v\xe2Gb\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xa6\xe4j\xf2Z\x13\xf5qi%Z4\xa4\xda\xc7\xce\x12\xbe\x04\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\v\xa8p[\xf5\\\xf2\x19\xc0\x95k^?\xc0\x1cDt\xa6\xcd\xc1\x89\x05%\xe0Y]Mk\x80\x00\u07d4\v\xafn\u0379\x1a\xcb6\x06\xa85|\v\xc4\xf4\\\xfd-~o\x8965\u026d\xc5\u07a0\x00\x00\u07d4\v\xb0_r$\xbbX\x04\x85eV\xc0~\xea\xdb\ud1fa\x8f|\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\v\xb0\xc1&\x82\xa2\xf1\\\x9bWA\xb28\\\xbeA\xf04\x06\x8e\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\v\xb2\\\xa7\u0448\xe7\x1eMi={\x17\a\x17\xd6\xf8\xf0\xa7\n\x89\x12C\x02\xa8/\xad\xd7\x00\x00\u07d4\v\xb2e\x0e\xa0\x1a\xcau[\xc0\xc0\x17\xb6K\x1a\xb5\xa6m\x82\xe3\x89Hz\x9a0E9D\x00\x00\u07d4\v\xb5Lr\xfdf\x10\xbf\xa463\x97\xe0 8K\x02+\fI\x89Hz\x9a0E9D\x00\x00\u07d4\v\xb7\x16\n\xba)7b\xf8sO>\x03&\xff\u0264\xca\xc1\x90\x8965\u026d\xc5\u07a0\x00\x00\u07d4\v\xc9\\\xb3-\xbbWL\x83/\xa8\x17J\x815m8\xbc\x92\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xd6}\xbd\xe0z\x85n\xbd\x89;^\xdcO:[\xe4 &\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xdb\xc5L\u023d\xbb\xb4\x02\xa0\x89\x11\xe2#*T`\u0386k\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\v\xddX\xb9n|\x91m\xd2\xfb05o*\xeb\xfa\xaf\x1d\x860\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\v\u1f39\x03C\xfa\xe501s\xf4a\xbd\x91JH9\x05l\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\v\xe1\xfd\xf6&\xeea\x89\x10-p\xd1;1\x01,\x95\xcd\x1c\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xe2\xb9J\xd9P\xa2\xa6&@\xc3[\xfc\xcdlg\xda\xe4P\xf6\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\v\u681eC\a\xfeH\xd4\x12\xb8\u0461\xa8(M\xceHba\x8a\x04\x0f\xbf\xf8\\\x0180\x00\x00\u07d4\v\xef\xb5G\a\xf6\x1b,\x9f\xb0G\x15\xab\x02n\x1b\xb7 B\xbd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\v\xf0dB\x8f\x83bg\"\xa7\xb5\xb2j\x9a\xb2\x04!\xa7r>\x89\a?u\u0460\x85\xba\x00\x00\u07d4\v\xfb\xb6\x92]\xc7^R\xcf&\x84\"K\xbe\x05P\xfe\xa6\x85\u04c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\f\b\x80\x06\xc6K0\xc4\u076f\xbc6\xcb_\x05F\x9e\xb6(4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f s\xbaD\xd3\u077d\xb69\xc0N\x19\x109\xa7\x17\x16#\u007f\x89M\x85<\x8f\x89\b\x98\x00\x00\xe0\x94\f\",|A\u0270H\xef\xcc\xe0\xa22CCb\xe1-g;\x8a\x02\x1e\x83Yivw8\x00\x00\xe0\x94\f(\b\xb9Q\ud787-{2y\x0f\xccY\x94\xaeA\xff\u070a\x15\x99n[<\u05b3\xc0\x00\x00\u07d4\f(\x84~O\t\xdf\xce_\x9b%\xaf|NS\x0fY\u0200\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f-\\\x92\x058\xe9S\u02af$\xf0s\u007fUL\u0192wB\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f0\xca\xcc?r&\x9f\x8bO\x04\xcf\a=+\x05\xa8=\x9a\u0449lyt\x12?d\xa4\x00\x00\u07d4\f29\xe2\xe8A$-\xb9\x89\xa6\x15\x18\xc2\"G\xe8\xc5R\b\x89\x0eJ\xf6G\x174d\x00\x00\xe0\x94\fH\r\xe9\xf7F\x10\x02\x90\x8bI\xf6\x0f\xc6\x1e+b\xd3\x14\v\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\fH\xaeb\xd1S\x97\x88\xeb\xa0\x13\xd7^\xa6\vd\xee\xbaN\x80\x89w\xfb\xdcC\xe00\x99\x80\x00\u07d4\fU\x89\xa7\xa8\x9b\x9a\xd1[\x02u\x190AYH\xa8u\xfb\xef\x89\x06\u0519\xeclc8\x00\x00\u07d4\fg\x03=\xd8\xee\u007f\f\x8a\xe54\xd4*Q\xf7\xd9\xd4\xf7\x97\x8f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\fhE\xbfA\xd5\xee'<>\u6d70\u059fo\xd5\xea\xbb\xf7\x89\xa2\xa1\xb9h.X\t\x00\x00\xe0\x94\f\u007f\x86\x9f\x8e\x90\xd5?\xdc\x03\u8c81\x9b\x01k\x9d\x18\xeb&\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\f\x86\x92\xee\xff*S\xd6\xd1h\x8e\xd5j\x9d\u06fdh\u06bb\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f\x8ff\xc6\x01{\xce[ 4r\x04\xb6\x02\xb7C\xba\u05cd`\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\f\x8f\xd7w^T\xa6\xd9\u0263\xbf\x89\x0ev\x1fewi?\xf0\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\f\x92Z\xd5\xeb5,\x8e\xf7m\f\"-\x11[\a\x91\xb9b\xa1\x89\xacc]\u007f\xa3N0\x00\x00\u07d4\f\x96~0a\xb8zu>\x84P~\xb6\t\x86x,\x8f0\x13\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\f\xa1*\xb0\xb9fl\xf0\xce\xc6g\x1a\x15)/&SGj\xb2\x8a,x'\xc4-\"\xd0|\x00\x00\u07d4\f\xa6p\xeb,\x8b\x96\u02e3y!\u007fY)\u00b8\x92\xf3\x9e\xf6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f\xae\x10\x8em\xb9\x9b\x9ecxv\xb0d\xc60>\u068ae\u0209\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\f\xbd\x92\x1d\xbe\x12\x15c\xb9\x8ahq\xfe\xcb\x14\xf1\xcc~\x88\u05c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\f\xbf\x87p\xf0\xd1\b.\\ \u016e\xad4\xe5\xfc\xa9\xaez\xe2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f\xc6\u007f\x82s\xe1\xba\xe0\x86\u007f\xd4.\x8b\x81\x93\xd7&y\xdb\xf8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\f\u05a1A\x91\x8d\x12k\x10m\x9f.\xbfi\xe1\x02\xdeM2w\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\f\xda\x12\xbfr\xd4a\xbb\xc4y\xeb\x92\xe6I\x1d\x05~kZ\u044a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\f\u0716\v\x99\x8c\x14\x19\x98\x16\r\xc1y\xb3l\x15\u0484p\xed\x89\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\f\xfb\x17#5\xb1l\x87\xd5\x19\xcd\x14uS\r W\u007f^\x0e\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\r\x1f*Wq>\xbcn\x94\xde)\x84n\x88D\xd3vfWc\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\r2e\xd3\u7f79=^\x8e\x8b\x1c\xa4\u007f!\ny>\u030e\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\r5@\x8f\"ef\x11o\xb8\xac\u06a9\xe2\xc9\u055bvh?\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\rU\x1e\xc1\xa2\x13<\x98\x1d_\u01a8\xc8\x17?\x9e|OG\xaf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r]\x98V\\d|\xa5\xf1w\xa2\xad\xb9\xd3\x02/\xac(\u007f!\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\re\x80\x14\xa1\x99\x06\x1c\xf6\xb3\x943\x14\x03\x03\xc2\x0f\xfdNZ\x8a\x01\xbc\x85\xdc*\x89\xbb \x00\x00\u07d4\rg\x87\x06\xd07\x18\u007f>\"\xe6\xf6\x9b\x99\xa5\x92\xd1\x1e\xbcY\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\ri\x10\f9\\\xe6\xc5\xea\xad\xf9]\x05\xd8r\x83~\xde\xdd!\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\rt~\u559b\xf7\x9dW8\x1do\xe3\xa2@l\xd0\xd8\xce'\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\r\x80#\x92\x9d\x91r4\xae@Q+\x1a\xab\xb5\xe8\xa4Q'q\x89\b\x05\xe9\x9f\xdc\xc5\xd0\x00\x00\xe0\x94\r\x8a\xab\x8ft\xea\x86,\xdfvh\x05\x00\x9d?>B\xd8\xd0\v\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\r\x8c@\xa7\x9e\x18\x99O\xf9\x9e\xc2Q\xee\x10\u0408\u00d1.\x80\x89\x066d\xfc\u04bb\xc4\x00\x00\u07d4\r\x8e\xd7\xd0\xd1V83\x0e\xd7\xe4\xea\u032b\x8aE\x8dus~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r\x92X/\u06e0^\xab\xc3\xe5\x158\xc5m\xb8\x817\x85\xb3(\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\r\x94C\xa7\x94h\xa5\xbb\xf7\xc1\xe5\xb9\x15\xb3d\x87\xf9\x16\x1f\x19\x84m\x10\x1431\x8a\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\r\xbdA|7+\x8b\r\x01\xbc\xd9Dpk\xd3.`\xae(\u0449\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\r\xc1\x00\xb1\a\x01\x1c\u007f\xc0\xa13\x96\x12\xa1l\xce\xc3(R\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r\u03dd\x8c\x98\x04E\x9fd|\x14\x13\x8e\xd5\x0f\xadV;AT\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4\r\xcf\xe87\xea\x1c\xf2\x8ce\xfc\xce\u00fe\xf1\xf8NY\xd1P\xc0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\r\xd4\xe6t\xbb\xad\xb1\xb0\u0702D\x98q=\xce;QV\xda)\x89\t79SM(h\x00\x00\u07d4\r\xfb\u0501pP\xd9\x1d\x9db\\\x02\x05<\xf6\x1a>\xe2\x85r\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\x0e\x02N\u007f\x02\x9cj\xaf:\x8b\x91\x0f^\b\bs\xb8W\x95\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0e\tdl\x99\xafC\x8e\x99\xfa'L\xb2\xf9\xc8V\xcbe\xf76\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x0e\f\x9d\x00^\xa0\x16\u0095\xcdy\\\xc9!>\x87\xfe\xbc3\xeb\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\x0e\rf3\xdb\x1e\f\u007f#Jm\xf1c\xa1\x0e\n\xb3\x9c \x0f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x0e\x11\xd7z\x89w\xfa\xc3\r&\x84E\xe51\x14\x9b1T\x1a$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\x12=}\xa6\xd1\xe6\xfa\xc2\u072d\xd2p)$\v\xb3\x90R\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0e\x18\x01\xe7\vbb\x86\x1b\x114\u033c9\x1fV\x8a\xfc\x92\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x0e \x94\xac\x16T\xa4k\xa1\xc4\u04e4\v\xb8\xc1}\xa7\U000d6209\x13h?\u007f<\x15\xd8\x00\x00\u07d4\x0e!\xaf\x1b\x8d\xbf'\xfc\xf6?7\xe0G\xb8z\x82\\\xbe|'\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x0e.PJ-\x11\"\xb5\xa9\xfe\xee\\\xb1E\x1b\xf4\u00ac\xe8{\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x0e/\x8e(\xa6\x81\xf7|X;\xd0\xec\xde\x16cK\xdd~\x00\u0349\x05'8\xf6Y\xbc\xa2\x00\x00\u07d4\x0e2\x02\x19\x83\x8e\x85\x9b/\x9f\x18\xb7.=@s\xcaP\xb3}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e3\xfc\xbb\xc0\x03Q\v\xe3W\x85\xb5*\x9c]!k\xc0\x05\xf4\x89e\xea=\xb7UF`\x00\x00\u07d4\x0e6\x96\xcf\x1fB\x17\xb1c\u047c\x12\xa5\xeas\x0f\x1c2\xa1J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x0e9\x0fD\x05=\xdf\xce\xf0\xd6\b\xb3^M\x9c,\xbe\x98q\xbb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x0e:(\xc1\u07ef\xb0P[\xdc\xe1\x9f\xe0%\xf5\x06\xa6\xd0\x1c\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e=\xd7\xd4\xe4)\xfe90\xa6A@5\xf5+\xdcY\x9dxM\x89\x02,\xa3X|\xf4\xeb\x00\x00\u07d4\x0eGey\x03Rek\xc6Vh,$\xfc^\xf3\xe7j#\u01c9\x02\x86\xd7\xfc\f\xb4\xf5\x00\x00\u07d4\x0eI\x88\x00Dqw\xb8\u022f\xc3\xfd\xfa\u007fi\xf4\x05\x1b\xb6)\x89t\x05\xb6\x9b\x8d\xe5a\x00\x00\u07d4\x0ek\xaa\xa3\u07b9\x89\xf2\x89b\x00vf\x86\x18\xe9\xac3(e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x0el\xd6d\xad\x9c\x1e\xd6K\xf9\x87I\xf4\x06D\xb6&\xe3y,\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\xe0\x94\x0em\xfdU;.\x87=*\xec\x15\xbd_\xbb?\x84r\xd8\u04d4\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\x0en\xc3\x137bq\xdf\xf5T#\xabT\"\xcc:\x8b\x06\xb2+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x0en\u0399\x11\x1c\xad\x19a\xc7H\xed=\xf5\x1e\xddi\u04a3\xb1\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x0e\x83\xb8PH\x1a\xb4MI\xe0\xa2)\xa2\xe4d\x90,iS\x9b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0e\x89\xed\xdd?\xa0\xd7\x1d\x8a\xb0\xff\x8d\xa5X\x06\x86\xe3\xd4\xf7O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\x90\x96\xd3C\xc0`\xdbX\x1a\x12\x01\x12\xb2x`~\xc6\xe5+\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x0e\x9cQ\x18d\xa1w\xf4\x9b\xe7\x82\x02w?`H\x9f\xe0NR\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x0e\xa2\xa2\x101+>\x86~\xe0\xd1\xcch,\xe1\xd6f\xf1\x8e\u054a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x0e\xb1\x89\xef,-Wb\xa9c\u05b7\xbd\xf9i\x8e\xa8\u7d0a\x89Hz\x9a0E9D\x00\x00\xe0\x94\x0e\xb5\xb6b\xa1\xc7\x18`\x8f\xd5/\f%\xf97\x880\x17\x85\x19\x8a\x01J7(\x1aa.t\x00\x00\xe0\x94\x0e\xc4f\x96\xff\xac\x1fX\x00_\xa8C\x98$\xf0\x8e\xed\x1d\xf8\x9b\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x0e\xc5\n\xa8#\xf4e\xb9FK\v\xc0\u0125w$\xa5U\xf5\u058a\f\x83\xd1Bj\u01f1\xf0\x00\x00\u07d4\x0e\xc50\x8b1(.!\x8f\xc9\xe7Y\xd4\xfe\xc5\xdb7\b\xce\u01096C\xaady\x86\x04\x00\x00\u07d4\x0e\xcc\xf6\x17\x84O\xd6\x1f\xbab\xcb\x0eD[z\u018b\xcc\x1f\xbe\x89\x14\xfeO\xe65e\xc6\x00\x00\u07d4\x0e\u04fb:N\xb5T\xcf\u0297\x94}WU\a\xcd\xfdm!\u0609\x1d\xb3 _\xcc#\u0540\x00\u07d4\x0e\xd7l,;]P\xff\x8f\xb5\v>\xea\xcdh\x15\x90\xbe\x1c-\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0e\u0680\xf4\xed\aJ\xeaiz\xed\xdf(;c\xdb\xca=\xc4\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\xddKX\x0f\xf1\x0f\xe0lJ\x03\x11b9\xef\x96b+\xae5\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x0e\xe3\x91\xf0^\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x0f\x92\x9c\xf8\x95\xdb\x01z\xf7\x9f>\xad\"\x16\xb1\xbdi\xc3}\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f\xa0\x10\xce\fs\x1d;b\x8e6\xb9\x1fW\x13\x00\u477e\xab\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\x0f\xa5\xd8\u0173\xf2\x94\xef\u0515\xabi\xd7h\xf8\x18rP\x85H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f\xa6\u01f0\x97=\v\xae)@T\x0e$}6'\xe3|\xa3G\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0f\xad\x05P|\u070f$\xb2\xbeL\xb7\xfa]\x92}\u06d1\x1b\x88\x89\xa2\xdf\x13\xf4A\xf0\t\x80\x00\u07d4\x0f\xb5\xd2\xc6s\xbf\xb1\xdd\xca\x14\x1b\x98\x94\xfdm?\x05\xdag \x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0f\u0260\xe3AE\xfb\xfd\xd2\xc9\u04a4\x99\xb6\x17\u05e0)i\xb9\x89\t\xc2\x00vQ\xb2P\x00\x00\xe0\x94\x0f\xcf\xc4\x06P\b\xcf\xd3#0_b\x86\xb5zM\xd7\xee\xe2;\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x0f\xdde@#\x95\u07db\u045f\xeeE\a\xefSE\xf7E\x10L\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x0f\xecN\xe0\xd7\xca\x18\x02\x90\xb6\xbd \xf9\x99#B\xf6\x0f\xf6\x8d\x89\x12 \u007f\x0e\xdc\xe9q\x80\x00\u07d4\x0f\ue06c3\x1e\xfd\x8f\x81\x16\x1cW8+\xb4P{\xb9\xeb\xec\x89\x15\xaf\x88\r\x8c\u06c3\x00\x00\u07d4\x0f\xfe\xa0mq\x13\xfbj\xec(i\xf4\xa9\u07f0\x90\a\xfa\xce\xf4\x89\f8F\x81\xb1\xe1t\x00\x00\u07d4\x10\tq\x98\xb4\xe7\xee\x91\xff\x82\xcc/;\xd9_\xeds\xc5@\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x10\vM\tw\xfc\xba\xd4\u07bd^d\xa0Iz\xea\xe5\x16\x8f\xab\x89\x11\f\x90s\xb5$Z\x00\x00\xe0\x94\x10\x1a\nd\xf9\xaf\xccD\x8a\x8a\x13\rM\xfc\xbe\xe8\x957\xd8T\x8a\x037\xfe_\xea\xf2\u0440\x00\x00\u07d4\x10,G}i\xaa\u06e9\xa0\xb0\xf6+tY\xe1\u007f\xbb\x1c\x15a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x101\xe0\xec\xb5I\x85\xae!\xaf\x17\x93\x95\r\xc8\x11\x88\x8f\xde|\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x104d\x14\xbe\xc6\xd3\xdc\xc4NP\xe5MT\u00b8\xc3sN>\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x108\x98X\xb8\x00\xe8\xc0\xec2\xf5\x1e\xd6\x1a5YF\xcc@\x9b\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x10Y\xcb\xc6>6\xc4>\x88\xf3\x00\b\xac\xa7\xce\x05\x8e\ua816\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\x10n\xd5\xc7\x19\xb5&\x14w\x89\x04%\xaeuQ\xdcY\xbd%\\\x8a\x02\x89jX\xc9[\xe5\x88\x00\x00\u07d4\x10q\x1c=\xda21x\x85\xf0\xa2\xfd\x8a\xe9.\x82\x06\x9b\r\v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x10sy\xd4\xc4gFO#[\xc1\x8eU\x93\x8a\xad>h\x8a\u05c9\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x10v!-Ou\x8c\x8e\xc7\x12\x1c\x1c}t%I&E\x92\x84\x8a\ai[Y\xb5\xc1{L\x00\x00\u07d4\x10x\xd7\xf6\x1b\x0eV\xc7N\xe6c[.\x18\x19\xef\x1e=\x87\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x10z\x03\xcf\bB\xdb\u07b0a\x8f\xb5\x87\xcai\x18\x9e\xc9/\xf5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x10\x80\xc1\xd85\x8a\x15\xbc\x84\xda\xc8%\x89\u0392\xb9\x81\x89t\xc1\xfa\xb8\xad\xb4T\x00\x00\u07d4\x10\xe1\xe37x\x85\xc4-}\xf2\x18R.\xe7vh\x87\xc0^j\x89\x10C\xc4<\xde\x1d9\x80\x00\u07d4\x10\u342d+\xa3=\x82\xb3s\x88\u041cED\u01b0\"]\xe5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x10\xf4\xbf\xf0\u02a5\x02|\nj-\xcf\xc9R\x82M\xe2\x94\t\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11\x00\x1b\x89\xed\x87>:\xae\xc1\x15V4\xb4h\x16C\x98c#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x11\x027\u03d1\x17\xe7g\x92/\u0121\xb7\x8dyd\u0682\xdf \x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x11\x11\xe5\xdb\xf4^o\x90mb\x86o\x17\b\x10\x17\x88\xdd\xd5q\x89F{\xe6S>\xc2\xe4\x00\x00\xe0\x94\x11\x17+'\x8d\xddD\xee\xa2\xfd\xf4\xcb\x1d\x16\x96#\x91\xc4S\u064a\xc6/=\x9b\xfdH\x95\xf0\x00\x00\u07d4\x11&4\xb4\xec0\xffxn\x02AY\xf7\x96\xa5y9\xea\x14N\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x110l}WX\x867x\x0f\xc9\xfd\xe8\xe9\x8e\xcb\x00\x8f\x01d\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x116\x12\xbc;\xa0\xeeH\x98\xb4\x9d\xd2\x023\x90_/E\x8fb\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x11A_\xaba\xe0\xdf\u0539\x06v\x14\x1aUz\x86\x9b\xa0\xbd\xe9\x89o\x05\xb5\x9d; \x00\x00\x00\u07d4\x11L\xbb\xbfo\xb5*\xc4\x14\xbe~\xc6\x1f{\xb7\x14\x95\xce\x1d\xfa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x11L\xfe\xfeP\x17\r\xd9z\xe0\x8f\nDTIx\u0159T\x8d\x89.\u0207\xe7\xa1J\x1c\x00\x00\u07d4\x11a\b\xc1 \x84a.\xed\xa7\xa9=\xdc\xf8\xd2`.'\x9e\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11d\u02aa\x8c\u0157z\xfe\x1f\xad\x8a}`(\xce-W)\x9b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x11gZ%UF\a\xa3\xb6\xc9*\x9e\xe8\xf3ou\xed\xd3\xe36\x89\b\xa9\xab\xa5W\xe3l\x00\x00\u07d4\x11j\t\xdff\xcb\x15\x0e\x97W\x8e)\u007f\xb0n\x13\x04\f\x89<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11o\xef^`\x16B\xc9\x18\u02c9\x16\x0f\xc2);\xa7\x1d\xa96\x89+|\xc2\xe9\xc3\"\\\x00\x00\u07d4\x11xP\x1f\xf9J\xdd\x1cX\x81\xfe\x88a6\xf6\xdf\xdb\xe6\x1a\x94\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x11y\xc6\r\xbd\x06\x8b\x15\v\aM\xa4\xbe#\x03; \u0185X\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\x11}\x9a\xa3\xc4\xd1;\xee\x12\xc7P\x0f\t\xf5\xdd\x1cf\xc4e\x04\x89\v*\xd3\x04\x90\xb2x\x00\x00\xe0\x94\x11}\xb867\u007f\xe1TU\xe0,.\xbd\xa4\v\x1c\xebU\x1b\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x11\x8c\x18\xb2\xdc\xe1p\xe8\xf4Eu;\xa5\xd7Q<\xb7cm-\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\x11\x8f\xbdu;\x97\x929Z\xefzMx\xd2c\xcd\u02ab\xd4\xf7\x8963\x03\"\xd5#\x8c\x00\x00\xe0\x94\x11\x92\x83x\xd2}U\xc5 \xce\xed\xf2L\xeb\x1e\x82-\x89\r\xf0\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x11\x9a\xa6M[}\x18\x1d\xae\x9d<\xb4I\x95\\\x89\xc1\xf9c\xfa\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\x11\xc05\x8a\xa6G\x9d\xe2\x18f\xfe!\a\x19$\xb6^p\xf8\xb9\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\xe0\x94\x11\xd2$z\"\x1ep\xc2\xd6m\x17\xee\x13\x8d8\xc5_\xfb\x86@\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x11\u05c4JG\x1e\xf8\x9a\x8d\x87uUX<\xee\xbd\x149\xea&\x8a\x02#i\u6e80\u0188\x00\x00\u07d4\x11\xdda\x85\u0668\xd7=\xdf\u06a7\x1e\x9bwtC\x1cM\xfe\u008965\u026d\xc5\u07a0\x00\x00\u07d4\x11\xe7\x99~\u0750E\x03\xd7}\xa6\x03\x8a\xb0\xa4\xc84\xbb\xd5c\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\x11\xec\x00\xf8I\xb61\x9c\xf5\x1a\xa8\u074ff\xb3U)\xc0\xbew\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11\ufe22\x04Q\x16\x1bdJ\x8c\u03bb\xc1\xd3C\xa3\xbb\xcbR\x89\xadx\xeb\u016cb\x00\x00\x00\xe0\x94\x11\xfe\xfb]\xc1\xa4Y\x8a\xa7\x12d\fQwu\u07e1\xd9\x1f\x8c\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x12\x0f\x9d\xe6\xe0\xaf~\xc0*\a\xc6\t\u0284G\xf1W\xe64L\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x12\x10\xf8\v\u06c2l\x17Tb\xab\a\x16\xe6\x9eF\xc2J\xd0v\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x12\x13N\u007fk\x01{\xf4\x8e\x85Z9\x9c\xa5\x8e.\x89/\xa5\u020965\u026d\xc5\u07a0\x00\x00\u07d4\x12\x170t\x98\x01S\xae\xaaK\r\xcb\xc7\x13.\xad\xce\xc2\x1bd\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4\x12\x1f\x85[p\x14\x9a\xc84s\xb9po\xb4MG\x82\x8b\x98;\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x12'\xe1\nM\xbf\x9c\xac\xa3\x1b\x17\x80#\x9fUv\x15\xfc5\xc1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x12-\xcf\xd8\x1a\u0779}\x1a\x0eI%\u0135I\x80n\x9f;\xeb\x89R 5\xccn\x01!\x00\x00\u07d4\x12/V\x12%I\xd1h\xa5\xc5\xe2g\xf5&b\xe5\xc5\xcc\xe5\u0209\n\ad\a\xd3\xf7D\x00\x00\xe0\x94\x121o\xc7\xf1x\xea\xc2.\xb2\xb2Z\xed\xea\xdf=u\xd0\x01w\x8a\x04<3\xbe\x05\xf6\xbf\xb9\x80\x00\xe0\x94\x127Y\xf33\xe1>0i\xe2\x03KO\x059\x89\x18\x11\x9d6\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x12\\\xc5\xe4\xd5k+\xcc.\xe1\xc7\t\xfb\x9eh\xfb\x17t@\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x12c#\x88\xb2v^\xe4E+P\x16\x1d\x1f\xff\xd9\x1a\xb8\x1fJ\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\u07d4\x12h\x97\xa3\x11\xa1J\xd4;x\xe0\x92\x01\x00\xc4Bk\xfdk\u07494\xc7&\x89?-\x94\x80\x00\u07d4\x12m\x91\xf7\xad\x86\u07bb\x05W\xc6\x12\xca'n\xb7\xf9m\x00\xa1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x12}?\xc5\x00;\xf6<\r\x83\xe99W\x83e\x15\xfd'\x90E\x89\x06\x10\xc9\".nu\x00\x00\xe0\x94\x12}\xb1\xca\xdf\x1bw\x1c\xbdtu\xe1\xb2ri\x0fU\x8c\x85e\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x12\x84\xf0\xce\xe9\xd2\xff)\x89\xb6Ut\xd0o\xfd\x9a\xb0\xf7\xb8\x05\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x12\x8b\x90\x8f\xe7C\xa44 =\xe2\x94\xc4A\xc7\xe2\n\x86\xeag\x89&\xab\x14\xe0\xc0\xe1<\x00\x00\xe0\x94\x12\x93\u01cc}jD;\x9dt\xb0\xba^\xe7\xbbG\xfdA\x85\x88\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\x12\x96\xac\xde\xd1\xe0c\xaf9\xfe\x8b\xa0\xb4\xb6=\xf7\x89\xf7\x05\x17\x89\x05k\xf9\x1b\x1ae\xeb\x00\x00\u07d4\x12\xaa}\x86\xdd\xfb\xad0\x16\x92\xfe\xac\x8a\b\xf8A\xcb!\\7\x89\amA\xc6$\x94\x84\x00\x00\xe0\x94\x12\xaf\xbc\xba\x14'\xa6\xa3\x9e{\xa4\x84\x9fz\xb1\xc45\x8a\xc3\x1b\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x12\xb5\xe2\x89E\xbb)i\xf9\xc6Lc\xcc\x05\xb6\xf1\xf8\xd6\xf4\u054a\x01\xa2\x9e\x86\x91;t\x05\x00\x00\u0794\x12\u03cb\x0eFR\x13!\x1a[S\u07f0\xdd'\x1a(,\x12\u0248\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x12\xd2\a\x90\xb7\xd3\xdb\u060c\x81\xa2y\xb8\x12\x03\x9e\x8a`;\u0409V\xf9\x85\u04c6D\xb8\x00\x00\xe0\x94\x12\xd6\re\xb7\xd9\xfcH\x84\v\xe5\xf8\x91\xc7E\xcev\xeeP\x1e\x8a\x04\x85\xe58\x8d\fv\x84\x00\x00\u0794\x12\xd9\x1a\x92\xd7O\xc8a\xa7)dm\xb1\x92\xa1%\xb7\x9fSt\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\x12\u992d*\xd5t\x84\xddp\x05e\xbd\xdbFB;\u067d1\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x12\xf3,\n\x1f-\xaa\xb6v\xfei\xab\xd9\xe0\x185-L\xcdE\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x12\xf4`\xaedl\xd2x\x0f\xd3\\P\xa6\xafK\x9a\xcc\xfa\x85\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x12\xff\xc1\x12\x86\x05\xcb\f\x13p\x9ar\x90Po&\x90\x97q\x93\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x13\x03$F\xe7\xd6\x10\xaa\x00\xec\x8cV\u0275t\xd3l\xa1\xc0\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\x1cy,\x19}\x18\xbd\x04]p$\x93|\x1f\x84\xb6\x0fD8\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13\x1d\xf8\xd30\xeb|\xc7\x14}\nUWo\x05\u078d&\xa8\xb7\x89\n1\x06+\xee\xedp\x00\x00\u07d4\x13\x1f\xae\xd1%a\xbbz\xee\x04\xe5\x18Z\xf8\x02\xb1\xc3C\x8d\x9b\x89\v\xdf\x0e\u0733\x90\xc9\xc8V\b\xb7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13!\xcc\xf2\x979\xb9t\xe5\xa5\x16\xf1\x8f:\x846q\xe3\x96B\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13'\xd7Y\xd5n\n\xb8z\xf3~\xcfc\xfe\x01\xf3\x10\xbe\x10\n\x89#\xbc<\xdbh\xa1\x80\x00\x00\u07d4\x13)\xdd\x19\xcdK\xaa\x9f\xc6C\x10\xef\xec\xea\xb2!\x17%\x1f\x12\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x137\x1f\x92\xa5n\xa88\x1eC\x05\x9a\x95\x12\x8b\xdcMC\u0166\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x13O\x15\xe1\xe3\x9cSCY0\xaa\xed\xf3\xe0\xfeV\xfd\xe8C\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x13Ac\xbe\x9f\xbb\xe1\xc5in\xe2U\xe9\v\x13%C\x95\xc3\x18\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x13\\\xec\xd9U\xe5y\x83pv\x920\x15\x93\x03\u0671\x83\x9ff\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x13]\x17\x19\xbf\x03\xe3\xf8f1$y\xfe3\x81\x18\xcd8~p\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x13^\xb8\xc0\xe9\xe1\x01\xde\xed\xec\x11\xf2\xec\xdbf\xae\x1a\xae\x88g\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x13`\xe8}\xf2Li\xeemQ\xc7nsv\u007f\xfe\x19\xa2\x13\x1c\x89\x04\xfc\xc1\xa8\x90'\xf0\x00\x00\u07d4\x13l\x83K\xf1\x112m s\x95)[.X>\xa7\xf35r\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x13mKf+\xbd\x10\x80\xcf\xe4D[\x0f\xa2\x13\x86D5\xb7\xf1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13oI\a\u02b4\x1e'\bK\x98E\x06\x9f\xf2\xfd\f\x9a\xdey\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13t\xfa\xcd{?\x8dhd\x9d`\xd4U\x0e\xe6\x9f\xf0HA3\x89\x0e\x9e\xd6\xe1\x11r\xda\x00\x00\u07d4\x13|\xf3A\xe8Ql\x81X\x14\xeb\xcds\xe6V\x9a\xf1L\xf7\xbc\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x13\x84\x8bF\xeau\xbe\xb7\xea\xa8_Y\xd8f\xd7\u007f\xd2L\xf2\x1a\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x13\x9d51\u0252*\xd5bi\xf60\x9a\xa7\x89\xfb$\x85\xf9\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13\x9eG\x97d\xb4\x99\xd6f \x8cJ\x8a\x04z\x97\x041c\u0749 w!*\xffm\xf0\x00\x00\u07d4\x13\xa5\xee\xcb80]\xf9Iq\xef-\x9e\x17\x9a\xe6\u03ba\xb37\x89\x11\u3ac3\x95\xc6\xe8\x00\x00\u07d4\x13\xac\xad\xa8\x98\n\xff\xc7PI!\xbe\x84\xebID\xc8\xfb\xb2\xbd\x89V\u04aa:\\\t\xa0\x00\x00\u07d4\x13\xb9\xb1\a\x15qL\t\xcf\xd6\x10\u03dc\x98F\x05\x1c\xb1\xd5\x13\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x13\xce3-\xffe\xa6\xab\x938\x97X\x8a\xa2>\x00\t\x80\xfa\x82\x89\x0e\x02\x056\xf0(\xf0\x00\x00\u07d4\x13\xd6z~%\xf2\xb1,\u06c5XP\t\xf8\xac\u011b\x96s\x01\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x13\xde\xe0>7\x99\x95-\a8\x84=K\xe8\xfc\n\x80?\xb2\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\xe0/\xb4H\xd6\xc8J\xe1}\xb3\x10\xad(m\x05a`\u0695\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\xe3!r\x8c\x9cWb\x80X\xe9?\xc8f\xa02\xdd\v\u0690\x89&\xbc\xca#\xfe.\xa2\x00\x00\u07d4\x13\xec\x81\"\x84\x02n@\x9b\xc0f\xdf\xeb\xf9\u0564\xa2\xbf\x80\x1e\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\x14\x01)\xea\xa7f\xb5\xa2\x9f[:\xf2WND\t\xf8\xf6\xd3\xf1\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u07d4\x14\x05\x18\xa3\x19K\xad\x13P\xb8\x94\x9ee\x05e\u07bem\xb3\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14\x06\x85M\x14\x9e\b\x1a\xc0\x9c\xb4\xcaV\r\xa4c\xf3\x120Y\x89Hz\x9a0E9D\x00\x00\u07d4\x14\f\xa2\x8f\xf3;\x9ff\xd7\xf1\xfc\x00x\xf8\xc1\xee\xf6\x9a\x1b\xc0\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x14\x0f\xbaX\xdb\xc0H\x03\xd8L!0\xf0\x19x\xf9\xe0\xc71)\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\x14\x1a^9\xee/h\n`\x0f\xbfo\xa2\x97\u0790\xf3\"\\\u074a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x14%N\xa1&\xb5-\x01B\xda\n~\x18\x8c\xe2U\xd8\xc4qx\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\x14+\x87\xc5\x04?\xfbZ\x91\xdf\x18\xc2\xe1\t\xce\xd6\xfeJq\u06c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x14\x87\xf5\xa5$\u0288Q^\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4\x14\xa75 f6D\x04\xdbP\xf0\xd0\u05cduJ\"\x19\x8e\xf4\x89e\xea=\xb7UF`\x00\x00\u07d4\x14\xab\x16K;RL\x82\u05ab\xfb\xc0\u0783\x11&\xae\x8d\x13u\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14\xb1`>\xc6+ \x02 3\xee\xc4\xd6\xd6eZ\xc2J\x01Z\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x14\xc6;\xa2\u0731\xddM\xf3=\u06b1\x1cO\x00\a\xfa\x96\xa6-\x8a\x03HA\xb6\x05z\xfa\xb0\x00\x00\xe0\x94\x14\xcd\u077c\x8b\t\xe6gZ\x9e\x9e\x05\t\x1c\xb9\"8\u00de\x1e\x8a\x01\x14x\xb7\xc3\n\xbc0\x00\x00\u07d4\x14\xd0\n\xad9\xa0\xa7\u045c\xa0SP\xf7\xb07'\xf0\x8d\xd8.\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x14\xee\xc0\x9b\xf0>5+\xd6\xff\x1b\x1e\x87k\xe6d\xce\xff\xd0\u03c9\x01\x16\xdc:\x89\x94\xb3\x00\x00\u07d4\x14\xf2!\x15\x95\x18x;\u0127\x06go\xc4\xf3\xc5\xee@X)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x14\xfc\xd19\x1e}s/Avl\xda\u0344\xfa\x1d\xeb\x9f\xfd\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x0e=\xbc\xbc\xfc\x84\xcc\xf8\x9bsBwc\xa5e\xc2>`\u0409\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\x15\x18b{\x885\x1f\xed\xe7\x96\xd3\xf3\b3d\xfb\u0508{\f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u0794\x15\"J\xd1\xc0\xfa\xceF\xf9\xf5V\xe4wJ0%\xad\x06\xbdR\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x15/+\xd2)\xdd\xf3\xcb\x0f\xda\xf4U\xc1\x83 \x9c\x0e\x1e9\xa2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x15/N\x86\x0e\xf3\xee\x80jP'w\xa1\xb8\xdb\xc9\x1a\x90vh\x89 \x86\xac5\x10R`\x00\x00\u07d4\x15<\b\xaa\x8b\x96\xa6\x11\xefc\xc0%>*C4\x82\x9eW\x9d\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\x15<\xf2\x84,\xb9\u0787l'o\xa6Gg\u0468\xec\xf5s\xbb\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15>\xf5\x8a\x1e.z>\xb6\xb4Y\xa8\n\xb2\xa5G\xc9A\x82\xa2\x8a\x14T+\xa1*3|\x00\x00\x00\u07d4\x15DY\xfa/!1\x8e44D\x97\x89\xd8&\xcd\xc1W\f\xe5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15G\xb9\xbfz\xd6bt\xf3A8'#\x1b\xa4\x05\ue308\xc1\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\x15H\xb7p\xa5\x11\x8e\u0787\u06e2\xf6\x903\u007fam\u60eb\x89\x1c\x99V\x85\u0fc7\x00\x00\u07d4\x15R\x83P\xe0\xd9g\n.\xa2\u007f{J3\xb9\xc0\xf9b\x1d!\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x15[7y\xbbmV4./\u0681{[-\x81\xc7\xf4\x13'\x89\x02\xb8\xaa:\al\x9c\x00\x00\u07d4\x15e\xaf\x83~\xf3\xb0\xbdN+#V\x8dP#\xcd4\xb1d\x98\x89\x15Q\xe9rJ\u013a\x00\x00\u07d4\x15f\x91\x80\xde\u2558\x86\x9b\b\xa7!\xc7\xd2LL\x0e\xe6?\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x15r\xcd\xfa\xb7*\x01\u0396\x8ex\xf5\xb5D\x8d\xa2\x98S\xfb\u074a\x01\x12blI\x06\x0f\xa6\x00\x00\xe0\x94\x15uY\xad\xc5Wd\xccm\xf7\x93#\t%4\xe3\xd6dZf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x15x\xbd\xbc7\x1bM$8E3\x05V\xff\xf2\xd5\xefM\xffg\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x15~\xb3\xd3\x11;\u04f5\x97qM:\x95N\xdd\x01\x89\x82\xa5\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x84\xa2\xc0f\xb7\xa4U\xdb\u05ae(\a\xa73N\x83\xc3_\xa5\x89\a\f\x1c\xc7;\x00\xc8\x00\x00\u07d4\x15\x87F\x86\xb6s=\x10\xd7\x03\xc9\xf9\xbe\xc6\xc5.\xb8b\x8dg\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x8a\ra\x92S\xbfD2\xb5\xcd\x02\u01f8b\xf7\u00b7V6\x89\a[\xac|[\x12\x18\x80\x00\u07d4\x15\x98\x12y\x82\xf2\xf8\xad;k\x8f\xc3\xcf'\xbfax\x01\xba+\x89\t`\xdbwh\x1e\x94\x00\x00\xe0\x94\x15\x9a\xdc\xe2z\xa1\vG#d)\xa3JZ\xc4,\xad[d\x16\x8a\x06\xbf\x90\xa9n\xdb\xfaq\x80\x00\u07d4\x15\xa0\xae\xc3\u007f\xf9\xff=T\t\xf2\xa4\xf0\xc1!*\xac\xcb\x02\x96\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x15\xaaS\r\xc3iX\xb4\xed\xb3\x8e\xeem\xd9\xe3\xc7}L\x91E\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15\xac\xb6\x15h\xecJ\xf7\xea(\x198a\x81\xb1\x16\xa6\xc5\xeep\x8a\x06\x90\x83l\n\xf5\xf5`\x00\x00\u07d4\x15\xb9o0\xc2;\x86d\xe7I\x06Q\x06k\x00\xc49\x1f\xbf\x84\x89\x16B\xe9\xdfHv)\x00\x00\u07d4\x15\xc7\xed\xb8\x11\x8e\xe2{4\"\x85\xebY&\xb4z\x85[\u01e5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x15\u0654hPz\xa0A?\xb6\r\xca*\xdc\u007fV\x9c\xb3kT\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15\u06f4\x8c\x980\x97d\xf9\x9c\xed6\x92\xdc\xca5\xee0k\xac\x8a\x1f\u00c4+\xd1\xf0q\xc0\x00\x00\xe0\x94\x15\u072f\xcc+\xac\xe7\xb5[T\xc0\x1a\x1cQF&\xbfa\xeb\u060a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x15\u3d44\x05kb\xc9s\xcf^\xb0\x96\xf1s>T\xc1\\\x91\x892\xc7Z\x02#\xdd\xf3\x00\x00\u07d4\x15\xeb\xd1\xc7\xca\u04af\xf1\x92u\xc6W\xc4\xd8\b\xd0\x10\xef\xa0\xf5\x89\n\xdf0\xbap\u0217\x00\x00\u07d4\x15\xee\x0f\xc6>\xbf\x1b\x1f\u011d{\xb3\x8f\x88c\x82:.\x17\u0489g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x15\xf1\xb3R\x11\rh\x90\x1d\x8fg\xaa\xc4jl\xfa\xfe\x03\x14w\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x15\xf2\xb7\xb1d2\xeeP\xa5\xf5[A#/c4\xedX\xbd\xc0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x16\x01\x9aM\xaf\xabC\xf4\u067fAc\xfa\xe0\x84}\x84\x8a\xfc\xa2\x89\x01[\xc7\x019\xf7J\x00\x00\u07d4\x16\x02&\xef\xe7\xb5:\x8a\xf4b\xd1\x17\xa0\x10\x80\x89\xbd\xec\xc2\u0449\n\xdf0\xbap\u0217\x00\x00\u07d4\x16\f\xebo\x98\x0e\x041_S\xc4\xfc\x98\x8b+\xf6\x9e(M}\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\xe0\x94\x16\x1c\xafZ\x97*\u0383y\xa6\u0420J\xe6\xe1c\xfe!\xdf+\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x16\x1d&\xefgY\xba[\x9f \xfd\xcdf\xf1a2\xc3RA^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16!\x10\xf2\x9e\xac_}\x02\xb5C\xd8\xdc\u057bY\xa5\xe3;s\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x16+\xa5\x03'b\x14\xb5\t\xf9u\x86\xbd\x84!\x10\xd1\x03\xd5\x17\x8a\x01\xe7\xff\u0609\\\"h\x00\x00\u07d4\x16-v\xc2\xe6QJ:\xfbo\xe3\xd3\u02d3\xa3\\Z\xe7\x83\xf1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16;\xadJ\x12+E}d\xe8\x15\nA>\xaeM\a\x02>k\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u07d4\x16<\u023e\"vF\xcb\tq\x91Y\xf2\x8e\u041c]\xc0\xdc\xe0\x89Hz\x9a0E9D\x00\x00\u07d4\x16=\xcas\xd7\xd6\xea?>`b2*\x874\x18\f\vx\uf25ft \x03\xcb}\xfc\x00\x00\u07d4\x16Mz\xac>\xec\xba\uc86dQ\x91\xb7S\xf1s\xfe\x12\xec3\x89(VR\xb8\xa4hi\x00\x00\u07d4\x16Rl\x9e\u07d4>\xfaOm\x0f\v\xae\x81\xe1\x8b1\xc5@y\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x16S\x05\xb7\x872.%\xdcj\xd0\xce\xfelo3Fx\xd5i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16e\xab\x179\xd7\x11\x19\xeea2\xab\xbd\x92j'\x9f\xe6yH\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x16k\xf6\u06b2-\x84\x1bHl8\xe7\xbaj\xb3:\x14\x87\ud30a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x16v\x99\xf4\x8ax\xc6\x15Q%\x15s\x99X\x993\x12WO\a\x89\x02\x1d;\xd5^\x80<\x00\x00\u07d4\x16x\xc5\xf2\xa5\"92%\x19ca\x89OS\xccu/\xe2\xf3\x89h\xf3e\xae\xa1\xe4@\x00\x00\u07d4\x16|\xe7\xdee\xe8G\bYZRT\x97\xa3\xeb^ZfPs\x89\x1f1Gsfo\xc4\x00\x00\u07d4\x16~>:\xe2\x003HE\x93\x92\xf7\xdf\xceD\xaf|!\xadY\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x16\x80\xce\xc5\x02\x1e\xe90P\xf8\xae\x12rQ\x83\x9et\xc1\xf1\xfd\x8a\x02\xc6\x14a\xe5\xd7C\u0580\x00\u07d4\x16\x81j\xac\x0e\xde\r-<\xd4B\xday\xe0c\x88\x0f\x0f\x1dg\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x16\x8bP\x19\xb8\x18i\x16D\x83_\xe6\x9b\xf2)\xe1q\x12\xd5,\x8a\x05\xed\xe2\x0f\x01\xa4Y\x80\x00\x00\u07d4\x16\x8b\xde\xc8\x18\xea\xfcm)\x92\xe5\xefT\xaa\x0e\x16\x01\xe3\xc5a\x8967Pz0\xab\xeb\x00\x00\u07d4\x16\x8d0\xe5?\xa6\x81\t+R\xe9\xba\xe1Z\r\xcbA\xa8\u027b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x16\x9b\xbe\xfcA\xcf\xd7\xd7\u02f8\xdf\xc60 \xe9\xfb\x06\u0515F\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xa5\x8e\x98]\xcc\xd7\a\xa5\x94\u0453\xe7\u0327\x8b]\x02xI\x89I\xb9\u029aiC@\x00\x00\u07d4\x16\xa9\xe9\xb7:\u92c6M\x17(y\x8b\x87f\xdb\xc6\xea\x8d\x12\x893\xe7\xb4K\r\xb5\x04\x00\x00\u07d4\x16\xaaR\xcb\vUG#\xe7\x06\x0f!\xf3'\xb0\xa6\x83\x15\xfe\xa3\x89\r\x8drkqw\xa8\x00\x00\u07d4\x16\xab\xb8\xb0!\xa7\x10\xbd\u01ce\xa54\x94\xb2\x06\x14\xffN\xaf\xe8\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x16\xaf\xa7\x87\xfc\x9f\x94\xbd\xffiv\xb1\xa4/C\n\x8b\xf6\xfb\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xba\xe5\xd2N\xff\x91w\x8c\u064bM:\x1c\xc3\x16/D\xaaw\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\x16\xbc@!Z\xbb\u066e](\v\x95\xb8\x01\vE\x14\xff\x12\x92\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x16\xbeu\u9299Z9R\"\xd0\v\u05df\xf4\xb6\xe68\u144a\a\x9f\x90\\o\xd3N\x80\x00\x00\u07d4\x16\xc1\xbf[}\xc9\xc8<\x17\x9e\xfa\xcb\xcf.\xb1t\xe3V\x1c\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x16\u01f3\x1e\x8c7b\x82\xac\"qr\x8c1\xc9^5\xd9R\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xf3\x13\u03ca\xd0\x00\x91J\n\x17m\u01a44+y\xec%8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xff\xac\x84\x03)@\xf0\x12\x1a\tf\x8b\x85\x8a~y\xff\xa3\xbb\x89\xd2J\xdan\x10\x87\x11\x00\x00\xe0\x94\x17\x03\xb4\xb2\x92\xb8\xa9\xde\xdd\xed\xe8\x1b\xb2]\x89\x17\x9fdF\xb6\x8a\x04+e\xa4U\xe8\xb1h\x00\x00\u07d4\x17\x04\x93\x11\x10\x1d\x81~\xfb\x1de\x91\x0ff6b\xa6\x99\u024c\x89lh\xcc\u041b\x02,\x00\x00\u07d4\x17\x04\xce\xfc\xfb\x131\xeczx8\x8b)9>\x85\xc1\xafy\x16\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17\n\x88\xa8\x99\u007f\x92\xd287\x0f\x1a\xff\xde\xe64pP\xb0\x13\x89\xa2\xacw5\x14\x880\x00\x00\u07d4\x17\x10\x8d\xab,P\xf9\x9d\xe1\x10\u1cf3\xb4\u0342\xf5\xdf(\xe7\x895 ;g\xbc\xca\xd0\x00\x00\xe0\x94\x17\x12[Y\xacQ\xce\xe0)\xe4\xbdx\xd7\xf5\x94}\x1e\xa4\x9b\xb2\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4\x17\x1a\u0660K\xed\u0238a\xe8\xedK\xdd\xf5qx\x13\xb1\xbbH\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17\x1c\xa0*\x8bmb\xbfL\xa4~\x90i\x14\a\x98a\x97,\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x17\"\xc4\xcb\xe7\n\x94\xb6U\x9dBP\x84\xca\xee\xd4\xd6\xe6n!\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x17X\vvotSR\\\xa4\u01a8\x8b\x01\xb5\x05p\xea\b\x8c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x17X\x9al\x00jT\xca\xd7\x01\x03\x12:\xae\n\x82\x13_\u07b4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x17Z\x18::#_\xfb\xb0;\xa85gRg\"\x94\x17\xa0\x91\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x17_\xee\xea*\xa4\xe0\xef\xda\x12\xe1X\x8d/H2\x90\xed\xe8\x1a\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x17e6\x1c.\xc2\xf86\x16\u0383c\xaa\xe2\x10%\xf2Vo@\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x17gR\\_Z\"\xed\x80\xe9\xd4\xd7q\x0f\x03b\u049e\xfa3\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17v%`\xe8*\x93\xb3\xf5\"\xe0\xe5$\xad\xb8a,:tp\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x17}\xaex\xbc\x01\x13\xd8\u04dcD\x02\xf2\xa6A\xae*\x10Z\xb8\x89b\x92BV \xb4H\x00\x00\xe0\x94\x17\x84\x94\x8b\xf9\x98H\u021eDV8PM\u0598'\x1bY$\x8a\x01GLA\r\x87\xba\xee\x00\x00\u07d4\x17\x88\u069bW\xfd\x05\xed\xc4\xff\x99\xe7\xfe\xf3\x01Q\x9c\x8a\n\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\x8e\xafk\x85T\xc4]\xfd\xe1kx\xce\f\x15\u007f.\xe3\x13Q\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\x17\x96\x1dc;\xcf \xa7\xb0)\xa7\xd9K}\xf4\xda.\xc5B\u007f\x89\fo\xf0p\U000532c0\x00\u07d4\x17\x96\xbc\xc9{\x8a\xbcq\u007fKJ|k\x106\xea!\x82c\x9f\x89\x13A\xf9\x1c\xd8\xe3Q\x00\x00\u07d4\x17\x99=1*\xa1\x10iW\x86\x8fjU\xa5\xe8\xf1/w\xc8C\x89\x18e\xe8\x14\xf4\x14.\x80\x00\u07d4\x17\x9a\x82^\x0f\x1fn\x98S\tf\x84e\xcf\xfe\xd46\xf6\xae\xa9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x17\xb2\xd6\xcfe\xc6\xf4\xa3G\xdd\xc6W&U5M\x8aA+)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xb8\a\xaf\xa3\xdd\xd6G\xe7#T.{R\xfe\xe3\x95'\xf3\x06\x89\x15\xaf@\xff\xa7\xfc\x01\x00\x00\u07d4\x17\xc0G\x86W\xe1\xd3\xd1z\xaa3\x1d\xd4)\xce\u03d1\xf8\xae]\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\x17\xc0\xfe\xf6\x98l\xfb.@A\xf9\x97\x9d\x99@\xb6\x9d\xff=\xe2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x17\u0511\x8d\xfa\xc1]w\xc4\u007f\x9e\xd4\x00\xa8P\x19\rd\xf1Q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xd5!\xa8\xd9w\x90#\xf7\x16M#<;d \xff\xd2#\xed\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x17\xd91\xd4\xc5b\x94\u073ew\xc8e[\xe4i_\x00mJ<\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xdfIQ\x8ds\xb1)\xf0\xda6\xb1\u0274\f\xb6d \xfd\u01ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x17\xe4\xa0\xe5+\xac>\xe4N\xfe\tT\xe7S\u0538]dN\x05\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xe5\x84\xe8\x10\xe5gp,a\xd5]CK4\u0375\xee0\xf6\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x17\xe8.px\xdcO\xd9\xe8y\xfb\x8aPf\u007fS\xa5\xc5E\x91\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x17\xe8o;[0\xc0\xbaY\xf2\xb2\xe8XB[\xa8\x9f\n\x10\xb0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xee\x9fT\xd4\xdd\xc8Mg\x0e\xff\x11\xe5Je\x9f\xd7/DU\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94\x17\xefJ\xcc\x1b\xf1G\xe3&t\x9d\x10\xe6w\xdc\xff\xd7o\x9e\x06\x8a\bwQ\xf4\xe0\xe1\xb50\x00\x00\u07d4\x17\xf1F2\xa7\xe2\x82\v\xe6\xe8\xf6\u07c25X(=\xad\xab-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xf5#\xf1\x17\xbc\x9f\xe9x\xaaH\x1e\xb4\xf5V\x17\x117\x1b\u0209li\xf7>)\x13N\x00\x00\u07d4\x17\xfd\x9bU\x1a\x98\xcba\xc2\xe0\u007f\xbfA\xd3\xe8\u02650\u02e5\x89\x01v\x8c0\x81\x93\x04\x80\x00\u07d4\x18\x04x\xa6U\u05cd\x0f;\fO +aH[\xc4\x00/\u0549lk\x93[\x8b\xbd@\x00\x00\u07d4\x18\x13l\x9d\xf1g\xaa\x17\xb6\xf1\x8e\"\xa7\x02\u020fK\u0082E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x18\x15'\x9d\xff\x99R\xda;\xe8\xf7rI\xdb\xe2\"C7{\xe7\x8a\x01\x01|\xb7n{&d\x00\x00\u07d4\x18\x1f\xbb\xa8R\xa7\xf5\x01x\xb1\xc7\xf0>\xd9\xe5\x8dT\x16))\x89$\x1a\x9bOaz(\x00\x00\xe0\x94\x18'\x03\x9f\tW\x02\x94\b\x8f\xdd\xf0G\x16\\3\u65a4\x92\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\x18-\xb8R\x93\xf6\x06\u8248\xc3pL\xb3\xf0\xc0\xbb\xbf\xcaZ\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x18H\x00<%\xbf\u052a\x90\xe7\xfc\xb5\u05f1k\xcd\f\xff\xc0\u060965\u026d\xc5\u07a0\x00\x00\xe0\x94\x18JO\v\xebq\xff\xd5X\xa6\xb6\xe8\xf2(\xb7\x87\x96\xc4\xcf>\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94\x18M\x86\xf3Fj\xe6h;\x19r\x99\x82\xe7\xa7\u1903G\xb2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x18Q\xa0c\xcc\xdb0T\x90w\xf1\xd19\xe7-\xe7\x97\x11\x97\u0549lk\x93[\x8b\xbd@\x00\x00\u07d4\x18UF\xe8v\x8dPhs\x81\x8a\xc9u\x1c\x1f\x12\x11j;\xef\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x18X\xcf\x11\xae\xa7\x9fS\x98\xad+\xb2\"g\xb5\xa3\xc9R\xeat\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\xe0\x94\x18Z\u007f\u012c\xe3h\xd23\xe6 \xb2\xa4Y5f\x12\x92\xbd\xf2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x18d\xa3\u01f4\x81UD\x8cT\u020cp\x8f\x16g\tsm1\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x18j\xfd\xc0\x85\xf2\xa3\xdc\xe4a^\xdf\xfb\xad\xf7\x1a\x11x\x0fP\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x18k\x95\xf8\xe5\xef\xfd\xdc\xc9O\x1a1[\xf0)];\x1e\xa5\x88\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x18}\x9f\f\a\xf8\xebt\xfa\xaa\xd1^\xbc{\x80Dt\x17\xf7\x82\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x18\x95\xa0\xebJCrr/\xcb\u016f\xe6\x93o(\x9c\x88\xa4\x19\x891T\xc9r\x9d\x05x\x00\x00\u07d4\x18\x99\xf6\x9fe;\x05\xa5\xa6\xe8\x1fH\a\x11\u041b\xbf\x97X\x8c\x89i\xfb\x13=\xf7P\xac\x00\x00\u07d4\x18\xa6\xd2\xfcR\xbes\b@#\xc9\x18\x02\xf0[\xc2JK\xe0\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x18\xb0@|\xda\xd4\xceR`\x06#\xbd^\x1fj\x81\xaba\xf0&\x89\x11Q\xcc\xf0\xc6T\u0180\x00\u07d4\x18\xb8\xbc\xf9\x83!\xdaa\xfbN>\xac\xc1\xecT\x17'-\xc2~\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\x18\xc6r:gS)\x9c\xb9\x14G}\x04\xa3\xbd!\x8d\xf8\xc7u\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x18\xe1\x13\xd8\x17|i\x1aa\xbexXR\xfa[\xb4z\uef6f\x89Hz\x9a0E9D\x00\x00\xe0\x94\x18\xe4\xceGH;S\x04\n\u06eb5\x17,\x01\xefdPn\f\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\xe0\x94\x18\xe52C\x98\x1a\xab\xc8v}\xa1\fsD\x9f\x13\x91V\x0e\xaa\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x18\xfa\x86%\xc9\u0704>\x00\x15\x9e\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94\x193\xe34\xc4\x0f:\u02ed\f\v\x85\x11X i$\xbe\xca:\x8a\x01\x99^\xaf\x01\xb8\x96\x18\x80\x00\xe0\x94\x197\xc5\xc5\x15\x05uS\u033dF\u0546dU\xcef)\x02\x84\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\u07d4\x19:\xc6Q\x83e\x18\x00\xe25\x80\xf8\xf0\xea\u04fbY~\xb8\xa4\x89\x02\xb6*\xbc\xfb\x91\n\x00\x00\u07d4\x19=7\xed4}\x1c/N55\r\x9aDK\xc5|\xa4\xdbC\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\x19@\u0713d\xa8R\x16_GAN'\xf5\x00$E\xa4\xf1C\x8a\x02L-\xffj<|H\x00\x00\u07d4\x19E\xfe7\u007f\xe6\u0537\x1e>y\x1fo\x17\xdb$<\x9b\x8b\x0f\x89vy\u7fb9\x886\x00\x00\u07d4\x19Jk\xb3\x02\xb8\xab\xa7\xa5\xb5y\u07d3\xe0\xdf\x15t\x96v%\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x19L\ubd12\x98\x82\xbf;K\xf9\x86L+\x1b\x0fb\u0083\xf9\x89\x1e\xf8aS\x1ft\xaa\x00\x00\u07d4\x19O\xf4J\xef\xc1{\xd2\x0e\xfdz LG\xd1b\f\x86\xdb]\x89\xa2\x99\th\u007fj\xa4\x00\x00\xe0\x94\x19O\xfex\xbb\xf5\xd2\r\u044a\x1f\x01\xdaU.\x00\xb7\xb1\x1d\xb1\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x19S1>*\xd7F#\x9c\xb2'\x0fH\xaf4\u063b\x9cDe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19W\x1a+\x8f\x81\u01bc\xf6j\xb3\xa1\x00\x83)V\x17\x15\x00\x03\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\xe0\x94\x19h}\xaa9\xc3h\x13\x9bn{\xe6\r\xc1u:\x9f\f\xbe\xa3\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x19l\x02!\nE\n\xb0\xb3cpe_qz\xa8{\xd1\xc0\x04\x89\x0e\x10\xac\xe1W\xdb\xc0\x00\x00\u07d4\x19n\x85\xdf~s+J\x8f\x0e\xd06#\xf4\u06dd\xb0\xb8\xfa1\x89\x01%\xb9/\\\xef$\x80\x00\u07d4\x19s+\xf9s\x05]\xbd\x91\xa4S:\u06a2\x14\x9a\x91\u04c3\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19vr\xfd9\xd6\xf2F\xcef\xa7\x90\xd1:\xa9\"\xd7\x0e\xa1\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x19y\x8c\xbd\xa7\x15\ua69b\x9dj\xab\x94,U\x12\x1e\x98\xbf\x91\x89A\rXj \xa4\xc0\x00\x00\u07d4\x19\x8b\xfc\xf1\xb0z\xe3\b\xfa,\x02\x06\x9a\xc9\xda\xfeq5\xfbG\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x19\x8e\xf1\xec2Z\x96\xcc5Lrf\xa08\xbe\x8b\\U\x8fg\x8a\x80\xd1\xe47>\u007f!\xda\x00\x00\xe0\x94\x19\x91\x8a\xa0\x9e}IN\x98\xff\xa5\xdbP5\b\x92\xf7\x15j\u018a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x19\xb3k\f\x87\xeafN\xd8\x03\x18\xdcw\xb6\x88\xdd\xe8}\x95\xa5\x89i\x9fI\x98\x020=\x00\x00\u07d4\x19\u07d4E\xa8\x1c\x1b=\x80J\xea\xebon NB6f?\x89\x02\x06\xd9NjI\x87\x80\x00\u07d4\x19\xe5\u07a37\n,tj\xae4\xa3|S\x1fA\xda&N\x83\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x19\xe7\xf3\xeb{\xf6\u007f5\x99 \x9e\xbe\b\xb6*\xd32\u007f\x8c\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\x19\xe9Nb\x00P\xaa\xd7f\xb9\xe1\xba\xd91#\x83\x12\u053fI\x89\x81\xe3-\xf9r\xab\xf0\x00\x00\u07d4\x19\xec\xf2\xab\xf4\f\x9e\x85{%/\xe1\xdb\xfd=L]\x8f\x81n\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19\xf5\xca\xf4\xc4\x0ei\b\x81<\aE\xb0\xae\xa9Xm\x9d\xd91\x89#\xfe\xd9\xe1\xfa+`\x00\x00\u07d4\x19\xf6C\xe1\xa8\xfa\x04\xae\x16\x00`(\x13\x833\xa5\x9a\x96\u0787\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x19\xf9\x9f,\vF\u0389\x06\x87]\xc9\xf9\n\xe1\x04\xda\xe3U\x94\x89\xf4WZ]M\x16*\x00\x00\u07d4\x19\xff$O\xcf\xe3\xd4\xfa/O\u065f\x87\xe5[\xb3\x15\xb8\x1e\xb6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1a\x04\xce\xc4 \xadC\"\x15$mw\xfe\x17\x8d3\x9e\u0435\x95\x89\x11!a\x85\u009fp\x00\x00\xe0\x94\x1a\x04\xd58\x9e\xb0\x06\xf9\u0388\f0\xd1SS\xf8\xd1\x1cK1\x8a\x03\x9d\x84\xb2\x18m\xc9\x10\x00\x00\u07d4\x1a\bA\xb9*\u007fpuV\x9d\xc4b~kv\u02b0Z\u0791\x89Rf<\u02b1\xe1\xc0\x00\x00\xe0\x94\x1a\b]C\xec\x92AN\xa2{\x91O\xe7g\xb6\xd4k\x1e\xefD\x8a\x06A\xe8\xa15c\xd8\xf8\x00\x00\u07d4\x1a\t\xfd\xc2\u01e2\x0e#WK\x97\u019e\x93\u07bag\xd3r \x89lO\xd1\xee$nx\x00\x00\u07d4\x1a\n\x1d\u07f01\xe5\xc8\xcc\x1dF\xcf\x05\x84-P\xfd\xdcq0\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1a\x1c\x9a&\xe0\xe0$\x18\xa5\xcfh}\xa7Z'\\b,\x94@\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x1a \x1bC'\u03a7\xf3\x99\x04bF\xa3\xc8~n\x03\xa3\u0368\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a$4\xccwD\"\u050dS\u055c]V,\u0384\a\xc9K\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\x1a%\xe1\u017c~_P\xec\x16\xf8\x88_!\x0e\xa1\xb98\x80\x0e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x1a&\x94\xec\a\xcf^Mh\xba@\xf3\xe7\xa1LS\xf3\x03\x8cn\x8966\xcd\x06\xe2\xdb:\x80\x00\u07d4\x1a5 E5\x82\xc7\x18\xa2\x1cB7[\xc5\as%RS\xe1\x89*\xd3s\xcef\x8e\x98\x00\x00\xe0\x94\x1a7n\x1b-/Y\ai\xbb\x85\x8dEu2\rN\x14\x99p\x8a\x01\x06q%v9\x1d\x18\x00\x00\u07d4\x1a:3\x0eO\xcbi\xdb\xef^i\x01x;\xf5\x0f\xd1\xc1SB\x89\u3bb5sr@\xa0\x00\x00\u07d4\x1aN\u01a0\xae\u007fZ\x94'\xd2=\xb9rL\r\f\xff\xb2\xab/\x89\t\xb4\x1f\xbf\x9e\n\xec\x00\x00\u07d4\x1aP^b\xa7N\x87\xe5wG>O:\xfa\x16\xbe\xdd<\xfaR\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x1a^\xe53\xac\xbf\xb3\xa2\xd7m[hRw\xb7\x96\xc5j\x05+\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1adJP\xcb\u00ae\xe8#\xbd+\xf2C\xe8%\xbeMG\xdf\x02\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\x1apD\xe28?\x87\b0[I[\xd1\x17k\x92\xe7\xef\x04:\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1ay\xc7\xf4\x03\x9cg\xa3\x9du\x13\x88L\xdc\x0e,4\"$\x90\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1a\x89\x89\x9c\xbe\xbd\xbbd\xbb&\xa1\x95\xa6<\bI\x1f\u035e\xee\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1a\x8a\\\xe4\x14\u079c\xd1r\x93~7\xf2\u055c\xffq\xceW\xa0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1a\x95\xa8\xa8\b.FR\xe4\x17\r\xf9'\x1c\xb4\xbbC\x05\xf0\xb2\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x1a\x95\u0277Tk]\x17\x86\u00c5\x8f\xb1#dF\xbc\f\xa4\u0389j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1a\x98~?\x83\xdeu\xa4/\x1b\xde|\x99|\x19!{J_$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1a\x9ep/8]\xcd\x10^\x8b\x9f\xa4(\xee\xa2\x1cW\xffR\x8a\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1a\xa1\x02\x1fU\n\xf1X\xc7Gf\x8d\xd1;F1`\xf9Z@\x89O\xb0Y\x1b\x9b08\x00\x00\u07d4\x1a\xa2v\x99\xca\u068d\u00e7oy3\xaaf\xc7\x19\x19\x04\x0e\x88\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x1a\xa4\x02p\xd2\x1e\\\u0786\xb61m\x1a\xc3\xc53IKy\xed\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1a\xb5:\x11\xbc\xc6=\u07ea@\xa0+\x9e\x18d\x96\u037b\x8a\xff\x89l?*\xac\x80\f\x00\x00\x00\u07d4\x1a\xbcN%;\b\n\xebCy\x84\xab\x05\xbc\xa0\x97\x9a\xa4>\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a\xc0\x89\u00fcM\x82\xf0j \x05\x1a\x9ds-\xc0\xe74\xcba\x89%\xf6\x9dc\xa6\xce\x0e\x00\x00\xe0\x94\x1a\xd4V>\xa5xk\xe1\x15\x995\xab\xb0\xf1\u0547\x9c>sr\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1a\xd7- \xa7n\u007f\xcckv@X\xf4\x8dA}Io\xa6\u0349lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1a\xda\xf4\xab\xfa\x86}\xb1\u007f\x99\xafj\xbe\xbfpz<\xf5]\xf6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1a\xf6\x03C6\x0e\v-u%R\x107W \xdf!\xdb\\}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a\xfc\u0145\x89l\xd0\xed\xe1)\xee-\xe5\xc1\x9e\xa8\x11T\vd\x89\xaf*\xba\f\x8e[\xef\x80\x00\u07d4\x1b\x05\xeajj\u022f|\xb6\xa8\xb9\x11\xa8\xcc\xe8\xfe\x1a*\xcf\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\v1\xaf\xffKm\xf3e:\x94\xd7\xc8yx\xae5\xf3J\xae\x89\x139\x10E?\xa9\x84\x00\x00\u07d4\x1b\r\ah\x17\xe8\u058e\xe2\xdfN\x1d\xa1\xc1\x14-\x19\x8cD5\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\x1b\x13\ro\xa5\x1d\\H\xec\x8d\x1dR\u070a\"{\xe8s\\\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b#\u02c6cUHq\xfb\xbe\r\x9e`9~\xfbo\xae\xdc>\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1b&9X\x8bU\xc3D\xb0#\xe8\xde_\xd4\b{\x1f\x04\x03a\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x1b9 \xd0\x01\xc4>r\xb2N|\xa4o\x0f\xd6\xe0\xc2\n_\xf2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1b<\xb8\x1eQ\x01\x1bT\x9dx\xbfr\v\r\x92J\xc7c\xa7\u008av\x95\xa9, \xd6\xfe\x00\x00\x00\u07d4\x1bC#,\xcdH\x80\xd6\xf4o\xa7Q\xa9l\xd8$s1XA\x89\x04V9\x18$O@\x00\x00\u07d4\x1bK\xbc\xb1\x81e!\x1b&[(\a\x16\xcb?\x1f!!v\xe8\x89\x19\x9a\xd3}\x03\xd0`\x80\x00\u07d4\x1bM\a\xac\u04c1\x83\xa6\x1b\xb2x=+{\x17\x8d\xd5\x02\xac\x8d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1bckzIo\x04MsYYn5:\x10F\x16Cok\x89\x13\x88\xea\x95\xc3?\x1d\x00\x00\u07d4\x1bd\x95\x89\x12@\xe6NYD\x93\xc2f!q\xdb^0\xce\x13\x89\tX\x87\u0595\xedX\x00\x00\u07d4\x1bf\x10\xfbh\xba\xd6\xed\x1c\xfa\xa0\xbb\xe3:$\xeb.\x96\xfa\xfb\x89\b=lz\xabc`\x00\x00\u07d4\x1by\x903\xefm\xc7\x12x\"\xf7EB\xbb\"\xdb\xfc\t\xa3\b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x1b~\xd9t\xb6\xe24\u0381$t\x98B\x9a[\u0520\xa2\xd19\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\x82o\xb3\xc0\x12\xb0\xd1Y\u253a[\x8aI\x9f\xf3\xc0\xe0<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\x8a\xa0\x16\f\u05df\x00_\x88Q\nqI\x13\xd7\n\u04fe3\x89\n\xef\xfb\x83\a\x9a\xd0\x00\x00\xe0\x94\x1b\x8b\xd6\xd2\xec\xa2\x01\x85\xa7\x8e}\x98\xe8\xe1\x85g\x8d\xacH0\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\x1b\x9b-\u0096\x0eL\xb9@\x8ft\x05\x82|\x9bY\a\x16\x12\xfd\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1b\xa9\"\x8d8\x87'\xf3\x89\x15\x0e\xa0;s\xc8-\xe8\xeb.\t\x8a\x01\x89t\xfb\xe1w\xc9(\x00\x00\u07d4\x1b\xa9\xf7\x99~S\x87\xb6\xb2\xaa\x015\xac$R\xfe6\xb4\xc2\r\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\x1b\xba\x03\xffkJ\u057f\x18\x18J\xcb!\xb1\x88\xa3\x99\xe9\xebJ\x89a\t=|,m8\x00\x00\u07d4\x1b\xbc\x19\x9eXg\x90\xbe\x87\xaf\xed\xc8I\xc0G&t\\]{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x1b\xbc`\xbc\xc8\x0e\\\xdc5\xc5Aj\x1f\n@\xa8=\xae\x86{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xc4L\x87a#\x1b\xa1\xf1\x1f_\xaa@\xfaf\x9a\x01>\x12\u0389\v\tR\xc4Z\xea\xad\x00\x00\u07d4\x1b\xcf4A\xa8f\xbd\xbe\x960\t\xce3\xc8\x1c\xbb\x02a\xb0,\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\x1b\u048c\xd5\u01ca\xeeQ5|\x95\xc1\xef\x925\xe7\xc1\x8b\xc8T\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xd8\xeb\xaavt\xbb\x18\u1458\xdb$OW\x03\x13\a_C\x89\b!\xab\rD\x14\x98\x00\x00\u07d4\x1b\xd9\t\xac\rJ\x11\x02\xec\x98\xdc\xf2\u0329j\n\xdc\u05e9Q\x89\x01\x16Q\xac>zu\x80\x00\u07d4\x1b\xe3T,6\x13hte\xf1Zp\xae\xeb\x81f+e\u0328\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xeaM\xf5\x12/\xaf\u07b3`~\xdd\xda\x1e\xa4\xff\u06da\xbf*\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d4\x1b\xecM\x02\u0385\xfcH\xfe\xb6$\x89\x84\x1d\x85\xb1pXj\x9b\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\x1b\xf9t\u0650OE\u0381\xa8E\xe1\x1e\xf4\xcb\xcf'\xafq\x9e\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x1c\x04VI\xcdS\xdc#T\x1f\x8e\xd4\xd3A\x81(\b\xd5\u075c\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x1c\x12\x8b\xd6\u0365\xfc\xa2uu\xe4\xb4;2S\xc8\xc4\x17*\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\x13\u04c67\xb9\xa4|\xe7\x9d7\xa8oP\xfb@\x9c\x06\a(\x89Hz\x9a0E9D\x00\x00\u07d4\x1c \x10\xbdf-\xf4\x17\xf2\xa2q\x87\x9a\xfb\x13\xefL\x88\xa3\xae\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x1c%z\u0525Q\x05\xea;X\xed7K\x19\x8d\xa2f\xc8_c\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x1c.6\a\xe1'\xca\xca\x0f\xbd\\YH\xad\xad}\xd80\xb2\x85\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x1c5l\xfd\xb9_\xeb\xb7\x14c;(\xd5\xc12\u0744\xa9\xb46\x89\x01Z\xf1\u05cbX\xc4\x00\x00\u07d4\x1c5\xaa\xb6\x88\xa0\u034e\xf8.vT\x1b\xa7\xac9R\u007ft;\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x1c>\xf0]\xae\x9d\xcb\u0509\xf3\x02D\bf\x9d\xe2D\xc5*\x02\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x1cJ\xf0\xe8c\xd2el\x865\xbco\xfe\xc8\u0759(\x90\x8c\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c`\x19\x93x\x92\a\xf9e\xbb\x86\\\xbbL\xd6W\xcc\xe7o\xc0\x89\x05T\x1ap7P?\x00\x00\u07d4\x1cc\xfa\x9e,\xbb\xf21a\xda3\xa1\xda}\xf7\r\x1b\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1c\xb6\xb2\xd7\xcf\xc5Y\xb7\xf4\x1eoV\xab\x95\xc7\xc9X\xcd\x0eL\x89Hz\x9a0E9D\x00\x00\u07d4\x1c\xc1\xd3\xc1O\x0f\xb8d\x0e6rM\xc42)\xd2\xeaz\x1eH\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x1c\xc9\bv\x00A\t\xcdy\xa3\u07a8f\u02c4\n\xc3d\xba\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xd1\xf0\xa3\x14\u02f2\x00\xde\n\f\xb1\xef\x97\xe9 p\x9d\x97\u0089lk\x93[\x8b\xbd@\x00\x00\u0794\x1c\xdaA\x1b\xd5\x16;\xae\xca\x1eU\x85c`\x1c\xe7 \xe2N\xe1\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1c\xe8\x1d1\xa7\x920\"\xe1%\xbfH\xa3\xe06\x93\xb9\x8d\xc9\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xeb\xf0\x98]\u007fh\n\xaa\x91\\D\xccb\xed\xb4\x9e\xab&\x9e\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1c\xedg\x15\xf8b\xb1\xff\x86\x05\x82\x01\xfc\xceP\x82\xb3nb\xb2\x8a\x01j^`\xbe\xe2s\xb1\x00\x00\u07d4\x1c\xf0L\xb1C\x80\x05\x9e\xfd?#\x8be\u057e\xb8j\xfa\x14\u0609\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1c\xf1\x05\xab#\x02;ULX>\x86\u05d2\x11y\xee\x83\x16\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1c\xf2\xebz\x8c\xca\u00ad\xea\xef\x0e\xe8sG\xd55\u04f9@X\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xfc\xf7Q\u007f\f\bE\x97 \x94+dz\u0452\xaa\x9c\x88(\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\x1d\t\xad$\x12i\x1c\u0141\xc1\xab6\xb6\xf9CL\xd4\xf0\x8bT\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x1d\x15|Xv\xc5\xca\xd5S\xc9\x12\xca\xf6\xce-Rw\xe0\\s\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1d&\x15\xf8\xb6\xcaP\x12\xb6c\xbd\u0414\xb0\xc5\x13|w\x8d\u07ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1d)\u01ea\xb4+ H\u04b2R%\u0518\u06e6z\x03\xfb\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0794\x1d4\x1f\xa5\xa3\xa1\xbd\x05\x1f}\xb8\a\xb6\xdb/\u01faO\x9bE\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1d4N\x96%g\xcb'\xe4M\xb9\xf2\xfa\u01f6\x8d\xf1\xc1\xe6\xf7\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x1d6h0c\xb7\xe9\xeb\x99F-\xab\xd5i\xbd\xdc\xe7\x16\x86\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1d7aky?\x94\x91\x188\xac\x8e\x19\xee\x94I\u07d2\x1e\u0109QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94\x1d9[0\xad\xda\x1c\xf2\x1f\t\x1aOJ{u3q\x18\x94A\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x1dEXn\xb8\x03\xca!\x90e\v\xf7H\xa2\xb1t1+\xb5\a\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1dW.\xdd-\x87\xca'\x1ag\x14\xc1Z;7v\x1d\u0320\x05\x89\x06\xeb\xd5*\x8d\xdd9\x00\x00\u07d4\x1dc0\x97\xa8R%\xa1\xffC!\xb1)\x88\xfd\xd5\\+8D\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x1di\xc8=(\xff\x04t\xce\xeb\xea\xcb:\xd2'\xa1D\xec\u78ca\x01(\xcc\x03\x92\nb\u0480\x00\u07d4\x1d\x96\xbc\u0544W\xbb\xf1\xd3\u00a4o\xfa\xf1m\xbf}\x83hY\x89\tIr\t\xd8F~\x80\x00\u07d4\x1d\x9ej\xaf\x80\x19\xa0_#\x0e]\xef\x05\xaf]\x88\x9b\xd4\xd0\xf2\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x1d\xab\x17.\xff\xa6\xfb\xeeSL\x94\xb1~yN\xda\xc5OU\xf8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1d\xb9\xac\x9a\x9e\xae\xec\nR7W\x05\fq\xf4rx\xc7-P\x89Hz\x9a0E9D\x00\x00\u07d4\x1d\xbe\x8e\x1c+\x8a\x00\x9f\x85\xf1\xad<\xe8\r.\x055\x0e\u3709\aW\rn\x9e\xbb\xe4\x00\x00\u07d4\x1d\xc7\xf7\xda\xd8]\xf5?\x12q\x15$\x03\xf4\xe1\xe4\xfd\xb3\xaf\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1d\u03bc\xb7em\xf5\u072a3h\xa0U\xd2/\x9e\xd6\xcd\xd9@\x89\x1b\x18\x1eK\xf24<\x00\x00\xe0\x94\x1d\xd7tA\x84J\xfe\x9c\xc1\x8f\x15\xd8\xc7{\xcc\xfbe^\xe04\x8a\x01\x06\xebEW\x99D\x88\x00\x00\u07d4\x1d\xde\xfe\xfd5\xab\x8fe\x8b$q\xe5G\x90\xbc\x17\xaf\x98\u07a4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1d\xee\xc0\x1a\xbe\\\r\x95-\xe9\x10l=\xc3\x069\xd8P\x05\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\x1d\xf6\x91\x16rg\x9b\xb0\xef5\t\x03\x8c\f'\xe3\x94\xfd\xfe0\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\x1d\xfa\xee\ar\x12\xf1\xbe\xaf\x0eo/\x18@Sz\xe1T\xad\x86\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1e\x06\r\xc6\xc5\xf1\u02cc\xc7\xe1E.\x02\xee\x16u\b\xb5eB\x8a\x02\xb1O\x02\xc8d\xc7~\x00\x00\xe0\x94\x1e\x13\xecQ\x14,\ubde2`\x83A,<\xe3QD\xbaV\xa1\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x1e\x1aH(\x11\x9b\xe3\t\xbd\x88#nMH+PM\xc5W\x11\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\x1e\x1a\ud178leb\u02cf\xa1\xebo\x8f;\xc9\u072eny\x89\xf4\xd2\u0744%\x9b$\x00\x00\u07d4\x1e\x1ccQwj\xc3\x10\x919~\xcf\x16\x00-\x97\x9a\x1b-Q\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1e\x1dz_$h\xb9N\xa8&\x98-\xbf!%yR*\xb7\xdf\n\u02ac\x9e\xee\xd3Y09\xe5\xacuy\x8a+\x14F\xddj\xef\xe4\x1c\x00\x00\u07d4\x1e{^M\x1fW+\xec\xf2\xc0\x0f\xc9\f\xb4v{Jn3\u0509\x06\x1f\xc6\x10u\x93\xe1\x00\x00\u07d4\x1e\x8eh\x9b\x02\x91|\xdc)$]\f\x9ch\xb0\x94\xb4\x1a\x9e\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xa34\xb5u\b\a\xeat\xaa\u016b\x86\x94\xec_(\xaaw\u03c9\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\x1e\xa4qU\x04\u01af\x10{\x01\x94\xf4\xf7\xb1\xcbo\xcc\xcdoK\x89 \x041\x97\xe0\xb0'\x00\x00\u07d4\x1e\xa4\x92\xbc\xe1\xad\x10~3\u007fK\u0527\xac\x9a{\xab\xcc\u036b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x1e\xa6\xbf/\x15\xae\x9c\x1d\xbcd\u06a7\xf8\xeaM\r\x81\xaa\xd3\xeb\x89\u3bb5sr@\xa0\x00\x00\u07d4\x1e\xb4\xbfs\x15j\x82\xa0\xa6\x82 \x80\xc6\xed\xf4\x9cF\x9a\xf8\xb9\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\x1e\xba\xcbxD\xfd\xc3\"\xf8\x05\x90O\xbf\x19b\x80-\xb1S|\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1e\xc4\xecKw\xbf\x19\u0411\xa8h\xe6\xf4\x91T\x18\x05A\xf9\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xd0n\xe5\x16b\xa8lcE\x88\xfbb\xdcC\xc8\xf2~|\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1e\u063b?\x06w\x8b\x03\x9e\x99a\xd8\x1c\xb7\x1as\xe6x|\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xda\bNye\x00\xba\x14\xc5\x12\x1c\r\x90\x84of\xe4\xbeb\x89\x1c\xfd\xd7F\x82\x16\xe8\x00\x00\u07d4\x1e\xeel\xbe\xe4\xfe\x96\xadaZ\x9c\xf5\x85zdy@\u07ccx\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4\x1e\xf2\u073f\xe0\xa5\x00A\x1d\x95n\xb8\u0213\x9c=l\xfef\x9d\x89*\x11)\u0413g \x00\x00\xe0\x94\x1e\xf5\xc9\xc76P\u03fb\xde\\\x88U1\xd4'\xc7\xc3\xfeUD\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1f\x04\x12\xbf\xed\u0356N\x83}\t,q\xa5\xfc\xba\xf3\x01&\xe2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\x17O@\xa0Dr4\xe6fS\x91Mu\xbc\x00>V\x90\u0709\b\xacr0H\x9e\x80\x00\x00\u07d4\x1f!\x86\xde\xd2>\f\xf9R\x16\x94\xe4\xe1dY>i\n\x96\x85\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x1f*\xfc\n\xed\x11\xbf\xc7\x1ew\xa9\ae{6\xeav\xe3\xfb\x99\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\x1f9Y\xfc)\x11\x10\xe8\x822\xc3kvg\xfcx\xa3ya?\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1f=\xa6\x8f\xe8~\xafC\xa8)\xabm~\u0166\xe0\t\xb2\x04\xfb\x89\x1e\x16\x01u\x8c,~\x00\x00\u07d4\x1fI\xb8m\r9EY\x06\x98\xa6\xaa\xf1g<7u\\\xa8\r\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\x1f_;4\xbd\x13K'\x81\xaf\xe5\xa0BJ\u0144l\xde\xfd\x11\x89\x05]\xe6\xa7y\xbb\xac\x00\x00\u07d4\x1fo\x0004\x97R\x06\x1c\x96\a+\xc3\xd6\xeb5I \x8dk\x89\x01K\x8d\xe1\xeb\x88\u06c0\x00\u07d4\x1f}\x8e\x86\xd6\xee\xb0%E\xaa\xd9\x0e\x912{\xd3i\xd7\xd2\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\x81\x16\xbd\n\xf5W\x0e\xaf\fV\u011cz\xb5\xe3zX\x04X\x89lk\x93[\x8b\xbd@\x00\x00\u0794\x1f\x88\xf8\xa13\x8f\xc7\xc1\tv\xab\xcd?\xb8\u04c5T\xb5\uc708\xb9\xf6]\x00\xf6<\x00\x00\u07d4\x1f\x9c2hE\x8d\xa3\x01\xa2\xbeZ\xb0\x82W\xf7{\xb5\xa9\x8a\xa4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1f\xa21\x9f\xed\x8c-F*\xdf.\x17\xfe\xecjo0Qn\x95\x89\x06\xca\xe3\x06!\xd4r\x00\x00\u07d4\x1f\xb4c\xa08\x99\x83\xdf}Y?{\xddmxI\u007f\xed\x88y\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\xb7\xbd1\r\x95\xf2\xa6\u067a\xaf\x8a\x8aC\n\x9a\x04E:\x8b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x1f\xcc|\xe6\xa8HX\x95\xa3\x19\x9e\x16H\x1fr\xe1\xf7b\xde\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1f\xcf\xd1\xd5\u007f\x87\"\x90V\f\xb6-`\x0e\x1d\xef\xbe\xfc\xcc\x1c\x89P\xc5\xe7a\xa4D\b\x00\x00\u0794\x1f\u0496\xbe\x03\xads|\x92\xf9\u0186\x9e\x8d\x80\xa7\x1cW\x14\xaa\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x1f\xdd\xd8_\u024b\xe9\xc4\x04Ya\xf4\x0f\x93\x80^\xccEI\xe5\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4 \x01\xbe\xf7{f\xf5\x1e\x15\x99\xb0/\xb1\x10\x19J\x00\x99\xb7\x8d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4 \x02d\xa0\x9f\x8ch\xe3\xe6b\x97\x95(\x0fV%O\x86@\u0409\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \x03qy\a\xa7%`\xf40\u007f\x1b\xee\xccT6\xf4=!\xe7\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4 \r\xfc\vq\xe3Y\xb2\xb4eD\n6\xa6\xcd\xc3Rw0\a\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4 \x13L\xbf\xf8\x8b\xfa\xdcFkR\xec\ua9d8W\x89\x1d\x83\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4 \x14&\x1f\x01\b\x9fSyV0\xba\x9d\xd2O\x9a4\xc2\xd9B\x89Hz\x9a0E9D\x00\x00\u07d4 \x16\x89]\xf3,\x8e\xd5G\x82iF\x84#\xae\xa7\xb7\xfb\xceP\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \x18\x1cKA\xf6\xf9r\xb6iX!_\x19\xf5p\xc1]\xdf\xf1\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4 \x18d\xa8\xf7\x84\xc2'{\v|\x9e\xe74\xf7\xb3w\xea\xb6H\x89\xf2(\x14\x00\xd1\xd5\xec\x00\x00\u07d4 \xb8\x1a\xe59&\xac\xe9\xf7\xd7AZ\x05\f\x03\x1dX_ \x89\x12\u007f\x19\xe8>\xb3H\x00\x00\xe0\x94 \x1d\x9e\xc1\xbc\v\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4 \xa1RV\xd5\f\xe0X\xbf\x0e\xacC\xaaS:\xa1n\u0273\x80\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \xa2\x9cPy\xe2k?\x181\x8b\xb2\xe5\x0e\x8e\x8b4n[\xe8\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4 \xa8\x16\x80\xe4e\xf8\x87\x90\xf0\aO`\xb4\xf3_]\x1ej\xa5\x89Ea\x80'\x8f\fw\x80\x00\u07d4 \xb9\xa9\u6f48\x80\u0659J\xe0\r\u0439(*\v\xea\xb8\x16\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4 \u0084\xba\x10\xa2\b0\xfc=i\x9e\xc9}-\xfa'\xe1\xb9^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4 \xd1A\u007f\x99\xc5i\u3fb0\x95\x85e0\xfe\x12\xd0\xfc\uaa89@\x15\xf9K\x11\x83i\x80\x00\u07d4 \u074f\u02f4n\xa4o\u3066\x8b\x8c\xa0\xea[\xe2\x1f\u9949lk\x93[\x8b\xbd@\x00\x00\xe0\x94 \xff>\u078c\xad\xb5\xc3{H\xcb\x14X\x0f\xb6^#\t\n{\x8a\b\xe4\xd3\x16\x82v\x86@\x00\x00\xe0\x94!\x008\x1d`\xa5\xb5J\xdc\t\u0456\x83\xa8\xf6\u057bK\xfb\u02ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94!\x18\xc1\x16\xab\f\xdfo\xd1\x1dT\xa40\x93\a\xb4w\xc3\xfc\x0f\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94!\x1b)\xce\xfcy\xae\x97gD\xfd\xeb\u03bd<\xbb2\xc5\x13\x03\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4! l\xe2.\xa4\x80\xe8Y@\xd3\x13\x14\xe0\xd6ONM:\x04\x8965\u026d\xc5\u07a0\x00\x00\u07d4!2\xc0Qj.\x17\x17J\xc5G\xc4;{\x00 \xd1\xebLY\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94!@\x8bMz,\x0en\xcaAC\xf2\xca\u037b\u033a\x12\x1b\u060a\x04<3\xc1\x93ud\x80\x00\x00\u07d4!Kt9U\xa5\x12\xden\r\x88j\x8c\xbd\x02\x82\xbe\xe6\u04a2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!L\x89\u017d\x8e}\"\xbcWK\xb3^H\x95\x02\x11\xc6\xf7v\x89\x01\x06T\xf2X\xfd5\x80\x00\xe0\x94!Ti\x14\xdf\u04ef*\xddA\xb0\xff>\x83\xff\xdat\x14\xe1\xe0\x8a\x01C\x95\xe78ZP.\x00\x00\u07d4!X.\x99\xe5\x02\xcb\xf3\xd3\xc2;\xdf\xfbv\xe9\x01\xacmV\xb2\x89\x05k\xc7^-c\x10\x00\x00\u07d4!Y$\b\x13\xa70\x95\xa7\xeb\xf7\u00f3t>\x80(\xae_\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!`\xb4\xc0,\xac\n\x81\u0791\b\xdeCE\x90\xa8\xbf\xe6\x875\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94!nA\x86N\xf9\x8f\x06\r\xa0\x8e\xca\xe1\x9a\xd1\x16j\x17\xd06\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4!\x84o/\xdfZA\xed\x8d\xf3n^\xd8TM\xf7Y\x88\xec\xe3\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94!\xa6\xdbe'F{\xc6\xda\xd5K\xc1n\x9f\xe2\x95;g\x94\xed\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4!\xa6\xfe\xb6\xab\x11\xc7f\xfd\xd9w\xf8\xdfA!\x15_G\xa1\xc0\x89\x03\x19\xcf8\xf1\x00X\x00\x00\u07d4!\xb1\x82\xf2\xda+8D\x93\xcf_5\xf8=\x9d\x1e\xe1O*!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!\xbf\xe1\xb4\\\xac\xdebt\xfd\x86\b\u0661x\xbf>\xebn\u0709l\xee\x06\u077e\x15\xec\x00\x00\u07d4!\xc0s\x80HOl\xbc\x87$\xad2\xbc\x86L;Z\xd5\x00\xb7\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94!\u00e8\xbb\xa2g\xc8\u0322{\x1a\x9a\xfa\xba\xd8o`z\xf7\b\x8a\x01\xe4\xa3lI\u06580\x00\x00\u07d4!\xcem[\x90\x18\xce\xc0J\u0596yD\xbe\xa3\x9e\x800\xb6\xb8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4!\xd0'\x05\xf3\xf6I\x05\xd8\x0e\xd9\x14y\x13\xea\x8cs\a\u0595\x89I\xed\xb1\xc0\x98\x876\x00\x00\u07d4!\xd1?\f@$\xe9g\xd9G\a\x91\xb5\x0f\"\xde:\xfe\xcf\x1b\x89\xf1Z\xd3^.1\xe5\x00\x00\xe0\x94!\xdb\u06c1z\r\x84\x04\u01bd\xd6\x15\x047N\x9cC\xc9!\x0e\x8a\x02\x1e\x18\xb9\xe9\xabE\xe4\x80\x00\xe0\x94!\xdf\x1e\xc2KNK\xfey\xb0\xc0\x95\u03ba\xe1\x98\xf2\x91\xfb\u044a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94!\xdf-\u036ft\xb2\xbf\x804\x04\xddM\xe6\xa3^\xab\xec\x1b\xbd\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4!\xe2\x19\u021c\xa8\xac\x14\xaeL\xbaa0\xee\xb7}\x9em9b\x89*\u035f\xaa\xa08\xee\x00\x00\u07d4!\xe5\u04ba\xe9\x95\xcc\xfd\b\xa5\xc1k\xb5$\xe1\xf60D\x8f\x82\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4!\xe5\xd7s 0L \x1c\x1eS\xb2a\xa1#\u0421\x06>\x81\x89\x04\xb6\xfa\x9d3\xddF\x00\x00\xe0\x94!\xea\xe6\xfe\xff\xa9\xfb\xf4\u0347OG9\xac\xe50\u033eY7\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4!\xec\xb2\u07e6Wy\xc7Y-\x04\x1c\xd2\x10Z\x81\xf4\xfdNF\x8965\u026d\xc5\u07a0\x00\x00\u07d4!\uff20\x9b5\x80\xb9\x8es\xf5\xb2\xf7\xf4\xdc\v\xf0,R\x9c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!\xfd\v\xad\xe5\xf4\xeftt\xd0X\xb7\xf3\xd8T\xcb\x13\x00RN\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94!\xfdG\xc5%`\x12\x19\x8f\xa5\xab\xf11\xc0mj\xa1\x96_u\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4!\xfdl]\x97\xf9\xc6\x00\xb7h!\xdd\xd4\xe7v5\x0f\xce+\xe0\x89lj\u04c2\xd4\xfba\x00\x00\u07d4\"\r\u018d\xf0\x19\xb6\xb0\u033f\xfbxKZZ\xb4\xb1]@`\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\"\x0e+\x92\xc0\xf6\xc9\x02\xb5\x13\xd9\xf1\xe6\xfa\xb6\xa8\xb0\xde\xf3\u05c9+^:\xf1k\x18\x80\x00\x00\u07d4\"V\x1cY1\x14560\x9c\x17\xe82X{b\\9\v\x9a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\"W\xfc\xa1jn\\*d|<)\xf3l\xe2)\xab\x93\xb1~\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\"]5\xfa\xed\xb3\x91\u01fc-\xb7\xfa\x90q\x16\x04\x05\x99m\x00\x89\t\x18T\xfc\x18bc\x00\x00\u07d4\"_\x9e\xb3\xfbo\xf3\xe9\xe3\xc8D~\x14\xa6n\x8dO7y\xf6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\"r\x18n\xf2}\xcb\xe2\xf5\xfc70P\xfd\xae\u007f*\xce#\x16\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4\"s\xba\u05fcNHv\"\xd1u\xefzf\x98\x8bj\x93\xc4\xee\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\"v&K\xec\x85&\xc0\xc0\xf2pgz\xba\xf4\xf0\xe4A\xe1g\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\"\x82B\xf83n\xec\xd8$.\x1f\x00\x0fA\x93~q\xdf\xfb\xbf\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\"\x84*\xb80\xdaP\x99\x13\xf8\x1d\xd1\xf0O\x10\xaf\x9e\xdd\x1cU\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\"\x94O\xbc\xa9\xb5yc\bN\xb8M\xf7\xc8_\xb9\xbc\u07f8V\x89\xfc\x11\x8f\uf43a8\x80\x00\u07d4\"\x9c\xc4q\x1bbu^\xa2\x96DZ\u00f7\u007f\xc63\x82\x1c\xf2\x89\x02#\xe8\xb0R\x192\x80\x00\u0794\"\x9eC\r\xe2\xb7OD&Q\xdd\u0377\x01v\xbc\x05L\xadT\x88\xbb\xf9\x81\xbcJ\xaa\x80\x00\u07d4\"\x9fO\x1a*OT\atP[G\a\xa8\x1d\xe4D\x10%[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\"\x9f\xf8\v\xf5p\x80\t\xa9\xf79\xe0\xf8\xb5`\x91@\x16\u0566\x89\x12\x11\xec\xb5m\x13H\x80\x00\u07d4\"\xa2X\x12\xabV\xdc\xc4#\x17^\xd1\u062d\xac\xce3\xcd\x18\x10\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94\"\xb9j\xb2\xca\xd5]\xb1\x00\xb50\x01\xf9\xe4\xdb7\x81\x04\xc8\a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\"\xbd\xff\xc2@\xa8\x8f\xf7C\x1a\xf3\xbf\xf5\x0e\x14\xda7\xd5\x18>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\"\xce4\x91Y\xee\xb1D\xef\x06\xff&6X\x8a\xefy\xf6(2\x89\n1\x06+\xee\xedp\x00\x00\u07d4\"\xdbU\x9f,<\x14u\xa2\xe6\xff\xe8:YyY\x91\x96\xa7\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\"\xe1QX\xb5\xee>\x86\xeb\x032\xe3\u6a6cl\u0675^\u0349\b\xacr0H\x9e\x80\x00\x00\u07d4\"\xe2H\x8e-\xa2jI\xae\x84\xc0\x1b\xd5K!\xf2\x94x\x91\u0189]\u0212\xaa\x111\xc8\x00\x00\u07d4\"\xe5\x12\x14\x9a\x18\xd3i\xb7\x86\xc9\xed\xab\xaf\x1d\x89N\xe0.g\x14a\\\x00\x00\u07d4\"\xeb}\xb0\xbaV\xb0\xf8\xb8\x16\u0332\x06\xe6\x15\xd9)\x18[\r\x89\x04])s~\"\xf2\x00\x00\u07d4\"\xee\xd3'\xf8\xeb\x1d\x138\xa3\xcb{\x0f\x8aK\xaaY\a\u0355\x89\x01E]_Hw\b\x80\x00\xe0\x94\"\xf0\x04\u07cd\xe9\xe6\xeb\xf5#\u032c\xe4W\xac\xcb&\xf9r\x81\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794\"\xf2\xdc\xffZ\u05cc>\xb6\x85\v\\\xb9Q\x12{e\x95\"\u623e -j\x0e\xda\x00\x00\u07d4\"\xf3\xc7y\xddy\x02>\xa9*x\xb6\\\x1a\x17\x80\xf6-\\J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\"\xfe\x88M\x907)\x1bMR\xe6(Z\xe6\x8d\xea\v\xe9\xff\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\x06\u07d3\x1a\x94\rX\xc0\x16e\xfaM\b\x00\x80,\x02\xed\xfe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94#\t\xd3@\x91D[22Y\v\xd7\x0fO\x10\x02[,\x95\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4#\x12\x00F\xf6\x83!\x02\xa7R\xa7fVi\x1c\x86>\x17\u5709\x11\xe0\xe4\xf8\xa5\v\xd4\x00\x00\u07d4#\x1a\x15\xac\xc1\x99\u021f\xa9\xcb\"D\x1c\xc7\x030\xbd\xcc\xe6\x17\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4#\x1d\x94\x15]\xbc\xfe*\x93\xa3\x19\xb6\x17\x1fc\xb2\v\u04b6\xfa\x89\xcf\x14{\xb9\x06\xe2\xf8\x00\x00\u07d4#(2\xcdYw\xe0\nL0\xd0\x16?.$\xf0\x88\xa6\xcb\t\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4#,m\x03\xb5\xb6\xe6q\x1e\xff\xf1\x90\xe4\x9c(\xee\xf3l\x82\xb0\x89Hz\x9a0E9D\x00\x00\xe0\x94#,\xb1\xcdI\x99<\x14J?\x88\xb3a\x1e#5i\xa8k\u058a\x03L`lB\u042c`\x00\x00\u07d4#,\xe7\x82Pb%\xfd\x98`\xa2\xed\xc1Jz0Gsm\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4#/R]U\x85\x9b}N`\x8d H\u007f\xaa\xdb\x00)15\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94#4\u0150\u01e4\x87i\x100E\u0176SL\x8a4i\xf4J\x8a\x03\xb1\x99\a=\xf7-\xc0\x00\x00\u07d4#7n\u02bftl\xe53!\xcfB\xc8fI\xb9+g\xb2\xff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#7\x8fB\x92m\x01\x84\xb7\x93\xb0\xc8'\xa6\xdd>=3O\u0349\x03\t'\xf7L\x9d\xe0\x00\x00\u07d4#8B\xb1\xd0i/\xd1\x11@\xcfZ\u0364\xbf\x960\xba\xe5\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#9\xe9I(p\xaf\xea%7\xf3\x89\xac/\x83\x83\x02\xa3<\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#;\xdd\xdd]\xa9HR\xf4\xad\xe8\xd2\x12\x88V\x82\xd9\ak\u0189\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#OF\xba\xb7?\xe4]1\xbf\x87\xf0\xa1\xe0Fa\x99\xf2\ubb09\x1aJ\xba\"\\ t\x00\x00\u07d4#U\x1fV\x97_\xe9+1\xfaF\x9cI\xeaf\xeefb\xf4\x1e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4#V\x95B\xc9}V`\x18\xc9\a\xac\xfc\xf3\x91\xd1@g\xe8~\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94#_\xa6l\x02^\xf5T\x00p\xeb\xcf\r7-\x81w\xc4g\xab\x8a\a\x12\x9e\x1c\xdf7>\xe0\x00\x00\xe0\x94#r\xc4\xc1\u0253\x9fz\xafl\xfa\xc0@\x90\xf0\x04t\x84\n\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4#s\f5z\x91\x02nD\xb1\xd0\xe2\xfc*Q\xd0q\xd8\xd7{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#v\xad\xa9\x033\xb1\u0441\bL\x97\xe6E\xe8\x10\xaa[v\xf1\x89(\xa8WBTf\xf8\x00\x00\u07d4#x\xfdC\x82Q\x1e\x96\x8e\u0452\x10g7\xd3$\xf4T\xb55\x8965\u026d\xc5\u07a0\x00\x00\u07d4#\x82\xa9\u050e\xc8>\xa3e(\x90\xfd\x0e\u7710{[-\xc1\x89\a?u\u0460\x85\xba\x00\x00\u07d4#\x83\xc2\"\xe6~\x96\x91\x90\xd3!\x9e\xf1M\xa3xP\xe2lU\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\x8akv5%/RDHl\n\xf0\xa7: s\x85\xe09\x89JD\x91\xbdm\xcd(\x00\x00\u07d4#\x9as>k\x85Z\u0152\xd6c\x15a\x86\xa8\xa1t\xd2D\x9e\x89X\xbe7X\xb2A\xf6\x00\x00\xe0\x94#\xab\t\xe7?\x87\xaa\x0f;\xe0\x13\x9d\xf0\xc8\xebk\xe5cO\x95\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94#\xab\xd9\xe9>yW\xe5\xb66\xbeey\x05\x1c\x15\xe5\xce\v\x0e\x8a\x03\xa3\xc8\xf7\xcb\xf4,8\x00\x00\u07d4#\xb1\u0111\u007f\xbd\x93\xee=H8\x93\x06\x95s\x84\xa5Il\xbf\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94#\xba8d\xdaX=\xabV\xf4 \x87<7g\x96\x90\xe0/\x00\x8a\x02\x13BR\r_\xec \x00\x00\u07d4#\xc5Z\xebW9\x87o\n\xc8\xd7\xeb\xea\x13\xber\x96\x85\xf0\x00\x89Hz\x9a0E9D\x00\x00\u07d4#\u025b\xa0\x87D\x8e\x19\xc9p\x1d\xf6n\f\xabR6\x831\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xcc\xc3\u01ac\xd8\\.F\fO\xfd\xd8+\xc7]\xc8I\xea\x14\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#\xcd%\x98\xa2\x0e\x14\x9e\xad*\u0593yWn\xce\xdb`\u3389lk\x93[\x8b\xbd@\x00\x00\u07d4#\u07cfH\xee\x00\x92V\xeay~\x1f\xa3i\xbe\xeb\xcfk\xc6c\x89|\xd3\xfa\xc2m\x19\x81\x80\x00\u07d4#\xe2\u01a8\xbe\x8e\n\u03e5\xc4\xdf^6\x05\x8b\xb7\u02ecZ\x81\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xeaf\x9e5d\x81\x9a\x83\xb0\xc2l\x00\xa1m\x9e\x82olF\x89M\x8dl\xa9h\xca\x13\x00\x00\u07d4#\xebo\xd8Vq\xa9\x06:\xb7g\x8e\xbe&Z \xf6\x1a\x02\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xf9\xec\xf3\xe5\xdd\u0723\x88\x15\xd3\xe5\x9e\xd3K[\x90\xb4\xa3S\x89\v\x17\x81\xa3\xf0\xbb \x00\x00\u07d4#\xfa~\xb5\x1aH\"\x95\x98\xf9~v+\xe0\x86\x96R\xdf\xfcf\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94$\x03\x05rs\x13\xd0\x1esT,w_\xf5\x9d\x11\xcd5\xf8\x19\x8a\x01A\x88Vf\x80\u007f\\\x80\x00\u07d4$\x04k\x91\u069ba\xb6)\u02cb\x8e\xc0\xc3Q\xa0~\a\x03\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4$\x0eU\x9e'J\xae\xf0\xc2X\x99\x8c\x97\x9fg\x1d\x11s\xb8\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94$\x13aU\x9f\xee\xf8\x0e\xf170!S\xbd\x9e\xd2\xf2]\xb3\xef\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94$;;\xcaj)\x93Y\xe8\x86\xce3\xa3\x03A\xfa\xfeMW=\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4$<\x84\xd1$ W\f\xc4\xef;\xab\xa1\xc9Y\u0083$\x95 \x89\u007f\x1fi\x93\xa8S\x04\x00\x00\xe0\x94$CJ>2\xe5N\xcf'/\xe3G\v_oQ/gU \x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4$HYo\x91\xc0\x9b\xaa0\xbc\x96\x10j-7\xb5p^](\x89lk\x93[\x8b\xbd@\x00\x00\u0794$Xn\xc5E\x175\xee\xaa\xebG\r\xc8sj\xaeu/\x82\xe5\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4$X\xd6U_\xf9\x8a\x12\x9c\xce@7\x95=\x00 n\xffB\x87\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4$b\x91\x16[Y3-\xf5\xf1\x8c\xe5\u0248V\xfa\xe9X\x97\u0589\\(=A\x03\x94\x10\x00\x00\u07d4$g\u01a5\u0196\xed\xe9\xa1\xe5B\xbf\x1a\xd0k\xccK\x06\xac\xa0\x89\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4$v\xb2\xbbu\x1c\xe7H\xe1\xa4\xc4\xff{#\v\xe0\xc1]\"E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4$z\n\x11\xc5\u007f\x03\x83\xb9I\xdeT\vf\xde\xe6\x86\x04\xb0\xa1\x899\xfb\xae\x8d\x04-\xd0\x00\x00\u07d4$\x87\xc3\u013e\x86\xa2r=\x91|\x06\xb4XU\x01p\xc3\xed\xba\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\x89\xac\x12i4\xd4\u05a9M\xf0\x87C\xda{v\x91\xe9y\x8e\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\x9d\xb2\x9d\xbc\x19\xd1#]\xa7)\x8a\x04\b\x1c1WB\u9b09a\xac\xff\x81\xa7\x8a\xd4\x00\x00\u07d4$\xa4\xeb6\xa7\xe4\x98\xc3o\x99\x97\\\x1a\x8dr\x9f\u05b3\x05\u05c9\r\xfcx!\x0e\xb2\xc8\x00\x00\u07d4$\xa7P\xea\xe5\x87G\x11\x11m\xd7\xd4{q\x86\u0399\r1\x03\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4$\xaa\x11Q\xbbv_\xa3\xa8\x9c\xa5\x0e\xb6\xe1\xb1\xc7\x06A\u007f\u0509\xa8\r$g~\xfe\xf0\x00\x00\u0794$\xac\xa0\x8d[\xe8^\xbb\x9f12\xdf\xc1\xb6 \x82N\xdf\xed\xf9\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4$\xb2\xbe\x11\x8b\x16\u0632\x17Gi\xd1{L\xf8O\a\u0294m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4$\xb8\xb4F\u07bd\x19G\x95]\u0404\xf2\xc5D\x933F\u04ed\x89\xeaim\x90@9\xbd\x80\x00\u07d4$\xb9^\xbe\xf7\x95\x00\xba\xa0\xed\xa7.w\xf8wA]\xf7\\3\x891T\xc9r\x9d\x05x\x00\x00\u07d4$\xb9\xe6dOk\xa4\xcd\xe1&'\r\x81\xf6\xab`\xf2\x86\xdf\xf4\x89\a?u\u0460\x85\xba\x00\x00\u07d4$\xbdY\x04\x05\x90\x91\xd2\xf9\xe1-j&\xa0\x10\xca\"\xab\x14\xe8\x89e\xea=\xb7UF`\x00\x00\u07d4$\xc0\u020bT\xa3TG\t\x82\x8a\xb4\xab\x06\x84\x05Y\xf6\xc5\u2250\xf54`\x8ar\x88\x00\x00\u07d4$\xc1\x17\xd1\u04b3\xa9z\xb1\x1aFy\u025awJ\x9e\xad\xe8\u044965\u026d\xc5\u07a0\x00\x00\u07d4$\xcf\xf0\xe93j\x9f\x80\xf9\xb1\u02d6\x8c\xafk\x1d\x1cI2\xa4\x89\n\xdaUGK\x814\x00\x00\u07d4$\u06aa\xdd\xf7\xb0k\xbc\ua6c0Y\x00\x85\xa8\x85gh+N\x89\x11K \x15\u04bb\xd0\x00\x00\u07d4$\xdc\xc2K\xd9\xc7!\f\xea\u03f3\r\xa9\x8a\xe0JM{\x8a\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\xf7E\r\xdb\xf1\x8b\x02\x0f\xeb\x1a 2\xd9\xd5Kc>\xdf7\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4$\xfcs\xd2\a\x93\t\x8e\t\u076bW\x98Pb$\xfa\x1e\x18P\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4$\xfd\x9al\x87L/\xab?\xf3n\x9a\xfb\xf8\xce\r2\xc7\u0792\x89Hz\x9a0E9D\x00\x00\u07d4%\n@\xce\xf3 #\x97\xf2@F\x95H\xbe\xb5bj\xf4\xf2<\x89\x05\x03\xb2\x03\xe9\xfb\xa2\x00\x00\u07d4%\niC\av\xf64w\x03\xf9R\x97\x83\x95Za\x97\xb6\x82\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4%\x0e\xb7\xc6o\x86\x9d\xdfI\u0685\xf39>\x98\f\x02\x9a\xa44\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%\x10j\xb6u]\xf8mkc\xa1\x87p;\f\xfe\xa0\u5520\x89\x01|@Z\xd4\x1d\xb4\x00\x00\xe0\x94%\x18_2Z\xcf-dP\x06\x98\xf6\\v\x9d\xdfh0\x16\x02\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4%\x1c\x12r,hy\"y\x92\xa3\x04\xeb5v\xcd\x18CN\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\x1eh8\xf7\xce\u0173\x83\xc1\xd9\x01F4\x12t\xda\xf8\xe5\x02\x89\a\xff\x1c\xcbua\xdf\x00\x00\u07d4%%\x9d\x97Z!\xd8:\xe3\x0e3\xf8\x00\xf5?7\u07e0\x198\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4%({\x81_\\\x828\ns\xb0\xb1?\xba\xf9\x82\xbe$\xc4\u04c9\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94%+eU\xaf\u0700\xf2\xd9m\x97-\x17\u06c4\xeaZ\xd5!\xac\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4%8S)6\x81<\x91\xe6S(O\x01|\x80\u00f8\xf8\xa3o\x89l\x87T\xc8\xf3\f\b\x00\x00\xe0\x94%>2\xb7N\xa4I\n\xb9&\x06\xfd\xa0\xaa%{\xf2=\u02cb\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94%?\x1et*,\uc1b0\u05f3\x06\xe5\xea\xcbl\xcb/\x85T\x8a\x04>^\xde\x1f\x87\x8c \x00\x00\u07d4%A1J\v@\x8e\x95\xa6\x94DIwq*Pq5\x91\xab\x89X\x9e\x1a]\xf4\u05f5\x00\x00\u07d4%L\x1e\xccc\f(w\u0780\x95\xf0\xa8\u06e1\xe8\xbf\x1fU\f\x89\\(=A\x03\x94\x10\x00\x00\u07d4%Z\xbc\x8d\b\xa0\x96\xa8\x8f=j\xb5_\xbcsR\xbd\u0739\u0389\x04t6\x821>\u0780\x00\u07d4%[\xdddt\u0302b\xf2j\"\u00cfE\x94\x0e\x1c\ue99b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%`\xb0\x9b\x89\xa4\xaehI\xedZ<\x99XBf1qDf\x89\\(=A\x03\x94\x10\x00\x00\u07d4%a\xa18\xdc\xf8;\xd8\x13\xe0\xe7\xf1\bd+\xe3\xde=o\x05\x8964\xf4\x84\x17@\x1a\x00\x00\u0794%a\xec\x0f7\x92\x18\xfe^\xd4\xe0(\xa3\xf7D\xaaAuLr\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u0794%b\x92\xa1\x91\xbd\xda4\xc4\xdakk\u0591G\xbfu\u2a6b\x88\xc2\xff.\r\xfb\x03\x80\x00\u07d4%i~\xf2\f\u032ap\xd3-7o\x82r\xd9\xc1\a\f=x\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4%o\xa1P\u0307\xb5\x05j\a\xd0\x04\xef\xc8E$s\x9eb\xb5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4%r\x1c\x87\xb0\xdc!7|r\x00\xe5$\xb1J\"\xf0\xafi\xfb\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%\x899\xbb\xf0\f\x9d\xe9\xafS8\xf5\xd7\x14\xab\xf6\xd0\xc1\xc6q\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94%\x90\x12hp\xe0\xbd\xe8\xa6c\xab\x04\nr\xa5W=\x8dA\u008a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4%\x9e\xc4\xd2e\xf3\xabSk|p\xfa\x97\xac\xa1Bi,\x13\xfc\x89\x01\x1b\x1b[\xea\x89\xf8\x00\x00\xe0\x94%\xa5\x00\xee\xeczf*\x84\x15R\xb5\x16\x8bp{\r\xe2\x1e\x9e\x8a\x02\x1f/o\x0f\xc3\xc6\x10\x00\x00\xe0\x94%\xa5\xa4M8\xa2\xf4Lj\x9d\xb9\u037ck\x1e.\x97\xab\xb5\t\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4%\xa7L*\xc7]\u023a\xa8\xb3\x1a\x9c|\xb4\xb7\x82\x9b$V\u0689lk\x93[\x8b\xbd@\x00\x00\xe0\x94%\xad\xb8\xf9o9I,\x9b\xb4|^\u0708bNF\aV\x97\x8a\x05\xa9\x94\v\xc5hyP\x00\x00\u07d4%\xae\xe6\x8d\t\xaf\xb7\x1d\x88\x17\xf3\xf1\x84\xecV/x\x97\xb74\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\xb0S;\x81\xd0*a{\x92)\xc7\xec]o/g.[Z\x8965\u026d\xc5\u07a0\x00\x00\u07d4%\xb7\x8c\x9f\xad\x85\xb43C\xf0\xbf\xcd\x0f\xac\x11\u0254\x9c\xa5\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\xbcI\xef(\x8c\xd1e\xe5%\xc6a\xa8\x12\u03c4\xfb\xec\x8f3\x89\x12Y!\xae\xbd\xa9\xd0\x00\x00\u07d4%\xbd\xfa>\xe2o8Ia{#\x00bX\x8a\x97\xe3\xca\xe7\x01\x8965\xe6\x19\xbb\x04\xd4\x00\x00\u07d4%\xc1\xa3~\xe5\xf0\x82e\xa1\xe1\r=\x90\xd5G)U\xf9x\x06\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4%\xc6\xe7O\xf1\xd9(\u07d8\x13z\xf4\u07c40\xdf$\xf0|\u05c9\x15$VU\xb1\x02X\x00\x00\xe0\x94%\xcf\xc4\xe2\\5\xc1;i\xf7\xe7}\xbf\xb0\x8b\xafXuk\x8d\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94%\xda\u0515\xa1\x1a\x86\xb9\xee\xec\xe1\xee\xec\x80^W\xf1W\xfa\xff\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94%\xe07\xf0\n\x18'\v\xa5\xec4 \"\x9d\xdb\n,\u33e2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4%\xe6a\xc99\x86:\xcc\x04No\x17\xb5i\x8c\xce7\x9e\xc3\u0309JD\x91\xbdm\xcd(\x00\x00\u07d4&\x04\x8f\xe8M\x9b\x01\nb\xe71b~I\xbc.\xb7?@\x8f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4&\x06\u00f3\xb4\xca\x1b\t\x14\x98`,\xb1\x97\x8b\xf3\xb9R!\xc0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4&\n#\x0eDe\a~\v\x14\xeeDB\xa4\x82\u0570\xc9\x14\xbf\x89Z\xf6\x06\xa0k[\x11\x80\x00\u07d4&\r\xf8\x94:\x8c\x9a]\xbayE2\u007f\xd7\xe0\x83|\x11\xad\a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4&\x14\xf4-]\xa8D7ux\xe6\xb4H\xdc$0[\xef+\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\x15\x10\x0e\xa7\xe2[\xba\x9b\xcat`X\xaf\xbb\xb4\xff\xbeBD\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4&\x15u\xe9\xcfY\xc8\"o\xa7\xaa\xf9\x1d\xe8o\xb7\x0fZ\u00ee\x89\x10C\xa4CjR?\x00\x00\xe0\x94&\x1e\x0f\xa6LQ\x13te\xee\xcf[\x90\xf1\x97\xf7\x93\u007f\xdb\x05\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4&*\x8b\xfd}\x9d\xc5\xdd:\u05c1a\xb6\xbbV\b$76U\x89?j\x83\x84\a+v\x00\x00\xe0\x94&*\xedK\xc0\xf4\xa4\xb2\xc6\xfb5y>\x83ZI\x18\x9c\xdf\xec\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94&-\xc16L\xcfm\xf8\\C&\x8e\xe1\x82UM\xaei.)\x8a\x01\v /\xect\xce\xd8\x00\x00\u07d4&8\x140\x9d\xe4\xe65\xcfX^\r6Tw\xfc@\xe6l\xf7\x89\a\xea(2uw\b\x00\x00\u07d4&9\xee\xe9\x87<\xee\xc2o\u0314T\xb5H\xb9\xe7\xc5J\xa6\\\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94&>W\xda\xcb\xe0\x14\x9f\x82\xfee\xa2fH\x98\x86o\xf5\xb4c\x8a\b\v\xfb\xef\xcb_\v\xc0\x00\x00\u07d4>\x19\xc0m_\x14z\xa5\x97$\x8e\xb4l\xf7\xbe\xfad\xa5\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4&L\xc8\bj\x87\x10\xf9\x1b!r\t\x05\x91,\u05d6J\xe8h\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94&S\x83\u058bR\xd04\x16\x1b\xfa\xb0\x1a\xe1\xb0G\x94/\xbc2\x8a\x04rq\xde\xe2\rt\\\x00\x00\u07d4&Y\xfa\xcb\x1e\x83CeS\xb5\xb4)\x89\xad\xb8\a_\x99S\xed\x89\x01\x97evw\x1a^\x00\x00\xe0\x94&o-\xa7\xf0\b^\xf3\xf3\xfa\t\xba\xee#+\x93\xc7D\xdb.\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4&qH\xfdr\xc5Ob\nY/\xb9'\x991\x9c\xc4S+\\\x89\x169\u46fa\x16(\x00\x00\xe0\x94&xJ\u0791\u0228:\x8e9e\x8c\x8d\x82wA<\u0319T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4&z~n\x82\xe1\xb9\x1dQ\xde\u0776D\xf0\xe9m\xbb\x1f\u007f~\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4&\x80q=@\x80\x8e*P\xed\x011P\xa2\xa6\x94\xb9j\u007f\x1d\x89a\t=|,m8\x00\x00\u07d4&\x97\xb39\x81;\f-\x96K$q\xeb\x1c`oN\u02d6\x16\x89>\x8e\xf7\x95\u0610\xc8\x00\x00\u07d4&\xa6\x8e\xab\x90Z\x8b=\xce\x00\xe3\x170\x82%\u06b1\xb9\xf6\xb8\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4&\xb1\x1d\x06e\x88\xcet\xa5r\xa8Zc(s\x92\x12\xaa\x8b@\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xba\xbfB\xb2g\xfd\xcf8a\xfd\xd4#j^GHH\xb3X\x8965\u026d\xc5\u07a0\x00\x00\u07d4&\xc0\x05Kp\r:|-\xcb\xe2uh\x9dOL\xad\x16\xa35\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xc2\xff\xc3\x0e\xfd\xc5'>v\x18:\x16\xc2i\x8dnS\x12\x86\x89*\x11)\u0413g \x00\x00\u07d4&\u025f\x88I\u0240+\x83\xc8a!\u007f\xd0z\x9e\x84\u0377\x9d\x89\x10CV\x1a\x88)0\x00\x00\u07d4&\xcf\xff\xd0R\x15+\xb3\xf9W\xb4x\xd5\xf9\x8b#:|+\x92\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4&\u0521h\x91\xf5)\"x\x92\x17\xfc\u0606\xf7\xfc\xe2\x96\xd4\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xd4\xec\x17\xd5\u03b2\u0214\xbd\u015d\nji]\xad+C\u0309\x9f\x1fxv\x1d4\x1a\x00\x00\u07d4&\xe8\x01\xb6,\x82q\x91\xddh\xd3\x1a\x01\x19\x90\x94\u007f\xd0\xeb\xe0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4&\xe9\xe2\xadr\x97\x02bd\x17\xef%\xde\r\xc8\x00\xf7\xa7y\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4&\xf9\xf7\xce\xfd~9K\x9d9$A+\xf2\u0083\x1f\xaf\x1f\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94&\xfe\x17L\xbfRfP\xe0\xcd\x00\x9b\xd6\x12e\x02\u038ehM\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\xe0\x94&\xff\nQ\xe7\xce\u0384\x00'ix\xdb\xd6#n\xf1b\xc0\xe6\x8a\x15.\x18V'T\nP\x00\x00\u07d4'\x10\x1a\x0fV\u04da\x88\u0168O\x9b2L\xdd\xe3>\\\xb6\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'\x14L\xa9\xa7w\x1a\x83j\xd5\x0f\x80?d\xd8i\xb2\xae+ \x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4'\x14i\x13V:\xa7E\xe2X\x840\xd94\x8e\x86\xea|5\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4'\x1d=H\x1c\xb8\x8evq\xad!iI\xb66^\x060=\xe0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4' \xf9\xcaBn\xf2\xf2\xcb\xd2\xfe\xcd9\x92\fO\x1a\x89\xe1m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'*\x13\x1aZejz:\xca5\u023d \"\"\xa7Y\"X\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4'D\xffgFA!\xe3Z\xfc)\"\x17qd\xfa/\xcb\x02g\x89\x05k\xc7^-c\x10\x00\x00\u07d4'J=w\x1a=p\x97\x96\xfb\xc4\xd5\xf4\x8f\xce/\xe3\x8cy\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4'Mi\x17\x0f\xe7\x14\x14\x01\x88+\x88j\xc4a\x8cj\xe4\x0e\u06c93\xc5I\x901r\f\x00\x00\u07d4'R\x1d\xeb;n\xf1An\xa4\u01c1\xa2\xe5\u05f3n\xe8\x1ca\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'Xu\xffO\xbb\f\xf3\xa40!1'H\u007fv\b\xd0L\xba\x89\x1b\x1c\x01\x0evmX\x00\x00\u07d4'j\x00n0(\xec\xd4L\xdbb\xba\nw\u0394\xeb\xd9\xf1\x0f\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4'k\x05!\xb0\xe6\x8b'}\xf0\xbb2\xf3\xfdH2cP\xbf\xb2\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4'o\xd7\xd2O\x8f\x88?Zz()[\xf1qQ\u01e8K\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'p\xf1N\xfb\x16]\u07bay\xc1\v\xb0\xaf1\xc3\x1eY3L\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4'vw\xab\xa1\xe5,;S\xbf\xa2\a\x1dN\x85\x9a\n\xf7\xe8\xe1\x8965\u026d\xc5\u07a0\x00\x00\u07d4'\x82Ff\xd2x\xd7\x04#\xf0=\xfe\x1d\u01e3\xf0/C\u2d4966\xc2^f\xec\xe7\x00\x00\u07d4'\x83\f_`#\xaf\xaa\xf7\x97Egl J\x0f\xac\u0360\xba\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94'\x84\x90?\x1d|\x1b\\\xd9\x01\xf8\x87]\x14\xa7\x9b<\xbe*V\x8a\x04\xbd\xa7\xe9\xd7J\xd5P\x00\x00\u07d4'\x8c\v\xdec\x0e\u00d3\xb1\xe7&\u007f\xc9\xd7\xd9p\x19\xe4\x14[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'\x98q\x10\"\x1a\x88\b&\xad\xb2\xe7\xab^\xcax\xc6\xe3\x1a\xec\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94'\xac\a;\xe7\x9c\xe6W\xa9:\xa6\x93\xeeC\xbf\x0f\xa4\x1f\xef\x04\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4'\xb1iN\xaf\xa1e\xeb\xd7\xcc{\u025et\x81J\x95\x14\x19\u0709+^:\xf1k\x18\x80\x00\x00\u07d4'\xb6(\x16\xe1\xe3\xb8\u045by\xd1Q=]\xfa\x85[\f:*\x89\x05j\xf5\xc1\xfdiP\x80\x00\u07d4'\xbf\x94<\x163\xfe2\xf8\xbc\xcc\xdbc\x02\xb4\a\xa5rND\x892\xf8Lm\xf4\b\xc0\x80\x00\u07d4'\xbf\x9fD\xba}\x05\xc35@\u00e5;\xb0,\xbb\xff\xe7\xc3\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4'\xc2\xd7\xcaPM\xaa=\x90f\xdc\t\x13}\xc4/:\xaa\xb4R\x89 \x86\xac5\x10R`\x00\x00\u07d4'\xd1X\xac=>\x11\t\xabnW\x0e\x90\xe8]8\x92\xcdv\x80\x89\x05k\xc7^-c\x10\x00\x00\u07d4'\xe69\x89\xca\x1e\x90;\xc6 \xcf\x1b\x9c?g\xb9\xe2\xaee\x81\x89Hz\x9a0E9D\x00\x00\xe0\x94'\xf0<\xf1\xab\xc5\xe1\xb5\x1d\xbcDK(\x9eT,\x9d\u07f0\xe6\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4'\xfc\x85\xa4\x9c\xff\x90\xdb\xcf\xda\u071d\xdd@\u05b9\xa2!\nl\x89\x05k\xc7^-c\x10\x00\x00\u07d4(\x05A^\x1d\u007f\xde\xc6\xde\u07f8\x9eR\x1d\x10Y-t<\x10\x89\x05k\xc7^-c\x10\x00\x00\u07d4(\a>\xfc\x17\xd0\\\xab1\x95\xc2\xdb3+a\x98Gw\xa6\x12\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x12P\xa2\x91!'\nN\xe5\u05cd$\xfe\xaf\xe8,p\xba:\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x13\xd2c\xfc_\xf2G\x9e\x97\x05\x95\u05b6\xb5`\xf8\xd6\xd6\u0449lk\x93[\x8b\xbd@\x00\x00\u07d4(.\x80\xa5T\x87ZVy\x9f\xa0\xa9\u007fU\x10\u7557LN\x8965\u026d\xc5\u07a0\x00\x00\u07d4(3\x96\xce<\xac9\x8b\xcb\xe7\"\u007f2>x\xff\x96\u0407g\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4(4\x9f~\xf9t\xeaU\xfe6\xa1X;4\xce\xc3\xc4Pe\xf0\x89\f\xb63\u051eeY\x00\x00\u07d4(6\x120F\xb2\x84\xe5\xef\x10+\xfd\"\xb1v^P\x81\x16\xad\x89\x16S\xfb\xb5\xc4'\xe4\x00\x00\u07d4(<#\x14(<\x92\u0530d\xf0\xae\xf9\xbbRF\xa7\x00\u007f9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4(>\x11 7I\xb1\xfaO2\xfe\xbbq\xe4\x9d\x13Y\x198*\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94(>bR\xb4\xef\xcfFT9\x1a\xcbu\xf9\x03\u015bx\xc5\xfb\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94(Q\x0en\xff\x1f\xc8)\xb6WoC(\xbc98\xecze\x80\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4(X\xac\xac\xaf!\xea\x81\u02b7Y\x8f\xdb\xd8kE.\x9e\x8e\x15\x89$\x1a\x9bOaz(\x00\x00\u07d4(Z\xe5\x1b\x95\x00\u014dT\x13e\xd9ui\xf1K\xb2\xa3p\x9b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(f\xb8\x1d\xec\xb0.\xe7\n\xe2P\xce\xe5\xcd\xc7{Y\u05f6y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(i\x06\xb6\xbdIr\xe3\xc7\x16U\xe0K\xaf6&\f|\xb1S\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4(k\x18ma\xea\x1f\u05cd\x990\xfe\x12\xb0e7\xb0\\=Q\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94(t\xf3\xe2\x98]_{@f'\xe1{\xaaw+\x01\xab\u031e\x8a\x01F\x05\x04\x10v_8\x00\x00\xe0\x94(|\xf9\u0410.\xf8\x19\xa7\xa5\xf1ID[\xf1w^\xe8\xc4|\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4(\x81\x8e\x18\xb6\x10\x00\x13!\xb3\x1d\xf6\xfe}(\x15\u036d\xc9\xf5\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x86\x83$3~\x11\xba\x10l\xb4\x81\u0696/:\x84S\x80\x8d\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94(\x90K\xb7\xc40)C\xb7\t\xb1Myp\xe4+\x83$\u184a\x02\x1f\x97\x84j\a-~\x00\x00\u07d4(\x95\xe8\t\x99\xd4\x06\xadY.+&'7\xd3_}\xb4\xb6\x99\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4(\x96r\x80!N!\x8a\x12\f]\xda7\x04\x1b\x11\x1e\xa3mt\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4(\xa3\xda\t\xa8\x19H\x19\xae\x19\x9f.m\x9d\x13\x04\x81~(\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(\xab\x16_\xfbi\xed\xa0\xc5I\xae8\xe9\x82o_\u007f\x92\xf8S\x89FM\xf6\xd7\xc8DY\x00\x00\u07d4(\xb7u\x85\xcb=U\xa1\x99\xab)\x1d:\x18\u018f\u8684\x8a\x89j@v\xcfy\x95\xa0\x00\x00\xe0\x94(\xd4\xeb\xf4\x1e=\x95\xf9\xbb\x9a\x89u#\\\x1d\x009>\x80\x00\u07d4)\nV\xd4\x1fn\x9e\xfb\xdc\xea\x03B\u0dd2\x9a\x8c\xdf\xcb\x05\x89\x12\xa5\xf5\x81h\xee`\x00\x00\u07d4)\x15bK\xcbg\x917\xb8\xda\xe9\xabW\xd1\x1bI\x05\xea\xeeK\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4)\x1e\xfe\x00\x81\xdc\xe8\xc1G\x99\xf7\xb2\xa46\x19\xc0\u00f3\xfc\x1f\x89A\rXj \xa4\xc0\x00\x00\u07d4)\x1f\x92\x9c\xa5\x9bT\xf8D>=Mu\xd9]\xee$<\xefx\x89\x1b\x1a\b\x927\a=\x00\x00\xe0\x94))\x8c\xcb\xdf\xf6\x89\xf8\u007f\xe4\x1a\xa6\xe9\x8f\u07f5=\xea\xf3z\x8a\x041\\2\xd7\x1a\x9e`\x00\x00\u07d4)/\"\x8b\n\x94t\x8c\x8e\xeca-$o\x98\x93c\xe0\x8f\b\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4)3\x84\xc4+o\x8f)\x05\xceR\xb7 \\\"t7la+\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4)4\xc0\xdf{\xbc\x17+l\x18k\vrTz\u038b\xf7TT\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4)<#\x06\xdf6\x04\xaeO\xda\r z\xbasog\xde\a\x92\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94)I\xfd\x1d\xef\\v\xa2\x86\xb3\x87$$\x80\x9a\a\xdb9f\xf3\x8a\x01\x1b\xd9\x06\u06a0\xc9C\x80\x00\u07d4)OIK?.\x14\xa3\xf8\xab\x00\x00\x00\u07d4)U\xc3W\xfd\x8fu\xd5\x15\x9a=\xfai\u0178z5\x9d\ua309lk\x93[\x8b\xbd@\x00\x00\u07d4)a\xfb9\x1ca\x95|\xb5\xc9\xe4\a\u0762\x938\u04f9,\x80\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4)h\x1d\x99\x12\xdd\xd0~\xaa\xbb\x88\xd0]\x90\xf7f\xe8bA}\x8965\u026d\xc5\u07a0\x00\x00\u07d4)kq\xc0\x01X\x19\xc2B\xa7\x86\x1eo\xf7\xed\xed\x8a_q\xe3\x89lh\xcc\u041b\x02,\x00\x00\u07d4)mf\xb5!W\x1aNA\x03\xa7\xf5b\xc5\x11\xe6\xaas-\x81\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4)o\x00\xde\x1d\u00fb\x01\xd4z\x8c\xcd\x1e]\x1d\u0661\xebw\x91\x8965\u026d\xc5\u07a0\x00\x00\u07d4)s\x85\xe8\x864FV\x85\xc21\xa3\x14\xa0\xd5\xdc\xd1F\xaf\x01\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4)v=\xd6\u069a|\x16\x11s\x88\x83!\ub9b6<\x8f\xb8E\x89\x11\xc7\xea\x16.x \x00\x00\u07d4)yt\x11t\xa8\xc1\xea\v\u007f\x9e\xdfe\x81w\x85\x94\x17\xf5\x12\x89\x19\x01\x96l\x84\x96\x83\x80\x00\u07d4)z\x88\x92\x1b_\xca\x10\u5edd\xed`\x02T7\xae\"\x16\x94\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94)}]\xbe\"//\xb5%1\xac\xbd\v\x01=\xc4F\xacsh\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4)\x82N\x94\xccCH\xbc\x962y\xdc\xdfG9\x17\x152L\u04c9i*\xe8\x89p\x81\xd0\x00\x00\u07d4)\x82\xd7j\x15\xf8G\xddA\xf1\x92*\xf3h\xfeg\x8d\x0eh\x1e\x89\x05k\xc7^-c\x10\x00\x00\u07d4)\x88\x87\xba\xb5|[\xa4\xf0aR)\xd7R_\xa1\x13\xb7\ua249\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94)\x8e\xc7kD\r\x88\a\xb3\xf7\x8b_\x90\x97\x9b\xeeB\xedC\u06ca\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4)\x93h`\x90B\xa8X\xd1\xec\xdf\x1f\xc0\xad\xa5\xea\xce\xca)\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4)\x9e\v\xcaU\xe0i\u0785\x04\xe8\x9a\xcan\xca!\u04ca\x9a]\x89\x03\x027\x9b\xf2\xca.\x00\x00\u07d4)\xac+E\x84T\xa3l~\x96\xc7:\x86g\"*\x12$,q\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94)\xad\u03c3\xb6\xb2\n\u01a44\xab\xb1\x99<\xbd\x05\xc6\x0e\xa2\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94)\xae\xf4\x8d\xe8\xc9\xfb\xadK\x9eL\xa9pyzU3\xebr-\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4)\xb3\xf5a\xeezn%\x94\x1e\x98\xa52[x\xad\u01d7\x85\xf3\x89\x05k\xc7^-c\x10\x00\x00\u07d4)\xbd\xc4\xf2\x8d\xe0\x18\x0fC<&\x94\xebt\xf5PL\xe9C7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4)\u0300M\x92+\xe9\x1fY\t\xf3H\xb0\xaa\xa5\xd2\x1b`x0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94)\xda>5\xb2;\xb1\xf7/\x8e\"X\xcf\u007fU3Y\xd2K\xac\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94)\xe6y\x90\xe1\xb6\xd5.\x10U\xff\xe0I\xc51\x95\xa8\x15B\u03ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4)\uab82v\x17b\xf4\xd2\xdbS\xa9\u018b\x0fk\vmNf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4)\xeb~\xef\xda\xe9\xfe\xb4I\xc6?\xf5\xf2y\xd6u\x10\xeb\x14\"\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4)\xf0\xed\xc6\x038\xe7\x11 \x85\xa1\xd1\x14\u068cB\u038fU\u0589\xa0Z\u007f\x0f\xd8%x\x00\x00\u07d4)\xf8\xfb\xa4\xc3\ar\xb0W\xed\xbb\xe6*\xe7B\f9\x05r\xe1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94)\xf9(l\x0es\x8d\x17!\xa6\x91\u01b9Z\xb3\u0667\x97\xed\xe8\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4*\b^%\xb6Hb\xf5\xe6\x8dv\x8e+\x0fz\x85)\x85\x8e\xee\x89k\x88:\xcdWf\xcd\x00\x00\u07d4**\xb6\xb7Lz\xf1\xd9Gk\xb5\xbc\xb4RG\x97\xbe\xdc5R\x8965\u026d\xc5\u07a0\x00\x00\u07d4*9\x19\nO\u0783\u07f3\xdd\xcbL_\xbb\x83\xaclIu\\\x8965\u026d\xc5\u07a0\x00\x00\u07d4*@\r\xff\x85\x94\xder(\xb4\xfd\x15\xc3#\"\xb7[\xb8}\xa8\x89\x051\xa1\u007f`z-\x00\x00\xe0\x94*D\xa7!\x8f\xe4Me\xa1\xb4\xb7\xa7\u0671\xc2\xc5,\x8c>4\x8a\r-\x06\xc3\x05\xa1\xebW\x80\x00\u07d4*F\xd3Swqv\xff\x8e\x83\xff\xa8\x00\x1fOp\xf9s:\xa5\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4*Y_\x16\xee\xe4\xcb\f\x17\u0662\xd99\xb3\xc1\x0flgrC\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4*Y\xe4~\xa5\xd8\xf0\xe7\xc0(\xa3\xe8\xe0\x93\xa4\x9c\x1bP\xb9\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4*[\xa9\xe3L\u054d\xa5L\x9a'\x12f:;\xe2t\xc8\xe4{\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94*^:@\xd2\xcd\x03%vm\xe7:=g\x18\x96\xb3b\xc7;\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94*cY\x0e\xfe\x99\x86\xc3\xfe\xe0\x9b\n\n3\x8b\x15\xbe\xd9\x1f!\x8a\x01^\x1cN\x05\xee&\xd0\x00\x00\u07d4*gf\n\x13h\xef\xcdbn\xf3k+\x1b`\x19\x80\x94\x1c\x05\x89\a?u\u0460\x85\xba\x00\x00\u07d4*t+\x89\x10\x94\x1e\t2\x83\n\x1d\x96\x92\xcf\u0484\x94\xcf@\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4*tl\xd4@'\xaf>\xbd7\xc3x\xc8^\xf7\xf7T\xab_(\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4*\x81\xd2|\xb6\xd4w\x0f\xf4\xf3\u0123\xba\x18\xe5\xe5\u007f\aQ|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4*\x91\xa9\xfe\xd4\x1b}\x0e\\\xd2\xd81X\xd3\xe8\xa4\x1a\x9a-q\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94*\x9cW\xfe{k\x13\x8a\x92\rgo{\x1a%\x10\x80\xff\xb9\x8a4\xf0\x86\xf3\xb3;h@\x00\x00\u07d4+p\x1d\x16\xc0\xd3\xcc\x1eL\xd8TE\xe6\xad\x02\ue92c\x01-\x89 \x86\xac5\x10R`\x00\x00\xe0\x94+q|\xd42\xa3#\xa4e\x909\x84\x8d;\x87\xde&\xfc\x95F\x8ai\xe1\r\xe7fv\u0400\x00\x00\u07d4+t\xc3s\xd0K\xfb\x0f\xd6\n\x18\xa0\x1a\x88\xfb\xe8Gp\u5309\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4+w\xa4\u060c\rV\xa3\xdb\xe3\xba\xe0J\x05\xf4\xfc\u0477W\xe1\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94+\x84\x88\xbd-<\x19z=&\x15\x18\x15\xb5\xa7\x98\xd2qh\u070a\x01j\x1f\x9f_\xd7\xd9`\x00\x00\u07d4+\x8a\r\xee\\\xb0\xe1\xe9~\x15\xcf\xcan\x19\xad!\xf9\x95\ufb49\x1bUC\x8d\x9a$\x9b\x00\x00\xe0\x94+\x8f\xe4\x16n#\xd1\x19c\xc0\x93+\x8a\u078e\x01E\xea\ap\x8a\t(\x96R\x9b\xad\u0708\x00\x00\xe0\x94+\x99\xb4.OBa\x9e\xe3k\xaa~J\xf2\xd6^\xac\xfc\xba5\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4+\xab\x0f\xbe(\u0544 \xb5 6w\n\x12\xf9\x95*\xeai\x11\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4+\xad\xe9\x1d\x15E\x17b\x0f\u05349\xac\x97\x15zA\x02\xa9\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4+\xaf\x8dn\"\x11t\x12H \xeeI+\x94Y\xecO\xad\xaf\xbb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4+\xaf\xbf\x9e\x9e\xd2\xc2\x19\xf7\xf2y\x13t\xe7\xd0\\\xb0gw\xe7\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\xe0\x94+\xb3f\xb9\xed\xcb\r\xa6\x80\xf0\xe1\v;n(t\x81\x90\xd6\u00ca\x01:b\u05f5v@d\x00\x00\xe0\x94+\xb6\xf5x\xad\xfb\u7ca1\x16\xb3UO\xac\xf9\x96\x98\x13\xc3\x19\x8a\x01\x91'\xa19\x1e\xa2\xa0\x00\x00\u07d4+\xbeb\xea\xc8\f\xa7\xf4\xd6\xfd\xee~}\x8e(\xb6:\xcfw\x0e\x89\x81\xe3-\xf9r\xab\xf0\x00\x00\u07d4+\xbeg*\x18WP\x8fc\x0f*^\xdbV=\x9e\x9d\xe9(\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4+\xc4)\xd6\x18\xa6jL\xf8-\xbb-\x82N\x93V\xef\xfa\x12j\x89lj\xccg\u05f1\xd4\x00\x00\u07d4+\xd2R\xe0\xd72\xff\x1d|x\xf0\xa0.l\xb2T#\xcf\x1b\x1a\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4+\xdd\x03\xbe\xbb\xee';l\xa1\x05\x9b4\x99\x9a[\xbda\xbby\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,\x04\x11\\>R\x96\x1b\r\xc0\xb0\xbf1\xfb\xa4ToYf\xfd\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94,\x06\u0752+aQJ\xaf\xed\xd8D\x88\xc0\u008em\xcf\x0e\x99\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94,\f\xc3\xf9QH,\u0222\x92X\x15hN\xb9\xf9N\x06\x02\x00\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4,\x0e\xe14\u0633aE\xb4{\xee\u7bcd'8\xdb\xda\b\xe8\x89\n\xe5os\x0em\x84\x00\x00\u07d4,\x0f[\x9d\xf46%y\x8e~\x03\xc1\xa5\xfdjm\t\x1a\xf8+\x89\x01\xb0\xfc\xaa\xb2\x000\x00\x00\u07d4,\x12\x8c\x95\xd9W!Q\x01\xf0C\u074f\u0142EmA\x01m\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4,\x18\x00\xf3_\xa0->\xb6\xff[%(_^J\xdd\x13\xb3\x8d\x891\"\u04ed\xaf\xde\x10\x00\x00\u07d4,\x1c\x19\x11N=m\xe2xQHK\x8d'\x15\xe5\x0f\x8a\x10e\x89\x05k\xc7^-c\x10\x00\x00\u07d4,\x1c\xc6\xe1\x8c\x15$\x88\xba\x11\xc2\xcc\x1b\xce\xfa-\xf3\x06\xab\u0449Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94,\x1d\xf8\xa7oH\xf6\xb5K\u03dc\xafV\xf0\xee\x1c\xf5z\xb3=\x8a\x02$\u007fu\x00\x89\xdaX\x00\x00\u07d4,!G\x94z\xe3?\xb0\x98\xb4\x89\xa5\xc1k\xff\xf9\xab\xcdN*\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4,#OP\\\xa8\xdc\xc7}\x9b~\x01\xd2W\xc3\x18\xcc\x199m\x89\x05k\xc7^-c\x10\x00\x00\u07d4,$(\xe4\xa6it\xed\xc8\"\xd5\xdb\xfb$\x1b'(\aQX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,-\x15\xff9V\x1c\x1br\xed\xa1\xcc\x02\u007f\xfe\xf27C\xa1D\x89\u0500\xed\x9e\xf3+@\x00\x00\u07d4,-\xb2\x8c3\t7^\xea1\x82\x1b\x84\xd4\b\x93\x0e\xfa\x1a\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4,Z-\n\xbd\xa0;\xbe!W\x81\xb4\xff)l\x8ca\xbd\xba\xf6\x89\x01\xa8\xe5oH\xc0\"\x80\x00\u07d4,[}{\x19Z7\x1b\xf9\xab\u0774/\xe0O/\x1d\x9a\x99\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4,]\xf8ffj\x19K&\u03bb@~J\x1f\xd7> \x8d^\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94,`?\xf0\xfe\x93alCW>\xf2y\xbf\xea@\x88\x8dj\xe7\x8a\x01\x00\xf4\xb6\xd6gW\x90\x00\x00\xe0\x94,hF\xa1\xaa\x99\x9a\"F\xa2\x87\x05`\x00\xbaM\u02e8\xe6=\x8a\x02\x1f/o\x0f\xc3\xc6\x10\x00\x00\u0794,j\xfc\xd4\x03|\x1e\xd1O\xa7O\xf6u\x8e\tE\xa1\x85\xa8\xe8\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4,ki\x9d\x9e\xad4\x9f\x06\u007fEq\x1a\aJd\x1d\xb6\xa8\x97\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,o\\\x12L\u01c9\xf8\xbb9\x8e?\x88\x97Q\xbcK`-\x9e\x89\x01Y\xf2\v\xed\x00\xf0\x00\x00\u07d4,\x83\xae\xb0/\xcf\x06}e\xa4p\x82\xfd\x97x3\xab\x1c\uc449\b'8#%\x8a\xc0\x00\x00\xe0\x94,\x89\xf5\xfd\xca=\x15T\t\xb68\xb9\x8at.U\xebFR\xb7\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4,\x96HI\xb1\xf6\x9c\xc7\u03a4D%8\xed\x87\xfd\xf1l\xfc\x8f\x89lk\x93[\x8b\xbd@\x00\x00\u0794,\x9f\xa7,\x95\xf3}\b\xe9\xa3`\t\u7930\u007f)\xba\xd4\x1a\x88\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4,\xafk\xf4\xec}Z\x19\xc5\xe0\x89z^\xeb\x01\x1d\xce\xceB\x10\x89\a\x93H5\xa01\x16\x00\x00\u07d4,\xb4\xc3\xc1k\xb1\xc5^|kz\x19\xb1'\xa1\xac\x93\x90\xcc\t\x89\xb8'\x94\xa9$O\f\x80\x00\xe0\x94,\xb5IZPS6\xc2FT\x10\xd1\xca\xe0\x95\xb8\xe1\xba\\\u074a\x04<3\xc1\x93ud\x80\x00\x00\u07d4,\xb6\x15\a:@\xdc\u06d9\xfa\xa8HW.\x98{;\x05n\xfb\x89+X\xad\u06c9\xa2X\x00\x00\u07d4,\xbam]\r\xc2\x04\xea\x8a%\xad\xa2\xe2oVu\xbd_/\u0709H#\xef}\u06da\xf3\x80\x00\u07d4,\xbb\fs\u07d1\xb9\x17@\xb6i;wJ}\x05\x17~\x8eX\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4,\xcbfIM\n\xf6\x89\xab\xf9H=6]x$D\xe7\u07ad\x8965\u026d\xc5\u07a0\x00\x00\u07d4,\xcc\x1f\x1c\xb5\xf4\xa8\x00.\x18k \x88]\x9d\xbc\x03\f\b\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,\u03c0\xe2\x18\x98\x12^\xb4\xe8\a\u0342\xe0\x9b\x9d(Y/n\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,\u0456\x94\u0452j\x0f\xa9\x18\x9e\u07ba\xfcg\x1c\xf1\xb2\u02a5\x8965\u026d\xc5\u07a0\x00\x00\u07d4,\u04d34\xac~\xacyrW\xab\xe3sa\x95\xf5\xb4\xb5\xce\x0f\x89\x05kGx^7&\x00\x00\u07d4,\u05de\xb5 '\xb1,\x18\x82\x8e>\xaa\xb2\x96\x9b\xfc\u0487\xe9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,\xd8xfV\x8d\xd8\x1a\xd4}\x9d:\u0404nZePss\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4,\xdb9De\x06\x16\xe4|\xb1\x82\xe0`2/\xa1Hyx\u0389b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4,\xe1\x1a\x92\xfa\xd0$\xff+>\x87\xe3\xb5B\xe6\xc6\r\xcb\u0656\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4-\x03&\xb2?\x04\t\xc0\xc0\xe9#hc\xa13\aZ\x94\xba\x18\x89\vg\x9b\xe7[\xe6\xae\x00\x00\u07d4-\r\xecQ\xa6\xe8s0\xa6\xa8\xfa*\x0fe\u060dJ\xbc\xdfs\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4-#vkok\x05s}\xad\x80\xa4\x19\xc4\x0e\xdaMw\x10>\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4-+\x03#Y\xb3c\x96O\xc1\x1aQ\x82c\xbf\xd0T1\xe8g\x89\b\x1c\x1d\xf7b\x9ep\x00\x00\u07d4-4\x80\xbf\be\aJr\xc7u\x9e\xe5\x13{Mp\xc5\x1c\xe9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4-5\xa9\xdfbu\u007f\u007f\xfa\xd1\x04\x9a\xfb\x06\xcaJ\xfcFLQ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4-@U\x8b\x06\xf9\n9#\x14U\x92\x12;gt\xe4n1\xf4\x8965\u026d\xc5\u07a0\x00\x00\u07d4-Bi\x12\xd0Y\xfa\xd9t\v.9\n.\xea\xc0To\xf0\x1b\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4-S-\xf4\xc69\x11\xd1\u0391\xf6\xd1\xfc\xbf\xf7\x96\x0fx\xa8\x85\x89Z\x85\x96\x8aXx\u0680\x00\u07d4-S\x91\xe98\xb3HX\u03d6[\x84\x051\xd5\xef\xdaA\v\t\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94-[B\xfcY\xeb\xda\r\xfdf\xae\x91K\u008c\x1b\nn\xf8:\x8a+\u0235\x9f\xdc\xd86c\x80\x00\u07d4-]s5\xac\xb06+G\u07e3\xa8\xa4\xd3\xf5\x94\x95D\u04c0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-a\xbf\xc5hs\x92<+\x00\t]\xc3\xea\xa0\xf5\x90\u062e\x0f\x8a\x04ef\xdf\xf8\xceU`\x00\x00\u07d4-e\x11\xfdz8\x00\xb2hT\xc7\xec9\xc0\u0735\xf4\xc4\xe8\xe8\x89\x15\xad\u077a/\x9ew\x00\x00\u07d4-}\\@\u076f\xc4P\xb0Jt\xa4\u06bc+\xb5\xd6e\x00.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4-\x89\xa8\x00jO\x13z \xdc+\xecF\xfe.\xb3\x12\xea\x96T\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4-\x8cR2\x9f8\u04a2\xfa\x9c\xba\xf5\u0143\xda\xf1I\v\xb1\x1c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4-\x8e\x06\x18\x92\xa5\xdc\xce!\x96j\xe1\xbb\a\x88\xfd>\x8b\xa0Y\x89\r\x8e\\\xe6\x17\xf2\xd5\x00\x00\u07d4-\x8e[\xb8\xd3R\x16\x95\xc7~|\x83N\x02\x91\xbf\xac\xeet\b\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4-\x90\xb4\x15\xa3\x8e.\x19\xcd\xd0/\U000ed069z\xf7\xcb\xf6r\x89\x05\xf3\xc7\xf6A1\xe4\x00\x00\u07d4-\x9b\xado\x1e\xe0*p\xf1\xf1=\xef\\\u0332z\x9a'@1\x89a\t=|,m8\x00\x00\u07d4-\x9c_\xec\u04b4O\xbbj\x1e\xc72\xea\x05\x9fO\x1f\x9d+\\\x896\xca2f\x1d\x1a\xa7\x00\x00\xe0\x94-\xa6\x17iP\t\xccW\xd2j\u0510\xb3*]\xfb\xeb\x93N^\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4-\xa7k|9\xb4 \u323a,\x10 \xb0\x85k\x02pd\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4-\u01ddn\u007fU\xbc\xe2\xe2\xd0\xc0*\xd0|\uca3bR\x93T\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\xe0\x94-\xca\x0eD\x9a\xb6F\xdb\xdf\u04d3\xa9fb\x96\v\u02b5\xae\x1e\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4-\xd3%\xfd\xff\xb9{\x19\x99R\x84\xaf\xa5\xab\xdbWJ\x1d\xf1j\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4-\xd5x\xf7@}\xfb\xd5H\xd0^\x95\xcc\u00dcHT)bj\x89\u3bb5sr@\xa0\x00\x00\u07d4-\xd8\xee\xef\x87\x19J\xbc,\xe7X]\xa1\xe3[|\xeax\f\xb7\x8965\xc6 G9\u0640\x00\u07d4-\xdf@\x90Wi\xbc\xc4&\xcb,)8\xff\xe0w\xe1\u8758\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4-\xe0\x96D\x00\u0082\xbd\u05ca\x91\x9ck\xf7|k_yay\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-\xe3\x1a\xfd\x18\x9a\x13\xa7o\xf6\xfes\xea\xd9\xf7K\xb5\u0126)\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4-\xec\x982\x9d\x1f\x96\u00e5\x9c\xaay\x81uTR\xd4\xdaI\u0549\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-\ue422\x8f\x19-gj\x87s#+V\xf1\x8f#\x9e/\xad\x8a\x03\xef\xa7\xe7G\xb6\u046d\x00\x00\xe0\x94.\b\x80\xa3E\x96#\a \xf0Z\xc8\xf0e\xaf\x86\x81\u0736\u008a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4.\fW\xb4qP\xf9Z\xa6\xa7\xe1j\xb9\xb1\xcb\xf5C(\x97\x9a\x89\x05k\xc7^-c\x10\x00\x00\u07d4.\x10\x91\v\xa6\xe0\xbc\x17\xe0UUf\x14\u02c7\t\x0fM~[\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94.$\xb5\x97\x87;\xb1A\xbd\xb27\xea\x8aZ\xb7Gy\x9a\xf0-\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4.(\x10\xde\xe4J\xe4\xdf\xf3\xd8cB\xab\x12fW\xd6S\xc36\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4.,\xbdz\xd8%G\xb4\xf5\xff\x8b:\xb5o\x94*dE\xa3\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4.-~\xa6k\x9fG\xd8\xccR\xc0\x1cR\xb6\u147c}G\x86\x89\xd8\xd4`,&\xbfl\x00\x00\u07d4.C\x93H\u07caBw\xb2*v\x84W\xd1\x15\x8e\x97\xc4\t\x04\x89*\x1e\x9f\xf2o\xbfA\x00\x00\xe0\x94.F\xfc\xeej;\xb1E\xb5\x94\xa2C\xa3\x91?\xce]\xado\xba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794.G\xf2\x87\xf4\x98#7\x13\x85\r1&\x82<\xc6}\xce\xe2U\x88\u029d\x9e\xa5X\xb4\x00\x00\u07d4.N\u1b99j\xa0\xa1\xd9$(\xd0fR\xa6\xbe\xa6\xd2\xd1]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.R\x91+\xc1\x0e\xa3\x9dT\xe2\x93\xf7\xae\u05b9\x9a\x0fLs\xbe\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4.a\x9fW\xab\xc1\u91ea\x93j\xe3\xa2&Ib\xe7\xeb-\x9a\x89(\xfb\x9b\x8a\x8aSP\x00\x00\u07d4.d\xa8\xd7\x11\x11\xa2/L]\xe1\xe09\xb36\xf6\x8d9\x8a|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.i3T=O,\xc0\vSP\xbd\x80h\xba\x92C\u05be\xb0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94.~\x05\xe2\x9e\u0767\xe4\xae%\xc5\x175C\xef\xd7\x1fm=\x80\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4.\u007fFU \xec5\xcc#\u058eue\x1b\xb6h\x95D\xa1\x96\x898\xec[r\x1a\x1a&\x80\x00\u07d4.\x8e\xb3\nqn_\xe1\\t#>\x03\x9b\xfb\x11\x06\xe8\x1d\x12\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94.\x98$\xb5\xc12\x11\x1b\xca$\xdd\xfb\xa7\xe5u\xa5\xcdr\x96\xc1\x8a\x03\xa4\x84Qnm\u007f\xfe\x00\x00\u07d4.\xa5\xfe\xe6?3z7nK\x91\x8e\xa8!H\xf9MH\xa6&\x89e\x0f\x8e\r\u0493\xc5\x00\x00\u07d4.\xafN*F\xb7\x89\xcc\u0088\xc8\xd1\xd9)N?\xb0\x858\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.\xaf\xf9\xf8\xf8\x110d\u04d5z\xc6\xd6\xe1\x1e\xeeB\xc8\x19]\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4.\xba\fn\xe5\xa1\x14\\\x1cW9\x84\x96:`]\x88\nz \x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4.\xc9X\"\xeb\x88{\xc1\x13\xb4q*M\xfd\u007f\x13\xb0\x97\xb5\xe7\x8965\u026d\xc5\u07a0\x00\x00\u07d4.\xcaj<]\x9fD\x9d\tV\xbdC\xfa{M{\xe8CYX\x89lk\xdaip\x9c\xc2\x00\x00\xe0\x94.\xca\xc5\x04\xb23\x86n\xb5\xa4\xa9\x9e{\u0490\x13Y\xe4;=\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4.\xeb\xf5\x942\xb5(\x92\xf98\v\xd1@\xaa\x99\xdc\xf8\xad\f\x0f\x89\b=lz\xabc`\x00\x00\u07d4.\xee\xd5\x04q\xa1\xa2\xbfS\xee0\xb1#.n\x9d\x80\xef\x86m\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4.\xefk\x14\x17\u05f1\x0e\xcf\xc1\x9b\x12:\x8a\x89\xe7>RlX\x89 \x86\xac5\x10R`\x00\x00\u07d4.\xf8i\xf05\vW\xd54x\xd7\x01\xe3\xfe\xe5)\xbc\x91\x1cu\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4.\xf9\xe4eqj\xca\u03f8\xc8%/\xa8\xe7\xbcyi\xeb\xf6\u4255\x9e\xb1\xc0\xe4\xae \x00\x00\xe0\x94.\xfcLd}\xacj\xca\xc3Uw\xad\"\x17X\xfe\xf6ao\xaa\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4/\x13eu&\xb1w\xca\xd5G\u00d0\x8c\x84\x0e\xffd{E\u0649?v\x84\x9c\xf1\xee,\x80\x00\u07d4/\x18}ZpMZ3\x8c[(v\xa0\x90\xdc\xe9d(N)\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94/%#\u0303O\x00\x86\x05$\x02bb\x96gQ\x86\xa8\u508a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4/(*\xbb\xb6\u0523\xc3\xcd;\\\xa8\x12\xf7d>\x800_\x06\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4/+\xba\x1b\x17\x96\x82\x1avo\xced\xb8O(\xech\xf1Z\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4/1]\x90\x16\xe8\xee_Sf\x81 /\x90\x84\xb02TMM\x898<\xd1+\x9e\x86<\x00\x00\u07d4/M\xa7SC\x0f\xc0\x9es\xac\xbc\xcd\xcd\xe9\xdad\u007f+]7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4/P\x80\xb8?~-\xc0\xa1\xdd\x11\xb0\x92\xad\x04+\xffx\x8fL\x89\xb4\xf8\xfby#\x1d+\x80\x00\u07d4/a\uf941\x9dp_+\x1eN\xe7T\xae\xb8\xa8\x19Pju\x89O%\x91\xf8\x96\xa6P\x00\x00\xe0\x94/f\xbf\xbf\"b\xef\u030d+\xd0DO\u0170ib\x98\xff\x1e\x8a\x02\x1a\xd95\xf7\x9fv\xd0\x00\x00\u07d4/m\xce\x130\u015e\xf9!`!TW-MK\xac\xbd\x04\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4/}2\x90\x85\x1b\xe5\u01b4\xb4?}Et2\x9fa\xa7\x92\u00c9\x05k\xc7^-c\x10\x00\x00\u07d4/\x858\x17\xaf\u04f8\xf3\xb8n\x9f`\xeew\xb5\xd9ws\xc0\xe3\x89N\xae\xeaD\xe3h\xb9\x00\x00\u07d4/\xa4\x91\xfbY \xa6WN\xbd(\x9f9\xc1\xb2C\r-\x9aj\x89lk\x93[\x8b\xbd@\x00\x00\u07d4/\xb5f\xc9K\xbb\xa4\xe3\xcbg\xcd\xda}_\xadq1S\x91\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4/\xbbPJ]\xc5'\xd3\xe3\xeb\x00\x85\xe2\xfc<}\xd58\xcbz\x89C\u00b1\x8a\xec<\n\x80\x00\u07d4/\xbc\x85y\x8aX5\x98\xb5\"\x16mn\x9d\xda\x12\x1db}\xbc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4/\xbc\xef3\x84\xd4 \xe4\xbfa\xa0f\x99\x90\xbcpT\U00065bc9lk\x93[\x8b\xbd@\x00\x00\xe0\x94/\xc8.\xf0v\x93#A&Oaz\f\x80\xddW\x1ej\xe99\x8a\x01\x84$\xf5\xf0\xb1\xb4\xe0\x00\x00\u07d4/\u075by\u07cd\xf50\xadc\xc2\x0eb\xafC\x1a\xe9\x92\x16\xb8\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4/\xe0\x02?W\"e\x0f:\x8a\xc0\x10\t\x12^t\xe3\xf8.\x9b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4/\xe0\xccBKS\xa3\x1f\t\x16\xbe\b\xec\x81\xc5\v\xf8\xea\xb0\xc1\x89 \x86\xac5\x10R`\x00\x00\u07d4/\xe1:\x8d\a\x85\u0787X\xa5\xe4\x18v\xc3n\x91l\xf7Pt\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4/\xea\x1b/\x83O\x02\xfcT3?\x8a\x80\x9f\x048\xe5\x87\n\xa9\x89\x01\x18T\xd0\xf9\xce\xe4\x00\x00\u07d4/\xee6\xa4\x9e\xe5\x0e\xcfqo\x10G\x91VFw\x9f\x8b\xa0?\x899B\"\xc4\u0686\xd7\x00\x00\u07d4/\xef\x81G\x8aK.\x80\x98\xdb_\xf3\x87\xba!S\xf4\xe2+y\x896'\xe8\xf7\x127<\x00\x00\u07d4/\xf1`\xc4Or\xa2\x99\xb5\xec-q\xe2\x8c\xe5Dm/\u02ef\x89\x13\x84\x00\xec\xa3d\xa0\x00\x00\u07d4/\xf1\xcaU\xfd\x9c\xec\x1b\x1f\xe9\U00029af7LQ<\x1e*\xaa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94/\xf5\u02b1,\r\x95\u007f\xd33\xf3\x82\xee\xb7Q\a\xa6L\xb8\xe8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94/\xf80\xcfU\xfb\x00\u0560\xe05\x14\xfe\xcdD1K\xd6\xd9\xf1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4/\xfe\x93\xec\x1aV6\xe9\xee4\xafp\xdf\xf5&\x82\xe6\xffpy\x89lk\x93[\x8b\xbd@\x00\x00\u07d40\x03y\x88p&q\xac\xbe\x89,\x03\xfeW\x88\xaa\x98\xaf(z\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d40$\x8dX\xe4\x14\xb2\x0f\xed:lH+Y\xd9\xd8\xf5\xa4\xb7\xe2\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4019\xbcYd\x03\xd5\u04d3\x1fwLf\u013aFtT\u06c9\\%\xe1J\xea(?\x00\x00\u079408\x00\x87xie\x14\x9e\x81B;\x15\xe3\x13\xba2\xc5\u01c3\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d40:0\xacB\x86\xae\x17\xcfH=\xad{\x87\fk\xd6M{J\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d40?\xba\xeb\xbeF\xb3[n[t\x94j_\x99\xbc\x15\x85\xca\xe7\x89/\x9a\xc0i_[\xba\x00\x00\u07d40ADZ3\xba\x15\x87A\x16\r\x9c4N\xb8\x8e\\0o\x94\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d40H\x01d\xbc\xd8It\xeb\xc0\xd9\f\x9b\x9a\xfa\xb6&\xcd\x1cs\x89+^:\xf1k\x18\x80\x00\x00\u07d40N\u019atTW!\xd71j\xefM\u03f4\x1a\u015e\xe2\xf0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x940Q\x182\x91\x8d\x804\xa7\xbe\xe7.\xf2\xbf\xeeD\x0e\u02fc\xf6\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d40Q?\u029f6\xfdx\x8c\xfe\xa7\xa3@\xe8m\xf9\x82\x94\xa2D\x89\x18;_\x03\xb1G\x9c\x00\x00\u07d40U\xef\xd2`)\xe0\xd1\x1b\x93\r\xf4\xf5;\x16,\x8c?\xd2\u0389\x1b\x1a\b\x927\a=\x00\x00\u07d40]&\xc1\v\xdc\x10?k\x9c!'.\xb7\xcb-\x91\b\xc4~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d40_x\xd6\x18\xb9\x90\xb4)[\xac\x8a-\xfa&(\x84\xf8\x04\xea\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x940d\x89\x9a\x96\x1a>\x1d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d40\x98\xb6]\xb9>\xca\xca\xf75\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d40\uc4d2$J!\b\u0247\xbc\\\xdd\xe0\ud7c3z\x81{\x89T\x99%\xf6\xc9\xc5%\x00\x00\xe0\x940\xed\x11\xb7{\xc1~^f\x94\u023c[nG\x98\xf6\x8d\x9c\xa7\x8a\x1eo\xb3B\x1f\xe0)\x9e\x00\x00\u07d40\xf7\xd0%\xd1o{\xee\x10U\x80Ho\x9fV\x1c{\xae?\xef\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x940\xfb\xe5\x88_\x9f\xcc\xe9\xea^\u06c2\xedJ\x11\x96\xdd%\x9a\xed\x8a\x01\x19\xe4\u007f!8\x1f@\x00\x00\u07d41\x04}p?c\xb94$\xfb\xbdn/\x1f\x9et\xde\x13\xe7\t\x89\x9a\x81f\xf7\u6ca7\x80\x00\u07d411?\xfdc[\xf2\xf32HA\xa8\x8c\a\xed\x14aD\xce\xeb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d41Y\xe9\fH\xa9\x15\x90J\xdf\u24b2/\xa5\xfd^ryk\x896\xaf\xe9\x8f&\x06\x10\x00\x00\u07d41]\xb7C\x9f\xa1\u0574#\xaf\xa7\xddq\x98\xc1\xcft\xc9\x18\xbc\x89 \x86\xac5\x10R`\x00\x00\u07d41^\xf2\xdab\x0f\xd30\xd1.\xe5]\xe5\xf3)\xa6\x96\xe0\xa9h\x89\b!\xab\rD\x14\x98\x00\x00\u07d41n\x92\xa9\x1b\xbd\xa6\x8b\x9e/\x98\xb3\xc0H\x93N<\xc0\xb4\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d41n\xb4\xe4}\xf7\x1bB\xe1mo\xe4h%\xb72{\xaf1$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d41q\x87~\x9d\x82\f\xc6\x18\xfc\t\x19\xb2\x9e\xfd3?\xdaI4\x8965\u026d\xc5\u07a0\x00\x00\u07d41|\xf4\xa2<\xb1\x91\xcd\xc5c\x12\u009d\x15\xe2\x10\xb3\xb9\xb7\x84\x89\a\xcef\xc5\x0e(@\x00\x00\u07d41\x8b.\xa5\xf0\xaa\xa8y\xc4\xd5\xe5H\xac\x9d\x92\xa0\xc6t\x87\xb7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d41\x8cv\xec\xfd\x8a\xf6\x8dpUSR\xe1\xf6\x01\xe3Y\x88\x04-\x89\x1b1\x19.h\xc7\xf0\x00\x00\u07d41\x8f\x1f\x8b\xd2 \xb0U\x8b\x95\xfb3\x10\x0f\xfd\xbbd\r|\xa6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d41\xaa;\x1e\xbe\x8cM\xbc\xb6\xa7\b\xb1\xd7H1\xe6\x0eIv`\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d41\xab\b\x89f\xec\xc7\"\x92X\xf6\t\x8f\xceh\xcf9\xb3\x84\x85\x8965\u026d\xc5\u07a0\x00\x00\xe0\x941\xadM\x99F\xef\t\xd8\xe9\x88\xd9F\xb1\"\u007f\x91A\x90\x176\x8a\x04\xd8S\xc8\xf8\x90\x89\x80\x00\x00\xe0\x941\xb4;\x01]\x00\x81d~h\x00\x00\u07d424\x86\xcad\xb3uGO\xb2\xb7Y\xa9\xe7\xa15\x85\x9b\xd9\xf6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d427I\xa3\xb9q\x95\x9eF\u0234\x82-\xca\xfa\xf7\xaa\xf9\xbdn\x89\x01\x16q\xa5\xb2Ep\x00\x00\u07d42:\xadA\xdfKo\xc8\xfe\u038c\x93\x95\x8a\xa9\x01\xfah\bC\x894\x95tD\xb8@\xe8\x00\x00\xe0\x942;<\xfe>\xe6+\xbd\xe2\xa2a\xe5<\xb3\xec\xc0X\x10\xf2\u018a\x02\ub3b1\xa1r\u0738\x00\x00\u07d42?\xca^\xd7\u007fi\x9f\x9d\x990\xf5\xce\xef\xf8\xe5oY\xf0<\x89Hz\x9a0E9D\x00\x00\u07d42H\\\x81\x87(\xc1\x97\xfe\xa4\x87\xfb\xb6\xe8)\x15\x9e\xba\x83p\x899!\xb4\x13\xbcN\xc0\x80\x00\xe0\x942P\xe3\xe8X\xc2j\xde\u032d\xf3jVc\xc2*\xa8LAp\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x942Y\xbd/\xdd\xfb\xbco\xba\u04f6\xe8t\xf0\xbb\xc0,\xda\x18\xb5\x8a\x02\x84`VI[\r\x18\x80\x00\u07d42uIo\xd4\u07491\xfdi\xfb\n\v\x04\xc4\xd1\xff\x87\x9e\xf5\x89\x18-~L\xfd\xa08\x00\x00\u07d42{\xb4\x9euOo\xb4\xf73\xc6\xe0o9\x89\xb4\xf6]K\xee\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d42\x82y\x1do\xd7\x13\xf1\xe9OK\xfdV^\xaax\xb3\xa0Y\x9d\x89Hz\x9a0E9D\x00\x00\u07d42\x83\xeb\u007f\x917\xdd9\xbe\xd5_\xfek\x8d\xc8E\xf3\xe1\xa0y\x89\x03\x97\n\xe9!Ux\x00\x00\u07d42\x86\t\x97\xd70\xb2\xd8;s$\x1a%\xd3f}Q\xc9\b\xef\x89\x1b\x1a\b\x927\a=\x00\x00\xe0\x942\x86\u047cez1,\x88G\xd9<\xb3\xcbyP\xf2\xb0\xc6\xe3\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x942\xa2\r\x02\x8e,b\x18\xb9\xd9[D\\w\x15$cj\"\xef\x8a\x02\x02\xfe\xfb\xf2\xd7\xc2\xf0\x00\x00\u07d42\xa7\x06\x91%\\\x9f\xc9y\x1aOu\u0238\x1f8\x8e\n%\x03\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d42\xb7\xfe\xeb\xc5\u015b\xf6^\x86\x1cL\v\xe4*v\x11\xa5T\x1a\x89w\u9aa8R\\\x10\x00\x00\xe0\x942\xba\x9a}\x04#\xe0:R_\xe2\xeb\xebf\x1d \x85w\x8b\u060a\x04<3\xc1\x93ud\x80\x00\x00\u07d42\xbb.\x96\x93\xe4\xe0\x854M/\r\xbdF\xa2\x83\u3807\xfd\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x942\xc2\xfd\u2daa\xbb\x80\u5ba2\xb9I\xa2\x17\xf3\xcb\t\"\x83\x8a\x010a`\xaf\xdf 7\x80\x00\u07d42\xd9P\xd5\xe9>\xa1\u0574\x8d\xb4qO\x86{\x03 \xb3\x1c\x0f\x897\b\xba\xed=h\x90\x00\x00\u07d42\u06f6qlT\xe81e\x82\x9aJ\xbb6uxI\xb6\xe4}\x8965\u026d\xc5\u07a0\x00\x00\u07d42\xebd\xbe\x1b]\xed\xe4\b\u01bd\xef\xben@\\\x16\xb7\xed\x02\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d42\xef\\\xdcg\x1d\xf5V*\x90\x1a\xee]\xb7\x16\xb9\xbev\xdc\xf6\x89lk\x93[\x8b\xbd@\x00\x00\u07d42\xf2\x9e\x87'\xa7LkC\x01\xe3\xff\xff\x06\x87\xc1\xb8p\xda\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d42\xfa\x0e\x86\xcd\b}\u058di1\x90\xf3-\x931\t\t\xedS\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d42\xfb\xee\xd6\xf6&\xfc\xdf\xd5\x1a\xca\xfbs\v\x9e\xef\xf6\x12\xf5d\x89lk\x93[\x8b\xbd@\x00\x00\u07943\x00\xfb\x14\x9a\xde\xd6[\u02e6\xc0N\x9c\u05b7\xa0;\x89;\xb1\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x943\x01\xd9\xca/;\xfe\x02by\xcdh\x19\xf7\x9a)=\x98\x15n\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\xe0\x943\b\xb04f\xc2z\x17\xdf\xe1\xaa\xfc\xeb\x81\xe1m)4Vo\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07943\x1a\x1c&\xcci\x94\xcd\xd3\xc1K\xec\xe2v\xff\xffK\x9d\xf7|\x88\xfaz\xed\xdfO\x06\x80\x00\xe0\x943&\xb8\x8d\xe8\x06\x18DT\xc4\v'\xf3\t\xd9\xddm\u03f9x\x8a\x03\xca\\f\u067cD0\x00\x00\xe0\x943)\xeb;\xafCE\xd6\x00\xce\xd4\x0en\x99ueo\x117B\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d432\r\xd9\x0f+\xaa\x11\r\xd34\x87*\x99\x8f\x14\x84&E<\x8965f3\xeb\xd8\xea\x00\x00\u07d436\xc3\xefn\x8bP\xee\x90\xe07\xb1d\xb7\xa8\xea_\xaa\xc6]\x89\x0e\u0223\xa7\x1c\"T\x00\x00\xe0\x9438\fo\xffZ\xcd&Q0\x96)\u06daq\xbf? \u017a\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d43:\xd1Yd\x01\xe0Z\xea-6\xcaG1\x8e\xf4\xcd,\xb3\u07c9\x9d\xc0\\\xce(\u00b8\x00\x00\u07d43C@\xeeK\x9c\u0701\xf8P\xa7Q\x16\xd5\x0e\u9d98%\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d43H\x1e\x85n\xbe\u050e\xa7\b\xa2t&\xef(\xe8g\xf5|\u0449\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x943V[\xa9\xda,\x03\xe7x\xce\x12)O\b\x1d\xfe\x81\x06M$\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07943X\x1c\xee#0\x88\xc0\x86\r\x94N\f\xf1\u03ab\xb8&\x1c.\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d43XX\xf7I\xf1i\u02bc\xfeR\xb7\x96\xe3\xc1\x1e\xc4~\xa3\u0089\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x943^\"\x02[zw\u00e0t\u01cb\x8e=\xfe\a\x13A\x94n\x8a\x02'\xcas\n\xb3\xf6\xac\x00\x00\u07d43b\x9b\xd5/\x0e\x10{\xc0q\x17ld\xdf\x10\x8fdw}I\x89\x01\xcf\xddth!n\x80\x00\u07d43{;\u07c6\xd7\x13\xdb\xd0{]\xbf\xcc\x02+z{\x19F\xae\x89\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d43|\xfe\x11W\xa5\u0191 \x10\xddV\x153y\x17i\u00b6\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d43\xb36\xf5\xba^\xdb{\x1c\xcc~\xb1\xa0\u0644\xc1#\x1d\x0e\u0709lk\x93[\x8b\xbd@\x00\x00\u07d43\xc4\a\x13;\x84\xb3\xcaL=\xed\x1fFX\x90\f8\x10\x16$\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\xe0\x943\xd1r\xab\a\\Q\xdb\x1c\xd4\n\x8c\xa8\xdb\xff\r\x93\xb8C\xbb\x8a\x016x\x05\x10\xd1-\xe3\x80\x00\u07d43\xe9\xb7\x18#\x95.\x1ff\x95\x8c'\x8f\u008b\x11\x96\xa6\u0164\x89\x05k\xc7^-c\x10\x00\x00\u07d43\xeakxU\xe0[\a\xab\x80\u06b1\xe1M\xe9\xb6I\xe9\x9bl\x89\x1c\xd6\xfb\xadW\xdb\xd0\x00\x00\u07d43\xf1R#1\rD\u078bf6h_:L=\x9cVU\xa5\x89\r\x94b\xc6\xcbKZ\x00\x00\u07d43\xf4\xa6G\x1e\xb1\xbc\xa6\xa9\xf8[;Hr\xe1\aU\xc8+\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d43\xfbWzM!O\xe0\x10\xd3,\xca|>\xed\xa6?\x87\xce\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d43\xfdq\x8f\v\x91\xb5\xce\u020a]\xc1^\xec\xf0\xec\xef\xa4\xef=\x89\x17r$\xaa\x84Lr\x00\x00\u07d44\x14\x80\u030c\xb4v\xf8\xd0\x1f\xf3\b\x12\xe7\xc7\x0e\x05\xaf\xaf]\x89lk\x93[\x8b\xbd@\x00\x00\u07d44'-^ut1]\xca\u9afd1{\xac\x90(\x9dGe\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x9440\xa1c\x81\xf8i\xf6\xeaT#\x91XU\xe8\x00\x885%\xa9\x8a\x03\xca\\f\u067cD0\x00\x00\u07d441\x86%\x81\x8e\xc1?\x11\x83Z\xe9sS\xce7}oY\n\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d449<]\x91\xb9\xdeYr\x03\xe7[\xacC\t\xb5\xfa=(\u00c9\n\x84Jt$\xd9\xc8\x00\x00\u07d449\x99\x8b$|\xb4\xbf\x8b\xc8\nm+5'\xf1\xdf\xe9\xa6\u0489\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d44C}\x14ed\v\x13l\xb5\x84\x1c?\x93O\x9b\xa0\xb7\t}\x89\t`\xdbwh\x1e\x94\x00\x00\u07d44J\x8d\xb0\x86\xfa\xedN\xfc7\x13\x1b:\"\xb0x-\xadp\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x944fM\"\x0f\xa7\xf3yX\x02J32\u0584\xbc\xc6\xd4\u023d\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d44f\xf6~9cl\x01\xf4;:!\xa0\xe8R\x93%\xc0\x86$\x89-\xb1\x16vP\xac\xd8\x00\x00\u07d44\x856\x1e\xe6\xbf\x06\xefe\b\xcc\xd2=\x94d\x1f\x81M>/\x89lk\x93[\x8b\xbd@\x00\x00\u07d44\x85\xf6!%d3\xb9\x8aB\x00\xda\xd8W\xef\xe5Y7\uc609lk\x93[\x8b\xbd@\x00\x00\u07d44\x95\x8aF\xd3\x0e0\xb2s\xec\xc6\xe5\xd3X\xa2\x12\xe50~\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d44\x97\xddf\xfd\x11\x80q\xa7\x8c,\xb3n@\xb6e\x1c\xc8%\x98\x89\x05\xf1\x01kPv\xd0\x00\x00\xe0\x944\x9a\x81k\x17\xab='\xbb\xc0\xae\x00Q\xf6\xa0p\xbe\x1f\xf2\x9d\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d44\x9d,\x91\x8f\u041e(\a1\x8ef\xceC)\t\x17k\xd5\v\x89<\xb7\x1fQ\xfcU\x80\x00\x00\u07d44\xa0C\x1f\xff^\xad\x92\u007f\xb6`\f\x1e\xa8\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d44\xff&\xeb`\xa8\u0469ZH\x9f\xae\x13n\xe9\x1dNX\bL\x89 \x86\xac5\x10R`\x00\x00\u07d44\xffX)R\xff$E\x8f{\x13\xd5\x1f\vO\x98p\"\xc1\xfe\x89\x98\x06\xde=\xa6\xe9x\x00\x00\u07d45\x10k\xa9N\x85c\u0533\xcb<\\i,\x10\xe6\x04\xb7\xce\u0609lk\x93[\x8b\xbd@\x00\x00\xe0\x945\x14_b\x03\x97\u019c\xb8\xe0\tb\x96\x1f\x0fH\x86d9\x89\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d45\x14t0\xc3\x10e\x00\u77e2\xf5\x02F.\x94p<#\xb1\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x945\x17\x87\x845\x05\xf8\xe4\xef\xf4ef\xcc\u695fM\x1c_\xe7\x8a\x01\xf5q\x89\x87fKH\x00\x00\xe0\x945\x1f\x16\xe5\xe0sZ\xf5gQ\xb0\xe2%\xb2B\x11q9@\x90\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\xe0\x945$\xa0\x00#N\xba\xaf\a\x89\xa14\xa2\xa4\x178<\xe5(*\x8a\x011yU\x94}\x8e,\x00\x00\u07d45&\xee\xce\x1ak\xdc>\xe7\xb4\x00\xfe\x93[HF?1\xbe\u05c9\x04w\x87\x9bm\x140\x00\x00\u07d45*x_J\x92\x162PL\xe5\xd0\x15\xf8\xd7FO\xa3\xdb\x14\xe7r\x92\x13\u03aa7\x8c\t^\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d45\xaf\x04\n\f\xc23zv\xaf(\x81T\xc7V\x1e\x1a#3I\x8965\u026d\xc5\u07a0\x00\x00\u07d45\xb0>\xa4$W6\xf5{\x85\xd2\xebyb\x8f\x03m\xdc\xd7\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d45\xbd$he\xfa\xb4\x90\xac\bz\xc1\xf1\xd4\xf2\xc1\r\f\xda\x03\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x945\xbff\x88R/5Fz\u007fu0#\x14\xc0+\xa1v\x80\x0e\x8a\x03\xafA\x82\x02\xd9T\xe0\x00\x00\u07d45\u022d\xc1\x11%C+;w\xac\xd6F%\xfeX\xeb\xee\x9df\x89lk\x93[\x8b\xbd@\x00\x00\u07d45\u0497\x0fI\xdc\xc8\x1e\xa9\xeep~\x9c\x8a\n\xb2\xa8\xbbtc\x89N\x10\x03\xb2\x8d\x92\x80\x00\x00\u07d45\xe0\x96\x12\r\xea\xa5\xc1\xec\xb1d^,\u02cbN\xdb\xd9)\x9a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d45\xea!c\xa3\x8c\u07da\x12?\x82\xa5\xec\x00%\x8d\xae\v\xc7g\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d45\xf1\xda\x12{\x837o\x1b\x88\xc8*3Y\xf6z^g\xddP\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d45\xf2\x94\x9c\xf7\x8b\xc2\x19\xbbO\x01\x90|\xf3\xb4\xb3\u04c6T\x82\x89\x0f\xb5\xc8l\x92\xe44\x00\x00\u07d45\xf5\x86\x01I\xe4\xbb\xc0K\x8a\u0172r\xbeU\xad\x1a\xcaX\xe0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d46\x02E\x8d\xa8omj\x9d\x9e\xb0=\xaf\x97\xfeV\x19\xd4B\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x057-\x93\xa9\x01\t\x88\x01\x8f\x9f1]\x03.\u0448\x0f\xa1\x89\x1b\x1b\xcfQ\x89j}\x00\x00\u07d46\x16\xd4H\x98_]2\xae\xfa\x8b\x93\xa9\x93\xe0\x94\xbd\x85I\x86\x89\v\"\u007fc\xbe\x81<\x00\x00\u07d46\x16\xfbF\xc8\x15x\xc9\xc8\xebM;\xf8\x80E\x1a\x887\x9d}\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d46\x1cu\x93\x16\x96\xbc=B}\x93\xe7lw\xfd\x13\xb2A\xf6\xf4\x89\x1d\xc5\xd8\xfc&m\xd6\x00\x00\u07d46\x1d\x9e\xd8\v[\xd2|\xf9\xf1\"o&u2X\xee_\x9b?\x89\xbfi\x14\xba}r\xc2\x00\x00\u07d46\x1f;\xa9\xed\x95kw\x0f%}6r\xfe\x1f\xf9\xf7\xb0$\f\x89 \x86\xac5\x10R`\x00\x00\u07d46\"|\u07e0\xfd;\x9d~jtF\x85\xf5\xbe\x9a\xa3f\xa7\xf0\x89\n\xc2s\x0e\xe9\xc6\xc1\x80\x00\u07d46/\xbc\xb1\x06b7\n\x06\x8f\xc2e&\x02\xa2Wy7\xcc\xe6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d460\xc5\xe5e\u03aa\x8a\x0f\x0f\xfe2\x87^\xae*l\xe6<\x19\x89\t7r+7t\xd0\x00\x00\u07d463\x9f\x84\xa5\u00b4L\xe5=\xfd\xb6\xd4\xf9}\xf7\x82\x12\xa7\u07c9\x11o\x18\xb8\x17\x15\xa0\x00\x00\u07d464:\xec\xa0{n\u054a\x0eb\xfaN\xcbI\x8a\x12O\xc9q\x89\x10CV\x1a\x88)0\x00\x00\u07d46au@4\x81\xe0\xab\x15\xbbQF\x15\u02f9\x89\xeb\u018f\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d46ro;\x88Z$\xf9)\x96\u0681b^\u022d\x16\xd8\xcb\xe6\x89S\xafu\u0441HW\x80\x00\xe0\x946s\x95C\x99\xf6\u07feg\x18\x18%\x9b\xb2x\xe2\xe9.\xe3\x15\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d46u\x8e\x04\x9c\u064b\u03a1\"w\xa6v\xf9)sb\x89\x00#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d46\u007fY\u0302yS)8NA\xe1(1\x15\xe7\x91\xf2j\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x81\x0f\xf9\xd2\x13\xa2q\xed\xa2\xb8\xaay\x8b\xe6T\xfaK\xbe\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x8cT\x14\xb5k\x84U\x17\x1f\xbf\ab \xc1\u02e4\xb5\xca1\x89\x1e>\xf9\x11\xe8=r\x00\x00\xe0\x946\x90$k\xa3\xc8\x06y\xe2.\xacD\x12\xa1\xae\xfc\xe6\xd7\u0342\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d46\x92\x8bU\xbc\x86\x15\t\xd5\x1c\x8c\xf1\xd5F\xbf\xecn>\x90\xaf\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d46\x98\"\xf5W\x8b@\xdd\x1fDqpk\"\u0357\x13R\xdak\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d46\x9e\xf7a\x19_:7>$\xec\xe6\xcd\"R\x0f\xe0\xb9\xe8n\x89\x1c\xff\xaf\xc9M\xb2\b\x80\x00\u07d46\xa0\x8f\xd6\xfd\x1a\xc1|\xe1^\xd5~\xef\xb1*+\u2048\xbf\x89Hz\x9a0E9D\x00\x00\u07d46\xa0\xe6\x1e\x1b\xe4\u007f\xa8~0\xd3(\x88\xee\x030\x90\x1c\xa9\x91\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x946\xb2\xc8^:\xee\xeb\xb7\rc\u0124s\f\xe2\xe8\xe8\x8a6$\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x946\xbfC\xff5\u07d0\x90\x88$3l\x9b1\xce3\x06~/P\x8aIr\x15\x10\xc1\xc1\xe9H\x00\x00\u07d46\xbf\xe1\xfa;{p\xc1r\xeb\x04/h\x19\xa8\x97%\x95A>\x8965\u026d\xc5\u07a0\x00\x00\xe0\x946\xc5\x10\xbf\x8dnV\x9b\xf2\xf3}G&]\xbc\xb5\x02\xff+\u038a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x946\xd8]\xc3h1V\xe6;\xf8\x80\xa9\xfa\xb7x\x8c\xf8\x14:'\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d46\u07cf\x88<\x12s\xec\x8a\x17\x1fz3\xcf\xd6I\xb1\xfe`u\x89\fRHJ\xc4\x16\x89\x00\x00\xe0\x946\xe1Va\f\xd8\xffd\xe7\x80\u061d\x00T8\\\xa7gU\xaa\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d46\xfe\xc6,,B^!\x9b\x18D\x8a\xd7W\x00\x9d\x8cT\x02o\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d47\x00\xe3\x02t$\xd99\xdb\xde]B\xfbx\xf6\xc4\xdb\xec\x1a\x8f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d47\x02\xe7\x04\xcc!at9\xadN\xa2zW\x14\xf2\xfd\xa1\xe92\x8965\u026d\xc5\u07a0\x00\x00\u07d47\x035\fMo\xe374,\xdd\xc6[\xf1\xe28k\xf3\xf9\xb2\x89m\x81!\xa1\x94\xd1\x10\x00\x00\xe0\x947\b\xe5\x9d\xe6\xb4\x05P\x88x)\x02\xe0W\x9cr\x01\xa8\xbfP\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d47\x126~^U\xa9mZ\x19\x16\x8fn\xb2\xbc~\x99q\xf8i\x8965\u026d\xc5\u07a0\x00\x00\u07d47\x19Zc]\xccb\xf5jq\x80I\xd4~\x8f\x9f\x96\x83(\x91\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d47'4\x1f&\xc1 \x01\xe3x@^\xe3\x8b-\x84d\xecq@\x89lk\x93[\x8b\xbd@\x00\x00\u07d47.E:kb\x9f'g\x8c\u022e\xb5\xe5|\xe8^\xc0\xae\xf9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d474\xcb\x18t\x91\xed\xe7\x13\xae[;-\x12(J\xf4k\x81\x01\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d477!n\xe9\x1f\x17w2\xfbX\xfa@\x97&r\a\xe2\xcfU\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d47M;\xbb\x057Q\xf9\xf6\x8d\xdb\a\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d48r\xf4\x8d\xc5\xe3\xf8\x17\xbck*\xd2\xd00\xfc^\x04q\x19=\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d48~\xea\xfdk@\t\u07af\x8b\u0578Zr\x98:\x8d\xcc4\x87\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d48\x81\xde\xfa\xe1\xc0{<\xe0Lx\xab\xe2k\f\u070ds\xf0\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d48\x83\xbe\xcc\b\xb9\xbeh\xad;\b6\xaa\u00f6 \xdc\x00\x17\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d48\x85\xfe\xe6q\a\xdc:\xa9\x8a\x1d\x99:t\xdf\\\xd7T\xb9\x8dR\x9a\x89a\t=|,m8\x00\x00\u07d48\xe4m\xe4E<8\xe9A\xe7\x93\x0fC0O\x94\xbb{+\xe8\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d48\xe7\u06e8\xfdO\x1f\x85\r\xbc&I\xd8\xe8O\tR\xe3\xeb<\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d48\xe8\xa3\x1a\xf2\xd2e\xe3\x1a\x9f\xff-\x8fF(m\x12E\xa4g\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d48\xeao[Z{\x88AuQ\xb4\x12=\xc1'\xdf\xe94-\xa6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d48\xee\xc6\xe2\x17\xf4\xd4\x1a\xa9 \xe4$\xb9RQ\x97\x04\x1c\xd4\u0189\xf0\r%\xeb\x92.g\x00\x00\xe0\x948\xf3\x87\xe1\xa4\xedJs\x10n\xf2\xb4b\xe4t\xe2\xe3\x14:\u040a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d49\x11a\xb0\xe4<0 f\u898d,\xe7\xe1\x99\xec\xdb\x1dW\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x949\x15\uad6b.Yw\xd0u\xde\xc4}\x96\xb6\x8bK\\\xf5\x15\x8a\r\a\x01\x81\x85\x12\x0f@\x00\x00\u07d49\x1aw@\\\t\xa7+^\x846#z\xaa\xf9]h\xda\x17\t\x89\x02\xa9&J\xf3\u0479\x00\x00\u07d49\x1f \x17m\x126\rrMQG\n\x90p6uYJM\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d49$3\xd2\u0383\xd3\xfbJv\x02\u0323\xfa\xcaN\xc1@\xa4\xb0\x89\x02\xc3\xc4e\xcaX\xec\x00\x00\xe0\x949?x;\\\u06c6\"\x1b\xf0)O\xb7\x14\x95\x9c{E\x89\x9c\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d49?\xf4%^\\e\x8f.\u007f\x10\xec\xbd)%rg\x1b\xc2\u0489lk\x93[\x8b\xbd@\x00\x00\u07d49A2`\x0fAU\xe0\u007fME\xbc>\xb8\xd9\xfbr\xdc\u05c4\x89\x9fn\x92\xed\xea\a\xd4\x00\x00\u07d49Q\xe4\x8e<\x86\x9ekr\xa1C\xb6\xa4Ph\u0379\xd4f\u0409\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x949T\xbd\xfe\v\xf5\x87\u0195\xa3\x05\xd9$L=[\xdd\xda\u027b\x8a\x04\x10'\x83'\xf9\x85`\x80\x00\u07d49]m%U \xa8\xdb)\xab\xc4}\x83\xa5\u06ca\x1a}\xf0\x87\x89\x05k\xc7^-c\x10\x00\x00\u07d49ck%\x81\x1b\x17j\xbf\xcf\xee\xcad\xbc\x87E/\x1f\xdf\xf4\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d49i\xb4\xf7\x1b\xb8u\x1e\xdeC\xc0\x166:zaOv\x11\x8e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x949x/\xfe\x06\xacx\x82*<:\x8a\xfe0^P\xa5a\x88\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d49zn\xf8v:\x18\xf0\x0f\xac!~\x05\\\r0\x94\x10\x10\x11\x89lk\x93[\x8b\xbd@\x00\x00\u07d49|\u06cc\x80\xc6yP\xb1\x8deB)a\x0e\x93\xbf\xa6\xee\x1a\x89?\x95\xc8\xe0\x82\x15!\x00\x00\u07d49\x82O\x8b\xce\xd1v\xfd>\xa2.\u01a4\x93\xd0\xcc\xc3?\xc1G\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d49\x93l'\x19E\v\x94 \xcc%\"\u03d1\xdb\x01\xf2'\xc1\xc1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d49\x95\xe0\x96\xb0\x8aZrh\x00\xfc\xd1}\x9cd\xc6N\b\x8d+\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d49\x9a\xa6\xf5\xd0x\xcb\tp\x88+\u0259 \x06\xf8\xfb\xdf4q\x8965\u026d\xc5\u07a0\x00\x00\u07d49\xaa\x05\xe5m}28T!\u03d36\xe9\r=\x15\xa9\xf8Y\x89\x01h\u048e?\x00(\x00\x00\u07d49\xaa\xf0\x85M\xb6\xeb9\xbc{.C\x84jv\x17\x1c\x04E\u0789dI\xe8NG\xa8\xa8\x00\x00\u07d49\xb1\xc4q\xae\x94\xe1!dE.\x81\x1f\xbb\xe2\xb3\xcdru\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d49\xb2\x992t\x90\xd7/\x9a\x9e\xdf\xf1\x1b\x83\xaf\xd0\xe9\xd3\xc4P\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d49\xba\u018d\x94xY\xf5\x9e\x92&\b\x9c\x96\xd6.\x9f\xbe<\u0789\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x949\xbf\xd9xh\x9b\xec\x04\x8f\xc7v\xaa\x15$\u007f^\x1d|9\xa2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d49\xc7s6|\x88%\xd3YlhoB\xbf\r\x141\x9e?\x84\x89\a?u\u0460\x85\xba\x00\x00\u07d49\u05291@,\fy\xc4W\x18o$\u07c7)\u03d5p1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d49\xd6\xca\xca\"\xbc\xcdjr\xf8~\xe7\u05b5\x9e\v\xde!\xd7\x19\x89l\x87T\xc8\xf3\f\b\x00\x00\u07d49\xe0\xdbM`V\x8c\x80\v\x8cU\x00\x02l%\x94\xf5v\x89`\x8965\u026d\xc5\u07a0\x00\x00\xe0\x949\xeeO\xe0\x0f\xbc\xeddph\xd4\xf5|\x01\xcb\"\xa8\v\xcc\u044a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d49\xf1\x983\x1eK!\xc1\xb7`\xa3\x15_J\xb2\xfe\x00\xa7F\x19\x89lk\x93[\x8b\xbd@\x00\x00\u07d49\xf4Fc\xd9%a\t\x1b\x82\xa7\r\xcfY=u@\x05\x97:\x89\n\u05cb.\xdc!Y\x80\x00\u07d4:\x03U\x94\xc7GGmB\xd1\xee\x96l6\"L\xdd\"I\x93\x89\x13J\xf7Ei\xf9\xc5\x00\x00\u07d4:\x04W(G\xd3\x1e\x81\xf7v\\\xa5\xbf\xc9\xd5W\x15\x9f6\x83\x89\a6-\r\xab\xea\xfd\x80\x00\xe0\x94:\x06\xe3\xbb\x1e\xdc\xfd\fD\xc3\aM\xe0\xbb`k\x04\x98\x94\xa2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794:\x10\x88\x8b~\x14\x9c\xae',\x010,2}\n\xf0\x1a\v$\x88\xeb\xec!\xee\x1d\xa4\x00\x00\u07d4:1\b\xc1\u6023;3l!\x13\x134@\x9d\x97\xe5\xad\xec\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4:6\x8e\xfeJ\u05c6\xe2c\x95\xec\x9f\u01adi\x8c\xae)\xfe\x01\x89\"E\x89\x96u\xf9\xf4\x00\x00\u07d4:=\xd1\x04\xcd~\xb0O!\x93/\xd43\xeaz\xff\u04d3i\xf5\x89\x13aO#\xe2B&\x00\x00\u07d4:B\x97\xda\xc4.\x1eO\xb8\xcb1\xec\xddC\xaew<\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94:\x99`&m\xf6I cS\x8a\x99\xf4\x87\xc9P\xa3\xa5\uc78a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4:\x9b\x11\x10)\xce\x1f \xc9\x10\x9czt\xee\xee\xf3OO.\xb2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4:\x9eTA\xd4K$;\xe5[u\x02z\x1c\ub7ac\xf5\r\xf2\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94:\xa0z4\xa1\xaf\u0216}=\x13\x83\xb9kb\u03d6\xd5\xfa\x90\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94:\xa4,!\xb9\xb3\x1c>'\xcc\xd1~\t\x9a\xf6y\xcd\xf5i\a\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4:\xa9H\xea\x029wU\xef\xfb/\x9d\xc99-\xf1\x05\x8f~3\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4:\xad\xf9\x8ba\xe5\u0216\xe7\xd1\x00\xa39\x1d2P\"]a\u07c9\f\xafg\x007\x01h\x00\x00\u07d4:\xaeHr\xfd\x90\x93\xcb\xca\xd1@o\x1e\x80x\xba\xb5\x03Y\xe2\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4:\xbb\x8a\xdf\xc6\x04\xf4\x8dY\x84\x81\x1d\u007f\x1dR\xfe\xf6u\x82p\x89\xf2\x97\x19\xb6o\x11\f\x00\x00\u07d4:\xc2\xf0\xff\x16\x12\xe4\xa1\xc3F\xd53\x82\xab\xf6\u0622[\xaaS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4:\xc9\xdczCj\xe9\x8f\xd0\x1cz\x96!\xaa\x8e\x9d\v\x8bS\x1d\x89a\t=|,m8\x00\x00\xe0\x94:\xd0aI\xb2\x1cU\xff\x86|\xc3\xfb\x97@\u04bc\xc7\x10\x121\x8a)\xb7d2\xb9DQ \x00\x00\u07d4:\xd7\x02C\u060b\xf0@\x0fW\xc8\xc1\xfdW\x81\x18H\xaf\x16*\x89.\x9e\xe5\u00c6S\xf0\x00\x00\u07d4:\xd9\x15\xd5P\xb7#AV \xf5\xa9\xb5\xb8\x8a\x85\xf3\x82\xf05\x8965\u026d\xc5\u07a0\x00\x00\u07d4:\xe1`\xe3\xcd`\xae1\xb9\xd6t-h\xe1Nv\xbd\x96\xc5\x17\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4:\xe6+\xd2q\xa7`c\u007f\xady\xc3\x1c\x94\xffb\xb4\xcd\x12\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4:\xeaN\x82\xd2@\x02H\xf9\x98q\xa4\x1c\xa2W\x06\r:\"\x1b\x8965\u026d\xc5\u07a0\x00\x00\u07d4:\xf6[>(\x89ZJ\x00\x11S9\x1d\x1ei\xc3\x1f\xb9\xdb9\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4;\a\xdbZ5\u007fZ\xf2HL\xbc\x9dw\xd7;\x1f\xd0Q\x9f\u01c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4;\n\u032fK`|\xfea\xd1s4\xc2\x14\xb7\\\xde\xfd\xbd\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\x13c\x1a\x1b\x89\xcbVeH\x89\x9a\x1d`\x91\\\xdc\xc4 [\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\x15\x90\x99\aR\a\u0180vc\xb1\xf0\xf7\xed\xa5J\xc8\xcc\xe3\x89j\xc4\xe6[i\xf9-\x80\x00\u07d4;\x197\xd5\u74f8\x9bc\xfb\x8e\xb5\xf1\xb1\xc9\xcak\xa0\xfa\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\"\xda*\x02q\xc8\xef\xe1\x02S'scji\xb1\xc1~\t\x89\x1b6\xa6DJ>\x18\x00\x00\u07d4;\"\u07a3\xc2_\x1bY\u01fd'\xbb\x91\u04e3\xea\xec\xef9\x84\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94;#g\xf8IK_\xe1\x8dh<\x05]\x89\x99\x9c\x9f=\x1b4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4;,E\x99\x0e!GDQ\xcfOY\xf0\x19U\xb31\xc7\xd7\u0249lk\x93[\x8b\xbd@\x00\x00\xe0\x94;A\x00\xe3\ns\xb0\xc74\xb1\x8f\xfa\x84&\u045b\x191/\x1a\x8a\v\xb5\u046ap\n\xfd\x90\x00\x00\u07d4;B\xa6m\x97\x9fX(4tz\x8b`B\x8e\x9bN\xec\xcd#\x89!\xa1\u01d0\xfa\xdcX\x00\x00\u07d4;Gh\xfdq\xe2\xdb,\xbe\u007f\xa0PH<'\xb4\xeb\x93\x1d\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;Vj\x8a\xfa\u0456\x82\xdc,\xe8g\x9a<\xe4D\xa5\xb0\xfdO\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94;\\%\x1d\u007f\u05c9;\xa2\t\xfeT\x1c\xec\xd0\xce%:\x99\r\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4;^\x8b\x17w\xca\x18A\x896\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94;\x93\xb1a6\xf1\x1e\xaf\x10\x99l\x95\x99\r;'9\xcc\xea_\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4;\xabK\x01\xa7\xc8K\xa1?\uea70\xbb\x19\x1bw\xa3\xaa\u0723\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4;\xb55\x98\xcc \xe2\x05]\xc5S\xb0I@J\u0277\xdd\x1e\x83\x89!W\x1d\xf7|\x00\xbe\x00\x00\u07d4;\xbc\x13\xd0J\xcc\xc0pz\xeb\u072e\xf0\x87\u0438~\v^\u327e\xd1\xd0&=\x9f\x00\x00\x00\u07d4;\xc6\xe3\xeezV\u038f\x14\xa3u2Y\x0fcqk\x99f\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\xc8]ls[\x9c\xdaK\xba_H\xb2K\x13\xe7\x0600{\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4;\xd6$\xb5H\xcbe\x976\x90~\u062a<\fp^$\xb5u\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\u0660m\x1b\xd3lN\xdd'\xfc\r\x1f[\b\x8d\xda\xe3\xc7*\x89\x1b\x1azB\v\xa0\r\x00\x00\u0794;\u077c\x814\xf7}UY\u007f\xc9|&\xd2f\x98\t\x06\x04\ub23e -j\x0e\xda\x00\x00\xe0\x94;\xf8n\u0623\x15>\xc93xj\x02\xac\t\x03\x01\x85^Wk\x8a_J\x8c\x83u\xd1U@\x00\x00\u07d4;\xfb\u04c4|\x17\xa6\x1c\xf3\xf1{R\xf8\ub879`\xb3\U000df262\xa1]\tQ\x9b\xe0\x00\x00\u07d4<\x03\xbb\xc0#\xe1\xe9?\xa3\xa3\xa6\xe4(\xcf\f\xd8\xf9^\x1e\u0189Rf<\u02b1\xe1\xc0\x00\x00\u07d4<\f=\ufb1c\xeaz\xcc1\x9a\x96\xc3\v\x8e\x1f\xed\xabEt\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4<\x15\xb3Q\x1d\xf6\xf04.sH\u0309\xaf9\xa1h\xb7s\x0f\x8965\u026d\xc5\u07a0\x00\x00\u07d4<\x1f\x91\xf3\x01\xf4\xb5e\xbc\xa2GQ\xaa\x1fv\x13\"p\x9d\u0749a\t=|,m8\x00\x00\xe0\x94<(l\xfb0\x14n_\u05d0\xc2\xc8T\x15RW\x8d\xe34\u060a\x02)\x1b\x11\xaa0n\x8c\x00\x00\u07d4<2.a\x1f\u06c2\rG\xc6\xf8\xfcd\xb6\xfa\xd7L\xa9_^\x89\r%\x8e\xce\x1b\x13\x15\x00\x00\u07d4\xa5\xe5\xbfb\xbb\u0309\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4<\x86\x9c\tie#\xce\xd8$\xa0pAF\x05\xbbv#\x1f\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4<\x92V\x19\u02731DF?\x057\u06165\x87\x06\xc5 \xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\x98YK\xf6\x8bW5\x1e\x88\x14\xae\x9em\xfd-%J\xa0o\x89\x10CV\x1a\x88)0\x00\x00\u07d4<\xad\xeb=>\xed?b1\x1dRU>p\xdfJ\xfc\xe5o#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94<\xae\xdbS\x19\xfe\x80eC\xc5nP!\xd3r\xf7\x1b\xe9\x06.\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4<\xaf\xaf^bPV\x15\x06\x8a\xf8\xeb\"\xa1:\u0629\xe5Pp\x89lf\x06E\xaaG\x18\x00\x00\u07d4<\xb1y\xcbH\x01\xa9\x9b\x95\u00f0\xc3$\xa2\xbd\xc1\x01\xa6S`\x89\x01h\u048e?\x00(\x00\x00\u07d4<\xb5a\u0386BK5\x98\x91\xe3d\xec\x92_\xfe\xff'}\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xcbq\xaah\x80\xcb\v\x84\x01-\x90\xe6\a@\xec\x06\xac\u05cf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\xce\xf8\x86yW9G\xe9I\x97y\x8a\x1e2~\b`:e\x89+\xc9\x16\u059f;\x02\x00\x00\xe0\x94<\xd1\xd9s\x1b\xd5H\xc1\xddo\u03a6\x1b\xebu\xd9\x17T\xf7\u04ca\x01\x16\x1d\x01\xb2\x15\xca\xe4\x80\x00\u07d4<\u04e6\xe95y\xc5mIAq\xfcS>z\x90\xe6\xf5\x94d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\u05b7Y<\xbe\xe7x0\xa8\xb1\x9d\b\x01\x95\x8f\xcdK\xc5z\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4<\xd7\xf7\xc7\xc257\x80\xcd\xe0\x81\xee\xecE\x82+%\xf2\x86\f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xe1\u0717\xfc\u05f7\xc4\u04e1\x8aI\xd6\xf2\xa5\xc1\xb1\xa9\x06\u05c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xea0*G*\x94\x03y\xdd9\x8a$\xea\xfd\xba\u07c8\xady\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94<\xec\xa9k\xb1\xcd\xc2\x14\x02\x9c\xbc^\x18\x1d9\x8a\xb9M=A\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4<\xf4\x84RO\xbd\xfa\xda\xe2m\xc1\x85\xe3++c\x0f\xd2\xe7&\x89\x18TR\xcb*\x91\xc3\x00\x00\u07d4<\xf9\xa1\xd4e\xe7\x8bp9\xe3iDx\xe2b{6\xfc\xd1A\x89J`S*\xd5\x1b\xf0\x00\x00\u07d4<\xfb\xf0fVYpc\x9e\x13\r\xf2\xa7\xd1k\x0e\x14\xd6\t\x1c\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94=\th\x8d\x93\xad\a\xf3\xab\xe6\x8cr'#\xcdh\t\x90C^\x8a\x06ZL\xe9\x9fv\x9en\x00\x00\u07d4=1X{_\u0546\x98Ex\x87%\xa6c)\nI\xd3g\x8c\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4=?\xadI\xc9\xe5\xd2u\x9c\x8e\x8eZzM`\xa0\xdd\x13V\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4=WO\xcf\x00\xfa\xe1\u064c\u023f\x9d\u07e1\xb3\x95;\x97A\xbc\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4=Z\x8b+\x80\xbe\x8b5\xd8\xec\xf7\x89\xb5\xedz\au\xc5\al\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4=f\xcdK\xd6M\\\x8c\x1b^\xea(\x1e\x10m\x1cZ\xad#s\x89i\xc4\xf3\xa8\xa1\x10\xa6\x00\x00\u0794=j\xe0S\xfc\xbc1\x8do\xd0\xfb\xc3S\xb8\xbfT.h\r'\x88\xc6s\xce<@\x16\x00\x00\u07d4=o\xf8,\x93w\x05\x9f\xb3\r\x92\x15r?`\xc7u\u0211\xfe\x89\r\x8e\\\xe6\x17\xf2\xd5\x00\x00\u07d4=y\xa8S\xd7\x1b\xe0b\x1bD\xe2\x97Yel\xa0u\xfd\xf4\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4=~\xa5\xbf\x03R\x81\x00\xed\x8a\xf8\xae\xd2e>\x92\x1bng%\x8965\u026d\xc5\u07a0\x00\x00\u07d4=\x81?\xf2\xb6\xedW\xb97\u06bf+8\x1d\x14\x8aA\x1f\xa0\x85\x89\x05k\xc7^-c\x10\x00\x00\u07d4=\x88\x143\xf0J}\r'\xf8ID\xe0\x8aQ-\xa3UR\x87\x89A\rXj \xa4\xc0\x00\x00\u07d4=\x89\xe5\x05\xcbF\xe2\x11\xa5?2\xf1g\xa8w\xbe\xc8\u007fK\n\x89\x01[5W\xf1\x93\u007f\x80\x00\xe0\x94=\x8d\a#r\x1es\xa6\xc0\xd8`\xaa\x05W\xab\xd1L\x1e\xe3b\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4=\x8f9\x88\x1b\x9e\xdf\xe9\x12'\xc3?\xa4\xcd\xd9\x1eg\x85D\xb0\x89\x04\xab\a\xbaC\xad\xa9\x80\x00\u07d4=\x9dk\xe5\u007f\xf8>\x06Y\x85fO\x12VD\x83\xf2\xe6\x00\xb2\x89n\xac\xe4?#\xbd\x80\x00\x00\u07d4=\xa3\x9c\xe3\xefJz9f\xb3.\xe7\xeaN\xbc#5\xa8\xf1\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4=\xaa\x01\u03b7\x0e\xaf\x95\x91\xfaR\x1b\xa4\xa2~\xa9\xfb\x8e\xdeJ\x89Zc\xd2\u027cvT\x00\x00\u07d4=\xb5\xfejh\xbd6\x12\xac\x15\xa9\x9aa\xe5U\x92\x8e\xec\xea\xf3\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4=\xb9\xed\u007f\x02L~&7/\xea\xcf+\x05\b\x03D^8\x10\x89E\xb1H\xb4\x99j0\x00\x00\u07d4=\xbf\r\xbf\xd7x\x90\x80\x053\xf0\x9d\xea\x83\x01\xb9\xf0%\u04a6\x8965\u026d\xc5\u07a0\x00\x00\u07d4=\xce\U0005c18b\x15\xd3N\xdaBn\xc7\xe0K\x18\xb6\x01p\x02\x89lh\xcc\u041b\x02,\x00\x00\xe0\x94=\xd1.Uj`76\xfe\xbaJo\xa8\xbdJ\xc4]f*\x04\x8a#u{\x91\x83\xe0x(\x00\x00\u07d4=\u078b\x15\xb3\u033a\xa5x\x01\x12\xc3\xd6t\xf3\x13\xbb\xa6\x80&\x89`\x1dQZ>O\x94\x00\x00\xe0\x94=\xde\xdb\xe4\x89#\xfb\xf9\xe56\xbf\x9f\xfb\aG\xc9\xcd\u04de\xef\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4=\xea\xe43'\x91?b\x80\x8f\xaa\x1bbv\xa2\xbdch\xea\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4=\xf7b\x04\x9e\u068a\u0192}\x90Lz\xf4/\x94\xe5Q\x96\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>\x04\r@\u02c0\xba\x01%\xf3\xb1_\xde\xfc\xc8?0\x05\xda\x1b\x898E$\xccp\xb7x\x00\x00\u07d4>\v\x8e\xd8n\xd6i\xe1'#\xafur\xfb\xac\xfe\x82\x9b\x1e\x16\x89QM\xe7\xf9\xb8\x12\xdc\x00\x00\xe0\x94>\f\xbejm\xcba\xf1\x10\xc4[\xa2\xaa6\x1d\u007f\xca\xd3\xdas\x8a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4>\x19KN\xce\xf8\xbbq\x1e\xa2\xff$\xfe\xc4\xe8{\xd02\xf7\u0449\x8b\x9d\xc1\xbc\x1a\x03j\x80\x00\xe0\x94>\x1b\"0\xaf\xbb\xd3\x10\xb4\x92jLwmZ\u705cf\x1d\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4>\x1cS0\x0eL\x16\x89\x12\x16<~\x99\xb9]\xa2h\xad(\n\x896b2\\\u044f\xe0\x00\x00\u07d4>\x1c\x96 c\xe0\xd5)YA\xf2\x10\u0723\xabS\x1e\xec\x88\t\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4>,\xa0\xd24\xba\xf6\a\xadFj\x1b\x85\xf4\xa6H\x8e\xf0\n\xe7\x89\x04\xda!\xa3H=V\x80\x00\u07d4>/&#^\x13zs$\xe4\xdc\x15K]\xf5\xafF\xea\x1aI\x89\x017\xaa\xd8\x03-\xb9\x00\x00\xe0\x94>1a\xf1\xea/\xbf\x12ny\xda\x18\x01\u0695\x12\xb3y\x88\u024a\nm\xd9\f\xaeQ\x14H\x00\x00\xe0\x94>6\xc1rS\xc1\x1c\xf3\x89t\xed\r\xb1\xb7Y\x16\r\xa67\x83\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4><\u04fe\xc0e\x91\xd64o%Kb\x1e\xb4\x1c\x89\x00\x8d1\x895\u07fe\u069f74\x00\x00\u07d4>E\xbdU\u06d0`\xec\xed\x92;\xb9\xcbs<\xb3W?\xb51\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4>M\x13\xc5Z\x84\xe4n\xd7\xe9\u02d0\xfd5^\x8a\u0651\u33c965\u026d\xc5\u07a0\x00\x00\u07d4>N\x92e\"<\x9782L\xf2\v\xd0`\x06\xd0\a>\u06cc\x89\a?u\u0460\x85\xba\x00\x00\xe0\x94>O\xbdf\x10\x15\xf6F\x1e\xd6s\\\xef\xef\x01\xf3\x14E\xde:\x8a\x03n4)\x98\xb8\xb0 \x00\x00\xe0\x94>S\xff!\a\xa8\u07be3(I:\x92\xa5\x86\xa7\xe1\xf4\x97X\x8a\x04\xe6\x9c*q\xa4\x05\xab\x00\x00\u07d4>Z9\xfd\xdap\xdf\x11&\xab\r\u011asx1\x1aSz\x1f\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\xe0\x94>Z\xbd\t\xceZ\xf7\xba\x84\x87\xc3Y\xe0\xf2\xa9:\x98k\v\x18\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4>\\\xb8\x92\x8cAx%\xc0:;\xfc\xc5!\x83\xe5\xc9\x1eB\u05c9\xe71\xd9\xc5,\x96/\x00\x00\u07d4>^\x93\xfbL\x9c\x9d\x12F\xf8\xf2G5\x8e\"\xc3\xc5\xd1{j\x89\b!\xab\rD\x14\x98\x00\x00\u07d4>a\x83P\xfa\x01ez\xb0\xef>\xba\xc8\xe3p\x12\xf8\xfc+o\x89\x98\x06\xde=\xa6\xe9x\x00\x00\u07d4>c\xce;$\xca(e\xb4\u0166\x87\xb7\xae\xa3Y~\xf6\xe5H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>f\xb8GiVj\xb6yE\xd5\xfa\x8175V\xbc\u00e1\xfa\x89\b=lz\xabc`\x00\x00\xe0\x94>v\xa6-\xb1\x87\xaat\xf68\x17S;0l\xea\xd0\xe8\u03be\x8a\x06\x9bZ\xfa\xc7P\xbb\x80\x00\x00\u07d4>z\x96k]\xc3W\xff\xb0~\x9f\xe0g\xc4W\x91\xfd\x8e0I\x89\x034-`\xdf\xf1\x96\x00\x00\xe0\x94>\x81w!u#~\xb4\xcb\xe0\xfe-\xca\xfd\xad\xff\xebj\x19\x99\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4>\x83I\xb6\u007fWED\x9fe\x93g\u066dG\x12\xdb[\x89Z\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4>\x83TO\x00\x82U%r\u01c2\xbe\xe5\xd2\x18\xf1\xef\x06J\x9d\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4>\x84\xb3\\[\"ePpa\xd3\vo\x12\xda\x03?\xe6\xf8\xb9\x89a\t=|,m8\x00\x00\u07d4>\x86A\xd4\x87E\xba2/_\xd6\xcbP\x12N\xc4f\x88\u01e6\x9a\u007f\xae\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4>\x91N0\x18\xac\x00D\x93A\u011d\xa7\x1d\x04\xdf\xee\xedb!\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4>\x94\x10\u04f9\xa8~\xd5\xe4Q\xa6\xb9\x1b\xb8\x92?\xe9\x0f\xb2\xb5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4>\x94\xdfS\x13\xfaR\x05p\xef#+\xc31\x1d_b/\xf1\x83\x89lk\x93[\x8b\xbd@\x00\x00\u0794>\x9b4\xa5\u007f3u\xaeY\xc0\xa7^\x19\u0136A\"\x8d\x97\x00\x88\xf8i\x93)g~\x00\x00\u07d4>\xad\xa8\xc9/V\x06~\x1b\xb7<\xe3x\xdaV\xdc,\xdf\xd3e\x89w\xcd\xe9:\xeb\rH\x00\x00\xe0\x94>\xaf\by\xb5\xb6\xdb\x15\x9bX\x9f\x84W\x8bjt\xf6\xc1\x03W\x8a\x01\x898\xb6q\xfae\xa2\x80\x00\u07d4>\xaf1k\x87a]\x88\xf7\xad\xc7|X\xe7\x12\xedMw\x96k\x89\x05m\xbcL\xee$d\x80\x00\u07d4>\xb8\xb3;!\xd2<\u0686\xd8(\x88\x84\xabG\x0e\x16F\x91\xb5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4>\xb9\xef\x06\xd0\xc2Y\x04\x03\x19\x94~\x8czh\x12\xaa\x02S\u0609\t\r\x97/22<\x00\x00\u07d4>\u030e\x16h\xdd\xe9\x95\xdcW\x0f\xe4\x14\xf4B\x11\xc54\xa6\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>\u03752\xe3\x97W\x96b\xb2\xa4aA\u73c25\x93j_\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4>\xeeo\x1e\x966\vv\x89\xb3\x06\x9a\xda\xf9\xaf\x8e\xb6\f\u404965\u026d\xc5\u07a0\x00\x00\xe0\x94?\b\u066d\x89O\x81>\x8e!H\xc1`\xd2K5:\x8et\xb0\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4?\f\x83\xaa\xc5qybsN\\\xea\xea\xec\u04db(\xad\x06\xbe\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94?\x10\x80\x02\x82\u0477\xdd\u01cf\xa9-\x820\aN\x1b\xf6\xae\xae\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4?\x123qO M\xe9\xdeN\xe9m\a;6\x8d\x81\x97\x98\x9f\x89\x02\x17\xc4\x10t\xe6\xbb\x00\x00\u07d4?\x17:\xa6\xed\xf4i\u0445\xe5\x9b\xd2j\xe4#k\x92\xb4\xd8\xe1\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4?\x1b\xc4 \xc5<\x00,\x9e\x90\x03|D\xfej\x8e\xf4\xdd\xc9b\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4?#a\b\xee\xc7\"\x89\xba\u00e6\\\u0483\xf9^\x04\x1d\x14L\x8964\xbf9\xab\x98x\x80\x00\u07d4?-\xa0\x93\xbb\x16\xeb\x06O\x8b\xfa\x9e0\xb9)\xd1_\x8e\x1cL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?-\xd5]\xb7\xea\xb0\xeb\xeee\xb3>\xd8 ,\x1e\x99.\x95\x8b\x89,s\xc97t,P\x00\x00\u07d4?/8\x14\x91y|\xc5\xc0\u0502\x96\xc1O\xd0\xcd\x00\xcd\xfa-\x89+\x95\xbd\xcc9\xb6\x10\x00\x00\u07d4?0\u04fc\x9f`\"2\xbcrB\x88\xcaF\xcd\v\a\x88\xf7\x15\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4?<\x8ea\xe5`L\xef\x06\x05\xd46\xdd\"\xac\u0346\"\x17\xfc\x89Hz\x9a0E9D\x00\x00\u07d4??F\xb7\\\xab\xe3{\xfa\u0307`(\x1fCA\xca\u007fF=\x89 \xacD\x825\xfa\xe8\x80\x00\u07d4?G)c\x19x\x83\xbb\xdaZ\x9b}\xfc\xb2-\xb1\x14@\xad1\x89\x1a\x19d<\xb1\xef\xf0\x80\x00\u07d4?L\xd19\x9f\x8a4\xed\u06da\x17\xa4q\xfc\x92+Xp\xaa\xfc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4?U\x1b\xa9<\xd5F\x93\xc1\x83\xfb\x9a\xd6\re\xe1`\x96s\u0249lk\x93[\x8b\xbd@\x00\x00\xe0\x94?bzv\x9ej\x95\x0e\xb8p\x17\xa7\u035c\xa2\bq\x13h1\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4?m\xd3e\x0e\xe4(\u0737u\x95S\xb0\x17\xa9j\x94(j\u0249Hz\x9a0E9D\x00\x00\u07d4?tr7\x80o\xed?\x82\x8ahR\xeb\bg\xf7\x90'\xaf\x89\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4?u\xaea\xcc\x1d\x80Be;[\xae\xc4D>\x05\x1c^z\xbd\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4?\xb7\u0457\xb3\xbaO\xe0E\xef\xc2=P\xa1E\x85\xf5X\u0672\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94?\xbc\x1eE\x18\xd74\x00\xc6\xd0F5\x949\xfbh\xea\x1aI\xf4\x8a\x03y\v\xb8U\x13v@\x00\x00\u07d4?\xbe\xd6\xe7\xe0\u029c\x84\xfb\xe9\xeb\u03ddN\xf9\xbbIB\x81e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?\u043bGy\x8c\xf4L\u07feM3=\xe67\xdfJ\x00\xe4\\\x89\x05lUy\xf7\"\x14\x00\x00\xe0\x94?\xe4\x0f\xbd\x91\x9a\xad(\x18\xdf\x01\xeeM\xf4lF\x84*\xc59\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4?\xe8\x01\xe6\x135\xc5\x14\r\xc7\xed\xa2\xefR\x04F\nP\x120\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?\xf86\xb6\xf5{\x90\x1bD\f0\xe4\xdb\xd0e\xcf7\xd3\u050c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4?\xfc\xb8p\xd4\x02=%]Qg\u0625\a\xce\xfc6kh\xba\x89#4^\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4@s\xfaI\xb8q\x17\u02d0\x8c\xf1\xabQ-\xa7T\xa92\xd4w\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4@\x8ai\xa4\a\x15\xe1\xb3\x13\xe15N`\b\x00\xa1\xe6\xdc\x02\xa5\x89\x01\u7e11\u0312T\x00\x00\u07d4@\x9b\xd7P\x85\x82\x1c\x1d\xe7\f\xdc;\x11\xff\xc3\xd9#\xc7@\x10\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4@\x9dZ\x96.\xde\uefa1x\x01\x8c\x0f8\xb9\u0372\x13\xf2\x89\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4@\xa31\x19[\x97s%\u00aa(\xfa/B\xcb%\xec<%<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4@\xa7\xf7(g\xa7\u0706w\v\x16+uW\xa44\xedP\xcc\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d4@\xab\n>\x83\xd0\u022c\x93f\x91\x05 \xea\xb1w+\xac;\x1a\x894\xf1\f-\xc0^|\x00\x00\u07d4@\xabf\xfe!>\xa5l:\xfb\x12\xc7[\xe3?\x8e2\xfd\b]\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94@\xadt\xbc\v\xce*E\xe5/6\xc3\u07bb\x1b:\xda\x1bv\x19\x8a\x01p\x16-\xe1\t\xc6X\x00\x00\u07d4@\u03c9\x05\x91\xea\u484f\x81*)T\xcb)_c3'\xe6\x89\x02\x9b\xf76\xfcY\x1a\x00\x00\u07d4@\u03d0\xef[v\x8c]\xa5\x85\x00,\xcb\xe6avP\xd8\xe87\x8963\x03\"\xd5#\x8c\x00\x00\xe0\x94@\xd4]\x9dv%\xd1QV\xc92\xb7q\xca{\x05'\x13\tX\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4@\xdb\x1b\xa5\x85\xce4S\x1e\xde\xc5IHI9\x13\x81\xe6\xcc\u04c9a\t=|,m8\x00\x00\xe0\x94@\xdfI^\xcf?\x8bL\xef*l\x18\x99W$\x8f\u813c+\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4@\xe0\xdb\xf3\xef\uf404\xea\x1c\xd7\xe5\x03\xf4\v;J\x84C\xf6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4@\xe2D\n\xe1B\u02006j\x12\xc6\xd4\x10/K\x844\xb6*\x8965\u026d\xc5\u07a0\x00\x00\u07d4@\xe3\u0083\xf7\xe2M\xe0A\f\x12\x1b\xee`\xa5`\u007f>)\xa6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94@\xeaPD\xb2\x04\xb20v\xb1\xa5\x80;\xf1\xd3\f\x0f\x88\x87\x1a\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94@\xed\xdbD\x8di\x0e\xd7.\x05\xc2%\xd3O\xc85\x0f\xa1\xe4\u014a\x01{x\x83\xc0i\x16`\x00\x00\xe0\x94@\xf4\xf4\xc0ls,\xd3[\x11\x9b\x89;\x12~}\x9d\aq\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4A\x01\x0f\u023a\xf8C}\x17\xa0Ci\x80\x9a\x16\x8a\x17\xcaV\xfb\x89\x05k\xc7^-c\x10\x00\x00\u07d4A\x03)\x96q\xd4gc\x97\x8f\xa4\xaa\x19\xee4\xb1\xfc\x95'\x84\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4A\x03<\x1bm\x05\xe1\u0289\xb0\x94\x8f\xc6DS\xfb\xe8z\xb2^\x89Hz\x9a0E9D\x00\x00\u07d4A\t\x8a\x81E#\x17\xc1\x9e>\xef\v\xd1#\xbb\xe1x\xe9\xe9\u0289\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4A\x16\x10\xb1x\xd5a}\xfa\xb94\u0493\xf5\x12\xa9>\\\x10\xe1\x89\t79SM(h\x00\x00\u07d4A\x1c\x83\x1c\xc6\xf4O\x19e\xecWW\xabN[<\xa4\xcf\xfd\x1f\x89\x17\n\x0fP@\xe5\x04\x00\x00\xe0\x94A*h\xf6\xc6EU\x9c\xc9w\xfcId\x04z \x1d\x1b\xb0\xe2\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4A?K\x02f\x9c\xcf\xf6\x80k\xc8&\xfc\xb7\xde\xca;\x0e\xa9\xbc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4AE\x99\t.\x87\x9a\xe2Sr\xa8MsZ\xf5\xc4\xe5\x10\xcdm\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4AHV\x12\xd04F\xecL\x05\xe5$NV?\x1c\xba\xe0\xf1\x97\x894\x95tD\xb8@\xe8\x00\x00\u07d4A]\tj\xb0b\x93\x18?<\x03=%\xf6\xcfqx\xac;\u01c9\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4Af\xfc\b\u0285\xf7f\xfd\xe81F\x0e\x9d\xc9<\x0e!\xaal\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Ag\x84\xaf`\x960\xb0p\u051a\x8b\xcd\x12#\\d(\xa4\b\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Ag\xcdH\xe73A\x8e\x8f\x99\xff\xd14\x12\x1cJJ\xb2x\u0109\xc5S%\xcat\x15\xe0\x00\x00\u07d4Al\x86\xb7 \x83\xd1\xf8\x90}\x84\xef\xd2\xd2\u05c3\xdf\xfa>\xfb\x89lj\xccg\u05f1\xd4\x00\x00\u07d4AsA\x9d\\\x9fc)U\x1d\xc4\xd3\xd0\u03ac\x1bp\x1b\x86\x9e\x89\x04\xc5>\xcd\xc1\x8a`\x00\x00\u07d4At\xfa\x1b\xc1*;q\x83\u02eb\xb7z\vYU{\xa5\xf1\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4Axj\x10\xd4G\xf4\x84\xd32D\u0337\xfa\u034bB{[\x8c\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Az<\u0454\x96S\nmB\x04\u00f5\xa1|\xe0\xf2\a\xb1\xa5\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4A~N&\x88\xb1\xfdf\xd8!R\x9eF\xedOB\xf8\xb3\xdb=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94A\x9aq\xa3l\x11\xd1\x05\xe0\xf2\xae\xf5\xa3\xe5\x98\a\x8e\x85\xc8\v\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94A\x9b\xdes\x16\xcc\x1e\u0495\u0205\xac\xe3B\u01db\xf7\xee3\xea\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4A\xa2\xf2\xe6\xec\xb8c\x94\xec\x0e3\x8c\x0f\xc9~\x9cU\x83\xde\u0489l\xee\x06\u077e\x15\xec\x00\x00\u07d4A\xa8\u0083\x00\x81\xb1\x02\xdfn\x011e|\a\xabc[T\u0389lj\xccg\u05f1\xd4\x00\x00\u07d4A\xa8\xe26\xa3\x0emc\xc1\xffdM\x13*\xa2\\\x89S~\x01\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4A\xa9\xa4\x04\xfc\x9f[\xfe\xe4\x8e\xc2e\xb1%#3\x8e)\xa8\xbf\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4A\xad6\x9fu\x8f\xef8\xa1\x9a\xa3\x14\x93y\x83,\x81\x8e\xf2\xa0\x8966\x9e\xd7t}&\x00\x00\u07d4A\xb2\xd3O\xde\v\x10)&+Ar\xc8\x1c\x15\x90@[\x03\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4A\xb2\xdb\u05dd\u069b\x86Ojp0'T\x19\u00dd>\xfd;\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4A\xc3\xc26u4\xd1;\xa2\xb3?\x18\\\xdb\xe6\xacC\xc2\xfa1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4A\u02d8\x96D_p\xa1\n\x14!R\x96\xda\xf6\x14\xe3,\xf4\u0549g\x8a\x93 b\xe4\x18\x00\x00\u07d4A\xcey\x95\t5\xcf\xf5[\xf7\x8eL\xce\xc2\xfec\x17\x85\u06d5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4A\u04f71\xa3&\xe7hX\xba\xa5\xf4\xbd\x89\xb5{6\x93#C\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\xe0\x94A\xe4\xa2\x02u\xe3\x9b\xdc\xef\xebe\\\x03\"tKvQ@\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4A\xed-\x8ep\x81H,\x91\x9f\xc2=\x8f\x00\x91\xb3\xc8,F\x85\x89F:\x1ev[\u05ca\x00\x00\xe0\x94A\xf2~tK\u049d\xe2\xb0Y\x8f\x02\xa0\xbb\x9f\x98\xe6\x81\ua90a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\u07d4A\xf4\x89\xa1\xect{\u009c>_\x9d\x8d\xb9xw\xd4\u0474\xe9\x89\a?u\u0460\x85\xba\x00\x00\u07d4B\x0f\xb8n}+Q@\x1f\xc5\xe8\xc7 \x15\xde\xcbN\xf8\xfc.\x8965\u026d\xc5\u07a0\x00\x00\u07d4B\x16\x84\xba\xa9\xc0\xb4\xb5\xf5S8\xe6\xf6\xe7\xc8\xe1F\xd4\x1c\xb7\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4B9\x96Y\xac\xa6\xa5\xa8c\xea\"E\xc93\xfe\x9a5\xb7\x88\x0e\x89n\xce2\xc2l\x82p\x00\x00\xe0\x94B;\xcaG\xab\xc0\fpW\xe3\xad4\xfc\xa6>7_\xbd\x8bJ\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4B<1\a\xf4\xba\xceANI\x9cd9\nQ\xf7F\x15\xca^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B<\xc4YL\xf4\xab\xb66\x8d\xe5\x9f\u04b1#\a4a!C\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94BD\xf13\x11X\xb9\xce&\xbb\xe0\xb9#k\x92\x03\xca5\x144\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794BQw\xebt\xad\n\x9d\x9aWR\"\x81G\xeemcV\xa6\u6239\x8b\xc8)\xa6\xf9\x00\x00\u07d4BW%\xc0\xf0\x8f\b\x11\xf5\xf0\x06\xee\xc9\x1c\\\\\x12k\x12\xae\x89\b!\xab\rD\x14\x98\x00\x00\xe0\x94BX\xfdf/\xc4\xce2\x95\xf0\xd4\xed\x8f{\xb1D\x96\x00\xa0\xa9\x8a\x01lE.\xd6\b\x8a\xd8\x00\x00\xe0\x94B\\\x18\x16\x86\x8fww\xcc+\xa6\xc6\u048c\x9e\x1eylR\xb3\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4B\\3\x8a\x13%\xe3\xa1W\x8e\xfa)\x9eW\u0646\xebGO\x81\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94BbY\xb0\xa7Vp\x1a\x8bf5(R!V\xc0(\x8f\x0f$\x8a\x02\x18\xae\x19k\x8dO0\x00\x00\u07d4Bm\x15\xf4\a\xa0\x115\xb1:kr\xf8\xf2R\v51\xe3\x02\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Box\xf7\r\xb2Y\xac\x854\x14[)4\xf4\xef\x10\x98\xb5\u0609\x13\x84\x00\xec\xa3d\xa0\x00\x00\u07d4Bs-\x8e\xf4\x9f\xfd\xa0K\x19x\x0f\xd3\xc1\x84i\xfb7A\x06\x89\x17\v\x00\xe5\u4a7e\x00\x00\u07d4Bt\x17\xbd\x16\xb1\xb3\xd2-\xbb\x90-\x8f\x96W\x01o$\xa6\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Btj\xee\xa1O'\xbe\xff\f\r\xa6BS\xf1\xe7\x97\x18\x90\xa0\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4B{F*\xb8NP\x91\xf4\x8aF\xeb\f\u0712\xdd\xcb&\xe0x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B~GQ\u00fa\xbex\xcf\xf8\x83\b\x86\xfe\xbc\x10\xf9\x90\x8dt\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94B~\xc6h\xac\x94\x04\xe8\x95\u0306\x15\x11\xd1b\nI\x12\xbe\x98\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4B\x80\xa5\x8f\x8b\xb1\v\x94@\u0794\xf4+OY! \x82\x01\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B\x8a\x1e\xe0\xed3\x1dyR\u033e\x1cyt\xb2\x85+\u0453\x8a\x89w\xb7JN\x8d\xe5e\x00\x00\u0794B\x9c\x06\xb4\x87\xe8Tj\xbd\xfc\x95\x8a%\xa3\xf0\xfb\xa5?o\x00\x88\xbbdJ\xf5B\x19\x80\x00\xe0\x94B\xa9\x8b\xf1`'\xceX\x9cN\xd2\xc9X1\xe2rB\x05\x06N\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4B\xc6\xed\xc5\x15\xd3UW\x80\x8d\x13\xcdD\xdc\xc4@\v%\x04\xe4\x89\n\xba\x14\u015b\xa72\x00\x00\u07d4B\xce\xcf\u0492\x10y\xc2\xd7\xdf?\b\xb0z\xa3\xbe\xee^!\x9a\x8965\u026d\xc5\u07a0\x00\x00\u07d4B\u04669\x9b0\x16\xa8Y\u007f\x8bd\t'\xb8\xaf\xbc\xe4\xb2\x15\x89\xa1\x8b\xce\xc3H\x88\x10\x00\x00\u07d4B\xd3I@\xed\xd2\xe7\x00]F\xe2\x18\x8eL\xfe\u0383\x11\xd7M\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4B\u04e5\xa9\x01\xf2\xf6\xbd\x93V\xf1\x12\xa7\x01\x80\xe5\xa1U\v`\x892$\xf4'#\xd4T\x00\x00\u07d4B\u05b2c\xd9\xe9\xf4\x11lA\x14$\xfc\x99Ux;\xa1\xc5\x1b\x81\x0f\xc4g\u057aM\xeaB\xf7\xa9\x88^i\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94C>\xb9J3\x90\x86\xed\x12\u067d\xe9\xcd\x1dE\x86\x03\xc9}\u058a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4CI\"Zb\xf7\n\xeaH\n\x02\x99\x15\xa0\x1eSy\xe6O\xa5\x89\x8c\xd6~#4\xc0\xd8\x00\x00\u07d4CT\"\x1eb\xdc\t\xe6@d6\x16:\x18^\xf0m\x11J\x81\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94CTC\xb8\x1d\xfd\xb9\xbd\x8cg\x87\xbc%\x18\xe2\xd4~W\xc1_\x8a\x01C\x8d\x93\x97\x88\x1e\xf2\x00\x00\u07d4Ca\u0504o\xaf\xb3w\xb6\xc0\xeeI\xa5\x96\xa7\x8d\xdf5\x16\xa3\x89\xc2\x12z\xf8X\xdap\x00\x00\xe0\x94Cd0\x9a\x9f\xa0p\x95`\x0fy\xed\xc6Q \xcd\xcd#\xdcd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Cg\xaeK\f\xe9d\xf4\xa5J\xfdK\\6\x84\x96\xdb\x16\x9e\x9a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Ct\x89(\xe8\xc3\xecD6\xa1\u0412\xfb\xe4:\xc7I\xbe\x12Q\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4Cv{\xf7\xfd*\xf9[r\xe91-\xa9D<\xb1h\x8eCC\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94Cy\x838\x8a\xb5\x9aO\xfc!_\x8e\x82iF\x10)\xc3\xf1\xc1\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4C\x89\x8cI\xa3MP\x9b\xfe\xd4\xf7`A\xee\x91\xca\xf3\xaaj\xa5\x89\x10CV\x1a\x88)0\x00\x00\u07d4C\x8c/T\xff\x8eb\x9b\xab6\xb1D+v\v\x12\xa8\x8f\x02\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\x98b\x8e\xa6c-9>\x92\x9c\xbd\x92\x84d\xc5h\xaaJ\f\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4C\x9d//Q\x10\xa4\u054b\x17W\x93P\x15@\x87@\xfe\xc7\xf8\x89\u03e5\xc5\x15\x0fL\x88\x80\x00\u07d4C\x9d\xee?vy\xff\x100s?\x93@\xc0\x96hkI9\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xb0y\xba\xf0ry\x99\xe6k\xf7C\u057c\xbfwl;\t\"\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xbc-M\xdc\xd6X;\xe2\u01fc\tK(\xfbr\xe6+\xa8;\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xc7\xeb\u0173\xe7\xaf\x16\xf4}\xc5az\xb1\x0e\x0f9\xb4\xaf\xbb\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4C\u02d6R\x81\x8coMg\x96\xb0\xe8\x94\t0ly\xdbcI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xcc\b\xd0s*\xa5\x8a\xde\xf7a\x9b\xedFU\x8a\xd7wAs\x89\xf0\xe7\u0730\x12*\x8f\x00\x00\xe0\x94C\u0567\x1c\xe8\xb8\xf8\xae\x02\xb2\xea\xf8\xea\xf2\xca(@\xb9?\xb6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794C\xdb\u007f\xf9Z\bm(\ubff8/\xb8\xfb_#\n^\xbc\u0348\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4C\xe7\xec\x84cX\xd7\xd0\xf97\xad\x1c5\v\xa0i\u05ffr\xbf\x89\x06p\xaeb\x92\x14h\x00\x00\u07d4C\xf1o\x1eu\xc3\xc0j\x94x\xe8\u0157\xa4\n<\xb0\xbf\x04\u0309\x9d\xf7\u07e8\xf7`H\x00\x00\u07d4C\xf4p\xede\x9e)\x91\xc3u\x95~]\xde\u017d\x1d8\"1\x89\x05k\xc7^-c\x10\x00\x00\u07d4C\xf7\xe8n8\x1e\xc5\x1e\u0110m\x14v\u02e9z=\xb5\x84\xe4\x8965\u026d\xc5\u07a0\x00\x00\u07d4C\xff8t>\xd0\xcdC0\x8c\x06e\t\u030e~r\xc8b\xaa\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94C\xff\x88S\xe9\x8e\xd8@k\x95\x00\n\u0684\x83b\u05a09*\x8a\x04\xae\v\x1cM.\x84\xd0\x00\x00\u07d4D\t\x88f\xa6\x9bh\xc0\xb6\xbc\x16\x82)\xb9`5\x87\x05\x89g\x89\n1\x06+\xee\xedp\x00\x00\u07d4D\x19\xaca\x8d]\xea|\xdc`w o\xb0}\xbd\xd7\x1c\x17\x02\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4D\x1aR\x00\x16a\xfa\xc7\x18\xb2\u05f3Q\xb7\xc6\xfbR\x1az\xfd\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94D\x1a\u0282c\x13$\xac\xbf\xa2F\x8b\xda2[\xbdxG{\xbf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4D\x1f7\xe8\xa0)\xfd\x02H/(\x9cI\xb5\xd0m\x00\xe4\b\xa4\x89\x12\x11\xec\xb5m\x13H\x80\x00\u07d4D \xaa5F[\xe6\x17\xad$\x98\xf3p\xde\n<\xc4\xd20\xaf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4D#/\xf6m\xda\xd1\xfd\x84\x12f8\x006\xaf\xd7\xcf}\u007fB\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4D%\rGn\x06$\x84\xe9\b\n9g\xbf:Js*\xd7?\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4D)\xa2\x9f\xee\x19\x84Pg,\f\x1d\a1b%\v\xecdt\x896*\xaf\x82\x02\xf2P\x00\x00\u07d4D5RS\xb2wH\xe3\xf3O\xe9\xca\xe1\xfbq\x8c\x8f$\x95)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4D8\xe8\x80\xcb'f\xb0\xc1\u03ae\xc9\xd2A\x8f\u03b9R\xa0D\x89\a?\xa0s\x90?\b\x00\x00\u07d4DL\xafy\xb7\x138\ue6a7\xc73\xb0*\u02a7\xdc\x02YH\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4D\\\xb8\xde^=\xf5 \xb4\x99\xef\u0240\xf5+\xff@\xf5\\v\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Dj\x809\xce\u03dd\xceHy\xcb\xca\xf3I;\xf5E\xa8\x86\x10\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4Dt)\x9d\x0e\xe0\x90\u0710x\x9a\x14\x86H\x9c=\rd^m\x8965\u026d\xc5\u07a0\x00\x00\u07d4D\x8b\xf4\x10\xad\x9b\xbc/\xec\xc4P\x8d\x87\xa7\xfc.K\x85a\xad\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4D\x90\x1e\r\x0e\b\xac=^\x95\xb8\xec\x9d^\x0f\xf5\xf1.\x03\x93\x89\x16\xa1\xf9\xf5\xfd}\x96\x00\x00\xe0\x94D\x93\x12<\x02\x1e\xce;3\xb1\xa4R\xc9&\x8d\xe1@\a\xf9\u04ca\x01je\x02\xf1Z\x1eT\x00\x00\xe0\x94D\x9a\xc4\xfb\xe3\x83\xe3g8\x85^6JW\xf4q\xb2\xbf\xa11\x8a)\xb7d2\xb9DQ \x00\x00\u07d4D\xa0\x1f\xb0J\xc0\xdb,\xce]\xbe(\x1e\x1cF\xe2\x8b9\xd8x\x89lj\xccg\u05f1\xd4\x00\x00\u07d4D\xa6=\x18BE\x87\xb9\xb3\a\xbf\xc3\xc3d\xae\x10\xcd\x04\xc7\x13\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94D\xa8\x98\x9e20\x81!\xf7$f\x97\x8d\xb3\x95\xd1\xf7l:K\x8a\x01\x88P)\x9fB\xb0j\x00\x00\u07d4D\xc1\x11\v\x18\x87\x0e\xc8\x11x\xd9=!X8\xc5Q\u050ed\x89\n\xd6\xf9\x85\x93\xbd\x8f\x00\x00\u07d4D\xc1Ge\x12|\xde\x11\xfa\xb4l],\xf4\u0532\x89\x00#\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94D\xc5N\xaa\x8a\xc9@\xf9\xe8\x0f\x1et\xe8/\xc1O\x16v\x85j\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4D\xcdwSZ\x89?\xa7\xc4\xd5\xeb:$\x0ey\u0419\xa7--\x89,s\xc97t,P\x00\x00\u07d4D\u07faP\xb8)\xbe\xcc_O\x14\u0470J\xab3 \xa2\x95\xe5\x8965\u026d\xc5\u07a0\x00\x00\u07d4D\xe2\xfd\xc6y\xe6\xbe\xe0\x1e\x93\xefJ:\xb1\xbc\xce\x01*\xbc|\x89\x16=\x19I\x00\xc5E\x80\x00\xe0\x94D\xf6/*\xaa\xbc)\xad:k\x04\xe1\xffo\x9c\xe4R\xd1\xc1@\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4D\xff\xf3{\xe0\x1a8\x88\u04f8\xb8\u1200\xa7\xdd\xef\xee\xea\u04c9\x0e\f[\xfc}\xae\x9a\x80\x00\u07d4E\x06\xfe\x19\xfaK\x00k\xaa9\x84R\x9d\x85\x16\xdb++P\xab\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\x1b6\x99G[\xed]y\x05\xf8\x90Z\xa3Eo\x1e\u05c8\xfc\x89\x8a\xc7#\x04\x89\xe8\x00\x00\x00\u0794E\x1bpp%\x9b\u06e2q\x00\xe3n#B\x8aS\xdf\xe3\x04\u9239\x8b\xc8)\xa6\xf9\x00\x00\u07d4E'+\x8fb\xe9\xf9\xfa\x8c\xe0D \u1ba3\xeb\xa9hn\xac\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94E+d\u06ce\xf7\xd6\u07c7\u01c8c\x9c\"\x90\xbe\x84\x82\xd5u\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4E>5\x9a3\x97\x94LZ'Z\xb1\xa2\xf7\n^Z?i\x89\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4EI\xb1Yy%_~e\xe9\x9b\rV\x04\u06d8\xdf\xca\u023f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4EKa\xb3D\xc0\xef\x96Qy#\x81U\xf2w\u00c2\x9d\v8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94EO\x01A\xd7!\xd3<\xbd\xc4\x10\x18\xbd\x01\x11\x9a\xa4xH\x18\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4ES3\x90\xe3@\xfe\r\xe3\xb3\xcf_\xb9\xfc\x8e\xa5R\xe2\x9eb\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4ES\x96\xa4\xbb\u067a\u8bdf\xb7\xc4\xd6MG\x1d\xb9\xc2E\x05\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4E[\x92\x96\x92\x1at\xd1\xfcAa\u007fC\xb80>o>\xd7l\x89\u3bb5sr@\xa0\x00\x00\u07d4E\\\xb8\xee9\xff\xbcu#1\xe5\xae\xfcX\x8e\xf0\xeeY4T\x8965F:x\r\xef\x80\x00\u07d4Ej\u0b24\x8e\xbc\xfa\xe1f\x06\x02PR_c\x96^v\x0f\x89\x10CV\x1a\x88)0\x00\x00\u07d4Eo\x8dtf\x82\xb2$g\x93I\x06M\x1b6\x8c|\x05\xb1v\x89\u0213\u041c\x8fQP\x00\x00\u07d4Ep)\xc4i\xc4T\x8d\x16\x8c\xec>e\x87.D(\xd4+g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Eq\xdeg+\x99\x04\xba\xd8t6\x92\xc2\x1cO\xdc\xeaL.\x01\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Ex\x1b\xbew\x14\xa1\xc8\xf7;\x1cty!\xdfO\x84'\x8bp\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E{\xce\xf3}\xd3\xd6\v-\xd0\x19\xe3\xfea\xd4k?\x1erR\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94E\x8e<\u025e\x94xD\xa1\x8ejB\x91\x8f\xef~\u007f_^\xb3\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\u07d4E\x93\x93\xd6:\x06>\xf3r\x1e\x16\xbd\x9f\xdeE\ue77dw\xfb\x89j\xba\u05a3\xc1S\x05\x00\x00\u07d4E\xa5p\xdc\xc2\t\f\x86\xa6\xb3\xea)\xa6\bc\xdd\xe4\x1f\x13\xb5\x89\f\x9a\x95\xee)\x86R\x00\x00\u07d4E\xa8 \xa0g/\x17\xdct\xa0\x81\x12\xbcd?\xd1\x16w6\u00c9\n\xd6\xc4;(\x15\xed\x80\x00\u07d4E\xb4q\x05\xfeB\xc4q-\xcen*!\xc0[\xff\xd5\xeaG\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\xbb\x82\x96R\u063f\xb5\x8b\x85'\xf0\xec\xb6!\u009e!.\u00c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\xc0\u045f\v\x8e\x05O\x9e\x8986\xd5\xec\xaey\x01\xaf(\x12\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4E\xc4\xec\xb4\xee\x89\x1e\xa9\x84\xa7\xc5\xce\xfd\x8d\xfb\x001\v(P\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4E\u028d\x95f\b\xf9\xe0\n/\x99t\x02\x86@\x88\x84ef\x8f\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\u0298b\x00;N@\xa3\x17\x1f\xb5\xca\xfa\x90(\xca\xc8\xde\x19\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4E\xd1\xc9\xee\xdf|\xabA\xa7y\x05{y9_T(\xd8\x05(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\u0535M7\xa8\xcfY\x98!#_\x06/\xa9\xd1p\xed\u8909\x11\x90g;_\u0690\x00\x00\xe0\x94E\xdb\x03\xbc\xcf\u05a5\xf4\xd0&k\x82\xa2*6\x87\x92\xc7}\x83\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4E\xe3\xa9>r\x14J\u0686\f\xbcV\xff\x85\x14Z\xda8\xc6\u0689WG=\x05\u06ba\xe8\x00\x00\u07d4E\u6378\u06fa\xba_\xc2\xcb3|b\xbc\xd0\xd6\x1b\x05\x91\x89\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\u6379L}\n\xb7\xacA\x85zq\xd6qG\x87\x0fNq\x8aT\xb4\v\x1f\x85+\xda\x00\x00\x00\u07d4E\xf4\xfc`\xf0\x8e\xac\xa1\x05\x98\xf03c)\x80\x1e<\x92\xcbF\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4F\rSU\xb2\xce\xebnb\x10}\x81\xe5\x12p\xb2k\xf4V \x89l\xb7\xe7Hg\xd5\xe6\x00\x00\xe0\x94F\"O2\xf4\xec\xe5\u0206p\x90\xd4@\x9dU\xe5\v\x18C-\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4F'\xc6\x06\x84&q\xab\u0782\x95\xee]\xd9L\u007fT\x954\xf4\x89\x0f\x89_\xbd\x872\xf4\x00\x00\u07d4F+g\x8bQ\xb5\x84\xf3\xedz\xda\a\v\\\u065c\v\xf7\xb8\u007f\x89\x05k\xc7^-c\x10\x00\x00\u07d4FM\x9c\x89\xcc\xe4\x84\xdf\x00\x02w\x19\x8e\xd8\a_\xa65r\u0449\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4FPNj!Z\xc8;\xcc\xf9V\xbe\xfc\x82\xabZg\x93q\u0209\x1c!(\x05\u00b4\xa5\x00\x00\xe0\x94FQ\xdcB\x0e\b\xc3);'\xd2Ix\x90\xebP\":\xe2\xf4\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4FS\x1e\x8b\x1b\xde\t\u007f\u07c4\x9dm\x11\x98\x85`\x8a\x00\x8d\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4Fb\x92\xf0\xe8\rC\xa7\x87t'u\x90\xa9\xebE\x96\x12\x14\xf4\x894\x95tD\xb8@\xe8\x00\x00\xe0\x94Fb\xa1v^\xe9!\x84-\u0708\x89\x8d\x1d\xc8bu\x97\xbd~\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Fe\xe4s\x96\xc7\u06d7\xeb*\x03\xd9\bc\xd5\u053a1\x9a\x94\x89 \x86\xac5\x10R`\x00\x00\u07d4Fo\xdak\x9bX\xc5S'P0j\x10\xa2\xa8\xc7h\x10;\a\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4Fq$\xae\u007fE/&\xb3\xd5t\xf6\b\x88\x94\xfa]\x1c\xfb;\x89\x92^\x06\xee\xc9r\xb0\x00\x00\u0794Fr*6\xa0\x1e\x84\x1d\x03\xf7\x80\x93^\x91}\x85\u0566z\xbd\x88\xce\xc7o\x0eqR\x00\x00\u07d4Fw\x9aVV\xff\x00\xd7>\xac:\xd0\u00cbl\x850\x94\xfb@\x89\f\x82S\xc9lj\xf0\x00\x00\u07d4Fw\xb0N\x03C\xa3!1\xfdj\xbb9\xb1\xb6\x15k\xba=[\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4F}Y\x88$\x9ahaG\x16e\x98@\xed\n\xe6\xf6\xf4W\xbc\x89\x15\x01\xa4\x8c\xef\xdf\xde\x00\x00\u07d4F~\x0e\xd5O;v\xae\x066\x17n\aB\b\x15\xa0!sn\x89lk\x93[\x8b\xbd@\x00\x00\u07d4F~\xa1\x04E\x82~\xf1\xe5\x02\xda\xf7k\x92\x8a \x9e\r@2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94F\u007f\xbfAD\x16\x00u\u007f\xe1X0\xc8\xcd_O\xfb\xbb\xd5`\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94F\x93Xp\x932\xc8+\x88~ \xbc\xdd\xd0\"\x0f\x8e\u06e7\u040a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4F\x97\xba\xaf\x9c\xcb`?\xd3\x040h\x9dCTE\xe9\u024b\xf5\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4F\xa3\v\x8a\x80\x891!tE\xc3\xf5\xa9>\x88,\x03E\xb4&\x89\r\x8d\xb5\xeb\u05f2c\x80\x00\u07d4F\xa40\xa2\u0528\x94\xa0\u062a?\xea\xc6\x156\x14\x15\xc3\xf8\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4F\xaaP\x18pg~\u007f\nPHv\xb4\xe8\x80\x1a\n\xd0\x1cF\x89+^:\xf1k\x18\x80\x00\x00\u07d4F\xbf\u0172\a\xeb \x13\xe2\xe6\x0fw_\xec\xd7\x18\x10\u0159\f\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4F\xc1\xaa\"D\xb9\u0229W\u028f\xacC\x1b\x05\x95\xa3\xb8h$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4F\xd8\x061(B\x03\xf6(\x8e\xcdNWX\xbb\x9dA\xd0]\xbe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\n\xc5\xd1\xf3\xef\xe2\x8f8\x02\xaf\x92[W\x1ec\x86\x8b9}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\x10\x10\xdaI/@\x18\x83;\b\x8d\x98r\x90\x1e\x06\x12\x91t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4G\x12T\x02e\xcb\xee\u00c4p\"\u015f\x1b1\x8dC@\n\x9e\x89\xbd\xbcA\xe04\x8b0\x00\x00\xe0\x94G\x14\u03e4\xf4k\u05bdps}u\x87\x81\x97\xe0\x8f\x88\xe61\x8a\x02\u007f>\u07f3Nn@\x00\x00\u07d4G H\xcc`\x9a\xeb$!e\uaa87\x05\x85\f\xf3\x12]\xe0\x8965\u026d\xc5\u07a0\x00\x00\u07d4G!\x92)\xe8\xcdVe\x9ae\u00a9C\xe2\u075a\x8fK\xfd\x89\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4G7\xd0B\xdcj\xe7>\xc7:\xe2Qz\u03a2\xfd\xd9d\x87\u014965\u026d\xc5\u07a0\x00\x00\u07d4GAX\xa1\xa9\xdci<\x13?e\xe4{\\:\xe2\xf7s\xa8o\x89\n\xdaUGK\x814\x00\x00\u07d4GE\xab\x18\x1a6\xaa\x8c\xbf\"\x89\xd0\xc4Qe\xbc~\xbe#\x81\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4GPf\xf9\xad&eQ\x96\xd5SS'\xbb\xeb\x9by)\xcb\x04\x89\xa4\xccy\x95c\u00c0\x00\x00\xe0\x94GR!\x8eT\xdeB?\x86\xc0P\x193\x91z\xea\b\xc8\xfe\u054a\x04<3\xc1\x93ud\x80\x00\x00\u07d4GZa\x93W-JNY\u05fe\t\u02d6\r\u074cS\x0e/\x89$,\xf7\x8c\xdf\a\xff\x80\x00\u07d4Gd\x8b\xed\x01\xf3\xcd2I\bNc]\x14\u06a9\xe7\xec<\x8a\x89\n\x84Jt$\xd9\xc8\x00\x00\u07d4Gh\x84\x10\xff%\xd6T\xd7.\xb2\xbc\x06\xe4\xad$\xf83\xb0\x94\x89\b\xb2\x8da\xf3\u04ec\x00\x00\u07d4GkU\x99\b\x9a?\xb6\xf2\x9clr\xe4\x9b.G@\ua00d\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4Gs\x0f_\x8e\xbf\x89\xacr\xef\x80\xe4l\x12\x19P8\xec\xdcI\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94G{$\xee\u80deO\u045d\x12P\xbd\vfEyJa\u028a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4G\x81\xa1\nM\xf5\uef02\xf4\xcf\xe1\a\xba\x1d\x8av@\xbdf\x89a\t=|,m8\x00\x00\u07d4G\x88Z\xba\xbe\xdfM\x92\x8e\x1c\x88\x83\xa6a\x9cl(\x11\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94G\xe2]\xf8\x82%8\xa8Yk(\xc67\x89kM\x14<5\x1d\x8a\x11\v\xe9\xeb$\xb8\x81P\x00\x00\u07d4G\xf4ik\xd4b\xb2\r\xa0\x9f\xb8>\xd2\x03\x98\x18\xd7v%\xb3\x89\b\x13\xcaV\x90m4\x00\x00\u07d4G\xfe\xf5\x85\x84FRH\xa0\x81\r`F>\xe9>Zn\xe8\u04c9\x0fX\xcd>\x12i\x16\x00\x00\u07d4G\xffo\xebC! `\xbb\x15\x03\u05e3\x97\xfc\b\xf4\xe7\x03R\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\xff\xf4,g\x85Q\xd1A\xebu\xa6\xee9\x81\x17\xdf>J\x8d\x89\x05k\xea\xe5\x1f\xd2\xd1\x00\x00\u07d4H\x01\x0e\xf3\xb8\xe9^?0\x8f0\xa8\xcb\u007fN\xb4\xbf`\xd9e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4H\n\xf5 v\x00\x9c\xa77\x81\xb7\x0eC\xb9Y\x16\xa6\"\x03\xab\x892\x19r\xf4\b=\x87\x80\x00\u07d4H\x0f1\xb9\x891\x1eA$\u01a7F_ZD\tM6\xf9\u04097\x90\xbb\x85Q7d\x00\x00\xe0\x94H\x11\x15)j\xb7\xdbRI/\xf7\xb6G\xd63)\xfb\\\xbck\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4H\x1e:\x91\xbf\xdc/\x1c\x84(\xa0\x11\x9d\x03\xa4\x16\x01A~\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4H(\xe4\xcb\xe3N\x15\x10\xaf\xb7,+\ueb0aE\x13\xea\xeb\u0649\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94H)\x82\xac\x1f\x1cm\x17!\xfe\xec\u0679\xc9l\xd9I\x80PU\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4H0,1\x1e\xf8\xe5\xdcfAX\xddX<\x81\x19Mn\rX\x89\xb6gl\xe0\xbc\xcb\\\x00\x00\u07d4H;\xa9\x904\xe9\x00\xe3\xae\xdfaI\x9d;+\xce9\xbe\xb7\xaa\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4HT\x8bK\xa6+\xcb/\r4\xa8\x8d\u019ah\x0eS\x9c\xf0F\x89\x05l\xf1\u02fbt2\x00\x00\u07d4Hc\x84\x979&Zc\xb0\xa2\xbf#jY\x13\xe6\xf9Y\xce\x15\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4He\x9d\x8f\x8c\x9a/\xd4Oh\u06a5]#\xa6\b\xfb\xe5\x00\u0709lk\x93[\x8b\xbd@\x00\x00\xe0\x94Hf\x9e\xb5\xa8\x01\u0637_\xb6\xaaX\xc3E\x1bpX\xc2C\xbf\x8a\x06\x8dB\xc18\u06b9\xf0\x00\x00\u07d4Hjl\x85\x83\xa8D\x84\xe3\xdfC\xa1#\x83\u007f\x8c~#\x17\u0409\x11\x87\xc5q\xab\x80E\x00\x00\u07d4Hz\xdf}p\xa6t\x0f\x8dQ\xcb\xddh\xbb?\x91\u0125\xceh\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4H~\x10\x85\x02\xb0\xb1\x89\uf70cm\xa4\xd0\xdbba\xee\xc6\xc0\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94H\x88\xfb%\xcdP\u06f9\xe0H\xf4\x1c\xa4}x\xb7\x8a'\xc7\u064a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u0794H\x934\u00b6\x95\xc8\xee\a\x94\xbd\x86B\x17\xfb\x9f\xd8\xf8\xb15\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4H\xa3\r\xe1\xc9\x19\xd3\xfd1\x80\xe9}_+*\x9d\xbd\x96M-\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4H\xbf\x14\u05f1\xfc\x84\xeb\xf3\xc9k\xe1/{\xce\x01\xaai\xb0>\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4H\xc2\ue465\aV\xd8\u039a\xbe\xebu\x89\xd2,o\xee]\xfb\x89\xae\x8ez\v\xb5u\xd0\x00\x00\u07d4H\xc5\u0197\v\x91a\xbb\x1c{z\xdf\xed\x9c\xde\u078a\x1b\xa8d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94H\xd2CKz}\xbb\xff\b\";c\x87\xb0]\xa2\xe5\t1&\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4H\xd4\xf2F\x8f\x96?\u05da\x00a\x98\xbbg\x89]-Z\xa4\u04c9K\xe4\xe7&{j\xe0\x00\x00\u07d4H\xe0\xcb\xd6\u007f\x18\xac\xdbzb\x91\xe1%M\xb3.\trs\u007f\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4H\xf6\n5HO\xe7y+\u030a{c\x93\xd0\u0761\xf6\xb7\x17\x89\xc3(\t>a\xee@\x00\x00\u07d4H\xf8\x83\xe5g\xb46\xa2{\xb5\xa3\x12M\xbc\x84\xde\xc7u\xa8\x00\x89)\xd7n\x86\x9d\u0340\x00\x00\xe0\x94I\x01E\xaf\xa8\xb5E\"\xbb!\xf3R\xf0m\xa5\xa7\x88\xfa\x8f\x1d\x8a\x01\xf4lb\x90\x1a\x03\xfb\x00\x00\u07d4I\t\xb3\x19\x98\xea\xd4\x14\xb8\xfb\x0e\x84k\xd5\xcb\xde995\xbe\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4I\x12\xd9\x02\x93\x16v\xff9\xfc4\xfe<<\xc8\xfb!\x82\xfaz\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4I\x13o\xe6\xe2\x8btS\xfc\xb1kk\xbb\u9aac\xba\x837\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94I\x15a\u06cbo\xaf\xb9\x00~b\xd0P\u0082\xe9,Kk\u020a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4I\x18]\xd7\xc262\xf4lu\x94s\ubb96`\b\xcd5\x98\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4I,\xb5\xf8a\xb1\x87\xf9\xdf!\xcdD\x85\xbe\xd9\vP\xff\xe2-\x89\x1b\x19\xe5\vD\x97|\x00\x00\u07d4I-\xe4j\xaf\x8f\x1dp\x8dY\u05da\xf1\xd0:\xd2\xcb`\x90/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I.p\xf0M\x18@\x8c\xb4\x1e%`70Pk5\xa2\x87k\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4I:g\xfe#\xde\xccc\xb1\r\xdau\xf3(v\x95\xa8\x1b\u056b\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4I=H\xbd\xa0\x15\xa9\xbf\xcf\x16\x03\x93n\xabh\x02L\xe5Q\xe0\x89\x018\xa3\x88\xa4<\x00\x00\x00\xe0\x94IBV\xe9\x9b\x0f\x9c\xd6\xe5\xeb\xca8\x99\x862R\x90\x01e\u020a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4IM\xecM^\xe8\x8a'q\xa8\x15\xf1\xeerd\x94/\xb5\x8b(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I[d\x1b\x1c\u07a3b\u00f4\u02fd\x0f\\\xc5\v\x1e\x17k\x9c\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Ih\xa2\xce\xdbEuU\xa19)Z\xea(wnT\x00<\x87\x8a\x02#\x1a\xef\u0266b\x8f\x00\x00\u07d4Im6U4S\n_\xc1W|\nRA\u02c8\xc4\xdapr\x89a\t=|,m8\x00\x00\xe0\x94In1\x95\x92\xb3A\xea\xcc\xd7x\u0767\xc8\x19mT\xca\xc7u\x8a\x01\xf5q\x89\x87fKH\x00\x00\u07d4IoXC\xf6\xd2L\u064d%^L#\xd1\xe1\xf0#\"uE\x89_\x17\x9f\u0526\xee\t\x80\x00\xe0\x94Ip\u04ec\xf7+[\x1f2\xa7\x00<\xf1\x02\xc6N\xe0TyA\x8a\x1d\xa5jK\b5\xbf\x80\x00\x00\u07d4Iw\xa7\x93\x9d\t9h\x94U\xce&9\xd0\xeeZL\xd9\x10\xed\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4Iy\x19N\xc9\xe9}\xb9\xbe\xe84;|w\xd9\xd7\xf3\xf1\u071f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Iy4c\xe1h\x10\x83\u05ab\xd6\xe7%\u057b\xa7E\xdc\xcd\xe8\x89\x1d\x98\xe9LNG\x1f\x00\x00\u07d4I\x81\xc5\xfff\xccN\x96\x80%\x1f\xc4\xcd/\xf9\a\xcb2xe\x89(\xa8WBTf\xf8\x00\x00\u07d4I\x89\u007f\xe92\xbb\xb3\x15L\x95\u04fc\xe6\xd9;ms)\x04\u0749\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4I\x89\xe1\xab^|\xd0\aF\xb3\x93\x8e\xf0\xf0\xd0d\xa2\x02[\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I\x8a\xbd\xeb\x14\xc2k{r4\xd7\x0f\u03ae\xf3a\xa7m\xffr\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4I\xa6E\xe0f}\xfd{2\xd0u\xcc$g\u074ch\t\a\u0109\a\x06\x01\x95\x8f\u02dc\x00\x00\xe0\x94I\xb7N\x16\x92e\xf0\x1a\x89\xecL\x90r\u0164\xcdr\xe4\xe85\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4I\xbd\xbc{\xa5\xab\xeb\xb68\x9e\x91\xa3(R \xd3E\x1b\xd2S\x8965\u026d\xc5\u07a0\x00\x00\u07d4I\xc9A\xe0\xe5\x01\x87&\xb7)\x0f\xc4s\xb4q\xd4\x1d\xae\x80\u0449\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94I\xc9w\x1f\xca\x19\u0579\xd2E\u0211\xf8\x15\x8f\xe4\x9fG\xa0b\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4I\xcf\x1eT\xbe61\x06\xb9 r\x9d-\v\xa4o\bg\x98\x9a\x89\x0e\x87?D\x13<\xb0\x00\x00\u07d4I\xd2\u008e\xe9\xbcT^\xaa\xf7\xfd\x14\xc2|@s\xb4\xbb_\x1a\x89O\xe9\xb8\x06\xb4\r\xaf\x00\x00\u07d4I\xdd\xee\x90.\x1d\f\x99\u0471\x1a\xf3\u030a\x96\xf7\x8eM\xcf\x1a\x89\n\u03a5\xe4\xc1\x8cS\x00\x00\u07d4I\xf0(9[Z\x86\xc9\xe0\u007fwxc\x0eL.=7:w\x89\x06\xa7JP8\u06d1\x80\x00\xe0\x94J\x19 5\xe2a\x9b$\xb0p\x9dVY\x0e\x91\x83\xcc\xf2\xc1\u064a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4J@S\xb3\x1d\x0e\xe5\u06ef\xb1\xd0k\u05ec\u007f\xf3\",G\u0589K\xe4\xe7&{j\xe0\x00\x00\u07d4JC\x01p\x15-\xe5\x17&3\u0742b\xd1\a\xa0\xaf\xd9j\x0f\x89\xabM\xcf9\x9a:`\x00\x00\u07d4JG\xfc>\x17\u007fVz\x1e8\x93\xe0\x00\xe3k\xba#R\n\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4JR\xba\xd2\x03W\"\x8f\xaa\x1e\x99k\xedy\f\x93gK\xa7\u0409Hz\x9a0E9D\x00\x00\u07d4JS\xdc\xdbV\xceL\xdc\xe9\xf8.\xc0\xeb\x13\xd6sR\xe7\u020b\x89\u3bb5sr@\xa0\x00\x00\u07d4J_\xae;\x03r\xc20\xc1%\xd6\xd4p\x14\x037\xab\x91VV\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4Jq\x90a\xf5(T\x95\xb3{\x9d~\xf8\xa5\x1b\a\xd6\u6b2c\x89\n\xd4\xc81j\v\f\x00\x00\u07d4Js8\x92\x98\x03\x1b\x88\x16\u0329FB\x1c\x19\x9e\x18\xb3C\u0589\"8h\xb8y\x14o\x00\x00\u07d4Js]\"G\x927m3\x13g\xc0\x93\xd3\x1c\x87\x944\x15\x82\x89f\xff\xcb\xfd^Z0\x00\x00\u07d4Jt\x94\xcc\xe4HU\u0300X(B\xbe\x95\x8a\r\x1c\x00r\ue242\x1a\xb0\xd4AI\x80\x00\x00\u07d4Ju\xc3\xd4\xfao\u033d]\u0567\x03\xc1Sy\xa1\xe7\x83\u9dc9b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94J\x81\xab\xe4\x98L|k\xefc\u0598 \xe5WC\xc6\x1f \x1c\x8a\x03d\x01\x00N\x9a\xa3G\x00\x00\u07d4J\x82iO\xa2\x9d\x9e!2\x02\xa1\xa2\t(]\xf6\xe7E\xc2\t\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4J\x83\\%\x82LG\xec\xbf\u01d49\xbf?\\4\x81\xaau\u0349K\xe4\xe7&{j\xe0\x00\x00\u07d4J\x91\x802C\x91Y\xbb1[g%\xb6\x83\r\xc86\x97s\x9f\x89\x12\xa3.\xf6x3L\x00\x00\u07d4J\x97\xe8\xfc\xf4c^\xa7\xfc^\x96\xeeQu.\u00c8qk`\x89\x1d\x99E\xab+\x03H\x00\x00\u07d4J\x9a&\xfd\n\x8b\xa1\x0f\x97}\xa4\xf7|1\x90\x8d\xabJ\x80\x16\x89a\t=|,m8\x00\x00\u07d4J\xa1H\xc2\xc34\x01\xe6j+Xnew\u0132\x92\xd3\xf2@\x89\v\xb8`\xb2\x85\xf7t\x00\x00\u07d4J\xa6\x93\xb1\"\xf3\x14H*G\xb1\x1c\xc7|h\xa4\x97\x87ab\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4J\xb2\xd3O\x04\x83O\xbftyd\x9c\xab\x92=,G%\xc5S\x89\xbe\xd1\xd0&=\x9f\x00\x00\x00\u07d4J\xc0vs\xe4/d\xc1\xa2^\xc2\xfa-\x86\xe5\xaa+4\xe09\x89lk\x93[\x8b\xbd@\x00\x00\u07d4J\u016c\xad\x00\v\x88w!L\xb1\xae\x00\xea\u0263}Y\xa0\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4J\u0250ZL\xb6\xab\x1c\xfdbTn\xe5\x91s\x00\xb8|O\u07897\b\xba\xed=h\x90\x00\x00\u07d4J\u03e9\xd9N\xdaf%\xc9\u07e5\xf9\xf4\xf5\xd1\a\xc4\x03\x1f\u07c9\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4J\xd0G\xfa\xe6~\xf1b\xfeh\xfe\xdb\xc2};e\xca\xf1\f6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4J\xd9]\x18\x8dddp\x9a\xdd%U\xfbM\x97\xfe\x1e\xbf1\x1f\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d4J\xdb\xf4\xaa\xe0\xe3\xefD\xf7\xddM\x89\x85\u03ef\tn\u010e\x98\x89\b!\xab\rD\x14\x98\x00\x00\u07d4J\xe2\xa0M9\t\xefENTL\xcf\xd6\x14\xbf\xef\xa7\x10\x89\xae\x89\x18\x01\x15\x9d\xf1\xee\xf8\x00\x00\xe0\x94J\xe90\x82\xe4Q\x87\xc2a`\xe6g\x92\xf5\u007f\xad5Q\xc7:\x8a\x04\x96\x15 \xda\xff\x82(\x00\x00\u07d4J\xf0\xdb\a{\xb9\xba^D>!\xe1H\xe5\x9f7\x91\x05\u0152\x89 \x86\xac5\x10R`\x00\x00\u07d4K\x06\x19\xd9\u062a1:\x951\xac}\xbe\x04\xca\rjZ\u0476\x89lk\x93[\x8b\xbd@\x00\x00\u07d4K\v\u062c\xfc\xbcS\xa6\x01\v@\xd4\u040d\xdd-\x9dib-\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4K\x19\xeb\f5K\xc199`\xeb\x06\x06;\x83\x92o\rg\xb2\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4K)C|\x97\xb4\xa8D\xbeq\u0323\xb6H\xd4\xca\x0f\u075b\xa4\x89\b$q\x984\u03ec\x00\x00\u07d4K1\xbfA\xab\xc7\\\x9a\xe2\u034f\u007f5\x16;n+tPT\x89\x14\xb5P\xa0\x13\xc78\x00\x00\u07d4K:|\u00e7\u05f0\x0e\xd5(\"!\xa6\x02Y\xf2[\xf6S\x8a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94K:\xab3^\xbb\xfa\xa8p\xccM`^}.t\xc6h6\x9f\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4K\xcd\xc1\x8a`\x00\x00\u07d4K`\xa3\xe2S\xbf8\xc8\xd5f \x10\xbb\x93\xa4s\xc9e\xc3\xe5\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4Kt\xf5\xe5\x8e.\xdfv\xda\xf7\x01Q\x96J\v\x8f\x1d\xe0f<\x89\x11\x90\xaeID\xba\x12\x00\x00\u07d4Kv!f\xdd\x11\x18\xe8Ci\xf8\x04\xc7_\x9c\xd6W\xbfs\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Ky.)h>\xb5\x86\u353b3Rl`\x01\xb3\x97\x99\x9e\x89 \x86\xac5\x10R`\x00\x00\u07d4K\x90N\x93K\xd0\u030b p_\x87\x9e\x90[\x93\xea\f\xcc0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94K\x92\x06\xbakT\x9a\x1a\u007f\x96\x9e\x1d]\xba\x86u9\xd1\xfag\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4K\x98N\xf2lWn\x81Z.\xae\xd2\xf5\x17\u007f\a\u06f1\xc4v\x89T\x91YV\xc4\t`\x00\x00\u07d4K\x9e\x06\x8f\xc4h\tv\xe6\x15\x04\x91)\x85\xfd\\\xe9K\xab\r\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4K\xa0\xd9\xe8\x96\x01w+IhG\xa2\xbbC@\x18g\x87\xd2e\x8965\u026d\xc5\u07a0\x00\x00\u07d4K\xa5:\xb5I\xe2\x01m\xfa\"<\x9e\u0563\x8f\xad\x91(\x8d\a\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94K\xa8\xe0\x11\u007f\xc0\xb6\xa3\xe5k$\xa3\xa5\x8f\xe6\xce\xf4B\xff\x98\x8a\x011\xbe\xb9%\xff\xd3 \x00\x00\u07d4K\xac\x84j\xf4\x16\x9f\x1d\x95C\x1b4\x1d\x88\x00\xb2!\x80\xaf\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4K\xb6\xd8k\x83\x14\xc2-\x8d7\xeaQm\x00\x19\xf1V\xaa\xe1-\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94K\xb9e\\\xfb*6\xea|cz{\x85\x9bJ1T\xe2n\xbe\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94K\xbc\xbf8\xb3\xc9\x01c\xa8K\x1c\u04a9;X\xb2\xa34\x8d\x87\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94K\xd6\xdd\f\xff#@\x0e\x170\xba{\x89E\x04W}\x14\xe7J\x8a+\xa0\xcc\xdd\xd0\xdfs\xb0\x00\x00\u07d4K\xe8b\x8a\x81T\x87N\x04\x8d\x80\xc1B\x18\x10\"\xb1\x80\xbc\xc1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4K\xe9\rA!)\u0564\xd0BCa\xd6d\x9dNG\xa6#\x16\x897\b\xba\xed=h\x90\x00\x00\xe0\x94K\xea(\x8e\xeaB\u0115^\xb9\xfa\xad*\x9f\xafG\x83\xcb\u076c\x8a\x06\x18\xbe\x16c\u012fI\x00\x00\u07d4K\xf4G\x97\x99\xef\x82\xee\xa2\tC7OV\xa1\xbfT\x00\x1e^\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4K\xf8\xbf\x1d5\xa211Wd\xfc\x80\x01\x80\x9a\x94\x92\x94\xfcI\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4K\xf8\xe2oL'\x90\xdae3\xa2\xac\x9a\xba\xc3\u019a\x19\x943\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0794L\n\xcaP\x8b<\xaf^\xe0(\xbcp}\xd1\xe8\x00\xb88\xf4S\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94L\v\x15\x15\xdf\xce\u05e1>\x13\xee\x12\xc0\xf5#\xaePO\x03+\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4L\x13\x98\f2\xdc\xf3\x92\vx\xa4\xa7\x903\x12\x90|\x1b\x12?\x89\x03A\x00\x15\xfa\xae\f\x00\x00\u07d4L\x15y\xaf3\x12\xe4\xf8\x8a\xe9\x95\xcc9W\xd2R\xce\v\xf0\xc8}[O\"4g.p\x89\x87\x86x2n\xac\x90\x00\x00\u07d4LB1y\x82i\x1d\x10\x89\x05k\xc7^-c\x10\x00\x00\u07d4LZ\xfe@\xf1\x8f\xfcH\u04e1\xae\xc4\x1f\u009d\xe1y\xf4\u0497\x89lk\x93[\x8b\xbd@\x00\x00\u07d4L[=\xc0\xe2\xb96\x0f\x91(\x9b\x1f\xe1<\xe1,\x0f\xbd\xa3\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Lfk\x86\xf1\xc5\ue324\x12\x85\xf5\xbd\xe4\xf7\x90R\b\x14\x06\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Lik\xe9\x9f:i\x04@\xc3CjY\xa7\xd7\xe97\u05ba\r\x89\xbb\x91%T\"c\x90\x00\x00\u07d4Lj$\x8f\xc9}p]\xefI\\\xa2\aY\x16\x9e\xf0\xd3dq\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4Lj\x9d\xc2\u02b1\n\xbb.|\x13p\x06\xf0\x8f\ucd77y\xe1\x89\x1b\r\x04 /G\xec\x00\x00\u07d4Lk\x93\xa3\xbe\xc1cIT\f\xbf\xca\xe9l\x96!\xd6dP\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Lu\x98\x13\xad\x13\x86\xbe\xd2\u007f\xfa\xe9\xe4\x81^60\u0323\x12\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Lv\f\xd9\xe1\x95\xeeO-k\xce%\x00\xff\x96\xda|C\ue44a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4Lv{e\xfd\x91\x16\x1fO\xbd\xccji\xe2\xf6\xadq\x1b\xb9\x18\x89'\b\x01\xd9F\xc9@\x00\x00\u07d4L~.+w\xad\f\xd6\xf4J\xcb(a\xf0\xfb\x8b(u\x0e\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4L\x85\xed6/$\xf6\xb9\xf0L\xdf\xcc\xd0\"\xaeSQG\u02f9\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4L\x93[\xb2Pw\x8b0\x9b==\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4L\xee\x90\x1bJ\u0231V\xc5\xe2\xf8\xa6\xf1\xbe\xf5r\xa7\xdc\xeb~\x8965\u026d\xc5\u07a0\x00\x00\u07d4L\xef\xbe#\x98\xe4}R\u73743L\x8bivu\U00053b89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4L\xf5S{\x85\x84/\x89\xcf\xee5\x9e\xaeP\x0f\xc4I\xd2\x11\x8f\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94M\bG\x1dh\x00z\xff*\xe2y\xbc^?\xe4\x15o\xbb\xe3\u078a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4M \x01\x10\x12@\b\xd5ov\x98\x12VB\f\x94jo\xf4\\\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4M$\xb7\xacG\xd2\xf2}\xe9\tt\xba=\xe5\xea\xd2\x03TK\u0349\x05k\xc7^-c\x10\x00\x00\u0794M)\xfcR:,\x16)S!!\u0699\x98\u9d6b\x9d\x1bE\x88\xdbD\xe0I\xbb,\x00\x00\u07d4M8\xd9\x0f\x83\xf4Q\\\x03\xccx2j\x15M5\x8b\u0602\xb7\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4ML\xf5\x80t)a^0\xcd\xfa\xce\x1eZ\xaeM\xad0U\xe6\x89 \x86\xac5\x10R`\x00\x00\u07d4MW\xe7\x16\x87l\f\x95\xef^\xae\xbd5\xc8\xf4\x1b\x06\x9bk\xfe\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Mg\U000ab159\xfe\xf5\xfcA9\x99\xaa\x01\xfd\u007f\xcep\xb4=\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Mn\x8f\xe1\t\xcc\xd2\x15\x8eM\xb1\x14\x13/\xe7_\xec\u023e[\x89\x01[5W\xf1\x93\u007f\x80\x00\xe0\x94Mq\xa6\xeb=\u007f2~\x184'\x8e(\v\x03\x9e\xdd\xd3\x1c/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4M|\xfa\xa8L\xb31\x06\x80\n\x8c\x80/\xb8\xaaF8\x96\u0159\x89a\t=|,m8\x00\x00\u07d4M\x80\x10\x93\xc1\x9c\xa9\xb8\xf3B\xe3<\xc9\xc7{\xbdL\x83\x12\u03c9\x12\xb3\xe7\xfb\x95\u0364\x80\x00\u07d4M\x82\x88\x94u/o%\x17]\xaf!w\tD\x87\x95Ko\x9f\x89O!+\xc2\u011c\x83\x80\x00\xe0\x94M\x82\xd7p\f\x12;\xb9\x19A\x9b\xba\xf0Fy\x9ck\x0e,f\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4M\x83m\x9d;\x0e,\xbdM\xe0PYo\xaaI\f\xff\xb6\r]\x89\x10CV\x1a\x88)0\x00\x00\u07d4M\x86\x97\xaf\x0f\xbf,\xa3n\x87h\xf4\xaf\"\x135phZ`\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4M\x92y\x96 )\xa8\xbdEc\x977\xe9\x8bQ\x1e\xff\aL!\x89Hz\x9a0E9D\x00\x00\u07d4M\x93io\xa2HY\xf5\u0493\x9a\xeb\xfaT\xb4\xb5\x1a\xe1\xdc\u0309\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4M\x9cw\xd0u\f^o\xbc$\u007f/\u05d2thl\xb3S\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4M\xa5\xed\u0188\xb0\xcbb\xe1@=\x17\x00\xd9\u0739\x9f\xfe?\u04c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94M\xa8\x03\ai\x84K\xc3A\x86\xb8\\\xd4\xc74\x88I\xffI\xe9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4M\xb1\xc4:\x0f\x83M}\x04x\xb8\x96\ag\xec\x1a\xc4L\x9a\xeb\x89/Q\x810V'7\x00\x00\u07d4M\xb2\x12\x84\xbc\xd4\xf7\x87\xa7Ue\x00\xd6\xd7\xd8\xf3f#\xcf5\x89i(7Ow\xa3c\x00\x00\u07d4M\xc3\xda\x13\xb2\xb4\xaf\xd4O]\r1\x89\xf4D\xd4\xdd\xf9\x1b\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4M\u013f^u\x89\xc4{(7\x8du\x03\u03d6H\x80a\u06fd\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4M\xc9\u057bK\x19\xce\u0354\xf1\x9e\xc2] \x0e\xa7/%\xd7\xed\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94M\xcd\x11\x81X\x18\xae)\xb8]\x016sI\xa8\xa7\xfb\x12\xd0k\x8a\x01\xacB\x86\x10\x01\x91\xf0\x00\x00\u07d4M\xcfb\xa3\xde?\x06\x1d\xb9\x14\x98\xfda\x06\x0f\x1fc\x98\xffs\x89lj\xccg\u05f1\xd4\x00\x00\u07d4M\xd11\xc7J\x06\x8a7\xc9\n\xde\xd4\xf3\t\xc2@\x9fdx\u04c9\x15\xaf9\u4ab2t\x00\x00\xe0\x94M\u0767Xk\"7\xb0S\xa7\xf3(\x9c\xf4`\xdcW\xd3z\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4M\xe3\xfe4\xa6\xfb\xf64\xc0Q\x99\u007fG\xcc\u007fHy\x1fX$\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4M\xf1@\xbaye\x85\xddT\x891[\xcaK\xbah\n\u06f8\x18\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4N\x02\ay\xb5\xdd\xd3\xdf\"\x8a\x00\xcbH\xc2\xfc\x97\x9d\xa6\xae8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4N\v\xd3$s\xc4\xc5\x1b\xf2VT\xde\xf6\x9fy|k)\xa22\x89V\xc9]\xe8\xe8\xca\x1d\x00\x00\u07d4N\"%\xa1\xbbY\xbc\x88\xa21ft\xd33\xb9\xb0\xaf\xcafU\x89\bg\x0e\x9e\xc6Y\x8c\x00\x00\u07d4N#\x10\x19\x1e\xad\x8d;\xc6H\x98s\xa5\xf0\xc2\xeck\x87\u1f8965\u026d\xc5\u07a0\x00\x00\u07d4N#-S\xb3\u6f8f\x89Sa\xd3\x1c4\xd4v+\x12\xc8.\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4N+\xfaJFo\x82g\x1b\x80\x0e\xeeBj\xd0\f\a\x1b\xa1p\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4N>\xda\u0506M\xabd\xca\xe4\xc5Azvw@S\xdcd2\x89 \b\xfbG\x8c\xbf\xa9\x80\x00\u07d4NC\x18\xf5\xe1>\x82JT\xed\xfe0\xa7\xedO&\xcd=\xa5\x04\x89lk\x93[\x8b\xbd@\x00\x00\u07d4N[w\xf9\x06aY\xe6\x15\x93?-\xdatw\xfaNG\xd6H\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94Nf\x00\x80b\x89EJ\u03630\xa2\xa3U`\x10\u07ec\xad\xe6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Ns\xcf#y\xf1$\x86\x0fs\xd6\xd9\x1b\xf5\x9a\xcc\\\xfc\x84[\x89\x02,\xa3X|\xf4\xeb\x00\x00\xe0\x94Nz\xa6~\x12\x18>\xf9\xd7F\x8e\xa2\x8a\xd29\xc2\xee\xf7\x1bv\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\xe0\x94N{TGM\x01\xfe\xfd8\x8d\xfc\xd5;\x9ff&$A\x8a\x05\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94N\x89.\x80\x81\xbf6\xe4\x88\xfd\xdb;&0\xf3\xf1\xe8\xda0\u048a\x02\x8a\xba0u$Q\xfc\x00\x00\xe0\x94N\x8amcH\x9c\xcc\x10\xa5\u007f\x88_\x96\xeb\x04\xec\xbbT`$\x8a\x03\xea\xe3\x13\x0e\u0316\x90\x00\x00\u07d4N\x8eG\xae;\x1e\xf5\f\x9dT\xa3\x8e\x14 \x8c\x1a\xbd6\x03\u0089y(\xdb\x12vf\f\x00\x00\u0794N\x90\u03312X\xac\xaa\x9fO\xeb\xc0\xa3B\x92\xf9Y\x91\xe20\x88\xdbD\xe0I\xbb,\x00\x00\u07d4N\xa5n\x11\x12d\x1c\x03\x8d\x05e\xa9\u0096\xc4c\xaf\xef\xc1~\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\xe0\x94N\xa7\x0f\x041?\xaee\xc3\xff\"J\x05\\=-\xab(\xdd\u07ca\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4N\xb1EKW8\x05\u022c\xa3~\xde\xc7\x14\x9aA\xf6\x12\x02\xf4\x89\x10CV\x1a\x88)0\x00\x00\u07d4N\xb8{\xa8x\x8e\xba\r\xf8~[\x9b\xd5\n\x8eE6\x80\x91\xc1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4N\xbcV)\xf9\xa6\xa6k,\xf36:\u0109\\\x03H\u8fc7\x8967\tlK\xcci\x00\x00\u07d4N\xc7h)^\xea\xba\xfcB\x95\x84\x15\xe2+\xe2\x16\xcd\xe7v\x18\x89\x03;\x1d\xbc9\xc5H\x00\x00\u07d4N\xcc\x19\x94\x8d\xd9\u0347\xb4\xc7 \x1a\xb4\x8eu\x8f(\xe7\xccv\x89\x1b\x1d\xaba\u04ead\x00\x00\u07d4N\xd1M\x81\xb6\v#\xfb%\x05M\x89%\u07e5s\u072eah\x89\x12nr\xa6\x9aP\xd0\x00\x00\xe0\x94N\xe1<\rA \vF\u045d\xee\\K\xce\xc7\x1d\x82\xbb\x8e8\x8a\x01\xab\xee\x13\u033e\ufbc0\x00\u07d4N\xea\xd4\n\xad\x8cs\xef\b\xfc\x84\xbc\n\x92\xc9\t/j6\xbf\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4N\xeb\xe8\f\xb6\xf3\xaeY\x04\xf6\xf4\xb2\x8d\x90\u007f\x90q\x89\xfc\xab\x89lj\xccg\u05f1\xd4\x00\x00\u07d4N\xeb\xf1 ]\f\xc2\f\xeel\u007f\x8f\xf3\x11_V\u050f\xba&\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4N\xf1\xc2\x14c:\xd9\xc0p;N#t\xa2\xe3>>B\x92\x91\x89Hz\x9a0E9D\x00\x00\u07d4N\xfc\xd9\u01df\xb43L\xa6${\n3\xbd\x9c\xc32\b\xe2r\x89Hz\x9a0E9D\x00\x00\xe0\x94O\x06$k\x8dK\u0496a\xf4>\x93v\"\x01\u0486\x93Z\xb1\x8a\x01\x059O\xfcF6\x11\x00\x00\u07d4O\x15+/\xb8e\x9dCwn\xbb\x1e\x81g:\xa8Ai\xbe\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4O\x17\u007f\x9dV\x95=\xedq\xa5a\x1f93\"\xc3\x02y\x89\\\x89\rU\uf422\xda\x18\x00\x00\u07d4O\x1a-\xa5JLm\xa1\x9d\x14$\x12\xe5n\x81WA\xdb#%\x89\x05k\xc7^-c\x10\x00\x00\u07d4O#\xb6\xb8\x17\xff\xa5\xc6d\xac\xda\u05db\xb7\xb7&\xd3\n\xf0\xf9\x89_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94O&i\f\x99+z1*\xb1.\x13\x85\xd9J\xcdX(\x8e{\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4O+G\xe2wZ\x1f\xa7\x17\x8d\xad\x92\x98Z[\xbeI;\xa6\u0589\n\u05ce\xbcZ\xc6 \x00\x00\u07d4O:HT\x91\x11E\xea\x01\xc6D\x04K\xdb.Z\x96\n\x98/\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4O?,g0i\xac\x97\xc2\x026\a\x15)\x81\xf5\xcd`c\xa0\x89 \x86\xac5\x10R`\x00\x00\xe0\x94OJ\x9b\xe1\f\xd5\xd3\xfb]\xe4\x8c\x17\xbe)o\x89V\x90d[\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4OR\xadap\xd2[*.\x85\x0e\xad\xbbRA?\xf20>\u007f\x89\xa4\xccy\x95c\u00c0\x00\x00\u07d4OX\x01\xb1\xeb0\xb7\x12\u0620WZ\x9aq\xff\x96]O4\xeb\x89\x10CV\x1a\x88)0\x00\x00\u07d4O]\xf5\xb9CW\u0794\x86\x04\xc5\x1bx\x93\xcd\xdf`v\xba\xad\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\u07d4Od\xa8^\x8e\x9a@I\x8c\fu\xfc\xeb\x037\xfbI\b>^\x8965\u026d\xc5\u07a0\x00\x00\u07d4Og9m%S\xf9\x98x_pN\a\xa69\x19}\u0454\x8d\x89\x10DrR\x1b\xa78\x00\x00\u07d4OmG7\u05e9@8$\x87&H\x86i|\xf7c\u007f\x80\x15\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4Os0\toy\xed&N\xe0\x12\u007f]0\xd2\xf7?!\xcb\u007f\x04\x89\x04\x82\xfe&\f\xbc\xa9\x00\x00\u07d4O\xeeP\xc5\xf9\x88 k\t\xa5sF\x9f\xb1\u0434.\xbbm\u0389l\xee\x06\u077e\x15\xec\x00\x00\u07d4O\xf6v\xe2\u007fh\x1a\x98-\x8f\xd9\xd2\x0ed\x8b=\xce\x05\xe9E\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4O\xf6\u007f\xb8\u007fn\xfb\xa9'\x990\u03fd\x1bz4L\u057a\x8bN\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94PFf\u03891\x17^\x11\xa5\xed\x11\xc1\u072a\x06\xe5\u007fNf\x8a\x02\u007f>\u07f3Nn@\x00\x00\u0794PXM\x92\x06\xa4l\xe1\\0\x11\x17\xee(\xf1\\0\xe6\x0eu\x88\xb9\xf6]\x00\xf6<\x00\x00\xe0\x94PZ3\xa1\x864\xddH\x00i)\x13N\x00\x00\u07d4P\u0286\xb5\xeb\x1d\x01\x87M\xf8\xe5\xf3IE\u051cl\x1a\xb8H\x8965\u026d\xc5\u07a0\x00\x00\u07d4P\u0357\xe97\x8b\\\xf1\x8f\x179c#l\x99Q\xeft8\xa5\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4P\u073c'\xbc\xad\x98@\x93\xa2\x12\xa9\xb4\x17\x8e\xab\xe9\x01ua\x89\a\xe3by\v\\\xa4\x00\x00\u07d4P\xe10#\xbd\x9c\xa9j\xd4\xc5?\xdf\xd4\x10\xcbk\x1fB\v\u07c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94P\xe1\xc8\xec\x98A[\xefD&\x18p\x87\x99C{\x86\xe6\xc2\x05\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4P\xf8\xfaK\xb9\xe2g|\x99\nN\xe8\xcep\xdd\x15#%\x1eO\x89\x01i=#\x16Ok\x00\x00\u07d4P\xfb6\xc2q\a\xee,\xa9\xa3#n'F\u0321\x9a\xcekI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4P\xfe\xf2\x96\x95U\x88\u02aet\xc6.\xc3*#\xa4T\xe0\x9a\xb8\x89A\x1d\xff\xab\xc5\a8\x00\x00\u07d4Q\x02\xa4\xa4 w\xe1\x1cX\xdfGs\u3b14F#\xa6m\x9f\x89lp\x15\xfdR\xed@\x80\x00\u07d4Q\x03\x93w\xee\xd0\xc5s\xf9\x86\xc5\xe8\xa9_\xb9\x9aY\xe93\x0f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Q\x03\xbc\t\x93>\x99!\xfdS\xdcSo\x11\xf0]\rG\x10}\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94Q\x04\xec\xc0\xe30\xdd\x1f\x81\xb5\x8a\xc9\u06f1\xa9\xfb\xf8\x8a<\x85\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4Q\r\x81Y\u0314Wh\xc7E\a\x90\xba\a>\xc0\xd9\xf8\x9e0\x89\x8a\xc7#\x04\x89\xe8\x00\x00\x00\u07d4Q\x0e\xdaV\x01I\x9a\r^\x1a\x00k\xff\xfd\x836r\xf2\xe2g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x12dF\xab=\x802U~\x8e\xbaeY}u\xfa\u0701\\\x89\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\xe0\x94Q\x18U}`\r\x05\xc2\xfc\xbf8\x06\xff\xbd\x93\xd0 %\xd70\x8a\x02g\u04ebd#\xf5\x80\x00\x00\u07d4Q\x1e\x0e\xfb\x04\xacN?\xf2\xe6U\x0eI\x82\x95\xbf\xcdV\xff\u0549$=M\x18\"\x9c\xa2\x00\x00\u07d4Q!\x16\x81{\xa9\xaa\xf8C\xd1P|e\xa5\xead\n{\x9e\xec\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4Q&F\ri,q\u026fo\x05WM\x93\x99\x83h\xa27\x99\x89\x02\u0465\x1c~\x00P\x00\x00\u07d4Q'\u007f\xe7\xc8\x1e\xeb\xd2R\xa0=\xf6\x9ak\x9f2n'\"\a\x89\x03@.y\u02b4L\x80\x00\u07d4Q)oPD'\r\x17pvF\x12\x9c\x86\xaa\xd1d^\xad\xc1\x89H|r\xb3\x10\xd4d\x80\x00\xe0\x94Q+\x91\xbb\xfa\xa9\xe5\x81\xefh?\xc9\r\x9d\xb2*\x8fI\xf4\x8b\x8aA\xa5\"8m\x9b\x95\xc0\x00\x00\u07d4Q5\xfb\x87W`\f\xf4tTbR\xf7M\xc0tm\x06&,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4QF2\xef\xbdd,\x04\xdel\xa3B1]@\u0750\xa2\u06e6\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4QKu\x12\u026e^\xa6<\xbf\x11q[c\xf2\x1e\x18\u0496\xc1\x89lj\xccg\u05f1\xd4\x00\x00\u07d4QS\xa0\xc3\u0211(\x81\xbf\x1c5\x01\xbfd\xb4VI\xe4\x82\"\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94QVQ\xd6\xdbO\xaf\x9e\xcd\x10:\x92\x1b\xbb\xbej\xe9p\xfd\u050a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94Q_0\xbc\x90\xcd\xf4W~\xe4}e\u05c5\xfb\xe2\xe87\u01bc\x8a\x02'\x1b^\x01\x8b\xa0X\x00\x00\u07d4Q`\xeda.\x1bH\xe7??\xc1[\xc42\x1b\x8f#\xb8\xa2K\x89\x1e\x82kB(e\xd8\x00\x00\u07d4Qa\xfdI\xe8G\xf6tU\xf1\u023bz\xbb6\xe9\x85&\r\x03\x89A\rXj \xa4\xc0\x00\x00\u07d4QiT\x02_\xca&\b\xf4}\xa8\x1c!^\xed\xfd\x84J\t\xff\x89\x14\xb5P\xa0\x13\xc78\x00\x00\u07d4Qi\xc6\n\xeeL\xee\u0444\x9a\xb3mfL\xff\x97\x06\x1e\x8e\xa8\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4Q|uC\r\xe4\x01\xc3A\x03&\x86\x11'\x90\xf4mM6\x9e\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4Q|\xd7`\x8e]\r\x83\xa2kq\u007f6\x03\xda\xc2'}\u00e4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x86]\xb1H\x88\x19Q\xf5\x12Qq\x0e\x82\xb9\xbe\r~\xad\xb2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x89\x1b,\xcd\xd2\xf5\xa4K*\x8b\u011a]\x9b\xcadw%\x1c\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4Q\x8c\xef'\xb1\x05\x82\xb6\xd1OiH=\u06a0\xdd<\x87\xbb\\\x89 \x86\xac5\x10R`\x00\x00\u07d4Q\xa6\xd6'\xf6j\x89#\u060d`\x94\xc4qS\x80\xd3\x05|\xb6\x89>s\xd2z5\x94\x1e\x00\x00\u07d4Q\xa8\xc2\x166\x02\xa3.\xe2L\xf4\xaa\x97\xfd\x9e\xa4\x14QiA\x89\x03h\xf7\xe6\xb8g,\x00\x00\u07d4Q\xb4u\x8e\x9e\x14P\xe7\xafBh\xc3\u01f1\xe7\xbdo\\uP\x8965\u026d\xc5\u07a0\x00\x00\u07d4Q\u028b\xd4\xdcdO\xacG\xafgUc\u0540J\r\xa2\x1e\xeb\x89*\xb7\xb2`\xff?\xd0\x00\x00\u07d4Q\xd2K\xc3so\x88\xddc\xb7\" &\x88f0\xb6\ub1cd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\u05cb\x17\x8dp~9n\x87\x10\x96\\OA\xb1\xa1\xd9\x17\x9d\x89\x05\xfe\xe2\"\x04\x1e4\x00\x00\u07d4Q\xe3/\x14\xf4\xca^(|\xda\xc0W\xa7y^\xa9\xe0C\x99S\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Q\xe4?\xe0\xd2\\x(`\xaf\x81\xea\x89\xddy<\x13\xf0\u02f1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4Q\xe7\xb5\\/\x98 \xee\xd78\x846\x1bPf\xa5\x9boE\u0189lk\x93[\x8b\xbd@\x00\x00\xe0\x94Q\xea\x1c\t4\xe3\xd0@\"\ud715\xa0\x87\xa1P\xefp^\x81\x8a\x01Tp\x81\xe7\"M \x00\x00\u07d4Q\xee\f\xca;\xcb\x10\xcd>\x987\"\xce\xd8I=\x92l\bf\x8965f3\xeb\xd8\xea\x00\x00\xe0\x94Q\xf4f:\xb4O\xf7\x93E\xf4'\xa0\xf6\xf8\xa6\u0225?\xf24\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Q\xf5^\xf4~dV\xa4\x18\xab2\xb9\"\x1e\xd2}\xbaf\b\xee\x89\u3bb5sr@\xa0\x00\x00\xe0\x94Q\xf9\xc42\xa4\xe5\x9a\xc8b\x82\u05ad\xabL.\xb8\x91\x91`\xeb\x8ap;[\x89\u00e6\xe7@\x00\x00\u07d4R\x0ff\xa0\xe2e\u007f\xf0\xacA\x95\xf2\xf0d\xcf/\xa4\xb2BP\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4R\x10#T\xa6\xac\xa9]\x8a.\x86\xd5\u07bd\xa6\xdei4`v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\x13\xf4Y\xe0x\xad:\xb9Z\t #\x9f\xcf\x163\xdc\x04\u0289\x8c\xf2\x18|*\xfb\x18\x80\x00\u07d4R\x15\x18;\x8f\x80\xa9\xbc\x03\xd2l\xe9\x12\a\x83*\r9\xe6 \x8965\u026d\xc5\u07a0\x00\x00\xe0\x94R!Cx\xb5@\x04\x05j|\xc0\x8c\x89\x13'y\x8a\u01b2H\x8a\x037\xfe_\xea\xf2\u0440\x00\x00\xe0\x94R##\xaa\xd7\x1d\xbc\x96\xd8Z\xf9\x0f\bK\x99\xc3\xf0\x9d\ucdca\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4R>\x14\r\xc8\x11\xb1\x86\xde\xe5\xd6\u020b\xf6\x8e\x90\xb8\xe0\x96\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R?mdi\x0f\xda\u0354(SY\x1b\xb0\xff \xd3em\x95\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4RO\xb2\x10R,^#\xbbg\u07ff\x8c&\xaaam\xa4\x99U\x8965b\xa6m4#\x80\x00\u07d4RU\xdci\x15ZE\xb9p\xc6\x04\xd3\x00G\xe2\xf50i\x0e\u007f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4R`\xdcQ\xee\a\xbd\u06ab\xab\xb9\xeetK9<\u007fG\x93\xa6\x89\x01\xd8f_\xa5\xfaL\x00\x00\u07d4Rg\xf4\xd4\x12\x92\xf3p\x86<\x90\u05d3)i\x03\x846%\u01c9K\xe4\xe7&{j\xe0\x00\x00\u07d4Rk\xb53\xb7n \xc8\xee\x1e\xbf\x12?\x1e\x9f\xf4\x14\x8e@\xbe\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4Rl\xb0\x9c\u3b63g.\xec\x1d\xebF [\xe8\x9aKV>\x89\x85\xcaa[\xf9\xc0\x10\x00\x00\u07d4Rs\x8c\x90\xd8`\xe0L\xb1/I\x8d\x96\xfd\xb5\xbf6\xfc4\x0e\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4Rz\x8c\xa1&\x863\xa6\xc99\xc5\xde\x1b\x92\x9a\ue4ae\xac\x8d\x890\xca\x02O\x98{\x90\x00\x00\u07d4R\x81\x01\xceF\xb7 \xa2!M\u036ef\x18\xa51w\xff\xa3w\x89\x1b\x96\x12\xb9\xdc\x01\xae\x00\x00\xe0\x94R\x81s4s\xe0\r\x87\xf1\x1e\x99U\u5275\x9fJ\u008ez\x8a\x8b\xd6/\xf4\xee\xc5Y \x00\x00\u07d4R\x98\xab\x18*\x195\x9f\xfc\xec\xaf\xd7\u0475\xfa!-\xed\xe6\u0749\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4R\x9a\xa0\x02\u0196*:\x85E\x02\u007f\u0630_\"\xb5\xbf\x95d\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4R\x9e\x82O\xa0rX+@2h:\xc7\xee\xcc\x1c\x04\xb4\xca\xc1\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94R\xa5\xe4\xdeC\x93\xee\xcc\xf0X\x1a\xc1\x1bR\u0183\xc7n\xa1]\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4R\xb4%|\xf4\x1bn(\x87\x8dP\xd5{\x99\x91O\xfa\x89\x87:\x89\xd5\r\u026a,Aw\x00\x00\u07d4R\xb8\xa9Y&4\xf70\v|\\Y\xa34[\x83_\x01\xb9\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\xbd\u066fYx\x85\v\xc2A\x10q\x8b7#u\x9bC~Y\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4R\xcd @;\xa7\xed\xa6\xbc0z=c\xb5\x91\x1b\x81|\x12c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794R\u04c0Q\x1d\xf1\x9d^\u0080{\xbc\xb6vX\x1bg\xfd7\xa3\x88\xb9\xf6]\x00\xf6<\x00\x00\xe0\x94R\xe1s\x13P\xf9\x83\xcc,A\x89\x84/\xde\x06\x13\xfa\xd5\f\xe1\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\u07d4R\xe4g\x832\x9av\x93\x01\xb1u\x00\x9d4gh\xf4\xc8~\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\xf0X\xd4aG\xe9\x00m)\xbf,\t0J\xd1\xcd\xddn\x15\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4R\xf1T#2<$\xf1\x9a\xe2\xabg7\x17\"\x9d?t}\x9b\x897\xa04\xcb\xe8\xe3\xf3\x80\x00\u07d4R\xf8\xb5\t\xfe\xe1\xa8t\xabo\x9d\x876\u007f\xbe\xaf\x15\xac\x13\u007f\x8965\u026d\xc5\u07a0\x00\x00\u07d4R\xfbF\xac]\x00\xc3Q\x8b,:\x1c\x17}D/\x81eU_\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4S\x00w\xc9\xf7\xb9\a\xff\x9c\xec\fw\xa4\x1ap\xe9\x02\x9a\xddJ\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\x03\x19\xdb\n\x8f\x93\xe5\xbb}M\xbfH\x161O\xbe\xd86\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\x04}\u022c\x90\x83\xd9\x06r\xe8\xb3G<\x10\f\xcd'\x83#\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4S\va\xe4/9Bm$\b\xd4\bR\xb9\xe3J\xb5\xeb\xeb\u0149\x0e~\xeb\xa3A\vt\x00\x00\u07d4S\x0f\xfa\u00fc4\x12\xe2\xec\x0e\xa4{y\x81\xc7p\xf5\xbb/5\x89\a?u\u0460\x85\xba\x00\x00\u07d4S\x17\xec\xb0#\x05,\xa7\xf5e+\xe2\xfa\x85L\xfeEc\xdfM\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4S\x19M\x8a\xfa>\x885\x02v~\xdb\xc3\x05\x86\xaf3\xb1\x14\u04c9lk\x93[\x8b\xbd@\x00\x00\u07d4S*}\xa0\xa5\xadt\aF\x8d;\xe8\xe0~i\xc7\xddd\xe8a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4S-2\xb0\x0f0[\xcc$\xdc\xefV\x81}b/4\xfb,$\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4S4DX@\x82\xeb\xa6T\xe1\xad0\xe1Is\\o{\xa9\"\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4S8\xefp\xea\xc9\u075a\xf5\xa0P;^\xfa\xd1\x03\x9eg\xe7%\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94S9oJ&\u00b4`D\x960lTB\xe7\xfc\xba'.6\x8a\x04?/\b\xd4\x0eZ\xfc\x00\x00\xe0\x94S:s\xa4\xa2\"\x8e\xee\x05\xc4\xff\xd7\x18\xbb\xf3\xf9\xc1\xb1)\xa7\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4S<\x06\x92\x8f\x19\u0429V\xcc(\x86k\xf6\xc8\xd8\xf4\x19\x1a\x94\x89\x0f\xd8\xc1C8\xe60\x00\x00\u07d4S@e6\x1c\xb8T\xfa\xc4+\xfb\\\x9f\xcd\xe0`J\xc9\x19\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4SC\u007f\xec\xf3J\xb9\xd45\xf4\u07b8\xca\x18\x15\x19\xe2Y 5\x89\n1\x06+\xee\xedp\x00\x00\u07d4SR\x01\xa0\xa1\xd74\"\x80\x1fU\xde\xd4\u07ee\xe4\xfb\xaan;\x89\x02&!\x1fy\x15B\x80\x00\xe0\x94S`\x81\x05\xceK\x9e\x11\xf8k\xf4\x97\xff\xca;x\x96{_\x96\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4SnM\x80)\xb7?Uy\u0723>p\xb2N\xba\x89\xe1\x1d~\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Sp\rS%MC\x0f\"x\x1aJv\xa4c\x93;]k\b\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94S\u007f\x9dM1\xefp\x83\x9d\x84\xb0\xd9\u0377+\x9a\xfe\xdb\xdf5\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\xe0\x94S\x81D\x85\x03\xc0\xc7\x02T+\x1d\xe7\xcc_\xb5\xf6\xab\x1c\xf6\xa5\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94S\x94.yI\xd6x\x8b\xb7\x80\xa7\xe8\xa0y'\x81\xb1aK\x84\x8a\x03]\xebFhO\x10\xc8\x00\x00\u07d4S\x95\xa4E]\x95\xd1x\xb4S*\xa4r[\x19?\xfeQ)a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94S\x98\x9e\xd30V?\xd5}\xfe\u027d4<7`\xb0y\x93\x90\x8a\x01P\x89N\x84\x9b9\x00\x00\x00\u07d4S\xa2Dg(\x95H\x0fJ+\x1c\xdf}\xa5\xe5\xa2B\xecM\xbc\x8965\u026d\xc5\u07a0\x00\x00\u07d4S\xa7\x14\xf9\x9f\xa0\x0f\xefu\x8e#\xa2\xe7F2m\xad$|\xa7\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4S\xaf2\xc2/\uf640?\x17\x8c\xf9\v\x80/\xb5q\xc6\x1c\xb9\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4S\xc0\xbb\u007f\u020e\xa4\"\xd2\xef~T\x0e-\x8f(\xb1\xbb\x81\x83\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94S\xc5\xfe\x01\x19\xe1\xe8Hd\f\xee0\xad\ua594\x0f*]\x8b\x8a\x04\x9a\xda_\xa8\xc1\f\x88\x00\x00\u07d4S\xc9\xec\xa4\ts\xf6;\xb5\x92{\xe0\xbcj\x8a\x8b\xe1\x95\x1ft\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\u0388\xe6lZ\xf2\U0009bf4fY*V\xa3\xd1_ l2\x89\a\xa2\x8c1\xcc6\x04\x00\x00\u07d4S\xce\xc6\u0200\x92\xf7V\xef\xe5o}\xb1\x12(\xa2\xdbE\xb1\"\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4S\xe3[\x12#\x1f\x19\xc3\xfdwL\x88\xfe\xc8\xcb\xee\xdf\x14\b\xb2\x89\x1b\xc1mgN\xc8\x00\x00\x00\u07d4S\xe4\xd9im\xcb?M{?p\u072aN\xec\xb7\x17\x82\xff\\\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4S\xfa\xf1e\xbe\x03\x1e\xc1\x830\xd9\xfc\xe5\xbd\x12\x81\xa1\xaf\b\u06c9\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4T\n\x18\x19\xbd|5\x86\x1ey\x18\x04\xe5\xfb\xb3\xbc\x97\u026b\xb1\x89N\xd7\xda\xc6B0 \x00\x00\xe0\x94T\f\a(\x02\x01N\xf0\xd5a4Z\xecH\x1e\x8e\x11\xcb5p\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94T\f\xf2=\xd9\\MU\x8a'\x9dw\x8d+75\xb3\x16A\x91\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4T\x10`\xfcX\xc7P\xc4\x05\x12\xf83i\xc0\xa63@\xc1\"\xb6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4T\x13\xc9\u007f\xfaJn*{\xba\x89a\u071f\u03850\xa7\x87\u05c965\u026d\xc5\u07a0\x00\x00\u07d4T\x1d\xb2\n\x80\xcf;\x17\xf1b\x1f\x1b?\xf7\x9b\x88/P\xde\xf3\x8965\u026d\xc5\u07a0\x00\x00\u07d4T.\x80\x96\xba\xfb\x88\x16&\x06\x00.\x8c\x8a>\u0458\x14\xae\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T1\v:\xa8\x87\x03\xa7%\u07e5}\xe6\xe6F\x93Qd\x80,\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4T1\xb1\u0447Q\xb9\x8f\xc9\u220a\xc7u\x9f\x155\xa2\xdbG\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T1\xcaB~ae\xa6D\xba\xe3&\xbd\tu\n\x17\x8ce\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T5\xc6\xc1y3\x17\xd3,\xe1;\xbaLO\xfe\xb9s\xb7\x8a\u0709\r\x8ek\x1c\x12\x85\xef\x00\x00\xe0\x94T6)\xc9\\\xde\xf4(\xad7\xd4S\u02958\xa9\xf9\t\x00\xac\x8a\t(\x96R\x9b\xad\u0708\x00\x00\u07d4T9\x1bM\x17mGl\xea\x16N_\xb55\u0197\x00\xcb%5\x89\x05l\xd5_\xc6M\xfe\x00\x00\xe0\x94T:\x8c\x0e\xfb\x8b\xcd\x15\xc5C\u29a4\xf8\aYv1\xad\xef\x8a\x01?\x80\xe7\xe1O-D\x00\x00\u07d4T?\x8cgN$b\xd8\xd5\u06a0\xe8\x01\x95\xa8p\x8e\x11\xa2\x9e\x89\x03wX\x83;:z\x00\x00\xe0\x94TK[5\x1d\x1b\xc8.\x92\x97C\x99H\xcfHa\xda\u026e\x11\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4TM\xdaB\x1d\xc1\xebs\xbb$\xe3\xe5j$\x80\x13\xb8|\x0fD\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4TW\\1\x14u\x1e\x14o\xfe\u00c7nE\xf2\x0e\xe8AJ\u07ba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4T\xb4B\x9b\x18/\x03w\xbe~bi9\xc5\xdbd@\xf7]z\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4T\xbc\xb8\xe7\xf7<\xda=s\xf4\u04cb-\bG\xe6\x00\xba\r\xf8\x89:pAX\x82\xdf\x18\x00\x00\u07d4T\xc9>\x03\xa9\xb2\xe8\xe4\xc3g(5\xa9\xeev\xf9a[\xc1N\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4T\u0388'YV\xde\xf5\xf9E\x8e;\x95\xde\xca\xcdH@!\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T\xdb^\x06\xb4\x81]1\xcbV\xa8q\x9b\xa3:\xf2\xd7>rR\x89$R\x1e*0\x17\xb8\x00\x00\xe0\x94T\xe0\x12\x83\u030b8E8\xdddgp\xb3W\xc9`\xd6\xca\u034a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4T\xecs\x00\xb8\x1a\xc8C3\xed\x1b\x03<\xd5\u05e39r\xe24\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4T\xfe\xbc\xce \xfez\x90\x98\xa7U\xbd\x90\x98\x86\x02\xa4\x8c\b\x9e\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4U\n\xad\xae\x12!\xb0z\xfe\xa3\x9f\xba.\xd6.\x05\u5df5\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4U\f0o\x81\xef]\x95\x80\xc0l\xb1\xab \x1b\x95\xc7H\xa6\x91\x89$\x17\xd4\xc4p\xbf\x14\x00\x00\xe0\x94U\x19\x99\xdd\xd2\x05V3'\xb9\xb50xZ\xcf\xf9\xbcs\xa4\xba\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4U\x1ew\x84w\x8e\xf8\xe0H\xe4\x95\xdfI\xf2aO\x84\xa4\xf1\u0709 \x86\xac5\x10R`\x00\x00\xe0\x94U)\x83\na\xc1\xf1<\x19~U\v\xed\xdf\u05bd\x19\\\x9d\x02\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4U)\x87\xf0e\x1b\x91[.\x1eS(\xc1!\x96\rK\xddj\xf4\x89a\t=|,m8\x00\x00\u07d4U;k\x1cW\x05\x0e\x88\xcf\f1\x06{\x8dL\xd1\xff\x80\xcb\t\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4U?7\xd9$fU\x0e\x9f\xd7u\xaet6-\xf00\x17\x912\x89lk\x93[\x8b\xbd@\x00\x00\u07d4UC6\xeeN\xa1U\xf9\xf2O\x87\xbc\xa9\xcar\xe2S\xe1,\u0489\x05k\xc7^-c\x10\x00\x00\u0794UC\xddm\x16\x9e\xec\x8a!;\xbfz\x8a\xf9\xff\xd1]O\xf7Y\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4UG\xfd\xb4\xae\x11\x95>\x01)+x\a\xfa\x92#\xd0\xe4`j\x89\x05]\x11}\xcb\x1d&\x00\x00\u07d4UR\xf4\xb3\xed>\x1d\xa7\x9a/x\xbb\x13\xe8\xaeZh\xa9\xdf;\x8965\u026d\xc5\u07a0\x00\x00\u07d4U\\\xa9\xf0\\\xc14\xabT\xae\x9b\xea\x1c?\xf8z\xa8Q\x98\u0289\x05k\xc7^-c\x10\x00\x00\xe0\x94U]\x8d<\xe1y\x8a\u0290'T\xf1d\xb8\xbe*\x022\x9cl\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4U]\xf1\x93\x90\xc1m\x01)\x87r\xba\xe8\xbc:\x11R\x19\x9c\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4U^\xbe\x84\u06a4+\xa2V\xeax\x91\x05\xce\u0136\x93\xf1/\x18\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94U\u007f^e\xe0\xda3\x99\x82\x19\xadN\x99W\x05E\xb2\xa9\xd5\x11\x8a\x02U\x9c\xbb\x98XB@\x00\x00\u07d4U\x83` h\x83\xdd\x1bmJYc\x9eV)\xd0\xf0\xc6u\u0409lk\x93[\x8b\xbd@\x00\x00\u07d4U\x84B0P\xe3\xc2\x05\x1f\v\xbd\x8fD\xbdm\xbc'\xec\xb6,\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4U\x85)CI)p\xf8\xd6)\xa1Sf\xcd\xda\x06\xa9OE\x13\x89lk\x93[\x8b\xbd@\x00\x00\u0794U\x86d\x86\xec\x16\x8fy\xdb\xe0\u1af1\x88d\u0649\x91\xae,\x88\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4U\x8cTd\x9a\x8an\x94r+\xd6\xd2\x1d\x14qOqx\x054\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\x91\x940O\x14\xb1\xb9:\xfeDO\x06$\xe0S\xc2:\x00\t\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4U\x93\xc9\u0536ds\x0f\xd9<\xa6\x01Q\xc2\\.\xae\xd9<;\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4U\x97\x06\xc32\xd2\ay\xc4_\x8am\x04ji\x91Y\xb7I!\x89\x14\x9bD.\x85\xa3\u03c0\x00\u07d4U\x98\xb3\xa7\x9aH\xf3+\x1f_\xc9\x15\xb8{d]\x80]\x1a\xfe\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4U\xa3\xdfW\xb7\xaa\xec\x16\xa1b\xfdS\x16\xf3[\xec\b(!\u03c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4U\xa4\xca\xc0\u02cbX-\x9f\xef8\xc5\xc9\xff\xf9\xbdS\t=\x1f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4U\xa6\x1b\x10\x94\x80\xb5\xb2\xc4\xfc\xfd\xef\x92\xd9\x05\x84\x16\f\r5\x89\x02lVM+S\xf6\x00\x00\u07d4U\xaa]1>\xbb\bM\xa0\xe7\x80\x10\x91\u2792\xc5\xde\u00ea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\xab\x99\xb0\xe0\xe5]{\xb8t\xb7\xcf\xe84\xdec\x1c\x97\xec#\x897\xe9\x8c\xe3h\x99\xe4\x00\x00\u07d4U\xaf\t/\x94\xbajy\x91\x8b\f\xf99\xea\xb3\xf0\x1b?Q\u01c9\b \xd5\xe3\x95v\x12\x00\x00\u07d4U\xc5dfAf\xa1\xed\xf3\x91>\x01i\xf1\xcdE\x1f\xdb]\f\x89\x82\x17\xeaIP\x8el\x00\x00\xe0\x94U\xcaj\xbey\xea$\x97\xf4o\u06f804`\x10\xfeF\x9c\xbe\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4U\xca\xffK\xba\x04\xd2 \u0265\xd2\x01\x86r\xec\x85\xe3\x1e\xf8>\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\xd0W\xbc\xc0K\xd0\xf4\xaf\x96BQ:\xa5\t\v\xb3\xff\x93\xfe\x89;\xfeE,\x8e\xddL\x00\x00\u07d4U\xd4.\xb4\x95\xbfF\xa64\x99{_.\xa3b\x81I\x18\u2c09\x05\xc0\xd2e\xb5\xb2\xa8\x00\x00\u07d4U\u069d\xcd\xcaa\xcb\xfe\x1f\x13<{\xce\xfc\x86{\x9c\x81\"\xf9\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4U\xe2 \x87bb\xc2\x18\xafOVxG\x98\xc7\xe5]\xa0\x9e\x91\x89\a=\x99\xc1VE\xd3\x00\x00\u07d4U\xfd\b\u0440d\xbd ,\x0e\xc3\xd2\xcc\xe0\xce\v\x9d\x16\x9cM\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4V\x00s\nU\xf6\xb2\x0e\xbd$\x81\x1f\xaa=\xe9m\x16b\xab\xab\x89e\xea=\xb7UF`\x00\x00\u07d4V\x03$\x1e\xb8\xf0\x8fr\x1e4\x8c\x9d\x9a\xd9/H\u342a$\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4V\x056yJ\x9e+\x00I\xd1\x023\xc4\x1a\xdc_A\x8a&J\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\aY\x00Y\xa9\xfe\xc1\x88\x11I\xa4K6\x94\x9a\xef\x85\xd5`\x89lk\x93[\x8b\xbd@\x00\x00\u07d4V\v\xec\xdfR\xb7\x1f=\x88'\xd9'a\x0f\x1a\x98\x0f3qo\x89\x17GMp_V\u0400\x00\xe0\x94V\r\xa3~\x95m\x86/\x81\xa7_\u0540\xa7\x13\\\x1b$cR\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94V\x0f\xc0\x8d\a\x9f\x04~\xd8\xd7\xdfuU\x1a\xa55\x01\xf5p\x13\x8a\x01\x9b\xff/\xf5yh\xc0\x00\x00\u07d4V\x1b\xe9)\x9b>k>c\xb7\x9b\t\x16\x9d\x1a\x94\x8a\xe6\xdb\x01\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94V \xe3\xedy-/\x185\xfe_UA}Q\x11F\fj\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4V \xf4m\x14Q\xc25=bC\xa5\u0534'\x13\v\xe2\xd4\a\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94V!\x05\xe8+\t\x975\xdeI\xf6&\x92\u0307\xcd8\xa8\xed\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94V*\x8d\u02fe\xee\xf7\xb3`h]'0;\u059e\tJ\xcc\xf6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4V+\xce\u04ca\xb2\xabl\b\x0f;\x05A\xb8Enp\x82K?\x89\"\xca5\x87\xcfN\xb0\x00\x00\xe0\x94V+\xe9Z\xba\x17\xc57\x1f\u2e82\x87\x99\xb1\xf5]!w\u058a\b\x16\xd3~\x87\xb9\xd1\xe0\x00\x00\u07d4V/\x16\u05da\xbf\xce\u00d4>4\xb2\x0f\x05\xf9{\xdf\u0366\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4V7=\xaa\xb4c\x16\xfd~\x15v\xc6\x1ej\xff\xcbeY\xdd\u05c9\v\xacq]\x14l\x9e\x00\x00\u07d4V9v8\xbb<\xeb\xf1\xf6 byK^\xb9B\xf9\x16\x17\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4V:\x03\xab\x9cV\xb6\x00\xf6\xd2[f\f!\xe1c5Qzu\x8965\u026d\xc5\u07a0\x00\x00\u07d4V<\xb8\x80<\x1d2\xa2['\xb6A\x14\x85+\xd0M\x9c \u0349\v\x14\x9e\xad\n\xd9\xd8\x00\x00\u07d4VXc\x91\x04\fW\xee\xc6\xf5\xaf\xfd\x8c\u052b\xde\x10\xb5\n\u0309\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Vl\x10\xd68\u8e0bG\xd6\xe6\xa4\x14Iz\xfd\xd0\x06\x00\u0509\x05k9Bc\xa4\f\x00\x00\u07d4Vl(\xe3L8\b\xd9vo\xe8B\x1e\xbfO+\x1cO}w\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4V\x8d\xf3\x18Vi\x9b\xb5\xac\xfc\x1f\xe1\u0580\u07d9`\xcaCY\x89J\xcfUR\xf3\xb2I\x80\x00\u07d4V\x91\xdd/gE\xf2\x0e\"\xd2\xe1\u0479U\xaa)\x03\xd6VV\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4V\xa1\xd6\r@\xf5\u007f0\x8e\xeb\xf0\x87\xde\xe3\xb3\u007f\x1e|,\xba\x89>\u072e\xc8-\x06\xf8\x00\x00\u07d4V\xac \xd6;\xd8\x03Y\\\xec\x03m\xa7\xed\x1d\xc6n\n\x9e\a\x89\x03w*S\xcc\xdce\x80\x00\u07d4V\xb6\xc2=\xd2\uc434r\x8f;\xb2\xe7d\xc3\xc5\f\x85\xf1D\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\xdf\x05\xba\xd4l?\x00\xaeGn\xcf\x01{\xb8\xc8w8?\xf1\x89\n\xb1]\xaa\xefp@\x00\x00\u07d4V\xee\x19\u007fK\xbf\x9f\x1b\x06b\xe4\x1c+\xbd\x9a\xa1\xf7\x99\xe8F\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\xf4\x93\xa3\xd1\b\xaa\xa2\u044d\x98\x92/\x8e\xfe\x16b\u03f7=\x89m\x81!\xa1\x94\xd1\x10\x00\x00\u07d4V\xfc\x1a{\xad@G#|\xe1\x16\x14b\x96#\x8e\a\x8f\x93\xad\x89\t\xa6?\b\xeac\x88\x00\x00\u07d4V\xfe\xbf\x9e\x10\x03\xaf\x15\xb1\xbdI\a\xec\b\x9aJ\x1b\x91\xd2h\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4W\x17\u0313\x01Q\x1dJ\x81\xb9\xf5\x83\x14\x8b\xee\xd3\xd3\u0303\t\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4W\x17\xf2\xd8\xf1\x8f\xfc\xc0\xe5\xfe$}:B\x19\x03|:d\x9c\x89\u063beI\xb0+\xb8\x00\x00\u07d4W\x19P\xea,\x90\xc1B}\x93\x9da\xb4\xf2\xdeL\xf1\u03ff\xb0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4W\x19\xf4\x9br\r\xa6\x88V\xf4\xb9\xe7\b\xf2VE\xbd\xbcKA\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4W*\xc1\xab\xa0\xde#\xaeA\xa7\xca\xe1\xdc\bB\u062b\xfc\x10;\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94W-\xd8\xcd?\xe3\x99\xd1\xd0\xec(\x121\xb7\xce\xfc \xb9\u4eca\x023\xc8\xfeBp>\x80\x00\x00\xe0\x94WI!\x83\x8c\xc7}l\x98\xb1}\x90::\xe0\xee\r\xa9[\u040a\vS(\x17\x8a\xd0\xf2\xa0\x00\x00\u07d4WJ\xd95S\x90\u421e\xf4*\xcd\x13\x8b*'\xe7\x8c\x00\xae\x89Tg\xb72\xa9\x134\x00\x00\u07d4WM\xe1\xb3\xf3\x8d\x91XF\xae7\x18VJZ\xda \xc2\xf3\xed\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94W\\\x00\u0081\x82\x10\u0085U\xa0\xff)\x01\x02\x89\xd3\xf8#\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94Ws\xb6\x02g!\xa1\xdd\x04\xb7\x82\x8c\xd6+Y\x1b\xfb4SL\x8a\x05\xb7\xacES\xdez\xe0\x00\x00\xe0\x94WwD\x1c\x83\xe0?\v\xe8\xdd4\v\xdechP\x84|b\v\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Wx\xff\u071b\x94\u0165\x9e\"N\xb9e\xb6\u0790\xf2\"\xd1p\x89\x12-\u007f\xf3f\x03\xfc\x00\x00\u07d4Wz\xee\xe8\u053c\b\xfc\x97\xab\x15n\xd5\u007f\xb9p\x92Sf\xbe\x89\x12\r\xf1\x14rX\xbf\x00\x00\u07d4W{-\a\xe9\xcfRJ\x18\u04c9\x15Vak\x96\x06g\x00\x00\u07d4W\xd5\xfd\x0e=0I3\x0f\xfc\xdc\xd0 Ei\x17e{\xa2\u0689k\xf2\x01\x95\xf5T\xd4\x00\x00\u07d4W\u0754q\xcb\xfa&'\t\xf5\U00106f37t\xc5\xf5'\xb8\xf8\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4W\xdf#\xbe\xbd\xc6^\xb7_\ub732\xfa\xd1\xc0si++\xaf\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4X\x00\u03410\x83\x9e\x94I]-\x84\x15\xa8\xea,\x90\xe0\xc5\u02c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94X\x03\xe6\x8b4\xda\x12\x1a\xef\b\xb6\x02\xba\u06ef\xb4\xd1$\x81\u028a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\xe0\x94X\x16\xc2hww\xb6\xd7\u04a2C-Y\xa4\x1f\xa0Y\xe3\xa4\x06\x8a\x1cO\xe4:\xdb\n^\x90\x00\x00\u07d4X\x1a:\xf2\x97\xef\xa4Cj)\xaf\x00r\x92\x9a\xbf\x98&\xf5\x8b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\x1b\x9f\xd6\xea\xe3r\xf3P\x1fB\xeb\x96\x19\xee\xc8 \xb7\x8a\x84\x8a\x04+\xe2\xc0\f\xa5;\x8d\x80\x00\u07d4X\x1b\xdf\x1b\xb2v\xdb\u0746\xae\xdc\xdb9z\x01\xef\xc0\xe0\f[\x8965\u026d\xc5\u07a0\x00\x00\u07d4X\x1f4\xb5#\xe5\xb4\x1c\t\xc8|)\x8e)\x9c\xbc\x0e)\xd0f\x89=X3\xaa\xfd9u\x80\x00\xe0\x94X$\xa7\xe2(8'q40\x8c_KP\u06b6^C\xbb1\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4X+pf\x9c\x97\xaa\xb7\u0581H\xd8\xd4\xe9\x04\x11\xe2\x81\rV\x8965f3\xeb\xd8\xea\x00\x00\u07d4X.|\xc4o\x1d{Nn\x9d\x95\x86\x8b\xfd7\x05s\x17\x8fL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4X>\x83\xbaU\xe6~\x13\xe0\xe7o\x83\x92\xd8s\xcd!\xfb\xf7\x98\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Xi\xfb\x86}q\xf18\u007f\x86;i\x8d\t\xfd\xfb\x87\u011b\\\x89\u01bb\xf8X\xb3\x16\b\x00\x00\u07d4X}hI\xb1h\xf6\xc33+z\xba\xe7\xeblB\xc3\u007fH\xbf\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4X\x87\xdcj3\xdf\xedZ\xc1\xed\xef\xe3^\xf9\x1a!b1\xac\x96\x89\r\x8drkqw\xa8\x00\x00\xe0\x94X\x8e\u0650\xa2\xaf\xf4J\x94\x10]X\xc3\x05%w5\xc8h\xac\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4X\xae-\xdc_L\x8a\u0697\xe0l\x00\x86\x17\x17g\xc4#\xf5\u05c9WG=\x05\u06ba\xe8\x00\x00\u07d4X\xae\xd6gJ\xff\xd9\xf6B3'*W\x8d\xd98k\x99\xc2c\x89\xb8Pz\x82\a( \x00\x00\xe0\x94X\xb8\b\xa6[Q\xe63\x89i\xaf\xb9^\xc7\a5\xe4Q\xd5&\x8a\bxK\xc1\xb9\x83z8\x00\x00\u07d4X\xb8\xae\x8fc\xef5\xed\ab\xf0\xb6#=J\xc1Nd\xb6M\x89lk\x93[\x8b\xbd@\x00\x00\u07d4X\xba\x15ie\x0e[\xbb\xb2\x1d5\xd3\xe1u\xc0\u05b0\xc6Q\xa9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4X\xc5U\xbc)<\xdb\x16\xc66.\xd9z\xe9U\v\x92\xea\x18\x0e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4X\xc6P\xce\xd4\v\xb6VA\xb8\xe8\xa9$\xa09\xde\xf4hT\u07c9\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4X\xc9\aT\xd2\xf2\n\x1c\xb1\xdd3\x06%\xe0KE\xfaa\x9d\\\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\xe2\xf1\x12#\xfc\x827\xf6\x9d\x99\xc6(\x9c\x14\x8c\x06\x04\xf7B\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4X\xe5T\xaf=\x87b\x96 \xdaa\xd58\xc7\xf5\xb4\xb5LJ\xfe\x89FP\x9diE4r\x80\x00\u07d4X\xe5\xc9\xe3D\xc8\x06e\r\xac\xfc\x90M3\xed\xbaQ\a\xb0\u0789\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4X\xe6a\u043as\xd6\xcf$\t\x9aUb\xb8\b\xf7\xb3g;h\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\xf0[&%`P<\xa7a\xc6\x18\x90\xa4\x03_Lsr\x80\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4X\xfb\x94sd\xe7iWe6\x1e\xbb\x1e\x80\x1f\xfb\x8b\x95\xe6\u0409\n\u05ce\xbcZ\xc6 \x00\x00\u07d4Y\x01\x81\xd4E\x00{\u0407Z\xaf\x06\x1c\x8dQ\x159\x00\x83j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y\x02\xe4J\xf7i\xa8rF\xa2\x1e\a\x9c\b\xbf6\xb0n\xfe\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4Y\n\xcb\xda7)\f\r>\xc8O\xc2\x00\rv\x97\xf9\xa4\xb1]\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94Y\f\xcbY\x11\xcfx\xf6\xf6\"\xf55\xc4t7_J\x12\xcf\u03ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y\x10\x10m\xeb\u0491\xa1\u0340\xb0\xfb\xbb\x8d\x8d\x9e\x93\xa7\xcc\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y\x16\x17I\xfe\xdc\xf1\xc7!\xf2 -\x13\xad\xe2\xab\xcfF\v=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Y\x1b\xef1q\xd1\u0155w\x17\xa4\xe9\x8d\x17\xeb\x14,!NV\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y <\xc3u\x99\xb6H1*|\xc9\xe0m\xac\xb5\x89\xa9\xaej\x89\b\x0fyq\xb6@\x0e\x80\x00\u07d4Y&\x81q\xb83\xe0\xaa\x13\xc5KR\xcc\xc0B.O\xa0:\ub262\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94Y'w&\x1e;\xd8R\u010e\u0295\xb3\xa4L[\u007f-B,\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y0Dg\x0f\xae\xff\x00\xa5[Z\xe0Q\xeb{\xe8p\xb1\x16\x94\x89\a?u\u0460\x85\xba\x00\x00\xe0\x94Y;E\xa1\x86J\xc5\xc7\xe8\xf0\u02ae\xba\r\x87<\xd5\xd1\x13\xb2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Y_^\xdajV\xf1N%\xe0\xc6\xf3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Z\x1a3ib\xd6\xe0\xc601\u0303\u01a5\u01a6\xf4G\x8e\u02c965\u026d\xc5\u07a0\x00\x00\u07d4Z\x1d--\x1dR\x03\x04\xb6 \x88IW\x047\xeb0\x91\xbb\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Z&s1\xfa\xcb&-\xaa\xec\xd9\xddc\xa9p\f_RY\u07c9\x05k\xc7^-c\x10\x00\x00\xe0\x94Z(WU9\x1e\x91NX\x02_\xaaH\xcch_O\xd4\xf5\xb8\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4Z)\x16\xb8\xd2\xe8\xcc\x12\xe2\a\xabFMC>#p\xd8#\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4Z+\x1c\x85:\xeb(\xc4U9\xafv\xa0\n\xc2\u0628$(\x96\x89\x01Z\xf1\u05cbX\xc4\x00\x00\u07d4Z-\xaa\xb2\\1\xa6\x1a\x92\xa4\xc8,\x99%\xa1\xd2\xefXX^\x89\f8\r\xa9\u01d5\f\x00\x00\u07d4Z0\xfe\xac7\xac\x9fr\u05f4\xaf\x0f+\xc79R\xc7O\xd5\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4ZTh\xfa\\\xa2&\xc7S.\xcf\x06\xe1\xbc\x1cE\"]~\u0249g\x8a\x93 b\xe4\x18\x00\x00\u07d4ZVR\x857JI\xee\xddPL\x95}Q\bt\xd0\x04U\xbc\x89\x05k\xc7^-c\x10\x00\x00\u07d4Z^\xe8\xe9\xbb\x0e\x8a\xb2\xfe\xcbK3\u0494x\xbeP\xbb\xd4K\x89*\x11)\u0413g \x00\x00\xe0\x94Z_\x85\b\xda\x0e\xbe\xbb\x90\xbe\x903\xbdM\x9e'A\x05\xae\x00\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4Z`q\xbc\xeb\xfc\xbaJ\xb5\u007fM\xb9o\u01e6\x8b\xec\xe2\xba[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z`\xc9$\x16(s\xfc~\xa4\xda\u007f\x97.5\x01g7`1\x89\x04\x87\xf2w\xa8\x85y\x80\x00\u07d4Zf\x86\xb0\xf1~\a\xed\xfcY\xb7Y\xc7}[\xef\x16M8y\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4Zp\x10o \xd6?\x87Re\xe4\x8e\r5\xf0\x0e\x17\xd0+\u0249\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794Zt\xbab\xe7\xc8\x1a4t\xe2}\x89O\xed3\xdd$\xad\x95\xfe\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94Zw5\x00}p\xb0hD\u0699\x01\xcd\xfa\xdb\x11\xa2X,/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Z\x82\xf9l\u0537\xe2\xd9=\x10\xf3\x18]\xc8\xf4=Ku\xaai\x89lc?\xba\xb9\x8c\x04\x00\x00\u07d4Z\x87\xf04\xe6\xf6\x8fNt\xff\xe6\fd\x81\x946\x03l\xf7\u05c9\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94Z\x89\x11U\xf5\x0eB\aCt\xc79\xba\xad\xf7\xdf&Q\x15:\x8a\x01\x02\xdao\xd0\xf7:<\x00\x00\u07d4Z\x9c\x8bi\xfcaMiVI\x99\xb0\r\xcbB\xdbg\xf9~\x90\x89\xb9\xe6\x15\xab\xad:w\x80\x00\xe0\x94Z\xaf\x1c1%Jn\x00_\xba\u007fZ\xb0\xecy\xd7\xfc+c\x0e\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4Z\xb1\xa5aSH\x00\x1c|w]\xc7WHf\x9b\x8b\xe4\xde\x14\x89%jr\xfb)\xe6\x9c\x00\x00\xe1\x94Z\xbf\xec%\xf7L\u06047c\x1aw1\x90i2wcV\xf9\x8b\t\xd8<\xc0\u07e1\x11w\xff\x80\x00\u07d4Z\u0090\x8b\x0f9\x8c\r\xf5\xba\xc2\xcb\x13\xcas\x14\xfb\xa8\xfa=\x89\n\xd4\xc81j\v\f\x00\x00\xe0\x94Z\u025a\u05c1j\xe9\x02\x0f\xf8\xad\xf7\x9f\xa9\x86\x9b|\xeaf\x01\x8a\x04ri\x8bA;C \x00\x00\u07d4Z\xd1,^\xd4\xfa\x82~!P\u03e0\u058c\n\xa3{\x17i\xb8\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94Z\xd5\xe4 uV\x13\x88o5\xaaV\xac@>\xeb\xdf\xe4\xb0\u040a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4Z\xdew\xfd\x81\xc2\\\n\xf7\x13\xb1\a\x02v\x8c\x1e\xb2\xf9u\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Z\xe6N\x85;\xa0\xa5\x12\x82\u02cd\xb5.Aa^|\x9fs?\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z\xed\x0el\xfe\x95\xf9\u0580\xc7dr\xa8\x1a+h\n\u007f\x93\xe2\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4Z\xef\x16\xa2&\xddh\a\x1f$\x83\xe1\xdaBY\x83\x19\xf6\x9b,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z\xf4j%\xac\t\xcbsakS\xb1O\xb4/\xf0\xa5\x1c\u0772\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Z\xf7\xc0r\xb2\u016c\xd7\x1cv\xad\xdc\xceS\\\xf7\xf8\xf95\x85\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94Z\xfd\xa9@\\\x8e\x976QEt\u0692\x8d\xe6tV\x01\t\x18\x8a\x01E\xb8\xb0#\x9aF\x92\x00\x00\u07d4[\x06\xd1\xe6\x93\f\x10Ti+y\xe3\xdb\xe6\xec\xceS\x96d \x89\v\"\u007fc\xbe\x81<\x00\x00\u07d4[%\xca\xe8m\xca\xfa*`\xe7r61\xfc_\xa4\x9c\x1a\xd8}\x89\x87\fXQ\x0e\x85 \x00\x00\u07d4[(|~sB\x99\xe7'bo\x93\xfb\x11\x87\xa6\rPW\xfe\x89\x05|\xd94\xa9\x14\xcb\x00\x00\u07d4[)\f\x01\x96|\x81.M\xc4\xc9\v\x17L\x1b@\x15\xba\xe7\x1e\x89\b \xeb4\x8dR\xb9\x00\x00\u07d4[+d\xe9\xc0X\u30a8\xb2\x99\"N\xec\xaa\x16\xe0\x9c\x8d\x92\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\xe0\x94[./\x16\x18U.\xab\r\xb9\x8a\xddUc|)Q\xf1\xfb\x19\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4[0`\x8cg\x8e\x1a\xc4d\xa8\x99L;3\xe5\xcd\xf3Iq\x12\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4[36\x96\xe0L\xca\x16\x92\xe7\x19\x86W\x9c\x92\rk)\x16\xf9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94[C\rw\x96\x96\xa3e?\xc6\x0et\xfb\u02ec\xf6\xb9\u00ba\xf1\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4[Cse\xae:\x9a/\xf9|h\xe6\xf9\nv \x18\x8c}\x19\x89l\x87T\xc8\xf3\f\b\x00\x00\u07d4[I\xaf\xcduDx8\xf6\xe7\xce\u068d!w}O\xc1\xc3\xc0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4[L\f`\xf1\x0e\u0489K\xdbB\xd9\xdd\x1d!\x05\x87\x81\n\r\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4[N\xa1m\xb6\x80\x9b\x03R\u0536\xe8\x1c9\x13\xf7jQ\xbb2\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4[[\xe0\xd8\xc6rv\xba\xab\xd8\xed\xb3\rH\xeaud\v\x8b)\x89,\xb1\xf5_\xb7\xbe\x10\x00\x00\u07d4[]Qp)2\x15b\x11\x1bC\bm\v\x045\x91\x10\x9ap\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\xe0\x94[]\x8c\x8e\xedl\x85\xac!Va\xde\x02fv\x82?\xaa\n\f\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4[mU\xf6q)g@\\e\x91)\xf4\xb1\xde\t\xac\xf2\xcb{\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4[p\u011c\u024b=\xf3\xfb\xe2\xb1Y\u007f\\\x1bcG\xa3\x88\xb7\x894\x95tD\xb8@\xe8\x00\x00\u07d4[sn\xb1\x83Sb\x9b\u0796v\xda\xdd\x16P4\xce^\xcch\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4[u\x9f\xa1\x10\xa3\x1c\x88F\x9fT\xd4K\xa3\x03\xd5}\xd3\xe1\x0f\x89[F\xdd/\x0e\xa3\xb8\x00\x00\u07d4[w\x84\xca\xea\x01y\x9c\xa3\x02'\x82vg\xce |\\\xbcv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4[x\xec\xa2\u007f\xbd\xeao&\xbe\xfb\xa8\x97+)^x\x146K\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94[\x80\v\xfd\x1b>\u0525}\x87Z\xed&\xd4/\x1aw\b\xd7*\x8a\x01Z\x82\xd1\u057b\x88\xe0\x00\x00\u07d4[\x85\xe6\x0e*\xf0TO/\x01\xc6N 2\x90\x0e\xbd8\xa3\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4[\xa2\xc6\xc3]\xfa\xec)h&Y\x19\x04\xd5DFJ\xea\xbd^\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94[\xafmt\x96 \x80>\x83H\xaf7\x10\xe5\xc4\xfb\xf2\x0f\u0214\x8a\x01\x0f@\x02a]\xfe\x90\x00\x00\u07d4[\xc1\xf9U\a\xb1\x01\x86B\xe4\\\xd9\xc0\xe2'3\xb9\xb1\xa3&\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94[\xd25GG\u007fm\t\u05f2\xa0\x05\xc5\xeee\fQ\fV\u05ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4[\xd2J\xac6\x12\xb2\f`\x9e\xb4gy\xbf\x95i\x84\a\xc5|\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4[\u0586-Q}M\xe4U\x9dN\xec\n\x06\xca\xd0^/\x94n\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4[\xe0EQ*\x02n?\x1c\xeb\xfdZ~\xc0\xcf\xc3o-\xc1k\x89\x06\x81U\xa46v\xe0\x00\x00\xe0\x94[\xf9\xf2\"nZ\xea\xcf\x1d\x80\xae\nY\xc6\xe3\x808\xbc\x8d\xb5\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4[\xfa\xfe\x97\xb1\xdd\x1dq+\xe8mA\xdfy\x89SE\x87Z\x87\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\\\x0f.Q7\x8fk\r{\xabas1X\vn9\xad<\xa5\x8a\x02\bj\xc3Q\x05&\x00\x00\x00\u07d4\\)\xf9\xe9\xa5#\xc1\xf8f\x94H\xb5\\H\xcb\xd4|%\xe6\x10\x894F\xa0\xda\xd0L\xb0\x00\x00\xe0\x94\\0\x8b\xacHW\xd3;\xae\xa0t\xf3\x95m6!\xd9\xfa(\xe1\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\\1*V\u01c4\xb1\"\t\x9bvM\x05\x9c!\xec\xe9^\x84\u0289\x05&c\u032b\x1e\x1c\x00\x00\u07d4\\1\x99m\xca\xc0\x15\xf9\xbe\x98[a\x1fF\x870\xef$M\x90\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\\24W\xe1\x87v\x1a\x82v\xe3Y\xb7\xb7\xaf?;n=\xf6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\\<\x1cd[\x91uC\x11;>l\x1c\x05M\xa1\xfet+\x9a\x89+^:\xf1k\x18\x80\x00\x00\u0794\\=\x19D\x1d\x19l\xb4Cf \xfc\xad\u007f\xbby\xb2\x9ex\x88\xc6s\xce<@\x16\x00\x00\u07d4\\?V\u007f\xaf\xf7\xba\u0475\x12\x00\"\xe8\xcb\u02a8+I\x17\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\\Ch\x91\x8a\xced\t\u01de\u0280\u036a\xe49\x1d+bN\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\\FA\x97y\x1c\x8a=\xa3\xc9%Co'z\xb1;\xf2\xfa\xa2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\\H\x81\x16\\\xb4+\xb8.\x979l\x8e\xf4J\xdb\xf1s\xfb\x99\x89\x05\xfe\xe2\"\x04\x1e4\x00\x00\xe0\x94\\H\x92\x90z\a \xdfo\xd3A>c\xffv}k9\x80#\x8a\x02\xcb\x00\x9f\u04f5y\x0f\x80\x00\u07d4\\O$\xe9\x94\ud3c5\x0e\xa7\x81\x8fG\x1c\x8f\xac;\xcf\x04R\x89]\x80h\x8d\x9e1\xc0\x00\x00\u07d4\\T\x19V\\:\xadNqN\a92\x8e5!\u024f\x05\u0309\x1c\x9fx\u0489>@\x00\x00\u07d4\\a6\xe2\x18\xde\na\xa17\xb2\xb3\x96-*a\x12\xb8\t\u05c9\x0f\xf3\u06f6_\xf4\x86\x80\x00\xe0\x94\\a\xaby\xb4\b\xdd2)\xf6bY7\x05\xd7/\x1e\x14{\xb8\x8a\x04\xd0$=4\x98\u0344\x00\x00\u07d4\\m\x04\x1d\xa7\xafD\x87\xb9\xdcH\xe8\xe1\xf6\af\u0425m\xbc\x89O\a\n\x00>\x9ct\x00\x00\u07d4\\o6\xaf\x90\xab\x1aeln\xc8\xc7\xd5!Q'b\xbb\xa3\xe1\x89lh\xcc\u041b\x02,\x00\x00\u07d4\\{\x9e\u01e2C\x8d\x1eD*\x86\x0f\x8a\x02\x1e\x18\x99\xf07z\xea\x00\x00\u07d4\\\xcc\xf1P\x8b\xfd5\xc2\x050\xaad%\x00\xc1\r\xeee\xea\xed\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\\\xcer\xd0h\xc7\xc3\xf5[\x1d(\x19T^w1|\xae\x82@\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\\\xd0\xe4u\xb5D!\xbd\xfc\f\x12\xea\x8e\b+\u05e5\xaf\nj\x89\x032\xca\x1bg\x94\f\x00\x00\u07d4\\\u0548\xa1N\xc6H\xcc\xf6G)\xf9\x16z\xa7\xbf\x8b\xe6\xeb=\x8965\u026d\xc5\u07a0\x00\x00\u07d4\\\u062f`\xdee\xf2M\xc3\xceW0\xba\x92e0\"\xdcYc\x89a\t=|,m8\x00\x00\u07d4\\\xdcG\b\xf1O@\xdc\xc1Zy_}\xc8\xcb\v\u007f\xaa\x9en\x89\x1d\x1c_>\xda \xc4\x00\x00\u07d4\\\u0d86,\u0391b\xe8~\bI\xe3\x87\xcb]\xf4\xf9\x11\x8c\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\\\xe2\xe7\u03aa\xa1\x8a\xf0\xf8\xaa\xfa\u007f\xba\xd7L\u021e<\xd46\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\\\xe4@h\xb8\xf4\xa3\xfey\x9ej\x83\x11\xdb\xfd\xed\xa2\x9d\xee\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u0794\\\xeb\xe3\v*\x95\xf4\xae\xfd\xa6ee\x1d\xc0\xcf~\xf5u\x81\x99\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\\\xf1\x8f\xa7\u0227\xc0\xa2\xb3\xd5\xef\u0459\x0fd\xdd\xc5i$,\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\\\xf4N\x10T\reqd#\xb1\xbc\xb5B\xd2\x1f\xf8:\x94\u034a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\\\xf8\xc0>\xb3\xe8r\xe5\x0f|\xfd\f/\x8d;?,\xb5\x18:\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\\\xfa\x8dV\x85ue\x8c\xa4\xc1\xa5\x93\xacL]\x0eD\xc6\aE\x89\x0f\xc6o\xae7F\xac\x00\x00\u07d4\\\xfa\x98w\xf7\x19\u01dd\x9eIJ\b\xd1\xe4\x1c\xf1\x03\xfc\x87\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4]\x1d\xc38{G\xb8E\x1eU\x10l\f\xc6}m\xc7+\u007f\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4]#\x1ap\xc1\xdf\xeb6\n\xbd\x97\xf6\x16\xe2\xd1\r9\xf3\u02b5\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4]$\xbd\xbc\x1cG\xf0\xeb\x83\xd1(\xca\xe4\x8a\xc3\xf4\xb5\x02bt\a\xda'/g\x81Jk\xec\u0509\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4]\x83\xb2\x1b\xd2q#`Ckg\xa5\x97\xee3x\xdb>z\xe4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94]\x87+\x12.\x99N\xf2|q\xd7\u07b4W\xbfeB\x9e\xcal\x8a\x01\xb1\xad\xed\x81\u04d4\x10\x80\x00\xe0\x94]\x8d1\xfa\xa8d\xe2!Y\xcdoQu\xcc\xec\xc5?\xa5Mr\x8a\x05\xb6\x96\xb7\r\xd5g\x10\x00\x00\xe0\x94]\x95\x8a\x9b\u0449\u0098_\x86\u014a\x8ci\xa7\xa7\x88\x06\xe8\u068a\x02(\xf1o\x86\x15x`\x00\x00\u07d4]\xa2\xa9\xa4\xc2\xc0\xa4\xa9$\xcb\xe0\xa5:\xb9\xd0\xc6'\xa1\u03e0\x89'\xbf8\xc6TM\xf5\x00\x00\u07d4]\xa4\u0288\x93\\'\xf5\\1\x10H\x84\x0eX\x9e\x04\xa8\xa0I\x89\x04V9\x18$O@\x00\x00\u07d4]\xa5G\x85\u027d0W\\\x89\u07b5\x9d A\xd2\n9\xe1{\x89j\xa2\t\xf0\xb9\x1de\x80\x00\xe0\x94]\xb6\x9f\xe9>o\xb6\xfb\xd4P\x96k\x97#\x8b\x11\n\xd8'\x9a\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4]\xb7\xbb\xa1\xf9W?$\x11]\x8c\x8cb\xe9\u0388\x95\x06\x8e\x9f\x89\x02\xb5\xaa\xd7,e \x00\x00\xe0\x94]\xb8D\x00W\x00i\xa9W<\xab\x04\xb4\u6d955\xe2\x02\xb8\x8a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4]\xc3m\xe55\x94P\xa1\xec\t\xcb\fD\xcf+\xb4+:\xe45\x89<\x94m\x89;3\x06\x00\x00\u07d4]\xc6\xf4_\xef&\xb0n3\x021?\x88M\xafH\xe2to\xb9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94]\u0376\xb8zP\xa9\xde\x02C\x80\x00\x00\u07d4^Q\xb8\xa3\xbb\t\xd3\x03\xea|\x86\x05\x15\x82\xfd`\x0f\xb3\xdc\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794^X\xe2U\xfc\x19\x87\n\x040_\xf2\xa0F1\xf2\xff)K\xb1\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4^ZD\x19t\xa8=t\u0187\xeb\xdcc?\xb1\xa4\x9e{\x1a\u05c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^eE\x8b\xe9d\xaeD\x9fqw7\x04\x97\x97f\xf8\x89\x87a\x89\x1c\xa7\xccs[o|\x00\x00\u07d4^g\u07c9i\x10\x1a\u06bd\x91\xac\xcdk\xb1\x99\x12t\xaf\x8d\xf2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4^n\x97G\xe1b\xf8\xb4\\en\x0fl\xaez\x84\xba\xc8\x0eN\x89lk\x93[\x8b\xbd@\x00\x00\u07d4^s\x1bU\xce\xd4R\xbb??\xe8q\xdd\xc3\xed~\xe6Q\n\x8f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^t\xed\x80\xe9eW\x88\xe1\xbb&\x97R1\x96g\xfeuNZ\x89\x03\t'\xf7L\x9d\xe0\x00\x00\u07d4^w.'\xf2\x88\x00\xc5\r\u0697;\xb3>\x10v.n\xea \x89a\t=|,m8\x00\x00\u07d4^{\x8cT\xdcW\xb0@ bq\x9d\xee~\xf5\xe3~\xa3]b\x89\x9b\xf9\x81\x0f\xd0\\\x84\x00\x00\u07d4^\u007fp7\x87uX\x9f\xc6j\x81\xd3\xf6S\xe9T\xf5U`\ub243\xf2\x89\x18\x1d\x84\xc8\x00\x00\xe0\x94^\x80n\x84W0\xf8\a>l\xc9\x01\x8e\xe9\x0f\\\x05\xf9\t\xa3\x8a\x02\x01\xe9m\xac\u03af \x00\x00\u07d4^\x8eM\xf1\x8c\xf0\xafw\tx\xa8\u07cd\xac\x90\x93\x15\x10\xa6y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4^\x90\xc8Xw\x19\x87V\xb06l\x0e\x17\xb2\x8eR\xb4FPZ\x89\x14JJ\x18\xef\xebh\x00\x00\u07d4^\x95\xfe_\xfc\xf9\x98\xf9\xf9\xac\x0e\x9a\x81\u06b8>\xadw\x00=\x89\x1dB\xc2\r2y\u007f\x00\x00\u07d4^\xad)\x03z\x12\x89dx\xb1)j\xb7\x14\xe9\u02d5B\x8c\x81\x89\x03\xe0C\a-@n\x00\x00\u07d4^\xb3q\xc4\a@lB{;}\xe2q\xad<\x1e\x04&\x95y\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^\u037a\xea\xb9\x10o\xfe]{Q\x96\x96`\x9a\x05\xba\ub16d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4^\xd0\xd63\x85Y\xefD\xdcza\xed\xeb\x89?\xa5\xd8?\xa1\xb5\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\xe0\x94^\u04fb\xc0R@\xe0\u04d9\xebm\xdf\xe6\x0fb\xdeM\x95\t\xaf\x8a)\x14\xc0$u\xf9\xd6\xd3\x00\x00\u0594^\xd3\xf1\xeb\xe2\xaegV\xb5\xd8\xdc\x19\xca\xd0,A\x9a\xa5w\x8b\x80\u07d4^\xd5a\x15\xbde\x05\xa8\x82s\xdf\\V\x83\x94p\xd2J-\xb7\x89\x03\x8ee\x91\xeeVf\x80\x00\xe0\x94^\xf8\xc9a\x86\xb3y\x84\xcb\xfe\x04\u0158@n;\n\xc3\x17\x1f\x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4^\xfb\xdf\xe58\x99\x99c<&`Z[\xfc,\x1b\xb5\x95\x93\x93\x89\x03\xc0W\xc9\\\xd9\b\x00\x00\xe0\x94_\x13\x15F1Fm\xcb\x13S\u0210\x93*|\x97\xe0\x87\x8e\x90\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4_\x16z\xa2B\xbcL\x18\x9a\xde\xcb:\u0127\xc4R\xcf\x19/\u03c9lkLM\xa6\u077e\x00\x00\xe0\x94_\x1c\x8a\x04\xc9\rs[\x8a\x15)\t\xae\xaeco\xb0\xce\x16e\x8a\x01{x'a\x8cZ7\x00\x00\u07d4_#\xba\x1f7\xa9lE\xbcI\x02YS\x8aT\u008b\xa3\xb0\u0549A\rXj \xa4\xc0\x00\x00\u07d4_&\xcf4Y\x9b\xc3n\xa6{\x9ez\x9f\x9bC0\xc9\xd5B\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4_)\xc9\xdev]\xde%\x85*\xf0}3\xf2\xceF\x8f\xd2\t\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_/\a\xd2\u0597\xe8\xc5g\xfc\xfd\xfe\x02\x0fI\xf3`\xbe!9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_2\x1b=\xaa\xa2\x96\xca\xdf)C\x9f\x9d\xab\x06*K\xff\xed\u0589\x04p%\x90>\xa7\xae\x00\x00\u07d4_3:;#\x10vZ\r\x182\xb9\xbeL\n\x03pL\x1c\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4_4K\x01\xc7\x19\x1a2\xd0v*\xc1\x88\xf0\xec-\xd4`\x91\x1d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94_6>\n\xb7G\xe0-\x1b;f\xab\xb6\x9e\xa5<{\xafR:\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\u07d4_7[\x86`\f@\u0328\xb2gkz\x1a\x1d\x16D\xc5\xf5,\x89\x04F\x18\xd7Lb?\x00\x00\u07d4_>\x1eg9\xb0\xc6\"\x00\xe0\n\x006\x91\xd9\xef\xb28\u061f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4_H?\xfb\x8fh\n\xed\xf2\xa3\x8fx3\xaf\xdc\xdeY\xb6\x1eK\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94_J\xceL\x1c\xc13\x91\xe0\x1f\x00\xb1\x98\xe1\xf2\v_\x91\xcb\xf5\x8a\x01\x0f\x0f\xa8\xb9\u04c1\x1a\x00\x00\xe0\x94_R\x12\x82\xe9\xb2x\u070c\x03Lr\xafS\xee)\xe5D=x\x8a\x01as-/\x8f:\xe0\x00\x00\u07d4_h\xa2L~\xb4\x11vgs{39?\xb3\xc2\x14\x8aS\xb6\x89\x02\xce\u0791\x8dE<\x00\x00\u07d4_p\x8e\xaf9\xd8#\x94lQ\xb3\xa3\u9df3\xc0\x03\xe2cA\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4_t.H~:\xb8\x1a\xf2\xf9J\xfd\xbe\x1b\x9b\x8f\\\u0301\xbc\x89u\xc4E\xd4\x11c\xe6\x00\x00\u07d4_t\xed\x0e$\xff\x80\u0672\u0124K\xaa\x99uB\x8c\u05b95\x89\xa1\x8b\xce\xc3H\x88\x10\x00\x00\u07d4_v\xf0\xa3\x06&\x9cx0k=e\r\xc3\xe9\xc3p\x84\xdba\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4_w\xa1\a\xab\x12&\xb3\xf9_\x10\ue0ee\xfcl]\xff>\u0709\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4_{;\xba\xc1m\xab\x83\x1aJ\x0f\xc5;\fT\x9d\xc3l1\u0289i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94_\x93\xff\x83't\xdbQ\x14\xc5[\xb4\xbfD\xcc\U000f53d0?\x8a(\xa9\xc9\x1a&4X)\x00\x00\u07d4_\x96\x16\xc4{Jg\xf4\x06\xb9Z\x14\xfeo\xc2h9o\x17!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4_\x98\x109\xfc\xf5\x02%\xe2\xad\xf7bu!\x12\xd1\xcc&\xb6\xe3\x89\x1b\x1aAj!S\xa5\x00\x00\u07d4_\x99\u070eI\xe6\x1dW\xda\xef`j\xcd\xd9\x1bMp\a2j\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4_\xa6\x1f\x15-\xe6\x125\x16\xc7Q$)y(_yj\u01d1\x89\v\x0f\x11\x97)c\xb0\x00\x00\u07d4_\xa7\xbf\xe0C\x88a'\xd4\x01\x1d\x83V\xa4~\x94yc\xac\xa8\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94_\xa8\xa5Nh\x17lO\xe2\xc0\x1c\xf6q\xc5\x15\xbf\xbd\xd5(\xa8\x8aE\xe1U\xfa\x01\x10\xfa@\x00\x00\u07d4_\xad\x96\x0fk,\x84V\x9c\x9fMG\xbf\x19\x85\xfc\xb2\xc6]\xa6\x8965f3\xeb\xd8\xea\x00\x00\u07d4_\xc6\xc1\x14&\xb4\xa1\xea\xe7\xe5\x1d\xd5\x12\xad\x10\x90\xc6\xf1\xa8[\x89\x93\xfe\\W\xd7\x10h\x00\x00\u07d4_\u0344Th\x96\xdd\b\x1d\xb1\xa3 \xbdM\x8c\x1d\xd1R\x8cL\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4_\u0368G\xaa\xf8\xd7\xfa\x8b\xca\b\x02\x9c\xa2\x84\x91f\xaa\x15\xa3\x89!\u02b8\x12Y\xa3\xbf\x00\x00\u07d4_\xd1\xc3\xe3\x17x'l\xb4.\xa7@\xf5\xea\xe9\xc6A\xdb\xc7\x01\x89\n\x84Jt$\xd9\xc8\x00\x00\u07d4_\xd3\xd6w~\xc2b\n\xe8:\x05R\x8e\xd4%\a-<\xa8\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_\xd9s\xaf6j\xa5\x15|Te\x9b\u03f2|\xbf\xa5\xac\x15\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4_\xe7w\x03\x80\x8f\x82>l9\x93R\x10\x8b\xdb,R|\xb8|\x89j@v\xcfy\x95\xa0\x00\x00\xe0\x94_\xecI\xc6e\xe6N\xe8\x9d\xd4A\xeet\x05n\x1f\x01\xe9(p\x8a\x01V\x9b\x9es4t\xc0\x00\x00\u07d4_\xf3&\xcd`\xfd\x13k$^)\xe9\bzj\u04e6R\u007f\r\x89e\xea=\xb7UF`\x00\x00\u07d4_\xf9=\xe6\xee\x05L\xadE\x9b-^\xb0\xf6\x87\x03\x89\xdf\xcbt\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\u07d4`\x06\xe3m\x92\x9b\xf4]\x8f\x16#\x1b\x12j\x01\x1a\xe2\x83\xd9%\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4`!\xe8Z\x88\x14\xfc\xe1\xe8*A\xab\xd1\u04f2\xda\xd2\xfa\xef\xe0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4`8t\n\xe2\x8df\xba\x93\xb0\xbe\bH+2\x05\xa0\xf7\xa0{\x89\x11!a\x85\u009fp\x00\x00\u07d4`?/\xabz\xfbn\x01{\x94v`i\xa4\xb4;8\x96I#\x89Y\xd2\xdb$\x14\u0699\x00\x00\u07d4`B'm\xf2\x98?\xe2\xbcGY\xdc\x19C\xe1\x8f\xdb\xc3Ow\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4`B\xc6D\xba\xe2\xb9o%\xf9M1\xf6x\xc9\r\xc9f\x90\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4`L\xdf\x18b\x8d\xbf\xa82\x91\x94\xd4x\xddR\x01\xee\xccK\xe7\x89\x01?0j$\t\xfc\x00\x00\u07d4`N\x94w\xeb\xf4r|t[\u02bb\xed\xcbl\xcf)\x99@\"\x8966\x9e\xd7t}&\x00\x00\u07d4`gm\x1f\xa2\x1f\xca\x05\"\x97\xe2K\xf9c\x89\u0171*p\u05c9\r\x17|Zzh\xd6\x00\x00\u07d4`gn\x92\u044b\x00\x05\t\xc6\x1d\xe5@\xe6\xc5\u0776v\xd5\t\x89A\rXj \xa4\xc0\x00\x00\u07d4`o\x17q!\xf7\x85\\!\xa5\x06#0\xc8v\"d\xa9{1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4`\x86B6\x93\r\x04\xd8@+]\xcb\xeb\x80\u007f<\xafa\x1e\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4`\xabq\xcd&\xeamnY\xa7\xa0\xf6'\xee\a\x9c\x88^\xbb\xf6\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4`\xaf\x0e\xe1\x18D<\x9b7\xd2\xfe\xadw\xf5\xe5!\u07be\x15s\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4`\xb3X\xcb=\xbe\xfa7\xf4}\xf2\xd76X@\u068e;\u024c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4`\xb8\u05b7;ySO\xb0\x8b\xb8\xcb\xce\xfa\xc7\xf3\x93\xc5{\xfe\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4`\xbeo\x95?*M%\xb6%o\xfd$#\xac\x148%.N\x89\b!\xab\rD\x14\x98\x00\x00\u0794`\xc3qO\xdd\xdbcFY\u48b1\xeaB\xc4r\x8c\u01f8\xba\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4`\xcc=D^\xbd\xf7j}z\xe5q\u0197\x1d\xffh\u0305\x85\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94`\xd5fq@\xd1&\x14\xb2\x1c\x8e^\x8a3\b.2\xdf\xcf#\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4`\xde\"\xa1Pt2\xa4{\x01\xcch\xc5*\v\xf8\xa2\xe0\u0418\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4`\xe0\xbd\u0422Y\xbb\x9c\xb0\x9d?7\xe5\u034b\x9d\xac\uafca\x89JD\x91\xbdm\xcd(\x00\x00\u07d4`\xe3\xccC\xbc\xdb\x02j\xadu\x9cpf\xf5U\xbb\xf2\xacf\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\x04+\x80\xfd`\x95\u0478{\xe2\xf0\x0f\x10\x9f\xab\xaf\xd1W\xa6\x89\x05k\xc7^-c\x10\x00\x00\u07d4a\a\xd7\x1d\xd6\xd0\xee\xfb\x11\xd4\xc9\x16@L\xb9\x8cu>\x11}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\x0f\xd6\xeeN\xeb\xab\x10\xa8\xc5]\vK\xd2\xe7\xd6\xef\x81qV\x89\x01\x15\x95a\x06]]\x00\x00\u07d4a\x14\xb0\xea\xe5Wi\x03\xf8\v\xfb\x98\x84-$\xed\x92#\u007f\x1e\x89\x05k\xc7^-c\x10\x00\x00\u07d4a!\xaf9\x8a[-\xa6\x9fe\xc68\x1a\xec\x88\u039c\xc6D\x1f\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4a&g\xf1r\x13[\x95\v,\xd1\xde\x10\xaf\xde\xcehW\xb8s\x8965\u026d\xc5\u07a0\x00\x00\u07d4a,\xed\x8d\xc0\u071e\x89\x9e\xe4oyb33\x15\xf3\xf5^D\x89\x12^5\xf9\xcd=\x9b\x00\x00\u07d4a4\xd9B\xf07\xf2\xcc=BJ#\f`=g\xab\xd3\xed\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a:\xc5;\xe5e\xd4e6\xb8 q[\x9b\x8d:\xe6\x8aK\x95\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\u07d4a?\xabD\xb1k\xbeUMD\xaf\xd1x\xab\x1d\x02\xf3z\ua949lk\x93[\x8b\xbd@\x00\x00\u07d4aN\x8b\xef=\xd2\u015bY\xa4\x14Vt@\x10\x185\x18\x84\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4aQ\x84d\xfd\u0637<\x1b\xb6\xacm\xb6\x00eI8\xdb\xf1z\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4aT}7nSi\xbc\xf9x\xfc\x16,1\xc9\b\"3\xb8%\xd0%\xbe?{\x10V\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4a\x91\xdd\u0276J\x8e\b\x90\xb427\t\u05e0|H\xb9*d\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4a\x96\xc3\xd3\xc0\x90\x8d%Cf\xb7\xbc\xa5WE\"-\x9dM\xb1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4a\x9f\x17\x14E\xd4+\x02\xe2\xe0p\x04\xad\x8a\xfeiO\xa5=j\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4a\xad\xf5\x92\x9a^)\x81hN\xa2C\xba\xa0\x1f}\x1f^\x14\x8a\x89\x05\xfa\xbfl\x98O#\x00\x00\u07d4a\xb1\xb8\xc0\x12\xcdLx\xf6\x98\xe4p\xf9\x02V\xe6\xa3\x0fH\u0749\n\u05ce\xbcZ\xc6 \x00\x00\u07d4a\xb3\xdf.\x9e\x9f\xd9h\x13\x1f\x1e\x88\xf0\xa0\xeb[\xd7eFM\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4a\xb9\x02\u0166s\x88X&\x82\r\x1f\xe1EI\xe4\x86_\xbd\u0089\x12$\xef\xed*\u1440\x00\u07d4a\xb9\x05\xdef?\xc1s\x86R;:(\xe2\xf7\xd07\xa6U\u0349\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4a\xba\x87\xc7~\x9bYm\xe7\xba\x0e2o\xdd\xfe\xec!c\xeff\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4a\xbf\x84\u056b\x02oX\xc8s\xf8o\xf0\xdf\u0282\xb5W3\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\xc4\xee|\x86LMk^7\xea\x131\xc2\x03s\x9e\x82k/\x89\x01\xa15;8*\x91\x80\x00\u07d4a\xc80\xf1eG\x18\xf0u\u032b\xa3\x16\xfa\xac\xb8[}\x12\v\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4a\xc8\xf1\xfaC\xbf\x84i\x99\xec\xf4{+2M\xfbkc\xfe:\x89+^:\xf1k\x18\x80\x00\x00\u07d4a\xc9\xdc\u8c98\x1c\xb4\x0e\x98\xb0@+\xc3\xeb(4\x8f\x03\xac\x89\n\xac\xac\u0679\xe2+\x00\x00\u07d4a\u03a7\x1f\xa4d\xd6*\a\x06?\x92\v\f\xc9\x17S\x973\u0609Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4a\xd1\x01\xa03\xee\x0e.\xbb1\x00\xed\xe7f\xdf\x1a\xd0$IT\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4a\xedU\x96\u0197 \u007f=U\xb2\xa5\x1a\xa7\xd5\x0f\a\xfa\t\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\xff\x8eg\xb3M\x9e\xe6\xf7\x8e\xb3o\xfe\xa1\xb9\xf7\xc1W\x87\xaf\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4b\x05\xc2\xd5dtp\x84\x8a8@\xf3\x88~\x9b\x01]4u\\\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4b(\xad\xe9^\x8b\xb1}\x1a\xe2;\xfb\x05\x18AMI~\x0e\xb8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94b)\xdc\xc2\x03\xb1\xed\xcc\xfd\xf0n\x87\x91\fE*\x1fMzr\x8a\x06\xe1\xd4\x1a\x8f\x9e\xc3P\x00\x00\u0794b+\xe4\xb4T\x95\xfc\xd91C\xef\xc4\x12\u0599\xd6\xcd\xc2=\u0148\xf0\x15\xf2W6B\x00\x00\u07d4b3\x1d\xf2\xa3\xcb\xee5 \xe9\x11\u07a9\xf7>\x90_\x89%\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4bVD\xc9Z\x87>\xf8\xc0l\u06de\x9fm\x8dv\x80\x04=b\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4be\xb2\xe7s\x0f6\xb7v\xb5-\f\x9d\x02\xad\xa5]\x8e<\xb6\x8965\u026d\xc5\u07a0\x00\x00\u07d4bh\n\x15\xf8\u0338\xbd\xc0/s`\xc2Z\xd8\u03f5{\x8c\u034965\u026d\xc5\u07a0\x00\x00\u07d4b\x94\xea\xe6\xe4 \xa3\xd5`\n9\xc4\x14\x1f\x83\x8f\xf8\xe7\xccH\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4b\x97\x1b\xf2cL\xee\v\xe3\u0249\x0fQ\xa5`\x99\u06f9Q\x9b\x89#\x8f\xd4,\\\xf0@\x00\x00\u07d4b\x9b\xe7\xab\x12jS\x98\xed\xd6\u069f\x18D~x\u0192\xa4\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4b\xb4\xa9\"nah\a\x1el\xbea\x11\xfe\xf0\xbcc\x8a\x03\xba\x19\x10\xbf4\x1b\x00\x00\x00\xe0\x94c\n\x91:\x901\xc9I*\xbdLA\u06f1PT\xcf\xecD\x16\x8a\x014X\xdbg\xaf5\xe0\x00\x00\xe0\x94c\fRs\x12mQ|\xe6q\x01\x81\x1c\xab\x16\xb8SL\xf9\xa8\x8a\x01\xfe\xcc\xc6%s\xbb\u04c0\x00\u07d4c\x100\xa5\xb2{\a(\x8aEio\x18\x9e\x11\x14\xf1*\x81\xc0\x89\x1b\x1azB\v\xa0\r\x00\x00\u07d4c\x10\xb0 \xfd\x98\x04IW\x99P\x92\t\x0f\x17\xf0NR\xcd\xfd\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4c+\x91I\xd7\x01x\xa7364'^\x82\u0555?'\x96{\x89%\xf2s\x93=\xb5p\x00\x00\u07d4c,\xec\xb1\f\xfc\xf3\x8e\u0246\xb4;\x87p\xad\xec\xe9 \x02!\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4c1\x02\x8c\xbbZ!H[\xc5\x1bVQB\x99;\xdb%\x82\xa9\x89\x1c\xfd\xd7F\x82\x16\xe8\x00\x00\u07d4c3O\xcf\x17E\x84\x0eK\tJ;\xb4\v\xb7o\x96\x04\xc0L\x89\u05e5\xd7\x03\xa7\x17\xe8\x00\x00\u07d4c4\nWqk\xfac\xebl\xd13r\x12\x02W[\xf7\x96\xf0\x89\va\xe0\xa2\f\x12q\x80\x00\u07d4cN\xfc$7\x11\a\xb4\xcb\xf0?y\xa9=\xfd\x93\xe41\xd5\xfd\x89B5\x82\xe0\x8e\xdc\\\x80\x00\xe0\x94c\\\x00\xfd\xf05\xbc\xa1_\xa3a\r\xf38N\x0f\xb7\x90h\xb1\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4ca.xb\xc2{X|\xfbm\xaf\x99\x12\xcb\x05\x1f\x03\n\x9f\x89\x02[\x19\u053f\xe8\xed\x00\x00\u07d4cfgU\xbdA\xb5\x98i\x97x<\x13\x040\b$+<\xb5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4c{\xe7\x1b:\xa8\x15\xffE=VB\xf70tE\vd\xc8*\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94c}g\xd8\u007fXo\nZG\x9e \xee\x13\xea1\n\x10\xb6G\x8a\n:Y&\xaf\xa1\xe70\x00\x00\u07d4c\u007fXi\xd6\xe4i_\x0e\xb9\xe2s\x11\u0107\x8a\xff33\x80\x89j\xc0Nh\xaa\xec\x86\x00\x00\u07d4c\x97|\xad}\r\xcd\xc5+\x9a\xc9\xf2\xff\xa16\xe8d(\x82\xb8\x89\x04\x10\u0546\xa2\nL\x00\x00\u07d4c\xa6\x1d\xc3\n\x8e;0\xa7c\xc4!<\x80\x1c\xbf\x98s\x81x\x8965\u026d\xc5\u07a0\x00\x00\u07d4c\xacT\\\x99\x12C\xfa\x18\xae\xc4\x1dOoY\x8eUP\x15\u0709 \x86\xac5\x10R`\x00\x00\u07d4c\xb9uMu\xd1-8@9\xeci\x06<\v\xe2\x10\xd5\xe0\u3252\v\x86\f\xc8\xec\xfd\x80\x00\u07d4c\xbbfO\x91\x17\x03v(YM\xa7\xe3\xc5\b\x9f\xd6\x18\xb5\xb5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4c\u00a3\xd25\xe5\xee\xab\xd0\u0526\xaf\u06c9\xd9F'9d\x95\x89CN\xf0[\x9d\x84\x82\x00\x00\u07d4c\xc8\xdf\xde\v\x8e\x01\xda\xdc.t\x8c\x82L\xc06\x9d\U00010cc9\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4c\xd5Z\u065b\x917\xfd\x1b \xcc+O\x03\xd4,\xba\xdd\xf34\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4c\xd8\x00H\x87u\x96\xe0\u0084\x89\xe6P\xcdJ\xc1\x80\tjI\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94c\xe4\x14`>\x80\xd4\xe5\xa0\xf5\xc1\x87t FB%\x82\b\xe4\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94c\xe8\x8e.S\x9f\xfbE\x03\x86\xb4\xe4g\x89\xb2#\xf5GlE\x8a\x01U\x17\nw\x8e%\xd0\x00\x00\u07d4c\xef/\xbc=\xaf^\xda\xf4\xa2\x95b\x9c\xcf1\xbc\xdf@8\xe5\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4c\xf0\xe5\xa7R\xf7\x9fg\x12N\xedc:\xd3\xfd'\x05\xa3\x97\u0509\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94c\xf5\xb5=y\xbf.A\x14\x89Re0\"8E\xfa\xc6\xf6\x01\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4c\xfc\x93\x00\x13\x05\xad\xfb\u0278])\xd9)\x1a\x05\xf8\xf1A\v\x8965\u026d\xc5\u07a0\x00\x00\u0794c\xfek\xccK\x8a\x98P\xab\xbeu\x8070\xc92%\x1f\x14[\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4d\x03\xd0bT\x96\x90\xc8\xe8\xb6>\xaeA\xd6\xc1\tGn%\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4d\x04+\xa6\x8b\x12\xd4\xc1Qe\x1c\xa2\x81;sR\xbdV\xf0\x8e\x89 \x86\xac5\x10R`\x00\x00\u0794d\x05\xdd\x13\xe9:\xbc\xff7~p\x0e<\x1a\x00\x86\xec\xa2})\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94d\n\xbam\xe9\x84\xd9E\x177x\x03p^\xae\xa7\t_J\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4d\v\xf8t\x15\xe0\xcf@s\x01\xe5Y\x9ah6m\xa0\x9b\xba\u0209\x1a\xbc\x9fA`\x98\x15\x80\x00\u07d4d \xf8\xbc\xc8\x16JaR\xa9\x9dk\x99i0\x05\xcc\xf7\xe0S\x8965f3\xeb\xd8\xea\x00\x00\u07d4d$\x1axD)\x0e\n\xb8U\xf1\u052au\xb5SE\x03\"$\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4d&J\xed\xd5-\xca\xe9\x18\xa0\x12\xfb\xcd\f\x03\x0e\xe6\xf7\x18!\x8965\u026d\xc5\u07a0\x00\x00\u07d4d7\x0e\x87 &E\x12Z5\xb2\a\xaf\x121\xfb`r\xf9\xa7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4d=\x9a\xee\u0531\x80\x94~\u04b9 |\xceL=\xdcU\xe1\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4dC\xb8\xaec\x9d\xe9\x1c\xf7\xf0p\xa5G\x03\xb7\x18NH'l\\\x00w\xefK4\x89\x11X\xe4`\x91=\x00\x00\x00\xe0\x94d\xe2\xde! \v\x18\x99\u00e0\xc0e;P@\x13m\r\xc8B\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4d\xec\x8a[t?4y\xe7\a\xda\xe9\xee \u076aO@\xf1\u0649\n\u05ce\xbcZ\xc6 \x00\x00\u07d4e\x03\x86\v\x19\x10\b\xc1U\x83\xbf\u0201X\t\x93\x01v((\x8965\u026d\xc5\u07a0\x00\x00\u07d4e\x051\x911\x9e\x06z%\xe66\x1dG\xf3\u007fc\x18\xf84\x19\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4e\t;#\x9b\xbf\xba#\xc7w\\\xa7\xdaZ\x86H\xa9\xf5L\xf7\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4e\t\xee\xb14~\x84/\xfbA>7\x15^,\xbcs\x82s\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94e\vBUU\xe4\xe4\xc5\x17\x18\x14h6\xa2\xc1\xeew\xa5\xb4!\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4e\f\xf6}\xb0`\xcc\xe1uh\xd5\xf2\xa4#h|Idv\t\x89\x05k\xc7^-c\x10\x00\x00\u07d4e\x10\xdfB\xa5\x99\xbc\xb0\xa5\x19\u0329a\xb4\x88u\x9aogw\x89lk\x93[\x8b\xbd@\x00\x00\u07d4e6u\xb8B\xd7\u0634a\xf7\"\xb4\x11|\xb8\x1d\xac\x8ec\x9d\x89\x01\xae6\x1f\xc1E\x1c\x00\x00\u07d4eK~\x80\x87\x99\xa8=r\x87\xc6w\x06\xf2\xab\xf4\x9aId\x04\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94eORHG\xb3\xa6\xac\xc0\xd3\xd5\xf1\xf3b\xb6\x03\xed\xf6_\x96\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4eY4\u068etN\xaa=\xe3M\xbb\xc0\x89LN\xda\va\xf2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4e]\\\xd7H\x96)\xe2ANIb?\xabb\xa1~M6\x11\x89\x05\fL\xb2\xa1\f`\x00\x00\u07d4e\xaf\x8d\x8b[\x1d\x1e\xed\xfaw\xbc\xbc\x96\xc1\xb13\xf83\x06\u07c9\x05P\x05\xf0\xc6\x14H\x00\x00\u07d4e\xaf\x90\x87\xe0QgqT\x97\u0265\xa7I\x18\x94\x89\x00M\xef\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u0794e\xb4/\xae\xcc\x1e\u07f1B\x83\u0297\x9a\xf5E\xf6;0\xe6\f\x88\xfc\x93c\x92\x80\x1c\x00\x00\u0794e\xd3>\xb3\x9c\xdadS\xb1\x9ea\xc1\xfeM\xb91p\xef\x9d4\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4e\xd8\xddN%\x1c\xbc\x02\x1f\x05\xb0\x10\xf2\xd5\xdcR\f8r\xe0\x89-CW\x9a6\xa9\x0e\x00\x00\u07d4e\xea&\xea\xbb\xe2\xf6L\xcc\xcf\xe0h)\xc2]F7R\x02%\x89%\xf2s\x93=\xb5p\x00\x00\u07d4e\xeag\xad?\xb5j\xd5\xfb\x948}\u04ce\xb3\x83\x00\x1d|h\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94e\xeb\xae\xd2~\u06dd\xcc\x19W\xae\xe5\xf4R\xac!\x05\xa6\\\x0e\x8a\t7\u07ed\xae%\u26c0\x00\u07d4e\xee \xb0m\x9a\u0549\xa7\xe7\xce\x04\xb9\xf5\xf7\x95\xf4\x02\xae\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4e\xf544m/\xfbx\u007f\xa9\xcf\x18]t[\xa4)\x86\xbdn\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94e\xf5\x87\x0f&\xbc\xe0\x89g}\xfc#\xb5\x00\x1e\xe4\x92H4(\x8a\x01\x12\xb1\xf1U\xaa2\xa3\x00\x00\u07d4e\xfd\x02\xd7\x04\xa1*M\xac\xe9G\x1b\x06E\xf9b\xa8\x96q\u0209\x01\x8d\x1c\xe6\xe4'\u0340\x00\u07d4e\xff\x87O\xaf\xceM\xa3\x18\xd6\xc9=W\xe2\u00ca\rs\xe8 \x8968\x02\x1c\xec\u06b0\x00\x00\xe0\x94f\x05W\xbbC\xf4\xbe:\x1b\x8b\x85\xe7\xdf{<[\xcdT\x80W\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4f\b,u\xa8\xde1\xa59\x13\xbb\xd4M\xe3\xa07O\u007f\xaaA\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4f\x11\xceY\xa9\x8b\a*\xe9Y\xdcI\xadQ\x1d\xaa\xaa\xa1\x9dk\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4f \x1b\xd2'\xaem\u01bd\xfe\xd5\xfb\u0781\x1f\xec\xfe^\x9d\u0649 >\x9e\x84\x92x\x8c\x00\x00\u07d4f#4\x81G$\x93[y1\xdd\xcaa\x00\xe0\rFw'\u0349\"\x88&\x9d\a\x83\xd4\x00\x00\u07d4f'O\xea\x82\xcd0\xb6\u009b#5\x0eOO=1\nX\x99\x89p7\x05P\xab\x82\x98\x00\x00\u07d4f,\xfa\x03\x8f\xab7\xa0\x17E\xa3d\u1e41'\xc5\x03tm\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4f5\xb4oq\x1d-\xa6\xf0\xe1cp\u034e\xe4>\xfb,-R\x89lk\x93[\x8b\xbd@\x00\x00\u07d4f6\x04\xb0P0F\xe6$\xcd&\xa8\xb6\xfbGB\xdc\xe0*o\x89\x03\x8b\x9by~\xf6\x8c\x00\x00\u07d4f6\u05ecczH\xf6\x1d8\xb1L\xfdHe\xd3m\x14(\x05\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4f@\xcc\xf0SU\\\x13\n\xe2\xb6Vd~\xa6\xe3\x167\xb9\xab\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4fBK\xd8x[\x8c\xb4a\x10*\x90\x02\x83\xc3]\xfa\a\xefj\x89\x02.-\xb2ff\xfc\x80\x00\u07d4fL\xd6}\xcc\u026c\x82(\xb4\\U\u06cdvU\ve\x9c\u0709\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4fNC\x11\x98p\xaf\x10zD\x8d\xb1'\x8b\x04H8\xff\u036f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4fQso\xb5\x9b\x91\xfe\xe9\xc9:\xa0\xbdn\xa2\xf7\xb2Pa\x80\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4f[\x00\x0f\vw'P\xcc\x89k\x91\x8a\xacIK\x16\x80\x00\xe0\x94g]\\\xaa`\x9b\xf7\n\x18\xac\xa5\x80F]\x8f\xb71\r\x1b\xbb\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4gc F\u0732ZT\x93i(\xa9oB?3 \xcb\ud489lk\x93[\x8b\xbd@\x00\x00\u07d4ge\xdf%(\x0e\x8eO8\u0531\xcfDo\xc5\xd7\xebe\x9e4\x89\x05k\xc7^-c\x10\x00\x00\u07d4gv\xe13\xd9\xdc5L\x12\xa9Q\b{c\x96P\xf59\xa43\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4g\x85Q<\xf72\xe4~\x87g\ap\xb5A\x9b\xe1\f\xd1\xfct\x89lk\x93[\x8b\xbd@\x00\x00\u07d4g\x947\xea\xcfCxx\xdc)=H\xa3\x9c\x87\xb7B\x1a!l\x89\x03\u007f\x81\x82\x1d\xb2h\x00\x00\u07d4g\x9b\x9a\x10\x990Q~\x89\x99\t\x9c\xcf*\x91LL\x8d\xd94\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4g\xa8\x0e\x01\x90r\x1f\x949\rh\x02r\x9d\xd1,1\xa8\x95\xad\x89lk\x13u\xbc\x91V\x00\x00\u07d4g\xb8\xa6\xe9\x0f\xdf\n\x1c\xacD\x17\x930\x1e\x87P\xa9\xfayW\x890\x84\x9e\xbe\x166\x9c\x00\x00\u07d4g\xbc\x85\xe8}\xc3LN\x80\xaa\xfa\x06k\xa8\u049d\xbb\x8eC\x8e\x89\x15\xd1\xcfAv\xae\xba\x00\x00\u07d4g\xc9&\t>\x9b\x89'\x938\x10\u0642\"\xd6.+\x82\x06\xbb\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4g\xcf\xdanp\xbfvW\u04d0Y\xb5\x97\x90\xe5\x14Z\xfd\xbea\x89#\x05\r\tXfX\x00\x00\u07d4g\u0582\xa2\x82\xefs\xfb\x8dn\x90q\xe2aOG\xab\x1d\x0f^\x8965\u026d\xc5\u07a0\x00\x00\u07d4g\u05a8\xaa\x1b\xf8\xd6\xea\xf78N\x99=\xfd\xf1\x0f\n\xf6\x8aa\x89\n\xbc\xbbW\x18\x97K\x80\x00\u07d4g\u0692.\xff\xa4r\xa6\xb1$\xe8N\xa8\xf8k$\xe0\xf5\x15\xaa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4g\xdf$-$\r\u0538\a\x1dr\xf8\xfc\xf3[\xb3\x80\x9dq\xe8\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4g\xee@n\xa4\xa7\xaej:8\x1e\xb4\xed\xd2\xf0\x9f\x17KI(\x898)c_\th\xb0\x00\x00\u07d4g\xf2\xbbx\xb8\xd3\xe1\x1f|E\x8a\x10\xb5\xc8\xe0\xa1\xd3tF}\x89a\t=|,m8\x00\x00\u07d4g\xfcR}\xce\x17\x85\xf0\xfb\x8b\xc7\xe5\x18\xb1\xc6i\xf7\xec\u07f5\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4h\x02}\x19U\x8e\xd73\x9a\b\xae\xe8\xde5Y\xbe\x06>\xc2\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4h\x06@\x83\x8b\xd0zD{\x16\x8dm\x92;\x90\xcflC\xcd\u0289]\u0212\xaa\x111\xc8\x00\x00\u07d4h\a\xdd\u020d\xb4\x89\xb03\xe6\xb2\xf9\xa8\x15SW\x1a\xb3\xc8\x05\x89\x01\x9f\x8euY\x92L\x00\x00\xe0\x94h\rY\x11\xed\x8d\xd9\xee\xc4\\\x06\f\"?\x89\xa7\xf6 \xbb\u054a\x04<3\xc1\x93ud\x80\x00\x00\u07d4h\x11\xb5L\u0456c\xb1\x1b\x94\xda\x1d\xe2D\x82\x85\u035fh\u0649;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4h\x19\f\xa8\x85\xdaB1\x87L\x1c\xfbB\xb1X\n!s\u007f8\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94h(\x97\xbcO\x8e\x89\x02\x91 \xfc\xff\xb7\x87\xc0\x1a\x93\xe6A\x84\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4h)^\x8e\xa5\xaf\xd9\t?\xc0\xa4e\xd1W\x92+]*\xe24\x89\x01\x15NS!}\xdb\x00\x00\u07d4h.\x96'oQ\x8d1\xd7\xe5n0\u07f0\t\xc1!\x82\x01\xbd\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4h5\xc8\xe8\xb7J,\xa2\xae?J\x8d\x0fk\x95J>*\x83\x92\x89\x03B\x9c3]W\xfe\x00\x00\u07d4h63\x01\n\x88hk\xeaZ\x98\xeaS\xe8y\x97\xcb\xf7>i\x89\x05k9Bc\xa4\f\x00\x00\u07d4h=\xba6\xf7\xe9O@\xeaj\xea\ry\xb8\xf5!\xdeU\an\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4hA\x9cm\xd2\xd3\xceo\u02f3\xc7>/\xa0y\xf0`Q\xbd\xe6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4hG;z}\x96Y\x04\xbe\u06e5V\u07fc\x17\x13l\xd5\xd44\x89\x05k\xc7^-c\x10\x00\x00\u07d4hG\x82[\xde\xe8$\x0e(\x04,\x83\xca\xd6B\U000868fd\u0709QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94hJD\xc0i3\x9d\b\xe1\x9auf\x8b\u06e3\x03\xbe\x85S2\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\u07d4hS\x1fM\u0680\x8fS vz\x03\x114(\xca\f\xe2\xf3\x89\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4hy'\xe3\x04\x8b\xb5\x16*\xe7\xc1\\\xf7k\xd1$\xf9I{\x9e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94h\x80\x9a\xf5\xd52\xa1\x1c\x1aMn2\xaa\xc7\\LR\xb0\x8e\xad\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4h\x86\xad\xa7\xbb\xb0a{\u0684!\x91\u018c\x92.\xa3\xa8\xac\x82\x89>\xe2;\xde\x0e} \x00\x00\xe0\x94h\x88>\x15.V`\xfe\xe5\x96&\xe7\xe3\xb4\xf0Q\x10\xe6\"/\x8a\v\x94c;\xe9u\xa6*\x00\x00\u07d4h\x8aV\x9e\x96U$\xeb\x1d\n\xc3\xd3s>\xab\x90\x9f\xb3\xd6\x1e\x89G\x8e\xae\x0eW\x1b\xa0\x00\x00\xe0\x94h\x8e\xb3\x85;\xbc\xc5\x0e\xcf\xee\x0f\xa8\u007f\n\xb6\x93\u02bd\xef\x02\x8a\x06\xb1\n\x18@\x06G\xc0\x00\x00\u07d4h\xa7B_\xe0\x9e\xb2\x8c\xf8n\xb1y>A\xb2\x11\xe5{\u058d\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4h\xa8l@#\x88\xfd\xdcY\x02\x8f\xecp!\u933f\x83\x0e\xac\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\xe0\x94h\xac\u06a9\xfb\x17\xd3\xc3\t\x91\x1aw\xb0_S\x91\xfa\x03N\xe9\x8a\x01\xe5.3l\xde\"\x18\x00\x00\u07d4h\xad\xdf\x01\x9dk\x9c\xabp\xac\xb1?\v1\x17\x99\x9f\x06.\x12\x89\x02\xb5\x12\x12\xe6\xb7\u0200\x00\u07d4h\xb3\x186\xa3\n\x01j\xda\x15{c\x8a\xc1]\xa7?\x18\xcf\u0789\x01h\u048e?\x00(\x00\x00\xe0\x94h\xb6\x85G\x88\xa7\xc6Il\xdb\xf5\xf8K\x9e\xc5\xef9+x\xbb\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4h\xc0\x84\x90\u021b\xf0\u05b6\xf3 \xb1\xac\xa9\\\x83\x12\xc0\x06\b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4h\xc7\xd1q\x1b\x01\x1a3\xf1o\x1fU\xb5\xc9\x02\xcc\xe9p\xbd\u05c9\b=lz\xabc`\x00\x00\u07d4h\xc8y\x1d\xc3B\xc3sv\x9e\xa6\x1f\xb7\xb5\x10\xf2Q\xd3 \x88\x8965\u026d\xc5\u07a0\x00\x00\u07d4h\u07d4|I[\ubbb8\u8273\xf9S\xd53\x87K\xf1\x06\x89\x1d\x99E\xab+\x03H\x00\x00\u07d4h\xe8\x02'@\xf4\xaf)\xebH\xdb2\xbc\xec\xdd\xfd\x14\x8d=\xe3\x8965\u026d\xc5\u07a0\x00\x00\u07d4h\xecy\u057eqUql@\x94\x1cy\u05cd\x17\u079e\xf8\x03\x89\x1b#8w\xb5 \x8c\x00\x00\u07d4h\xee\xc1\u222c1\xb6\xea\xba~\x1f\xbdO\x04\xadW\x9ak]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4h\xf5%\x92\x1d\xc1\x1c2\x9buO\xbf>R\x9f\xc7#\xc84\u0349WG=\x05\u06ba\xe8\x00\x00\u07d4h\xf7\x19\xae4+\xd7\xfe\xf1\x8a\x05\u02f0/pZ\u04ce\u0572\x898\xeb\xad\\\u0710(\x00\x00\xe0\x94h\xf7W<\xd4W\xe1L\x03\xfe\xa4>0-04|\x10p\\\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4h\xf8\xf4QU\xe9\x8cP)\xa4\xeb\u0175'\xa9.\x9f\xa81 \x89\xf0{D\xb4\a\x93 \x80\x00\u07d4h\xfe\x13W!\x8d\tXI\xcdW\x98B\u012a\x02\xff\x88\x8d\x93\x89lk\x93[\x8b\xbd@\x00\x00\u07d4i\x02(\xe4\xbb\x12\xa8\u0535\u09d7\xb0\xc5\xcf*u\t\x13\x1e\x89e\xea=\xb7UF`\x00\x00\u07d4i\x05\x94\xd3\x06a<\xd3\xe2\xfd$\xbc\xa9\x99J\u064a=s\xf8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94i\a2ir\x9ed\x14\xb2n\xc8\xdc\x0f\xd95\xc7;W\x9f\x1e\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x94i\x19\xdd^]\xfb\x1a\xfa@G\x03\xb9\xfa\xea\x8c\xee5\xd0\rp\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4i4\x92\xa5\xc5\x13\x96\xa4\x82\x88\x16i\xcc\xf6\xd8\xd7y\xf0\tQ\x89\x12\xbfPP:\xe3\x03\x80\x00\u07d4i=\x83\xbe\tE\x9e\xf89\v.0\xd7\xf7\u008d\xe4\xb4(N\x89lk\x93[\x8b\xbd@\x00\x00\u07d4iQp\x83\xe3\x03\xd4\xfb\xb6\xc2\x11E\x14!]i\xbcF\xa2\x99\x89\x05k\xc7^-c\x10\x00\x00\u07d4iUPel\xbf\x90\xb7]\x92\xad\x91\"\xd9\r#\xcah\xcaM\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94iX\xf8;\xb2\xfd\xfb'\xce\x04\t\xcd\x03\xf9\xc5\xed\xbfL\xbe\u074a\x04<3\xc1\x93ud\x80\x00\x00\u0794i[\x0fRBu7\x01\xb2d\xa6pq\xa2\u0708\b6\xb8\u06c8\u3601\x1b\xech\x00\x00\xe0\x94i[L\xce\bXV\xd9\xe1\xf9\xff>y\x94 #5\x9e_\xbc\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94if\x06:\xa5\xde\x1d\xb5\xc6q\xf3\xddi\x9dZ\xbe!>\xe9\x02\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4it\u0224\x14\u03ae\xfd<.M\xfd\xbe\xf40V\x8d\x9a\x96\v\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\xe0\x94iximQP\xa9\xa2cQ?\x8ft\u0196\xf8\xb19|\xab\x8a\x01g\xf4\x82\xd3\u0171\xc0\x00\x00\xe0\x94iy{\xfb\x12\u027e\u0582\xb9\x1f\xbcY5\x91\xd5\xe4\x027(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94i\u007fUSk\xf8Z\xdaQ\x84\x1f\x02\x87b:\x9f\x0e\u041a\x17\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4i\x82\xfe\x8a\x86~\x93\xebJ\v\xd0QX\x93\x99\xf2\xec\x9aR\x92\x89lk\x93[\x8b\xbd@\x00\x00\u07d4i\x8a\x8ao\x01\xf9\xabh/c|yi\xbe\x88_lS\x02\xbf\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4i\x8a\xb9\xa2\xf33\x81\xe0|\fGC=\r!\xd6\xf36\xb1'\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4i\x94\xfb21\xd7\xe4\x1dI\x1a\x9dh\xd1\xfaL\xae,\xc1Y`\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\x9c\x9e\xe4q\x95Q\x1f5\xf8b\xcaL\"\xfd5\xae\x8f\xfb\xf4\x89\x04V9\x18$O@\x00\x00\u07d4i\x9f\xc6\u058aGuW<\x1d\u036e\xc80\xfe\xfdP9|N\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4i\xaf(\xb0tl\xac\r\xa1p\x84\xb99\x8c^6\xbb:\r\xf2\x896w\x03n\xdf\n\xf6\x00\x00\u07d4i\xb8\x0e\xd9\x0f\x84\x83J\xfa?\xf8.\xb9dp;V\tw\u0589\x01s\x17\x90SM\xf2\x00\x00\xe0\x94i\xb8\x1dY\x81\x14\x1e\u01e7\x14\x10`\xdf\u03cf5\x99\xff\xc6>\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4i\xbc\xfc\x1dC\xb4\xba\x19\xde{'K\xdf\xfb5\x13\x94\x12\xd3\u05c95e\x9e\xf9?\x0f\xc4\x00\x00\u07d4i\xbd%\xad\xe1\xa34lY\xc4\xe90\xdb*\x9dq^\xf0\xa2z\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\xc0\x8dtGT\xdep\x9c\xe9n\x15\xae\r\x1d9[:\"c\x8965\u026d\xc5\u07a0\x00\x00\u07d4i\xc2\xd85\xf1>\xe9\x05\x80@\x8ej2\x83\xc8\u0326\xa44\xa2\x89#\x8f\xd4,\\\xf0@\x00\x00\u07d4i\xc9N\a\u0129\xbe3\x84\xd9]\xfa<\xb9)\x00Q\x87;{\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4i\xcb>!S\x99\x8d\x86\xe5\xee \xc1\xfc\u0466\xba\uec86?\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\u04ddQ\b\x89\xe5R\xa3\x96\x13[\xfc\xdb\x06\xe3~8v3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94i\u064f8\xa3\xba=\xbc\x01\xfa\\,\x14'\xd8b\x83//p\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94i\xe2\xe2\xe7\x040|\xcc[\\\xa3\xf1d\xfe\xce.\xa7\xb2\xe5\x12\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4i\xffB\x90t\u02dblc\xbc\x91B\x84\xbc\xe5\xf0\xc8\xfb\xf7\u0409\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4i\xff\x89\x01\xb5Av?\x81|_)\x98\xf0-\xcf\xc1\xdf)\x97\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4j\x02:\xf5}XM\x84^i\x876\xf10\u06dd\xb4\r\xfa\x9a\x89\x05[ \x1c\x89\x00\x98\x00\x00\u07d4j\x04\xf5\xd5?\xc0\xf5\x15\xbe\x94+\x8f\x12\xa9\xcbz\xb0\xf3\x97x\x89\xa9\xaa\xb3E\x9b\xe1\x94\x00\x00\u07d4j\x05\xb2\x1cO\x17\xf9\xd7?_\xb2\xb0\u02c9\xffSV\xa6\xcc~\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94j\x0f\x05`f\xc2\xd5f(\x85\x02s\xd7\xec\xb7\xf8\xe6\xe9\x12\x9e\x8a\x01\x0f\r)<\u01e5\x88\x00\x00\u07d4j\x13\xd5\xe3,\x1f\xd2m~\x91\xffn\x051`\xa8\x9b,\x8a\xad\x89\x02\xe6/ \xa6\x9b\xe4\x00\x00\u07d4j.\x86F\x9a[\xf3|\xee\x82\xe8\x8bL8c\x89](\xfc\xaf\x89\x1c\"\x92f8[\xbc\x00\x00\u07d4j6\x94BL|\u01b8\xbc\u067c\u02baT\f\xc1\xf5\xdf\x18\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94jB\u0297\x1cex\u056d\xe2\x95\xc3\xe7\xf4\xad3\x1d\xd3BN\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4jD\xaf\x96\xb3\xf02\xaed\x1b\xebg\xf4\xb6\xc83B\xd3|]\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4jL\x89\a\xb6\x00$\x80W\xb1\xe4cT\xb1\x9b\u0705\x9c\x99\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4jQNbB\xf6\xb6\x8c\x13~\x97\xfe\xa1\u73b5U\xa7\xe5\xf7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4jS\xd4\x1a\xe4\xa7R\xb2\x1a\xbe\xd57FI\x95:Q=\xe5\xe5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4jaY\aJ\xb5s\xe0\xeeX\x1f\x0f=\xf2\u05a5\x94b\x9bt\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4jc7\x83?\x8fjk\xf1\f\xa7\xec!\xaa\x81\x0e\xd4D\xf4\u02c97\xbd$4\\\xe8\xa4\x00\x00\u07d4jcS\xb9qX\x9f\x18\xf2\x95\\\xba(\xab\xe8\xac\xcejWa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4jc\xfc\x89\xab\xc7\xf3n(-\x80x{{\x04\xaf\xd6U>q\x89\b\xacr0H\x9e\x80\x00\x00\u07d4jg\x9e7\x8f\xdc\xe6\xbf\xd9\u007f\xe6/\x04)Z$\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4j\x8c\xea-\xe8J\x8d\xf9\x97\xfd?\x84\xe3\b=\x93\xdeW\u0369\x89\x05k\xe0<\xa3\xe4}\x80\x00\xe0\x94j\x97Xt;`>\xea:\xa0RKB\x88\x97#\xc4\x159H\x8a\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4j\xa5s/;\x86\xfb\x8c\x81\xef\xbek[G\xb5cs\v\x06\u020965\u026d\xc5\u07a0\x00\x00\u07d4j\xb3#\xaePV\xed\nE0r\u016b\xe2\xe4/\xcf]q9\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4j\xb5\xb4\xc4\x1c\u0778)i\f/\xda\u007f \xc8^b\x9d\xd5\u0549d\u052fqL2\x90\x00\x00\u07d4j\xc4\x0fS-\xfe\xe5\x11\x81\x17\u04ad5-\xa7}Om\xa2\u0209\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4j\xc4\u053e-\xb0\u065d\xa3\xfa\xaa\xf7RZ\xf2\x82\x05\x1dj\x90\x89\x04X\xcaX\xa9b\xb2\x80\x00\u07d4j\xcd\u0723\xcd+I\x90\xe2\\\xd6\\$\x14\x9d\t\x12\t\x9ey\x89\xa2\xa1\xe0|\x9fl\x90\x80\x00\u07d4j\xd9\v\xe2R\xd9\xcdFM\x99\x81%\xfa\xb6\x93\x06\v\xa8\xe4)\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4j\u0753!\x93\xcd8IJ\xa3\xf0:\xec\xccKz\xb7\xfa\xbc\xa2\x89\x04\xdbs%Gc\x00\x00\x00\xe0\x94j\xe5\u007f'\x91|V*\x13*M\x1b\xf7\xec\n\u01c5\x83)&\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4j\xeb\x9ftt.\xa4\x91\x81=\xbb\xf0\xd6\xfc\xde\x1a\x13\x1dM\xb3\x89\x17\xe5T0\x8a\xa00\x00\x00\u07d4j\xf25\u04bb\xe0P\xe6)\x16\x15\xb7\x1c\xa5\x82\x96X\x81\x01B\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4j\xf6\xc7\xee\x99\xdf'\x1b\xa1[\xf3\x84\xc0\xb7d\xad\xcbM\xa1\x82\x8965f3\xeb\xd8\xea\x00\x00\u07d4j\xf8\xe5Yih,q_H\xadO\xc0\xfb\xb6~\xb5\x97\x95\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4j\xf9@\xf6>\u0278\xd8v'*\u0296\xfe\xf6\\\xda\xce\xcd\ua262\xa1]\tQ\x9b\xe0\x00\x00\u07d4j\xf9\xf0\xdf\uebbb_d\xbf\x91\xabw\x16i\xbf\x05)US\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4j\xff\x14f\xc2b6u\xe3\xcb\x0eu\xe4#\xd3z%\xe4B\xeb\x89]\u0212\xaa\x111\xc8\x00\x00\xe0\x94k\r\xa2Z\xf2g\u05c3l\"k\xca\xe8\xd8r\xd2\xceR\xc9A\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4k\x10\xf8\xf8\xb3\xe3\xb6\r\xe9\n\xa1-\x15_\x9f\xf5\xff\xb2,P\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\x17Y\x8a\x8e\xf5Oyz\xe5\x15\u0336Q}\x18Y\xbf\x80\x11\x89\x05k\xc7^-c\x10\x00\x00\u07d4k \xc0\x80`jy\xc7;\xd8\xe7[\x11qzN\x8d\xb3\xf1\u00c9\x10?sX\x03\xf0\x14\x00\x00\u07d4k\"\x84D\x02!\xce\x16\xa88-\xe5\xff\x02)G\"i\xde\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4k0\xf1\x829\x10\xb8m:\xcbZj\xfc\x9d\xef\xb6\xf3\xa3\v\xf8\x89\u3bb5sr@\xa0\x00\x00\u07d4k8\u0784\x1f\xad\u007fS\xfe\x02\xda\x11[\xd8j\xaff$f\xbd\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4kK\x99\xcb?\xa9\xf7\xb7L\u3903\x17\xb1\xcd\x13\t\n\x1az\x89\x03\x1b2~i]\xe2\x00\x00\u07d4kZ\xe7\xbfx\xecu\xe9\f\xb5\x03\xc7x\xcc\u04f2KO\x1a\xaf\x89+^:\xf1k\x18\x80\x00\x00\u07d4kc\xa2\u07f2\xbc\xd0\xca\xec\x00\"\xb8\x8b\xe3\f\x14Q\xeaV\xaa\x89+\xdbk\xf9\x1f\u007fL\x80\x00\u07d4kew\xf3\x90\x9aMm\xe0\xf4\x11R-Ep8d\x004\\\x89e\xea=\xb7UF`\x00\x00\u07d4kr\xa8\xf0a\xcf\xe6\x99j\xd4G\xd3\xc7,(\xc0\xc0\x8a\xb3\xa7\x89\xe7\x8cj\u01d9\x12b\x00\x00\u07d4kv\rHw\xe6\xa6'\xc1\xc9g\xbe\xe4Q\xa8P}\xdd\u06eb\x891T\xc9r\x9d\x05x\x00\x00\u07d4k\x83\xba\xe7\xb5e$EXU[\xcfK\xa8\xda \x11\x89\x1c\x17\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\x92]\xd5\xd8\xeda2\xabm\b`\xb8,D\xe1\xa5\x1f\x1f\xee\x89P; >\x9f\xba \x00\x00\xe0\x94k\x94a]\xb7Pej\u00cc~\x1c\xf2\x9a\x9d\x13g\u007fN\x15\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4k\x95\x1aC'N\xea\xfc\x8a\t\x03\xb0\xaf.\xc9+\xf1\xef\xc89\x89\x05k\xc7^-c\x10\x00\x00\u07d4k\x99%!\xec\x85#p\x84\x8a\u0597\xcc-\xf6Nc\xcc\x06\xff\x8965\u026d\xc5\u07a0\x00\x00\u07d4k\xa8\xf7\xe2_\xc2\xd8qa\x8e$\xe4\x01\x84\x19\x917\xf9\xf6\xaa\x89\x15\xafd\x86\x9ak\xc2\x00\x00\u07d4k\xa9\xb2\x1b5\x10k\xe1Y\xd1\xc1\xc2ez\xc5l\u049f\xfdD\x89\xf2\xdc}G\xf1V\x00\x00\x00\u07d4k\xafz*\x02\xaex\x80\x1e\x89\x04\xadz\xc0Q\b\xfcV\xcf\xf6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94k\xb2\xac\xa2?\xa1bm\x18\xef\xd6w\u007f\xb9}\xb0-\x8e\n\xe4\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4k\xb4\xa6a\xa3:q\xd4$\u051b\xb5\xdf(b.\xd4\xdf\xfc\xf4\x89\",\x8e\xb3\xfff@\x00\x00\u07d4k\xb5\b\x13\x14j\x9a\xddB\xee\"\x03\x8c\x9f\x1fti\xd4\u007fG\x89\n\xdaUGK\x814\x00\x00\u07d4k\xbc?5\x8af\x8d\u0461\x1f\x03\x80\xf3\xf71\bBj\xbdJ\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4k\xbd\x1eq\x93\x90\xe6\xb9\x10C\xf8\xb6\xb9\u07c9\x8e\xa8\x00\x1b4\x89llO\xa6\xc3\xdaX\x80\x00\u07d4k\xc8Z\xcdY(r.\xf5\tS1\xee\x88\xf4\x84\xb8\u03c3W\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4k\xd3\xe5\x9f#\x9f\xaf\xe4wk\xb9\xbd\xddk\ue0fa]\x9d\x9f\x8965\u026d\xc5\u07a0\x00\x00\u07d4k\xd4W\xad\xe0Qy]\xf3\xf2F\\89\xae\xd3\xc5\xde\xe9x\x8964\xbf9\xab\x98x\x80\x00\u07d4k\xe1c\x13d>\xbc\x91\xff\x9b\xb1\xa2\xe1\x16\xb8T\xea\x93:E\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4k\xe7Y^\xa0\xf0hH\x9a'\x01\xecFI\x15\x8d\xdcC\xe1x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\xe9\x03\x0e\xe6\xe2\xfb\u0111\xac\xa3\xde@\"\xd3\x01w+{}\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94k\xec1\x1a\xd0P\b\xb4\xaf5<\x95\x8c@\xbd\x06s\x9a?\xf3\x8a\x03w\xf6*\x0f\nbp\x00\x00\u07d4k\xf7\xb3\xc0e\xf2\xc1\xe7\xc6\xeb\t+\xa0\xd1Pf\xf3\x93\u0478\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4k\xf8o\x1e/+\x802\xa9\\Mw8\xa1\t\xd3\xd0\xed\x81\x04\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4l\x05\xe3N^\xf2\xf4.\u041d\xef\xf1\x02l\xd6k\xcbi`\xbb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4l\b\xa6\xdc\x01s\xc74)U\xd1\xd3\xf2\xc0e\xd6/\x83\xae\u01c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4l\n\xe9\xf0C\xc84\xd4Bq\xf14\x06Y=\xfe\tO8\x9f\x89RD*\xe13\xb6*\x80\x00\u07d4l\f\xc9\x17\xcb\xee}|\t\x97c\xf1Nd\xdf}4\xe2\xbf\t\x89\r\x8drkqw\xa8\x00\x00\xe0\x94l\x0eq/@\\Yr_\xe8)\xe9wK\xf4\xdf\u007fM\xd9e\x8a\f(h\x88\x9c\xa6\x8aD\x00\x00\xe0\x94l\x10\x12\x05\xb3#\xd7uD\xd6\xdcR\xaf7\xac\xa3\xce\xc6\xf7\xf1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4l\x15\xec5 \xbf\x8e\xbb\xc8 \xbd\x0f\xf1\x97x7T\x94\u03dd\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\xe0\x94l\x1d\xdd3\xc8\x19f\u0706!w`q\xa4\x12\x94\x82\xf2\xc6_\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4l%2\u007f\x8d\u02f2\xf4^V\x1e\x86\xe3]\x88P\xe5:\xb0Y\x89;\xcd\xf9\xba\xfe\xf2\xf0\x00\x00\u07d4l.\x9b\xe6\u052bE\x0f\xd1%1\xf3?\x02\x8caFt\xf1\x97\x89\xc2\x12z\xf8X\xdap\x00\x00\u07d4l5\x9eX\xa1=Ex\xa93\x8e3\\g\xe7c\x9f_\xb4\u05c9\v\xd1[\x94\xfc\x8b(\x00\x00\u07d4l=\x18pA&\xaa\x99\xee3B\xce`\xf5\xd4\xc8_\x18g\u0349\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4lGK\xc6jTx\x00f\xaaOQ.\xef\xa7s\xab\xf9\x19\u01c9\x05\x18\x83\x15\xf7v\xb8\x00\x00\u07d4lNBn\x8d\xc0\x05\u07e3Ql\xb8\xa6\x80\xb0.\ua56e\x8e\x89Hz\x9a0E9D\x00\x00\u07d4lR\xcf\b\x95\xbb5\xe6V\x16\x1eM\xc4j\xe0\xe9m\xd3\xe6,\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4lT\"\xfbK\x14\xe6\u064b`\x91\xfd\xecq\xf1\xf0\x86@A\x9d\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4l\\:T\u0367\xc2\xf1\x18\xed\xbaCN\xd8\x1en\xbb\x11\xddz\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4lc\xf8EV\u0490\xbf\u0359\xe44\ue657\xbf\xd7yWz\x89lk\x93[\x8b\xbd@\x00\x00\u07d4lc\xfc\x85\x02\x9a&T\u05db+\xeaM\xe3I\xe4REw\u0149#\xc7W\a+\x8d\xd0\x00\x00\u07d4led\xe5\xc9\xc2N\xaa\xa7D\xc9\xc7\xc9h\xc9\xe2\xc9\xf1\xfb\xae\x89I\x9bB\xa2\x119d\x00\x00\xe0\x94lg\xd6\xdb\x1d\x03Ql\x12\x8b\x8f\xf24\xbf=I\xb2m)A\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4lg\xe0\u05f6.*\bPiE\xa5\xdf\xe3\x82c3\x9f\x1f\"\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4lj\xa0\xd3\vdr\x19\x90\xb9PJ\x86?\xa0\xbf\xb5\xe5}\xa7\x89\x92^\x06\xee\xc9r\xb0\x00\x00\u07d4lqJX\xff\xf6\xe9}\x14\xb8\xa5\xe3\x05\xeb$@eh\x8b\xbd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4l\x80\rKI\xba\a%\x04`\xf9\x93\xb8\xcb\xe0\v&j%S\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4l\x80\x8c\xab\xb8\xff_\xbbc\x12\xd9\xc8\xe8J\xf8\xcf\x12\xef\bu\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94l\x82 )!\x8a\xc8\xe9\x8a&\f\x1e\x06@)4\x889\x87[\x8a\x01\x0f\x97\xb7\x87\xe1\xe3\b\x00\x00\u07d4l\x84\u02e7|m\xb4\xf7\xf9\x0e\xf1=^\xe2\x1e\x8c\xfc\u007f\x83\x14\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94l\x86\x87\xe3Aw\x10\xbb\x8a\x93U\x90!\xa1F\x9ej\x86\xbcw\x8a\x02[-\xa2x\xd9k{\x80\x00\xe0\x94l\x88,'s,\xef\\|\x13\xa6\x86\xf0\xa2\xeawUZ\u0089\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4l\xa5\xde\x00\x81}\xe0\xce\xdc\xe5\xfd\x00\x01(\xde\xde\x12d\x8b<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4l\xa6\xa12\xce\x1c\u0488\xbe\xe3\x0e\xc7\xcf\xef\xfb\x85\xc1\xf5\nT\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94l\xb1\x1e\xcb2\xd3\u0382\x96\x011\x066\xf5\xa1\f\xf7\u03db_\x8a\x04?\u851c8\x01\xf5\x00\x00\u07d4l\xc1\xc8x\xfal\u078a\x9a\v\x83\x11$~t\x1eFB\xfem\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94l\xcb\x03\xac\xf7\xf5<\xe8z\xad\xcc!\xa9\x93-\xe9\x15\xf8\x98\x04\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4l\xd2\x12\xae\xe0N\x01?=*\xba\u04a0#`k\xfb\\j\u01c9lj\xccg\u05f1\xd4\x00\x00\u07d4l\xd2(\xdcq!i0\u007f\xe2|\xebtw\xb4\x8c\xfc\x82r\xe5\x89\x044\xea\x94\u06caP\x00\x00\u07d4l\xe1\xb0\xf6\xad\xc4pQ\xe8\xab8\xb3\x9e\xdbA\x86\xb0;\xab\u0309Ay\x97\x94\xcd$\xcc\x00\x00\u07d4l\xea\xe3s=\x8f\xa4=l\xd8\f\x1a\x96\xe8\xeb\x93\x10\x9c\x83\xb7\x89\x10'\x94\xad \xdah\x00\x00\u07d4m\x05i\xe5U\x8f\xc7\xdf'f\xf2\xba\x15\u070a\xef\xfc[\xebu\x89\xd8\xe6\x00\x1el0+\x00\x00\u07d4m\x12\x0f\f\xaa\xe4O\xd9K\xca\xfeU\xe2\xe2y\uf5ba\\z\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4m\x14V\xff\xf0\x10N\xe8D\xa31G7\x8438\xd2L\xd6l\x89\a\xb0l\xe8\u007f\xddh\x00\x00\u07d4m \xef\x97\x04g\nP\v\xb2i\xb5\x83.\x85\x98\x02\x04\x9f\x01\x89\a\f\x1c\xc7;\x00\xc8\x00\x00\xe0\x94m/\x97g4\xb9\xd0\a\r\x18\x83\xcfz\u02b8\xb3\xe4\x92\x0f\xc1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4m9\xa9\u93c1\xf7i\xd7:\xad,\xea\xd2v\xac\x13\x87\xba\xbe\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4m;x6\xa2\xb9\u0619r\x1aM#{R#\x85\xdc\xe8\xdf\u034966\xc2^f\xec\xe7\x00\x00\u07d4m?+\xa8V\u033b\x027\xfava\x15k\x14\xb0\x13\xf2\x12@\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94m@\b\xb4\xa8\x88\xa8&\xf2H\xeej\v\r\xfd\xe9\xf92\x10\xb9\x8a\x01'\xfc\xb8\xaf\xae \xd0\x00\x00\u07d4m@\xca'\x82m\x97s\x1b>\x86\xef\xfc\u05f9*Aa\xfe\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4mD\x97J1\u0447\xed\xa1m\xddG\xb9\xc7\xecP\x02\xd6\x1f\xbe\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94mK\\\x05\xd0j \x95~\x17H\xabm\xf2\x06\xf3C\xf9/\x01\x8a\x02\x1f6\x06\x99\xbf\x82_\x80\x00\xe0\x94mL\xbf=\x82\x84\x83:\xe9\x93D0>\b\xb4\xd6\x14\xbf\xda;\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4mY\xb2\x1c\xd0\xe2t\x88\x04\u066b\xe0d\xea\u00be\xf0\xc9_'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4mc\u04ce\xe8\xb9\x0e\x0en\xd8\xf1\x92\xed\xa0Q\xb2\u05a5\x8b\xfd\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4mf4\xb5\xb8\xa4\x01\x95\xd9I\x02z\xf4\x82\x88\x02\t,\ued89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94m}\x1c\x94\x95\x11\xf8\x83\x03\x80\x8c`\xc5\xea\x06@\xfc\xc0&\x83\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4m\x84m\xc1&W\xe9\x1a\xf2P\bQ\x9c>\x85\u007fQp}\u0589\xf8\xd3\v\xc9#B\xf8\x00\x00\u07d4m\x91\x93\x99k\x19F\x17!\x11\x06\xd1c^\xb2l\u0136ll\x89\x15\xaa\x1e~\x9d\xd5\x1c\x00\x00\u07d4m\x99\x97P\x98\x82\x02~\xa9G#\x14$\xbe\xde\xde)e\u043a\x89l\x81\u01f3\x11\x95\xe0\x00\x00\u07d4m\xa0\xed\x8f\x1di3\x9f\x05\x9f*\x0e\x02G\x1c\xb4O\xb8\u00fb\x892\xbc8\xbbc\xa8\x16\x00\x00\u07d4m\xb7+\xfdC\xfe\xf4e\xcaV2\xb4Z\xabra@N\x13\xbf\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94m\xbe\x8a\xbf\xa1t(\x06&9\x817\x1b\xf3\xd3U\x90\x80kn\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4m\xc3\xf9+\xaa\x1d!\u06b78+\x892a\xa05o\xa7\xc1\x87\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4m\xc7\x05:q\x86\x16\xcf\u01cb\xeec\x82\xeeQ\xad\xd0\xc7\x030\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94m\xcc~d\xfc\xaf\xcb\xc2\xdcl\x0e^f,\xb3G\xbf\xfc\xd7\x02\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4m\xda_x\x8alh\x8d\u07d2\x1f\xa3\x85.\xb6\xd6\xc6\xc6)f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4m\xdb`\x92w\x9dXB\xea\xd3x\xe2\x1e\x81 \xfdLk\xc12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4m\xdf\xefc\x91U\u06ab\n\\\xb4\x95:\xa8\u016f\xaa\x88\x04S\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4m\xe0/-\xd6~\xfd\xb794\x02\xfa\x9e\xaa\xcb\xcfX\x9d.V\x89@\x13\x8b\x91~\u07f8\x00\x00\u07d4m\u4d418\\\xf7\xfc\x9f\xe8\xc7}\x13\x1f\xe2\xeew$\xc7j\x89})\x97s=\xcc\xe4\x00\x00\u07d4m\xe4\xd1R\x19\x18/\xaf:\xa2\xc5\xd4\xd2Y_\xf20\x91\xa7'\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4m\xed\xf6.t?M,*K\x87\xa7\x87\xf5BJz\xeb9<\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4m\xf2Of\x85\xa6/y\x1b\xa37\xbf?\xf6~\x91\xf3\u053c:\x89ukI\xd4\nH\x18\x00\x00\u07d4m\xf5\xc8O{\x90\x9a\xab>a\xfe\x0e\xcb\x1b;\xf2`\"*\u0489\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4m\xff\x90\xe6\xdc5\x9d%\x90\x88+\x14\x83\xed\xbc\xf8\x87\xc0\xe4#\x8965\u026d\xc5\u07a0\x00\x00\u07d4n\x01\xe4\xadV\x9c\x95\xd0\a\xad\xa3\r^-\xb1(\x88I\"\x94\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\a;f\u0478\xc6gD\u0600\x96\xa8\u0759\xec~\x02(\u0689\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\x0e\xe7\x06\x12\xc9v(}I\x9d\u07e6\xc0\xdc\xc1,\x06\xde\xea\x89\a\v\u0579V!F\x00\x00\xe0\x94n\x12\xb5\x1e\"[JCr\xe5\x9a\u05e2\xa1\xa1>\xa3\u04e17\x8a\x03\x00F\xc8\xccw_\x04\x00\x00\u07d4n\x1a\x04l\xaf[JW\xf4\xfdK\xc1sb!&\xb4\xe2\xfd\x86\x89a\t=|,m8\x00\x00\u07d4n\x1e\xa4\xb1\x83\xe2R\u027bwg\xa0\x06\u05346\x96\u02ca\xe9\x89\x0f\xf3x<\x85\xee\u0400\x00\u07d4n%[p\n\xe7\x13\x8aK\xac\xf2(\x88\xa9\xe2\xc0\n(^\xec\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n'\n\xd5)\xf1\xf0\xb8\xd9\xcbm$'\xec\x1b~-\xc6Jt\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4n.\xab\x85\u0709\xfe)\xdc\n\xa1\x852G\u06b4:R=V\x89\x04V9\x18$O@\x00\x00\u07d4n:Q\xdbt=3M/\xe8\x82$\xb5\xfe|\x00\x8e\x80\xe6$\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4nL*\xb7\xdb\x02i9\xdb\u04fch8J\xf6`\xa6\x18\x16\xb2\x89\t\r\x97/22<\x00\x00\u07d4nM.9\u0203f)\u5d07\xb1\x91\x8af\x9a\xeb\u07556\x8965\u026d\xc5\u07a0\x00\x00\u07d4n\\-\x9b\x1cTj\x86\xee\xfd]\nQ \xc9\xe4\xe70\x19\x0e\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4n`\xae\u19cf\x8e\u068bBLs\xe3S5J\xe6|0B\x89\xbd5\xa4\x8d\x99\x19\xe6\x00\x00\u07d4nd\xe6\x12\x9f\"N7\x8c\x0ensj~z\x06\xc2\x11\xe9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4nm[\xbb\xb9\x05;\x89\xd7D\xa2s\x16\u00a7\xb8\xc0\x9bT}\x891Rq\n\x02>m\x80\x00\u07d4nr\xb2\xa1\x18j\x8e)\x16T;\x1c\xb3jh\x87\x0e\xa5\u0457\x89\n\x15D\xbe\x87\x9e\xa8\x00\x00\u07d4nv\x1e\xaa\x0f4_w{TA\xb7:\x0f\xa5\xb5k\x85\xf2-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4ny\xed\u0504[\anL\u060d\x18\x8bnC-\xd9?5\xaa\x893\xc5I\x901r\f\x00\x00\u07d4n\x82\x12\xb7\"\xaf\xd4\b\xa7\xa7>\xd3\xe29^\xe6EJ\x030\x89\b\x9e\x91y\x94\xf7\x1c\x00\x00\u07d4n\x84\x87m\xbb\x95\xc4\vfV\xe4+\xa9\xae\xa0\x8a\x99;T\u0709;\xbc`\xe3\xb6\u02fe\x00\x00\u07d4n\x84\xc2\xfd\x18\xd8\tW\x14\xa9h\x17\x18\x9c\xa2\x1c\xcab\xba\xb1\x89\x12{lp&!\u0340\x00\u07d4n\x86m\x03-@Z\xbd\xd6\\\xf6QA\x1d\x807\x96\xc2#\x11\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94n\x89\x9eY\xa9\xb4\x1a\xb7\xeaA\xdfu\x17\x86\x0f*\xcbY\xf4\xfd\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4n\x89\xc5\x1e\xa6\xde\x13\xe0l\xdct\x8bg\xc4A\x0f\u9f2b\x03\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\x8a&h\x9fz/\xde\xfd\x00\x9c\xba\xaaS\x10%4P\u06ba\x89o!7\x17\xba\xd8\xd3\x00\x00\u07d4n\x96\xfa\xed\xa3\x05C\x02\xc4_X\xf1a2L\x99\xa3\xee\xbbb\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4n\xb0\xa5\xa9\xae\x96\xd2,\xf0\x1d\x8f\xd6H;\x9f8\xf0\x8c,\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xb3\x81\x96\x17@@X&\x8f\f<\xff5\x96\xbf\xe9\x14\x8c\x1c\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94n\xb5W\x8ak\xb7\xc3!S\x19[\r\x80 \xa6\x91HR\xc0Y\x8a\x8b\u00ab\xf4\x02!\xf4\x80\x00\x00\u07d4n\xbb^iW\xaa\x82\x1e\xf6Y\xb6\x01\x8a9:PL\xaeDP\x89lk\x93[\x8b\xbd@\x00\x00\u07d4n\xbc\xf9\x95\u007f_\xc5\u916d\xd4u\";\x04\xb8\xc1Jz\xed\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4n\xc3e\x95q\xb1\x1f\x88\x9d\xd49\xbc\xd4\xd6u\x10\xa2[\xe5~\x89\x06\xaa\xf7\xc8Qm\f\x00\x00\u07d4n\u021b9\xf9\xf5'jU>\x8d\xa3\x0en\xc1z\xa4~\xef\u01c9\x18BO_\v\x1bN\x00\x00\u07d4n\xc9m\x13\xbd\xb2M\u01e5W)?\x02\x9e\x02\xddt\xb9zU\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xca\xef\xa6\xfc>\xe54bm\xb0,o\x85\xa0\u00d5W\x1ew\x89 \x86\xac5\x10R`\x00\x00\u07d4n\u04a1+\x02\xf8\u0188\u01f5\u04e6\xea\x14\xd66\x87\u06b3\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4n\u0604E\x9f\x80\x9d\xfa\x10\x16\xe7p\xed\xaf>\x9f\xefF\xfa0\x89\xb8R\xd6x \x93\xf1\x00\x00\xe0\x94n\xdf\u007fR\x83r\\\x95>\xe6C\x17\xf6a\x88\xaf\x11\x84\xb03\x8a\x01\xb4d1\x1dE\xa6\x88\x00\x00\u07d4n\xe8\xaa\xd7\xe0\xa0e\u0605-|;\x9an_\xdcK\xf5\f\x00\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4n\xef\u0705\x0e\x87\xb7\x15\xc7'\x91w<\x03\x16\xc3U\x9bX\xa4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xf9\xe8\u0276!}Vv\x9a\xf9}\xbb\x1c\x8e\x1b\x8b\xe7\x99\u0489\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4n\xfb\xa8\xfb*\u0176s\a)\xa9r\xec\"D&\xa2\x87\u00ed\x89\x0fY\x85\xfb\xcb\xe1h\x00\x00\xe0\x94n\xfd\x90\xb55\xe0\v\xbd\x88\x9f\xda~\x9c1\x84\xf8y\xa1Q\u06ca\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4o\x05\x16f\xcbO{\u04b1\x90r!\xb8)\xb5U\u05e3\xdbt\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4o\x0e\xdd#\xbc\xd8_`\x15\xf9(\x9c(\x84\x1f\xe0L\x83\xef\xeb\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4o\x13zq\xa6\xf1\x97\xdf,\xbb\xf0\x10\u073d\x89a\t=|,m8\x00\x00\u07d4p\x10\xbe-\xf5{\u042b\x9a\xe8\x19l\xd5\n\xb0\xc5!\xab\xa9\xf9\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4p#\xc7\tV\xe0J\x92\xd7\x00%\xaa\u0497\xb59\xaf5Xi\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p%\x96]+\x88\xda\x19}DY\xbe=\xc98cD\xcc\x1f1\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d4p(\x02\xf3m\x00%\x0f\xabS\xad\xbc\u0596\xf0\x17oc\x8aI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4pH\x19\xd2\xe4Mn\xd1\xda%\xbf\u0384\u011f\u0322V\x13\xe5\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4pJn\xb4\x1b\xa3O\x13\xad\xdd\xe7\xd2\xdb}\xf0I\x15\u01e2!\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4pJ\xb1\x15\r^\x10\xf5\xe3I\x95\b\xf0\xbfpe\x0f\x02\x8dK\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4pJ\xe2\x1dv-n\x1d\xde(\xc25\xd11\x04Yr6\xdb\x1a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4pM$<)x\xe4l,\x86\xad\xbe\xcd$n;)_\xf63\x89m\x12\x1b\xeb\xf7\x95\xf0\x00\x00\u07d4pM]\xe4\x84m9\xb5<\xd2\x1d\x1cI\xf0\x96\xdb\\\x19\xba)\x89\b=lz\xabc`\x00\x00\u07d4p]\xdd85T\x82\xb8\xc7\u04f5\x15\xbd\xa1P\r\xd7\u05e8\x17\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94pan(\x92\xfa&\x97\x05\xb2\x04k\x8f\xe3\xe7/\xa5X\x16\u04ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4pg\x0f\xbb\x05\xd30\x14DK\x8d\x1e\x8ew\x00%\x8b\x8c\xaam\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\x81\xfak\xaa\xd6\u03f7\xf5\x1b,\xca\x16\xfb\x89p\x99\x1ad\xba\x89\f\xae\xc0\x05\xf6\xc0\xf6\x80\x00\xe0\x94p\x85\xae~~M\x93!\x97\xb5\u01c5\x8c\x00\xa3gF&\xb7\xa5\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4p\x86\xb4\xbd\xe3\xe3]J\xeb$\xb8%\xf1\xa2\x15\xf9\x9d\x85\xf7E\x89lh\xcc\u041b\x02,\x00\x00\u07d4p\x8a*\xf4%\u03b0\x1e\x87\xff\xc1\xbeT\xc0\xf52\xb2\x0e\xac\u0589\aE\u0503\xb1\xf5\xa1\x80\x00\u07d4p\x8e\xa7\a\xba\xe45\u007f\x1e\xbe\xa9Y\u00e2P\xac\u05aa!\xb3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794p\x8f\xa1\x1f\xe3=\x85\xad\x1b\xef\u02ee8\x18\xac\xb7\x1fj}~\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4p\x9101\x16\xd5\xf28\x9b##\x8bMej\x85\x96\u0644\u04c9;N~\x80\xaaX3\x00\x00\u07d4p\x99\xd1/n\xc6V\x89\x9b\x04\x9avW\x06]b\x99h\x92\u0209\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4p\x9f\xe9\xd2\xc1\xf1\xceB |\x95\x85\x04J`\x89\x9f5\x94/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\xa05I\xaaah\xe9~\x88\xa5\b3\nZ\v\xeatq\x1a\x89Hz\x9a0E9D\x00\x00\u07d4p\xa4\x06}D\x8c\xc2]\xc8\xe7\x0ee\x1c\xea|\xf8N\x92\x10\x9e\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4p\xab4\xbc\x17\xb6o\x9c;c\xf1Q'O*r|S\x92c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\xc2\x13H\x8a\x02\f<\xfb9\x01N\xf5\xbad\x04rK\u02a3\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4p\xd2^\xd2\u022d\xa5\x9c\b\x8c\xf7\r\xd2+\xf2\u06d3\xac\xc1\x8a\x899GEE\u4b7c\x00\x00\u07d4p\xe5\xe9\xdas_\xf0w$\x9d\u02da\xaf=\xb2\xa4\x8d\x94\x98\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4p\xfe\xe0\x8b\x00\xc6\xc2\xc0Jp\xc0\xce=\x92\u03ca\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u0794q\v\xe8\xfd^)\x18F\x8b\u2abe\xa8\r\x82\x845\u05d6\x12\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4q\x13]\x8f\x05\x96<\x90ZJ\a\x92)\t#Z\x89jR\ua262\xa1]\tQ\x9b\xe0\x00\x00\u07d4q\x1e\xcfw\xd7\x1b=\x0e\xa9\\\xe4u\x8a\xfe\u0379\xc11\a\x9d\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4q!?\xca14\x04 N\u02e8q\x97t\x1a\xa9\xdf\xe9c8\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94q+vQ\x02\x14\xdcb\x0fl:\x1d\u049a\xa2+\xf6\xd2\x14\xfb\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4q/\xf77\n\x13\xed6\ts\xfe\u071f\xf5\xd2\xc9:P^\x9e\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4q3\x84:x\xd99\u019dD\x86\xe1\x0e\xbc{`*4\x9f\xf7\x89\x11\xd5\xca\xcc\xe2\x1f\x84\x00\x00\u07d4qH\xae\xf32a\xd8\x03\x1f\xac?q\x82\xff5\x92\x8d\xafT\u0649\xdeB\xee\x15D\u0750\x00\x00\u07d4qcu\x8c\xbblLR^\x04\x14\xa4\n\x04\x9d\xcc\xcc\xe9\x19\xbb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4qh\xb3\xbb\x8c\x16s!\u067d\xb0#\xa6\xe9\xfd\x11\xaf\u026f\u0649a\t=|,m8\x00\x00\u07d4qirN\xe7\"q\xc54\xca\xd6B\x0f\xb0N\xe6D\u02c6\xfe\x89\x16<+@\u06e5R\x00\x00\u07d4qj\xd3\xc3:\x9b\x9a\n\x18\x96sW\x96\x9b\x94\xee}*\xbc\x10\x89\x1a!\x17\xfeA*H\x00\x00\xe0\x94qk\xa0\x1e\xad*\x91'\x065\xf9_%\xbf\xaf-\xd6\x10\xca#\x8a\ty\xe7\x01 V\xaax\x00\x00\u07d4qmP\u0320\x1e\x93\x85\x00\xe6B\x1c\xc0p\xc3P|g\u04c7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4qv,cg\x8c\x18\xd1\xc67\x8c\xe0h\xe6f8\x13\x15\x14~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4qxL\x10Q\x17\xc1\xf6\x895y\u007f\xe1Y\xab\xc7NC\xd1j\x89l\x81\u01f3\x11\x95\xe0\x00\x00\xe0\x94qyro\\q\xae\x1bm\x16\xa6\x84(\x17Nk4\xb26F\x8a\x01\x8e\xa2P\t|\xba\xf6\x00\x00\xe0\x94q|\xf9\xbe\xab680\x8d\xed~\x19^\f\x86\x13-\x16?\xed\x8a\x032n\xe6\xf8e\xf4\"\x00\x00\u07d4q\x80\xb8>\xe5WC\x17\xf2\x1c\x80r\xb1\x91\u0615\xd4aS\u00c9\x18\xef\xc8J\xd0\u01f0\x00\x00\u07d4q\x94kq\x17\xfc\x91^\xd1\a8_B\u065d\xda\xc62I\u0089lk\x93[\x8b\xbd@\x00\x00\xe0\x94q\x9e\x89\x1f\xbc\xc0\xa3>\x19\xc1-\xc0\xf0 9\xca\x05\xb8\x01\u07ca\x01OU8F:\x1bT\x00\x00\u07d4q\xc7#\n\x1d5\xbd\u0581\x9e\u0539\xa8\x8e\x94\xa0\xeb\a\x86\u0749\uc80b5=$\x14\x00\x00\u07d4q\xd2\xccm\x02W\x8ce\xf7\r\xf1\x1bH\xbe\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4r\x83\xcdFu\xdaX\u0116UaQ\xda\xfd\x80\xc7\xf9\x95\xd3\x18\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4r\x86\xe8\x9c\xd9\u078fz\x8a\x00\xc8o\xfd\xb59\x92\u0752Q\u0449i*\xe8\x89p\x81\xd0\x00\x00\u07d4r\x8f\x9a\xb0\x80\x15}\xb3\a1V\xdb\xca\x1a\x16\x9e\xf3\x17\x94\a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4r\x94\xc9\x18\xb1\xae\xfbM%\x92~\xf9\u05d9\xe7\x1f\x93\xa2\x8e\x85\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94r\x94\uc763\x10\xbckK\xbd\xf5C\xb0\xefE\xab\xfc>\x1bM\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4r\x9a\xadF'tNS\xf5\xd6c\t\xaatD\x8b:\xcd\xf4o\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xa2\xfc\x86u\xfe\xb9r\xfaA\xb5\r\xff\u06fa\xe7\xfa*\u07f7\x89\x9a\xb4\xfcg\xb5(\xc8\x00\x00\u07d4r\xa8&\b&)G&\xa7[\xf3\x9c\u066a\x9e\a\xa3\xea\x14\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4r\xb0Yb\xfb*\u0549\xd6Z\xd1j\"U\x9e\xba\x14X\xf3\x87\x89\a?u\u0460\x85\xba\x00\x00\u07d4r\xb5c?\xe4w\xfeT.t/\xac\xfdi\f\x13xT\xf2\x16\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4r\xb7\xa0=\xda\x14\u029cf\x1a\x1dF\x9f\xd376\xf6s\xc8\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xb9\x04D\x0e\x90\xe7 \u05ac\x1c*\u05dc2\x1d\xcc\x1c\x1a\x86\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94r\xb9\nM\xc0\x97#\x94\x92\u0179w}\xcd\x1eR\xba+\xe2\u008a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4r\xbb'\u02d9\xf3\xe2\xc2\u03d0\xa9\x8fp}0\xe4\xa2\x01\xa0q\x89X\xe7\x92n\xe8X\xa0\x00\x00\xe0\x94r\xc0\x83\xbe\xad\xbd\xc2'\xc5\xfbC\x88\x15\x97\xe3.\x83\xc2`V\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4r\xcd\x04\x8a\x11\x05tH)\x83I-\xfb\x1b\xd2yB\xa6\x96\xba\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xd0=M\xfa\xb3P\f\xf8\x9b\x86\x86o\x15\xd4R\x8e\x14\xa1\x95\x89\xf3K\x82\xfd\x8e\x91 \x00\x00\u07d4r\u06bb[n\ud799\xbe\x91X\x88\xf6V\x80V8\x16\b\xf8\x89\vL\x96\xc5,\xb4\xfe\x80\x00\u07d4r\xfbI\u009d#\xa1\x89P\u0132\xdc\r\xdfA\x0fS-oS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xfe\xaf\x12EyR9Td[\u007f\xaf\xff\x03x\xd1\xc8$.\x8965\u026d\xc5\u07a0\x00\x00\u07d4s\x01\xdcL\xf2mq\x86\xf2\xa1\x1b\xf8\xb0\x8b\xf2)F?d\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s\x04G\xf9|\xe9\xb2_\"\xba\x1a\xfb6\xdf'\xf9Xk\ub6c9,s\xc97t,P\x00\x00\u07d4s\x06\xde\x0e(\x8bV\xcf\u07d8~\xf0\xd3\xcc)f\a\x93\xf6\u0749\x1b\x8a\xbf\xb6.\xc8\xf6\x00\x00\xe0\x94s\r\x87c\u01a4\xfd\x82J\xb8\xb8Y\x16\x1e\xf7\xe3\xa9j\x12\x00\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4s\x12\x81sH\x95(\x01.v\xb4\x1a^(\u018b\xa4\xe3\xa9\u050965\u026d\xc5\u07a0\x00\x00\u07d4s\x13F\x12\bETUFTE\xa4Y\xb0l7s\xb0\xeb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s/\xea\xd6\x0f{\xfd\u05a9\xde\u0101%\xe3s]\xb1\xb6eO\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4sB#\xd2\u007f\xf2>Y\x06\xca\xed\"YW\x01\xbb4\x83\f\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4sG>r\x11Q\x10\xd0\xc3\xf1\x17\b\xf8nw\xbe+\xb0\x98<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4sRXm\x02\x1a\xd0\xcfw\xe0\xe9(@JY\xf3t\xffE\x82\x89\xb8Pz\x82\a( \x00\x00\u07d4sU\v\xebs+\xa9\u076f\xdaz\xe4\x06\xe1\x8f\u007f\xeb\x0f\x8b\xb2\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4s[\x97\xf2\xfc\x1b\xd2K\x12\an\xfa\xf3\xd1(\x80s\xd2\f\x8c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4s^2\x86f\xedV7\x14+3\x06\xb7|\xccT`\xe7,=\x89j\xb8\xf3xy\u0251\x00\x00\u07d4sc\u0350\xfb\xab[\xb8\u011a\xc2\x0f\xc6,9\x8f\xe6\xfbtL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4skDP=\xd2\xf6\xddTi\xffL[-\xb8\xeaO\xece\u0409\x11\x04\xeeu\x9f!\xe3\x00\x00\xe0\x94sk\xf1@,\x83\x80\x0f\x89>X1\x92X*\x13N\xb52\xe9\x8a\x02\x1e\x19\u0493\xc0\x1f&\x00\x00\xe0\x94s\x8c\xa9M\xb7\u038b\xe1\xc3\x05l\u0598\x8e\xb3v5\x9f3S\x8a\x05f[\x96\xcf5\xac\xf0\x00\x00\u07d4s\x91K\"\xfc/\x13\x15\x84$}\x82\xbeO\ucfd7\x8a\u053a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s\x93'\t\xa9\u007f\x02\u024eQ\xb0\x911(e\x12#\x85\xae\x8e\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4s\x93\xcb\xe7\xf9\xba!e\xe5\xa7U5\x00\xb6\xe7]\xa3\xc3:\xbf\x89\x05k\xc7^-c\x10\x00\x00\u07d4s\xb4\u0519\xde?8\xbf5\xaa\xf7i\xa6\xe3\x18\xbcm\x126\x92\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94s\xbe\xddo\xda{\xa3'!\x85\b{cQ\xfc\x13=HN7\x8a\x01\x12&\xbf\x9d\xceYx\x00\x00\u07d4s\xbf\xe7q\x0f1\u02b9I\xb7\xa2`O\xbfR9\xce\xe7\x90\x15\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94s\u03c0\xae\x96\x88\xe1X\x0eh\xe7\x82\xcd\b\x11\xf7\xaaIM,\x8a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\xe0\x94s\xd7&\x9f\xf0l\x9f\xfd3uL\xe5\x88\xf7J\x96j\xbb\xbb\xba\x8a\x01e\xc9fG\xb3\x8a \x00\x00\u07d4s\xd8\xfe\xe3\u02c6M\xce\"\xbb&\u029c/\bm^\x95\xe6;\x8965\u026d\xc5\u07a0\x00\x00\u07d4s\xdf<>yU\xf4\xf2\xd8Y\x83\x1b\xe3\x80\x00\xb1\ak8\x84\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4s\u48b6\f\U0010e2ef+w~\x17Z[\x1eM\f-\x8f\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94t\n\xf1\xee\xfd3e\u05cb\xa7\xb1,\xb1\xa6s\xe0j\arF\x8a\x04+\xf0kx\xed;P\x00\x00\xe0\x94t\v\xfdR\xe0\x16g\xa3A\x9b\x02\x9a\x1b\x8eEWj\x86\xa2\u06ca\x03\x8e\xba\xd5\xcd\xc9\x02\x80\x00\x00\u07d4t\x0fd\x16\x14w\x9d\u03e8\x8e\xd1\xd4%\xd6\r\xb4*\x06\f\xa6\x896\"\xc6v\b\x10W\x00\x00\u07d4t\x12\u027c0\xb4\xdfC\x9f\x021\x00\xe69$\x06j\xfdS\xaf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4t\x16\x93\xc3\x03vP\x85\x13\b \xcc+c\xe9\xfa\x92\x13\x1b\x89A\rXj \xa4\xc0\x00\x00\u07d4t!\xce[\xe3\x81s\x8d\u0703\xf0&!\x97O\xf0hly\xb8\x89Xx\x8c\xb9K\x1d\x80\x00\x00\u07d4t1j\xdf%7\x8c\x10\xf5v\u0574\x1aoG\xfa\x98\xfc\xe3=\x89\x128\x13\x1e\\z\xd5\x00\x00\u07d4t6Q\xb5^\xf8B\x9d\xf5\f\xf8\x198\xc2P\x8d\xe5\u0207\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t=\xe5\x00&\xcag\xc9M\xf5O\x06b`\xe1\xd1J\xcc\x11\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tE /\ft)z\x00N\xb3rj\xa6\xa8-\xd7\xc0/\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tK\x03\xbb\xa8X*\xe5I\x8e-\xc2-\x19\x94\x94g\xabS\xfc\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4tL\fw\xba\u007f#i \xd1\xe44\xde]\xa3>H\xeb\xf0,\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4tP\xff\u007f\x99\xea\xa9\x11bu\u07ach\xe4(\xdf[\xbc\u0639\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tV\u0172\xc5Cn>W\x10\b\x93?\x18\x05\xcc\xfe4\xe9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4tZ\u04eb\xc6\xee\xeb$qh\x9bS\x9ex\x9c\xe2\xb8&\x83\x06\x89=A\x94\xbe\xa0\x11\x92\x80\x00\xe0\x94tZ\xec\xba\xf9\xbb9\xb7Jg\xea\x1c\xe6#\xde6\x84\x81\xba\xa6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4t\\\xcf-\x81\x9e\u06fd\u07a8\x11{\\I\xed<*\x06n\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4tb\u021c\xaa\x9d\x8dx\x91\xb2T]\xef!otd\u057b!\x89\x05\xea\xedT\xa2\x8b1\x00\x00\u07d4td\x8c\xaa\xc7H\xdd\x13\\\xd9\x1e\xa1L(\xe1\xbdM\u007f\xf6\xae\x89\xa8\r$g~\xfe\xf0\x00\x00\xe0\x94tq\xf7.\xeb0\x06$\xeb(.\xabM\x03r\x00\x00\x00\xe0\x94t\x84\xd2k\xec\xc1\xee\xa8\xc61^\xc3\xee\nE\x01\x17\u0706\xa0\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4t\x86:\xce\xc7]\x03\xd5>\x86\x0ed\x00/,\x16^S\x83w\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\x89\u030a\xbeu\u0364\xef\r\x01\xce\xf2`^G\xed\xa6z\xb1\x89\a?u\u0460\x85\xba\x00\x00\u07d4t\x8c(^\xf1#?\xe4\xd3\x1c\x8f\xb17\x833r\x1c\x12\xe2z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t\x90\x87\xac\x0fZ\x97\xc6\xfa\xd0!S\x8b\xf1\xd6\u0361\x8e\r\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\x95\xaex\xc0\xd9\x02a\xe2\x14\x0e\xf2\x061\x04s\x1a`\xd1\xed\x89\x01\xdbPq\x89%!\x00\x00\u07d4t\x9aJv\x8b_#rH\x93\x8a\x12\xc6#\x84{\xd4\xe6\x88\u0709\x03\xe73b\x87\x14 \x00\x00\u07d4t\x9a\xd6\xf2\xb5pk\xbe/h\x9aD\u0136@\xb5\x8e\x96\xb9\x92\x89\x05k\xc7^-c\x10\x00\x00\u07d4t\xa1\u007f\x06K4N\x84\xdbce\u0695\x91\xff\x16(%vC\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4t\xae\xec\x91]\xe0\x1c\u019b,\xb5\xa65o\xee\xa1FX\xc6\u0149\f\x9a\x95\xee)\x86R\x00\x00\u07d4t\xaf\xe5I\x02\xd6\x15x%v\xf8\xba\xac\x13\xac\x97\f\x05\x0fn\x89\t\xa1\xaa\xa3\xa9\xfb\xa7\x00\x00\u07d4t\xb7\xe0\"\x8b\xae\xd6YW\xae\xbbM\x91m3:\xae\x16O\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t\xbcJ^ E\xf4\xff\x8d\xb1\x84\xcf:\x9b\f\x06Z\xd8\a\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4t\xbc\xe9\xec86-l\x94\u032c&\xd5\xc0\xe1:\x8b;\x1d@\x8965&A\x04B\xf5\x00\x00\u07d4t\xbfzZ\xb5\x92\x93\x14\x9b\\`\xcf6Bc\xe5\xeb\xf1\xaa\r\x89\x06G\f>w\x1e<\x00\x00\xe0\x94t\xc7<\x90R\x8a\x15s6\xf1\xe7\xea b\n\xe5?\xd2G(\x8a\x01\xe6:.S\x8f\x16\xe3\x00\x00\u07d4t\u0464\xd0\xc7RN\x01\x8dN\x06\xed;d\x80\x92\xb5\xb6\xaf,\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94t\xd3f\xb0{/VG}|pw\xaco\xe4\x97\xe0\xebeY\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4t\xd3zQt{\xf8\xb7q\xbf\xbfC\x9493\xd1\x00\xd2\x14\x83\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\xd6q\u065c\xbe\xa1\xabW\x90cu\xb6?\xf4+PE\x1d\x17\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\xeb\xf4BVF\xe6\u03c1\xb1\t\xce{\xf4\xa2\xa6=\x84\x81_\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4t\xed3\xac\xf4?5\xb9\x8c\x920\xb9\xe6d.\xcbS0\x83\x9e\x89$\xf6\xdf\xfbI\x8d(\x00\x00\u07d4t\xef(i\xcb\xe6\b\x85`E\xd8\xc2\x04\x11\x18W\x9f\"6\xea\x89\x03<\xd6E\x91\x95n\x00\x00\u07d4t\xfcZ\x99\xc0\xc5F\x05\x03\xa1;\x05\tE\x9d\xa1\x9c\xe7\u0350\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4u\v\xbb\x8c\x06\xbb\xbf$\bC\xccux.\xe0/\b\xa9tS\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4u\x14\xad\xbd\xc6?H?0M\x8e\x94\xb6\u007f\xf30\x9f\x18\v\x82\x89!\u0120n-\x13Y\x80\x00\u0794u\x17\xf1l(\xd12\xbb@\xe3\xba6\u01ae\xf11\xc4b\xda\x17\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4u\x1a,\xa3Nq\x87\xc1c\u048e6\x18\xdb(\xb1<\x19m&\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94u\x1a\xbc\xb6\xcc\x030Y\x91\x18\x15\xc9o\u04516\n\xb0D-\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4u&\xe4\x82R\x9f\n\x14\xee\u0248q\xdd\xdd\x0er\x1b\f\u0662\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4u)\xf3y{\xb6\xa2\x0f~\xa6I$\x19\xc8L\x86vA\xd8\x1c\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94u*^\xe22a,\xd3\x00_\xb2n[Y}\xe1\x9fwk\xe6\x8a\x01'\xfc\xb8\xaf\xae \xd0\x00\x00\u07d4u,\x9f\xeb\xf4/f\xc4x{\xfa~\xb1|\xf53;\xbaPp\x89j\x99\xf2\xb5O\xddX\x00\x00\u07d4u930F\u07b1\xef\x8e\u07b9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94u\xc1\xad#\xd2?$\xb3\x84\xd0\xc3\x14\x91w\xe8f\x97a\r!\x8a\x01\\[\xcdl(\x8b\xbd\x00\x00\u07d4u\xc2\xff\xa1\xbe\xf5I\x19\xd2\t\u007fz\x14-.\x14\xf9\xb0JX\x89\x90\xf3XP@2\xa1\x00\x00\u07d4u\xd6|\xe1N\x8d)\xe8\xc2\xff\u3051{\x93\v\x1a\xff\x1a\x87\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4u\xde~\x93R\xe9\v\x13\xa5\x9aXx\xff\xec\u01c3\x1c\xacM\x82\x89\x94\x89#z\u06daP\x00\x00\u07d4u\xf7S\x9d0\x9e\x909\x98\x9e\xfe.\x8b-\xbd\x86Z\r\xf0\x88\x89\x85[[\xa6\\\x84\xf0\x00\x00\u07d4v\b\xf47\xb3\x1f\x18\xbc\vd\u04c1\xae\x86\xfd\x97\x8e\u05f3\x1f\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94v\x0f\xf35N\x0f\u0793\x8d\x0f\xb5\xb8,\xef[\xa1\\=)\x16\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4v\x1an6,\x97\xfb\xbd|Yw\xac\xba-\xa7F\x876_I\x89\t\xf7J\xe1\xf9S\xd0\x00\x00\u07d4v\x1el\xae\xc1\x89\xc20\xa1b\xec\x00e0\x19>g\u03dd\x19\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94v\x1f\x8a:*\U00028f7e\x1d\xa0\t2\x1f\xb2\x97d\xebb\xa1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4v)\x98\xe1\xd7R'\xfc\xedzp\xbe\x10\x9aL\vN\xd8d\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4v-o0\u06b9\x915\xe4\xec\xa5\x1dRC\xd6\xc8b\x11\x02\u0549\x0fI\x89A\xe6d(\x00\x00\u07d4v3\x1e0yl\xe6d\xb2p\x0e\rASp\x0e\u0706\x97w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v8\x86\xe33\xc5o\xef\xf8[\xe3\x95\x1a\xb0\xb8\x89\xce&.\x95\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v:|\xba\xb7\rzd\u0427\xe5)\x80\xf6\x81G%\x93I\f\x89 \x86\xac5\x10R`\x00\x00\u07d4v>\xec\u0c0a\u021e2\xbf\xa4\xbe\xcev\x95\x14\xd8\xcb[\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4v@\xa3\u007f\x80R\x98\x15\x15\xbc\xe0x\u0693\xaf\xa4x\x9bW4\x89lk\x93[\x8b\xbd@\x00\x00\u0794vA\xf7\xd2j\x86\xcd\xdb+\xe10\x81\x81\x0e\x01\xc9\xc8E\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94vO\xc4mB\x8bm\xbc\"\x8a\x0f_U\xc9P\x8cw.\xab\x9f\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4vPn\xb4\xa7\x80\xc9Q\xc7J\x06\xb0=;\x83b\xf0\x99\x9dq\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94v[\xe2\xe1/b\x9ecI\xb9}!\xb6*\x17\xb7\xc80\xed\xab\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94vb\x81P\xe2\x99[['\x9f\xc8>\r\xd5\xf1\x02\xa6q\xdd\x1c\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4vk7Y\xe8yN\x92m\xacG=\x91:\x8f\xb6\x1a\xd0\xc2\u0249\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4vp\xb0/,<\xf8\xfdOG0\xf38\x1aq\xeaC\x1c3\u01c9\x0e~\xeb\xa3A\vt\x00\x00\u07d4vz\x03eZ\xf3`\x84\x1e\x81\r\x83\xf5\xe6\x1f\xb4\x0fL\xd1\x13\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4vz\u0190y\x1c.#E\x10\x89\xfelp\x83\xfeU\u07b6+\x89,s\xc97t,P\x00\x00\u07d4v\u007f\xd7y}Qi\xa0_sd2\x1c\x19\x84:\x8c4\x8e\x1e\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u0794v\x84o\r\xe0;Zv\x97\x1e\xad)\x8c\xdd\b\x84:K\xc6\u0188\xd7\x1b\x0f\u088e\x00\x00\xe0\x94v\x84\x98\x93N7\xe9\x05\xf1\xd0\xe7{D\xb5t\xbc\xf3\xecJ\xe8\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4v\x8c\xe0\u06a0)\xb7\xde\xd0\"\xe5\xfcWM\x11\xcd\xe3\xec\xb5\x17\x89\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\xe0\x94v\x93\xbd\xebo\xc8+[\xcar\x13U\"1u\xd4z\bKM\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4v\xaa\xf8\xc1\xac\x01/\x87R\xd4\xc0\x9b\xb4f\a\xb6e\x1d\\\xa8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4v\xab\x87\xddZ\x05\xad\x83\x9aN/\xc8\xc8Z\xa6\xba\x05d\x170\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v\xaf\xc2%\xf4\xfa0}\xe4\x84U+\xbe\x1d\x9d?\x15\aLJ\x89\xa2\x90\xb5\u01ed9h\x00\x00\xe0\x94v\xbe\xca\xe4\xa3\x1d6\xf3\xcbW\u007f*CYO\xb1\xab\xc1\xbb\x96\x8a\x05C\xa9\xce\x0e\x132\xf0\x00\x00\u07d4v\xc2u5\xbc\xb5\x9c\xe1\xfa-\x8c\x91\x9c\xab\xebJk\xba\x01\u0449lk\x93[\x8b\xbd@\x00\x00\u07d4v\xca\"\xbc\xb8y\x9eS'\u012a*}\tI\xa1\xfc\xce_)\x89R\xa0?\"\x8cZ\xe2\x00\x00\u07d4v\xca\u0108\x11\x1aO\u0555\xf5h\xae:\x85\x87p\xfc\x91]_\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94v\u02dc\x8bi\xf48vu\u0102S\xe24\xcb~\rt\xa4&\x8a\x01\x90\xf4H.\xb9\x1d\xae\x00\x00\u07d4v\xf8:\xc3\xda0\xf7\t&(\xc73\x9f \x8b\xfc\x14,\xb1\ue25a\x18\xff\xe7B}d\x00\x00\xe0\x94v\xf9\xad=\x9b\xbd\x04\xae\x05\\\x14w\xc0\xc3^u\x92\xcb* \x8a\b\x83?\x11\xe3E\x8f \x00\x00\u07d4v\xff\xc1W\xadk\xf8\xd5m\x9a\x1a\u007f\u077c\x0f\xea\x01\n\xab\xf4\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\x02\x8e@\x9c\xc4:;\xd3=!\xa9\xfcS\xec`n\x94\x91\x0e\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4w\f/\xb2\u0128\x17S\xac\x01\x82\xeaF\x0e\xc0\x9c\x90\xa5\x16\xf8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4w\r\x98\xd3\x1bCS\xfc\xee\xe8V\fL\u03c0>\x88\xc0\xc4\xe0\x89 \x86\xac5\x10R`\x00\x00\xe0\x94w\x13\xab\x807A\x1c\t\xbah\u007fo\x93d\xf0\xd3#\x9f\xac(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4w\x15\a\xae\xeej%]\xc2\u035d\xf5QT\x06-\b\x97\xb2\x97\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4w\x19\x88\x87\x95\xadtY$\xc7W`\u0771\x82}\xff\xd8\u0368\x89lkLM\xa6\u077e\x00\x00\u07d4w'\xaf\x10\x1f\n\xab\xa4\xd2:\x1c\xaf\xe1|n\xb5\u06b1\xc6\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4w,)\u007f\n\u0454H.\xe8\xc3\xf06\xbd\xeb\x01\xc2\x01\xd5\u0309\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94w0o\xfe.J\x8f<\xa8&\xc1\xa2I\xf7!-\xa4:\xef\xfd\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4w1A\x12}\x8c\xf3\x18\xae\xbf\x886Z\xdd=U'\xd8[j\x8966\u05ef^\u024e\x00\x00\u07d4wF\xb6\xc6i\x9c\x8f4\xca'h\xa8 \xf1\xff\xa4\xc2\a\xfe\x05\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4wQ\xf3c\xa0\xa7\xfd\x053\x19\b\t\u076f\x93@\xd8\xd1\x12\x91\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4wW\xa4\xb9\xcc=\x02G\u032a\xeb\x99\t\xa0\xe5n\x1d\xd6\xdc\u0089\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4w\\\x10\xc9>\r\xb7 [&CE\x823\xc6O\xc3?\xd7[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4wa~\xbcK\xeb\xc5\xf5\xdd\xeb\x1bzp\xcd\xebj\xe2\xff\xa0$\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4wiC\xff\xb2\xef\\\xdd5\xb8<(\xbc\x04k\xd4\xf4gp\x98\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94wp\x1e,I=\xa4|\x1bX\xf4!\xb5I]\xeeE\xbe\xa3\x9b\x8a\x01H\xf6I\xcfaB\xa5\x80\x00\u07d4wy\x8f \x12W\xb9\xc3R\x04\x95pW\xb5Ft\xae\xfaQ\u07c9\b\x13\xcaV\x90m4\x00\x00\u07d4w\x8cC\xd1\x1a\xfe;Xo\xf3t\x19-\x96\xa7\xf2=+\x9b\u007f\x89\x8b\xb4\xfc\xfa;}k\x80\x00\u07d4w\x8cy\xf4\xde\x19S\xeb\u0398\xfe\x80\x06\xd5:\x81\xfbQ@\x12\x8963\x03\"\xd5#\x8c\x00\x00\u07d4w\x92t\xbf\x18\x03\xa36\xe4\u04f0\r\u0753\xf2\xd4\xf5\xf4\xa6.\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\xa1q\"\xfa1\xb9\x8f\x17\x11\xd3*\x99\xf0>\xc3&\xf3=\b\x89\\(=A\x03\x94\x10\x00\x00\u07d4w\xa3I\a\xf3\x05\xa5L\x85\xdb\t\xc3c\xfd\xe3\xc4~j\xe2\x1f\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4w\xa7i\xfa\xfd\xec\xf4\xa68v-[\xa3\x96\x9d\xf61 \xa4\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4w\xbekd\xd7\xc73\xa46\xad\xec^\x14\xbf\x9a\xd7@+\x1bF\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\xbf\xe9<\u0367P\x84~A\xa1\xaf\xfe\xe6\xb2\u0696\xe7!N\x89\x10CV\x1a\x88)0\x00\x00\u07d4w\u0126\x97\xe6\x03\xd4+\x12\x05l\xbb\xa7a\xe7\xf5\x1d\x04C\xf5\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4w\xcc\x02\xf6#\xa9\u03d8S\t\x97\xeag\xd9\\;I\x18Y\xae\x89Is\x03\xc3n\xa0\xc2\x00\x00\u07d4w\xd4?\xa7\xb4\x81\xdb\xf3\xdbS\f\xfb\xf5\xfd\xce\xd0\xe6W\x181\x89lk\x93[\x8b\xbd@\x00\x00\u07d4w\xda^lr\xfb6\xbc\xe1\xd9y\x8f{\xcd\xf1\u044fE\x9c.\x89\x016\x95\xbbl\xf9>\x00\x00\u07d4w\xf4\xe3\xbd\xf0V\x88<\xc8r\x80\xdb\xe6@\xa1\x8a\r\x02\xa2\a\x89\n\x81\x99:+\xfb[\x00\x00\u0794w\xf6\t\u0287 \xa0#&,U\xc4o-&\xfb90\xaci\x88\xf0\x15\xf2W6B\x00\x00\u07d4w\xf8\x1b\x1b&\xfc\x84\xd6\u0797\uf2df\xbdr\xa310\xccJ\x8965\u026d\xc5\u07a0\x00\x00\u07d4x\x19\xb0E\x8e1N+S\xbf\xe0\f8I_\u0539\xfd\xf8\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4x\x1b\x15\x01dz.\x06\xc0\xedC\xff\x19\u007f\xcc\xec5\xe1p\v\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4x/R\xf0\xa6v\xc7w\x16\xd5t\xc8\x1e\xc4hO\x9a\x02\n\x97\x89.\x14\xe2\x06\xb70\xad\x80\x00\u07d4x5]\xf0\xa20\xf8=\x03,p1TAM\xe3\xee\u06b5W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4x6\xf7\xefk\u01fd\x0f\xf3\xac\xafD\x9c\x84\xddk\x1e,\x93\x9f\x89\xe0\x8d\xe7\xa9,\xd9|\x00\x00\u07d4x7\xfc\xb8v\xda\x00\xd1\xeb;\x88\xfe\xb3\xdf?\xa4\x04/\xac\x82\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4x>\uc2a5\xda\xc7{.f#\xedQ\x98\xa41\xab\xba\xee\a\x89\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4x\\\x8e\xa7t\xd70D\xa74\xfay\n\x1b\x1et>w\xed|\x89\f\xf1Rd\f\\\x83\x00\x00\u07d4x`\xa3\xde8\xdf8*\xe4\xa4\xdc\xe1\x8c\f\a\xb9\x8b\xce=\xfa\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94xcCq\xe1s\x04\xcb\xf39\xb1E*L\xe48\xdcvL\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4xd\u0719\x9f\xe4\xf8\xe0\x03\xc0\xf4=\xec\u00da\xae\x15\"\xdc\x0f\x89\x05\x1e\x10+\xd8\xec\xe0\x00\x00\u07d4xtj\x95\x8d\xce\xd4\xc7d\xf8vP\x8cAJh4,\uce49\x02\xbe7O\xe8\xe2\xc4\x00\x00\xe0\x94x}1?\xd3k\x05>\xee\xae\xdb\xcet\xb9\xfb\x06x32\x89\x8a\x05\xc0X\xb7\x84'\x19`\x00\x00\u07d4x\x85\x9c[T\x8bp\r\x92\x84\xce\xe4\xb6c=GJ\x8a\x04{\x92\xc4\x15B$-\n\b\xc7\x0f\x99\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4x\u03c36\xb3(\xdb=\x87\x81:G+\x9e\x89\xb7^\f\xf3\xbc\x8965\u026d\xc5\u07a0\x00\x00\u07d4x\xd4\xf8\xc7\x1c\x1eh\xa6\x9a\x98\xf5/\xcbE\u068a\xf5n\xa1\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4x\xdf&\x81\xd6\xd6\x02\xe2!B\xd5A\x16\u07a1]EIW\xaa\x89\x10'\x94\xad \xdah\x00\x00\u07d4x\xe0\x8b\xc53A<&\u2473\x14?\xfa|\u026f\xb9{x\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4x\xe8?\x80\xb3g\x8cz\nN>\x8c\x84\xdc\xcd\xe0dBbw\x89a\t=|,m8\x00\x00\u07d4x\xf5\xc7G\x85\xc5f\x8a\x83\x80r\x04\x8b\xf8\xb4SYM\u06ab\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4y\x0f\x91\xbd]\x1c\\\xc4s\x9a\xe9\x13\x00\u06c9\xe1\xc10<\x93\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\x17\u5f42\xa9y\x0f\xd6P\xd0C\xcd\xd90\xf7y\x963\u06c9\xd8\xd4`,&\xbfl\x00\x00\u07d4y\x19\xe7b\u007f\x9b}T\xea;\x14\xbbM\xd4d\x9fO9\xde\xe0\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4y\x1f`@\xb4\xe3\xe5\r\xcf5S\xf1\x82\u0357\xa9\x060\xb7]\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4y0\xc2\xd9\xcb\xfa\x87\xf5\x10\xf8\xf9\x87w\xff\x8a\x84H\xcaV)\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4yE)\u041d\x01rq5\x970\x02pu\xb8z\xd8=\xaen\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4yKQ\u00deS\xd9\xe7b\xb0a;\x82\x9aD\xb4r\xf4\xff\xf3\x89$5\xe0dxA\u0300\x00\xe0\x94yU\x1c\xed\xe3v\xf7G\xe3ql\x8dy@\rvm.\x01\x95\x8a\t\xcb7\xaf\xa4\xffxh\x00\x00\u07d4y^\xbc&&\xfc9\xb0\xc8b\x94\xe0\xe87\xdc\xf5#U0\x90\x8965\u026d\xc5\u07a0\x00\x00\u07d4yn\xbb\xf4\x9b>6\xd6v\x94\xady\xf8\xff6vz\xc6\xfa\xb0\x89\x03K\xc4\xfd\xde'\xc0\x00\x00\u07d4yo\x87\xbaaz)0\xb1g\v\xe9.\xd1(\x1f\xb0\xb3F\xe1\x89\x06\xf5\xe8o\xb5((\x00\x00\u07d4yt'\xe3\xdb\xf0\xfe\xaez%\x06\xf1-\xf1\xdc@2n\x85\x05\x8965\u026d\xc5\u07a0\x00\x00\u07d4yu\x10\xe3\x86\xf5c\x93\xce\xd8\xf4w7\x8aDLHO}\xad\x8965\u026d\xc5\u07a0\x00\x00\u07d4y{\xb7\xf1W\xd9\xfe\xaa\x17\xf7m\xa4\xf7\x04\xb7M\xc1\x03\x83A\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4y\x88\x90\x131\xe3\x87\xf7\x13\xfa\u03b9\x00\\\xb9\xb6Q6\xeb\x14\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4y\x89\u041f8&\xc3\u5bccu*\x81\x15r:\x84\xd8\tp\x89\x16\x86\xf8aL\xf0\xad\x00\x00\xe0\x94y\x95\xbd\x8c\xe2\xe0\xc6{\xf1\u01e51\xd4w\xbc\xa1\xb2\xb9ua\x8a\x01BH\xd6\x17\x82\x9e\xce\x00\x00\u07d4y\xae\xb3Ef\xb9t\xc3ZX\x81\xde\xc0 \x92}\xa7\xdf]%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\xb1 \xeb\x88\x06s#!(\x8fgZ'\xa9\"_\x1c\xd2\ub245\xa0\xbf7\xde\xc9\xe4\x00\x00\u07d4y\xb4\x8d-a7\u00c5Ma\x1c\x01\xeaBBz\x0fY{\xb7\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4y\xb8\xaa\xd8y\xdd0V~\x87x\xd2\xd21\xc8\xf3z\xb8sN\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\xbf/{n2\x8a\xaf&\xe0\xbb\t?\xa2-\xa2\x9e\xf2\xf4q\x89a\t=|,m8\x00\x00\u07d4y\xc10\xc7b\xb8v[\x19\u04ab\u0260\x83\xab\x8f:\xady@\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4y\xc1\xbe\x19q\x1fs\xbe\xe4\xe61j\xe7T\x94Y\xaa\u03a2\xe0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4y\xc6\x00/\x84R\xca\x15\u007f\x13\x17\xe8\n/\xaf$GUY\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4y\xca\xc6IO\x11\xef'\x98t\x8c\xb52\x85\xbd\x8e\"\xf9|\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4y\u03e9x\n\xe6\xd8{,1\x88?\t'i\x86\u021ag5\x8965\u026d\xc5\u07a0\x00\x00\u07d4y\u06e2VG-\xb4\xe0X\xf2\xe4\xcd\xc3\xeaN\x8aBw83\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4y\xed\x10\xcf\x1fm\xb4\x82\x06\xb5\t\x19\xb9\xb6\x97\b\x1f\xbd\xaa\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u0794y\xf0\x8e\x01\xce\t\x88\xe6<\u007f\x8f)\b\xfa\xdeC\xc7\xf9\xf5\u0248\xfc\x93c\x92\x80\x1c\x00\x00\u07d4y\xfdmH1Pf\xc2\x04\xf9e\x18i\xc1\tl\x14\xfc\x97\x81\x89lk\x93[\x8b\xbd@\x00\x00\u0794y\xff\xb4\xac\x13\x81*\vx\u0123{\x82u\">\x17k\xfd\xa5\x88\xf0\x15\xf2W6B\x00\x00\u07d4z\x05\x89\xb1C\xa8\xe5\xe1\a\u026cf\xa9\xf9\xf8Yz\xb3\u7ac9Q\xe92\xd7n\x8f{\x00\x00\u07d4z\nx\xa9\xcc9?\x91\xc3\xd9\xe3\x9ak\x8c\x06\x9f\a^k\xf5\x89Hz\x9a0E9D\x00\x00\u07d4z\x13p\xa7B\xec&\x87\xe7a\xa1\x9a\u0167\x942\x9e\xe6t\x04\x89\xa2\xa12ga\xe2\x92\x00\x00\xe0\x94z-\xfcw\x0e$6\x811\xb7\x84w\x95\xf2\x03\xf3\xd5\r[V\x8a\x02i\xfe\xc7\xf06\x1d \x00\x00\u07d4z3\x83N\x85\x83s>-R\xae\xadX\x9b\u046f\xfb\x1d\xd2V\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94z6\xab\xa5\xc3\x1e\xa0\xca~'{\xaa2\xecF\u0393\xcfu\x06\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94z8\x11\"\xba\xday\x1az\xb1\xf6\x03}\xac\x80C'S\xba\xad\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94zH\xd8w\xb6:\x8f\x8f\x93\x83\xe9\xd0\x1eS\xe8\fR\x8e\x95_\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4zO\x9b\x85\x06\x90\xc7\xc9F\x00\xdb\xee\f\xa4\xb0\xa4\x11\xe9\xc2!\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4zc\x86\x9f\xc7g\xa4\u01b1\xcd\x0e\x06I\xf3cL\xb1!\xd2K\x89\x043\x87Oc,\xc6\x00\x00\u07d4zg\xdd\x04:PO\xc2\xf2\xfcq\x94\xe9\xbe\xcfHL\xec\xb1\xfb\x89\r\x8drkqw\xa8\x00\x00\xe0\x94zk&\xf48\u0663RD\x91U\xb8\x87l\xbd\x17\xc9\u065bd\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4zmx\x1cw\u013a\x1f\xca\xdfhsA\xc1\xe3\x17\x99\xe9='\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4zph\xe1\xc37\\\x0eY\x9d\xb1\xfb\xe6\xb2\xea#\xb8\xf4\a\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4zt\xce\xe4\xfa\x0fcp\xa7\x89O\x11l\xd0\f\x11G\xb8>Y\x89+^:\xf1k\x18\x80\x00\x00\u07d4zy\xe3\x0f\xf0W\xf7\n=\x01\x91\xf7\xf5?v\x157\xaf}\xff\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94zzO\x80sW\xa4\xbb\xe6\x8e\x1a\xa8\x0692\x10\xc4\x11\u0333\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4z\x85c\x86y\x01 o?+\xf0\xfa>\x1c\x81\t\u02bc\u0345\x89\amA\xc6$\x94\x84\x00\x00\xe0\x94z\x87\x97i\n\xb7{Tp\xbf|\f\x1b\xbaa%\b\xe1\xac}\x8a\x01\xe0\x92\x96\xc37\x8d\xe4\x00\x00\u07d4z\x8c\x89\xc0\x14P\x9dV\u05f6\x810f\x8f\xf6\xa3\xec\xecsp\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94z\x94\xb1\x99\x92\u03b8\xcec\xbc\x92\xeeKZ\xde\xd1\fM\x97%\x8a\x03\x8d\x1a\x80d\xbbd\xc8\x00\x00\u07d4z\xa7\x9a\xc0C\x16\u030d\b\xf2\x00e\xba\xa6\xd4\x14(\x97\xd5N\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4z\xadM\xbc\u04ec\xf9\x97\u07d3XiV\xf7+d\u062d\x94\xee\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94z\xb2V\xb2\x04\x80\n\xf2\x017\xfa\xbc\xc9\x16\xa22Xu%\x01\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4z\xbaV\xf6:H\xbc\b\x17\u05b9p9\x03\x9az\xd6/\xae.\x89 \x86\xac5\x10R`\x00\x00\xe0\x94z\xbb\x10\xf5\xbd\x9b\xc3;\x8e\xc1\xa8-d\xb5[k\x18wuA\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4z\u010d@\xc6d\u031am\x89\xf1\xc5\xf5\xc8\n\x1cp\xe7D\u6263\x10b\xbe\xee\xd7\x00\x00\x00\u07d4z\u014fo\xfcO\x81\a\xaen07\x8eN\x9f\x99\xc5\u007f\xbb$\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4z\xd3\xf3\aao\x19\u0731C\xe6DM\xab\x9c<3a\x1fR\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4z\xd8,\xae\xa1\xa8\xb4\xed\x051\x9b\x9c\x98p\x17<\x81N\x06\xee\x89!d\xb7\xa0J\u0220\x00\x00\u07d4z\xde]f\xb9D\xbb\x86\f\x0e\xfd\xc8bv\u054fFS\xf7\x11\x89lk\x93[\x8b\xbd@\x00\x00\u07d4z\xdf\xed\xb0m\x91\xf3\xccs\x90E\v\x85U\x02p\x88<{\xb7\x89\x11x\xfa@Q]\xb4\x00\x00\u07d4z\xe1\xc1\x9eS\xc7\x1c\xeeLs\xfa\xe2\xd7\xfcs\xbf\x9a\xb5\u348965\u026d\xc5\u07a0\x00\x00\u07d4z\xe6Y\xeb;\xc4hR\xfa\x86\xfa\xc4\xe2\x1cv\x8dP8\x89E\x89\x0f\x81\f\x1c\xb5\x01\xb8\x00\x00\u07d4z\xea%\xd4+&\x12(n\x99\xc56\x97\u01bcA\x00\xe2\u06ff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4z\xef{U\x1f\v\x9cF\xe7U\xc0\xf3\x8e[:s\xfe\x11\x99\xf5\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4{\v1\xffn$t^\xad\x8e\u067b\x85\xfc\v\xf2\xfe\x1dU\u0509+^:\xf1k\x18\x80\x00\x00\xe0\x94{\x0f\xea\x11v\xd5!Y3:\x14<)IC\xda6\xbb\u0774\x8a\x01\xfc}\xa6N\xa1L\x10\x00\x00\u07d4{\x11g<\xc0\x19bk)\f\xbd\xce&\x04o~m\x14\x1e!\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4{\x12!b\xc9\x13\xe7\x14l\xad\v~\xd3z\xff\xc9*\v\xf2\u007f\x89Q\xaf\tk#\x01\u0440\x00\u07d4{\x1b\xf5:\x9c\xbe\x83\xa7\u07a44W\x9f\xe7*\xac\x8d*\f\u0409\n\xd4\xc81j\v\f\x00\x00\u07d4{\x1d\xaf\x14\x89\x1b\x8a\x1e\x1b\xd4)\u0633k\x9aJ\xa1\u066f\xbf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4{\x1f\xe1\xabM\xfd\x00\x88\xcd\xd7\xf6\x01c\xefY\xec*\xee\x06\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4{%\xbb\x9c\xa8\xe7\x02!~\x933\"RP\xe5<6\x80MH\x89e\xea=\xb7UF`\x00\x00\u07d4{'\xd0\xd1\xf3\xdd<\x14\x02\x94\xd0H\x8bx>\xbf@\x15'}\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94{@\a\xc4^ZW?\u06f6\xf8\xbdtk\xf9J\xd0J<&\x8a\x038!\xf5\x13]%\x9a\x00\x00\u07d4{C\xc7\xee\xa8\xd6#U\xb0\xa8\xa8\x1d\xa0\x81\xc6Dk3\xe9\xe0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4{M*8&\x90i\xc1\x85Ww\rY\x1d$\xc5\x12\x1f^\x83\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94{au\xec\x9b\xef\xc78$\x955\xdd\xde4h\x8c\xd3n\xdf%\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94{f\x12hy\x84M\xfa4\xfee\xc9\xf2\x88\x11\u007f\xef\xb4I\xad\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4{j\x84q\x8d\xd8nc3\x84)\xac\x81\x1d|\x8a\x86\x0f!\xf1\x89a\t=|,m8\x00\x00\xe0\x94{q,z\xf1\x16v\x00jf\xd2\xfc\\\x1a\xb4\xc4y\xce`7\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4{s$-u\u029a\xd5X\xd6P)\r\xf1v\x92\xd5L\u0638\x89lnY\xe6|xT\x00\x00\u07d4{v\x1f\xeb\u007f\u03e7\xde\xd1\xf0\xeb\x05\x8fJ`\v\xf3\xa7\b\u02c9\xf9]\xd2\xec'\xcc\xe0\x00\x00\xe0\x94{\x82|\xae\u007f\xf4t\t\x18\xf2\xe00\xab&\u02d8\xc4\xf4l\xf5\x8a\x01\x94hL\v9\xde\x10\x00\x00\xe0\x94{\x892\x86B~r\xdb!\x9a!\xfcM\xcd_\xbfY(<1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4{\x92&\xd4o\xe7Q\x94\v\xc4\x16\xa7\x98\xb6\x9c\xcf\r\xfa\xb6g\x89\u3bb5sr@\xa0\x00\x00\u07d4{\x98\xe2<\xb9k\xee\xe8\n\x16\x80i\ube8f \xed\xd5\\\u03c9\v\xa0\xc9\x15\x87\xc1J\x00\x00\u07d4{\xb0\xfd\xf5\xa6c\xb5\xfb\xa2\x8d\x9c\x90*\xf0\xc8\x11\xe2R\xf2\x98\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4{\xb9W\x1f9K\v\x1a\x8e\xbaVd\xe9\u0635\xe8@g{\xea\x89\x01\x11du\x9f\xfb2\x00\x00\xe0\x94{\xb9\x84\xc6\u06f9\xe2y\x96j\xfa\xfd\xa5\x9c\x01\xd0&'\xc8\x04\x8a\x01\xb4d1\x1dE\xa6\x88\x00\x00\u07d4{\xbb\xec^p\xbd\xea\u063b2\xb4(\x05\x98\x8e\x96H\xc0\xaa\x97\x8966\u05ef^\u024e\x00\x00\u07d4{\xca\x1d\xa6\xc8\nf\xba\xa5\xdbZ\u0245A\u013e'kD}\x89$\xcf\x04\x96\x80\xfa<\x00\x00\u07d4{\u0772\xee\x98\xde\x19\xeeL\x91\xf6a\xee\x8eg\xa9\x1d\x05K\x97\x8965\u026d\xc5\u07a0\x00\x00\u0794{\xe2\xf7h\f\x80-\xa6\x15L\x92\xc0\x19J\xe72Qzqi\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4{\xe7\xf2Eiq\x88;\x9a\x8d\xbeL\x91\xde\xc0\x8a\xc3N\x88b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4{\xe8\u0334\xf1\x1bf\xcan\x1dW\xc0\xb59b!\xa3\x1b\xa5:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94{\xeb\x81\xfb/^\x91Rk*\xc9y^v\u019b\xcf\xf0K\xc0\x8a\x0e\xb2.yO\n\x8d`\x00\x00\u07d4|\b\x83\x05L-\x02\xbcz\x85+\x1f\x86\xc4'w\xd0\xd5\xc8V\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4|\x0f^\a C\xc9\xeet\x02B\x19~x\xccK\x98\xcd\xf9`\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|\x1d\xf2JO\u007f\xb2\u01f4r\xe0\xbb\x00l\xb2}\xcd\x16AV\x8965\u026d\xc5\u07a0\x00\x00\u07d4|)\xd4}W\xa73\xf5k\x9b!pc\xb5\x13\xdc;1Y#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4|+\x96\x03\x88JO.FN\u03b9}\x17\x93\x8d\x82\x8b\xc0,\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4|8,\x02\x96a.N\x97\xe4@\xe0-8q';U\xf5;\x89\n\xb6@9\x12\x010\x00\x00\u07d4|>\xb7\x13\xc4\xc9\xe08\x1c\xd8\x15L|\x9a}\xb8d\\\xde\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|D\x01\xae\x98\xf1.\xf6\xde9\xae$\u03df\xc5\x1f\x80\xeb\xa1k\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|E\xf0\xf8D*V\xdb\u04dd\xbf\x15\x99\x95A\\R\xedG\x9b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94|S-\xb9\xe0\xc0l&\xfd@\xac\xc5j\xc5\\\x1e\xe9-<:\x8a?\x87\bW\xa3\xe0\xe3\x80\x00\x00\u07d4|`\xa0_zJ_\x8c\xf2xC\x916.uZ\x83A\xefY\x89f\x94\xf0\x18*7\xae\x00\x00\u07d4|`\xe5\x1f\v\xe2(\xe4\xd5o\xdd)\x92\xc8\x14\xdaw@\u01bc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|i$\xd0|>\xf5\x89\x19f\xfe\nxV\xc8{\xef\x9d 4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94|\x8b\xb6Zo\xbbI\xbdA3\x96\xa9\xd7\xe3\x10S\xbb\xb3z\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94|\x9a\x11\f\xb1\x1f%\x98\xb2\xb2\x0e,\xa4\x002^A\xe9\xdb3\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4|\xbc\xa8\x8f\xcaj\x00`\xb9`\x98\\\x9a\xa1\xb0%4\xdc\"\b\x89\x19\x12z\x13\x91\xea*\x00\x00\u07d4|\xbe\xb9\x992\xe9~n\x02\x05\x8c\xfcb\u0432k\xc7\u0325+\x89lk\x93[\x8b\xbd@\x00\x00\u07d4|\xc2Jj\x95\x8c \xc7\xd1$\x96`\xf7Xb&\x95\v\r\x9a\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4|\xd2\x0e\u0335\x18\xb6\f\xab\t[r\x0fW\x15p\u02aaD~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4|\xd5\xd8\x1e\xab7\xe1\x1ebv\xa3\xa1\t\x12Q`~\r~8\x89\x03hM^\xf9\x81\xf4\x00\x00\u07d4|\xdft!9E\x95=\xb3\x9a\xd0\xe8\xa9x\x1a\xddy.M\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4|\xe4hdF\U000547be\xd6r\x15\xeb\rZ\x1d\xd7,\x11\xb8\x89x9\xd3!\xb8\x1a\xb8\x00\x00\u07d4|\xefMC\xaaA\u007f\x9e\xf8\xb7\x87\xf8\xb9\x9dS\xf1\xfe\xa1\ue209g\x8a\x93 b\xe4\x18\x00\x00\u07d4}\x03P\xe4\v3\x8d\xdasfa\x87+\xe3?\x1f\x97R\xd7U\x89\x02\xb4\xf5\xa6\U00051500\x00\xe0\x94}\x04\xd2\xed\xc0X\xa1\xaf\xc7a\xd9\u025a\xe4\xfc\\\x85\xd4\u0226\x8aB\xa9\xc4g\\\x94g\xd0\x00\x00\u07d4}\v%^\xfbW\xe1\x0fp\b\xaa\"\xd4\x0e\x97R\xdf\xcf\x03x\x89\x01\x9f\x8euY\x92L\x00\x00\xe0\x94}\x13\xd6pX\x84\xab!W\u074d\xccpF\xca\xf5\x8e\xe9K\xe4\x8a\x1d\r\xa0|\xbb>\xe9\xc0\x00\x00\u07d4}'>c~\xf1\xea\u0101\x11\x94\x13\xb9\x1c\x98\x9d\xc5\xea\xc1\"\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4}*R\xa7\xcf\f\x846\xa8\xe0\a\x97kl&\xb7\"\x9d\x1e\x15\x89\x17\xbf\x06\xb3*$\x1c\x00\x00\u07d4}4\x805i\xe0\v\u05b5\x9f\xff\b\x1d\xfa\\\n\xb4\x19zb\x89\\\xd8|\xb7\xb9\xfb\x86\x00\x00\u07d4}4\xffY\xae\x84\nt\x13\u01baL[\xb2\xba,u\xea\xb0\x18\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4}9(R\xf3\xab\xd9/\xf4\xbb[\xb2l\xb6\bt\xf2\xbeg\x95\x8966\xc2^f\xec\xe7\x00\x00\u07d4}DRg\u015a\xb8\u04a2\xd9\xe7\t\x99\x0e\th%\x80\u011f\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94}U\x13\x97\xf7\x9a)\x88\xb0d\xaf\xd0\xef\xeb\xee\x80,w!\xbc\x8a\bW\xe0\xd6\xf1\xdav\xa0\x00\x00\u07d4}Z\xa3?\xc1KQ\x84\x1a\x06\x90n\xdb+\xb4\x9c*\x11ri\x89\x10D\x00\xa2G\x0eh\x00\x00\xe0\x94}]/s\x94\x9d\xad\xda\bV\xb2\x06\x98\x9d\xf0\a\x8dQ\xa1\xe5\x8a\x02\xc4:H\x1d\xf0M\x01wb\xed\xcb\\\xaab\x9bZ\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4~\x8f\x96\xcc)\xf5{\tu\x12\f\xb5\x93\xb7\u0743=`kS\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4~\x97*\x8a|*D\xc9;!Cl8\xd2\x1b\x92R\xc3E\xfe\x89a\t=|,m8\x00\x00\u07d4~\x99\u07fe\x98\x9d;\xa5)\u0457Q\xb7\xf41\u007f\x89S\xa3\xe2\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4~\xa0\xf9n\xe0\xa5s\xa30\xb5h\x97v\x1f=L\x010\xa8\xe3\x89Hz\x9a0E9D\x00\x00\u0794~\xa7\x91\xeb\xab\x04E\xa0\x0e\xfd\xfcNJ\x8e\x9a~ue\x13m\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4~\xab\xa05\xe2\xaf7\x93\xfdtgK\x10%@\xcf\x19\n\u0779\x89E\x02l\x83[`D\x00\x00\xe0\x94~\xb4\xb0\x18\\\x92\xb6C\x9a\b\xe72!h\xcb5<\x8awJ\x8a\x02'\x19l\xa0I\x83\xca\x00\x00\xe0\x94~\xbd\x95\xe9\xc4p\xf7(5\x83\xdcn\x9d,M\xce\v\ua3c4\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4~\u0425\xa8G\xbe\xf9\xa9\xda|\xba\x1dd\x11\xf5\xc3\x161&\x19\x89\x02(\xeb7\xe8u\x1d\x00\x00\u07d4~\xda\xfb\xa8\x98K\xafc\x1a\x82\vk\x92\xbb\xc2\xc56U\xf6\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4~\xdb\x02\xc6\x1a\"r\x87a\x1a\xd9Pici\xccNdzh\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4~\xe5\u0280]\xce#\xaf\x89\xc2\xd4D\xe7\xe4\af\xc5Lt\x04\x89\r\v\xd4\x12\xed\xbd\x82\x00\x00\xe0\x94~\xe6\x04\u01e9\xdc)\t\xce2\x1d\u6e72OWgWuU\x8a\x01+\xf9\u01d8\\\xf6-\x80\x00\u07d4~\xf1o\xd8\xd1[7\x8a\x0f\xba0k\x8d\x03\u0758\xfc\x92a\x9f\x89%\xf2s\x93=\xb5p\x00\x00\u07d4~\xf9\x8bR\xbe\xe9S\xbe\xf9\x92\xf3\x05\xfd\xa0'\xf8\x91\x1cXQ\x89\x1b\xe7\" i\x96\xbc\x80\x00\u07d4~\xfc\x90vj\x00\xbcR7,\xac\x97\xfa\xbd\x8a<\x83\x1f\x8e\u0349\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4~\xfe\xc0\xc6%<\xaf9\u007fq(|\x1c\a\xf6\xc9X+[\x86\x89\x1a,\xbc\xb8O0\u0540\x00\u07d4\u007f\x01\xdc|7G\xca`\x8f\x98=\xfc\x8c\x9b9\xe7U\xa3\xb9\x14\x89\v8l\xad_zZ\x00\x00\u07d4\u007f\x06b\xb4\x10)\x8c\x99\xf3\x11\u04e1EJ\x1e\xed\xba/\xeav\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\u007f\x06\u021dY\x80\u007f\xa6\v\xc6\x016\xfc\xf8\x14\u02ef%C\xbd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u007f\v\x90\xa1\xfd\u050f'\xb2h\xfe\xb3\x83\x82\xe5]\xdbP\xef\x0f\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\u007f\x0e\xc3\u06c0F\x92\xd4\xd1\xea2E6Z\xab\x05\x90\a[\u0109\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u007f\x0f\x04\xfc\xf3zS\xa4\xe2N\xden\x93\x10Nx\xbe\x1d<\x9e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u007f\x13\xd7`I\x8dq\x93\xcahY\xbc\x95\xc9\x018d#\xd7l\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\u007f\x15\n\xfb\x1aw\u00b4Y(\xc2h\xc1\u9f74d\x1dG\u0609lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\x16\x19\x98\x8f7\x15\xe9O\xf1\xd2S&-\xc5X\x1d\xb3\xde\x1c\x890\xca\x02O\x98{\x90\x00\x00\u07d4\u007f\x1c\x81\xee\x16\x97\xfc\x14K|\v\xe5I;V\x15\xae\u007f\xdd\u0289\x1b\x1d\xaba\u04ead\x00\x00\u07d4\u007f#\x82\xff\xd8\xf89VFy7\xf9\xbar7F#\xf1\x1b8\x89 \x86\xac5\x10R`\x00\x00\u07d4\u007f7\t9\x1f?\xbe\xba5\x92\xd1u\xc7@\xe8z\tT\x1d\x02\x89\x1a\x05V\x90\xd9\u06c0\x00\x00\u07d4\u007f8\x9c\x12\xf3\xc6\x16OdFVlwf\x95\x03\xc2y%'\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\xe0\x94\u007f:\x1eE\xf6~\x92\u0200\xe5s\xb43y\xd7\x1e\xe0\x89\xdbT\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\u007f=r\x03\u0224G\xf7\xbf6\u060a\xe9\xb6\x06*^\xeex\xae\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\u007fF\xbb%F\r\xd7\xda\xe4!\x1c\xa7\xf1Z\xd3\x12\xfc}\xc7\\\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\u007fI\xe7\xa4&\x98\x82\xbd\x87\"\u0526\xf5f4v)b@y\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u007fI\xf2\a&G\x1a\xc1\u01e8>\xf1\x06\xe9w\\\xebf%f\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\u007fK^'\x85x\xc0F\xcc\xea\xf6W0\xa0\xe0h2\x9e\u0576\x89e\xea=\xb7UF`\x00\x00\u07d4\u007fOY;a\x8c3\v\xa2\xc3\xd5\xf4\x1e\xce\xeb\x92\xe2~Bl\x89\x96n\xdcuk|\xfc\x00\x00\u07d4\u007fT\x14\x91\u04ac\x00\xd2a/\x94\xaa\u007f\v\xcb\x01FQ\xfb\u0509\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\u007fZ\xe0Z\xe0\xf8\xcb\xe5\xdf\xe7!\xf0D\u05e7\xbe\xf4\xc2y\x97\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\u007f`:\xec\x17Y\xea_\a\xc7\xf8\xd4\x1a\x14(\xfb\xba\xf9\xe7b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u007falo\x00\x8a\u07e0\x82\xf3M\xa7\xd0e\x04`6\x80u\xfb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u007fa\xfal\xf5\xf8\x98\xb4@\xda\u016b\xd8`\rmi\x1f\xde\xf9\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94\u007fe\\g\x89\xed\xdfE\\\xb4\xb8\x80\x99r\x0698\x9e\ubb0a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u007fk(\u0204!\xe4\x85~E\x92\x81\u05c4ai$\x89\xd3\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007fn\xfboC\x18\x87m.\xe6$\xe2u\x95\xf4DF\xf6\x8e\x93\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u007fq\x92\xc0\xdf\x1c}\xb6\xd9\xede\xd7\x11\x84\xd8\xe4\x15Z\x17\xba\x89\x04Sr\x8d3\x94,\x00\x00\u07d4\u007fz:!\xb3\xf5\xa6]\x81\xe0\xfc\xb7\xd5-\xd0\n\x1a\xa3m\xba\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u007f\x8d\xbc\xe1\x80\xed\x9cV65\xaa\xd2\xd9{L\xbcB\x89\x06\u0649\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\u007f\x99=\xdb~\x02\u0082\xb8\x98\xf6\x15_h\x0e\xf5\xb9\xaf\xf9\a\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\u007f\x9f\x9bV\xe4(\x9d\xfbX\xe7\x0f\xd5\xf1*\x97\xb5m5\u01a5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u007f\xa3~\xd6x\x87u\x1aG\x1f\x0e\xb3\x06\xbeD\xe0\xdb\xcd`\x89\x899vt\u007f\xe1\x1a\x10\x00\x00\u07d4\u007f\xaa0\xc3\x15\x19\xb5\x84\xe9rP\xed*<\xf38^\xd5\xfdP\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xcf[\xa6fo\x96lTH\xc1{\xf1\xcb\v\xbc\xd8\x01\x9b\x06\x89\x05k\xc3\u042e\xbeI\x80\x00\xe0\x94\u007f\xd6y\xe5\xfb\r\xa2\xa5\xd1\x16\x19M\xcbP\x83\x18\xed\u0140\xf3\x8a\x01c\x9eI\xbb\xa1b\x80\x00\x00\u07d4\u007f\u06e01\u01cf\x9c\tmb\xd0Z6\x9e\uac3c\xccU\u5257\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\u007f\xdb\u00e8D\xe4\r\x96\xb2\xf3\xa652.`e\xf4\xca\x0e\x84\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xdf\u020dx\xbf\x1b(Z\xc6O\x1a\xdb5\xdc\x11\xfc\xb09Q\x89|\x06\xfd\xa0/\xb06\x00\x00\u07d4\u007f\xea\x19b\xe3]b\x05\x97h\xc7I\xbe\u0756\u02b90\xd3x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xef\x8c8w\x9f\xb3\a\xeco\x04K\xeb\xe4\u007f<\xfa\xe7\x96\xf1\x89\t#@\xf8l\xf0\x9e\x80\x00\u07d4\u007f\xf0\xc6?p$\x1b\xec\xe1\x9bs~SA\xb1+\x10\x901\u0609\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\u007f\xfa\xbf\xbc9\f\xbeC\u0389\x18\x8f\bh\xb2}\xcb\x0f\f\xad\x8a\x01YQ\x82\"K&H\x00\x00\xe0\x94\u007f\xfd\x02\xed7\fp`\xb2\xaeS\xc0x\xc8\x01!\x90\u07fbu\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794\x80\x02*\x12\a\xe9\x10\x91\x1f\xc9(I\xb0i\xab\f\xda\xd0C\u04c8\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x80\t\xa7\xcb\u0452\xb3\xae\u052d\xb9\x83\xd5(ER\xc1ltQ\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80\x0e}c\x1cnW:\x903/\x17\xf7\x1f_\u045bR\x8c\xb9\x89\b=lz\xabc`\x00\x00\u07d4\x80\x15m\x10\ufa320\u0254\x10c\r7\xe2i\xd4\t<\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\x172\xa4\x81\u00c0\xe5~\xd6-l)\u0799\x8a\xf3\xfa;\x13\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x1de\xc5\x18\xb1\x1d\x0e?OG\x02!Ap\x13\xc8\xe5>\u0149\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80&CZ\xacr\x8dI{\x19\xb3\xe7\xe5|(\xc5c\x95O+\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x80-\xc3\xc4\xff-}\x92^\u215fJ\x06\u05fa`\xf10\x8c\x89\x05P\x94\f\x8f\xd3L\x00\x00\u07d4\x800\xb1\x11\u0198?\x04\x85\u076c\xa7b$\xc6\x18\x064x\x9f\x89\x04V9\x18$O@\x00\x00\u07d4\x805\xbc\xff\xae\xfd\xee\xea5\x83\fI}\x14(\x9d6 #\u0789\x10CV\x1a\x88)0\x00\x00\u07d4\x805\xfeNkj\xf2z\u44a5xQ^\x9d9\xfao\xa6[\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80C\xed\"\xf9\x97\u58a4\xc1n6D\x86\xaed\x97V\x92\u0109=I\x04\xff\xc9\x11.\x80\x00\u07d4\x80C\xfd\u043cL\x97=\x16c\xd5_\xc15P\x8e\xc5\xd4\xf4\xfa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x80L\xa9IrcOc:Q\xf3V\v\x1d\x06\xc0\xb2\x93\xb3\xb1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x80R-\u07d4N\xc5.'\xd7$\xedL\x93\xe1\xf7\xbe`\x83\u0589\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x80Y\x1aB\x17\x9f4\xe6M\x9d\xf7]\xcdF;(hoUt\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x80\\\xe5\x12\x97\xa0y;\x81 g\xf0\x17\xb3\xe7\xb2\u07db\xb1\xf9\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x80]\x84o\xb0\xbc\x02\xa73r&\u0585\xbe\x9e\xe7s\xb9\x19\x8a\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x80c7\x9a{\xf2\u02d2:\x84\xc5\t>h\xda\xc7\xf7T\x81\u0149\x11v\x10.n2\xdf\x00\x00\u07d4\x80hTX\x8e\xcc\xe5AI_\x81\u008a)\x03s\xdf\x02t\xb2\x89\x1f\x8c\xdf\\n\x8dX\x00\x00\u07d4\x80oD\xbd\xebh\x807\x01^\x84\xff!\x80I\xe3\x823*3\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\x80tF\x18\xde9jT1\x97\xeeH\x94\xab\xd0c\x98\xdd|'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80w\xc3\xe4\xc4EXn\tL\xe1\x02\x93\u007f\xa0[s{V\x8c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x90\u007fY1H\xb5|F\xc1w\xe2=%\xab\u012a\xe1\x83a\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x97s\x16\x94NYB\xe7\x9b\x0e:\xba\u04cd\xa7F\be\x19\x89\x02\x1auJm\xc5(\x00\x00\xe0\x94\x80\xa0\xf6\xcc\x18l\xf6 \x14\x00sn\x06Z9\x1fR\xa9\xdfJ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x80\xab\xecZ\xa3n\\\x9d\t\x8f\x1b\x94(\x81\xbdZ\xca\u0196=\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xb2=8\v\x82\\F\xe098\x99\xa8UVF-\xa0\u1309lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xb4-\xe1p\xdb\xd7#\xf4T\xe8\x8fw\x16E-\x92\x98P\x92\x89\x10F#\xc0v-\xd1\x00\x00\u07d4\x80\xb7\x9f3\x83\x90\u047a\x1b77\xa2\x9a\x02W\xe5\xd9\x1e\a1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x80\xbf\x99^\u063a\x92p\x1d\x10\xfe\u011f\x9e}\x01M\xbe\xe0&\x89\x1f\x047\xca\x1a~\x12\x80\x00\u07d4\x80\xc0N\xfd1\x0fD\x04\x83\xc7?tK[\x9edY\x9c\xe3\xec\x89A\rXj \xa4\xc0\x00\x00\u07d4\x80\u00e9\xf6\x95\xb1m\xb1Yr\x86\u0473\xa8\xb7il9\xfa'\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\xc5>\xe7\xe35\u007f\x94\xce\rxh\x00\x9c \x8bJ\x13\x01%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xcc!\xbd\x99\xf3\x90\x05\u014f\xe4\xa4H\x90\x92 !\x8ff\u02c966\xc9yd6t\x00\x00\u07d4\x80\xd5\xc4\fY\xc7\xf5N\xa3\xa5_\xcf\xd1uG\x1e\xa3P\x99\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x80\xda/\u0762\x9a\x9e'\xf9\xe1\x15\x97^i\xae\x9c\xfb\xf3\xf2~\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x80\xe7\xb3 R0\xa5f\xa1\xf0a\xd9\"\x81\x9b\xb4\xd4\u04a0\xe1\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x80\xea\x1a\xcc\x13n\xcaKh\xc8B\xa9Z\xdfk\u007f\xee~\xb8\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80\xf0z\xc0\x9e{,<\n=\x1e\x94\x13\xa5D\xc7:A\xbe\u02c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x81\r\xb2Vu\xf4^\xa4\xc7\xf3\x17\u007f7\xce)\xe2-g\x99\x9c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81\x13\x9b\xfd\u0326V\xc40 ?r\x95\x8cT;e\x80\xd4\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x81\x14a\xa2\xb0\u0290\xba\xda\xc0j\x9e\xa1nx{3\xb1\x96\u0309\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\x81\x16M\xeb\x10\x81J\xe0\x83\x91\xf3,\bf{bH\xc2}z\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\x81\x18i1\x18A7\xd1\x19*\u020c\xd3\xe1\xe5\xd0\xfd\xb8jt\x89\x9d5\x95\xab$8\xd0\x00\x00\u0794\x81*U\xc4<\xae\xdcYr\x187\x90\x00\xceQ\rT\x886\xfd\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x81.\xa7\xa3\xb2\xc8n\xed2\xffO,sQL\xc6;\xac\xfb\u038965\u026d\xc5\u07a0\x00\x00\u07d4\x814\xdd\x1c\x9d\xf0\xd6\u0225\x81$&\xbbU\xc7a\u0283\x1f\b\x89\x06\xa2\x16\v\xb5|\xcc\x00\x00\u07d4\x81A5\u068f\x98\x11\aW\x83\xbf\x1a\xb6pb\xaf\x8d>\x9f@\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x81I\x8c\xa0{\x0f/\x17\xe8\xbb\xc7\xe6\x1a\u007fJ\xe7\xbef\xb7\x8b\x89\x05\x81\xfb\xb5\xb3;\xb0\x00\x00\u07d4\x81Um\xb2sI\xab\x8b'\x00ID\xedP\xa4n\x94\x1a\x0f_\x89\u063beI\xb0+\xb8\x00\x00\u07d4\x81U\xfalQ\xeb1\xd8\bA-t\x8a\xa0\x86\x10P\x18\x12/\x89e\xea=\xb7UF`\x00\x00\xe0\x94\x81V6\v\xbd7\ta\xce\xcakf\x91\xd7P\x06\xad L\xf2\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x81a\xd9@\xc3v\x01\x00\xb9\b\x05)\xf8\xa6\x03%\x03\x0fn\u0709\x10CV\x1a\x88)0\x00\x00\xe0\x94\x81d\xe7\x83\x14\xae\x16\xb2\x89&\xccU=,\xcb\x16\xf3V'\r\x8a\x01\xca\x13N\x95\xfb2\xc8\x00\x00\u07d4\x81e\u02b0\xea\xfbZ2\x8f\xc4\x1a\xc6M\xaeq[.\xef,e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81h\xed\xce\u007f)a\xcf)[\x9f\xcdZE\xc0l\xde\xdan\xf5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81m\x97r\xcf\x119\x91\x16\xcc\x1er\xc2lgt\xc9\xed\xd79\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81s\xc85djg.\x01R\xbe\x10\xff\xe8Ab\xdd%nL\x89\x1a\xab\xdf!E\xb40\x00\x00\u07d4\x81t\x93\u035b\xc6#p*$\xa5o\x9f\x82\xe3\xfdH\xf3\xcd1\x89\x9eK#\xf1-L\xa0\x00\x00\u07d4\x81y\xc8\tp\x18,\u0177\xd8*M\xf0n\xa9M\xb6:%\xf3\x89'o%\x9d\xe6k\xf4\x00\x00\u07d4\x81z\xc3;\xd8\xf8GVsr\x95\x1fJ\x10\u05e9\x1c\xe3\xf40\x89\n\xd7\xc4\x06\xc6m\xc1\x80\x00\xe0\x94\x81\x8f\xfe'\x1f\u00d75e\xc3\x03\xf2\x13\xf6\xd2\u0689\x89~\xbd\x8a\x016\xe0SB\xfe\u1e40\x00\u07d4\x81\x97\x94\x81!s.c\xd9\xc1H\x19N\xca\xd4n0\xb7I\u0209\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x81\x9a\xf9\xa1\xc2s2\xb1\xc3i\xbb\xda\x1b=\xe1\xc6\xe93\xd6@\x89\x11\t\xe6T\xb9\x8fz\x00\x00\xe0\x94\x81\x9c\u06a506x\xef|\xecY\u050c\x82\x16:\xcc`\xb9R\x8a\x03\x13QT_y\x81l\x00\x00\u07d4\x81\x9e\xb4\x99\vZ\xbaUG\t=\xa1+k<\x10\x93\xdfmF\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81\xa8\x81\x96\xfa\xc5\xf2<>\x12\xa6\x9d\xecK\x88\x0e\xb7\xd9s\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x81\xbc\xcb\xff\x8fD4~\xb7\xfc\xa9['\xce|\x95$\x92\xaa\xad\x89\b@\xc1!e\xddx\x00\x00\u07d4\x81\xbdu\xab\xd8e\xe0\xc3\xf0J\vO\xdb\xcbt\xd3@\x82\xfb\xb7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x81\xc1\x8c*#\x8d\xdcL\xba#\n\a-\xd7\xdc\x10\x1eb\x02s\x89Hz\x9a0E9D\x00\x00\u07d4\x81\xc9\xe1\xae\xe2\xd36]S\xbc\xfd\u0356\xc7\xc58\xb0\xfd~\xec\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\x81\u03edv\t\x13\xd3\xc3\"\xfc\xc7{I\u00ae9\a\xe7On\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x81\xd6\x19\xffW&\xf2@_\x12\x90Lr\xeb\x1e$\xa0\xaa\xeeO\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x81\xef\u25aev\xc8`\xd1\xc5\xfb\xd3=G\xe8\u0399\x96\xd1W\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81\xf8\xde,(=_\u052f\xbd\xa8]\xed\xf9v\x0e\xab\xbb\xb5r\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x82\f\x19)\x11\x96P[e\x05\x9d\x99\x14\xb7\t\v\xe1\u06c7\u0789\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x82\x1c\xb5\xcd\x05\xc7\uf41f\xe1\xbe`s=\x89c\xd7`\xdcA\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x82\x1dy\x8a\xf1\x99\x89\u00ee[\x84\xa7\xa7(<\xd7\xfd\xa1\xfa\xbe\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x82\x1e\xb9\t\x94\xa2\xfb\xf9K\xdc23\x91\x02\x96\xf7o\x9b\xf6\xe7\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x82$\x9f\xe7\x0fa\u01b1o\x19\xa3$\x84\x0f\xdc\x02\x021\xbb\x02\x8a\x02\x036\xb0\x8a\x93c[\x00\x00\u07d4\x82(\xeb\xc0\x87H\x0f\xd6EG\xca(\x1f^\xac\xe3\x04\x14S\xb9\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\x82)\u03b9\xf0\xd7\b9I\x8dD\xe6\xab\xed\x93\xc5\xca\x05\x9f]\x8a\x1a\x1c\x1b<\x98\x9a \x10\x00\x00\u07d4\x82.\xdf\xf66V:a\x06\xe5.\x9a%\x98\xf7\xe6\xd0\xef'\x82\x89\x01\xf4\xf9i=B\u04c0\x00\u07d4\x822\x19\xa2Yv\xbb*\xa4\xaf\x8b\xadA\xac5&\xb4\x936\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x822\xd1\xf9t.\u07cd\xd9'\xda5;*\xe7\xb4\xcb\xceu\x92\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\x824\xf4c\u0444\x85P\x1f\x8f\x85\xac\xe4\x97,\x9bc-\xbc\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\x827htg7\xcem\xa3\x12\xd5>TSN\x10o\x96|\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82;\xa7dr8\xd1\x13\xbc\xe9\x96JC\u0420\x98\x11\x8b\xfeM\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x82@t1(\x06\xdaGHCBf\xee\x00!@\u305a\u0089Q\xb1\u04c3\x92a\xac\x00\x00\u07d4\x82C\x8f\u04b3*\x9b\xddgKI\xd8\xcc_\xa2\xef\xf9x\x18G\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82HW(\xd0\xe2\x81V7X\xc7Z\xb2~\xd9\u80a0\x00-\x89\a\xf8\b\xe9)\x1el\x00\x00\u07d4\x82K<\x19)]~\xf6\xfa\xa7\xf3t\xa4y\x84\x86\xa8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82Q5\x8c\xa4\xe0`\u0775Y\xcaX\xbc\v\u077e\xb4\a\x02\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82Q5\xb1\xa7\xfc\x16\x05aL\x8a\xa4\u042cm\xba\u040fH\x0e\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\x82S\t\xa7\xd4]\x18\x12\xf5\x1en\x8d\xf5\xa7\xb9ol\x90\x88\x87\x89\x804\xf7\u0671f\xd4\x00\x00\u07d4\x82Z\u007fN\x10\x94\x9c\xb6\xf8\x96Bh\xf1\xfa_W\xe7\x12\xb4\u0109\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82a\xfa#\f\x90\x1dC\xffW\x9fG\x80\u04d9\xf3\x1e`v\xbc\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82b\x16\x9baXp\x13N\xb4\xacl_G\x1ck\xf2\xf7\x89\xfc\x89\x19\x12z\x13\x91\xea*\x00\x00\u07d4\x82c\xec\xe5\xd7\t\xe0\u05eeq\u0328h\xed7\xcd/\xef\x80{\x895\xab\x02\x8a\xc1T\xb8\x00\x00\xe0\x94\x82l\xe5y\x052\xe0T\x8ca\x02\xa3\r>\xac\x83k\xd68\x8f\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4\x82n\xb7\xcds\x19\xb8-\xd0z\x1f;@\x90q\xd9n9g\u007f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x82u1\xa6\u0141z\xe3_\x82\xb0\v\x97T\xfc\xf7LU\xe22\x89\xc3(\t>a\xee@\x00\x00\u0794\x82u\xcdhL6y\u0548}\x03fN3\x83E\xdc<\xdd\xe1\x88\xdbD\xe0I\xbb,\x00\x00\u07d4\x82\x84\x92;b\u62ff|+\x9f4\x14\xd1>\xf6\xc8\x12\xa9\x04\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\x82\x8b\xa6Q\u02d3\x0e\xd9xqV)\x9a=\xe4L\u040br\x12\x89Hz\x9a0E9D\x00\x00\u07d4\x82\xa1\\\xef\x1dl\x82`\xea\xf1Y\xea?\x01\x80\xd8g}\xce\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82\xa8\xb9kl\x9e\x13\xeb\xec\x1e\x9f\x18\xac\x02\xa6\x0e\xa8\x8aH\xff\x89lk\x8c@\x8es\xb3\x00\x00\u07d4\x82\xa8\u02ff\xdf\xf0+.8\xaeK\xbf\xca\x15\xf1\xf0\xe8;\x1a\xea\x89\x04\x9b\x99\x1c'\xefm\x80\x00\u07d4\x82\xe4F\x1e\xb9\xd8I\xf0\x04\x1c\x14\x04!\x9eBr\u0110\n\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82\xe5w\xb5\x15\xcb+\b`\xaa\xfe\x1c\xe0\x9aY\xe0\x9f\xe7\xd0@\x89 \x86\xac5\x10R`\x00\x00\u07d4\x82\xea\x01\xe3\xbf.\x83\x83nqpN\"\xa2q\x93w\xef\xd9\u00c9\xa4\xccy\x95c\u00c0\x00\x00\u07d4\x82\xf2\xe9\x91\xfd2L_]\x17v\x8e\x9fa3]\xb61\x9dl\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x82\xf3\x9b'X\xaeB'{\x86\u059fu\xe6(\xd9X\xeb\u02b0\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94\x82\xf8T\xc9\xc2\xf0\x87\xdf\xfa\x98Z\xc8 \x1ebl\xa5Fv\x86\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x82\xffqo\xdf\x03>\xc7\xe9B\xc9\t\u0643\x18g\xb8\xb6\xe2\xef\x89a\t=|,m8\x00\x00\u07d4\x83\b\xed\n\xf7\xf8\xa3\xc1u\x1f\xaf\xc8w\xb5\xa4*\xf7\xd3X\x82\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x83\x1cD\xb3\b@G\x18K*\xd2\x18h\x06@\x907P\xc4]\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x83!\x05\x83\xc1jN\x1e\x1d\xac\x84\xeb\xd3~=\x0f|W\ub909lk\x93[\x8b\xbd@\x00\x00\u07d4\x83,T\x17k\xdfC\xd2\u027c\u05f8\b\xb8\x95V\xb8\x9c\xbf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x833\x16\x98]Gt+\xfe\xd4\x10`J\x91\x95<\x05\xfb\x12\xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x834vK{9zNW\x8fP6M`\xceD\x89\x9b\xff\x94\x89\x05\x03\xb2\x03\xe9\xfb\xa2\x00\x00\xe0\x94\x83;j\x8e\xc8\xda@\x81\x86\xac\x8a}*m\xd6\x15#\xe7\u0384\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x83=?\xaeT*\xd5\xf8\xb5\f\xe1\x9b\xde+\xecW\x91\x80\u020c\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\x83=\xb4,\x14\x16<{\xe4\u02b8j\u0153\xe0bf\u0599\u054a$\xe4\r+iC\xef\x90\x00\x00\xe0\x94\x83V;\xc3d\ud060\xc6\xda;V\xffI\xbb\xf2g\x82z\x9c\x8a\x03\xab\x91\xd1{ \xdeP\x00\x00\u07d4\x83zd]\xc9\\IT\x9f\x89\x9cN\x8b\u03c7S$\xb2\xf5|\x89 \x8c9J\xf1\u0208\x00\x00\u07d4\x83\x8b\xd5e\xf9\x9f\xdeH\x05?y\x17\xfe3<\xf8J\xd5H\xab\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x83\x90\x8a\xa7G\x8am\x1c\x9b\x9b\x02\x81\x14\x8f\x8f\x9f$+\x9f\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\x83\x92\xe57vq5x\x01[\xffI@\xcfC\x84\x9d}\u02e1\x89\bM\xf05]V\x17\x00\x00\xe0\x94\x83\x97\xa1\xbcG\xac\xd6GA\x81Y\xb9\x9c\xeaW\xe1\xe6S-n\x8a\x01\xf1\x0f\xa8'\xb5P\xb4\x00\x00\u07d4\x83\x98\xe0~\xbc\xb4\xf7_\xf2\x11m\xe7|\x1c*\x99\xf3\x03\xa4\u03c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xa3\x14\x883\xd9dI\x84\xf7\xc4u\xa7\x85\a\x16\ufd00\xff\x89\xb8Pz\x82\a( \x00\x00\u07d4\x83\xa4\x02C\x8e\x05\x19w=TH2k\xfba\xf8\xb2\f\xf5-\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\x83\xa9;[\xa4\x1b\xf8\x87 \xe4\x15y\f\xdc\vg\xb4\xaf4\u0109\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x83\xc2=\x8aP!$\xee\x15\x0f\b\xd7\x1d\xc6rt\x10\xa0\xf9\x01\x8a\a3\x1f;\xfef\x1b\x18\x00\x00\u07d4\x83\u0217\xa8Ki^\xeb\xe4fy\xf7\xda\x19\xd7vb\x1c&\x94\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xd52\u04cdm\xee?`\xad\u018b\x93a3\u01e2\xa1\xb0\u0749\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xdb\xf8\xa1(S\xb4\n\xc6\x19\x96\xf8\xbf\x1d\xc8\xfd\xba\xdd\xd3)\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x83\xdb\xfd\x8e\xda\x01\xd0\u078e\x15\x8b\x16\u0413_\xc28\n]\u01c9 \x86\xac5\x10R`\x00\x00\u07d4\x83\xe4\x80U2|(\xb5\x93o\xd9\xf4D~s\xbd\xb2\xdd3v\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\x83\xfeZ\x1b2\x8b\xaeD\a\x11\xbe\xafj\xad`&\xed\xa6\xd2 \x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x84\x00\x8ar\xf8\x03o?\xeb\xa5B\xe3Px\xc0W\xf3*\x88%\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x84\x0e\xc8>\xa96!\xf04\xe7\xbb7b\xbb\x8e)\xde\xd4\xc4y\x89\x87\x86x2n\xac\x90\x00\x00\xe0\x94\x84\x11E\xb4H@\xc9F\xe2\x1d\xbc\x19\x02d\xb8\xe0\xd5\x02\x93i\x8a?\x87\bW\xa3\xe0\xe3\x80\x00\x00\u07d4\x84#!\a\x93+\x12\xe01\x86X5%\xce\x02:p>\xf8\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4\x84$O\xc9ZiW\xed|\x15\x04\xe4\x9f0\xb8\xc3^\xcaKy\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x841'}{\xdd\x10E}\xc0\x17@\x8c\x8d\xbb\xbdAJ\x8d\xf3\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4\x847Z\xfb\xf5\x9b:\x1da\xa1\xbe2\xd0u\xe0\xe1ZO\xbc\xa5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x84;\xd3P/E\xf8\xbcM\xa3p\xb3#\xbd\xac?\xcf_\x19\xa6\x89P\x03\x9dc\xd1\x1c\x90\x00\x00\u07d4\x84P34c\rw\xf7AG\xf6\x8b.\bf\x13\xc8\xf1\xad\xe9\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x84R\x03u\x0fqH\xa9\xaa&)!\xe8mC\xbfd\x19t\xfd\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x84a\xec\u0126\xa4^\xb1\xa5\xb9G\xfb\x86\xb8\x80i\xb9\x1f\xcdo\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84g^\x91wrmE\xea\xa4k9\x92\xa3@\xba\u007fq\f\x95\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x84hl{\xadv,T\xb6g\u055f\x90\x94<\xd1M\x11z&\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\x89\xf6\xad\x1d\x9a\x94\xa2\x97x\x91V\x89\x9d\xb6AT\xf1\u06f5\x89\x13t\a\xc0<\x8c&\x80\x00\u07d4\x84\x8c\x99Jy\x00?\xe7\xb7\xc2l\xc62\x12\xe1\xfc/\x9c\x19\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\x8f\xbd)\xd6|\xf4\xa0\x13\xcb\x02\xa4\xb1v\xef$N\x9e\u6349\x01\x17*ck\xbd\xc2\x00\x00\u07d4\x84\x94\x9d\xbaU\x9ac\xbf\xc8E\xde\xd0n\x9f-\x9b\u007f\x11\xef$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\x9a\xb8\a\x90\xb2\x8f\xf1\xff\u05ba9N\xfctc\x10\\6\xf7\x89\x01\xe0+\xe4\xael\x84\x00\x00\u07d4\x84\x9b\x11oYc\x01\xc5\u063bb\xe0\xe9z\x82H\x12n9\xf3\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x84\xa7L\xee\xcf\xf6\\\xb9;/\x94\x9dw>\xf1\xad\u007f\xb4\xa2E\x89\x05\n\x9bDF\x85\xc7\x00\x00\u07d4\x84\xaa\xc7\xfa\x19\u007f\xf8\\0\xe0;zS\x82\xb9W\xf4\x1f:\xfb\x89\b\x8b#\xac\xff\u0650\x00\x00\u07d4\x84\xaf\x1b\x15sB\xd5Ch&\r\x17\x87b0\xa54\xb5K\x0e\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x84\xb0\xeek\xb87\u04e4\xc4\xc5\x01\x1c:\"\x8c\x0e\u06b4cJ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\xb4\xb7Nf#\xba\x9d\x15\x83\xe0\u03feId?\x168AI\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\xb6\xb6\xad\xbe/[>-h,f\xaf\x1b\u0110S@\xc3\xed\x89!\x92\xf8\xd2\"\x15\x00\x80\x00\xe0\x94\x84\xb9\x1e.)\x02\xd0^+Y\x1bA\b;\u05fe\xb2\xd5,t\x8a\x02\x15\xe5\x12\x8bE\x04d\x80\x00\u07d4\x84\xbc\xbf\"\xc0\x96\a\xac\x844\x1d.\xdb\xc0;\xfb\x179\xd7D\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x84\xbf\xce\xf0I\x1a\n\xe0iK7\u03ac\x02E\x84\xf2\xaa\x04g\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x84\xcb}\xa0P-\xf4\\\xf5a\x81{\xbd#b\xf4Q\xbe\x02\u0689Hz\x9a0E9D\x00\x00\u07d4\x84\xccxx\xda`_\xdb\x01\x9f\xab\x9bL\xcf\xc1Wp\x9c\u0765\x89Hy\x85\x13\xaf\x04\xc9\x00\x00\u07d4\x84\xdb\x14Y\xbb\x00\x81.\xa6~\xcb=\xc1\x89\xb7!\x87\xd9\xc5\x01\x89\b\x11\xb8\xfb\u0685\xab\x80\x00\u07d4\x84\u9516\x80\xbe\xcehA\xb9\xa7\xe5%\r\b\xac\xd8}\x16\u0349\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x84\xe9\u03c1f\xc3j\xbf\xa4\x90S\xb7\xa1\xad@6 &\x81\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\xec\x06\xf2G\x00\xfeBAL\xb9\x89|\x15L\x88\xde/a2\x89Hz\x9a0E9D\x00\x00\xe0\x94\x84\xf5\"\xf0R\x0e\xbaR\xdd\x18\xad!\xfaK\x82\x9f+\x89\u02d7\x8a\x01\fQ\x06\xd5\x13O\x13\x00\x00\u07d4\x85\v\x9d\xb1\x8f\xf8K\xf0\xc7\xdaI\xea7\x81\xd9 \x90\xad~d\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\x85\x10\xee\x93O\f\xbc\x90\x0e\x10\a\xeb8\xa2\x1e*Q\x01\xb8\xb2\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4\x85\x16\xfc\xafw\u0213\x97\x0f\xcd\x1a\x95\x8b\xa9\xa0\x0eI\x04@\x19\x89\n\xa3\xeb\x16\x91\xbc\xe5\x80\x00\u07d4\x85\x1a\xa9\x1c\x82\xf4/\xad]\xd8\xe8\xbb^\xa6\x9c\x8f:Yw\u0449\b\x0eV\x1f%xy\x80\x00\u07d4\x85\x1c\rb\xbeF5\xd4w~\x805\xe3~K\xa8Q|a2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x85\x1d\u00ca\xdbE\x93r\x9av\xf3:\x86\x16\u06b6\xf5\xf5\x9aw\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x852I\b\x97\xbb\xb4\u038b\u007fk\x83~L\xba\x84\x8f\xbe\x99v\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x85>j\xba\xf4Di\xc7/\x15\x1dN\"8\x19\xac\xedN7(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x85F\x91\xceqO2\\\xedU\xceY(\u039b\xa1/\xac\u0478\x89\xedp\xb5\xe9\xc3\xf2\xf0\x00\x00\u07d4\x85L\fF\x9c$k\x83\xb5\u0473\xec\xa4C\xb3\x9a\xf5\xee\x12\x8a\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x85]\x9a\xef,9\xc6#\r\t\u025e\xf6II\x89\xab\u61c5\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\x85c\u0113a\xb6%\xe7hw\x1c\x96\x15\x1d\xbf\xbd\x1c\x90iv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x85fa\t\x01\xaa\xce8\xb82D\xf3\xa9\xc810jg\xb9\u0709\xb0\x82\x13\xbc\xf8\xff\xe0\x00\x00\xe0\x94\x85j\xa2<\x82\xd7![\xec\x8dW\xf6\n\xd7^\xf1O\xa3_D\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x85nZ\xb3\xf6L\x9a\xb5k\x00\x93\x93\xb0\x16d\xfc\x03$\x05\x0e\x89a\t=|,m8\x00\x00\u07d4\x85n\xb2\x04$\x1a\x87\x83\x0f\xb2)\x03\x13C\xdc0\x85OX\x1a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x85s,\x06\\\xbdd\x11\x99A\xae\xd40\xacYg\vlQ\u0109'\xa5sb\xab\n\x0e\x80\x00\xe0\x94\x85x\xe1\x02\x12\xca\x14\xff\a2\xa8$\x1e7F}\xb8V2\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x85y\xda\xdf\x1a9Z4q\xe2\vov=\x9a\x0f\xf1\x9a?o\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x85\u007f\x10\v\x1aY0\"^\xfc~\x90 \u05c3'\xb4\x1c\x02\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x85\x94mV\xa4\xd3q\xa93hS\x96\x90\xb6\x0e\xc8%\x10tT\x89]\u0212\xaa\x111\xc8\x00\x00\xe0\x94\x85\x99\xcb\u0566\xa9\xdc\u0539f\xbe8}iw]\xa5\xe3C'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86$_Yf\x91\t>\xce?=<\xa2&>\xac\xe8\x19A\u0649\n1\x06+\xee\xedp\x00\x00\u07d4\x86%i!\x1e\x8cc'\xb5A^:g\xe5s\x8b\x15\xba\xafn\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x86)}s\x0f\xe0\xf7\xa9\xee$\xe0\x8f\xb1\b{1\xad\xb3\x06\xa7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86D\xcc(\x1b\xe32\xcc\xce\xd3m\xa4\x83\xfb*\aF\u067a.\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x86I\x9a\x12(\xff-~\xe3\au\x93dPo\x8e\x8c\x83\a\xa5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x86K\xecPi\xf8U\xa4\xfdX\x92\xa6\xc4I\x1d\xb0|\x88\xff|\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86W\n\xb2Y\u0271\xc3,\x97) /w\xf5\x90\xc0}\xd6\x12\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86c\xa2A\xa0\xa8\x9ep\xe1\x82\xc8E\xe2\x10\\\x8a\xd7&K\u03ca\x03#\xb1=\x83\x98\xf3#\x80\x00\u07d4\x86g\xfa\x11U\xfe\xd72\u03f8\u0725\xa0\xd7e\xce\r\a\x05\xed\x89\x04n\xc9e\u00d3\xb1\x00\x00\u07d4\x86h\xaf\x86\x8a\x1e\x98\x88_\x93\u007f&\x15\xde\xd6u\x18\x04\xeb-\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x86t\nFd\x8e\x84Z]\x96F\x1b\x18\t\x1f\xf5{\xe8\xa1o\x8a\x14\xc0\x974\x85\xbf9@\x00\x00\xe0\x94\x86~\xbaVt\x8aY\x045\r,\xa2\xa5\u039c\xa0\vg\n\x9b\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x86\x80dt\xc3X\x04}\x94\x06\xe6\xa0\u007f@\x94[\xc82\x8eg\x8a\x01u.\xb0\xf7\x01=\x10\x00\x00\u07d4\x86\x88=T\xcd9\x15\xe5I\tU0\xf9\xab\x18\x05\xe8\xc5C-\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x86\x8c#\xbe\x874f\xd4\xc7L\"\n\x19\xb2E\xd1x~\x80\u007f\x89J\x13\xbb\xbd\x92\u020e\x80\x00\xe0\x94\x86\x92O\xb2\x11\xaa\xd2<\xf5\xce`\x0e\n\xae\x80c\x96D@\x87\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x86\x93\u9e3e\x94B^\xefyi\xbci\xf9\xd4/|\xadg\x1e\x8967\tlK\xcci\x00\x00\xe0\x94\x86\x9f\x1a\xa3\x0eDU\xbe\xb1\x82 \x91\xde\\\xad\xecy\xa8\xf9F\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x86\xa1\xea\xde\xeb0F\x13E\xd9\xefk\xd0R\x16\xfa$|\r\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86\xa5\xf8%\x9e\u0570\x9e\x18\x8c\xe3F\xee\x92\xd3J\xa5\u0753\xfa\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xb7\xbdV<\uad86\xf9bD\xf9\xdd\xc0*\u05f0\xb1K\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x86\u008bVx\xaf7\xd7'\xec\x05\xe4Dw\x90\xf1_q\xf2\xea\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xc4\xce\x06\u066c\x18[\xb1H\xd9o{z\xbes\xf4A\x00m\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x86\xc8\xd0\u0642\xb59\xf4\x8f\x980\xf9\x89\x1f\x9d`z\x94&Y\x8a\x02\xce\xd3wa\x82O\xb0\x00\x00\u07d4\x86\xc94\xe3\x8eS\xbe;3\xf2t\xd0S\x9c\xfc\xa1Y\xa4\xd0\u04494\x95tD\xb8@\xe8\x00\x00\xe0\x94\x86\xca\x01E\x95~k\r\xfe6\x87_\xbez\r\xecU\xe1z(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x86\u02af\xac\xf3*\xa01|\x03*\xc3k\xab\xed\x97G\x91\xdc\x03\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x86\u0377\xe5\x1a\xc4Gr\xbe6\x90\xf6\x1d\x0eYvn\x8b\xfc\x18\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x86\xdfs\xbd7\u007f,\t\xdec\xc4]g\xf2\x83\xea\xef\xa0\xf4\xab\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86\xe3\xfe\x86\xe9=\xa4\x86\xb1Bf\xea\xdf\x05l\xbf\xa4\xd9\x14C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86\xe8g\x0e'Y\x8e\xa0\x9c8\x99\xabw\x11\u04f9\xfe\x90\x1c\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xefd&!\x19I\xcc7\xf4\xc7^xP6\x9d\f\xf5\xf4y\x8a\x02\xd6_2\xea\x04Z\xf6\x00\x00\u07d4\x86\xf0]\x19\x06>\x93i\xc6\x00N\xb3\xf1#\x94:|\xffN\xab\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x86\xf2>\x9c\n\xaf\u01cb\x9c@M\xcd`3\x9a\x92[\xff\xa2f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x86\xf4\xf4\n\u0644\xfb\xb8\t3\xaebn\x0eB\xf93?\xddA\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x86\xf9\\[\x11\xa2\x93\x94\x0e5\xc0\xb8\x98\u0637_\b\xaa\xb0m\x8a\x06D\xe3\xe8u\xfc\xcft\x00\x00\u07d4\x86\xff\xf2 \xe5\x93\x05\xc0\x9fH8`\xd6\xf9N\x96\xfb\xe3/W\x89\x02S[j\xb4\xc0B\x00\x00\u07d4\x87\a\x96\xab\xc0\u06c4\xaf\x82\xdaR\xa0\xedhsM\xe7\xe66\xf5\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x87\x0f\x15\xe5\u07cb\x0e\xab\xd0%iSz\x8e\xf9;Vx\\B\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\x87\x181`\xd1r\xd2\xe0\x84\xd3'\xb8k\xcb|\x1d\x8eg\x84\xef\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94\x87\x1b\x8a\x8bQ\u07a1\x98\x9aY!\xf1>\xc1\xa9U\xa5\x15\xadG\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x87%\xe8\xc7S\xb3\xac\xbf\u0725_I\x13\\3\x91\x99\x10`)\n\xa7\xf6\u0338\xf8Zx\u06c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x87Pa\xee\x12\xe8 \x04\x1a\x01\x94,\xb0\xe6[\xb4'\xb0\x00`\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\x87XJ?a;\xd4\xfa\xc7L\x1ex\v\x86\xd6\xca\xeb\x89\f\xb2\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x87d\xd0'\"\x00\t\x96\xec\xd4u\xb43)\x8e\x9fT\v\x05\xbf\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x87l?!\x8bGv\xdf<\xa9\xdb\xfb'\r\xe1R\xd9N\xd2R\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x87u\xa6\x10\xc5\x02\xb9\xf1\xe6\xadL\xda\u06cc\xe2\x9b\xffu\xf6\xe4\x89 \x86\xac5\x10R`\x00\x00\u07d4\x87vN6w\xee\xf6\x04\xcb\u015a\xed$\xab\xdcVk\t\xfc%\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\x87\x87\xd1&w\xa5\xec)\x1eW\xe3\x1f\xfb\xfa\xd1\x05\xc32K\x87\x8a\x02\xa2N\xb52\b\xf3\x12\x80\x00\u07d4\x87\x94\xbfG\xd5E@\xec\xe5\xc7\"7\xa1\xff\xb5\x11\u0777Gb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x87\xa5>\xa3\x9fY\xa3[\xad\xa85%!dU\x94\xa1\xa7\x14\u02c9g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x87\xa7\xc5\b\xefqX-\u0665Cr\xf8\x9c\xb0\x1f%/\xb1\x80\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x87\xaf%\xd3\xf6\xf8\xee\xa1S\x13\xd5\xfeEW\xe8\x10\xc5$\xc0\x83\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x87\xb1\x0f\x9c(\x00\x98\x17\x9a+v\xe9\u0390\xbea\xfc\x84M\r\x89Hz\x9a0E9D\x00\x00\u07d4\x87\xbf|\xd5\u0629)\xe1\u01c5\xf9\xe5D\x91\x06\xac#$c\u0249\x047\xb1\x1f\xccEd\x00\x00\u07d4\x87\u0118\x17\t4\xb8#=\x1a\xd1\xe7i1}\\G_/@\x897\b\xba\xed=h\x90\x00\x00\u07d4\x87\xcf6\xad\x03\xc9\xea\xe9\x05:\xbbRB\u0791\x17\xbb\x0f*\v\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x87\u05ec\x06S\xcc\xc6z\xa9\xc3F\x9e\xefCR\x19?}\xbb\x86\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94\x87\xe3\x06+#!\xe9\u07f0\x87\\\u311c\x9b.5\"\xd5\n\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x87\xe6\x03N\xcf#\xf8\xb5c\x9d_\x0e\xa7\n\"S\x8a\x92\x04#\x89\x11\xc7\xea\x16.x \x00\x00\u07d4\x87\xefm\x8bj|\xbf\x9b\\\x8c\x97\xf6~\xe2\xad\u00a7;?w\x89\n\xdd\x1b\xd2<\x00L\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x88F\x92\x8dh2\x89\xa2\xd1\x1d\xf8\xdbz\x94t\x98\x8e\xf0\x13H\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x88I\x80\xebEe\xc1\x04\x83\x17\xa8\xf4\u007f\u06f4a\x96[\u4049\xd8\xd6\x11\x9a\x81F\x05\x00\x00\xe0\x94\x88Jz9\u0411n\x05\xf1\xc2B\xdfU`\u007f7\u07cc_\u068a\x04\xf4\x84<\x15|\x8c\xa0\x00\x00\u07d4\x88T\x93\xbd\xa3j\x042\x97eF\xc1\xdd\xceq\xc3\xf4W\x00!\x89\v\xbfQ\r\xdf\xcb&\x00\x00\xe0\x94\x88`\x9e\nF[n\x99\xfc\xe9\a\x16mW\xe9\xda\b\x14\xf5\u020a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x88m\n\x9e\x17\xc9\xc0\x95\xaf.\xa25\x8b\x89\xecpR\x12\ue509\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\x88y~Xg^\xd5\xccL\x19\x98\a\x83\xdb\xd0\xc9V\bQS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x88|\xacA\xcdpo3E\xf2\xd3J\xc3N\x01u*nY\t\x89 F\\\ue7617\x00\x00\u07d4\x88\x88\x8aW\xbd\x96\x87\xcb\xf9P\xae\xea\u03d7@\xdc\xc4\xd1\xefY\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u0794\x88\x89D\x83\x16\xcc\xf1N\xd8m\xf8\xe2\xf4x\xdcc\xc43\x83@\x88\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x88\x8c\x16\x14I3\x19|\xac&PM\xd7n\x06\xfdf\x00\u01c9\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x88\x8e\x94\x91p\x83\xd1R +S\x1699\x86\x9d'\x11u\xb4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x88\x90\x87\xf6o\xf2\x84\xf8\xb5\xef\xbd)I;pg3\xab\x14G\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\x88\x95\xebrb&\xed\xc3\xf7\x8c\u01a5\x15\a{2\x96\xfd\xb9^\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x88\x97Z_\x1e\xf2R\x8c0\v\x83\xc0\xc6\a\xb8\xe8}\u0593\x15\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\u07d4\x88\x9d\xa4\x0f\xb1\xb6\x0f\x9e\xa9\xbdzE>XL\xf7\xb1\xb4\xd9\xf7\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x88\x9d\xa6b\xebJ\n*\x06\x9d+\xc2K\x05\xb4\xee.\x92\xc4\x1b\x89Z,\x8cTV\xc9\xf2\x80\x00\u07d4\x88\xa1\"\xa28,R91\xfbQ\xa0\u032d;\xeb[rY\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x88\xa2\x15D0\xc0\xe4\x11G\xd3\xc1\xfe\u3cf0\x06\xf8Q\xed\xbd\x8965f3\xeb\xd8\xea\x00\x00\u07d4\x88\xb2\x17\u0337\x86\xa2T\xcfM\xc5\u007f]\x9a\xc3\xc4U\xa3\x04\x83\x892$\xf4'#\xd4T\x00\x00\xe0\x94\x88\xbcC\x01.\xdb\x0e\xa9\xf0b\xacCxC%\n9\xb7\x8f\xbb\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x88\xc2Qj|\xdb\t\xa6'mr\x97\xd3\x0fZM\xb1\xe8K\x86\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x88\xc3ad\rki7;\b\x1c\xe0\xc43\xbdY\x02\x87\xd5\xec\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x88\xd5A\xc8@\xceC\xce\xfb\xafm\x19\xafk\x98Y\xb5s\xc1E\x89\t79SM(h\x00\x00\u07d4\x88\xde\x13\xb0\x991\x87|\x91\rY1e\xc3d\u0221d\x1b\u04c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x88\xde\u017d?N\xba-\x18\xb8\xaa\xce\xfa{r\x15H\xc3\x19\xba\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\x88\xe6\xf9\xb2G\xf9\x88\xf6\xc0\xfc\x14\xc5o\x1d\xe5>\u019dC\u0309\x05k\xc7^-c\x10\x00\x00\u07d4\x88\xee\u007f\x0e\xfc\x8fw\x8ckh~\xc3+\xe9\xe7\xd6\xf0 \xb6t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x88\xf1\x04_\x19\xf2\xd3\x19\x18\x16\xb1\xdf\x18\xbbn\x145\xad\x1b8\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x89\x00\x9e\a\xe3\xfahc\xa7x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x89Wr~r\xcfb\x90 \xf4\xe0^\xdfy\x9a\xa7E\x80b\u0409wC\"\x17\xe6\x83`\x00\x00\u07d4\x89]iN\x88\v\x13\xcc\u0404\x8a\x86\xc5\xceA\x1f\x88Gk\xbf\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x89^\xc5TVD\u0dc30\xff\xfa\xb8\xdd\xea\xc9\xe83\x15l\x89 \x86\xac5\x10R`\x00\x00\u07d4\x89`\tRj,{\f\t\xa6\xf6:\x80\xbd\U0009d707\u079c\x89\xbb\xb8k\x82#\xed\xeb\x00\x00\u07d4\x89g\u05f9\xbd\xb7\xb4\xae\xd2.e\xa1]\xc8\x03\xcbz!?\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x89n3\\\xa4z\xf5yb\xfa\x0fM\xbf>E\xe6\x88\u02e5\x84\x89J/\xc0\xab`R\x12\x00\x00\u07d4\x89s\xae\xfd^\xfa\xee\x96\t]\x9e(\x8fj\x04l\x977KC\x89\a\xa4\u0120\xf32\x14\x00\x00\u07d4\x89\x8cr\xddseX\xef\x9eK\xe9\xfd\xc3O\xefT\xd7\xfc~\b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x89\x9b<$\x9f\fK\x81\xdfu\xd2\x12\x00M=m\x95/\xd2#\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x89\xab\x13\xee&mw\x9c5\xe8\xbb\x04\u034a\x90\xcc!\x03\xa9[\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\x89\xc43\xd6\x01\xfa\xd7\x14\xdaci0\x8f\xd2l\x1d\u0254+\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x89\xd7[\x8e\b1\xe4o\x80\xbc\x17A\x88\x18N\x00o\xde\x0e\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x89\u3d5a\x15\x86G7\u0513\xc1\xd2<\xc5=\xbf\x8d\xcb\x13b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x89\xfc\x8eM8k\r\v\xb4\xa7\a\xed\xf3\xbdV\r\xf1\xad\x8fN\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\x89\xfe\xe3\r\x17(\xd9l\xec\xc1\u06b3\xda.w\x1a\xfb\u03eaA\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x8a\x1c\u016c\x11\x1cI\xbf\xcf\xd8H\xf3}\xd7h\xaae\u0208\x02\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x8a \xe5\xb5\xce\xe7\xcd\x1fU\x15\xba\xce;\xf4\xf7\u007f\xfd\xe5\xcc\a\x89\x04V9\x18$O@\x00\x00\xe0\x94\x8a!}\xb3\x8b\xc3_!_\xd9)\x06\xbeBCo\xe7\xe6\xed\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x8a$:\n\x9f\xeaI\xb89TwE\xff-\x11\xaf?K\x05\"\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x8a$}\x18e\x10\x80\x9fq\xcf\xfcEYG\x1c9\x10\x85\x81!\x89a\t=|,m8\x00\x00\u07d4\x8a4p(-^**\xef\u05e7P\x94\xc8\"\xc4\xf5\xae\uf289\r(\xbc`dx\xa5\x80\x00\u07d4\x8a6\x86\x9a\xd4x\x99|\xbfm\x89$\xd2\n<\x80\x18\xe9\x85[\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8aC\x14\xfba\u0353\x8f\xc3>\x15\xe8\x16\xb1\x13\U000ac267\xfb\x89\x17vNz\xede\x10\x00\x00\u07d4\x8aOJ\u007fR\xa3U\xba\x10_\xca r\xd3\x06_\xc8\xf7\x94K\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8aX1(,\xe1Jezs\r\xc1\x88&\xf7\xf9\xb9\x9d\xb9h\x89\uaf8a[A\xc16\x00\x00\u07d4\x8a_\xb7W\x93\xd0C\xf1\xbc\xd48\x85\xe07\xbd0\xa5(\xc9'\x89\x13Snm.\x9a\xc2\x00\x00\u07d4\x8af\xab\xbc-0\xce!\xa83\xb0\u06ceV\x1dQ\x05\xe0\xa7,\x89%\xf1\xde\\v\xac\xdf\x00\x00\u07d4\x8atl]g\x06G\x11\xbf\xcah[\x95\xa4\xfe)\x1a'\x02\x8e\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8ax\n\xb8z\x91E\xfe\x10\xed`\xfaGjt\n\xf4\u02b1\u0489\x12\x1b.^ddx\x00\x00\u07d4\x8az\x06\xbe\x19\x9a:X\x01\x9d\x84j\xc9\xcb\xd4\xd9]\xd7W\u0789\xa2\xa4#\x94BV\xf4\x00\x00\u07d4\x8a\x81\x01\x14\xb2\x02]\xb9\xfb\xb5\x00\x99\xa6\xe0\u02de.\xfak\u0709g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x8a\x86\xe4\xa5\x1c\x01;\x1f\xb4\xc7k\xcf0f|x\xd5.\xed\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\x9e\u029cZ\xba\x8e\x13\x9f\x80\x03\xed\xf1\x16:\xfbp\xaa:\xa9\x89#\xc7W\a+\x8d\xd0\x00\x00\u07d4\x8a\xb89\xae\xaf*\xd3|\xb7\x8b\xac\xbb\xb63\xbc\xc5\xc0\x99\xdcF\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\u021b\u06780\x1ek\x06w\xfa%\xfc\xf0\xf5\x8f\f\u01f6\x11\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x8a\xdcS\xef\x8c\x18\xed0Qx]\x88\xe9\x96\xf3\xe4\xb2\x0e\xcdQ\x8a\b\xe4\xd3\x16\x82v\x86@\x00\x00\u07d4\x8a\xe6\xf8\vp\xe1\xf2<\x91\xfb\u0569f\xb0\xe4\x99\xd9]\xf82\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x8a\xe9\uf30a\x8a\u07e6\xaby\x8a\xb2\xcd\xc4\x05\b*\x1b\xbbp\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\xf6&\xa5\xf3'\xd7Pe\x89\xee\xb7\x01\x0f\xf9\xc9D` \u0489K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\x8b\x01\xda4\xd4p\xc1\xd1\x15\xac\xf4\xd8\x11\xe1\x01\xdb\x1e\x14\xec\xc7\xd3\"\xc7+\x8c\x04s\x89\x18\xb2j1>\x8a\xe9\x00\x00\xe0\x94\x8bH\xe1\x9d9\xdd5\xb6nn\x1b\xb6\xb9\xc6W\xcb,\xf5\x9d\x04\x8a\x03\xc7U\xac\x9c\x02J\x01\x80\x00\xe0\x94\x8bP^(q\xf7\u07b7\xa68\x95 \x8e\x82'\u072a\x1b\xff\x05\x8a\f\xf6\x8e\xfc0\x8dy\xbc\x00\x00\u07d4\x8bW\xb2\xbc\x83\u030dM\xe31 N\x89?/;\x1d\xb1\a\x9a\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8b\\\x91K\x12\x8b\xf1i\\\b\x89#\xfaF~y\x11\xf3Q\xfa\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\xe0\x94\x8b_)\xcc/\xaa&,\xde\xf3\x0e\xf5T\xf5\x0e\xb4\x88\x14n\xac\x8a\x01;hp\\\x97 \x81\x00\x00\u07d4\x8bpV\xf6\xab\xf3\xb1\x18\xd0&\xe9D\xd5\xc0sC<\xa4Q\u05c965\xc6 G9\u0640\x00\u07d4\x8bqE\"\xfa(9b\x04p\xed\xcf\fD\x01\xb7\x13f=\xf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x8bt\xa7\xcb\x1b\xb8\u014f\xce&tf\xa3\x03X\xad\xafR\u007fa\x8a\x02\xe2WxN%\xb4P\x00\x00\u07d4\x8b~\x9fo\x05\xf7\xe3dv\xa1n>q\x00\xc9\x03\x1c\xf4\x04\xaf\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8b\x81\x15ni\x869\x94<\x01\xa7Rr\xad=5\x85\x1a\xb2\x82\x89\x12\xb3\x16_e\xd3\xe5\x00\x00\u07d4\x8b\x95w\x92\x00S\xb1\xa0\x01\x890M\x88\x80\x10\xd9\xef,\xb4\xbf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8b\x98A\x86.w\xfb\xbe\x91\x94p\x93U\x83\xa9<\xf0'\xe4P\x89llS4B\u007f\x1f\x00\x00\u07d4\x8b\x99}\xbc\a\x8a\xd0)a5]\xa0\xa1Y\xf2\x92~\xd4=d\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x8b\x9f\xda}\x98\x1f\xe9\xd6B\x87\xf8\\\x94\xd8?\x90t\x84\x9f\u030a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x8b\xb0!/2\x95\xe0)\u02b1\xd9a\xb0A3\xa1\x80\x9e{\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8b\xbe\xac\xfc)\xcf\xe94\x02\xdb\xd6j\x1a\xcbvv\x85c7\xb9;\xf0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8b\xf3s\xd0v\x81L\xbcW\xe1\xc6\xd1j\x82\u017e\x13\xc7=7\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x8c\x10#\xfd\xe1WM\xb8\xbbT\xf1s\x96p\x15|\xa4}\xa6R\x8a\x01y\u03da\u00e1\xb1w\x00\x00\u07d4\x8c\x1f\xbe_\n\xea5\x9cZ\xa1\xfa\b\u0209T\x12\u028e\x05\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\"B`U\xb7o\x11\xf0\xa2\xde\x1a\u007f\x81\x9aa\x96\x85\xfe`\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4\x8c+}\x8b`\x8d(\xb7\u007f\\\xaa\x9c\xd6E$*\x82>L\u0649b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\x8c/\xbe\ue3ac\xc5\xc5\xd7|\x16\xab\xd4b\ue701E\xf3K\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x8c:\x9e\xe7\x1fr\x9f#l\xba8g\xb4\u05dd\x8c\xee\xe2]\xbc\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8cP\xaa*\x92\x12\xbc\xdeVA\x8a\xe2a\xf0\xb3^z\x9d\xbb\x82\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8cT\xc7\xf8\xb9\x89nu\xd7\xd5\xf5\xc7`%\x86\x99\x95qB\xad\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8c]\x16\xede\xe3\xed~\x8b\x96\u0297+\xc8as\xe3P\v\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8cj\xa8\x82\xee2,\xa8HW\x8c\x06\xcb\x0f\xa9\x11\xd3`\x83\x05\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\x8cj\xe7\xa0Z\x1d\xe5u\x82\xae'h Bv\xc0\xffG\xed\x03\x8a,\v\xb3\xdd0\xc4\xe2\x00\x00\x00\u07d4\x8co\x9fN[z\xe2v\xbfXI{\u05ff*}%$_d\x89\x93\xfe\\W\xd7\x10h\x00\x00\u07d4\x8cu\x95n\x8f\xedP\xf5\xa7\xdd|\xfd'\xda \x0fgF\xae\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c|\xb4\xe4\x8b%\x03\x1a\xa1\xc4\xf9)%\xd61\xa8\xc3\xed\xc7a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\u007f\xa5\xca\xe8/\xed\xb6\x9a\xb1\x89\xd3\xff'\xae \x92\x93\xfb\x93\x89\x15\xaf\x88\r\x8c\u06c3\x00\x00\xe0\x94\x8c\x81A\x0e\xa85L\xc5\xc6\\A\xbe\x8b\xd5\xdes<\v\x11\x1d\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\x8c\x83\xd4$\xa3\xcf$\xd5\x1f\x01\x92=\xd5J\x18\u05b6\xfe\xde{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8c\x90\n\x826\xb0\x8c+e@]9\xd7_ \x06*ua\xfd\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\x8c\x93\xc3\xc6\u06dd7q}\xe1e\u00e1\xb4\xfeQ\x95,\b\u0789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8c\x99\x95\x91\xfdr\xefq\x11\xef\xcaz\x9e\x97\xa25k;\x00\n\x89\xddd\xe2\xaa\ngP\x00\x00\u07d4\x8c\xa6\x98\x97F\xb0n2\xe2Hta\xb1\u0399j':\xcf\u05c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8c\xb3\xaa?\xcd!(T\xd7W\x8f\xcc0\xfd\xed\xe6t*1*\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x8c\xc0\xd7\xc0\x16\xfaz\xa9P\x11J\xa1\xdb\tH\x82\xed\xa2t\xea\x89\b\xa9\xab\xa5W\xe3l\x00\x00\u07d4\x8c\xc6R\xdd\x13\xe7\xfe\x14\u06bb\xb3m]2\r\xb9\xff\xee\x8aT\x89a\t=|,m8\x00\x00\u07d4\x8c\u02bf%\a\u007f:\xa4\x15E4MS\xbe\x1b+\x9c3\x90\x00\x89[\xe8f\xc5b\xc5D\x00\x00\u07d4\x8c\xcf:\xa2\x1a\xb7BWj\xd8\xc4\"\xf7\x1b\xb1\x88Y\x1d\ua28965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\xd0\xcd\"\xe6 \xed\xa7\x9c\x04a\xe8\x96\xc9\xd1b)\x12K_z\xfb\xec\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x8c\xe2/\x9f\xa3rD\x9aB\x06\x10\xb4z\xe0\xc8\xd5eH\x122\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x8c\u451d\x8a\x16T-B<\x17\x98Ng9\xfar\u03b1w\x8a\x05K@Y&\xf4\xa6=\x80\x00\u07d4\x8c\xe5\xe3\xb5\xf5\x91\xd5\uc8ca\xbf\"\x8f.<5\x13K\xda\xc0\x89}\xc3[\x84\x89|8\x00\x00\xe0\x94\x8c\xee8\xd6YW\x88\xa5n?\xb9F4\xb3\xff\xe1\xfb\xdb&\u058a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x8c\xee\xa1^\xec;\xda\xd8\x02?\x98\xec\xf2[+\x8f\xef'\xdb)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8c\xf3To\xd1\u0363=X\x84_\xc8\xfc\xfe\u02bc\xa7\xc5d*\x89\x1f\x1e9\x93,\xb3'\x80\x00\u07d4\x8c\xf6\xda\x02\x04\xdb\u0106\vF\xad\x97?\xc1\x11\x00\x8d\x9e\fF\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x8c\xfe\xde\xf1\x98\xdb\n\x91C\xf0\x91)\xb3\xfdd\u073b\x9bIV\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\x04\xa5\xeb\xfb]\xb4\t\xdb\x06\x17\xc9\xfaV1\xc1\x92\x86\x1fJ\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x8d\x06\xe4d$\\\xadaI9\xe0\xaf\bE\xe6\xd70\xe2\x03t\x89\n\u070a(\xf3\xd8}\x80\x00\u07d4\x8d\a\xd4-\x83\x1c-|\x83\x8a\xa1\x87+:\xd5\xd2w\x17h#\x89\x12\xee\x1f\x9d\xdb\xeeh\x00\x00\u07d4\x8d\v\x9e\xa5?\xd2cA^\xac\x119\x1f|\xe9\x12V\xb9\xfb\x06`\xf6\xf0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8dy\\_JV\x89\xadb\u0696\x16q\xf0(\x06R\x86\xd5T\x89o\x05\xb5\x9d; \x00\x00\x00\u07d4\x8d\u007f>a)\x9c-\xb9\xb9\xc0H|\xf6'Q\x9e\xd0\n\x91#\x89^t\xa8P^\x80\xa0\x00\x00\xe0\x94\x8d\x89\x17\v\x92\xb2\xbe,\b\xd5|H\xa7\xb1\x90\xa2\xf1Fr\x0f\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x8d\x93\xda\u01c5\xf8\x8f\x1a\x84\xbf\x92}Se+E\xa1T\xcc\u0749\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x8d\x99R\u043bN\xbf\xa0\xef\xd0\x1a:\xa9\xe8\xe8\u007f\x05%t.\x89\xbb\x91%T\"c\x90\x00\x00\u07d4\x8d\x9a\fp\xd2& B\xdf\x10\x17\xd6\xc3\x03\x13 $w'\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\x9e\xd7\xf4U0X\xc2ox6\xa3\x80-0d\xeb\x1b6=\x89\x04\xe1\x00;(\xd9(\x00\x00\u07d4\x8d\xa1\x17\x8fU\xd9wr\xbb\x1d$\x11\x1a@JO\x87\x15\xb9]\x89/\x9a\xc3\xf6\xde\x00\x80\x80\x00\u07d4\x8d\xa1\xd3Y\xbal\xb4\xbc\xc5}zCw \xd5]\xb2\xf0\x1cr\x89\x04V9\x18$O@\x00\x00\u07d4\x8d\xab\x94\x8a\xe8\x1d\xa3\x01\xd9r\xe3\xf6\x17\xa9\x12\xe5\xa7Sq.\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8d\xad\xdfR\xef\xbdt\u0695\xb9i\xa5GoO\xbb\xb5c\xbf\u0489-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4\x8d\xb1\x85\xfe\x1bp\xa9Jj\b\x0e~#\xa8\xbe\xdcJ\xcb\xf3K\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x8d\xb5\x8e@n -\xf9\xbcpl\xb43\xe1\x94\xf4\x0f\x82\xb4\x0f\xaa\xdb\x1f\x8b\x85a\x16\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x8d\xc1\xd5\x11\x1d\t\xaf%\xfd\xfc\xacE\\|\xec(>mgu\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\u0504\xff\x8a0sd\xebf\xc5%\xa5q\xaa\xc7\x01\xc5\xc3\x18\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\u05a9\xba\xe5\u007fQ\x85I\xad\xa6wFo\ua2b0O\u0674\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\xde<\xb8\x11\x85h\xefE\x03\xfe\x99\x8c\xcd\xf56\xbf\x19\xa0\x98\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\xde`\xeb\b\xa0\x99\xd7\u06a3V\u06aa\xb2G\r{\x02Zk\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x8d\xf39!Kj\u0472Fc\xceq`4t\x9dn\xf88\u064a\x02TO\xaaw\x80\x90\xe0\x00\x00\xe0\x94\x8d\xf5=\x96\x19\x14q\xe0Y\xdeQ\xc7\x18\xb9\x83\xe4\xa5\x1d*\xfd\x8a\x06\u01b95\xb8\xbb\xd4\x00\x00\x00\u07d4\x8d\xfb\xaf\xbc\x0e[\\\x86\xcd\x1a\u0597\xfe\xea\x04\xf41\x88\u0796\x89\x15%+\u007f_\xa0\xde\x00\x00\u07d4\x8e\a;\xad%\xe4\"\x18a_J\x0ek.\xa8\xf8\xde\"0\xc0\x89\x82=b\x9d\x02k\xfa\x00\x00\u07d4\x8e\x0f\xee8hZ\x94\xaa\xbc\xd7\u0385{k\x14\t\x82Ou\xb8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e#\xfa\xcd\x12\xc7e\xc3j\xb8\x1am\xd3M\x8a\xa9\xe6\x89\x18\xae\x89\t\x11\u418d\xba\x9b\x00\x00\xe0\x94\x8e/\x904\xc9%G\x19\u00ceP\u026ad0^\u0596\xdf\x1e\x8a\x01\x00N.E\xfb~\xe0\x00\x00\u07d4\x8e2@\xb0\x81\x0e\x1c\xf4\a\xa5\x00\x80G@\u03cdad2\xa4\x89\x02/fU\xef\v8\x80\x00\u07d4\x8eHj\x04B\xd1q\xc8`[\xe3H\xfe\xe5~\xb5\b^\xff\r\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8eaV3k\xe2\u037e2\x14\r\xf0\x8a+\xa5_\u0425\x84c\x89\x04\t\x9e\x1dcW\x18\x00\x00\u07d4\x8eg\b\x15\xfbg\xae\xae\xa5{\x86SN\xdc\x00\xcd\xf5d\xfe\u5272\xe4\xb3#\xd9\xc5\x10\x00\x00\u07d4\x8emt\x85\xcb\u942c\xc1\xad\x0e\xe9\xe8\xcc\xf3\x9c\f\x93D\x0e\x893\xc5I\x901r\f\x00\x00\xe0\x94\x8et\xe0\u0477~\xbc\x82:\xca\x03\xf1\x19\x85L\xb1 '\xf6\u05ca\x16\xb3R\xda^\x0e\xd3\x00\x00\x00\u07d4\x8ex\xf3QE}\x01oJ\xd2u^\xc7BN\\!\xbamQ\x89\a\xea(2uw\b\x00\x00\u07d4\x8ey6\u0552\x00\x8f\xdcz\xa0N\xde\xebuZ\xb5\x13\u06f8\x9d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8e\u007f\xd28H\xf4\xdb\a\x90j}\x10\xc0K!\x80;\xb0\x82'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8e\x92\xab\xa3\x8er\xa0\x98\x17\v\x92\x95\x92FSz.UV\xc0\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x8e\x98ve$\xb0\xcf'G\xc5\r\xd4;\x95gYM\x971\u0789lD\xb7\xc2a\x82(\x00\x00\u07d4\x8e\x9b5\xadJ\n\x86\xf7XDo\xff\xde4&\x9d\x94\f\xea\u0349\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8e\x9c\b\xf78f\x1f\x96v#n\xff\x82\xbaba\xdd?H\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8e\x9cB\x92f\xdf\x05~\xfax\xdd\x1d_w\xfc@t*\xd4f\x89\x10D.\u0475l|\x80\x00\u07d4\x8e\xa6V\xe7\x1e\xc6Q\xbf\xa1|ZWY\xd8`1\xcc5\x99w\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8e\xae)CU\x98\xba\x8f\x1c\x93B\x8c\xdb>+M1\a\x8e\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\xb1\xfb\xe4\xe5\xd3\x01\x9c\xd7\xd3\r\xae\x9c\r[Lv\xfbc1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\xb5\x17t\xaf k\x96k\x89\t\xc4Z\xa6r'H\x80,\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e\xb8\xc7\x19\x82\xa0\x0f\xb8Bu)2S\xf8\x04ED\xb6kI\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\x8e\xcb\u03ec\xbf\xaf\xe9\xf0\f9\"\xa2N,\xf0\x02gV\xca \x8a\x011\xbe\xb9%\xff\xd3 \x00\x00\u07d4\x8e\u03b2\xe1$Sl[_\xfcd\x0e\xd1O\xf1^\u0668\xcbq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\u042f\x11\xff(p\xda\x06\x81\x00J\xfe\x18\xb0\x13\xf7\xbd8\x82\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\x8e\xd1Cp\x1f/r(\x0f\xd0J{Ad(\x19y\xea\x87\u0248\xc2I\xfd\xd3'x\x00\x00\u07d4\x8e\xd1R\x8bD~\xd4)y\x02\xf69\xc5\x14\u0414J\x88\xf8\u0209\n\xc6\xe7z\xb6c\xa8\x00\x00\u07d4\x8e\xd4(L\x0fGD\x9c\x15\xb8\u0673$]\u8fb6\u0380\xbf\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\x8e\xde~=\xc5\aI\xc6\xc5\x0e.(\x16\x84x\xc3M\xb8\x19F\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x8e\xe5\x843}\xdb\xc8\x0f\x9e4\x98\xdfU\xf0\xa2\x1e\xac\xb5\u007f\xb1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8e\xeb\xec\x1ab\xc0\x8b\x05\xa7\xd1\u0551\x80\xaf\x9f\xf0\u044e?6\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e\xf4\u0622\xc2o\xf7\x14\xb6u\x89\x19\x80\x1c\x83\xb6\xc7\xc0\x00\x00\u07d4\x8fM\x1dAi>F,\xf9\x82\xfd\x81\u042ap\x1d:St\u0249\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8fM\x1e~Ea(J4\xfe\xf9g<\r4\xe1*\xf4\xaa\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8fO\xb1\xae\xa7\xcd\x0fW\x0e\xa5\xe6\x1b@\xa4\xf4Q\vbd\xe4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8fV\x1bA\xb2\t\xf2H\u0229\x9f\x85\x87\x887bP`\x9c\xf3\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94\x8fX\xd84\x8f\xc1\xdcN\r\xd84;eC\xc8W\x04^\xe9@\x8a\x02\xe3\x03\x8d\xf4s\x03(\x00\x00\u07d4\x8f`\x89_\xbe\xbb\xb5\x01\u007f\xcb\xff<\u0763\x97)+\xf2[\xa6\x89\x17D\x06\xff\x9fo\u0480\x00\u07d4\x8fd\xb9\xc1$m\x85x1d1\a\xd3U\xb5\xc7_\xef]O\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x8ff\x0f\x8b.L|\u00b4\xac\x9cG\xed(P\x8d_\x8f\x86P\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x8fi\xea\xfd\x023\xca\xdb@Y\xabw\x9cF\xed\xf2\xa0PnH\x89`\xf0f \xa8IE\x00\x00\xe0\x94\x8fq~\xc1U/LD\x00\x84\xfb\xa1\x15J\x81\xdc\x00>\xbd\xc0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x8f\x8a\xcb\x10v\a8\x84y\xf6K\xaa\xab\xea\x8f\xf0\a\xad\xa9}\x8a\x05\xc6\xf3\b\n\xd4#\xf4\x00\x00\u07d4\x8f\x8c\xd2n\x82\xe7\xc6\xde\xfd\x02\u07ed\a\x97\x90!\xcb\xf7\x15\f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x8f\x8f7\u042d\x8f3]*q\x01\xb4\x11V\xb6\x88\xa8\x1a\x9c\xbe\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\x8f\x92\x84O(*\x92\x99\x9e\u5d28\xd7s\xd0kiM\xbd\x9f\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x8f\xact\x8fxJ\x0f\xedh\u06e43\x19\xb4*u\xb4d\x9cn\x891T\xc9r\x9d\x05x\x00\x00\u07d4\x8f\u0665\xc3:}\x9e\xdc\xe0\x99{\xdfw\xab0d$\xa1\x1e\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8f\xef\xfa\xdb8z\x15G\xfb(M\xa9\xb8\x14\u007f>|m\xc6\u0689-b{\xe4S\x05\b\x00\x00\u07d4\x8f\xf4`Ehw#\xdc3\xe4\u0419\xa0i\x04\xf1\ubd44\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\x8f\xfa\x06!\"\xac0t\x18\x82\x1a\u06d3\x11\aZ7\x03\xbf\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8f\xfe2)\x97\xb8\xe4\x04B-\x19\xc5J\xad\xb1\x8f[\xc8\u9dc9\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x90\x01\x94\u0131\aC\x05\u045d\xe4\x05\xb0\xacx(\x0e\xca\xf9g\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x90\x03\xd2p\x89\x1b\xa2\xdfd=\xa84\x15\x83\x195E\xe3\xe0\x00\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x90\x05z\xf9\xaaf0~\xc9\xf03\xb2\x97$\u04f2\xf4\x1e\xb6\xf9\x8a\x19\xd1\u05aa\xdb,R\xe8\x00\x00\u07d4\x90\x0f\v\x8e5\xb6h\xf8\x1e\xf2R\xb18U\xaaP\a\xd0\x12\xe7\x89\x17\n\x0fP@\xe5\x04\x00\x00\u07d4\x90\x18\xcc\x1fH\xd20\x8e%*\xb6\b\x9f\xb9\x9a|\x1dV\x94\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x90\x1d\x99\xb6\x99\xe5\u0191\x15\x19\xcb v\xb4\xc7c0\xc5M\"\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x90-t\xa1W\xf7\u04b9\xa37\x8b\x1fVp70\xe0:\x17\x19\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x904\x13\x87\x8a\xea;\xc1\bc\t\xa3\xfev\x8beU\x9e\x8c\xab\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x90If\xcc\"\x13\xb5\xb8\xcb[\xd6\b\x9e\xf9\xcd\xdb\xef~\xdf\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\x90L\xaaB\x9ca\x9d\x94\x0f\x8egA\x82j\r\xb6\x92\xb1\x97(\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x90R\xf2\xe4\xa3\xe3\xc1-\xd1\xc7\x1b\xf7\x8aN\xc3\x04=\u020b~\x89\x0e~\xeb\xa3A\vt\x00\x00\u0794\x90U&V\x8a\xc1#\xaf\xc0\xe8J\xa7\x15\x12O\xeb\xe8=\xc8|\x88\xf8i\x93)g~\x00\x00\u07d4\x90\x92\x91\x87\a\xc6!\xfd\xbd\x1d\x90\xfb\x80\xebx\u007f\xd2osP\x89\x85[[\xa6\\\x84\xf0\x00\x00\u07d4\x90\x9b^v:9\xdc\u01d5\"=s\xa1\u06f7\xd9L\xa7Z\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\x90\xac\xce\xd7\xe4\x8c\b\u01b94dm\xfa\n\xdf)\u0714\aO\x89\x03\vK\x15{\xbdI\x00\x00\u07d4\x90\xb1\xf3p\xf9\xc1\xeb\v\xe0\xfb\x8e+\x8a\xd9jAcq\u074a\x890\xca\x02O\x98{\x90\x00\x00\u07d4\x90\xb6/\x13\x1a_)\xb4UqQ>\xe7\xa7J\x8f\v#\"\x02\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x90\xbdb\xa0P\x84Ra\xfaJ\x9f|\xf2A\xeac\v\x05\ufe09\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x90\xc4\x1e\xba\x00\x8e \xcb\xe9'\xf3F`?\u0206\x98\x12Yi\x89\x02F\xdd\xf9yvh\x00\x00\u07d4\x90\u0480\x9a\xe1\xd1\xff\xd8\xf6>\xda\x01\xdeI\xddU-\xf3\u047c\x89\u063beI\xb0+\xb8\x00\x00\u07d4\x90\xdc\t\xf7\x17\xfc*[i\xfd`\xba\b\xeb\xf4\v\xf4\xe8$l\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x90\xe3\x00\xacqE\x1e@\x1f\x88\u007fnw(\x85\x16G\xa8\x0e\a\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x90\xe3Z\xab\xb2\xde\xef@\x8b\xb9\xb5\xac\xefqDW\xdf\xdebr\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\x90\xe7\a\x0fM\x03?\xe6\x91\f\x9e\xfeZ'\x8e\x1f\xc6#M\xef\x89\x05q8\b\x19\xb3\x04\x00\x00\u07d4\x90\xe9>M\xc1q!HyR36\x14\x00+\xe4#VI\x8e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x90\u9a68.\u06a8\x14\u0084\xd22\xb6\u9e90p\x1dIR\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\x90\xf7t\xc9\x14}\u0790\x85=\xdcC\xf0\x8f\x16\xd4U\x17\x8b\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x90\xfcS{!\x06Xf\n\x83\xba\xa9\xacJ\x84\x02\xf6WF\xa8\x89e\xea=\xb7UF`\x00\x00\u07d4\x91\x05\n\\\xff\xad\xed\xb4\xbbn\xaa\xfb\xc9\xe5\x014(\xe9l\x80\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x91\x05\x17d\xafk\x80\x8eB\x12\xc7~0\xa5W.\xaa1pp\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x91\v}Wz~9\xaa#\xac\xf6*\xd7\xf1\xef4)4\xb9h\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x91\x0e\x99eC4Lh\x15\xfb\x97\u0367\xafK\x86\x98vZ[\x89\x05\x9a\xf6\x98)\xcfd\x00\x00\u07d4\x91\x1f\xee\xa6\x1f\xe0\xedP\u0179\xe5\xa0\xd6`q9\x9d(\xbd\u0189\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x91\x1f\xf23\xe1\xa2\x11\xc0\x17,\x92\xb4l\xf9\x97\x03\x05\x82\xc8:\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x91 \xe7\x11s\xe1\xba\x19\xba\x8f\x9fO\xdb\u072a4\xe1\u05bbx\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91!\x17\x12q\x9f+\bM;8u\xa8Pi\xf4f61A\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x91#\x04\x11\x8b\x80G=\x9e\x9f\xe3\xeeE\x8f\xbea\x0f\xfd\xa2\xbb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x91Tky\xec\xf6\x9f\x93kZV\x15\b\xb0\xd7\xe5\f\u0159/\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x91V\u0440)5\x0eG\x04\b\xf1_\x1a\xa3\xbe\x9f\x04\ng\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x91b\x0f>\xb3\x04\xe8\x13\u048b\x02\x97Ume\xdcN]\u5a89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\x91k\xf7\xe3\xc5E\x92\x1d2\x06\xd9\x00\xc2O\x14\x12|\xbd^p\x8a\x03\xd0\u077c}\xf2\xbb\x10\x00\x00\u0794\x91l\xf1}qA(\x05\xf4\xaf\xc3DJ\v\x8d\xd1\xd93\x9d\x16\x88\xc6s\xce<@\x16\x00\x00\u07d4\x91{\x8f\x9f:\x8d\t\xe9 ,R\u009erA\x96\xb8\x97\xd3^\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\x91\x89g\x91\x8c\u0617\xdd\x00\x05\xe3m\xc6\u0203\xefC\x8f\xc8\u01c9\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x91\x89\x8e\xab\x8c\x05\xc0\"(\x83\xcdM\xb2;w\x95\xe1\xa2J\u05c9lk\x93[\x8b\xbd@\x00\x00\u0794\x91\x91\xf9F\x98!\x05\x16\xcfc!\xa1B\a\x0e Yvt\xed\x88\xee\x9d[\xe6\xfc\x11\x00\x00\u07d4\x91\xa4\x14\x9a,{\x1b:g\xea(\xaf\xf3G%\u0fcdu$\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x91\xa7\x87\xbcQ\x96\xf3HW\xfe\f7/M\xf3v\xaa\xa7f\x13\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91\xa8\xba\xae\xd0\x12\xea.c\x80;Y=\r\f*\xabL[\n\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x91\xac\\\xfeg\xc5J\xa7\xeb\xfb\xa4HflF\x1a;\x1f\xe2\xe1\x89\x15\xc94\x92\xbf\x9d\xfc\x00\x00\u07d4\x91\xbb?y\x02+\xf3\xc4S\xf4\xff%n&\x9b\x15\xcf,\x9c\xbd\x89RX\\\x13\xfe:\\\x00\x00\u07d4\x91\xc7^<\xb4\xaa\x89\xf3F\x19\xa1d\xe2\xa4x\x98\xf5gM\x9c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91\xc8\f\xaa\b\x1b85\x1d*\x0e\x0e\x00\xf8\n4\xe5dt\xc1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x91\xccF\xaa7\x9f\x85jf@\xdc\xcdZd\x8ay\x02\xf8I\u0649\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x91\u04a9\xee\x1am\xb2\x0fS\x17\u0327\xfb\xe218\x95\u06ce\xf8\x8a\x01\xcc\u00e5/0n(\x00\x00\u07d4\x91\xd6n\xa6(\x8f\xaaK=`l*\xa4\\{k\x8a%'9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x91\u06f6\xaa\xad\x14\x95\x85\xbeG7\\]m\xe5\xff\t\x19\x15\x18\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x91\xe8\x81\x06R\xe8\xe6\x16\x15%\xd6;\xb7u\x1d\xc2\x0fg`v\x89'Mej\xc9\x0e4\x00\x00\u07d4\x91\xf5\x16\x14l\xda (\x17\x19\x97\x80`\u01beAI\x06|\x88\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x91\xf6$\xb2J\x1f\xa5\xa0V\xfeW\x12)\xe77\x9d\xb1K\x9a\x1e\x8a\x02\x8a\x85\x17\xc6i\xb3W\x00\x00\xe0\x94\x91\xfe\x8aLad\u07cf\xa6\x06\x99]k\xa7\xad\xca\xf1\u0213\u038a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4\x92\x1fRa\xf4\xf6\x12v\a\x06\x89&%\xc7^{\u0396\xb7\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92!\xc9\xce\x01#&et\x10\x96\xac\a#Y\x03\xad\x1f\xe2\xfc\x89\x06\xdbc3U\"b\x80\x00\u07d4\x92%\x988`\xa1\xcbF#\xc7$\x80\xac\x16'+\f\x95\xe5\xf5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x92%\xd4jZ\x80\x949$\xa3\x9e[\x84\xb9m\xa0\xacE\x05\x81\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x92* \u01da\x1d:&\xdd8)g{\xf1\xd4\\\x8fg+\xb6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x92C\x8eR\x03\xb64o\xf8\x86\xd7\xc3b\x88\xaa\xcc\xccx\xce\u028965\u026d\xc5\u07a0\x00\x00\u07d4\x92C\xd7v-w({\x12c\x86\x88\xb9\x85N\x88\xa7i\xb2q\x8965\u026d\xc5\u07a0\x00\x00\u0794\x92K\xcez\x85<\x97\v\xb5\xec{\xb7Y\xba\xeb\x9ct\x10\x85{\x88\xbe -j\x0e\xda\x00\x00\u07d4\x92N\xfam\xb5\x95\xb7\x93\x13'~\x881\x96%\akX\n\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92U\x82&\xb3\x84bl\xadH\xe0\x9d\x96k\xf19^\xe7\xea]\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4\x92`\x82\xcb~\xedK\x19\x93\xad$ZGrg\xe1\xc3<\xd5h\x89\x14Jt\xba\u07e4\xb6\x00\x00\u07d4\x92b\t\xb7\xfd\xa5N\x8d\u06dd\x9eM=\x19\xeb\u070e\x88\u009f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92h\xd6&FV6\x11\xdc;\x83*0\xaa#\x94\xc6F\x13\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92i\x8e4Sx\xc6-\x8e\xda\x18M\x946j\x14K\f\x10[\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x92y:\u0173rhwJq0\xde+\xbd3\x04\x05f\x17s\x89\x02,\xa3X|\xf4\xeb\x00\x00\xe0\x94\x92y\xb2\"\x8c\xec\x8f{M\xda?2\x0e\x9a\x04f\xc2\xf5\x85\u028a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x92|\xb7\xdc\x18p6\xb5B{\xc7\xe2\x00\xc5\xecE\f\x1d'\u0509\v\xb5\x9a'\x95<`\x00\x00\u07d4\x92|\u00bf\xda\x0e\b\x8d\x02\xef\xf7\v8\xb0\x8a\xa5<\xc3\tA\x89do`\xa1\xf9\x866\x00\x00\xe0\x94\x92\x84\xf9m\xdbG\xb5\x18n\xe5X\xaa12M\xf56\x1c\x0fs\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x92\x9d6\x8e\xb4j-\x1f\xbd\xc8\xff\xa0`~\xdeK\xa8\x8fY\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xa7\u0166Cb\xe9\xf8B\xa2=\xec\xa2\x105\x85\u007f\x88\x98\x00\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x92\xa8\x98\xd4o\x19q\x9c8\x12j\x8a<'\x86z\xe2\xce\u5589lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xa9q\xa79y\x9f\x8c\xb4\x8e\xa8G]r\xb2\xd2GAr\xe6\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x92\xaa\xe5\x97h\xed\xdf\xf8<\xfe`\xbbQ.s\n\x05\xa1a\u05c9\\\x97xA\fv\u0440\x00\u07d4\x92\xad\x1b=u\xfb\xa6}Tf=\xa9\xfc\x84\x8a\x8a\xde\x10\xfag\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xae[|~\xb4\x92\xff\x1f\xfa\x16\xddB\xad\x9c\xad@\xb7\xf8\u0709.\xe4IU\b\x98\xe4\x00\x00\u07d4\x92\xc0\xf5s\xec\xcfb\xc5H\x10\xeek\xa8\xd1\xf1\x13T+0\x1b\x89\xb7ro\x16\u0331\xe0\x00\x00\u07d4\x92\xc1?\xe0\xd6\u0387\xfdP\xe0=\uf7e6@\x05\t\xbdps\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x92\xc9L( \xdf\xcfqV\xe6\xf10\x88\xec\u754b6v\xfd\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4\x92\xcf\xd6\x01\x88\xef\u07f2\xf8\xc2\xe7\xb1i\x8a\xbb\x95&\xc1Q\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\u062d\x9aMah;\x80\u0526g.\x84\xc2\rbB\x1e\x80\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x92\u0725\xe1\x02\xb3\xb8\x1b`\xf1\xa5\x04cIG\xc3t\xa8\x8c\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xe454\x0e\x9d%<\x00%c\x89\xf5+\x06}U\x97Nv\x89\x0e\x87?D\x13<\xb0\x00\x00\xe0\x94\x92\xe49(\x16\xe5\xf2\xef_\xb6X7\xce\xc2\xc22\\\xc6I\"\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x92\xe6X\x1e\x1d\xa1\xf9\xb8F\xe0\x93G3=\xc8\x18\xe2\u04acf\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\x93\x1d\xf3M\x12%\xbc\xd4\"Nch\r\\L\t\xbc\xe75\xa6\x89\x03\xaf\xb0\x87\xb8v\x90\x00\x00\u07d4\x93\x1f\xe7\x12\xf6B\a\xa2\xfdP\"r\x88CT\x8b\xfb\x8c\xbb\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x93#_4\r(c\xe1\x8d/LR\x99e\x16\x13\x8d\"\x02g\x89\x04\x00.D\xfd\xa7\xd4\x00\x00\u07d4\x93%\x82U\xb3|\u007fX\xf4\xb1\x06s\xa92\xdd:\xfd\x90\xf4\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x93(\xd5\\\xcb?\xceS\x1f\x19\x93\x823\x9f\x0eWn\xe8@\xa3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x93)\xff\xdc&\x8b\xab\u0788t\xb3f@l\x81D[\x9b-5\x89\x16\xe6/\x8cs\f\xa1\x80\x00\u07d4\x93+\x9c\x04\xd4\r*\xc80\x83\xd9B\x98\x16\x9d\xae\x81\xab.\u0409lk\x93[\x8b\xbd@\x00\x00\u07d4\x9346\xc8G&U\xf6L:\xfa\xaf|Lb\x1c\x83\xa6+8\x8965\u026d\xc5\u07a0\x00\x00\u0794\x93;\xf3?\x82\x99p+:\x90&B\xc3>\v\xfa\xea\\\x1c\xa3\x88\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x93@4\\\xa6\xa3\uaf77sc\xf2X`C\xf2\x948\xce\v\x89\x1c\xc8\x05\xda\r\xff\xf1\x00\x00\xe0\x94\x93@\xb5\xf6x\xe4^\xe0^\xb7\b\xbbz\xbbn\xc8\xf0\x8f\x1bk\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x93J\xf2\x1b~\xbf\xa4g\xe2\xce\xd6Z\xa3N\xdd:\x0e\xc7\x132\x8a\a\x80\x1f>\x80\xcc\x0f\xf0\x00\x00\xe0\x94\x93PiDJj\x98M\xe2\bNFi*\xb9\x9fg\x1f\xc7'\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\xe0\x94\x93P~\x9e\x81\x19\xcb\xce\u068a\xb0\x87\xe7\xec\xb0q8=i\x81\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x93g\x8a\x00\x00\xe0\x94\x93m\xcf\x00\x01\x94\xe3\xbf\xf5\n\u0174$:;\xa0\x14\xd6a\u060a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x93o8\x13\xf5\xf6\xa1;\x8eO\xfe\xc8?\xe7\xf8&\x18jq\u0349\x1c0s\x1c\xec\x03 \x00\x00\u07d4\x93t\x86\x9dJ\x99\x11\xee\x1e\xafU\x8b\xc4\u00b6>\xc6:\xcf\u074965\u026d\xc5\u07a0\x00\x00\u07d4\x93uc\u0628\x0f\u05657\xb0\xe6m \xa0%%\xd5\u0606`\x89\x87\x86x2n\xac\x90\x00\x00\u07d4\x93v\xdc\xe2\xaf.\xc8\xdc\xdat\x1b~sEfF\x81\xd96h\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x93\x86\x8d\xdb*yM\x02\xeb\xda/\xa4\x80|v\xe3`\x98X\u0709m\xee\x15\xfc|$\xa7\x80\x00\xe0\x94\x93\x9cC\x13\xd2(\x0e\xdf^\a\x1b\xce\xd8F\x06?\n\x97]T\x8a\x19i6\x89t\xc0[\x00\x00\x00\xe0\x94\x93\xa6\xb3\xabB0\x10\xf9\x81\xa7H\x9dJ\xad%\xe2b\\WA\x8a\x04F\x80\xfej\x1e\xdeN\x80\x00\u07d4\x93\xaa\x8f\x92\xeb\xff\xf9\x91\xfc\x05^\x90ne\x1a\xc7h\xd3+\u02092\xf5\x1e\u06ea\xa30\x00\x00\u07d4\x93\xb4\xbf?\xdf\xf6\xde?NV\xbamw\x99\xdcK\x93\xa6T\x8f\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4\x93\xbc}\x9aJ\xbdD\u023b\xb8\xfe\x8b\xa8\x04\xc6\x1a\xd8\xd6Wl\x89\xd8\xd6\x11\x9a\x81F\x05\x00\x00\u07d4\x93\xc2\xe6N]\xe5X\x9e\xd2P\x06\xe8C\x19n\xe9\xb1\xcf\v>\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\x93\u020e-\x88b\x1e0\xf5\x8a\x95\x86\xbe\xd4\t\x89\x99\xebg\u074a\x06\x9bZ\xfa\xc7P\xbb\x80\x00\x00\u07d4\x93\xe0\xf3~\xcd\xfb\x00\x86\xe3\xe8b\xa9p4D{\x1eM\xec\x1a\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\xe0\x94\x93\xe3\x03A\x1a\xfa\xf6\xc1\a\xa4A\x01\u026c[6\xe9\xd6S\x8b\x8a\r\xf9\xdd\xfe\xcd\x03e@\x00\x00\u07d4\x93\xf1\x8c\xd2R`@v\x14\x88\xc5\x13\x17M\x1eycv\x8b,\x89\x82\xff\xac\x9a\u0553r\x00\x00\u07d4\x94\x0fqQ@P\x9f\xfa\xbf\x97EF\xfa\xb3\x90\"\xa4\x19R\u0489K\xe4\xe7&{j\xe0\x00\x00\u07d4\x94,k\x8c\x95[\xc0\u0608\x12g\x8a#g%\xb3'9\xd9G\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\x94=7\x86JJS}5\xc8\u0657#\xcdd\x06\xce%b\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94C\x9c\xa9\xcc\x16\x9ay\u0520\x9c\xae^gvJo\x87\x1a!\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x94D\x9c\x01\xb3*\u007f\xa5Z\xf8\x10OB\xcd\xd8D\xaa\x8c\xbc@\x8a\x03\x81\x11\xa1\xf4\xf0<\x10\x00\x00\xe0\x94\x94E\xba\\0\xe9\x89a\xb8`$a\xd08]@\xfb\xd8\x03\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x94O\a\xb9o\x90\xc5\xf0\xd7\xc0\u0140S1I\xf3\xf5\x85\xa0x\x89\x04\x02\xf4\xcf\xeeb\xe8\x00\x00\u07d4\x94T\xb3\xa8\xbf\xf9p\x9f\xd0\u1407~l\xb6\u0219t\xdb\u0589\x90\xf54`\x8ar\x88\x00\x00\u07d4\x94]\x96\xeaW>\x8d\xf7&+\xbf\xa5r\"\x9bK\x16\x01k\x0f\x89\vX\x9e\xf9\x14\xc1B\x00\x00\u07d4\x94^\x18v\x9d~\xe7'\xc7\x01?\x92\xde$\xd1\x17\x96\u007f\xf3\x17\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94a'\x81\x03;W\xb1F\xeet\xe7S\xc6r\x01\u007fS\x85\xe4\x89\xc3(\t>a\xee@\x00\x00\xe0\x94\x94dJ\xd1\x16\xa4\x1c\xe2\xca\u007f\xbe\xc6\t\xbd\xefs\x8a*\xc7\u01ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x94p\xcc6YE\x86\x82\x18!\xc5\u0256\xb6\xed\xc8;mZ2\x89\x01M\x11 \u05f1`\x00\x00\xe0\x94\x94u\xc5\x10\xec\x9a&\x97\x92GtL=\x8c;\x0e\v_D\u04ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x94~\x11\xe5\xea)\ro\u00f3\x80H\x97\x9e\f\xd4N\xc7\xc1\u007f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94\x83\u064f\x14\xa3?\xdc\x11\x8d@9U\u00995\xed\xfc_p\x89\x18\xea;4\xefQ\x88\x00\x00\u07d4\x94\x911\xf2\x89C\x92\\\xfc\x97\xd4\x1e\f\xea\v&)s\xa70\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\x94\x9f\x84\xf0\xb1\xd7\u0127\xcfI\xee\u007f\x8b,J\x13M\xe3(x\x89%\"H\u07b6\xe6\x94\x00\x00\u07d4\x94\x9f\x8c\x10{\xc7\xf0\xac\xea\xa0\xf1pR\xaa\xdb\xd2\xf9s+.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94\xa7\u0368\xf4\x81\xf9\u061dB\xc3\x03\xae\x162\xb3\xb7\t\xdb\x1d\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x94\xa9\xa7\x16\x911| d'\x1bQ\xc95?\xbd\xed5\x01\xa8\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x94\xadK\xad\x82K\xd0\ub7a4\x9cX\u03bc\xc0\xff^\b4k\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x94\xbb\xc6}\x13\xf8\x9e\xbc\xa5\x94\xbe\x94\xbcQp\x92\f0\xd9\xf3\x89\x04X\xff\xa3\x15\nT\x00\x00\u07d4\x94\xbe:\xe5Ob\xd6c\xb0\xd4\u031e\x1e\xa8\xfe\x95V\ua7bf\x89\x01C\x13,\xa8C\x18\x00\x00\xe0\x94\x94\xc0U\xe8X5z\xaa0\xcf A\xfa\x90Y\xce\x16J\x1f\x91\x8a\x04<%\xe0\xdc\xc1\xbd\x1c\x00\x00\xe0\x94\x94\xc7B\xfdz\x8by\x06\xb3\xbf\xe4\xf8\x90O\xc0\xbe\\v\x803\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x94\xcaV\xdew\u007f\xd4S\x17\u007f^\x06\x94\xc4x\xe6j\xff\x8a\x84\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x94\xd8\x10t\xdbZ\xe1\x97\u04bb\x13s\xab\x80\xa8}\x12\x1cK\u04ca\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x94\u06c0xs\x86\n\xac=Z\xea\x1e\x88^R\xbf\xf2\x86\x99T\x89\xae\x8ez\v\xb5u\xd0\x00\x00\u07d4\x94\xe1\xf5\u02db\x8a\xba\xce\x03\xa1\xa6B\x82VU;i\f#U\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x94\xef\x8b\xe4Pw\xc7\xd4\xc5e'@\u0794jbbOq?\x89\x05l\xf5Y:\x18\xf8\x80\x00\u07d4\x94\xf1?\x9f\b6\xa3\xee$7\xa8I\"\u0498M\xc0\xf7\xd5;\x89\xa2\xa02\x9b\u00ca\xbe\x00\x00\u07d4\x94\xf8\xf0W\xdb~`\xe6u\xad\x94\x0f\x15X\x85\u0464w4\x8e\x89\x15\xbeat\xe1\x91.\x00\x00\xe0\x94\x94\xfc\u03ad\xfe\\\x10\x9c^\xae\xafF-C\x871B\u020e\"\x8a\x01\x045a\xa8\x82\x93\x00\x00\x00\u07d4\x95\x03N\x16!\x86Q7\xcdG9\xb3F\xdc\x17\xda:'\xc3N\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\x95\fh\xa4\t\x88\x15M#\x93\xff\xf8\xda|\u0369\x96\x14\xf7,\x89\xf9AF\xfd\x8d\xcd\xe5\x80\x00\xe0\x94\x95\x0f\xe9\xc6\xca\xd5\f\x18\xf1\x1a\x9e\xd9\xc4W@\xa6\x18\x06\x12\u040a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x95!\x83\xcf\u04ce5.W\x9d6\xde\xce\u0171\x84P\xf7\xfb\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95'\x8b\b\xde\xe7\xc0\xf2\xc8\xc0\xf7\"\xf9\xfc\xbb\xb9\xa5$\x1f\u0689\x82\x93\t\xf6O\r\xb0\x00\x00\u07d4\x95,W\xd2\xfb\x19Q\a\xd4\xcd\\\xa3\x00wA\x19\u07ed/x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x955r\xf0\xeam\xf9\xb1\x97\xca\xe4\x0eK\x8e\xcc\x05lCq\u014965\u026d\xc5\u07a0\x00\x00\u07d4\x95>\xf6R\xe7\xb7i\xf5=nxjX\x95/\xa9>\xe6\xab\u725b\ny\x1f\x12\x110\x00\x00\u07d4\x95DpF1;/:^\x19\xb9H\xfd;\x8b\xed\xc8,q|\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x95]\xb3\xb7C`\xb9\xa2hg~s\u03a8!f\x8a\xf6\xfa\u038a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\x95`\xe8\xacg\x18\xa6\xa1\xcd\xcf\xf1\x89\xd6\x03\xc9\x06>A=\xa6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x95g\xa0\u0781\x1d\xe6\xff\t[~\xe6N\u007f\x1b\x83\xc2a[\x80\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x95h\x1c\xda\xe6\x9b I\xce\x10\x1e2\\u\x98\x92\xca\xc3\xf8\x11\x89\x9a\xe9*\x9b\xc9L@\x00\x00\xe0\x94\x95h\xb7\xdeuV(\xaf5\x9a\x84T=\xe25\x04\xe1^A\xe6\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x95i\xc6:\x92\x84\xa8\x05bm\xb3\xa3.\x9d#c\x93GaQ\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x95\x80\x9e\x8d\xa3\xfb\xe4\xb7\xf2\x81\xf0\xb8\xb1q_B\x0f}}c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95\x9fW\xfd\xedj\xe3y\x13\xd9\x00\xb8\x1e_H\xa7\x93\"\xc6'\x89\r\xdb&\x10GI\x11\x80\x00\u07d4\x95\x9f\xf1\u007f\x1dQ\xb4s\xb4@\x10\x05'U\xa7\xfa\x8cu\xbdT\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x95\xa5w\xdc.\xb3\xael\xb9\xdf\xc7z\xf6\x97\xd7\xef\xdf\xe8\x9a\x01\x89\a_a\x0fp\xed \x00\x00\u07d4\x95\xcbm\x8acy\xf9J\xba\x8b\x88ViV,MD\x8eV\xa7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95\xd5PB{ZQLu\x1ds\xa0\xf6\u049f\xb6]\"\xed\x10\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x95\u064d\f\x10i\x90\x8f\x06zR\xac\xac+\x8bSM\xa3z\xfd\x89oY\xb60\xa9)p\x80\x00\xe0\x94\x95\xdfN4E\xd7f&$\u010e\xbat\u03de\nS\xe9\xf72\x8a\v\xdb\xc4\x1e\x03H\xb3\x00\x00\x00\u07d4\x95\xe6\xa5K-_g\xa2JHu\xafu\x10|\xa7\xea\x9f\xd2\xfa\x89Hz\x9a0E9D\x00\x00\xe0\x94\x95\xe6\xf9=\xac\"\x8b\xc7XZ%sZ\xc2\xd0v\xcc:@\x17\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x95\xe7ad$\xcd\ta\xa7\x17'$t7\xf0\x06\x92r(\x0e\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x95\xe8\n\x82\xc2\f\xbe= `$,\xb9-sX\x10\xd04\xa2\x89\x01\xc3.F?\u0539\x80\x00\u07d4\x95\xf6-\x02C\xed\xe6\x1d\xad\x9a1e\xf59\x05'\rT\xe2B\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\x95\xfbZ\xfb\x14\xc1\uf6b7\xd1y\xc5\xc3\x00P?\xd6j^\xe2\x89\x01\xda\xf7\xa0+\r\xbe\x80\x00\u07d4\x96\x10Y\"\x02\u0082\xab\x9b\u0628\x84Q\x8b>\v\xd4u\x817\x89\x0e\x87?D\x13<\xb0\x00\x00\xe0\x94\x96\x1cY\xad\xc7E\x05\u0446M\x1e\xcf\u02ca\xfa\x04\x12Y<\x93\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x96,\r\xec\x8a=FK\xf3\x9b\x12\x15\xea\xfd&H\n\xe4\x90\u0349l\x82\xe3\xea\xa5\x13\xe8\x00\x00\u07d4\x96,\xd2*\x8e\xdf\x1eONU\xb4\xb1]\xdb\xfb]\x9dT\x19q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x963K\xfe\x04\xff\xfaY\x02\x13\xea\xb3e\x14\xf38\xb8d\xb76\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x967\xdc\x12r=\x9cxX\x85B\uac02fO?\x03\x8d\x9d\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x96N\xabK'kL\u0618>\x15\xcar\xb1\x06\x90\x0f\xe4\x1f\u0389\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x96b\xee\x02\x19&h+1\xc5\xf2\x00\xceEz\xbe\xa7ll\xe9\x89$Y\x0e\x85\x89\xebj\x00\x00\xe0\x94\x96l\x04x\x1c\xb5\xe6}\xde25\xd7\xf8b\x0e\x1a\xb6c\xa9\xa5\x8a\x10\r P\xdacQ`\x00\x00\u07d4\x96pv\xa8w\xb1\x8e\xc1ZA[\xb1\x16\xf0n\xf3&E\u06e3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96{\xfa\xf7bC\u0379@\t\xae<\x8d5\x05\xe9\xc0\x80EK\xe0\xe8\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x96\x92A\x91\xb7\xdfe[3\x19\xdcma7\xf4\x81\xa7:\x0f\xf3\x89\xd9\xec\xb4\xfd \x8eP\x00\x00\u07d4\x96\x96\x05!83\x8cr/\x11@\x81\\\xf7t\x9d\r;:t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x96\xa5_\x00\xdf\xf4\x05\xdcM\xe5\xe5\x8cW\xf6\xf6\xf0\xca\xc5]/\x89jf\x167\x9c\x87\xb5\x80\x00\u07d4\x96\xaaW?\xed/#4\x10\u06eeQ\x80\x14[#\xc3\x1a\x02\xf0\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x96\xadW\x9b\xbf\xa8\u06ce\xbe\xc9\u0486\xa7.Fa\xee\xd8\xe3V\x89:\v\xa4+\xeca\x83\x00\x00\u07d4\x96\xb44\xfe\x06W\xe4*\u0302\x12\xb6\x86Q9\xde\xde\x15\x97\x9c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x96\xb9\x06\xear\x9fFU\xaf\xe3\xe5}5'|\x96}\xfa\x15w\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x96\xd6-\xfdF\b\u007fb@\x9d\x93\xdd`a\x88\xe7\x0e8\x12W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96\xd9\u0328\xf5^\xea\x00@\xecn\xb3H\xa1wK\x95\xd9>\xf4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x96\xe7\xc0\xc9\u057f\x10\x82\x1b\xf1@\xc5X\xa1E\xb7\xca\xc2\x13\x97\x899>\xf1\xa5\x12|\x80\x00\x00\u07d4\x96\xeaj\u021a+\xac\x954{Q\u06e6=\x8b\xd5\xeb\xde\xdc\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96\xea\xfb\xf2\xfboM\xb9\xa46\xa7LE\xb5eDR\xe28\x19\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x96\xebR>\x83/P\n\x01}\xe1>\xc2\u007f]6lV\x0e\xff\x89\x10\xac\u03baC\xee(\x00\x00\u07d4\x96\xf0F*\xe6\xf8\xb9`\x88\xf7\xe9\u018ct\xb9\u062d4\xb3G\x89a\t=|,m8\x00\x00\u07d4\x96\xf8 P\vp\xf4\xa3\xe3#\x9da\x9c\xff\x8f\" u\xb15\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x96\xfeY\xc3\u06f3\xaa|\xc8\xcbbH\fe\xe5nb\x04\xa7\xe2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x96\xffoP\x99h\xf3l\xb4,\xbaH\xdb2\xf2\x1fVv\xab\xf8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x97\t8R*\xfb^\x8f\x99Hs\xc9\xfb\xdc&\xe3\xb3~1L\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x97\n\xbdS\xa5O\xcaJd) |\x18-MW\xbb9\u0520\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97\r\x8b\x8a\x00\x16\xd1C\x05O\x14\x9f\xb3\xb8\xe5P\xdc\a\x97\u01c965\u026d\xc5\u07a0\x00\x00\u07d4\x97,/\x96\xaa\x00\u03ca/ Z\xbc\xf8\x93|\fu\xf5\xd8\u0649\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97?N6\x1f\xe5\xde\u0358\x9dL\x8f}|\xc9y\x908]\xaf\x89\x15\x0f\x85C\xa3\x87B\x00\x00\u07d4\x97M\x05A\xabJG\xec\u007fu6\x9c\x00i\xb6J\x1b\x81w\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u0794\x97M/\x17\x89_)\x02\x04\x9d\xea\xae\xcf\t\xc3\x04e\a@-\x88\xcc\x19\u00947\xab\x80\x00\u07d4\x97R\xd1O^\x10\x93\xf0qq\x1c\x1a\xdb\xc4\xe3\xeb\x1e\\W\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97V\xe1v\xc9\xefi>\xe1\xee\u01b9\xf8\xb1Q\xd3\x13\xbe\xb0\x99\x89A\rXj \xa4\xc0\x00\x00\u07d4\x97_7d\xe9{\xbc\xcfv|\xbd;y[\xa8m\x8b\xa9\x84\x0e\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\x97j\x18Sj\xf4\x18tBc\b\x87\x1b\xcd\x15\x12\xa7u\xc9\xf8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x97n<\xea\xf3\xf1\xafQ\xf8\u009a\xff]\u007f\xa2\x1f\x03\x86\xd8\xee\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x97w\xcca\xcfuk\xe3\xb3\xc2\f\xd4I\x1ci\xd2u\xe7\xa1 \x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x97\x81\v\xaf\xc3~\x840c2\xaa\xcb5\xe9*\xd9\x11\xd2=$\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x97\x8cC\f\xe45\x9b\x06\xbc,\xdf\\)\x85\xfc\x95\x0eP\xd5\u0209\x1a\x05V\x90\xd9\u06c0\x00\x00\u07d4\x97\x95\xf6C\x19\xfc\x17\xdd\x0f\x82a\xf9\xd2\x06\xfbf\xb6L\xd0\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97\x99\xca!\xdb\xcfi\xbf\xa1\xb3\xf7+\xacQ\xb9\xe3\xcaX|\xf9\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x97\x9c\xbf!\xdf\xec\x8a\xce?\x1c\x19m\x82\u07d6%4\xdf9O\x89\x99\x91\xd4x\xddM\x16\x00\x00\u07d4\x97\x9dh\x1ca}\xa1o!\xbc\xac\xa1\x01\xed\x16\xed\x01Z\xb6\x96\x89e\xea=\xb7UF`\x00\x00\u07d4\x97\x9f0\x15\x8bWK\x99\x9a\xab4\x81\a\xb9\xee\xd8[\x1f\xf8\xc1\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x97\xa8o\x01\xce?|\xfdDA3\x0e\x1c\x9b\x19\xe1\xb1\x06\x06\xef\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x97\xb9\x1e\xfesP\xc2\xd5~~@k\xab\x18\xf3a{\xcd\xe1J\x8a\x02\x1e\x19\x99\xbb\xd5\u04be\x00\x00\u07d4\x97\xd0\xd9r^;p\xe6u\x841s\x93\x8e\xd3q\xb6,\u007f\xac\x89\t79SM(h\x00\x00\u07d4\x97\xd9\xe4jv\x04\u05f5\xa4\xeaN\xe6\x1aB\xb3\xd25\x0f\xc3\xed\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97\xdc&\xecg\n1\xe0\"\x1d*u\xbc]\xc9\xf9\f\x1fo\u0509\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x97\xde!\xe4!\xc3\u007f\xe4\xb8\x02_\x9aQ\xb7\xb3\x90\xb5\xdfx\x04\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\x97\xe2\x89s\xb8`\xc5g@(\x00\xfb\xb6<\xe3\x9a\x04\x8a=y\x89\x05B%:\x12l\xe4\x00\x00\u07d4\x97\xe5\xcca'\xc4\xf8\x85\xbe\x02\xf4KB\xd1\u0230\xac\x91\u44c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97\xf1\xfeL\x80\x83\xe5\x96!*\x18w(\xdd\\\xf8\n1\xbe\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x97\xf7v\x06W\xc1\xe2\x02u\x90\x86\x96>\xb4!\x1c_\x819\xb9\x8a\n\x8a\t\u007f\xcb=\x17h\x00\x00\xe0\x94\x97\xf9\x9bk\xa3\x13F\u0358\xa9\xfeL0\x8f\x87\u0165\x8cQQ\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x98\n\x84\xb6\x86\xfc1\xbd\xc8<\"\x10XTjq\xb1\x1f\x83\x8a\x89*AUH\xaf\x86\x81\x80\x00\u07d4\x98\x10\xe3J\x94\xdbn\xd1V\xd08\x9a\x0e+\x80\xf4\xfdk\n\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\x1d\xdf\x04\x04\xe4\xd2-\xdaUj\a&\xf0\v-\x98\xab\x95i\x8965f3\xeb\xd8\xea\x00\x00\xe0\x94\x98\x1fq'u\xc0\xda\xd9u\x18\xff\xed\xcbG\xb9\xad\x1dl'b\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\x984h!\x80\xb9\x82\xd1f\xba\u06dd\x9d\x1d\x9b\xbf\x01m\x87\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x986\xb4\xd3\x04sd\x1a\xb5j\xee\xe1\x92Bv\x1drrQx\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x989sB\xec_=L\xb8w\xe5N\xf5\xd6\xf1\xd3fs\x1b\u050a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\x98Fd\x886\xa3\a\xa0W\x18O\xd5\x1fb\x8a_\x8c\x12B|\x8a\x04\vi\xbfC\xdc\xe8\xf0\x00\x00\xe0\x94\x98Jy\x85\xe3\xcc~\xb5\xc96\x91\xf6\xf8\xcc{\x8f$]\x01\xb2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x98]p\xd2\a\x89+\xed9\x85\x90\x02N$!\xb1\xcc\x11\x93Y\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x98m\xf4~v\xe4\u05e7\x89\xcd\xee\x91<\u0243\x16P\x93l\x9d\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x98t\x80?\xe1\xf3\xa06^y\"\xb1Bp\xea\xeb\x03,\xc1\xb5\x89<\xf5\x92\x88$\xc6\xc2\x00\x00\u07d4\x98ub4\x95\xa4l\xdb\xf2YS\x0f\xf88\xa1y\x9e\u00c9\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98v\x18\xc8VV |{\xac\x15\a\xc0\xff\xef\xa2\xfbd\xb0\x92\x89\x03}\xfeC1\x89\xe3\x80\x00\u07d4\x98|\x9b\xcdn?9\x90\xa5+\xe3\xed\xa4q\f'Q\x8fOr\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x98\x82\x96|\xeeh\u04a89\xfa\u062bJ|=\xdd\xf6\xc0\xad\u0209Hx\xbe\x1f\xfa\xf9]\x00\x00\u07d4\x98\x85\\}\xfb\xee3SD\x90J\x12\xc4\fs\x17\x95\xb1:T\x899\xfb\xae\x8d\x04-\xd0\x00\x00\u07d4\x98\x9c\f\xcf\xf6T\xda\x03\xae\xb1\x1a\xf7\x01\x05Ea\xd6)~\x1d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x98\xa0\xe5Lm\x9d\u023e\x96'l\xeb\xf4\xfe\xc4`\xf6#]\x85\x89j\u0202\x10\tR\u01c0\x00\u07d4\x98\xb7i\xcc0\\\xec\xfbb\x9a\x00\xc9\a\x06\x9d~\xf9\xbc:\x12\x89\x01h\u048e?\x00(\x00\x00\xe0\x94\x98\xbaN\x9c\xa7/\xdd\xc2\fi\xb49ov\xf8\x18?z*N\x8a\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\x00\u07d4\x98\xbeimQ\xe3\x90\xff\x1cP\x1b\x8a\x0fc1\xb6(\xdd\u016d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\xbe\u04e7.\xcc\xfb\xaf\xb9#H\x92\x93\xe4)\xe7\x03\xc7\xe2[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\xbfJ\xf3\x81\v\x84#\x87\xdbp\xc1MF\t\x96&\x00=\x10\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x98\xc1\x0e\xbf,O\x97\u02e5\xa1\xab?*\xaf\xe1\xca\xc4#\xf8\u02c9\x10CV\x1a\x88)0\x00\x00\u07d4\x98\xc1\x9d\xba\x81\v\xa6\x11\xe6\x8f/\x83\xee\x16\xf6\xe7tO\f\x1f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x98\xc5IJ\x03\xac\x91\xa7h\xdf\xfc\x0e\xa1\xdd\u0b3f\x88\x90\x19\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\x98\xd2\x04\xf9\b_\x8c\x8e}\xe2>X\x9bd\xc6\xef\xf6\x92\xccc\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x98\xd3s\x19\x92\xd1\xd4\x0e\x12\x11\xc7\xf75\xf2\x18\x9a\xfa\a\x02\xe0\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x98\xe2\xb6\xd6\x06\xfd-i\x91\xc9\xd6\xd4\a\u007f\xdf?\xddE\x85\u06890\xdf\x1ao\x8a\xd6(\x00\x00\u07d4\x98\xe3\xe9\v(\xfc\xca\ue087y\xb8\xd4\nUh\xc4\x11n!\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x98\xe6\xf5G\u06c8\xe7_\x1f\x9c\x8a\xc2\xc5\xcf\x16'\xbaX\v>\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x98\xf4\xaf:\xf0\xae\xde_\xaf\xdcB\xa0\x81\xec\xc1\xf8\x9e<\xcf \x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x98\xf6\xb8\xe6!=\xbc\x9aU\x81\xf4\xcc\xe6e_\x95%+\xdb\a\x89\x11Xr\xb0\xbc\xa40\x00\x00\u07d4\x99\te\r\u05719{\x8b\x8b\x0e\xb6\x94\x99\xb2\x91\xb0\xad\x12\x13\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x99\x11s`\x19G\xc2\bJb\xd69R~\x96\x15\x12W\x9a\xf9\x89 \x86\xac5\x10R`\x00\x00\u07d4\x99\x12\x9d[<\f\xdeG\xea\r\xefM\xfc\a\r\x1fJY\x95'\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x99\x17\u058dJ\xf3A\xd6Q\xe7\xf0\a\\m\xe6\xd7\x14Nt\t\x8a\x012\xd4Gl\b\xe6\xf0\x00\x00\u07d4\x99\x1a\xc7\xcap\x97\x11_& ^\xee\x0e\xf7\xd4\x1e\xb4\xe3\x11\xae\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\x99#e\xd7d\xc5\xce5@9\xdd\xfc\x91.\x02:u\xb8\xe1h\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x99&F\xac\x1a\u02ab\xf5\u076b\xa8\xf9B\x9a\xa6\xa9Nt\x96\xa7\x8967Pz0\xab\xeb\x00\x00\u07d4\x99&\x83'\xc3s3.\x06\xc3\xf6\x16B\x87\xd4U\xb9\xd5\xfaK\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99(\xffqZ\xfc:+`\xf8\xebL\u013aN\xe8\u06b6\u5749\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4\x992\xef\x1c\x85\xb7Z\x9b*\x80\x05}P\x874\xc5\x10\x85\xbe\u0309\x02\xb8?\xa50\x1dY\x00\x00\xe0\x94\x99?\x14ax`^f\xd5\x17\xbex.\xf0\xb3\xc6\x1aN\x19%\x8a\x01|\x1f\x055\u05e5\x83\x00\x00\xe0\x94\x99A7\x04\xb1\xa3.p\xf3\xbc\ri\u0748\x1c8VkT\u02ca\x05\xcckiF1\xf7\x12\x00\x00\u07d4\x99AR\xfc\x95\xd5\xc1\u028b\x88\x11:\xbb\xadMq\x0e@\xde\xf6\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x99D\xfe\xe9\xd3JJ\x88\x00#\u01c92\xc0\vY\xd5\xc8*\x82\x89(\xa8\xa5k6\x90\a\x00\x00\u07d4\x99L\u00b5\"~\xc3\xcf\x04\x85\x12F|A\xb7\xb7\xb7H\x90\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99q\xdf`\xf0\xaef\xdc\xe9\xe8\xc8N\x17\x14\x9f\t\xf9\xc5/d\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x99v\x94~\xff_j\xe5\xda\b\xddT\x11\x92\xf3x\xb4(\xff\x94\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x99}e\x92\xa3\x15\x89\xac\xc3\x1b\x99\x01\xfb\xeb<\xc3\xd6[2\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\x82\xa5\x89\x0f\xfbT\x06\u04ec\xa8\u04bf\xc1\xddp\xaa\xa8\n\xe0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\x87\x8f\x9dn\n~\u066e\u01c2\x97\xb78y\xa8\x01\x95\xaf\xe0\x89\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d4\x99\x8c\x1f\x93\xbc\xdbo\xf2<\x10\xd0\u0712G(\xb7;\xe2\xff\x9f\x896[\xf3\xa43\xea\xf3\x00\x00\u07d4\x99\x91aL[\xaaG\xddl\x96\x87FE\xf9z\xdd,=\x83\x80\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x99\x92J\x98\x16\xbb}\xdf?\xec\x18D\x82\x8e\x9a\xd7\xd0k\xf4\xe6\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x99\x99vh\xf7\xc1\xa4\xff\x9e1\xf9\x97z\xe3\"K\u02c8z\x85\x89\x0f\xc969(\x01\xc0\x00\x00\u07d4\x99\x9cI\xc1t\xca\x13\xbc\x83l\x1e\n\x92\xbf\xf4\x8b'\x15C\u0289\xb1\xcf$\xdd\u0431@\x00\x00\u07d4\x99\xa4\xde\x19\xde\u05d0\b\xcf\xdc\xd4]\x01M.XK\x89\x14\xa8\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x99\xa9k\xf2$.\xa1\xb3\x9e\xceo\xcc\r\x18\xae\xd0\f\x01y\xf3\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x99\xb0\x18\x93+\xca\xd3U\xb6y+%]\xb6p-\xec\x8c\xe5\u0749\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x99\xb7C\xd1\xd9\xef\xf9\r\x9a\x194\xb4\xdb!\xd5\x19\u061bJ8\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x99\xb8\xc8$\x86\x9d\xe9\xed$\xf3\xbf\xf6\x85L\xb6\xddE\xcc?\x9f\x89e\xea=\xb7UF`\x00\x00\u07d4\x99\xc0\x17L\xf8N\a\x83\xc2 \xb4\xebj\xe1\x8f\xe7\x03\x85J\u04c9py\xa2W=\fx\x00\x00\u07d4\x99\xc1\xd9\xf4\fj\xb7\xf8\xa9/\xce/\xdc\xe4zT\xa5\x86\xc5?\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x99\xc26\x14\x1d\xae\xc87\xec\xe0O\xda\xee\x1d\x90\u03cb\xbd\xc1\x04\x89ve\x16\xac\xac\r \x00\x00\u07d4\x99\xc3\x1f\xe7HX7\x87\xcd\xd3\xe5%\xb2\x81\xb2\x18\x96\x179\xe3\x897\b\xba\xed=h\x90\x00\x00\u07d4\x99\xc4u\xbf\x02\xe8\xb9!J\xda_\xad\x02\xfd\xfd\x15\xba6\\\f\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\x99\u0203%\x85F\xcc~N\x97\x1fR.8\x99\x18\xda^\xa6:\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x99\xc9\xf9>E\xfe<\x14\x18\xc3S\xe4\u016c8\x94\xee\xf8\x12\x1e\x89\x05\x85\xba\xf1E\x05\v\x00\x00\xe0\x94\x99\xd1W\x9c\xd4&\x82\xb7dN\x1dOq(D\x1e\xef\xfe3\x9d\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x99\u0475\x85\x96_@jB\xa4\x9a\x1c\xa7\x0fv\x9evZ?\x98\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\x99\xdf\xd0PL\x06\xc7C\xe4e4\xfd{U\xf1\xf9\xc7\xec3)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\xf4\x14|\xcck\u02c0\u0304.i\xf6\xd0\x0e0\xfaA3\u0649\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x99\xf7\u007f\x99\x8b \xe0\xbc\xdc\xd9\xfc\x83\x86ARl\xf2Y\x18\xef\x89a\t=|,m8\x00\x00\u07d4\x99\xfa\xd5\x008\xd0\xd9\xd4\xc3\xfb\xb4\xbc\xe0V\x06\xec\xad\xcdQ!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\xfe\r \x12(\xa7S\x14VU\xd4(\xeb\x9f\xd9I\x85\xd3m\x89i \xbf\xf3QZ:\x00\x00\u07d4\x9a\a\x9c\x92\xa6)\xca\x15\xc8\xca\xfa.\xb2\x8d[\xc1z\xf8(\x11\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9a\r<\xee=\x98\x92\xea;7\x00\xa2\u007f\xf8A@\xd9\x02T\x93\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x9a$\u038dH\\\xc4\xc8nI\u07b3\x90\"\xf9,t0\xe6~\x89Fy\x1f\xc8N\a\xd0\x00\x00\u07d4\x9a,\xe4;]\x89\u0593k\x8e\x8c5G\x91\xb8\xaf\xff\x96$%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9a9\x01bS^9\x88w\xe4\x16x}b9\xe0uN\x93|\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9a=\xa6P#\xa10 \xd2!E\xcf\xc1\x8b\xab\x10\xbd\x19\xceN\x89\x18\xbfn\xa3FJ:\x00\x00\xe0\x94\x9a>+\x1b\xf3F\xdd\a\v\x02sW\xfe\xacD\xa4\xb2\xc9}\xb8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9aL\xa8\xb8!\x17\x89NC\xdbr\xb9\xfax\xf0\xb9\xb9:\xce\t\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x9aR.R\xc1\x95\xbf\xb7\xcf_\xfa\xae\u06d1\xa3\xbath\x16\x1d\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9aZ\xf3\x1c~\x063\x9a\u0234b\x8d|M\xb0\xce\x0fE\u0224\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\x9ac?\xcd\x11,\xce\xebv_\xe0A\x81ps*\x97\x05\u7708\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x9ac\u0445\xa7\x91)\xfd\xab\x19\xb5\x8b\xb61\xea6\xa4 TN\x89\x02F\xdd\xf9yvh\x00\x00\u07d4\x9ag\b\u0778\x90<(\x9f\x83\xfe\x88\x9c\x1e\xdc\xd6\x1f\x85D#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9ao\xf5\xf6\xa7\xaf{z\xe0\xed\x9c \xec\xecP#\u0481\xb7\x86\x89\x8a\x12\xb9\xbdjg\xec\x00\x00\xe0\x94\x9a\x82\x82m<)H\x1d\xcc+\u0495\x00G\xe8\xb6\x04\x86\xc38\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9a\x8e\xcaA\x89\xffJ\xa8\xff~\u0536\xb7\x03\x9f\t\x02!\x9b\x15\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9a\x95;[\xccp\x93y\xfc\xb5Y\u05f9\x16\xaf\u06a5\f\xad\u0309\x05k\xc7^-c\x10\x00\x00\u07d4\x9a\x99\v\x8a\xebX\x8d~\xe7\xec.\xd8\xc2\xe6Os\x82\xa9\xfe\xe2\x89\x01\xd1'\xdbi\xfd\x8b\x00\x00\u07d4\x9a\x9d\x1d\xc0\xba\xa7}n \xc3\xd8I\u01c8b\xdd\x1c\x05L\x87\x89/\xb4t\t\x8fg\xc0\x00\x00\xe0\x94\x9a\xa4\x8cf\xe4\xfbJ\u0419\x93N2\x02.\x82t'\xf2w\xba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9a\xa80\x8fB\x91\x0eZ\xde\t\xc1\xa5\xe2\x82\xd6\xd9\x17\x10\xbd\xbf\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9a\xaa\xfa\x00gd~\u0659\x06kzL\xa5\xb4\xb3\xf3\xfe\xaao\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9a\xb9\x88\xb5\x05\xcf\xee\x1d\xbe\x9c\u044e\x9bTs\xb9\xa2\xd4\xf56\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\x9a\xb9\x8dm\xbb\x1e\xaa\xe1mE\xa0EhT\x1a\xd3\xd8\xfe\x06\u0309\x0e\xc5\x04d\xfe#\xf3\x80\x00\xe0\x94\x9a\xba+^'\xffx\xba\xaa\xb5\xcd\u0248\xb7\xbe\x85\\\xeb\xbd\u038a\x02\x1e\f\x00\x13\a\n\xdc\x00\x00\u07d4\x9a\xc4\xdaQ\xd2x\"\xd1\xe2\b\xc9n\xa6J\x1e[U)\x97#\x89\x05lUy\xf7\"\x14\x00\x00\u0794\x9a\xc8S\x97y*i\u05cf(k\x86C*\a\xae\u03b6\x0ed\x88\xc6s\xce<@\x16\x00\x00\xe0\x94\x9a\xc9\a\xee\x85\xe6\xf3\xe2#E\x99\x92\xe2V\xa4?\xa0\x8f\xa8\xb2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9a\xd4\u007f\xdc\xf9\u0354-(\xef\xfd[\x84\x11[1\xa6X\xa1>\x89\xb2Y\xec\x00\xd5;(\x00\x00\u07d4\x9a\xdb\u04fc{\n\xfc\x05\xd1\xd2\xed\xa4\x9f\xf8c\x93\x9cH\xdbF\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x9a\xdfE\x8b\xff5\x99\xee\xe1\xa2c\x98\x85\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x9a\xf9\xdb\xe4t\"\xd1w\xf9E\xbd\xea\xd7\xe6\xd8)05b0\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x9a\xfaSkLf\xbc8\xd8u\u0133\x00\x99\xd9&\x1f\xdb8\xeb\x89\v*\x8f\x84*w\xbc\x80\x00\u07d4\x9b\x06\xad\x84\x1d\xff\xbeL\xcfF\xf1\x03\x9f\u00c6\xf3\xc3!Dn\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\x11h\u078a\xb6KGU/3\x89\x80\n\x9c\xc0\x8bFf\u03c9]\u0212\xaa\x111\xc8\x00\x00\u07d4\x9b\x18\x11\xc3\x05\x1fF\xe6d\xaeK\xc9\xc8$\u0445\x92\xc4WJ\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x9b\x18G\x86U\xa4\x85\x1c\xc9\x06\xe6`\xfe\xaca\xf7\xf4\u023f\xfc\x89\xe2G\x8d8\x90}\x84\x00\x00\u07d4\x9b\"\xa8\r\\{3t\xa0[D`\x81\xf9}\n4\a\x9e\u007f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x9b+\xe7\xf5gT\xf5\x05\xe3D\x1a\x10\xf7\xf0\xe2\x0f\xd3\xdd\xf8I\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\x9b2\xcfOQ\x15\xf4\xb3J\x00\xa6La}\xe0c\x875C#\x89\x05\xb8\x1e\u0608 |\x80\x00\u07d4\x9bC\u0739_\xde1\x80u\xa5g\xf1\xe6\xb5v\x17\x05^\xf9\xe8\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x9bDO\xd37\xe5\xd7R\x93\xad\xcf\xffp\xe1\xea\x01\xdb\x022\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9bH$\xff\x9f\xb2\xab\xdaUM\xeeO\xb8\xcfT\x91eW\x061\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9bL'\x15x\f\xa4\xe9\x9e`\xeb\xf2\x19\xf1Y\f\x8c\xadP\n\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x9bY\xeb!;\x1eue\xe4PG\xe0N\xa07O\x10v-\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\\9\xf7\xe0\xac\x16\x8c\x8e\xd0\xed4\x04w\x11}\x1bh.\xe9\x89\x05P\x05\xf0\xc6\x14H\x00\x00\u07d4\x9b^\xc1\x8e\x83\x13\x88}\xf4a\u0490.\x81\xe6z\x8f\x11;\xb1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9bd\xd3\u034d+s\xf6hA\xb5\xc4k\xb6\x95\xb8\x8a\x9a\xb7]\x89\x01 :Ov\f\x16\x80\x00\u07d4\x9be\x8f\xb3a\xe0F\xd4\xfc\xaa\x8a\xefm\x02\xa9\x91\x11\"6%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9bfA\xb1>\x17/\xc0r\xcaK\x83'\xa3\xbc(\xa1[f\xa9\x89\x06\x81U\xa46v\xe0\x00\x00\xe0\x94\x9bh\xf6t\x16\xa6;\xf4E\x1a1\x16L\x92\xf6r\xa6\x87Y\xe9\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\x9bw6i\xe8}v\x01\x8c\t\x0f\x82U\xe5D\t\xb9\u0728\xb2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9bw\xeb\xce\xd7\xe2\x15\xf0\x92\x0e\x8c+\x87\x00$\xf6\xec\xb2\xff1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9b|\x88\x10\xcc|\u021e\x80Nm>8\x12\x18PG(w\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\xa5=\xc8\xc9^\x9aG/\xeb\xa2\xc4\xe3,\x1d\xc4\xdd{\xabF\x89Hz\x9a0E9D\x00\x00\xe0\x94\x9b\xac\xd3\xd4\x0f;\x82\xac\x91\xa2d\xd9\u060d\x90\x8e\xac\x86d\xb9\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9b\xb7`\xd5\u0089\xa3\xe1\xdb\x18\xdb\tSE\xcaA;\x9aC\u0089\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x9b\xb7b\x04\x18j\xf2\xf6;\xe7\x91h`\x16\x87\xfc\x9b\xadf\x1f\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9b\xb9\xb0*&\xbf\xe1\xcc\xc3\xf0\xc6!\x9e&\x1c9\u007f\xc5\xcax\x89Hz\x9a0E9D\x00\x00\u07d4\x9b\xc5s\xbc\xda#\xb8\xb2o\x90s\xd9\f#\x0e\x8eq\xe0'\v\x896/u\xa40]\f\x00\x00\u07d4\x9b\xd7\u00caB\x100JMe>\xde\xff\x1b<\xe4_\xcexC\x89\x0fI\x89A\xe6d(\x00\x00\xe0\x94\x9b\u0600h\xe10u\xf3\xa8\xca\xc4d\xa5\xf9I\xd6\xd8\x18\xc0\xf6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9b\xd9\x05\xf1q\x9f\u01ec\xd0\x15\x9dM\xc1\xf8\xdb/!G#8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9b\xdb\u071b\x9741\xd1<\x89\xa3\xf9u~\x9b;bu\xbf\u01c9\x1b\x1a}\u03caD\u04c0\x00\u07d4\x9b\xe3\xc3)\xb6*(\xb8\xb0\x88l\xbd\x8b\x99\xf8\xbc\x93\f\xe3\xe6\x89\x04\t\xe5+H6\x9a\x00\x00\xe0\x94\x9b\xf5\x8e\xfb\xea\a\x84\xeb\x06\x8a\xde\u03e0\xbb!P\x84\xc7:5\x8a\x01:k+VHq\xa0\x00\x00\u07d4\x9b\xf6r\xd9y\xb3fR\xfcR\x82Tzjk\xc2\x12\xaeCh\x89#\x8f\xd4,\\\xf0@\x00\x00\xe0\x94\x9b\xf7\x03\xb4\x1c6$\xe1_@T\x96#\x90\xbc\xba0R\xf0\xfd\x8a\x01H>\x01S<.<\x00\x00\u07d4\x9b\xf7\x1f\u007f\xb57\xacT\xf4\xe5\x14\x94\u007f\xa7\xffg(\xf1m/\x89\x01\u03c4\xa3\n\n\f\x00\x00\u07d4\x9b\xf9\xb3\xb2\xf2<\xf4a\xebY\x1f(4\v\xc7\x19\x93\x1c\x83d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x9b\xfce\x9c\x9c`\x1e\xa4*k!\xb8\xf1p\x84\xec\x87\xd7\x02\x12\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9b\xff\xf5\r\xb3jxUU\xf0vR\xa1S\xb0\xc4+\x1b\x8bv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\x05\xe9\xd0\xf0u\x8eyS\x03q~1\xda!<\xa1W\u618965\u026d\xc5\u07a0\x00\x00\u07d4\x9c\x1bw\x1f\t\xaf\x88*\xf0d0\x83\xde*\xa7\x9d\xc0\x97\xc4\x0e\x89\x86p\xe9\xece\x98\xc0\x00\x00\u07d4\x9c(\xa2\xc4\b`\x91\xcb]\xa2&\xa6W\xce2H\xe8\xea{o\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\x9c/\xd5@\x89\xaff]\xf5\x97\x1ds\xb8\x04a`9dsu\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c4@\x98\xbaaZ9\x8f\x11\xd0\t\x90[\x17|D\xa7\xb6\x02\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c=\x06\x92\xce\xee\xf8\n\xa4\x96\\\xee\xd2b\xff\xc7\xf0i\xf2\u0709\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9c@\\\xf6\x97\x95a8\x06^\x11\xc5\xf7U\x9eg$[\u0465\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9cE *%\xf6\xad\x00\x11\xf1\x15\xa5\xa7\"\x04\xf2\xf2\x19\x88f\x8a\x01\x0f\xcf:b\xb0\x80\x98\x00\x00\xe0\x94\x9cI\xde\xffG\b_\xc0\x97\x04\u02a2\u0728\u0087\xa9\xa17\u068a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x9cK\xbc\xd5\xf1dJo\aX$\xdd\xfe\x85\xc5q\u05ab\xf6\x9c\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4\x9cRj\x14\x06\x83\xed\xf1C\x1c\xfa\xa1(\xa95\xe2\xb6\x14\u060b\x89\x06\x04o7\xe5\x94\\\x00\x00\xe0\x94\x9cT\xe4\xedG\x9a\x85h)\u01bbB\u069f\vi*u\xf7(\x8a\x01\x97\xa8\xf6\xddU\x19\x80\x00\x00\xe0\x94\x9cX\x1a`\xb6\x10(\xd94\x16y)\xb2-p\xb3\x13\xc3O\u040a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x9c\\\xc1\x11\t,\x12!\x16\xf1\xa8_N\xe3\x14\bt\x1a}/\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\x9ck\u0264k\x03\xaeT\x04\xf0C\xdf\xcf!\x88>A\x10\xcc3\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9cx\x96?\xbc&<\t\xbdr\xe4\xf8\xde\xf7J\x94u\xf7\x05\\\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4\x9cx\xfb\xb4\xdfv\x9c\xe2\xc1V\x92\f\xfe\xdf\xda\x03:\x0e%J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x9c{m\xc5\x19\x0f\xe2\x91)c\xfc\xd5yh>\xc79Q\x16\xb0\x89*\x11)\u0413g \x00\x00\u07d4\x9c\x80\xbc\x18\xe9\xf8\u0516\x8b\x18]\xa8\u01df\xa6\xe1\x1f\xfc>#\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x9c\x98\xfd\xf1\xfd\u034b\xa8\xf4\u0170L:\xe8X~\xfd\xf0\xf6\xe6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x9c\x99\xa1\u0691\u0552\v\xc1N\f\xb9\x14\xfd\xf6+\x94\u02c3X\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9c\x99\xb6&\x06(\x1b\\\xef\xab\xf3aV\xc8\xfeb\x83\x9e\xf5\xf3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x9c\x9a\a\xa8\xe5|1r\xa9\x19\xefdx\x94tI\x0f\r\x9fQ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9c\x9d\xe4G$\xa4\x05M\xa0\xea\xa6\x05\xab\u0300&hw\x8b\xea\x89\n\xd7\xd5\xca?\xa5\xa2\x00\x00\u07d4\x9c\x9f;\x8a\x81\x1b!\xf3\xff?\xe2\x0f\xe9p\x05\x1c\xe6j\x82O\x89>\xc2\u07bc\a\u053e\x00\x00\xe0\x94\x9c\x9f\x89\xa3\x91\x0fj*\xe8\xa9\x10G\xa1z\xb7\x88\xbd\xde\xc1p\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9c\xa0B\x9f\x87O\x8d\xce\xe2\xe9\xc0b\xa9\x02\n\x84*Xz\xb9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\xa4.\u7838\x98\xf6\xa5\xcc`\xb5\xa5\u05f1\xbf\xa3\xc321\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\xb2\x8a\xc1\xa2\n\x10o\u007f76\x92\xc5\xceLs\xf172\xa1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c\xcd\u0732\xcf\u00b2[\br\x9a\n\x98\xd9\xe6\xf0 .\xa2\xc1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9c\xe2\u007f$^\x02\xd1\xc3\x12\xc1\xd5\x00x\x8c\x9d\xefv\x90E;\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9c\xe56;\x13\xe8#\x8a\xa4\xdd\x15\xac\u0432\xe8\xaf\xe0\x872G\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9c\xf2\x92\x8b\xee\xf0\x9a@\xf9\xbf\xc9S\xbe\x06\xa2Q\x11a\x82\xfb\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9d\x06\x91\x97\xd1\xdeP\x04Z\x18o^\xc7D\xac@\u8bd1\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\x9d\x0e}\x92\xfb0XS\u05d8&;\xf1^\x97\xc7+\xf9\xd7\xe0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9d\x0f4~\x82k}\u03aa\xd2y\x06\n5\xc0\x06\x1e\xcf3K\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d u\x17B,\xc0\xd6\r\xe7\xc27\tzMO\xce \x94\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x9d%\n\xe4\xf1\x10\xd7\x1c\xaf\u01f0\xad\xb5.\x8d\x9a\xcbfy\xb8\x8a\x02\x15mn\x99r\x13\xc0\x00\x00\xe0\x94\x9d+\xfc6\x10o\x03\x82P\xc0\x18\x01hW\x85\xb1l\x86\xc6\r\x8aPw\xd7]\xf1\xb6u\x80\x00\x00\xe0\x94\x9d0\xcb#{\xc0\x96\xf1p6\xfc\x80\xdd!\xcah\x99,\xa2\u064a\x06n\xe71\x8f\u070f0\x00\x00\u07d4\x9d2\x96.\xa9\x97\x00\xd92(\xe9\xdb\xda\xd2\xcc7\xbb\x99\xf0~\x89\xb4c+\xed\xd4\xde\xd4\x00\x00\u07d4\x9d4\xda\xc2[\xd1X(\xfa\xef\xaa\xf2\x8fq\aS\xb3\x9e\x89\u0709;\x1cV\xfe\xd0-\xf0\x00\x00\u07d4\x9d6\x91e\xfbp\xb8\x1a:v_\x18\x8f\xd6\f\xbe^{\th\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9d@\xe0\x12\xf6\x04%\xa3@\xd8-\x03\xa1\xc7W\xbf\xab\xc7\x06\xfb\x89\t4o:\xdd\u020d\x80\x00\u07d4\x9dAt\xaaj\xf2\x84v\xe2)\xda\xdbF\x18\b\b\xc6u\x05\xc1\x89B\x1a\xfd\xa4.\u0597\x00\x00\u07d4\x9dB\x133\x9a\x01U\x18avL\x87\xa9<\xe8\xf8_\x87\x95\x9a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9dF\f\x1b7\x9d\xdb\x19\xa8\xc8[LgG\x05\r\xdf\x17\xa8u\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x9dG\xba[L\x85\x05\xad\x8d\xa4)4(\va\xa0\xe1\xe8\xb9q\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9dM2\x11w%n\xbd\x9a\xfb\xda0A5\xd5\x17\xc3\xdcV\x93\x89!d\xb7\xa0J\u0220\x00\x00\u07d4\x9dO\xf9\x89\xb7\xbe\u066b\x10\x9d\x10\xc8\xc7\xe5_\x02\xd7g4\xad\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x9dQ\x15C\xb3\xd9\xdc`\xd4\u007f\t\u051d\x01\xb6\u0118\xd8 x\x8a\x02a\x97\xb9Qo\u00d4\x00\x00\u07d4\x9dn\u03e0:\xf2\xc6\xe1D\xb7\xc4i*\x86\x95\x1e\x90.\x9e\x1f\x89\xa2\xa5\xaa`\xad$?\x00\x00\u07d4\x9dvU\xe9\xf3\xe5\xba]n\x87\xe4\x12\xae\xbe\x9e\xe0\u0512G\ue24e\t1\x1c\x1d\x80\xfa\x00\x00\u07d4\x9dx1\xe84\xc2\v\x1b\xaaiz\xf1\xd8\xe0\xc6!\u016f\xff\x9a\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4\x9dx\xa9u\xb7\xdb^M\x8e(\x84\\\xfb\xe7\xe3\x14\x01\xbe\r\u0649H\xa40k\xa2\u5e5c\x8ahX\u02f5,\f\xf75\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\x9d\u007f\xdapp\xbf>\xe9\xbb\u0664\x1fU\xca\u0505J\xe6\xc2,\x8a\x02U\u02e3\xc4o\xcf\x12\x00\x00\u07d4\x9d\x81\xae\xa6\x9a\xedj\xd0p\x89\xd6\x14E4\x8c\x17\xf3K\xfc[\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9d\x91\x1f6\x82\xf3/\xe0y.\x9f\xb6\xff<\xfcG\xf5\x89\xfc\xa5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\x91;]3\x9c\x95\xd8wEV%c\xfe\xa9\x8b#\xc6\f\u0109\tA0,\u007fM#\x00\x00\u07d4\x9d\x93\xfa\xb6\xe2(E\xf8\xf4Z\aIo\x11\xdeqS\r\xeb\u01c9lO\xd1\xee$nx\x00\x00\u07d4\x9d\x99\xb1\x89\xbb\u0664\x8f\xc2\xe1n\x8f\u0363;\xb9\x9a1{\xbb\x89=\x16\xe1\vm\x8b\xb2\x00\x00\u07d4\x9d\x9cN\xfe\x9fC9\x89\xe2;\xe9@I!S)\xfaU\xb4\u02c9\r\u3c89\x03\u01b5\x80\x00\u07d4\x9d\x9eW\xfd\xe3\x0ePh\xc0>I\x84\x8e\xdc\xe3C\xb7\x02\x83X\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x9d\xa30\"@\xaf\x05\x11\xc6\xfd\x18W\xe6\u07779Ow\xabk\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\x9d\xa4\xec@pw\xf4\xb9p{-\x9d.\xde^\xa5(+\xf1\u07c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\xa6\t\xfa:~l\xf2\xcc\x0ep\u036b\xe7\x8d\xc4\xe3\x82\xe1\x1e\x89A\rXj \xa4\xc0\x00\x00\xe0\x94\x9d\xa6\x1c\xcdb\xbf\x86\x06V\xe02]qW\xe2\xf1`\xd9;\xb5\x8a\x01\x0f\f\xa9V\xf8y\x9e\x00\x00\xe0\x94\x9d\xa6\xe0u\x98\x9ct\x19\tL\xc9\xf6\xd2\u44d3\xbb\x19\x96\x88\x8a\x02Y\xbbq\u056d\xf3\xf0\x00\x00\u07d4\x9d\xa8\xe2,\xa1\x0eg\xfe\xa4NR^GQ\xee\xac6\xa3\x11\x94\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\x9d\xb2\xe1\\\xa6\x81\xf4\xc6`H\xf6\xf9\xb7\x94\x1e\u040b\x1f\xf5\x06\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\xc1\x0f\xa3\x8f\x9f\xb0h\x10\xe1\x1f`\x17>\xc3\xd2\xfdju\x1e\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x9d\xd2\x19f$\xa1\xdd\xf1J\x9d7^_\a\x15+\xaf\"\xaf\xa2\x89A\xb0^$c\xa5C\x80\x00\u07d4\x9d\xd4k\x1cm?\x05\u279co\x03~\xed\x9aYZ\xf4\xa9\xaa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9d\xdd5^cN\xe9\x92~K\u007fl\x97\xe7\xbf:/\x1ehz\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x9d\xe2\n\xe7j\xa0\x82c\xb2\x05\xd5\x14$a\x96\x1e$\b\xd2f\x89\r\xa93\xd8\xd8\xc6p\x00\x00\u07d4\x9d\xe2\v\xc3~\u007fH\xa8\x0f\xfdz\xd8O\xfb\xf1\xa1\xab\xe1s\x8c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9d\xe78m\xde@\x1c\xe4\xc6{q\xb6U?\x8a\xa3N\xa5\xa1}\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x9d\xeb9\x02z\xf8w\x99+\x89\xf2\xecJ\x1f\x82.\xcd\xf1&\x93\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9d\xef\xe5j\x0f\xf1\xa1\x94}\xba\t#\xf7\xdd%\x8d\x8f\x12\xfaE\x8a\x05\xb1*\ufbe8\x04\x00\x00\x00\u07d4\x9d\xf0W\xcd\x03\xa4\xe2~\x8e\x03/\x85y\x85\xfd\u007f\x01\xad\xc8\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9d\xf3*P\x1c\vx\x1c\x02\x81\x02/B\xa1)?\xfd{\x89*\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4\x9e\x01vZ\xff\b\xbc\"\x05P\xac\xa5\xea.\x1c\xe8\u5c19#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9e \xe5\xfd6\x1e\xab\xcfc\x89\x1f[\x87\xb0\x92h\xb8\xeb7\x93\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9e#,\b\xc1M\xc1\xa6\xed\v\x8a;(h\x97{\xa5\xc1}\x10\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e#\xc5\u4dc2\xb0\n_\xad\U0006eb47\xda\xcf[\x03g\xa1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e59\x90q\xa4\xa1\x01\xe9\x19M\xaa?\t\xf0J\v_\x98p\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9e>\xb5\t'\x8f\xe0\xdc\xd8\xe0\xbb\xe7\x8a\x19N\x06\xb6\x809C\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\x9eBrrQk>g\xd4\xfc\xbf\x82\xf5\x93\x90\xd0L\x8e(\xe5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x9eL\xec5:\xc3\u3043^<\t\x91\xf8\xfa\xa5\xb7\u0428\xe6\x8a\x02\x1e\x18\xb9\xe9\xabE\xe4\x80\x00\u07d4\x9eX\x11\xb4\v\xe1\xe2\xa1\xe1\u048c;\at\xac\xde\n\t`=\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x9eZ1\x1d\x9fi\x89\x8a|j\x9dc`h\x048\xe6z{/\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x9e| P\xa2'\xbb\xfd`\x93~&\x8c\xea>h\xfe\xa8\xd1\xfe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9e\u007fe\xa9\x0e\x85\b\x86{\xcc\xc9\x14%j\x1e\xa5t\xcf\a\xe3\x89C8t\xf62\xcc`\x00\x00\xe0\x94\x9e\x81D\xe0\x8e\x89dx\x11\xfekr\xd4E\u05a5\xf8\n\xd2D\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9e\x8fd\xdd\xcd\u9e34Q\xba\xfa\xa25\xa9\xbfQ\x1a%\xac\x91\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\x9e\x95\x1fm\xc5\xe3R\xaf\xb8\xd0B\x99\xd2G\x8aE\x12Y\xbfV\x89\x03\xe7A\x98\x81\xa7:\x00\x00\u07d4\x9e\x96\r\xcd\x03\u057a\x99\xcb\x11]\x17\xffL\t$\x8a\xd4\u043e\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9e\xafj2\x8a@v\x02N\xfakg\xb4\x8b!\xee\xdc\xc0\xf0\xb8\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x9e\xb1\xffqy\x8f(\xd6\xe9\x89\xfa\x1e\xa0X\x8e'\xba\x86\xcb}\x89\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\x9e\xb2\x81\xc3'\x19\xc4\x0f\xdb>!m\xb0\xf3\u007f\xbcs\xa0&\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e\xb3\xa7\xcb^g&Bz:6\x1c\xfa\x8dad\xdb\u043a\x16\x89+\x95\xbd\xcc9\xb6\x10\x00\x00\u07d4\x9e\xb7\x83N\x17\x1dA\xe0i\xa7yG\xfc\xa8v\"\xf0\xbaNH\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x9e\xc0>\x02\u51f7v\x9d\xefS\x84\x13\xe9\u007f~U\xbeq\u060a\x04+\xf0kx\xed;P\x00\x00\u07d4\x9e\u02eb\xb0\xb2'\x82\xb3uD)\xe1uz\xab\xa0K\x81\x18\x9f\x89,\xa7\xbb\x06\x1f^\x99\x80\x00\u07d4\x9e\xce\x14\x00\x80\t6\xc7\xc6H_\xcd\xd3b`\x17\u041a\xfb\xf6\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4\x9e\xd4\xe6?ReB\xd4O\xdd\xd3MY\xcd%8\x8f\xfdk\u0689\u049b4\xa4cH\x94\x00\x00\u07d4\x9e\xd8\x0e\xda\u007fU\x05M\xb9\xfbR\x82E\x16\x88\xf2k\xb3t\xc1\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9e\u0710\xf4\xbe!\be!J\xb5\xb3^Z\x8d\xd7t\x15'\x9d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9e\u07acL\x02k\x93\x05M\u0171\xd6a\fo9`\xf2\xads\x89A\rXj \xa4\xc0\x00\x00\u07d4\x9e\xe9?3\x9eg&\xece\xee\xa4O\x8aK\xfe\x10\xda=2\x82\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9e\xe9v\f\xc2s\xd4pj\xa0\x83u\xc3\xe4o\xa20\xaf\xf3\u054a\x01\xe5.3l\xde\"\x18\x00\x00\u07d4\x9e\xeb\a\xbd+x\x90\x19^}F\xbd\xf2\a\x1bf\x17QM\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x9e\xefD-)\x1aD}t\xc5\xd2S\u011e\xf3$\xea\xc1\xd8\xf0\x89\xb9f\b\xc8\x10;\xf0\x00\x00\u07d4\x9e\xf1\x89k\x00|2\xa1Q\x14\xfb\x89\xd7=\xbdG\xf9\x12+i\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9f\x01w\x06\xb80\xfb\x9c0\ufc20\x9fPk\x91WEu4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x10\xf2\xa0F;e\xae0\xb0p\xb3\xdf\x18\xcfF\xf5\x1e\x89\xbd\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x9f\x19\xfa\u0223$7\xd8\n\u0183z\v\xb7\x84\x17)\xf4\x97.\x89#=\xf3)\x9far\x00\x00\u07d4\x9f\x1a\xa8\xfc\xfc\x89\xa1\xa52\x8c\xbdcD\xb7\x1f'\x8a,\xa4\xa0\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9f!0,\xa5\tk\xeat\x02\xb9\x1b\x0f\xd5\x06%O\x99\x9a=\x89C\x97E\x1a\x00=\xd8\x00\x00\u07d4\x9f'\x1d(U\x00\xd78F\xb1\x8fs>%\u074bO]J\x8b\x89'#\xc3F\xae\x18\b\x00\x00\u07d4\x9f4\x97\xf5\xef_\xe60\x95\x83l\x00N\xb9\xce\x02\xe9\x01;K\x89\"V\x86\x1b\xf9\xcf\b\x00\x00\xe0\x94\x9f:t\xfd^~\xdc\xc1\x16)\x93\x17\x13\x81\u02f62\xb7\xcf\xf0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9fF\xe7\xc1\xe9\a\x8c\xae\x860Z\xc7\x06\v\x01F}f\x85\xee\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\x9fIl\xb2\x06\x95c\x14M\b\x11g{\xa0\xe4q:\nAC\x89<\xd2\xe0\xbfc\xa4H\x00\x00\u07d4\x9fJq\x95\xac|\x15\x1c\xa2X\xca\xfd\xa0\u02b0\x83\xe0I\xc6\x02\x89SS\x8c2\x18\\\xee\x00\x00\u07d4\x9fJ\xc9\xc9\xe7\xe2L\xb2DJ\x04T\xfa[\x9a\xd9\xd9-8S\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4\x9f_D\x02kWjJ\xdbA\xe9YaV\x1dA\x03\x9c\xa3\x91\x89\r\x8drkqw\xa8\x00\x00\u07d4\x9f`{?\x12F\x9fDa!\u03bf4u5kq\xb42\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9fa\xbe\xb4o^\x85=\n\x85!\xc7Dnh\xe3L}\ts\x89\x1e[\x8f\xa8\xfe*\xc0\x00\x00\u07d4\x9fd\xa8\xe8\xda\xcfJ\xde0\xd1\x0fMY\xb0\xa3\u056b\xfd\xbft\x8966\x9e\xd7t}&\x00\x00\u07d4\x9ff.\x95'A!\xf1wVncm#\x96L\xf1\xfdho\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9fj2*mF\x99\x81Bj\xe8D\x86]~\xe0\xbb\x15\u01f3\x89\x02\xb5\xeeW\x92\x9f\u06c0\x00\u07d4\x9fy\x86\x92J\xeb\x02h|\xd6A\x89\x18\x9f\xb1g\xde\xd2\xdd\\\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x9fz\x03\x92\xf8Ws.0\x04\xa3u\xe6\xb1\x06\x8dI\xd801\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x82E\u00eb}\x171d\x86\x1c\u04d9\x1b\x94\xf1\xba@\xa9:\x89\x9b\ny\x1f\x12\x110\x00\x00\u07d4\x9f\x83\xa2\x93\xc3$\xd4\x10l\x18\xfa\xa8\x88\x8fd\u0499\x05L\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9f\x86\xa0f\xed\xb6\x1f\xcbXV\u0793\xb7\\\x8cy\x18d\xb9{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x98\xeb4\xd4iy\xb0\xa6\u078b\x05\xaaS:\x89\xb8%\xdc\xf1\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\xe0\x94\x9f\x9f\xe0\xc9_\x10\xfe\xe8z\xf1\xaf r6\xc8\xf3aN\xf0/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9f\xae\xa1\xc5\xe8\x1ez\xcb?\x17\xf1\xc3Q\xee.\u0649\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa0\b\x01\x98c\xc1\xa7|\x14\x99\xeb9\xbb\u05ff-\u05e3\x1c\xb9\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xa0\t\xbf\ao\x1b\xa3\xfaW\u04a7!r\x18\xbe\xd5VZzz\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa0\x1e\x94v\u07c4C\x18%\xc86\xe8\x80:\x97\xe2/\xa5\xa0\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\xa0\x1f\x12\xd7\x0fD\xaa{\x11;(\\\"\xdc\xdbE\x874T\xa7\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xa0\x1f\u0450j\x90\x85\x06\xde\xda\xe1\xe2\b\x12\x88r\xb5n\u7489\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa0\"\x82@\xf9\x9e\x1d\xe9\xcb2\xd8,\x0f/\xa9\xa3\xd4K\v\xf3\x89V\xbcu\xe2\xd61\x00\x00\x00\xe0\x94\xa0+\xdedahn\x19\xace\f\x97\r\x06r\xe7m\xcbO\u008a\x01\xe0\x92\x96\xc37\x8d\xe4\x00\x00\u07d4\xa0,\x1e4\x06O\x04u\xf7\xfa\x83\x1c\xcb%\x01L:\xa3\x1c\xa2\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xa0-\u01aa2\x8b\x88\r\u97acTh#\xfc\xcfw@G\xfb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa0.?\x8fYY\xa7\xaa\xb7A\x86\x12\x12\x9bp\x1c\xa1\xb8\x00\x10\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa04\u007f\n\x98wc\x90\x16\\\x16m2\x96;\xf7M\xcd\n/\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa05\xa3e$x\xf8-\xbdm\x11_\xaa\x8c\xa9F\xec\x9eh\x1d\x89\x05\xf4\xe4-\u052f\xec\x00\x00\u07d4\xa0:=\xc7\xc53\xd1tB\x95\xbe\x95]a\xaf?R\xb5\x1a\xf5\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa0E\x9e\xf3i:\xac\xd1d|\xd5\u0612\x989 L\xefS\xbe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa0O*\xe0*\xdd\x14\xc1/\xafe\xcb%\x90\"\u0403\n\x8e&\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xa0l\xd1\xf3\x969l\ndFFQ\xd7\xc2\x05\xef\xaf8|\xa3\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xa0ri\x1c\x8d\xd7\xcdB7\xffr\xa7\\\x1a\x95\x06\xd0\xce[\x9e\x89\x14\x0e\xc8\x0f\xa7\xee\x88\x00\x00\u07d4\xa0r\u03beb\xa9\xe9\xf6\x1c\xc3\xfb\xf8\x8a\x9e\xfb\xfe>\x9a\x8dp\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa0v\x82\x00\v\x1b\xcf0\x02\xf8\\\x80\xc0\xfa)I\xbd\x1e\x82\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa0z\xa1mt\xae\u8a63(\x8dR\xdb\x15Q\u0553\x882\x97\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa0\x8d![[j\xacHa\xa2\x81\xac~@\vx\xfe\xf0L\xbf\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa0\x95\x19p\xdf\u0403/\xb8;\xda\x12\xc25E\xe7\x90Aul\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa0\x9fM^\xaae\xa2\xf4\xcbu\nI\x924\x01\xda\u5410\xaf\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\xe0\x94\xa0\xa0\xe6R\x04T\x1f\u029b/\xb2\x82\u0355\x13\x8f\xae\x16\xf8\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa0\xaa_\x02\x01\xf0M;\xbe\xb8\x98\x13/|\x11g\x94f\xd9\x01\x89\x01\xfb\xedR\x15\xbbL\x00\x00\u07d4\xa0\xaa\xdb\xd9P\x97\"p_m#X\xa5\u01df7\x97\x0f\x00\xf6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa0\xb7q\x95\x1c\xe1\xde\xee6:\xe2\xb7q\xb7>\a\u0135\xe8\x00\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa0\xde\\`\x1eif5\u0198\xb7\xae\x9c\xa4S\x9f\u01f9A\xec\x89\x12\xc3\xcb\xd7\x04\xc9w\x00\x00\u07d4\xa0\xe8\xbaf\x1bH\x15L\xf8C\xd4\u00a5\xc0\xf7\x92\xd5(\xee)\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa0\xfc~S\xc5\xeb\xd2z*\xbd\xacE&\x1f\x84\xab;Q\xae\xfb\x89\xa3\x13\xda\xec\x9b\xc0\xd9\x00\x00\xe0\x94\xa0\xff[L\xf0\x16\x02~\x83#I}D(\xd3\xe5\xa8;\x87\x95\x8a\x01e\x98\xd3\xc8>\xc0B\x00\x00\u07d4\xa1\x06F[\xbd\x19\u1dbc\xe5\r\x1b\x11W\xdcY\tZ60\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa1\x06\xe6\x92>\xddS\u028e\xd6P\x96\x8a\x91\b\xd6\xcc\xfd\x96p\x8a\x02\x02\xfe\x15\x05\xaf\uc240\x00\u07d4\xa1\t\u12f0\xa3\x9c\x9e\xf8/\xa1\x95\x97\xfc^\xd8\xe9\xebmX\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\xa1\x1a\x03\u013b&\xd2\x1e\xffg}]U\\\x80\xb2TS\xeez\x89\x03\xcb'Y\xbcA\x0f\x80\x00\u07d4\xa1\x1e\xff\xabl\xf0\xf5\x97,\xff\xe4\xd5e\x96\xe9\x89h\x14J\x8f\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xa1 M\xad_V\a(\xa3\\\r\x8f\u01d4\x81\x05{\xf7s\x86\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa1&#\xe6)\u07d3\tg\x04\xb1`\x84\xbe,\u061dV-\xa4\x8a\x01\xcc\xc92E\x11\xe4P\x00\x00\xe0\x94\xa1*l-\x98]\xaf\x0eO_ z\xe8Q\xaa\xf7)\xb32\u034a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xa13m\xfb\x96\xb6\xbc\xbeK>\xdf2\x05\xbeW#\xc9\x0f\xadR\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa1;\x9d\x82\xa9\x9b<\x9b\xbaZ\xe7.\xf2\x19\x9e\xdc};\xb3l\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xa1<\xfe\x82mm\x18A\u072eD;\xe8\u00c7Q\x816\xb5\xe8\x8a\x1d\xa5jK\b5\xbf\x80\x00\x00\xe0\x94\xa1C.\xd2\u01b7wz\x88\xe8\xd4m8\x8epG\u007f \x8c\xa5\x8a\x01\xb1\xa7\xe4\x13\xa1\x96\xc5\x00\x00\u07d4\xa1D\xf6\xb6\x0fr\xd6J!\xe30\xda\xdbb\u0619\n\xde+\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa1P%\xf5\x95\xac\xdb\xf3\x11\x0fw\u017f$G~eH\xf9\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1X\x14\x8a.\x0f>\x92\xdc,\xe3\x8f\xeb\xc2\x01\a\xe3%<\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1a`\x85\x1d+\x9c4\x9b\x92\xe4o\x82\x9a\xbf\xb2\x10\x945\x95\x89a\t=|,m8\x00\x00\u07d4\xa1f\xf9\x11\xc6D\xac2\x13\u049e\x0e\x1a\xe0\x10\xf7\x94\u056d&\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1m\x9e=c\x98aY\xa8\x00\xb4h7\xf4^\x8b\xb9\x80\xee\v\x89n\x11u\xdaz\xd1 \x00\x00\u07d4\xa1pp\xc2\xe9\u0169@\xa4\xec\x0eIT\xc4\xd7\xd6C\xbe\x8fI\x89lk\x17\x03;6\x1c\x80\x00\u07d4\xa1|\x9eC#\x06\x95\x18\x18\x9dR\a\xa0r\x8d\u02d20j?\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa1\x83`\xe9\x85\xf2\x06.\x8f\x8e\xfe\x02\xad,\xbc\x91\xad\x9aZ\xad\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa1\x91\x14\x05\xcfn\x99\x9e\xd0\x11\xf0\xdd\xcd*O\xf7\u008f%&\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa1\x92i\x80\a\xcc\x11\xaa`=\"\x1d_\xee\xa0v\xbc\xf7\xc3\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\x92\xf0j\xb0R\xd5\xfd\u007f\x94\xee\xa81\x8e\x82x\x15\xfegz\x89\a\x1f\x8a\x93\xd0\x1eT\x00\x00\u07d4\xa1\x99\x81D\x96\x8a\\p\xa6AUT\xce\xfe\u0082F\x90\u0125\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa1\xa1\xf0\xfam \xb5\nyO\x02\xefR\b\\\x9d\x03j\xa6\u028965\u026d\xc5\u07a0\x00\x00\u07d4\xa1\xae\x8dE@\xd4\xdbo\xdd\xe7\x14oA[C\x1e\xb5\\y\x83\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xa1\xb4|M\x0e\xd6\x01\x88B\xe6\xcf\xc8c\n\u00e3\x14.^k\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xa1\xc4\xf4Z\x82\xe1\xc4x\xd8E\b.\xb1\x88u\xc4\xeae9\xab\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xa1\xdc\xd0\xe5\xb0Z\x97|\x96#\xe5\xae/Y\xb9\xad\xa2\xf3>1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa1\xe48\n;\x1ft\x96s\xe2p\"\x99\x93\xeeU\xf3Vc\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\xf1\x93\xa0Y/\x1f\xeb\x9f\xdf\xc9\n\xa8\x13xN\xb8\x04q\u0249K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa1\xf2\x85@P\xf8re\x8e\xd8.R\xb0\xad{\xbc\x1c\xb9!\xf6\x89m\x03\x17\xe2\xb3&\xf7\x00\x00\u07d4\xa1\xf5\xb8@\x14\rZ\x9a\xce\xf4\x02\xac<\u00c8jh\xca\xd2H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\xf7e\xc4O\xe4_y\x06w\x94HD\xbeO-B\x16_\xbd\x89\xc7\xe9\xcf\xdev\x8e\xc7\x00\x00\u07d4\xa1\xf7\xdd\xe1\xd78\xd8\xcdg\x9e\xa1\xee\x96[\xee\"K\xe7\xd0M\x89=\x18DP\xe5\xe9<\x00\x00\u07d4\xa1\xf8\u063c\xf9\x0ew\u007f\x19\xb3\xa6Iu\x9a\xd9P'\xab\xdf\u00c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\x02TrB\x80onp\xe7@X\xd6\xe5)-\xef\xc8\xc8\u0509l\x87T\xc8\xf3\f\b\x00\x00\u07d4\xa2\r\a\x1b\x1b\x000cI}y\x90\xe1$\x9d\xab\xf3l5\xf7\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa2\r\x8f\xf6\f\xaa\xe3\x1d\x02\xe0\xb6e\xfaC]v\xf7|\x94B\x89\x1a\x8a\x90\x9d\xfc\xef@\x00\x00\u07d4\xa2\x11\xda\x03\xcc\x0e1\xec\xceS\t\x99\x87\x18QU(\xa0\x90\u07c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\x14B\xab\x054\n\xdeh\xc9\x15\xf3\xc39\x9b\x99U\xf3\xf7\xeb\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xa2\"\"Y\u075c>=\xed\x12p\x84\xf8\b\xe9*\x18\x870,\x89\b\xc83\x9d\xaf\xedH\x00\x00\u07d4\xa2*\xde\r\xdb\\n\xf8\xd0\u034d\xe9M\x82\xb1\x10\x82\xcb.\x91\x897KW\xf3\xce\xf2p\x00\x00\u07d4\xa2L:\xb6!\x81\xe9\xa1[x\xc4b\x1eL|X\x81'\xbe&\x89\b\xcd\xe4:\x83\xd31\x00\x00\u07d4\xa2W\xadYK\u0603(\xa7\xd9\x0f\xc0\xa9\a\u07d5\xee\xca\xe3\x16\x89\x1c7\x86\xff8F\x93\x00\x00\u07d4\xa2[\bd7\xfd!\x92\u0420\xf6On\xd0D\xf3\x8e\xf3\xda2\x89\x12)\x0f\x15\x18\v\xdc\x00\x00\u07d4\xa2v\xb0X\u02d8\u060b\xee\xdbg\xe5CPl\x9a\r\x94p\u0609\x90\xaa\xfcv\xe0/\xbe\x00\x00\u07d4\xa2\x82\xe9i\xca\xc9\xf7\xa0\xe1\xc0\u0350\xf5\xd0\xc48\xacW\r\xa3\x89\"\a\xeb\x89\xfc'8\x00\x00\xe0\x94\xa2\x91\xe9\u01d9\rU-\u046e\x16\u03bc?\xca4,\xba\xf1\u044a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa2\x93\x19\xe8\x10i\xe5\xd6\r\xf0\x0f=\xe5\xad\xee5\x05\xec\xd5\xfb\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa2\x96\x8f\xc1\xc6K\xac\vz\xe0\u058b\xa9I\x87Mm\xb2S\xf4\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xa2\x9d[\xdat\xe0\x03GHr\xbdX\x94\xb8\x853\xffd\u00b5\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa2\x9df\x1acv\xf6m\vt\xe2\xfe\x9d\x8f&\xc0$~\xc8L\x89\xdf3\x04\a\x9c\x13\xd2\x00\x00\u07d4\xa2\xa45\xdeD\xa0\x1b\xd0\ucc9eD\xe4vD\xe4j\f\xdf\xfb\x89\x1b\x1dDZz\xff\xe7\x80\x00\u07d4\xa2\xac\xe4\u0253\xbb\x1eS\x83\xf8\xact\xe1y\x06n\x81O\x05\x91\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa2\xb7\x01\xf9\xf5\xcd\u041eK\xa6+\xae\xba\u3a02W\x10X\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa2\u0145O\xf1Y\x9f\x98\x89,W%\xd2b\xbe\x1d\xa9\x8a\xad\xac\x89\x11\t\xff30\x10\xe7\x80\x00\u07d4\xa2\xc7\xea\xff\xdc,\x9d\x93sE l\x90\x9aR\u07f1LG\x8f\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\xa2\u04aabk\t\xd6\xd4\xe4\xb1?\u007f\xfcZ\x88\xbdz\xd3gB\x89\xfb\x80xPuS\x83\x00\x00\u07d4\xa2\u04cd\xe1\xc79\x06\xf6\xa7\xcan\xfe\xb9|\xf6\xf6\x9c\xc4!\xbe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa2\xdce\xee%kY\xa5\xbdy)wO\x90K5\x8d\U000ed84a\x04\x83\xbc\xe2\x8b\xeb\t\xf8\x00\x00\u07d4\xa2\xe0h:\x80]\xe6\xa0^\xdb/\xfb\xb5\xe9o\x05p\xb67\u00c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa2\u1e2a\x90\x0e\x9c\x13\x9b?\xa1\"5OaV\xd9*\x18\xb1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa2\u2d54\x1e\f\x01\x94K\xfe\x1d_\xb4\xe8\xa3K\x92,\u03f1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\xe4`\xa9\x89\xcb\x15V_\x9e\u0327\xd1!\xa1\x8eN\xb4\x05\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa2\xec\xce,I\xf7*\t\x95\xa0\xbd\xa5z\xac\xf1\xe9\xf0\x01\xe2*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xa2\xf4r\xfeO\"\xb7}\xb4\x89!\x9e\xa4\x02=\x11X*\x93)\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xa2\xf7\x98\xe0w\xb0}\x86\x12N\x14\a\xdf2\x89\r\xbbKcy\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\xf8k\xc0a\x88N\x9e\xef\x05d\x0e\xddQ\xa2\xf7\xc0Yli\x89llD\xfeG\xec\x05\x00\x00\u07d4\xa2\xfa\x17\xc0\xfbPl\xe4\x94\x00\x8b\x95W\x84\x1c?d\x1b\x8c\xae\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa3\x04X\x8f\r\x85\f\xd8\u04cfv\xe9\xe8<\x1b\xf6>3>\u0789\x02(V\x01!l\x8c\x00\x00\u07d4\xa3\x05\x8cQszN\x96\xc5_.\xf6\xbd{\xb3X\x16~\u00a7\x89 \xdb:\xe4H\x1a\u0500\x00\u07d4\xa3\t\xdfT\u02bc\xe7\f\x95\xec03\x14\x9c\xd6g\x8ao\xd4\u03c9\f\x1f\x12\xc7Q\x01X\x00\x00\u07d4\xa3\nER\x0eR\x06\xd9\x00@p\xe6\xaf>{\xb2\xe8\xddS\x13\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa3\x0e\n\xcbSL\x9b0\x84\xe8P\x1d\xa0\x90\xb4\xeb\x16\xa2\xc0\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3 0\x95\xed\xb7\x02\x8ehq\xce\n\x84\xf5HE\x9f\x830\n\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa3!\t\x1d0\x18\x06By\xdb9\x9d+*\x88\xa6\xf4@\xae$\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4\xa3#-\x06\x8dP\x06I\x03\xc9\xeb\xc5c\xb5\x15\xac\u0237\xb0\x97\x89l\x87T\xc8\xf3\f\b\x00\x00\xe0\x94\xa3$\x1d\x89\n\x92\xba\xf5)\b\xdcJ\xa0Irk\xe4&\xeb\u04ca\x04<-\xa6a\xca/T\x00\x00\u07d4\xa3)F&\xec)\x84\xc4;C\xdaM]\x8eFi\xb1\x1dKY\x896\xa4\xcfcc\x19\xc0\x00\x00\u07d4\xa3,\xf7\xdd\xe2\f=\xd5g\x9f\xf5\xe3%\x84\\p\u0156&b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa39\xa3\xd8\xca(\x0e'\xd2A[&\xd1\xfcy2(\xb6`C\x896\U00086577\x8f\xf0\x00\x00\u07d4\xa3<\xb4P\xf9[\xb4n%\xaf\xb5\x0f\xe0_\xee\xe6\xfb\x8c\xc8\xea\x89*\x11)\u0413g \x00\x00\u07d4\xa3?p\xdaru\xef\x05q\x04\u07e7\xdbd\xf4r\xe9\xf5\xd5S\x89\x04YF\xb0\xf9\xe9\xd6\x00\x00\u07d4\xa3@v\xf8K\xd9\x17\xf2\x0f\x83B\u024b\xa7\x9eo\xb0\x8e\xcd1\x89\u3bb5sr@\xa0\x00\x00\u07d4\xa3C\x0e\x1fd\u007f2\x1e\xd3G9V##\xc7\xd6#A\vV\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xa3O\x9dV\x8b\xf7\xaf\xd9L*[\x8a_\xf5\\f\xc4\by\x99\x89\x84}P;\"\x0e\xb0\x00\x00\u07d4\xa3V\x06\xd5\x12 \xee\u007f!F\xd4\x11X.\xe4\xeeJEYn\x89\u062a\xbe\b\v\xc9@\x00\x00\u07d4\xa3VU\x1b\xb7}OE\xa6\xd7\xe0\x9f\n\b\x9ey\u0322I\u02c9\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xa3\\\x19\x13,\xac\x195Wj\xbf\xedl\x04\x95\xfb\a\x88\x1b\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3e\x91\x8b\xfe?&'\xb9\xf3\xa8gu\xd8un\x0f\u0629K\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa3n\r\x94\xb9Sd\xa8&q\xb6\b\xcb-72Ea)\t\x89\b!\xd2!\xb5)\x1f\x80\x00\u07d4\xa3u\xb4\xbc$\xa2N\x1fyu\x93\xcc0+/3\x10c\xfa\\\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa3v\"\xac\x9b\xbd\xc4\xd8+u\x01]t[\x9f\x8d\xe6Z(\uc25d\xc0\\\xce(\u00b8\x00\x00\xe0\x94\xa3y\xa5\a\fP=/\xac\x89\xb8\xb3\xaf\xa0\x80\xfdE\xedK\xec\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xa3\x80-\x8ae\x9e\x89\xa2\xc4~\x90T0\xb2\xa8'\x97\x89P\xa7\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa3\x83\x06\xcbp\xba\xa8\u4446\xbdh\xaap\xa8=$/)\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\x84vi\x1d4\x94.\xeak/v\x88\x92#\x04}\xb4az\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\x87\xceN\x96\x1axG\xf5`\a\\d\xe1YkVA\xd2\x1c\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xa3\x87\xec\xde\x0e\xe4\xc8\a\x94\x99\xfd\x8e\x03G;\u060a\xd7R*\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa3\x88:$\xf7\xf1f _\x1aj\x99I\al&\xa7nqx\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xa3\x8b[\xd8\x1a\x9d\xb9\u04b2\x1d^\xc7\xc6\x05R\xcd\x02\xedV\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa3\x90\xca\x12+\x85\x01\xee>^\a\xa8\xcaKA\x9f~M\xae\x15\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xa3\x93*1\xd6\xffu\xfb;\x12q\xac\xe7\u02a7\xd5\xe1\xff\x10Q\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xa3\x94\xadO\xd9\xe6S\x0eo\\S\xfa\xec\xbe\u0781\xcb\x17-\xa1\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xa3\x97\x9a\x92v\n\x13Z\xdfi\xd7/u\xe1gu_\x1c\xb8\u00c9\x05k\xc7^-c\x10\x00\x00\xe0\x94\xa3\x9b\xfe\xe4\xae\u027du\xbd\"\u01b6r\x89\x8c\xa9\xa1\xe9]2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa3\xa2b\xaf\u0493h\x19#\b\x92\xfd\xe8O-ZYJ\xb2\x83\x89e\xea=\xb7UF`\x00\x00\u07d4\xa3\xa2\xe3\x19\xe7\u04e1D\x8bZ\xa2F\x89S\x16\f-\xbc\xbaq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\xa5{\a\x16\x13(\x04\xd6\n\xac(\x11\x97\xff+=#{\x01\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa3\xa9>\xf9\xdb\xea&6&=\x06\xd8I/jA\u0790|\"\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\xa3\xae\x18y\x00}\x80\x1c\xb5\xf3RqjM\u063a'!\xde=\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xa3\xba\r:6\x17\xb1\xe3\x1bNB,\xe2i\xe8s\x82\x8d]i\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\xa3\xbc\x97\x9bp\x80\t/\xa1\xf9/n\x0f\xb3G\u2359PE\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xa3\xbf\xf1\u07e9\x97\x16h6\f\r\x82\x82\x842\xe2{\xf5Ng\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa3\xc1J\xce(\xb1\x92\u02f0b\x14_\u02fdXi\xc6rq\xf6\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa3\xc3:\xfc\x8c\xb4pN#\x15=\xe2\x04\x9d5\xaeq3$r\x89+X\xad\u06c9\xa2X\x00\x00\u07d4\xa3\u0430<\xff\xbb&\x9fyj\u009d\x80\xbf\xb0}\xc7\u01ad\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\u0543\xa7\xb6[#\xf6\vy\x05\xf3\xe4\xaab\xaa\xc8\u007fB'\x898\xbe\xfa\x12mZ\x9f\x80\x00\u07d4\xa3\xdb6J3-\x88K\xa9;&\x17\xaeM\x85\xa1H\x9b\xeaG\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xa3\xe0Q\xfbtJ\xa3A\f;\x88\xf8\x99\xf5\xd5\u007f\x16\x8d\xf1-\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\xa3\xe3\xa6\xeaP\x95s\xe2\x1b\xd0#\x9e\xce\x05#\xa7\xb7\u061b/\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa3\xf4\xad\x14\xe0\xbbD\xe2\xce,\x145\x9cu\xb8\xe72\xd3pT\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa3\xfa\xccP\x19\\\vI3\xc8X\x97\xfe\xcc[\xbd\x99\\4\xb8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\x03Z\xb1\xe5\x18\b!\xf0\xf3\x80\xf1\x13\x1bs\x87\xc8\u0641\u0349\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\n\xa2\xbb\xce\fr\xb4\xd0\xdf\xff\xccBq[+T\xb0\x1b\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4\x19\xa9\x84\x14#c&uuV`\x894\x0e\xea\x0e\xa2\b\x19\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xa4!\u06f8\x9b:\aA\x90\x84\xad\x10\xc3\xc1]\xfe\x9b2\xd0\u008a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa4\"\xe4\xbf\v\xf7AG\u0309[\xed\x8f\x16\xd3\xce\xf3BaT\x89\x12\xef?b\xee\x116\x80\x00\u07d4\xa4%\x9f\x83E\xf7\u3a37+\x0f\xec,\xf7^2\x1f\xdaM\u0089g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xa4)\b\xe7\xfeS\x98\n\x9a\xbf@D\xe9W\xa5Kp\u973e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4)\xfa\x88s\x1f\xdd5\x0e\x8e\xcdn\xa5B\x96\xb6HO\u6549j\xc5\xc6-\x94\x86\a\x00\x00\xe0\x94\xa40\x99]\xdb\x18[\x98e\xdb\xe6%9\xad\x90\xd2.Ks\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa46\xc7TS\xcc\xcaJ\x1f\x1bb\xe5\u0123\r\x86\xdd\xe4\xbeh\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa47\xfen\xc1\x03\u028d\x15\x8fc\xb34\"N\u032c[>\xa3\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4;m\xa6\xcbz\xacW\x1d\xff'\xf0\x9d9\xf8F\xf57i\xb1\x89\x14\x99\x8f2\xacxp\x00\x00\u07d4\xa4;\x81\xf9\x93V\xc0\xaf\x14\x1a\x03\x01\rw\xbd\x04,q\xc1\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4>\x19G\xa9$+5Ua\xc3\n\x82\x9d\xfe\uc881Z\xf8\x89\xd2=\x99\x96\x9f\u0591\x80\x00\u07d4\xa4H\x9aP\xea\xd5\xd5DZ{\xeeM-U6\u00a7lA\xf8\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa4O\xe8\x00\xd9o\xca\xd7;qp\xd0\xf6\x10\u02cc\x06\x82\xd6\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa4T2\xa6\xf2\xac\x9dVW{\x93\x8a7\xfa\xba\xc8\xcc|F\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4f\xd7p\u0618\xd8\xc9\xd4\x05\xe4\xa0\xe5Q\xef\xaf\xcd\xe5<\xf9\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\xe0\x94\xa4g\a1\x17X\x93\xbb\xcf\xf4\xfa\x85\u0397\xd9O\xc5\x1cK\xa8\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4kC\x87\xfbM\xcc\xe0\x11\xe7nMsT}D\x81\xe0\x9b\xe5\x89Hz\x9a0E9D\x00\x00\u07d4\xa4l\xd27\xb6>\xeaC\x8c\x8e;e\x85\xf6y\xe4\x86\b2\xac\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4wy\u063c\x1c{\xce\x0f\x01\x1c\xcb9\xefh\xb8T\xf8\u078f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4\x82kl8\x82\xfa\xd0\xed\\\x8f\xbb%\xcc@\xccO3u\x9f\x89p\x1bC\xe3D3\xd0\x00\x00\u07d4\xa4\x87Y(E\x8e\xc2\x00]\xbbW\x8c\\\xd35\x80\xf0\xcf\x14R\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4\x9fR:\xa5\x13d\xcb\xc7\u0655\x16=4\xebY\r\xed/\b\x89\x90'B\x1b*\x9f\xbc\x00\x00\u07d4\xa4\xa4\x9f\v\xc8h\x8c\xc9\xe6\xdc\x04\xe1\xe0\x8dR\x10&\xe6Ut\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa4\xa7\xd3\x06\xf5\x10\xcdX5\x94(\xc0\xd2\xf7\xc3`\x9dVt\u05c9\xb5\x8c\xb6\x1c<\xcf4\x00\x00\u07d4\xa4\xa8:\a8y\x9b\x97\x1b\xf2\xdep\x8c.\xbf\x91\x1c\xa7\x9e\xb2\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa4\xb0\x9d\xe6\xe7\x13\xdciTnv\xef\n\xcf@\xb9O\x02A\xe6\x89\x11}\xc0b~\xc8p\x00\x00\xe0\x94\xa4\u04b4)\xf1\xadSI\xe3\x17\x04\x96\x9e\xdc_%\xee\x8a\xca\x10\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xa4\xd6\xc8.\u076eYG\xfb\xe9\xcd\xfb\xd5H\xae3\xd9\x1aq\x91\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4\xda4E\r\"\xec\x0f\xfc\xed\xe0\x00K\x02\xf7\x87.\xe0\xb7:\x89\x05\x0fafs\xf0\x83\x00\x00\xe0\x94\xa4\xddY\xab^Q}9\x8eI\xfaS\u007f\x89\x9f\xedL\x15\xe9]\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa4\xe6#E\x1e~\x94\xe7\u86e5\xed\x95\u0228:b\xff\xc4\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\xed\x11\xb0r\u061f\xb16u\x9f\u019bB\x8cH\xaa]L\xed\x89\x0e?\x15'\xa0<\xa8\x00\x00\u07d4\xa4\xfb\x14@\x9ag\xb4V\x88\xa8Y>\\\xc2\xcfYl\xedo\x11\x89a\t=|,m8\x00\x00\xe0\x94\xa5\x14\xd0\x0e\xddq\b\xa6\xbe\x83\x9ac\x8d\xb2AT\x18\x17A\x96\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x94\xa5\"\xde~\xb6\xae\x12PR*Q13\xa9;\xd4(IG\\\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa5$\xa8\xcc\xccIQ\x8d\x17\n2\x82p\xa2\xf8\x813\xfb\xaf]\x89\x0f\xf7\x02-\xac\x10\x8a\x00\x00\u07d4\xa59\xb4\xa4\x01\xb5\x84\xdf\xe0\xf3D\xb1\xb4\"\xc6UC\x16~.\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa5>\xadT\xf7\x85\n\xf2\x148\xcb\xe0z\xf6\x86'\x9a1[\x86\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa5C\xa0f\xfb2\xa8f\x8a\xa0sj\f\x9c\xd4\rx\t\x87'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa5gw\vj\xe3 \xbd\xdeP\xf9\x04\xd6c\xe7F\xa6\x1d\xac\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5h\xdbMW\xe4\xd6tb\xd73\u019a\x9e\x0f\xe2n!\x83'\x89;k\xff\x92f\xc0\xae\x00\x00\u07d4\xa5i\x8059\x1eg\xa4\x90\x13\xc0\x00 yY1\x14\xfe\xb3S\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4\xa5p\":\xe3\u02a8QA\x8a\x98C\xa1\xacU\xdbH$\xf4\xfd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa5s`\xf0\x02\xe0\xd6M-tE}\x8c\xa4\x85~\xe0\v\xcd\u07c9\x123\xe22\xf6\x18\xaa\x00\x00\u07d4\xa5u\xf2\x89\x1d\xcf\u0368<\\\xf0\x14t\xaf\x11\xee\x01\xb7-\u0089\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\xa5x;\xf342\xff\x82\xacI\x89\x85\xd7\xd4`\xaeg\xec6s\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xa5\x87MuF5\xa7b\xb3\x81\xa5\xc4\u01d2H:\xf8\xf2=\x1d\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\xa5\xa4\"\u007fl\xf9\x88%\xc0\u057a\xffS\x15u,\xcc\x1a\x13\x91\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa5\xabK\xd3X\x8fF\xcb'.V\xe9=\xee\u04c6\xba\x8bu=\x89HB\xf0A\x05\x87,\x80\x00\xe0\x94\xa5\xba\xd8e\t\xfb\xe0\xe0\xe3\xc0\xe9?m8\x1f\x1a\xf6\xe9\u0501\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa5\xc36\b;\x04\xf9G\x1b\x8cn\xd76y\xb7Mf\xc3c\uc263e\nL\x9d \xe2\x00\x00\u07d4\xa5\xcd\x129\x92\x19K4\xc4x\x13\x140;\x03\xc5IH\xf4\xb9\x89l\xfc\xc3\xd9\x1d\xa5c\x00\x00\u07d4\xa5\u0578\xb6-\x00-\xef\x92A7\x10\xd1;o\xf8\xd4\xfc}\u04c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xa5\xd9ni}F5\x8d\x11\x9a\xf7\x81\x9d\xc7\b\u007fj\xe4\u007f\xef\x8a\x03\x17\xbe\xe8\xaf3\x15\xa7\x80\x00\u07d4\xa5\xde^CO\xdc\xddh\x8f\x1c1\xb6\xfbQ,\xb1\x96rG\x01\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xa5\xe0\xfc<:\xff\xed=\xb6q\tG\xd1\xd6\xfb\x01\u007f>'m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5\xe9;I\xea|P\x9d\xe7\xc4Ml\xfe\xdd\xefY\x10\u07aa\xf2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5\xe9\xcdKt%]\"\xb7\u0672z\xe8\xddC\xedn\xd0%+\x89)\x8d\xb2\xf5D\x11\u0640\x00\xe0\x94\xa5\xf0\a{5\x1flP\\\xd5\x15\u07e6\xd2\xfa\u007f\\L\u0487\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xa5\xf0u\xfd@\x135W{f\x83\u0081\xe6\xd1\x01C-\xc6\xe0\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xa5\xfe,\xe9\u007f\x0e\x8c8V\xbe\r\xe5\xf4\u0732\xce]8\x9a\x16\x89\x01=\xb0\xb8\xb6\x86>\x00\x00\u07d4\xa5\xffb\"-\x80\xc0\x13\xce\xc1\xa0\xe8\x85\x0e\xd4\xd3T\xda\xc1m\x89\vA\a\\\x16\x8b\x18\x00\x00\u07d4\xa6\t\xc2m\xd3P\xc25\xe4K+\x9c\x1d\xdd\xcc\u0429\xd9\xf87\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa6\f\x12\tuO]\x87\xb1\x81\xdaO\b\x17\xa8\x18Y\xef\x9f\u0609\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\xa6\x10\x1c\x96\x1e\x8e\x1c\x15y\x8f\xfc\xd0\xe3 \x1dw\x86\xec7:\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xa6\x13Ei\x96@\x8a\xf1\xc2\xe9>\x17w\x88\xabU\x89^+2\x8a\x01Y\x19\xffG|\x88\xb8\x00\x00\u07d4\xa6\x18\x87\x81\x8f\x91J \xe3\x10w)\v\x83qZk-n\xf9\x89e\xea=\xb7UF`\x00\x00\u07d4\xa6\x1aT\xdfxJD\xd7\x1bw\x1b\x871u\t!\x13\x81\xf2\x00\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa6\x1c\u06ed\xf0K\x1eT\u0203\xde`\x05\xfc\xdf\x16\xbe\xb8\xeb/\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa69\xac\xd9k1\xbaS\xb0\u0407c\"\x9e\x1f\x06\xfd\x10^\x9d\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa6BP\x10\x04\xc9\x0e\xa9\xc9\xed\x19\x98\xba\x14\nL\xd6,o_\x89\r\x94\xfb\x8b\x10\xf8\xb1\x80\x00\u07d4\xa6D\xed\x92,\xc27\xa3\xe5\u0117\x9a\x99Tw\xf3nP\xbcb\x89\x1f\xa7=\x84]~\x96\x00\x00\u07d4\xa6F\xa9\\moY\xf1\x04\xc6T\x1dw`uz\xb3\x92\xb0\x8c\x89\u3bb5sr@\xa0\x00\x00\xe0\x94\xa6HL\u0184\xc4\xc9\x1d\xb5>\xb6\x8aM\xa4Zjk\xda0g\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa6N_\xfbpL,\x919\xd7~\xf6\x1d\x8c\u07e3\x1dz\x88\xe9\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\xe0\x94\xa6T&\xcf\xf3x\xed#%5\x13\xb1\x9fIm\xe4_\xa7\u13ca\x01\x86P\x12|\xc3\u0700\x00\x00\u07d4\xa6jIc\xb2\u007f\x1e\xe1\x93+\x17+\xe5\x96N\r:\xe5KQ\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4\xa6\u007f8\x81\x95eB:\xa8_>:\xb6\x1b\xc7c\u02eb\x89\u0749sw\xb0\"\u01be\b\x00\x00\u07d4\xa6\x8c14E\xc2-\x91\x9e\xe4l\xc2\xd0\xcd\xff\x04:uX%\x89\x04\x13t\xfd!\xb0\u0600\x00\u07d4\xa6\x8e\f0\u02e3\xbcZ\x88>T\x03 \xf9\x99\xc7\xcdU\x8e\\\x89a\x9237b\xa5\x8c\x80\x00\u07d4\xa6\x90\xf1\xa4\xb2\n\xb7\xba4b\x86 \u079c\xa0@\xc4<\x19c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa6\x9d|\xd1}HB\xfe\x03\xf6*\x90\xb2\xfb\xf8\xf6\xaf{\xb3\x80\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa6\xa0\x82R\xc8YQw\xcc.`\xfc'Y>#y\xc8\x1f\xb1\x89\x01\x16Q\xac>zu\x80\x00\u07d4\xa6\xa0\xdeB\x1a\xe5Om\x17(\x13\b\xf5dm/9\xf7w]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa6\xb2\xd5s)s`\x10,\a\xa1\x8f\xc2\x1d\xf2\xe7I\x9f\xf4\xeb\x89\xd9o\u0390\u03eb\xcc\x00\x00\xe0\x94\xa6\xc9\x10\xceMIJ\x91\x9c\u036a\xa1\xfc;\x82\xaat\xba\x06\u03ca\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa6\u3ea3\x8e\x10J\x1e'\xa4\xd8(i\xaf\xb1\xc0\xaen\xff\x8d\x89\x01\x11@\ueb4bq\x00\x00\u07d4\xa6\xee\xbb\xe4d\u04d1\x87\xbf\x80\u029c\x13\xd7 '\xec[\xa8\xbe\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa6\xf6+\x8a=\u007f\x11\"\a\x01\xab\x9f\xff\xfc\xb3'\x95\x9a'\x85\x89\x1bn)\x1f\x18\u06e8\x00\x00\u07d4\xa6\xf93\a\xf8\xbc\xe01\x95\xfe\u0387 C\xe8\xa0?{\xd1\x1a\x89\x9csK\xadQ\x11X\x00\x00\u07d4\xa7\x01\xdfy\xf5\x94\x90\x1a\xfe\x14DH^k \u00fd\xa2\xb9\xb3\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa7\x02L\xfdt,\x1e\xc1<\x01\xfe\xa1\x8d0B\xe6_\x1d]\xee\x8a\x02c\x11\x9a(\xab\u0430\x80\x00\u07d4\xa7\x18\xaa\xadY\xbf9\\\xba+#\xe0\x9b\x02\xfe\f\x89\x81bG\x8960<\x97\xe4hx\x00\x00\u07d4\xa7$|S\xd0Y\xeb|\x93\x10\xf6(\xd7\xfclj\nw?\b\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xa7%7c\xcfJu\u07d2\xca\x1evm\xc4\xee\x8a'E\x14{\x8a\x02F7p\xe9\n\x8fP\x00\x00\u07d4\xa7.\xe6f\u0133^\x82\xa5\x06\x80\x8bD<\xeb\xd5\xc62\xc7\u0749+^:\xf1k\x18\x80\x00\x00\u07d4\xa7DD\xf9\x0f\xbbT\xe5o:\u0276\xcf\u032aH\x19\xe4aJ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa7GC\x9a\xd0\u04d3\xb5\xa08a\xd7r\x962m\u8edd\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa7`{BW;\xb6\xf6\xb4\xd4\xf2<~*&\xb3\xa0\xf6\xb6\xf0\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\xa7i)\x89\n{G\xfb\x85\x91\x96\x01lo\u0742\x89\u03b7U\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u0794\xa7kt?\x98\x1bi0r\xa11\xb2+\xa5\x10\x96\\/\xef\u05c8\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xa7m?\x15bQ\xb7,\f\xcfKG\xa39<\xbdoI\xa9\u0149Hz\x9a0E9D\x00\x00\u07d4\xa7t(\xbc\xb2\xa0\xdbv\xfc\x8e\xf1\xe2\x0eF\x1a\n2\u016c\x15\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xa7u\x8c\xec\xb6\x0e\x8faL\u0396\x13~\xf7+O\xbd\awJ\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa7w^J\xf6\xa2:\xfa \x1f\xb7\x8b\x91^Q\xa5\x15\xb7\xa7(\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\xa7\u007f>\u1793\x88\xbb\xbb\"\x15\xc6#\x97\xb9e`\x13#`\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa7\x85\x9f\xc0\u007fun\xa7\xdc\xeb\xbc\xcdB\xf0X\x17X-\x97?\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa7\x96lH\x9fLt\x8az\u902a'\xa5t%\x17g\xca\xf9\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa7\xa3\xbba9\xb0\xad\xa0\f\x1f\u007f\x1f\x9fV\u0654\xbaM\x1f\xa8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa7\xa3\xf1S\xcd\u00c8!\xc2\f]\x8c\x82A\xb2\x94\xa3\xf8+$\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa7\xa5\x17\u05ed5\x82\v\t\u0517\xfa~U@\xcd\xe9IXS\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa7\xc9\u04c8\xeb\xd8s\xe6k\x17\x13D\x83\x97\xd0\xf3\u007f\x8b\u04e8\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa7\u073b\xa9\xb9\xbfgb\xc1EAlPjq\u3d17 \x9c\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xa7\xe7O\v\xdb'\x8f\xf0\xa8\x05\xa6Ha\x8e\xc5+\x16o\xf1\xbe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa7\xe87r\xbc \x0f\x90\x06\xaa*&\r\xba\xa8H=\xc5+0\x89\vB\xd56f7\xe5\x00\x00\xe0\x94\xa7\xef5\u0387\xed\xa6\u008d\xf2HxX\x15\x05>\xc9zPE\x8a\x01\x0f\f\xe9I\xe0\x0f\x93\x00\x00\u07d4\xa7\xf9\"\f\x80G\x82k\xd5\xd5\x18?Ngjmw\xbf\xed6\x89\bPh\x97k\xe8\x1c\x00\x00\u07d4\xa8\a\x10O'\x03\xd6y\xf8\u07af\xc4B\xbe\xfe\x84\x9eB\x95\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\f\xb1s\x8b\xac\b\xd4\xf9\xc0\x8bM\xef\xf5\x15T_\xa8XO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa8\x19\xd2\xec\xe1\"\xe0(\xc8\xe8\xa0J\x06M\x02\xb9\x02\x9b\b\xb9\x8965\u026d\xc5\u07a0\x00\x00\u0794\xa8%\xfdZ\xbby&\xa6|\xf3k\xa2F\xa2K\xd2{\xe6\xf6\xed\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4\xa8(U9\x86\x9d\x88\xf8\xa9aS7Uq}~\xb6Uv\xae\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa83\x82\xb6\xe1Rg\x97J\x85P\xb9\x8fqv\xc1\xa3S\xf9\xbe\x89\xbf\xfd\xaf/\xc1\xb1\xa4\x00\x00\xe0\x94\xa8DlG\x81\xa77\xacC(\xb1\xe1[\x8a\v?\xbb\x0f\xd6h\x8a\x04\x87\x94\xd1\xf2F\x19*\x00\x00\u07d4\xa8E[A\x17e\u0590\x1e1\x1erd\x03\t\x1eB\xc5f\x83\x89\xb7:\xec;\xfe\x14P\x00\x00\xe0\x94\xa8f\x13\xe6\u0124\xc9\xc5_\\\x10\xbc\xda2\x17]\u02f4\xaf`\x8a\x02C\xd6\xc2\xe3k\xe6\xae\x00\x00\u07d4\xa8m\xb0}\x9f\x81/G\x96b-@\xe0=\x13Xt\xa8\x8at\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa8\u007fz\xbdo\xa3\x11\x94(\x96x\xef\xb6<\xf5\x84\xee^*a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xa8\x80\u2a3f\x88\xa1\xa8&H\xb4\x01W\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa8\x9d\xf3HY\xed\xd7\xc8 \u06c8w@\xd8\xff\x9e\x15\x15|{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\xa4<\x00\x91\x00al\xb4\xaeN\x03?\x1f\xc5\xd7\xe0\xb6\xf1R\x89\u0548\xd0x\xb4?M\x80\x00\u07d4\xa8\xa7\b\xe8O\x82\u06c6\xa3U\x02\x19;Ln\xe9\xa7n\xbe\x8f\x897\b\xba\xed=h\x90\x00\x00\xe0\x94\xa8\xa7\xb6\x8a\u06b4\xe3\xea\xdf\xf1\x9f\xfaX\xe3J?\xce\xc0\xd9j\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xa8\xa8\xdb\xdd\x1a\x85\u047e\xee%i\xe9\x1c\xccM\t\xae\u007fn\xa1\x8a\x01:k+VHq\xa0\x00\x00\u07d4\xa8\xac\xa7H\xf9\xd3\x12\xect\u007f\x8bex\x14&\x94\xc7\xe9\xf3\x99\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\xb6[\xa3\x17\x1a?w\xa65\v\x9d\xaf\x1f\x8dU\xb4\xd2\x01\xeb\x89(b\xf3\xb0\xd2\"\x04\x00\x00\u07d4\xa8\xbe\xb9\x1c+\x99\u0216J\xa9[kJ\x18K\x12i\xfc4\x83\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u0794\xa8\xc0\xb0/\xaf\x02\xcbU\x19\u0768\x84\xde{\xbc\x8c\x88\xa2\u0681\x88\xe7\xc2Q\x85\x05\x06\x00\x00\u07d4\xa8\xc1\u05aaA\xfe=e\xf6{\xd0\x1d\xe2\xa8f\xed\x1e\u066eR\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\xa8\xca\xfa\xc3\"\x80\xd0!\x02\v\xf6\xf2\xa9x(\x83\u05ea\xbe\x12\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa8\xdb\v\x9b \x14S3A<;\fb\xf5\xf5.\u0544\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa8\xf3\u007f\n\xb3\xa1\xd4H\xa9\xe3\xce@\x96_\x97\xa6F\b:4\x89\x11\xe0\xe4\xf8\xa5\v\xd4\x00\x00\u07d4\xa8\xf8\x9d\xd5\xccnd\u05f1\xee\xac\xe0\a\x02\x02,\xd7\xd2\xf0=\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\xa9\x04v\xe2\xef\xdf\xeeO8{\x0f2\xa5\x06x\xb0\xef\xb5s\xb5\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa9\x14PF\xfa6(\xcf_\xd4\xc6\x13\x92{\xe51\xe6\xdb\x1f\u0749\x06\x12O\xee\x99;\xc0\x00\x00\u07d4\xa9\x14\u0375q\xbf\xd9=d\xdaf\xa4\xe1\b\xea\x13NP\xd0\x00\x89M\x878\x99G\x13y\x80\x00\xe0\x94\xa9\x1aZ{4\x1f\x99\xc55\x14N \xbe\x9ck;\xb4\u008eM\x8a\x01&u:\xa2$\xa7\v\x00\x00\u07d4\xa9%%Q\xa6$\xaeQ7\x19\u06beR\a\xfb\xef\xb2\xfdwI\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa9'\u050b\xb6\u02c1K\xc6\t\xcb\u02a9\x15\x1f]E\x9a'\xe1\x89\x0e\xb95\t\x00d\x18\x00\x00\u07d4\xa9)\u023dq\xdb\f0\x8d\xac\x06\b\n\x17G\xf2\x1b\x14e\xaa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa9K\xbb\x82\x14\u03cd\xa0\xc2\xf6h\xa2\xacs\xe8bHR\x8dK\x894\n\xad!\xb3\xb7\x00\x00\x00\u07d4\xa9Q\xb2D\xffP\u03eeY\x1d^\x1a\x14\x8d\xf6\xa98\xef*\x1a\x89^\x00\x15\x84\xdf\xcfX\x00\x00\u07d4\xa9`\xb1\xca\xdd;\\\x1a\x8el\xb3\xab\xca\xf5.\xe7\xc3\xd9\xfa\x88\x89R\x8b\xc3T^Rh\x00\x00\u07d4\xa9a\x17\x1fSB\xb1s\xddp\xe7\xbf\xe5\xb5\xca#\x8b\x13\xbc\u0749\xb8'\x94\xa9$O\f\x80\x00\u07d4\xa9u\xb0w\xfc\xb4\u030e\xfc\xbf\x83\x84Y\xb6\xfa$:AY\u0589\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xa9{\xeb:H\xc4_\x15((L\xb6\xa9_}\xe4S5\x8e\u018a\x06\x90\x83l\n\xf5\xf5`\x00\x00\u07d4\xa9~\a!DI\x9f\xe5\xeb\xbd5J\xcc~~\xfbX\x98]\b\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xa9\x86v/zO)O.\v\x172y\xad,\x81\xa2\"4X\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa9\x8f\x10\x985\xf5\xea\xcd\x05Cd|4\xa6\xb2i\xe3\x80/\xac\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa9\x97\xdf\u01d8j'\x05\bH\xfa\x1cd\u05e7\xd6\xe0z\u0322\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\xa9\x99\x91\u03bd\x98\xd9\xc88\xc2_zt\x16\xd9\xe2D\xca%\r\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa9\xa1\xcd\xc3;\xfd7o\x1c\rv\xfbl\x84\xb6\xb4\xac'Mh\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa9\xa8\xec\xa1\x1a#\xd6F\x89\xa2\xaa>A}\xbb=3k\xb5\x9a\x89\x0e4S\xcd;g\xba\x80\x00\u07d4\xa9\xac\xf6\x00\b\x1b\xb5[\xb6\xbf\xba\xb1\x81_\xfcN\x17\xe8Z\x95\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa9\xad\x19&\xbcf\xbd\xb31X\x8e\xa8\x197\x88SM\x98,\x98\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xa9\xaf!\xac\xbeH/\x811\x89j\"\x806\xbaQ\xb1\x94S\u00c9\x02\xb5\xe0!\x98\f\xc1\x80\x00\xe0\x94\xa9\xb2\xd2\xe0IN\xab\x18\xe0}7\xbb\xb8V\xd8\x0e\x80\xf8L\u04ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa9\xbaoA;\x82\xfc\xdd\xf3\xaf\xfb\xbd\u0412\x87\xdc\xf5\x04\x15\u0289\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa9\xbe\x88\xad\x1eQ\x8b\v\xbb\x02J\xb1\xd8\xf0\xe7?y\x0e\fv\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xa9\xbf\xc4\x10\xdd\xdb q\x1eE\xc0s\x87\xea\xb3\n\x05N\x19\xac\x89>\x99`\x1e\xdfNS\x00\x00\u07d4\xa9\u0522\xbc\xbe[\x9e\bi\xd7\x0f\x0f\xe2\xe1\u05aa\xcdE\xed\u0149\n\xc6\xe7z\xb6c\xa8\x00\x00\xe0\x94\xa9\xd6KO;\xb7\x85\a\"\xb5\x8bG\x8b\xa6\x917^\"NB\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa9\xd6\xf8q\xcax\x1au\x9a \xac:\u06d7,\xf1()\xa2\b\x892$\xf4'#\xd4T\x00\x00\xe0\x94\xa9\xdc\x04$\u0196\x9dy\x83X\xb3\x93\xb1\x93:\x1fQ\xbe\xe0\n\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa9\xe1\x94f\x1a\xacpN\xe9\u07a0C\x97N\x96\x92\xde\xd8J]\x89\x1a&\xa5\x14\"\xa0p\x00\x00\u07d4\xa9\xe2\x837\xe65q\x93\xd9\xe2\xcb#k\x01\xbeD\xb8\x14'\u07c9wC\"\x17\xe6\x83`\x00\x00\u07d4\xa9\xe6\xe2^ekv%Xa\x9f\x14z!\x98[\x88t\xed\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa9\xe9\xdb\xcez,\xb06\x94y\x98\x97\xbe\xd7\xc5M\x15_\u06a8\x89\n\xb5\xae\x8f\u025de\x80\x00\u07d4\xa9\xed7{}n\xc2Yq\xc1\xa5\x97\xa3\xb0\xf3\xbe\xadW\u024f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xaa\x02\x00\xf1\xd1~\x9cT\xda\x06G\xbb\x969]W\xa7\x858\u06099>\xf1\xa5\x12|\x80\x00\x00\u07d4\xaa\f\xa3ss7\x17\x8a\f\xaa\xc3\t\x9cXK\x05lV0\x1c\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xaa\x13kG\x96+\xb8\xb4\xfbT\r\xb4\xcc\xf5\xfd\xd0B\xff\xb8\u03c9\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\xaa\x14B-o\n\xe5\xa7X\x19N\xd1W\x80\xc88\xd6\u007f\x1e\xe1\x8a\x06\t2\x05lD\x9d\xe8\x00\x00\u07d4\xaa\x16&\x9a\xac\x9c\r\x800h\xd8/\u01d1Q\xda\xdd3Kf\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xaa\x16p&\u04da\xb7\xa8V5\x94N\xd9\xed\xb2\xbf\xeb\xa1\x18P\x8a\x01\xc1\xd5\xe2\x1bO\xcfh\x00\x00\u07d4\xaa\x1b7h\xc1m\x82\x1fX\x0ev\xc8\xe4\xc8\xe8m}\u01c8S\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xaa\x1d\xf9.Q\xdf\xf7\v\x19s\xe0\xe9$\xc6b\x87\xb4\x94\xa1x\x89\x1c\xf8J0\xa0\xa0\xc0\x00\x00\u07d4\xaa,g\x00\x96\xd3\xf990S%B~\xb9U\xa8\xa6\r\xb3\u0149l\x95Y\x06\x99#-\x00\x00\u07d4\xaa15\xcbT\xf1\x02\xcb\xef\xe0\x9e\x96\x10:\x1ayg\x18\xffT\x89\x03\"\"\xd9\xc31\x94\x00\x00\u07d4\xaa2\x1f\xdb\xd4I\x18\r\xb8\xdd\xd3O\x0f\xe9\x06\xec\x18\xee\t\x14\x89%\"H\u07b6\xe6\x94\x00\x00\xe0\x94\xaa9%\xdc\"\v\xb4\xae!w\xb2\x880x\xb6\xdc4l\xa1\xb2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xaa?)`\x1a\x131t^\x05\xc4(0\xa1^q\x93\x8ab7\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94\xaaG\xa4\xff\xc9y622\u025b\x99\xfa\xda\x0f'4\xb0\xae\xee\x8a\x01\xb8H\x9d\xf4\xdb\xff\x94\x00\x00\u07d4\xaaI=?O\xb8fI\x1c\xf8\xf8\x00\xef\xb7\xe22N\xd7\xcf\xe5\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xaaV\xa6]\u012b\xb7/\x11\xba\xe3+o\xbb\aDG\x91\xd5\u0249(\x94\xe9u\xbfIl\x00\x00\xe0\x94\xaaZ\xfc\xfd\x83\t\xc2\u07dd\x15\xbe^jPN}pf$\u014a\x01<\xf4\"\xe3\x05\xa17\x80\x00\u07d4\xaa\x8e\xb0\x82;\a\xb0\xe6\xd2\n\xad\xda\x0e\x95\xcf85\xbe\x19.\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xaa\x91#~t\r%\xa9/\u007f\xa1F\xfa\xa1\x8c\xe5m\xc6\xe1\xf3\x892$\xf4'#\xd4T\x00\x00\u07d4\xaa\x96\x0e\x10\xc5#\x91\xc5N\x158|\xc6z\xf8'\xb51m\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\x9b\xd4X\x955\xdb'\xfa+\xc9\x03\xca\x17\xd6y\xddeH\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\xa8\xde\xfe\x11\xe3a?\x11\x06\u007f\xb9\x83bZ\b\x99Z\x8d\xfc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaa\xaa\xe6\x8b2\x14\x02\xc8\xeb\xc14h\xf3A\xc6<\f\xf0?\u0389Rf<\u02b1\xe1\xc0\x00\x00\u07d4\xaa\xad\x1b\xaa\xdeZ\xf0N+\x17C\x9e\x93Y\x87\xbf\x8c+\xb4\xb9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xaa\xb0\n\xbfX(\xd7\xeb\xf2kG\u03ac\u0378\xba\x032Qf\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xaa\xbd\xb3\\\x15\x14\x98J\x03\x92\x13y?3E\xa1h\xe8\x1f\xf1\x89\x10\xca\u0216\xd29\x00\x00\x00\u07d4\xaa\xca`\xd9\xd7\x00\u7156\xbb\xbb\xb1\xf1\xe2\xf7\x0fF'\xf9\u060965\xbbw\xcbK\x86\x00\x00\u07d4\xaa\xce\u0629V;\x1b\xc3\x11\xdb\xdf\xfc\x1a\xe7\xf5u\x19\xc4D\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\u04b7\xf8\x10f\x95\a\x8el\x13\x8e\xc8\x1at\x86\xaa\xca\x1e\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaa\xe6\x1eC\xcb\r\f\x96\xb3\x06\x99\xf7~\x00\xd7\x11\u0423\x97\x9b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaa\xe72\xed\xa6Y\x88\u00e0\f\u007fG/5\x1cF;\x1c\x96\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\xf0#\xfe\U0009091b\xb7\x8b\xb7\xab\xc9]f\x9cP\xd5(\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xaa\xf5\xb2\a\xb8\x8b\r\xe4\xac@\xd7G\xce\xe0n\x17-\xf6\xe7E\x8a\x06\xa7\xb7\x1d\u007fQ\u0410\x00\x00\u07d4\xaa\xf9\xeeK\x88lm\x1e\x95Io\xd2t#[\xf4\xec\xfc\xb0}\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xaa\xfb{\x01:\xa1\xf8T\x1c~2{\xf6P\xad\xbd\x19L \x8f\x89I\x9e\t-\x01\xf4x\x00\x00\xe0\x94\xab\t\x863\xee\xee\f\xce\xfd\xf62\xf9WTV\xf6\u0740\xfc\x86\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xab\f\xedv.\x16a\xfa\xe1\xa9*\xfb\x14\b\x88\x94\x13yH%\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xab\x14\xd2!\xe3=TF)\x19\x8c\u0416\xedc\u07e2\x8d\x9fG\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xab \x9f\u0729y\u0426G\x01\n\xf9\xa8\xb5/\xc7\xd2\r\x8c\u044a\x01\xee\xe2S,|-\x04\x00\x00\u07d4\xab'\xbax\xc8\xe5\xe3\xda\xef1\xad\x05\xae\xf0\xff\x03%r\x1e\b\x89\x19^\xce\x00n\x02\xd0\x00\x00\u07d4\xab(q\xe5\a\u01fe9eI\x8e\x8f\xb4b\x02Z\x1a\x1cBd\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xab8a\"o\xfe\xc1(\x91\x87\xfb\x84\xa0\x8e\xc3\xed\x042d\xe8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab=\x86\xbc\x82\x92~\f\xd4!\xd1F\xe0\u007f\x91\x93'\xcd\xf6\xf9\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xab>b\xe7z\x8b\"^A\x15\x92\xb1\xaf0\aR\xfeA$c\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xab>x)K\xa8\x86\xa0\xcf\xd5\xd3H\u007f\xb3\xa3\a\x8d3\x8dn\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xab@\x04\xc0@?~\xab\xb0\xeaXo!!V\xc4 =g\xf1\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xabAo\xe3\rX\xaf\xe5\xd9EL\u007f\xce\u007f\x83\v\xccu\x03V\x89\x0657\x01\xc6\x05\u06c0\x00\u07d4\xabEr\xfb\xb1\xd7+W]i\xecj\xd1s3\x87>\x85R\xfc\x89lj\xc5L\xdahG\x00\x00\u07d4\xabZy\x01av2\ts\xe8\xcd8\xf67U0\x02%1\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab]\xfc\x1e\xa2\x1a\xdcB\u03cc?n6\x1e$?\xd0\xdaa\xe5\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xabke\xea\xb8\xdf\xc9\x17\xec\x02Q\xb9\xdb\x0e\u03e0\xfa\x03(I\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xabp\x91\x93.K\u00dd\xbbU#\x80\u0293O\xd7\x16m\x1en\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\xabt\x16\xff2%IQ\u02fcbN\xc7\xfbE\xfc~\u02a8r\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xab|B\xc5\xe5-d\x1a\a\xadu\t\x9cb\x92\x8b\u007f\x86b/\x89\x126\x1a\xa2\x1d\x14\xba\x00\x00\u07d4\xab}T\xc7\xc6W\x0e\xfc\xa5\xb4\xb8\xcep\xf5*Ws\xe5\xd5;\x89\x0f(:\xbe\x9d\x9f8\x00\x00\u07d4\xab~\v\x83\xed\x9aBLm\x1ejo\x87\xa4\xdb\xf0d\t\xc7\u0589\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xab\x84\xa0\xf1G\xad&T\x00\x00+\x85\x02\x9aA\xfc\x9c\xe5\u007f\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab\x93\xb2n\xce\n\n\xa2\x13e\xaf\xed\x1f\xa9\xae\xa3\x1c\xd5Dh\x89W+{\x98sl \x00\x00\u07d4\xab\x94\x8aJ\xe3y\\\xbc\xa11&\xe1\x92S\xbd\xc2\x1d:\x85\x14\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xab\x9a\xd3n\\t\xce.\x969\x9fW\x83\x941\xd0\u77d6\xab\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\xab\xb2\xe6\xa7*@\xban\xd9\b\u037c\xec\x91\xac\xfdwx0\xd1dcG\x8a\xe0\xfcw \x89\a?u\u0460\x85\xba\x00\x00\xe0\x94\xab\u071f\x1b\xcfM\x19\xee\x96Y\x100\xe7r\xc340/}\x83\x8a\b~^\x11\xa8\x1c\xb5\xf8\x00\x00\u07d4\xab\xde\x14{*\xf7\x89\ua946T~f\xc4\xfa&d\xd3(\xa4\x89\rk`\x81\xf3L\x12\x80\x00\xe0\x94\xab\xe0|\xedj\xc5\xdd\xf9\x91\xef\xf6\xc3\xda\"jt\x1b\xd2C\xfe\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xab\xf1/\xa1\x9e\x82\xf7lq\x8f\x01\xbd\xca\x00\x03gE#\xef0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xab\xf7(\u03d3\x12\xf2!(\x02NpF\xc2Q\xf5\xdcY\x01\xed\x8a\x06A\xe8\xa15c\xd8\xf8\x00\x00\u07d4\xab\xf8\xff\xe0p\x8a\x99\xb5(\xcc\x1e\xd4\xe9\xceK\r\x060\xbe\x8c\x89z\xb5\u00ae\xee\xe68\x00\x00\u07d4\xab\xfc\xf5\xf2P\x91\xceW\x87_\xc6t\xdc\xf1\x04\xe2\xa7=\xd2\xf2\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xab\xfe\x93d%\xdc\u01f7K\x95P\x82\xbb\xaa\xf2\xa1\x1dx\xbc\x05\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xac\x02OYO\x95X\xf0ICa\x8e\xb0\xe6\xb2\xeeP\x1d\xc2r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\x12*\x03\xcd\x05\x8c\x12._\xe1{\x87/Hw\xf9\u07d5r\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xac\x14.\xda\x11W\xb9\xa9\xa6C\x90\xdf~j\xe6\x94\xfa\u0249\x05\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\x1d\xfc\x98Kq\xa1\x99)\xa8\x1d\x81\xf0J|\xbb\x14\a7\x03\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xac!\xc1\xe5\xa3\xd7\xe0\xb5\x06\x81g\x9d\xd6\u01d2\xdb\u0287\xde\u02ca\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xac(\x89\xb5\x96o\f\u007f\x9e\xdbB\x89\\\xb6\x9d\x1c\x04\xf9#\xa2\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xac(\xb5\xed\xea\x05\xb7o\x8c_\x97\bEA'|\x96ijL\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xac,\x8e\t\xd0d\x93\xa68XC{\xd2\v\xe0\x19bE\x03e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xac.vm\xac?d\x8fcz\xc6q?\u0770h\xe4\xa4\xf0M\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\xac9\x00)\x8d\xd1M|\xc9mJ\xbbB\x8d\xa1\xba\xe2\x13\xff\xed\x8a\x05<\xa1)t\x85\x1c\x01\x00\x00\u07d4\xac=\xa5&\xcf\u0388)s\x02\xf3LI\xcaR\r\xc2q\xf9\xb2\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xacD`\xa7nm\xb2\xb9\xfc\xd1R\xd9\xc7q\x8d\x9a\xc6\xed\x8co\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xacJ\xcf\xc3n\xd6\tJ'\xe1\x18\xec\xc9\x11\xcdG>\x8f\xb9\x1f\x89a\x91>\x14@<\f\x00\x00\u07d4\xacL\xc2V\xaet\xd6$\xac\xe8\r\xb0x\xb2 \u007fW\x19\x8fk\x89lyt\x12?d\xa4\x00\x00\u07d4\xacN\xe9\xd5\x02\xe7\xd2\xd2\xe9\x9eY\xd8\xca}_\x00\xc9KM\u058965\u026d\xc5\u07a0\x00\x00\u07d4\xacR\xb7~\x15fH\x14\xf3\x9eO'\x1b\xe6A0\x8d\x91\xd6\u0309\v\xed\x1d\x02c\xd9\xf0\x00\x00\u07d4\xacY\x99\xa8\x9d-\u0486\u0568\fm\xee~\x86\xaa\xd4\x0f\x9e\x12\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xac_br1H\r\r\x950.m\x89\xfc2\xcb\x1dO\xe7\xe3\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xac`\x8e+\xac\x9d\xd2\a(\u0494~\xff\xbb\xbf\x90\n\x9c\xe9K\x8a\x01EK\r\xb3uh\xfc\x00\x00\u07d4\xacm\x02\xe9\xa4k7\x9f\xacJ\u0271\u05f5\xd4{\xc8P\xce\x16\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xacoh\xe87\xcf\x19a\xcb\x14\xabGDm\xa1h\xa1m\u0789\x89Hz\x9a0E9D\x00\x00\u07d4\xacw\xbd\xf0\x0f\u0558[]\xb1+\xbe\xf8\x008\n\xbc*\x06w\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xac~\x03p'#\xcb\x16\xee'\xe2-\u0438\x15\xdc-\\\xae\x9f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xac\x8bP\x9a\xef\xea\x1d\xbf\xaf+\xb35\x00\xd6W\vo\xd9mQ\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xac\x8e\x87\xdd\xda^x\xfc\xbc\xb9\xfa\u007f\xc3\xce\x03\x8f\x9f}.4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xac\x9f\xffh\xc6\x1b\x01\x1e\xfb\xec\xf08\xedr\u06d7\xbb\x9er\x81\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xac\xa1\xe6\xbcd\xcc1\x80\xf6 \xe9M\u0171\xbc\xfd\x81X\xe4]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\xa2\xa883\v\x170-\xa71\xd3\r\xb4\x8a\x04\xf0\xf2\a\xc1\x89Hz\x9a0E9D\x00\x00\u07d4\xac\xaa\xdd\xcb\xf2\x86\xcb\x0e!]\xdaUY\x8f\u007f\xf0\xf4\xad\xa5\u018965\u026d\xc5\u07a0\x00\x00\u07d4\xac\xb9C8UK\u0108\u0308\xae-\x9d\x94\b\rk\u07c4\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xac\xbc-\x19\xe0l;\xab\xbb[o\x05+k\xf7\xfc7\xe0r)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\xbd\x18U\x89\xf7\xa6\x8ag\xaaK\x1b\xd6Pw\xf8\xc6NN!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\xc0bp,Ya]4D\xefb\x14\xb8\x86+\x00\x9a\x02\xed\x89QO\xcb$\xff\x9cP\x00\x00\u07d4\xac\xc0\x90\x9f\xda.\xa6\xb7\xb7\xa8\x8d\xb7\xa0\xaa\xc8h\t\x1d\xdb\xf6\x89\x013v_\x1e&\u01c0\x00\u07d4\xac\xc1\u01c7\x86\xabM+;'q5\xb5\xba\x12>\x04\x00Hk\x89\x04E\x91\xd6\u007f\xec\xc8\x00\x00\u07d4\xac\xc4j*U\\t\xde\u0522\xbd\tN\x82\x1b\x97\x84;@\xc0\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xac\u015f;0\xce\xff\xc5da\xcc[\x8d\xf4\x89\x02$\x0e\x0e{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\xce\x01\xe0\xa7\x06\x10\xdcp\xbb\x91\xe9\x92o\xa9\x95\u007f7/\xba\x89\x1d\x1c_>\xda \xc4\x00\x00\u07d4\xac\xd8\u0751\xf7\x14vLEg|c\xd8R\xe5n\xb9\xee\xce.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\u2af6;\x06\x04@\x9f\xbd\xe3\xe7\x16\u0487mD\xe8\xe5\u0749\b=lz\xabc`\x00\x00\xe0\x94\xac\xec\x91\xefiA\xcfc\v\xa9\xa3\u71e0\x12\xf4\xa2\xd9\x1d\u050a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xad\nJ\xe4x\xe9cn\x88\xc6\x04\xf2B\xcfT9\xc6\xd4V9\x89\xbe\xd1\xd0&=\x9f\x00\x00\x00\u07d4\xad\x17\x99\xaa\xd7`+E@\u0343/\x9d\xb5\xf1\x11P\xf1hz\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xad\x1dh\xa08\xfd%\x86\x06~\xf6\xd15\xd9b\x8ey\xc2\xc9$\x89\xfe\t\xa5'\x9e*\xbc\x00\x00\u07d4\xad*\\\x00\xf9#\xaa\xf2\x1a\xb9\xf3\xfb\x06n\xfa\n\x03\xde/\xb2\x8965\xbbw\xcbK\x86\x00\x00\u07d4\xad5e\xd5+h\x8a\xdd\xed\b\x16\x8b-8r\xd1}\n&\xae\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xad7|\xd2^\xb5>\x83\xae\t\x1a\n\x1d+E\x16\xf4\x84\xaf\u0789i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xadAM)\xcb~\xe9s\xfe\xc5N\"\xa3\x88I\x17\x86\xcfT\x02\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xadD5~\x01~$OGi1\u01f8\x18\x9e\xfe\xe8\n]\n\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xadW\xaa\x9d\x00\xd1\fC\x9b5\xef\xcc\v\xec\xac.9U\xc3\x13\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xadY\xa7\x8e\xb9\xa7J\u007f\xbd\xae\xfa\xfa\x82\xea\u0684u\xf0\u007f\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xadZ\x8dV\x01L\xfc\xb3`\xf4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xadr\x81!\x87?\x04V\xd0Q\x8b\x80\xabe\x80\xa2\x03pe\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xads,\x97e\x93\xee\xc4x;N.\xcdy9yx\v\xfe\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xad}\xd0S\x85\x9e\xdf\xf1\xcbo\x9d*\xcb\xedm\xd5\xe32Bo\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xad\x80\xd8e\xb8\\4\xd2\xe6IK.z\xef\xeak\x9a\xf1\x84\u06c9\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xad\x8b\xfe\xf8\u018aH\x16\xb3\x91o5\xcb{\xfc\xd7\xd3\x04\tv\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xad\x8eH\xa3wi]\xe0\x146:R:(\xb1\xa4\fx\xf2\b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xad\x91\n#\u0585\x06\x13eJ\xf7\x863z\u04a7\bh\xacm\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xad\x92~\x03\xd1Y\x9ax\xca+\xf0\xca\u04a1\x83\xdc\xebq\xea\xc0\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xad\x92\xca\x06n\xdb|q\x1d\xfc[\x16a\x92\xd1\xed\xf8\xe7q\x85\x8a\a\x9f\x90\\o\xd3N\x80\x00\x00\u07d4\xad\x94#_\u00f3\xf4z$\x13\xaf1\u8111I\b\xef\fE\x89\x1b\x1b\x01B\xd8\x15\x84\x00\x00\u07d4\xad\x9e\x97\xa0H/5:\x05\xc0\xf7\x92\xb9w\xb6\xc7\xe8\x11\xfa_\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xad\x9fL\x89\n;Q\x1c\xeeQ\xdf\xe6\xcf\xd7\xf1\t;vA,\x89\x1bv|\xbf\xeb\f\xe4\x00\x00\u07d4\xad\xaa\x0eT\x8c\x03Z\xff\xedd\xcag\x8a\x96?\xab\xe9\xa2k\xfd\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\xad\xb9H\xb1\xb6\xfe\xfe }\xe6^\x9b\xbc-\xe9\x8e`]\vW\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xad\xc1\x9e\xc85\xaf\xe3\u5347\u0713\xa8\xa9!<\x90E\x13&\x89j\xdb\xe54\"\x82\x00\x00\x00\u07d4\xad\xc8\"\x8e\xf9(\xe1\x8b*\x80}\x00\xfb1\xfcgX\x15\xaa\x00\x00\u07d4\xad\xff\r\x1d\v\x97G\x1ev\u05c9\xd2\u470at\xf9\xbdT\xff\x89e\xea=\xb7UF`\x00\x00\u07d4\xae\x06,D\x86\x18d0u\xdez\x0004-\xce\xd6=\xba\u05c9,\xc6\u034c\u0082\xb3\x00\x00\xe0\x94\xae\x10\xe2z\x01O\r0k\xaf&mH\x97\u021a\xee\xe2\xe9t\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xae\x12k8,\xf2W\xfa\xd7\xf0\xbc}\x16)~T\xccrg\u0689\x10CV\x1a\x88)0\x00\x00\u07d4\xae\x13\xa0\x85\x11\x11\x0f2\xe5;\xe4\x12xE\xc8C\xa1\xa5|{\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xae\x17\x9aF\r\xb6c&t=$\xe6u#\xa5{$m\xaf\u007f\x8a\x01\x00\a\xae|\xe5\xbb\xe4\x00\x00\u07d4\xae\"(ey\x90y\xaa\xf4\xf0gJ\f\u06ab\x02\xa6\xd5p\xff\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xae#\x9a\xcf\xfdN\xbe.\x1b\xa5\xb4\x17\x05r\xdcy\xcce3\xec\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xae/\x9c\x19\xacv\x13e\x94C#\x93\xb0G\x1d\b\x90!d\u04c9%\xdf\x05\u01a8\x97\xe4\x00\x00\u07d4\xae4\x86\x1d4\"S\x19O\xfcfR\xdf\xdeQ\xabD\xca\xd3\xfe\x89\x19F\bhc\x16\xbd\x80\x00\u07d4\xae6\xf7E!!\x91>\x80\x0e\x0f\xcd\x1ae\xa5G\x1c#\x84o\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\xae?\x98\xa4C\xef\xe0\x0f>q\x1dR]\x98\x94\u071aa\x15{\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\xe0\x94\xaeG\xe2`\x9c\xfa\xfe6\x9df\xd4\x15\xd99\xde\x05\b\x1a\x98r\x8a\x05\xba\xec\xf0%\xf9\xb6P\x00\x00\u07d4\xaeO\x12.5\xc0\xb1\xd1\xe4\x06\x92\x91E|\x83\xc0\u007f\x96_\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaePU\x81L\xb8\xbe\f\x11{\xb8\xb1\xc8\u04b6;F\x98\xb7(\x89\x01\xbc\x93.\xc5s\xa3\x80\x00\u07d4\xaeS\x8cs\u0173\x8d\x8dXM~\xbd\xad\xef\xb1\\\xab\xe4\x83W\x896'\xe8\xf7\x127<\x00\x00\u07d4\xaeW\xcc\x12\x9a\x96\xa8\x99\x81\xda\xc6\r/\xfb\x87}]\xc5\xe42\x89<:#\x94\xb3\x96U\x00\x00\u07d4\xaeZ\xa1\xe6\u00b6\x0fo\xd3\xef\xe7!\xbbJq\x9c\xbe=o]\x89+$\u01b5Z^b\x00\x00\u07d4\xae\\\x9b\xda\xd3\xc5\u0221\"\x04D\xae\xa5\xc2)\xc1\x83\x9f\x1dd\x89\x19\xe2\xa4\xc8\x18\xb9\x06\x00\x00\u07d4\xae\\\xe35Z{\xa9\xb32v\f\tP\u00bcE\xa8_\xa9\xa0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xae]\"\x1a\xfc\xd3\u0493U\xf5\b\xea\xdf\xca@\x8c\xe3<\xa9\x03\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xaec[\xf781\x11\x9d-)\xc0\xd0O\xf8\xf8\xd8\u0425zF\x89Hz\x9a0E9D\x00\x00\xe0\x94\xaed\x81U\xa6X7\x0f\x92\x9b\xe3\x84\xf7\xe0\x01\x04~I\xddF\x8a\x02\xdf$\xae2\xbe D\x00\x00\xe0\x94\xaeo\fs\xfd\xd7|H\x97'Q!t\u0675\x02\x96a\x1cL\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xaep\xe6\x9d,J\n\xf8\x18\x80{\x1a'\x05\xf7\x9f\u0435\xdb\u01095e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xaew9\x12N\xd1S\x05%\x03\xfc\x10\x14\x10\xd1\xff\xd8\xcd\x13\xb7\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xaex\xbb\x84\x919\xa6\xba8\xae\x92\xa0\x9ai`\x1c\xc4\xcbb\u0449\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xae\x84\"\x10\xf4M\x14\u0124\u06d1\xfc\x9d;;P\x01O{\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xae\x84.\x81\x85\x8e\xcf\xed\xf6Plhm\xc2\x04\xac\x15\xbf\x8b$\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xae\x89T\xf8\xd6\x16m\xe5\a\xcfa)}\x0f\xc7\xcak\x9eq(\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xae\x9e\xcdk\u0755.\xf4\x97\xc0\x05\n\u0aca\x82\xa9\x18\x98\u0389\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\xae\x9f\\?\xbb\xe0\u027c\xbf\x1a\xf8\xfft\xea(\v:]\x8b\b\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xae\xad\x88\u0589Ak\x1c\x91\xf26D!7[}\x82\xd0RR\n\xfb\\Wm\x9f~\xb9>\u048a\r\xd0A \xba\t\xcf\xe6\x00\x00\u07d4\xae\xc2\u007f\xf5\xd7\xf9\xdd\u0691\x18?F\xf9\xd5%C\xb6\xcd+/\x89\x18e\x01'\xcc=\xc8\x00\x00\u07d4\xae\xe4\x9dh\xad\xed\xb0\x81\xfdCpZ_x\xc7x\xfb\x90\xdeH\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xae\xf5\xb1\"X\xa1\x8d\xec\a\xd5\xec.1et\x91\x9dy\xd6\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\xae\xfc\xfe\x88\xc8&\xcc\xf11\xd5N\xb4\ua7b8\x0ea\xe1\xee%\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xaf\x06\xf5\xfam\x12\x14\xecC\x96}\x1b\xd4\xdd\xe7J\xb8\x14\xa98\x89\x04\xc5>\xcd\xc1\x8a`\x00\x00\u07d4\xaf\x11H\xefl\x8e\x10=u0\xef\xc9\x16y\u026c'\x00\t\x93\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaf >\"\x9d~mA\x9d\xf47\x8e\xa9\x87\x15Q_c\x14\x85\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xaf X\xc7(,\xf6|\x8c<\xf90\x13<\x89a|\xe7])\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4\xaf&\xf7\u01bfE> x\xf0\x89S\u4c80\x04\xa2\xc1\xe2\t\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xaf0\x87\xe6.\x04\xbf\x90\rZT\xdc>\x94bt\u0692B;\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf6\x14\u0736\x8a6\xe4ZN\x91\x1ebybG\"-Y[\x89z\x81\x06_\x11\x03\xbc\x00\x00\u07d4\xaf6\x15\u01c9\u0431\x15*\xd4\xdb%\xfe]\xcf\"(\x04\xcfb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaf<\xb5\x96Y3\xe7\xda\u0603i;\x9c>\x15\xbe\xb6\x8aHs\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xafD\x93\xe8R\x1c\xa8\x9d\x95\xf5&|\x1a\xb6?\x9fEA\x1e\x1b\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xafL\xf4\x17\x85\x16\x1fW\x1d\f\xa6\x9c\x94\xf8\x02\x1fA)N\u028a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xafR\x9b\xdbE\x9c\xc1\x85\xbe\xe5\xa1\u014b\xf7\xe8\xcc\xe2\\\x15\r\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xafg\xfd>\x12\u007f\xd9\xdc6\xeb?\xcdj\x80\u01feOu2\xb2\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xafw\x1094Z40\x01\xbc\x0f\x8aY#\xb1&\xb6\rP\x9c\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\xaf\u007fy\xcbAZ\x1f\xb8\u06fd\tF\a\xee\x8dA\xfb|Z;\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xaf\x87\xd27\x1e\xf3x\x95\u007f\xbd\x05\xba/\x1df\x93\x1b\x01\u2e09%\xf2s\x93=\xb5p\x00\x00\u07d4\xaf\x88\x0f\xc7V}U\x95\xca\xcc\xe1\\?\xc1L\x87B\xc2l\x9e\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xaf\x8e\x1d\xcb1L\x95\r6\x87CM0\x98X\xe1\xa8s\x9c\u0509\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xaf\x99-\xd6i\xc0\x88>U\x15\xd3\xf3\x11*\x13\xf6\x17\xa4\xc3g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaf\xa1\u056d8\xfe\xd4GY\xc0[\x89\x93\xc1\xaa\r\xac\xe1\x9f@\x89\x04V9\x18$O@\x00\x00\xe0\x94\xaf\xa59XnG\x19\x17J;F\xb9\xb3\xe6c\xa7\u0475\xb9\x87\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xaf\xa6\x94n\xff\xd5\xffS\x15O\x82\x01\x02S\xdfG\xae(\f\u0309j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xaf\xc8\xeb\u860b\xd4\x10Z\xccL\x01\x8eTj\x1e\x8f\x9cx\x88\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xaf\xcc}\xbb\x83V\xd8B\xd4:\xe7\xe2<\x84\"\xb0\"\xa3\b\x03\x8a\x06o\xfc\xbf\xd5\xe5\xa3\x00\x00\x00\u07d4\xaf\xd0\x19\xff6\xa0\x91U4ki\x97H\x15\xa1\xc9\x12\xc9\n\xa4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaf\xda\xc5\xc1\xcbV\xe2E\xbfp3\x00f\xa8\x17\uabecL\u0449\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf\xdd\x1bxab\xb81~ \xf0\xe9y\xf4\xb2\xceHmv]\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf\xf1\x04Z\xdf'\xa1\xaa2\x94a\xb2M\xe1\xba\u950ai\x8b\x89\x01\u03c4\xa3\n\n\f\x00\x00\u07d4\xaf\xf1\a\x96\v~\xc3N\u0590\xb6e\x02M`\x83\x8c\x19\x0fp\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xaf\xf1\x1c\xcfi\x93\x04\xd5\xf5\x86*\xf8`\x83E\x1c&\xe7\x9a\xe5\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xaf\xf1at\nm\x90\x9f\xe9\x9cY\xa9\xb7yE\xc9\x1c\xc9\x14H\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\xaf\xfc\x99\xd5\ubd28O\xe7x\x8d\x97\xdc\xe2t\xb08$\x048\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xaf\xfe\xa0G7\"\xcb\u007f\x0e\x0e\x86\xb9\xe1\x18\x83\xbfB\x8d\x8dT\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xb0\t\x96\xb0Vn\xcb>rC\xb8\"y\x88\u0733R\xc2\x18\x99\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xb0\x1e8\x9b(\xa3\x1d\x8eI\x95\xbd\xd7\xd7\xc8\x1b\xee\xab\x1eA\x19\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb0-\x06(s3EE\u03a2\x92\x18\xe4\x05w`Y\x0ft#\x89\xac\xb6\xa1\xc7\xd9:\x88\x00\x00\u07d4\xb0/\xa2\x93\x87\xec\x12\xe3\u007fi\"\xacL\xe9\x8c[\t\xe0\xb0\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb06\x91k\xda\u03d4\xb6\x9eZ\x8ae`)u\xeb\x02a\x04\u0749\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb0A1\x0f\xe9\xee\u0586L\xed\u053e\xe5\x8d\xf8\x8e\xb4\xed<\xac\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb0U\xafL\xad\xfc\xfd\xb4%\xcfe\xbad1\a\x8f\a\xec\u056b\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xb0W\x11S\xdb\x1cN\u05ec\xae\xfe\x13\xec\xdf\xdbr\xe7\xe4\xf0j\x8a\x11\f\xffyj\xc1\x95 \x00\x00\u07d4\xb0n\xab\t\xa6\x10\u01a5=V\xa9F\xb2\xc44\x87\xac\x1d[-\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb0rI\xe0U\x04J\x91U5\x9a@)7\xbb\xd9T\xfeH\xb6\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xb0v\x182\x8a\x90\x13\a\xa1\xb7\xa0\xd0X\xfc\xd5xn\x9er\xfe\x8a\x06gI]JC0\xce\x00\x00\u07d4\xb0y\xbbM\x98f\x14:m\xa7*\xe7\xac\x00\"\x06)\x811\\\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4\xb0{\xcc\bZ\xb3\xf7)\xf2D\x00Ah7\xb6\x996\xba\x88s\x89lm\x84\xbc\xcd\xd9\xce\x00\x00\u07d4\xb0{\xcf\x1c\xc5\xd4F.Q$\xc9e\xec\xf0\xd7\r\xc2z\xcau\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\xb0|\xb9\xc1$\x05\xb7\x11\x80uC\u0113De\xf8\u007f\x98\xbd-\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb0\u007f\u07af\xf9\x1dD`\xfel\xd0\u8870\xbd\x8d\"\xa6.\x87\x8a\x01\x1d%)\xf3SZ\xb0\x00\x00\xe0\x94\xb0\x9f\xe6\xd44\x9b\x99\xbc7\x93\x80T\x02-T\xfc\xa3f\xf7\xaf\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94\xb0\xaa\x00\x95\f\x0e\x81\xfa2\x10\x17>r\x9a\xaf\x16:'\xcdq\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xb0\xacN\xfff\x80\xee\x14\x16\x9c\xda\xdb\xff\xdb0\x80Om%\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb0\xb3j\xf9\xae\xee\u07d7\xb6\xb0\"\x80\xf1\x14\xf19\x84\xea2`\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xb0\xb7y\xb9K\xfa<.\x1fX{\u031c~!x\x92\"7\x8f\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xb0\xba\xeb0\xe3\x13wlLm$t\x02\xbaAg\xaf\u0361\u0309j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb0\xbb)\xa8a\xea\x1dBME\xac\u053f\u0112\xfb\x8e\xd8\t\xb7\x89\x04V9\x18$O@\x00\x00\xe0\x94\xb0\xc1\xb1w\xa2 \xe4\x1f|t\xd0|\u0785i\xc2\x1cu\xc2\xf9\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xb0\xc7\xceL\r\xc3\u00bb\xb9\x9c\xc1\x85{\x8aE_a\x17\x11\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb0\xce\xf8\xe8\xfb\x89\x84\xa6\x01\x9f\x01\xc6y\xf2r\xbb\xe6\x8f\\w\x89\b=lz\xabc`\x00\x00\xe0\x94\xb0\xd3+\xd7\xe4\u6577\xb0\x1a\xa3\xd0Ao\x80U}\xba\x99\x03\x8a\x03s\x9f\xf0\xf6\xe6\x130\x00\x00\xe0\x94\xb0\xd3\u0247+\x85\x05n\xa0\xc0\xe6\xd1\xec\xf7\xa7~<\u6ac5\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\xb0\xe4i\u0206Y8\x15\xb3IV8Y]\xae\xf0f_\xaeb\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xb0\xe7`\xbb\a\xc0\x81wsE\xe0W\x8e\x8b\u0218\"mN;\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1\x040\x04\xec\x19A\xa8\xcfO+\x00\xb1W\x00\u076co\xf1~\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb1\x05\xdd=\x98|\xff\xd8\x13\xe9\xc8P\n\x80\xa1\xad%}V\u0189lj\xccg\u05f1\xd4\x00\x00\u07d4\xb1\x0f\u04a6G\x10/\x88\x1ft\xc9\xfb\xc3}\xa62\x94\x9f#u\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xb1\x15\xee:\xb7d\x1e\x1a\xa6\xd0\x00\xe4\x1b\xfc\x1e\xc7!\f/2\x8a\x02\xc0\xbb=\xd3\fN \x00\x00\u07d4\xb1\x17\x8a\xd4s\x83\xc3\x1c\x814\xa1\x94\x1c\xbc\xd4t\xd0bD\xe2\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xb1\x17\x95\x89\u1779\xd4\x15W\xbb\xec\x1c\xb2L\xcc-\xec\x1c\u007f\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xb1\x19\u76a9\xb9\x16Re\x81\xcb\xf5!\xefGJ\xe8M\xcf\xf4\x89O\xba\x10\x01\xe5\xbe\xfe\x00\x00\u07d4\xb1\x1f\xa7\xfb'\n\xbc\xdfZ.\xab\x95\xaa0\u013566\uffc9+^:\xf1k\x18\x80\x00\x00\u07d4\xb1$\xbc\xb6\xff\xa40\xfc\xae.\x86\xb4_'\xe3\xf2\x1e\x81\xee\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1)\xa5\xcbq\x05\xfe\x81\v\u0615\xdcr\x06\xa9\x91\xa4TT\x88\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\xe0\x94\xb1.\xd0{\x8a8\xadU\x066?\xc0z\vmy\x996\xbd\xaf\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb14\xc0\x049\x1a\xb4\x99(x3zQ\xec$/B(WB\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1?\x93\xaf0\xe8\xd7fs\x81\xb2\xb9[\xc1\xa6\x99\xd5\xe3\xe1)\x89\x16\u012b\xbe\xbe\xa0\x10\x00\x00\u07d4\xb1E\x92\x85\x86>\xa2\xdb7Y\xe5F\u03b3\xfb7a\xf5\x90\x9c\x89<\xd7*\x89@\x87\xe0\x80\x00\u07d4\xb1F\xa0\xb9%U<\xf0o\xca\xf5J\x1bM\xfe\xa6!)\aW\x89lnY\xe6|xT\x00\x00\xe0\x94\xb1Jz\xaa\x8fI\xf2\xfb\x9a\x81\x02\u05bb\xe4\u010a\xe7\xc0o\xb2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb1K\xbe\xffpr\tu\xdca\x91\xb2\xa4O\xf4\x9f&r\x87<\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\xe0\x94\xb1L\xc8\xde3\xd63\x826S\x9aH\x90 \xceFU\xa3+\u018a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb1M\xdb\x03\x86\xfb`c\x98\xb8\xccGVZ\xfa\xe0\x0f\xf1\xd6j\x89\xa1*\xff\b>f\xf0\x00\x00\u07d4\xb1S\xf8(\xdd\amJ|\x1c%t\xbb-\xee\x1aD\xa3\x18\xa8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb1T\x0e\x94\xcf\xf3F\\\xc3\u0447\xe7\xc8\u3f6f\x98FY\u2262\x15\xe4C\x90\xe33\x00\x00\u07d4\xb1X\xdbC\xfab\xd3\x0ee\xf3\u041b\xf7\x81\u01f6sr\uba89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xb1ar_\xdc\xed\xd1yR\xd5{#\xef([~K\x11i\xe8\x89\x02\xb6\xdf\xed6d\x95\x80\x00\u07d4\xb1dy\xba\x8e}\xf8\xf6>\x1b\x95\xd1I\u0345)\xd75\xc2\u0689-\xe3:j\xac2T\x80\x00\u07d4\xb1f\xe3}.P\x1a\xe7<\x84\x14+_\xfbZ\xa6U\xddZ\x99\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xb1\x83\xeb\xeeO\xcbB\xc2 \xe4wt\xf5\x9dlT\xd5\xe3*\xb1\x89V\xf7\xa9\xc3<\x04\xd1\x00\x00\u07d4\xb1\x88\a\x84D\x02~8g\x98\xa8\xaehi\x89\x19\xd5\xcc#\r\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xb1\x89j7\xe5\u0602Z-\x01vZ\xe5\xdeb\x99w\u0783R\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb1\x8eg\xa5\x05\n\x1d\xc9\xfb\x19\t\x19\xa3=\xa88\xefDP\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb1\xa2\xb4:t3\xdd\x15\v\xb8\"'\xedQ\x9c\u05b1B\u04c2\x89\x94mb\rtK\x88\x00\x00\u07d4\xb1\xc0\u040b6\xe1\x84\xf9\x95*@7\xe3\xe5:f}\a\nN\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb1\xc3(\xfb\x98\xf2\xf1\x9a\xb6do\n|\x8cVo\xdaZ\x85@\x89\x87\x86x2n\xac\x90\x00\x00\xe0\x94\xb1\xc7Qxi9\xbb\xa0\xd6q\xa6w\xa1X\u01ab\xe7&^F\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xb1\xcdK\xdf\xd1\x04H\x9a\x02n\u025dYs\a\xa0By\xf1s\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb1\u03d4\xf8\t\x15\x05\x05_\x01\n\xb4\xba\u0196\xe0\xca\x0fg\xa1\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xb1\u05b0\x1b\x94\xd8T\xfe\x8b7J\xa6^\x89\\\xf2*\xa2V\x0e\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94\xb1\u06e5%\v\xa9bWU$n\x06yg\xf2\xad/\a\x91\u078a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xb1\xe2\u0755\xe3\x9a\xe9w\\U\xae\xb1?\x12\xc2\xfa#0S\xba\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1\xe6\xe8\x10\xc2J\xb0H\x8d\xe9\xe0\x1eWH7\x82\x9f|w\u0409\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb1\xe9\xc5\xf1\xd2\x1eauzk.\xe7Y\x13\xfcZ\x1aA\x01\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\x03\u049elV\xb9&\x99\u0139-\x1fo\x84d\x8d\xc4\u03fc\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\x16\xdcY\xe2|=ry\xf5\xcd[\xb2\xbe\u03f2`n\x14\u0649\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\x1byy\xbf|\\\xa0\x1f\xa8-\xd6@\xb4\x1c9\xe6\u01bcu\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xb2#\xbf\x1f\xbf\x80H\\\xa2\xb5V}\x98\xdb{\xc3SM\xd6i\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb2-PU\xd9b15\x96\x1ej\xbd'<\x90\xde\xea\x16\xa3\xe7\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xb2-\xad\xd7\xe1\xe0R2\xa927\xba\xed\x98\xe0\u07d2\xb1\x86\x9e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb24\x03_uDF<\xe1\xe2+\xc5S\x06F\x84\xc5\x13\xcdQ\x89\r\x89\xfa=\u010d\xcf\x00\x00\u07d4\xb2G\u03dcr\xecH*\xf3\xea\xa7Ye\x8fy=g\nW\f\x891p\x8a\xe0\x04T@\x00\x00\u07d4\xb2ghA\xee\x9f-1\xc1r\xe8#\x03\xb0\xfe\x9b\xbf\x9f\x1e\t\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb2y\xc7\xd3U\u0088\x03\x92\xaa\u046a!\xee\x86|;5\a\u07c9D[\xe3\xf2\uf1d4\x00\x00\u07d4\xb2|\x1a$ L\x1e\x11\x8du\x14\x9d\xd1\t1\x1e\a\xc0s\xab\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb2\x81\x81\xa4X\xa4@\xf1\u01bb\x1d\xe8@\x02\x81\xa3\x14\x8fL5\x89\x14b\fW\xdd\xda\xe0\x00\x00\xe0\x94\xb2\x82E\x03|\xb1\x92\xf7W\x85\u02c6\xcb\xfe|\x93\r\xa2X\xb0\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xb2\x87\xf7\xf8\xd8\u00c7,\x1bXk\xcd}\n\xed\xbf~s'2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb2\x8b\xb3\x9f4fQ|\xd4o\x97\x9c\xf5\x96S\xee}\x8f\x15.\x89\x18e\x01'\xcc=\xc8\x00\x00\u07d4\xb2\x8d\xbf\xc6I\x98\x94\xf7:q\xfa\xa0\n\xbe\x0fK\xc9\u045f*\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xb2\x96\x8f}5\xf2\b\x87\x161\xc6h{?=\xae\xab\xc6al\x89\bu\xc4\u007f(\x9fv\x00\x00\u07d4\xb2\x9f[|\x190\xd9\xf9z\x11^\x06pf\xf0\xb5M\xb4K;\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb2\xa1D\xb1\xeag\xb9Q\x0f\"g\xf9\xda9\xd3\xf9=\xe2fB\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xa2\xc2\x11\x16\x12\xfb\x8b\xbb\x8e}\xd97\x8dg\xf1\xa3\x84\xf0P\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb2\xa4\x98\xf0;\xd7\x17\x8b\u0627\x89\xa0\x0fR7\xafy\xa3\xe3\xf8\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\xb2\xaa/\x1f\x8e\x93\xe7\x97\x13\xd9,\xea\x9f\xfc\xe9\xa4\n\xf9\xc8-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xb5\x16\xfd\u045e\u007f8d\xb6\xd2\xcf\x1b%*AV\xf1\xb0;\x89\x02\xe9\x83\xc7a\x15\xfc\x00\x00\u07d4\xb2\xb7\u0374\xffKa\u0577\xce\v\"p\xbb\xb5&\x97C\xec\x04\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xbd\xbe\u07d5\x90\x84v\xd7\x14\x8a7\f\u0193t6(\x05\u007f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb2\xbf\xaaX\xb5\x19l\\\xb7\xf8\x9d\xe1_G\x9d\x188\xdeq=\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4\xb2\xc5>\xfa3\xfeJ:\x1a\x80 \\s\xec;\x1d\xbc\xad\x06\x02\x89h\x01\u06b3Y\x18\x93\x80\x00\xe0\x94\xb2\xd06\x05\x15\xf1}\xab\xa9\x0f\u02ec\x82\x05\xd5i\xb9\x15\u05ac\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xb2\xd1\xe9\x9a\xf9\x121\x85\x8epe\xdd\x19\x183\r\xc4\xc7G\u054a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\xb2\u066b\x96d\xbc\xf6\xdf <4o\u0192\xfd\x9c\xba\xb9 ^\x89\x17\xbex\x97`e\x18\x00\x00\u07d4\xb2\u0777\x86\xd3yN'\x01\x87\xd0E\x1a\xd6\u0237\x9e\x0e\x87E\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\xe0\x85\xfd\xdd\x14h\xba\aA['NsN\x11#\u007f\xb2\xa9\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xb2\xe9\xd7k\xf5\x0f\xc3k\xf7\u04d4Kc\xe9\u0288\x9bi\x99h\x89\x902\xeab\xb7K\x10\x00\x00\xe0\x94\xb2\xf9\xc9r\xc1\xe9swU\xb3\xff\x1b0\x88s\x83\x969[&\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xb2\xfc\x84\xa3\xe5\nP\xaf\x02\xf9M\xa08>\u055fq\xff\x01\u05ca\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xb3\x05\v\xef\xf9\xde3\xc8\x0e\x1f\xa1R%\xe2\x8f,A:\xe3\x13\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\xb3\x11\x96qJH\xdf\xf7&\xea\x943\xcd)\x12\xf1\xa4\x14\xb3\xb3\x89\x91Hx\xa8\xc0^\xe0\x00\x00\xe0\x94\xb3\x14[tPm\x1a\x8d\x04|\xdc\xdcU9*{SPy\x9a\x8a\x1bb)t\x1c\r=]\x80\x00\u07d4\xb3 \x83H6\xd1\xdb\xfd\xa9\xe7\xa3\x18M\x1a\xd1\xfdC \xcc\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3#\u073f.\xdd\xc58.\u4efb \x1c\xa3\x93\x1b\xe8\xb48\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb3$\x00\xfd\x13\xc5P\t\x17\xcb\x03{)\xfe\"\xe7\xd5\"\x8f-\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xb3%gL\x01\xe3\xf7)\rR&3\x9f\xbe\xacg\xd2!'\x9f\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xb3(%\xd5\xf3\xdb$\x9e\xf4\xe8\\\xc4\xf31S\x95\x89v\u8f09\x1b-\xf9\xd2\x19\xf5y\x80\x00\u07d4\xb3*\xf3\xd3\xe8\xd0u4I&To.2\x88{\xf9;\x16\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb3/\x1c&\x89\xa5\xcey\xf1\xbc\x97\v1XO\x1b\xcf\"\x83\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb3<\x03#\xfb\xf9\xc2l\x1d\x8a\xc4N\xf7C\x91\u0400F\x96\u0689\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb3O\x04\xb8\xdbe\xbb\xa9\xc2n\xfcL\xe6\xef\xc5\x04\x81\xf3\xd6]\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb3U}9\xb5A\x1b\x84D__T\xf3\x8fb\xd2qM\x00\x87\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xb3X\xe9|p\xb6\x05\xb1\xd7\xd7)\u07f6@\xb4<^\xaf\xd1\xe7\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xb3^\x8a\x1c\r\xac~\x0ef\u06ecsjY*\xbdD\x01%a\x88\xcf\xceU\xaa\x12\xb3\x00\x00\xe0\x94\xb3fx\x94\xb7\x86<\x06\x8a\xd3D\x87?\xcf\xf4\xb5g\x1e\x06\x89\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb3qw1\xda\xd6Q2\xday-\x87`0\xe4j\xc2'\xbb\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3s\x1b\x04l\x8a\u0195\xa1'\xfdy\u0425\xd5\xfaj\xe6\xd1.\x89lO\xd1\xee$nx\x00\x00\u07d4\xb3|+\x9fPc{\xec\xe0\u0295\x92\b\xae\xfe\xe6F;\xa7 \x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb3\x88\xb5\xdf\xec\xd2\xc5\u4d56W|d%V\xdb\xfe'xU\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb3\x8cNS{]\xf90\xd6Zt\xd0C\x83\x1dkH[\xbd\xe4\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xb3\x919Wa\x94\xa0\x86a\x95\x15\x1f3\xf2\x14\n\xd1\u0306\u03ca\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xb3\x9fL\x00\xb2c\f\xab}\xb7)^\xf4=G\xd5\x01\xe1\u007f\u05c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb3\xa6K\x11vrOT\t\xe1AJ5#f\x1b\xae\xe7KJ\x89\x01ch\xffO\xf9\xc1\x00\x00\u07d4\xb3\xa6\xbdA\xf9\xd9\xc3 \x1e\x05\v\x87\x19\x8f\xbd\xa3\x994\"\x10\x89\xc4a\xe1\xdd\x10)\xb5\x80\x00\u07d4\xb3\xa8\xc2\xcb}5\x8eW9\x94\x1d\x94[\xa9\x04Z\x02:\x8b\xbb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3\xaeT\xfb\xa0\x9d>\xe1\u05bd\xd1\xe9W\x929\x19\x02L5\xfa\x89\x03\x8d,\xeee\xb2*\x80\x00\u07d4\xb3\xb7\xf4\x93\xb4J,\x8d\x80\xecx\xb1\xcd\xc7Ze+s\xb0l\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb3\xc2(s\x1d\x18m-\xed[_\xbe\x00Lfl\x8eF\x9b\x86\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4\xb3\xc2``\x9b\x9d\xf4\t^l]\xff9\x8e\xeb^-\xf4\x99\x85\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xb3\xc6[\x84Z\xbal\xd8\x16\xfb\xaa\xe9\x83\xe0\xe4l\x82\xaa\x86\"\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3\xc9H\x11\xe7\x17[\x14\x8b(\x1c\x1a\x84[\xfc\x9b\xb6\xfb\xc1\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb3\xe2\x0e\xb4\xde\x18\xbd\x06\x02!h\x98\x94\xbe\u5bb2SQ\xee\x89\x03\xfc\x80\xcc\xe5\x16Y\x80\x00\u07d4\xb3\xe3\xc49\x06\x98\x80\x15f\x00\u0089.D\x8dA6\xc9-\x9b\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\xe0\x94\xb3\xf8*\x87\xe5\x9a9\xd0\u0480\x8f\aQ\xebr\xc22\x9c\xdc\u014a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb3\xfc\x1dh\x81\xab\xfc\xb8\xbe\xcc\v\xb0!\xb8\xb7;r3\u0751\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xb4\x05\x94\xc4\xf3fN\xf8I\u0326\"{\x8a%\xaai\t%\xee\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb4\x1e\xaf]Q\xa5\xba\x1b\xa3\x9b\xb4\x18\u06f5O\xabu\x0e\xfb\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4$\u058d\x9d\r\x00\xce\xc1\x93\x8c\x85N\x15\xff\xb8\x80\xba\x01p\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb4%bs\x96+\xf61\xd0\x14U\\\xc1\xda\r\xcc1akI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb40g\xfep\u0675Ys\xbaX\xdcd\xdd\u007f1\x1eUBY\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb46W\xa5\x0e\xec\xbc0w\xe0\x05\xd8\xf8\xd9O7xv\xba\u0509\x01\xec\x1b:\x1f\xf7Z\x00\x00\u07d4\xb4<'\xf7\xa0\xa1\"\bK\x98\xf4\x83\x92%A\u0203l\xee,\x89&\u009eG\u0104L\x00\x00\xe0\x94\xb4A5v\x86\x9c\b\xf9Q*\xd3\x11\xfe\x92Y\x88\xa5-4\x14\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb4F\x05U$q\xa6\xee\xe4\u06abq\xff;\xb4\x13&\xd4s\xe0\x89-~=Q\xbaS\xd0\x00\x00\u07d4\xb4GW\x1d\xac\xbb>\u02f6\xd1\xcf\v\f\x8f88\xe5#$\xe2\x89\x01\xa3\x18f\u007f\xb4\x05\x80\x00\u07d4\xb4G\x83\xc8\xe5{H\a\x93\xcb\u059aE\xd9\f{O\fH\xac\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb4H\x15\xa0\xf2\x8eV\x9d\x0e\x92\x1aJ\u078f\xb2d%&Iz\x89\x03\x027\x9b\xf2\xca.\x00\x00\u07d4\xb4Im\xdb'y\x9a\"$W\xd79y\x11g(\u8844[\x89\x8d\x81\x9e\xa6_\xa6/\x80\x00\xe0\x94\xb4RL\x95\xa7\x86\x0e!\x84\x02\x96\xa6\x16$@\x19B\x1cJ\xba\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb4\\\xca\r6\x82fbh<\xf7\u0432\xfd\xach\u007f\x02\xd0\u010965\u026d\xc5\u07a0\x00\x00\u0794\xb4d@\u01d7\xa5V\xe0L}\x91\x04f\x04\x91\xf9k\xb0v\xbf\x88\xce\xc7o\x0eqR\x00\x00\u07d4\xb4j\u0386^,P\xeaF\x98\xd2\x16\xabE]\xffZ\x11\xcdr\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4m\x11\x82\xe5\xaa\xca\xff\r&\xb2\xfc\xf7/<\x9f\xfb\xcd\xd9}\x89\xaa*`<\xdd\u007f,\x00\x00\u07d4\xb4\x89!\xc9h}U\x10tE\x84\x93n\x88\x86\xbd\xbf-\xf6\x9b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\x98\xbb\x0fR\x00\x05\xb6!jD%\xb7Z\xa9\xad\xc5-b+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xb4\xb1\x1d\x10\x9f`\x8f\xa8\xed\xd3\xfe\xa9\xf8\xc3\x15d\x9a\xeb=\x11\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb4\xb1K\xf4TU\u042b\b\x035\x8bu$\xa7+\xe1\xa2\x04[\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb4\xb1\x85\xd9C\xee+Xc\x1e3\xdf\xf5\xafhT\xc1y\x93\xac\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\xbf$\u02c3hk\xc4i\x86\x9f\xef\xb0D\xb9\tqi\x93\xe2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb4\xc2\x00@\xcc\u0661\xa3(=\xa4\u0522\xf3e\x82\bC\xd7\xe2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\xc8\x17\x0f{*\xb56\xd1\u0662[\xdd :\xe1(\x8d\xc3\u0549\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb4\xd8/.i\x94?}\xe0\xf5\xf7t8y@o\xac.\x9c\xec\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xb4\xddF\f\xd0\x16rZd\xb2.\xa4\xf8\xe0n\x06gN\x03>\x8a\x01#\x1b\xb8t\x85G\xa8\x00\x00\u07d4\xb4\xddT\x99\xda\xeb%\a\xfb-\xe1\"\x97s\x1dLr\xb1k\xb0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb5\x04l\xb3\xdc\x1d\xed\xbd6E\x14\xa2\x84\x8eD\xc1\xdeN\xd1G\x8a\x03{}\x9b\xb8 @^\x00\x00\xe0\x94\xb5\b\xf9\x87\xb2\xde4\xaeL\xf1\x93\u0785\xbf\xf6\x13\x89b\x1f\x88\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xb5\tU\xaan4\x15q\x98f\b\xbd\u0211\xc2\x13\x9fT\f\u07c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb5\f\x14\x9a\x19\x06\xfa\xd2xo\xfb\x13Z\xabP\x177\xe9\xe5o\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\xb5\f\x9fW\x89\xaeD\xe2\xdc\xe0\x17\xc7\x14\xca\xf0\f\x83\x00\x84\u0089\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xb5\x14\x88,\x97\x9b\xb6B\xa8\r\u04c7T\u0578\xc8)m\x9a\a\x893\xc5I\x901r\f\x00\x00\u07d4\xb5\x1d\u0734\xddN\x8a\xe6\xbe3m\xd9eIq\xd9\xfe\xc8kA\x89\x16\xd4d\xf8=\u2500\x00\u07d4\xb5\x1eU\x8e\xb5Q/\xbc\xfa\x81\xf8\u043d\x93\x8cy\xeb\xb5$+\x89&\u009eG\u0104L\x00\x00\u07d4\xb5#\xff\xf9t\x98q\xb3S\x88C\x887\xf7\xe6\xe0\u07a9\xcbk\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb5-\xfbE\xde]t\xe3\xdf \x832\xbcW\x1c\x80\x9b\x8d\xcf2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xb55\xf8\u06c7\x9f\xc6\u007f\xecX\x82J\\\xbenT\x98\xab\xa6\x92\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xb57\xd3jp\xee\xb8\xd3\xe5\xc8\r\xe8\x15\"\\\x11X\u02d2\u0109QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xb5;\xcb\x17L%\x184\x8b\x81\x8a\xec\xe0 6E\x96Fk\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5I>\xf1srDE\xcf4\\\x03]'\x9b\xa7Y\xf2\x8dQ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb5S\xd2]kT!\xe8\x1c*\xd0^\v\x8b\xa7Q\xf8\xf0\x10\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5Tt\xbaX\xf0\xf2\xf4\x0el\xba\xbe\xd4\xea\x17n\x01\x1f\xca\u0589j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb5U\xd0\x0f\x91\x90\xcc6w\xae\xf3\x14\xac\xd7?\xdc99\x92Y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5W\xab\x949\xefP\xd27\xb5S\xf0%\b6JFj\\\x03\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5jx\x00(\x03\x9c\x81\xca\xf3{gu\xc6 \u7195Gd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5j\u04ae\xc6\xc8\xc3\xf1\x9e\x15\x15\xbb\xb7\u0751(RV\xb69\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5t\x13\x06\n\xf3\xf1N\xb4y\x06_\x1e\x9d\x19\xb3uz\xe8\u0309\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xb5uI\xbf\xbc\x9b\xdd\x18\xf76\xb2&P\xe4\x8as`\x1f\xa6\\\x89\x18-~L\xfd\xa08\x00\x00\xe0\x94\xb5w\xb6\xbe\xfa\x05N\x9c\x04\x04a\x85P\x94\xb0\x02\xd7\xf5{\u05ca\x18#\xf3\xcfb\x1d#@\x00\x00\u07d4\xb5{\x04\xfa#\xd1 ?\xae\x06\x1e\xacEB\xcb`\xf3\xa5v7\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xb5\x87\f\xe3B\xd43C36s\x03\x8bGd\xa4n\x92_>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5\x87\xb4J,\xa7\x9eK\xc1\u074b\xfd\xd4: qP\xf2\xe7\xe0\x89\",\x8e\xb3\xfff@\x00\x00\u07d4\xb5\x89gm\x15\xa0DH4B0\xd4\xff'\xc9^\xdf\x12,I\x8965\u026d\xc5\u07a0\x00\x00\u0794\xb5\x8bR\x86^\xa5]\x806\xf2\xfa\xb2`\x98\xb3R\u0283~\x18\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xb5\x90k\n\u9881X\xe8\xacU\x0e9\xda\bn\xe3\x15v#\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5\xa4g\x96\x85\xfa\x14\x19l.\x920\xc8\xc4\xe3;\xff\xbc\x10\xe2\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xb5\xa5\x89\u075f@q\u06f6\xfb\xa8\x9b?]]\xae}\x96\xc1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5\xa6\x06\xf4\xdd\u02f9G\x1e\xc6\u007fe\x8c\xaf+\x00\xees\x02^\x89\xeaun\xa9*\xfct\x00\x00\u07d4\xb5\xadQW\u0769!\xe6\xba\xfa\u0350\x86\xaes\xae\x1fa\x1d?\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5\xad\xd1\u701f}\x03\x06\x9b\xfe\x88;\n\x93\"\x10\xbe\x87\x12\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5\xba)\x91|x\xa1\xd9\xe5\xc5\xc7\x13fl\x1eA\x1d\u007fi:\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb5\xc8\x16\xa8(<\xa4\xdfh\xa1\xa7=c\xbd\x80&\x04\x88\xdf\b\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5\xca\xc5\xed\x03G}9\v\xb2g\xd4\xeb\xd4a\x01\xfb\xc2\xc3\u0689\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xb5\u037cA\x15@oR\u5a85\xd0\xfe\xa1p\u0497\x9c\u01fa\x89Hz\x9a0E9D\x00\x00\u0794\xb5\u0653M{)+\xcf`;(\x80t\x1e\xb7`(\x83\x83\xa0\x88\xe7\xc2Q\x85\x05\x06\x00\x00\u07d4\xb5\xddP\xa1]\xa3Ih\x89\nS\xb4\xf1?\xe1\xaf\b\x1b\xaa\xaa\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb5\xfa\x81\x84\xe4>\xd3\u0e2b\x91!da\xb3R\x8d\x84\xfd\t\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xb5\xfb~\xa2\xdd\xc1Y\x8bfz\x9dW\xdd9\xe8Z8\xf3]V\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb6\x00B\x97R\xf3\x99\xc8\r\a4tK\xae\n\x02.\xcag\u0189\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb6\x00\xfe\xabJ\xa9lSu\x04\xd9`W\"1Ai,\x19:\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb6\x04|\u07d3-\xb3\xe4\x04_Iv\x12#AS~\u0556\x1e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb6\x15\xe9@\x14>\xb5\u007f\x87X\x93\xbc\x98\xa6\x1b=a\x8c\x1e\x8c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb6\x1c4\xfc\xac\xdap\x1aZ\xa8p$Y\u07b0\u4b83\x8d\xf8\x8a\aiZ\x92\xc2\ro\xe0\x00\x00\xe0\x94\xb60d\xbd3U\xe6\xe0~-7p$\x12Z3wlJ\xfa\x8a\b7Z*\xbc\xca$@\x00\x00\u07d4\xb65\xa4\xbcq\xfb(\xfd\xd5\xd2\xc3\"\x98:V\u0084Bni\x89\t79SM(h\x00\x00\u07d4\xb6F\u07d8\xb4\x94BtkaR\\\x81\xa3\xb0K\xa3\x10bP\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb6YA\xd4LP\xd2Ffg\r6Gf\xe9\x91\xc0.\x11\u0089 \x86\xac5\x10R`\x00\x00\xe0\x94\xb6[\u05c0\xc7CA\x15\x16 'VR#\xf4NT\x98\xff\x8c\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\xb6d\x11\xe3\xa0-\xed\xb7&\xfay\x10}\xc9\v\xc1\xca\xe6MH\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb6fu\x14.1\x11\xa1\xc2\xea\x1e\xb2A\x9c\xfaB\xaa\xf7\xa24\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xb6o\x92\x12K^c\x03XY\xe3\x90b\x88i\xdb\u07a9H^\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xb6rsJ\xfc\xc2$\xe2\xe6\t\xfcQ\xd4\xf0Ys'D\xc9H\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\xe0\x94\xb6w\x1b\v\xf3B\u007f\x9a\xe7\xa9>|.a\xeec\x94\x1f\xdb\b\x8a\x03\xfb&i)T\xbf\xc0\x00\x00\u07d4\xb6z\x80\xf1p\x19}\x96\xcd\xccJ\xb6\u02e6'\xb4\xaf\xa6\xe1,\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xb6\x88\x99\xe7a\rL\x93\xa255\xbc\xc4H\x94[\xa1fo\x1c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb6\xa8)3\xc9\xea\u06bd\x98\x1e]m`\xa6\x81\x8f\xf8\x06\xe3k\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xb6\xaa\u02cc\xb3\v\xab*\xe4\xa2BF&\xe6\xe1+\x02\xd0F\x05\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb6\xb3J&?\x10\xc3\xd2\xec\xeb\n\xccU\x9a{*\xb8\\\xe5e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb6\xbf\xe1\xc3\xef\x94\xe1\x84o\xb9\xe3\xac\xfe\x9bP\xc3\xe9\x06\x923\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xb6\xcdt2\xd5\x16\x1b\xe7\x97h\xadE\xde>Dz\a\x98 c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb6\xceM\xc5`\xfcs\xdci\xfbzb\xe3\x88\xdb~r\xeavO\x894]\xf1i\xe9\xa3X\x00\x00\u07d4\xb6\xde\u03c2\x96\x98\x19\xba\x02\xde)\xb9\xb5\x93\xf2\x1bd\xee\xda\x0f\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\xe0\x94\xb6\xe6\xc3\"+ko\x9b\xe2\x87]*\x89\xf1'\xfbd\x10\x0f\xe2\x8a\x01\xb2\x1dS#\xcc0 \x00\x00\u07d4\xb6\xe8\xaf\xd9=\xfa\x9a\xf2\u007f9\xb4\xdf\x06\ag\x10\xbe\xe3\u07eb\x89\x01Z\xf1\u05cbX\xc4\x00\x00\xe0\x94\xb6\xf7\x8d\xa4\xf4\xd0A\xb3\xbc\x14\xbc[\xa5\x19\xa5\xba\f2\xf1(\x8a$}\xd3,?\xe1\x95\x04\x80\x00\xe0\x94\xb6\xfb9xbP\b\x14&\xa3B\xc7\rG\xeeR\x1e[\xc5c\x8a\x03-&\xd1.\x98\v`\x00\x00\u07d4\xb7\r\xba\x93\x91h+J6Nw\xfe\x99%c\x01\xa6\xc0\xbf\x1f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\x16#\xf3Q\a\xcft1\xa8?\xb3\xd2\x04\xb2\x9e\u0c67\xf4\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xb7\x1a\x13\xba\x8e\x95\x16{\x803\x1bR\u059e7\x05O\xe7\xa8&\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\x1bb\xf4\xb4H\xc0+\x12\x01\xcb^9J\xe6'\xb0\xa5`\xee\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb7\" \xad\xe3d\xd06\x9f--\xa7\x83\xcaGM{\x9b4\u0389\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\xe0\x94\xb7#\r\x1d\x1f\xf2\xac\xa3f\x969\x14\xa7\x9d\xf9\xf7\xc5\xea,\x98\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\xb7$\n\U000af433<\b\xae\x97d\x10>5\xdc\xe3c\x84(\x8a\x01\xca\xdd/\xe9hnc\x80\x00\u07d4\xb7'\xa9\xfc\x82\xe1\xcf\xfc\\\x17_\xa1HZ\x9b\xef\xa2\u037d\u04496'\xe8\xf7\x127<\x00\x00\u07d4\xb7,*\x01\x1c\r\xf5\x0f\xbbn(\xb2\n\xe1\xaa\xd2\x17\x88g\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb78-7\xdb\x03\x98\xacrA\f\xf9\x81=\xe9\xf8\xe1\uc36d\x8966\xc2^f\xec\xe7\x00\x00\u07d4\xb7;O\xf9\x9e\xb8\x8f\u061b\vmW\xa9\xbc3\x8e\x88o\xa0j\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xb7=jwU\x9c\x86\xcfet$)\x039K\xac\xf9n5p\x89\x04\xf1\xa7|\xcd;\xa0\x00\x00\u07d4\xb7Cr\xdb\xfa\x18\x1d\xc9$/9\xbf\x1d71\xdf\xfe+\xda\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7G\x9d\xabP\"\xc4\xd5\u06ea\xf8\xde\x17\x1bN\x95\x1d\u0464W\x89\x04V9\x18$O@\x00\x00\u07d4\xb7I\xb5N\x04\u0571\x9b\xdc\xed\xfb\x84\xdaw\x01\xabG\x8c'\xae\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xb7N\xd2f`\x01\xc1c3\xcfz\xf5\x9eJ=H`6;\x9c\x89\n~\xbd^Cc\xa0\x00\x00\u07d4\xb7QI\xe1\x85\xf6\xe3\x92pWs\x90s\xa1\x82*\xe1\xcf\r\xf2\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xb7S\xa7_\x9e\xd1\v!d:\n=\xc0Qz\xc9k\x1a@h\x89\x15\xc8\x18[,\x1f\xf4\x00\x00\xe0\x94\xb7V\xadR\xf3\xbft\xa7\xd2LgG\x1e\b\x87Ci6PL\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb7Wn\x9d1M\xf4\x1e\xc5Pd\x94):\xfb\x1b\xd5\xd3\xf6]\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb7X\x89o\x1b\xaa\x86O\x17\xeb\xed\x16\xd9S\x88o\xeeh\xaa\xe6\x8965\u026d\xc5\u07a0\x00\x00\u0794\xb7h\xb5#N\xba:\x99h\xb3Mm\xdbH\x1c\x84\x19\xb3e]\x88\xcf\xceU\xaa\x12\xb3\x00\x00\u07d4\xb7\x82\xbf\xd1\xe2\xdep\xf4gdo\x9b\xc0\x9e\xa5\xb1\xfc\xf4P\xaf\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xb7\xa2\xc1\x03r\x8bs\x05\xb5\xaen\x96\x1c\x94\xee\x99\xc9\xfe\x8e+\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xb7\xa3\x1a|8\xf3\xdb\t2.\xae\x11\xd2'!A\xea\"\x99\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7\xa6y\x1c\x16\xebN!b\xf1Ke7\xa0+=c\xbf\xc6\x02\x89*Rc\x91\xac\x93v\x00\x00\u07d4\xb7\xa7\xf7|4\x8f\x92\xa9\xf1\x10\fk\xd8)\xa8\xacm\u007f\u03d1\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xb7\xc0w\x94ft\xba\x93A\xfbLtz]P\xf5\xd2\xdad\x15\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb7\xc0\xd0\xcc\vM4-@b\xba\xc6$\xcc\xc3\xc7\f\xc6\xda?\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb7\xc9\xf1+\x03\x8esCm\x17\xe1\xc1/\xfe\x1a\xec\u0373\xf5\x8c\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\xb7\xcck\x1a\xcc2\u0632\x95\xdfh\xed\x9d^`\xb8\xf6L\xb6{\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xb7\xcehK\t\xab\xdaS8\x9a\x87Si\xf7\x19X\xae\xac;\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7\xd1.\x84\xa2\xe4\u01264Z\xf1\xdd\x1d\xa9\xf2PJ*\x99n\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\xd2R\xee\x94\x02\xb0\xee\xf1D)_\x0ei\xf0\xdbXl\bq\x89#\xc7W\a+\x8d\xd0\x00\x00\xe0\x94\xb7\u0541\xfe\n\xf1\xec8?;\xce\x00\xaf\x91\x99\xf3\xcf_\xe0\xcc\xe2\x8c\xd1J\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\xb8R\x18\xf3B\xf8\x01.\u069f'Nc\xce!R\xb2\xdc\xfd\xab\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb8UP\x10wn<\\\xb3\x11\xa5\xad\xee\xfe\x9e\x92\xbb\x9ad\xb9\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xb8_&\xdd\x0er\xd9\u009e\xba\xf6\x97\xa8\xafwG,+X\xb5\x8a\x02\x85\x19\xac\xc7\x19\fp\x00\x00\u07d4\xb8_\xf0>{_\xc4\"\x98\x1f\xae^\x99A\xda\xcb\u06bau\x84\x89Hz\x9a0E9D\x00\x00\xe0\x94\xb8f\a\x02\x1bb\xd3@\xcf&R\xf3\xf9_\xd2\xdcgi\x8b\u07ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb8}\xe1\xbc\u0492i\xd5!\xb8v\x1c\u00dc\xfbC\x19\xd2\xea\u054965\u026d\xc5\u07a0\x00\x00\u07d4\xb8\u007fSv\xc2\xde\vl\xc3\xc1y\xc0`\x87\xaaG=kFt\x89Hz\x9a0E9D\x00\x00\u07d4\xb8\x84\xad\u060d\x83\xdcVJ\xb8\xe0\xe0,\xbd\xb69\x19\xae\xa8D\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\x8a7\xc2\u007fx\xa6\x17\xd5\xc0\x91\xb7\u0577:7a\xe6_*\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\x94x\"\u056c\u79ad\x83&\xe9T\x96\"\x1e\v\xe6\xb7=\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb8\x9c\x03n\xd7\u0112\x87\x99!\xbeA\xe1\f\xa1i\x81\x98\xa7L\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xb8\x9fF2\xdfY\t\xe5\x8b*\x99d\xf7O\xeb\x9a;\x01\xe0\u014a\x04\x88u\xbc\xc6\xe7\xcb\xeb\x80\x00\u07d4\xb8\xa7\x9c\x84\x94^G\xa9\xc3C\x86\x83\u05b5\x84,\xffv\x84\xb1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\xa9y5'Y\xba\t\xe3Z\xa5\x93]\xf1u\xbf\xf6x\xa1\b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb8\xab9\x80[\xd8!\x18Ol\xbd=$s4{\x12\xbf\x17\\\x89\x06hZ\xc1\xbf\xe3,\x00\x00\xe0\x94\xb8\xac\x11}\x9f\r\xba\x80\x90\x14E\x82:\x92\x11\x03\xa51o\x85Zew\x9d\x1b\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4\xb9\xe9\f\x11\x92\xb3\xd5\xd3\xe3\xab\a\x00\xf1\xbfe_]\xd44z\x89\x1b\x19\xe5\vD\x97|\x00\x00\u07d4\xb9\xfd83\xe8\x8e|\xf1\xfa\x98y\xbd\xf5Z\xf4\xb9\x9c\xd5\xce?\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xba\x02I\xe0\x1d\x94[\xef\x93\xee^\xc6\x19%\xe0<\\\xa5\t\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xba\x0f9\x02;\xdb)\xeb\x18b\xa9\xf9\x05\x9c\xab]0nf/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\x10\xf2vB\x90\xf8uCCr\xf7\x9d\xbfq8\x01\u02ac\x01\x893\xc5I\x901r\f\x00\x00\u07d4\xba\x151\xfb\x9ey\x18\x96\xbc\xf3\xa8\x05X\xa3Y\xf6\xe7\xc1D\xbd\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xba\x17m\xbe2I\xe3E\xcdO\xa9g\xc0\xed\x13\xb2LG\u5189\x15\xae\xf9\xf1\xc3\x1c\u007f\x00\x00\xe0\x94\xba\x1f\x0e\x03\u02da\xa0!\xf4\xdc\xeb\xfa\x94\xe5\u0209\xc9\u01fc\x9e\x8a\x06\u0450\xc4u\x16\x9a \x00\x00\u07d4\xba\x1f\xca\xf2#\x93~\xf8\x9e\x85gU\x03\xbd\xb7\xcaj\x92\x8bx\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\xe0\x94\xba$\xfcCgS\xa79\xdb,\x8d@\xe6\xd4\xd0LR\x8e\x86\xfa\x8a\x02\xc0\xbb=\xd3\fN \x00\x00\u07d4\xbaB\xf9\xaa\xceL\x18E\x04\xab\xf5BWb\xac\xa2oq\xfb\u0709\x02\a\a}\u0627\x9c\x00\x00\u07d4\xbaF\x9a\xa5\u00c6\xb1\x92\x95\u0521\xb5G;T\x03S9\f\x85\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbad@\xae\xb3s{\x8e\xf0\xf1\xaf\x9b\f\x15\xf4\xc2\x14\xff\xc7\u03c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbam1\xb9\xa2a\xd6@\xb5\u07a5\x1e\xf2\x16,1\t\xf1\uba0a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xbap\xe8\xb4u\x9c\f<\x82\xcc\x00\xacN\x9a\x94\xdd[\xaf\xb2\xb8\x890C\xfa3\xc4\x12\xd7\x00\x00\u07d4\xba\x8ac\xf3\xf4\r\u4a03\x88\xbcP!/\xea\x8e\x06O\xbb\x86\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\x8eF\u059d.#C\xd8l`\xd8,\xf4, A\xa0\xc1\u0089\x05k\xc7^-c\x10\x00\x00\u07d4\xba\xa4\xb6L+\x15\xb7\x9f_ BF\xfdp\xbc\xbd\x86\xe4\xa9*\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xba\u0212,J\xcc},\xb6\xfdY\xa1N\xb4\\\xf3\xe7\x02!K\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xba\xd25\xd5\b]\u01f0h\xa6|A&w\xb0>\x186\x88L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\xd4B^\x17\x1c>r\x97^\xb4j\xc0\xa0\x15\xdb1Z]\x8f\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xba\xdc*\xef\x9fYQ\xa8\u05cak5\xc3\u0433\xa4\xe6\xe2\xe79\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xba\xdeCY\x9e\x02\xf8OL0\x14W\x1c\x97k\x13\xa3le\xab\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xba\xe9\xb8/r\x99c\x14\be\x9d\xd7N\x89\x1c\xb8\xf3\x86\x0f\xe5\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xbb\x03f\xa7\u03fd4E\xa7\r\xb7\xfeZ\xe3H\x85uO\xd4h\x8a\x01M\xef,B\xeb\xd6@\x00\x00\u07d4\xbb\aj\xac\x92 \x80i\xea1\x8a1\xff\x8e\xeb\x14\xb7\xe9\x96\xe3\x89\b\x13\xcaV\x90m4\x00\x00\u07d4\xbb\bW\xf1\xc9\x11\xb2K\x86\u0227\x06\x81G?\u6aa1\xcc\xe2\x89\x05k\xc7^-c\x10\x00\x00\u0794\xbb\x19\xbf\x91\u02edt\xcc\xeb_\x81\x1d\xb2~A\x1b\xc2\xea\x06V\x88\xf4?\xc2\xc0N\xe0\x00\x00\xe0\x94\xbb'\u01a7\xf9\x10uGZ\xb2)a\x90@\xf8\x04\xc8\xeczj\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xbb7\x1cr\xc9\xf01l\xea+\xd9\xc6\xfb\xb4\a\x9ewT)\xef\x89_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94\xbb;\x01\v\x18\xe6\xe2\xbe\x115\x87\x10&\xb7\xba\x15\xea\x0f\xde$\x8a\x02 |\x800\x9bwp\x00\x00\xe0\x94\xbb;\x90\x05\xf4o\xd2\xca;0\x16%\x99\x92\x8cw\xd9\xf6\xb6\x01\x8a\x01\xb1\xae\u007f+\x1b\xf7\xdb\x00\x00\u07d4\xbb?\xc0\xa2\x9c\x03Mq\b\x12\xdc\xc7u\xc8\u02b9\u048diu\x899\xd4\xe8D\xd1\xcf_\x00\x00\u07d4\xbbH\xea\xf5\x16\xce-\xec>A\xfe\xb4\xc6y\xe4\x95vA\x16O\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\xbbKJKT\x80p\xffAC,\x9e\b\xa0\xcao\xa7\xbc\x9fv\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\xbbV\xa4\x04r<\xff \xd0hT\x88\xb0Z\x02\xcd\xc3Z\xac\xaa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbba\x8e%\"\x1a\u0667@\xb2\x99\xed\x14\x06\xbc94\xb0\xb1m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbba\xa0K\xff\xd5|\x10G\rE\u00d1\x03\xf6FP4v\x16\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbbh#\xa1\xbd\x81\x9f\x13QU8&J-\xe0R\xb4D\"\b\x89\x01ch\xffO\xf9\xc1\x00\x00\u07d4\xbbl(J\xac\x8ai\xb7\\\u0770\x0f(\xe1EX;V\xbe\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xbbu\xcbPQ\xa0\xb0\x94KFs\xcau*\x97\x03\u007f|\x8c\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbb\x99;\x96\xee\x92Z\xda}\x99\u05c6W=?\x89\x18\f\u3a89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xa3\u0180\x04$\x8eH\x95s\xab\xb2t6w\x06k$\u0227\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xa4\xfa\xc3\xc4 9\xd8(\xe7B\xcd\xe0\xef\xff\xe7t\x94\x1b9\x89lj\u04c2\xd4\xfba\x00\x00\u07d4\xbb\xa8\xab\"\xd2\xfe\xdb\xcf\xc6?hL\b\xaf\xdf\x1c\x17P\x90\xb5\x89\x05_)\xf3~N;\x80\x00\u07d4\xbb\xa9v\xf1\xa1!_u\x12\x87\x18\x92\xd4_pH\xac\xd3V\u0209lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbb\xab\x00\v\x04\b\xed\x01Z7\xc0GG\xbcF\x1a\xb1N\x15\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xbb\xab\xf6d;\xebK\xd0\x1c\x12\v\xd0Y\x8a\t\x87\xd8)g\u0449\xb52\x81x\xad\x0f*\x00\x00\u07d4\xbb\xb4\xee\x1d\x82\xf2\xe1VD,\xc938\xa2\xfc(o\xa2\x88d\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\xbb\xb5\xa0\xf4\x80,\x86H\x00\x9e\x8ai\x98\xaf5,\u0787TO\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4\xbb\xb6C\xd2\x18{6J\xfc\x10\xa6\xfd6\x8d}U\xf5\r\x1a<\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbb\xb8\xff\xe4?\x98\u078e\xae\x18F#\xaeRd\xe4$\u0438\u05c9\x05\xd5?\xfd\xe9(\b\x00\x00\u07d4\xbb\xbdn\u02f5u(\x91\xb4\u03b3\xcc\xe7:\x8fGpY7o\x89\x01\xf3\x99\xb1C\x8a\x10\x00\x00\u07d4\xbb\xbf9\xb1\xb6y\x95\xa4\"APO\x97\x03\u04a1JQV\x96\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xbb\xc8\xea\xffc~\x94\xfc\u014d\x91\xdb\\\x89\x12\x1d\x06\xe1/\xff\x98\x80\x00\u07d4\xbc\u065e\xdc!`\xf2\x10\xa0^:\x1f\xa0\xb0CL\xed\x00C\x9b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbc\u07ec\xb9\xd9\x02<4\x17\x18.\x91\x00\xe8\xea\x1d73\x93\xa3\x89\x034-`\xdf\xf1\x96\x00\x00\u07d4\xbc\xe1>\"2*\u03f3U\xcd!\xfd\r\xf6\f\xf9:\xdd&\u0189\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xbc\xe4\x04u\xd3E\xb0q-\xeep=\x87\xcdvW\xfc\u007f;b\x8a\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\xbc\xed\xc4&|\u02c9\xb3\x1b\xb7d\xd7!\x11q\x00\x8d\x94\xd4M\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbc\xfc\x98\xe5\xc8+j\xdb\x18\n?\xcb\x12\v\x9av\x90\xc8j?\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbd\x04;g\xc6>`\xf8A\xcc\xca\x15\xb1)\xcd\xfee\x90\xc8\xe3\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbd\x04\u007f\xf1\xe6\x9c\u01b2\x9a\xd2d\x97\xa9\xa6\xf2z\x90?\xc4\u0749.\xe4IU\b\x98\xe4\x00\x00\u07d4\xbd\b\xe0\xcd\xde\xc0\x97\xdby\x01\ua05a=\x1f\xd9\u0789Q\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbd\t\x12l\x89\x1cJ\x83\x06\x80Y\xfe\x0e\x15ylFa\xa9\xf4\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xbd\f\\\u05d9\xeb\u0106B\xef\x97\xd7N\x8eB\x90d\xfe\u4489\x11\xac(\xa8\xc7)X\x00\x00\u07d4\xbd\x17\xee\xd8+\x9a%\x92\x01\x9a\x1b\x1b<\x0f\xba\xd4\\@\x8d\"\x89\r\x8drkqw\xa8\x00\x00\u07d4\xbd\x18\x037\v\u0771)\xd29\xfd\x16\xea\x85&\xa6\x18\x8a\u5389\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbd+p\xfe\xcc7d\x0fiQO\xc7\xf3@IF\xaa\xd8k\x11\x89A\rXj \xa4\xc0\x00\x00\u07d4\xbd0\x97\xa7\x9b<\r.\xbf\xf0\xe6\xe8j\xb0\xed\xad\xbe\xd4p\x96\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xbd2]@)\xe0\xd8r\x9fm9\x9cG\x82$\xae\x9ez\xe4\x1e\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xbdC*9\x16$\x9bG$):\xf9\x14nI\xb8(\n\u007f*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbdG\xf5\xf7n;\x93\x0f\xd9HR\t\xef\xa0\xd4v=\xa0uh\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbdK`\xfa\xect\n!\xe3\a\x13\x91\xf9j\xa54\xf7\xc1\xf4N\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xbdK\u0571\"\xd8\xef{|\x8f\x06gE\x03 \xdb!\x16\x14.\x89 \x86\xac5\x10R`\x00\x00\u07d4\xbdQ\xee.\xa1C\u05f1\u05b7~~D\xbd\xd7\xda\x12\U00105b09G~\x06\u0332\xb9(\x00\x00\u07d4\xbdY\tN\aO\x8dy\x14*\xb1H\x9f\x14\x8e2\x15\x1f \x89\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbdZ\x8c\x94\xbd\x8b\xe6G\x06D\xf7\f\x8f\x8a3\xa8\xa5\\cA\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbd^G:\xbc\xe8\xf9zi2\xf7|/\xac\xaf\x9c\xc0\xa0\x05\x14\x89<\x92X\xa1\x06\xa6\xb7\x00\x00\u07d4\xbd_F\u02ab,=K(\x93\x96\xbb\xb0\u007f *\x06\x11>\xd4\xc3\xfb\xa1\xa8\x91;\x19@~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\x9eV\xe9\x02\xf4\xbe\x1f\xc8v\x8d\x808\xba\xc6>*\u02ff\x8e\x8965f3\xeb\xd8\xea\x00\x00\u07d4\xbd\xa4\xbe1~~K\xed\x84\xc0I^\xee2\xd6\a\xec8\xcaR\x89}2'yx\xefN\x80\x00\u07d4\xbd\xb6\v\x82:\x11s\xd4Z\a\x92$_\xb4\x96\xf1\xfd3\x01\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xba\xf6CM@\xd65[\x1e\x80\xe4\f\u012b\x9ch\xd9a\x16\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbd\xc0,\xd43\f\x93\xd6\xfb\xdaOm\xb2\xa8]\xf2/C\xc23\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xc4aF+c\"\xb4b\xbd\xb3?\"y\x9e\x81\b\xe2A}\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xbd\xc79\xa6\x99p\v.\x8e,JL{\x05\x8a\x0eQ=\u07be\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xc7Hs\xaf\x92+\x9d\xf4t\x85;\x0f\xa7\xff\v\xf8\xc8&\x95\x89\xd8\xc9F\x00c\xd3\x1c\x00\x00\u07d4\xbd\xca*\x0f\xf3E\x88\xafb_\xa8\xe2\x8f\xc3\x01Z\xb5\xa3\xaa\x00\x89~\xd7?w5R\xfc\x00\x00\u07d4\xbd\xd3%N\x1b:m\xc6\xcc,i}Eq\x1a\xca!\xd5\x16\xb2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbd\u07e3M\x0e\xbf\x1b\x04\xafS\xb9\x9b\x82IJ\x9e=\x8a\xa1\x00\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xbd\xe4\xc7?\x96\x9b\x89\xe9\u03aef\xa2\xb5\x18DH\x0e\x03\x8e\x9a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbd\xe9xj\x84\xe7[H\xf1\x8erm\u05cdp\xe4\xaf>\xd8\x02\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4\xbd\xed\x11a/\xb5\xc6\u0699\xd1\xe3\x0e2\v\xc0\x99Tf\x14\x1e\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xbd\xed~\a\xd0q\x1ehM\xe6Z\u0232\xabW\xc5\\\x1a\x86E\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\xbd\xf6\x93\xf83\xc3\xfeG\x17S\x18G\x88\xebK\xfeJ\xdc?\x96\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbd\xf6\xe6\x8c\f\xd7X@\x80\xe8G\xd7,\xbb#\xaa\xd4j\xeb\x1d\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbe\n/8_\t\xdb\xfc\xe9g2\xe1+\xb4\n\xc3I\x87\x1b\xa8\x89WL\x11^\x02\xb8\xbe\x00\x00\u07d4\xbe\f*\x80\xb9\xde\bK\x17(\x94\xa7l\xf4szOR\x9e\x1a\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xbe\x1c\xd7\xf4\xc4r\a\th\xf3\xbd\xe2h6k!\xee\xea\x83!\x89\xe9\x1a|\u045f\xa3\xb0\x00\x00\u07d4\xbe#F\xa2\u007f\xf9\xb7\x02\x04OP\r\xef\xf2\xe7\xff\xe6\x82EA\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbe$q\xa6\u007f`G\x91\x87r\xd0\xe3h9%^\xd9\u0591\xae\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbe+\"\x80R7h\xea\x8a\xc3\\\xd9\xe8\x88\xd6\nq\x93\x00\u0509lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbe+2nx\xed\x10\xe5P\xfe\xe8\xef\xa8\xf8\a\x03\x96R/Z\x8a\bW\xe0\xd6\xf1\xdav\xa0\x00\x00\xe0\x94\xbe0Zyn3\xbb\xf7\xf9\xae\xaee\x12\x95\x90f\xef\xda\x10\x10\x8a\x02M\xceT\xd3J\x1a\x00\x00\x00\u07d4\xbeG\x8e\x8e=\xdek\xd4\x03\xbb-\x1ce|C\x10\xee\x19'#\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\xbeN}\x98?.*ck\x11\x02\xecp9\xef\xeb\xc8B\u9349\x03\x93\xef\x1aQ'\xc8\x00\x00\u07d4\xbeO\xd0sap\"\xb6\u007f\\\x13I\x9b\x82\u007fv69\xe4\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbeRZ3\xea\x91aw\xf1r\x83\xfc\xa2\x9e\x8b5\v\u007fS\v\x89\x8f\x01\x9a\xafF\xe8x\x00\x00\u07d4\xbeS2/C\xfb\xb5\x84\x94\xd7\xcc\xe1\x9d\xda'+$P\xe8'\x89\n\xd7\u03afB\\\x15\x00\x00\u07d4\xbeS\x82F\xddNo\f \xbfZ\xd17<;F:\x13\x1e\x86\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbeZ`h\x99\x98c\x9a\xd7[\xc1\x05\xa3qt>\xef\x0fy@\x89\x1b2|s\xe1%z\x00\x00\u07d4\xbe\\\xba\x8d7By\x86\xe8\xca&\x00\xe8X\xbb\x03\xc3YR\x0f\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\xbe`\x03~\x90qJK\x91~a\xf1\x93\xd84\x90g\x03\xb1:\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xbec:77\xf6\x849\xba\xc7\xc9\nR\x14 X\ue38ao\x894\n\xad!\xb3\xb7\x00\x00\x00\xe0\x94\xbee\x9d\x85\xe7\xc3O\x883\xea\u007fH\x8d\xe1\xfb\xb5\xd4\x14\x9b\xef\x8a\x01\xeb\xd2:\xd9\u057br\x00\x00\u07d4\xbes'M\x8cZ\xa4J<\xbe\xfc\x82c\xc3{\xa1!\xb2\n\u04c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbe\x86\u0430C\x84\x19\u03b1\xa081\x927\xbaR\x06\xd7.F\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xbe\x8d\u007f\x18\xad\xfe]l\xc7u9I\x89\xe1\x93\f\x97\x9d\x00}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbe\x91\x86\xc3JRQJ\xbb\x91\a\x86\x0fgO\x97\xb8!\xbd[\x89\x1b\xa0\x1e\xe4\x06\x03\x10\x00\x00\u07d4\xbe\x93W\x93\xf4[p\xd8\x04]&T\xd8\xdd:\xd2K[a7\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xbe\x98\xa7\u007f\xd4\x10\x97\xb3OY\xd7X\x9b\xaa\xd0!e\x9f\xf7\x12\x890\xca\x02O\x98{\x90\x00\x00\u07d4\xbe\x9b\x8c4\xb7\x8e\xe9G\xff\x81G.\xdaz\xf9\xd2\x04\xbc\x84f\x89\b!\xab\rD\x14\x98\x00\x00\u07d4\xbe\xa0\r\xf1pg\xa4:\x82\xbc\x1d\xae\xca\xfbl\x140\x0e\x89\xe6\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xbe\xa0\xaf\xc9:\xae!\b\xa3\xfa\xc0Yb;\xf8o\xa5\x82\xa7^\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xbe\xb35\x8cP\u03dfu\xff\xc7mD<,\u007fU\aZ\x05\x89\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xbe\xb4\xfd1UYC`E\u0739\x9dI\xdc\xec\x03\xf4\fB\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\xbe\xc2\xe6\xde9\xc0|+\xaeUj\u03fe\xe2\xc4r\x8b\x99\x82\xe3\x89\x1f\x0f\xf8\xf0\x1d\xaa\xd4\x00\x00\u07d4\xbe\xc6d\x0fI\t\xb5\x8c\xbf\x1e\x80cB\x96\x1d`u\x95\tl\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xbe\xc8\xca\xf7\xeeIF\x8f\xeeU.\xff:\xc5#N\xb9\xb1}B\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbe\xce\xf6\x1c\x1cD+\xef|\xe0Ks\xad\xb2I\xa8\xba\x04~\x00\x896;V\u00e7T\xc8\x00\x00\u0794\xbe\xd4d\x9d\xf6F\u2052)\x03-\x88hUo\xe1\xe0S\u04c8\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\xbe\xd4\xc8\xf0\x06\xa2|\x1e_|\xe2\x05\xdeu\xf5\x16\xbf\xb9\xf7d\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xbe\xe8\u0430\bB\x19T\xf9-\x00\r9\x0f\xb8\xf8\xe6X\xea\xee\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbe\xec\u05af\x90\f\x8b\x06J\xfc\xc6\a?-\x85\u055a\xf1\x19V\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbe\xef\x94!8y\xe0&\"\x14+\xeaa)\tx\x93\x9a`\u05ca\x016\x85{2\xad\x86\x04\x80\x00\xe0\x94\xbe\xf0}\x97\xc3H\x1f\x9dj\xee\x1c\x98\xf9\xd9\x1a\x18\n2D+\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xbe\xfbD\x8c\f_h?\xb6~\xe5p\xba\xf0\xdbV\x86Y\x97Q\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbf\x05\a\f,4!\x93\x11\xc4T\x8b&\x14\xa48\x81\r\xedm\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbf\x05\xff^\xcf\r\xf2\u07c8wY\xfb\x82t\xd928\xac&}\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\xbf\t\xd7pH\xe2p\xb6b3\x0e\x94\x86\xb3\x8bC\xcdx\x14\x95\x8a\\S\x9b{\xf4\xff(\x80\x00\x00\u07d4\xbf\x17\xf3\x97\xf8\xf4o\x1b\xaeE\u0447\x14\x8c\x06\xee\xb9Y\xfaM\x896I\u0156$\xbb0\x00\x00\u07d4\xbf\x186A\xed\xb8\x86\xce`\xb8\x19\x02a\xe1OB\xd9<\xce\x01\x89\x01[5W\xf1\x93\u007f\x80\x00\u07d4\xbf*\xeaZ\x1d\xcfn\u04f5\xe829D\xe9\x83\xfe\xdf\u046c\xfb\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xbf@\x96\xbcT}\xbf\xc4\xe7H\t\xa3\x1c\x03\x9e{8\x9d^\x17\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xbfI\xc1H\x981eg\u0637\t\xc2\xe5\x05\x94\xb3f\xc6\u04cc\x89'\xbf8\xc6TM\xf5\x00\x00\u07d4\xbfLs\xa7\xed\xe7\xb1d\xfe\a!\x14\x846T\xe4\xd8x\x1d\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xbfP\xce.&K\x9f\xe2\xb0h0az\xed\xf5\x02\xb25\x1bE\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbfY\xae\xe2\x81\xfaC\xfe\x97\x19CQ\xa9\x85~\x01\xa3\xb8\x97\xb2\x89 \x86\xac5\x10R`\x00\x00\u07d4\xbfh\u048a\xaf\x1e\xee\xfe\xf6F\xb6^\x8c\xc8\u0450\xf6\xc6\u069c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbfi%\xc0\aQ\x00\x84@\xa6s\x9a\x02\xbf+l\u06ab^:\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbfw\x01\xfcb%\u0561x\x15C\x8a\x89A\xd2\x1e\xbc]\x05\x9d\x89e\xea=\xb7UF`\x00\x00\u07d4\xbf\x8b\x80\x05\xd66\xa4\x96d\xf7Bu\xefBC\x8a\xcde\xac\x91\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbf\x92A\x8a\fl1$M\"\x02`\xcb>\x86}\u05f4\xefI\x89\x05i\x00\xd3<\xa7\xfc\x00\x00\u07d4\xbf\x9a\xcdDE\xd9\xc9UF\x89\u02bb\xba\xb1\x88\x00\xff\x17A\u008965\u026d\xc5\u07a0\x00\x00\u07d4\xbf\x9f'\x1fz~\x12\xe3m\xd2\xfe\x9f\xac\xeb\xf3\x85\xfeaB\xbd\x89\x03f\xf8O{\xb7\x84\x00\x00\u07d4\xbf\xa8\xc8X\xdf\x10,\xb1$!\x00\x8b\n1\xc4\xc7\x19\n\xd5`\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbf\xae\xb9\x10ga}\u03cbD\x17+\x02\xafaVt\x83]\xba\x89\b\xb5\x9e\x88H\x13\b\x80\x00\xe0\x94\xbf\xb0\xea\x02\xfe\xb6\x1d\xec\x9e\"\xa5\a\tY3\x02\x99\xc40r\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xbf\xbc\xa4\x18\xd3R\x9c\xb3\x93\b\x10b\x03*n\x11\x83\u01b2\u070a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xbf\xbe\x05\u831c\xbb\xcc\x0e\x92\xa4\x05\xfa\xc1\xd8]\xe2H\xee$\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xbf\xbf\xbc\xb6V\u0099+\xe8\xfc\u0782\x19\xfb\xc5J\xad\u055f)\x8a\x02\x1e\x18\xd2\xc8!\xc7R\x00\x00\u07d4\xbf\xc5z\xa6f\xfa\u239f\x10zI\xcbP\x89\xa4\xe2!Q\u074965\u026d\xc5\u07a0\x00\x00\u07d4\xbf\u02d70$c\x04p\r\xa9\vAS\xe7\x11Ab.\x1cA\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbf\xd9<\x90\u009c\a\xbc_\xb5\xfcI\xae\xeaU\xa4\x0e\x13O5\x8a\x05\xed\xe2\x0f\x01\xa4Y\x80\x00\x00\xe0\x94\xbf\xe3\xa1\xfcn$\xc8\xf7\xb3%\x05`\x99\x1f\x93\u02e2\u03c0G\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xbf\u6f30\xf0\xc0xRd3$\xaa]\xf5\xfdb%\xab\xc3\u0289\x04\t\xe5+H6\x9a\x00\x00\u07d4\xbf\xf5\xdfv\x994\xb8\x94<\xa9\x13}\x0e\xfe\xf2\xfen\xbb\xb3N\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbf\xfbi)$\x1fx\x86\x93'>p\"\xe6\x0e>\xab\x1f\xe8O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\x06O\x1d\x94t\xab\x91]V\x90l\x9f\xb3 \xa2\xc7\t\x8c\x9b\x89\x13h?\u007f<\x15\xd8\x00\x00\u07d4\xc0\a\xf0\xbd\xb6\xe7\x00\x92\x02\xb7\xaf>\xa9\t\x02i|r\x14\x13\x89\xa2\xa0\xe4>\u007f\xb9\x83\x00\x00\u07d4\xc0\n\xb0\x80\xb6C\xe1\u00ba\xe3c\xe0\u0455\xde.\xff\xfc\x1cD\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\xc0 wD\x9a\x13Jz\xd1\xef~M\x92z\xff\xec\ueb75\xae\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xc0$q\xe3\xfc.\xa0S&\x15\xa7W\x1dI2\x89\xc1<6\xef\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0-n\xad\xea\xcf\x1bx\xb3\u0285\x03\\c{\xb1\xce\x01\xf4\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc03\xb12Z\n\xf4Tr\xc2U'\x85;\x1f\x1c!\xfa5\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xc03\xbe\x10\xcbHa;\xd5\xeb\xcb3\xedI\x02\xf3\x8bX0\x03\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc04[3\xf4\x9c\xe2\u007f\xe8,\xf7\xc8M\x14\x1ch\xf5\x90\xcev\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc0=\xe4*\x10\x9bezd\xe9\"$\xc0\x8d\xc1'^\x80\u0672\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0@i\u07f1\x8b\tlxg\xf8\xbe\xe7zm\xc7Gz\xd0b\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xc0A?Z|-\x9aK\x81\b(\x9e\xf6\xec\xd2qx\x15$\xf4\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xc0C\xf2E-\u02d6\x02\xefb\xbd6\x0e\x03=\xd29q\xfe\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc0OK\xd4\x04\x9f\x04F\x85\xb8\x83\xb6)Y\xaec\x1df~5\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\xc0V\u053dk\xf3\u02ec\xace\xf8\xf5\xa0\xe3\x98\v\x85'@\xae\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xc0[t\x06 \xf1s\xf1nRG\x1d\u00cb\x9cQJ\v\x15&\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\xc0i\xef\x0e\xb3B\x99\xab\xd2\xe3-\xab\xc4yD\xb2r3H$\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\xc0l\xeb\xbb\xf7\xf5\x14\x9af\xf7\xeb\x97k>G\xd5e\x16\xda/\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc0r^\u00bd\xc3:\x1d\x82`q\u07a2\x9db\xd48Z\x8c%\x8a\b\xa0\x85\x13F:\xa6\x10\x00\x00\u07d4\xc0~8g\xad\xa0\x96\x80z\x05\x1al\x9c4\xcc;?J\xd3J\x89`\xf0f \xa8IE\x00\x00\u07d4\xc0\x89^\xfd\x05m\x9a:\x81\xc3\xdaW\x8a\xda1\x1b\xfb\x93V\u03c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc0\x90\xfe#\xdc\xd8k5\x8c2\xe4\x8d*\xf9\x10$%\x9fef\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc0\x9af\x17*\xea7\r\x9ac\xda\x04\xffq\xff\xbb\xfc\xff\u007f\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\x9e<\xfc\x19\xf6\x05\xff>\xc9\xc9\xc7\x0e%@\xd7\xee\x97Cf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc0\xa0*\xb9N\xbeV\xd0E\xb4\x1bb\x9b\x98F.:\x02J\x93\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xc0\xa3\x93\b\xa8\x0e\x9e\x84\xaa\xaf\x16\xac\x01\xe3\xb0\x1dt\xbdk-\x89\afM\xddL\x1c\v\x80\x00\u07d4\xc0\xa6\u02edwi*=\x88\xd1A\xefv\x9a\x99\xbb\x9e<\x99Q\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xc0\xa7\xe8C]\xff\x14\xc2Uws\x9d\xb5\\$\u057fW\xa3\u064a\nm\xd9\f\xaeQ\x14H\x00\x00\u07d4\xc0\xae\x14\xd7$\x83./\xce'x\xde\u007f{\x8d\xaf{\x12\xa9>\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0\xaf\xb7\u0637\x93p\xcf\xd6c\u018c\u01b9p*7\u035e\xff\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc0\xb0\xb7\xa8\xa6\xe1\xac\xdd\x05\xe4\u007f\x94\xc0\x96\x88\xaa\x16\u01ed\x8d\x89\x03{m\x02\xacvq\x00\x00\xe0\x94\xc0\xb3\xf2D\xbc\xa7\xb7\xde[H\xa5>\u06dc\xbe\xab\vm\x88\xc0\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\xc0\xc0M\x01\x06\x81\x0e>\xc0\xe5J\x19\U000ab157\xe6\x9aW=\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xc0\xca2w\x94.tE\x87K\xe3\x1c\xeb\x90)rqO\x18#\x89\r\x8drkqw\xa8\x00\x00\u07d4\xc0\u02ed<\xcd\xf6T\xda\"\xcb\xcf\\xe\x97\xca\x19U\xc1\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\xcb\xf6\x03/\xa3\x9e|F\xffw\x8a\x94\xf7\xd4E\xfe\"\xcf0\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4\xc0\xe0\xb9\x03\b\x8e\fc\xf5=\xd0iWTR\xaf\xf5$\x10\u00c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc0\xe4W\xbdV\xec6\xa1$k\xfa20\xff\xf3\x8eY&\xef\"\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xc0\xed\rJ\xd1\r\xe045\xb1S\xa0\xfc%\xde;\x93\xf4R\x04\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\xc0\xf2\x9e\xd0\af\x11\xb5\xe5^\x13\x05G\xe6\x8aH\xe2m\xf5\u4262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc1\x13(x#\\]\u06e5\xd9\xf3\"\x8bR6\xe4p \xdco\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc1\x17\r\xba\xad\xb3\xde\xe6\x19\x8e\xa5D\xba\xec\x93%\x18`\xfd\xa5\x89A\rXj \xa4\xc0\x00\x00\xe0\x94\xc1&W=\x87\xb0\x17ZR\x95\xf1\xdd\a\xc5u\u03cc\xfa\x15\xf2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc1'\xaa\xb5\x90e\xa2\x86D\xa5k\xa3\xf1^.\xac\x13\xda)\x95\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xc1+\u007f@\u07da/{\xf9\x83f\x14\"\xab\x84\xc9\xc1\xf5\bX\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xc1,\xfb{=\xf7\x0f\xce\xca\x0e\xde&5\x00\xe2xs\xf8\xed\x16\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc1/\x88\x1f\xa1\x12\xb8\x19\x9e\xcb\xc7>\xc4\x18W\x90\xe6\x14\xa2\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc18Lnq~\xbeK#\x01NQ\xf3\x1c\x9d\xf7\xe4\xe2[1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc1C\x8c\x99\xddQ\xef\x1c\xa88j\xf0\xa3\x17\xe9\xb0AEx\x88\x89\f\x1d\xaf\x81\u0623\xce\x00\x00\u07d4\xc1c\x12(\xef\xbf*.:@\x92\xee\x89\x00\xc69\xed4\xfb\u02093\xc5I\x901r\f\x00\x00\u07d4\xc1u\xbe1\x94\xe6iB-\x15\xfe\xe8\x1e\xb9\xf2\xc5lg\xd9\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc1\x82v\x86\xc0\x16\x94\x85\xec\x15\xb3\xa7\xc8\xc0\x15\x17\xa2\x87M\xe1\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xc1\x8a\xb4g\xfe\xb5\xa0\xaa\xdf\xff\x91#\x0f\xf0VFMx\xd8\x00\x89lk\x93[\x8b\xbd@\x00\x00\u0794\xc1\x95\x05CUM\x8aq0\x03\xf6b\xbba,\x10\xadL\xdf!\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xc1\xa4\x1aZ'\x19\x92&\xe4\xc7\xeb\x19\x8b\x03\x1bY\x19o\x98B\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xc1\xb2\xa0\xfb\x9c\xadE\xcdi\x91\x92\xcd'T\v\x88\xd38By\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc1\xb2\xaa\x8c\xb2\xbfb\xcd\xc1:G\xec\xc4e\u007f\xac\xaa\x99_\x98\x8967\x93\xfa\x96\u6980\x00\u07d4\xc1\xb5\x00\x01\x1c\xfb\xa9]|\xd66\xe9^l\xbfagFK%\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc1\xb9\xa5pM5\x1c\xfe\x98?y\xab\xee\xc3\u06fb\xae;\xb6)\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc1\xcb\xd2\xe23*RL\xf2\x19\xb1\r\x87\x1c\xcc \xaf\x1f\xb0\xfa\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xc1\xcd\xc6\x01\xf8\x9c\x04(\xb3\x13\x02\u0447\xe0\xdc\b\xad}\x1cW\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xc1\u052f8\xe9\xbay\x90@\x89HI\xb8\xa8!\x93u\xf1\xacx\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc1\xe1@\x9c\xa5,%CQ4\xd0\x06\u00a6\xa8T-\xfbrs\x89\x01\xdd\x1eK\xd8\xd1\xee\x00\x00\u07d4\xc1\xeb\xa5hJ\xa1\xb2L\xbac\x15\x02c\xb7\xa9\x13\x1a\xee\u008d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc1\xec\x81\xdd\x12=K|-\u0674\xd48\xa7\a,\x11\u0707L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc1\xf3\x9b\xd3]\xd9\xce\xc37\xb9oG\xc6w\x81\x81`\xdf7\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\xc1\xff\xad\a\u06d6\x13\x8cK*S\x0e\xc1\xc7\xde)\xb8\xa0Y,\x88\xf4?\xc2\xc0N\xe0\x00\x00\xe0\x94\xc2\x1f\xa6d:\x1f\x14\xc0)\x96\xadqD\xb7Y&\xe8~\xcbK\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc24\nL\xa9L\x96x\xb7IL<\x85%(\xed\xe5\xeeR\x9f\x89\x02\xa3k\x05\xa3\xfd|\x80\x00\u07d4\xc29\xab\u07ee>\x9a\xf5E\u007fR\xed+\x91\xfd\n\xb4\xd9\xc7\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2;/\x92\x1c\xe4\xa3z%\x9e\u4b4b!X\xd1]fOY\x89\x01`\x89\x95\xe8\xbd?\x80\x00\u07d4\xc2C\x99\xb4\xbf\x86\xf73\x8f\xbfd^;\"\xb0\u0dd79\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2L\u03bc#D\xcc\xe5d\x17\xfbhL\xf8\x16\x13\xf0\xf4\xb9\xbd\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xc2Rf\xc7gf2\xf1>\xf2\x9b\xe4U\ud50a\xddVw\x92\x89Hz\x9a0E9D\x00\x00\u07d4\xc2\\\xf8&U\f\x8e\xaf\x10\xaf\"4\xfe\xf9\x04\u0779R\x13\xbe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc2f?\x81E\xdb\xfe\xc6\xc6F\xfc\\I\x96\x13E\xde\x1c\x9f\x11\x89%g\xacp9+\x88\x00\x00\u07d4\xc2pEh\x854+d\vL\xfc\x1bR\x0e\x1aTN\xe0\xd5q\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xc2sv\xf4]!\xe1^\xde;&\xf2e_\xce\xe0,\xcc\x0f*\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xc2w\x97q\xf0Smy\xa8p\x8fi1\xab\xc4K05\u964a\x047\u04ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xc2\xc1>r\xd2h\xe7\x15\r\u01d9\xe7\xc6\xcf\x03\u0209T\xce\u05c9%\xf2s\x93=\xb5p\x00\x00\u07d4\xc2\xcb\x1a\xda]\xa9\xa0B8s\x81G\x93\xf1aD\xef6\xb2\xf3\x89HU~;p\x17\xdf\x00\x00\u07d4\xc2\xd1w\x8e\xf6\xee_\xe4\x88\xc1E\xf3Xkn\xbb\xe3\xfb\xb4E\x89>\x1f\xf1\xe0;U\xa8\x00\x00\xe0\x94\xc2\xd9\xee\xdb\xc9\x01\x92c\xd9\xd1l\u016e\a-\x1d=\xd9\xdb\x03\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc2\xe0XJq4\x8c\xc3\x14\xb7; )\xb6#\v\x92\u06f1\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2\xe2\u0518\xf7\r\xcd\bY\xe5\v\x02:q\nmK!3\xbd\x8989\x11\xf0\f\xbc\xe1\x00\x00\u07d4\xc2\xed_\xfd\u046d\xd8U\xa2i/\xe0b\xb5\xd6\x18t#`\u0509A\rXj \xa4\xc0\x00\x00\u07d4\xc2\xee\x91\xd3\xefX\xc9\u0465\x89\x84N\xa1\xae1%\xd6\u017ai\x894\x95tD\xb8@\xe8\x00\x00\u07d4\xc2\xfa\xfd\xd3\n\xcbmg\x06\xe9)<\xb0&A\xf9\xed\xbe\a\xb5\x89Q\x00\x86\vC\x0fH\x00\x00\u07d4\xc2\xfd\v\xf7\xc7%\xef>\x04~Z\xe1\u009f\xe1\x8f\x12\xa7)\x9c\x89Hz\x9a0E9D\x00\x00\u07d4\xc2\xfe}us\x1fcm\xcd\t\xdb\xda\x06q9;\xa0\xc8*}\x89wC\"\x17\xe6\x83`\x00\x00\u07d4\xc3\x10z\x9a\xf32-R8\xdf\x012A\x911b\x959W}\x89\x1a\xb4\xe4d\xd4\x141\x00\x00\xe0\x94\xc3\x11\v\xe0\x1d\xc9sL\xfcn\x1c\xe0\u007f\x87\xd7}\x13E\xb7\xe1\x8a\x01\x0f\f\xe9I\xe0\x0f\x93\x00\x00\u07d4\xc3 8\xcaR\xae\xe1\x97E\xbe\\1\xfc\xdcT\x14\x8b\xb2\xc4\u0409\x02\xb5\xaa\xd7,e \x00\x00\u07d4\xc3%\xc3R\x80\x1b\xa8\x83\xb3\"l_\xeb\r\xf9\xea\xe2\xd6\xe6S\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xc3.\xc7\xe4*\xd1l\xe3\xe2UZ\xd4\xc5C\x06\xed\xa0\xb2gX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc32\xdfP\xb1<\x014\x90\xa5\xd7\xc7]\xbf\xa3f\u0687\xb6\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc3:\u0373\xba\x1a\xab'P{\x86\xb1]g\xfa\xf9\x1e\xcfb\x93\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3>\u0393Z\x8fN\xf98\xea~\x1b\xac\x87\u02d2]\x84\x90\u028a\a\x03\x8c\x16x\x1fxH\x00\x00\u07d4\xc3@\xf9\xb9\x1c&r\x8c1\xd1!\xd5\xd6\xfc;\xb5m=\x86$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3F\xcb\x1f\xbc\xe2\xab(]\x8eT\x01\xf4-\xd7#M7\xe8m\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\xe0\x94\xc3H=n\x88\xac\x1fJ\xe7<\xc4@\x8dl\x03\xab\xe0\xe4\x9d\u028a\x03\x99\x92d\x8a#\u0220\x00\x00\xe0\x94\xc3H\xfcZF\x13#\xb5{\xe3\x03\u02c96\x1b\x99\x19\x13\xdf(\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xc3N;\xa12.\xd0W\x11\x83\xa2O\x94 N\xe4\x9c\x18fA\x89\x03'\xaf\uf927\xbc\x00\x00\xe0\x94\xc3[\x95\xa2\xa3s|\xb8\xf0\xf5\x96\xb3E$\x87+\xd3\r\xa24\x8a\x01\x98\xbe\x85#^-P\x00\x00\xe0\x94\xc3c\x1cv\x98\xb6\xc5\x11\x19\x89\xbfE''\xb3\xf99Zm\xea\x8a\x02C'X\x96d\x1d\xbe\x00\x00\u07d4\xc3l\vc\xbf\xd7\\/\x8e\xfb\x06\b\x83\xd8h\xcc\xcdl\xbd\xb4\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xc3uk\xcd\xcc~\xect\xed\x89j\xdf\xc35'Y0&n\b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u00c4\xacn\xe2|9\xe2\xf2x\xc2 \xbd\xfa[\xae\xd6&\xd9\u04c9 \x86\xac5\x10R`\x00\x00\u07d4\u00e0F\xe3\u04b2\xbfh\x14\x88\x82n2\xd9\xc0aQ\x8c\xfe\x8c\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\u00e9\"j\xe2u\xdf,\xab1+\x91\x10@cJ\x9c\x9c\x9e\xf6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u00f9(\xa7o\xadex\xf0O\x05U\xe69R\xcd!\xd1R\n\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3\xc2)s)\xa6\xfd\x99\x11~T\xfcj\xf3y\xb4\xd5VT~\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc3\xc3\xc2Q\rg\x80 HZcs]\x13\a\xecL\xa60+\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc3\xcbk6\xafD?,n%\x8bJ9U:\x81\x87G\x81\x1f\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\xc3\xdbVW\xbbr\xf1\rX\xf21\xfd\xdf\x11\x98\n\xffg\x86\x93\x8a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\xc3\u06df\xb6\xf4lH\n\xf3De\u05d7S\xb4\xe2\xb7Jg\u038a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc3\xddX\x908\x860;\x92\x86%%z\xe1\xa0\x13\xd7\x1a\xe2\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\xe0G\x1cd\xff5\xfaR2\xcc1!\xd1\u04cd\x1a\x0f\xb7\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\xe2\f\x96\u07cdN8\xf5\v&Z\x98\xa9\x06\xd6\x1b\xc5\x1aq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\u31f0<\xe9\\\xcf\xd7\xfaQ\u0744\x01\x83\xbcCS(\t\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3\xf8\xf6r\x95\xa5\xcd\x04\x93d\xd0]#P&#\xa3\xe5.\x84\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc4\x01\xc4'\xcc\xcf\xf1\r\xec\xb8d /6\xf5\x80\x83\"\xa0\xa8\x89\xb4{Q\xa6\x9c\xd4\x02\x00\x00\u07d4\xc4\b\x8c\x02_>\x85\x01?T9\xfb4@\xa1s\x01\xe5D\xfe\x89~\t\xdbM\x9f?4\x00\x00\u07d4\xc4\x14a\xa3\u03fd2\u0246UU\xa4\x8117\xc0v1#`\x8965\xc6 G9\u0640\x00\u07d4\xc4 8\x8f\xbe\xe8J\xd6V\xddh\xcd\xc1\xfb\xaa\x93\x92x\v4\x89\n-\xcac\xaa\xf4\u0140\x00\u07d4\xc4\"P\xb0\xfeB\xe6\xb7\xdc\xd5\u0210\xa6\xf0\u020f__\xb5t\x89\b\x1e\xe4\x82SY\x84\x00\x00\u07d4\xc4-j\xebq\x0e:P\xbf\xb4Ml1\t)i\xa1\x1a\xa7\xf3\x89\b\"c\xca\xfd\x8c\xea\x00\x00\xe0\x94\xc4@\xc7\xca/\x96Kir\xeffJ\"a\xdd\xe8\x92a\x9d\x9c\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xc4K\xde\xc8\xc3l\\h\xba\xa2\xdd\xf1\xd41i2)rlC\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xc4OJ\xb5\xbc`9|s~\xb0h3\x91\xb63\xf8\xa2G\x1b\x12\x1c\xa4\x89 .h\xf2\u00ae\xe4\x00\x00\u07d4\xc4h\x1es\xbb\x0e2\xf6\xb7& H1\xffi\xba\xa4\x87~2\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xc4k\xbd\xefv\xd4\xca`\xd3\x16\xc0\u007f]\x1ax\x0e;\x16_~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc4}a\v9\x92P\xf7\x0e\xcf\x13\x89\xba\xb6),\x91&O#\x89\x0f\xa7\xe7\xb5\xdf<\xd0\x00\x00\u07d4\u0100;\xb4\a\xc7b\xf9\vu\x96\xe6\xfd\u1513\x1ev\x95\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0106Q\xc1\xd9\xc1k\xffL\x95T\x88l??&C\x1foh\x89#\xab\x95\x99\xc4?\b\x00\x00\u07d4\u0109\xc8?\xfb\xb0%*\xc0\xdb\xe3R\x12\x17c\x0e\x0fI\x1f\x14\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u010bi<\xac\xef\xdb\xd6\xcb]x\x95\xa4.1\x962~&\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0113H\x9eV\u00fd\xd8)\x00}\xc2\xf9VA)\x06\xf7k\xfa\x89\x02\xa7\x91H\x8eqT\x00\x00\u07d4\u0116\u02f0E\x9aj\x01`\x0f\u0149\xa5Z2\xb4T!\u007f\x9d\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4\u011c\xfa\xa9g\xf3\xaf\xbfU\x03\x10a\xfcL\xef\x88\xf8]\xa5\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0136\xe5\xf0\x9c\xc1\xb9\r\xf0x\x03\xce=M\x13vj\x9cF\xf4\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\u013e\xc9c\b\xa2\x0f\x90\u02b1\x83\x99\u0113\xfd=\x06Z\xbfE\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xc4\xc0\x1a\xfc>\x0f\x04R!\xda\x12\x84\u05c7\x85tD/\xb9\xac\x8a\x01\x92\xb5\u0249\x02J\x19\xc1\xbdo\x12\x80\x00\xe0\x94\xc5\x00\xb7 sN\xd2)8\u05cc^H\xb2\xba\x93g\xa5u\xba\x8a\a\x12\x9e\x1c\xdf7>\xe0\x00\x00\u07d4\xc5\x0f\xe4\x15\xa6A\xb0\x85lNu\xbf\x96\x05\x15D\x1a\xfa5\x8d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5\x13L\xfb\xb1\xdfz \xb0\xedpWb.\xee\u0480\x94}\xad\x89\xcd\xff\x97\xfa\xbc\xb4`\x00\x00\xe0\x94\xc5\x17\xd01\\\x87\x88\x13\xc7\x17\u132f\xa1\xea\xb2eN\x01\u068a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc5\x18y\x9aY%Wb\x13\xe2\x18\x96\xe0S\x9a\xbb\x85\xb0Z\xe3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc5\"\xe2\x0f\xbf\x04\xed\u007fk\x05\xa3{G\x18\xd6\xfc\xe0\x14.\x1a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc5$\bmF\xc8\x11+\x12\x8b/\xafo|}\x81`\xa88l\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc5-\x1a\fs\u00a1\xbe\x84\x91Q\x85\xf8\xb3O\xaa\n\xdf\x1d\xe3\x89K\xe4\xea\xb3\xfa\x0f\xa6\x80\x00\xe0\x94\xc55\x94\xc7\u03f2\xa0\x8f(L\xc9\u05e6;\xbd\xfc\v1\x972\x8a\nk#(\xff:b\xc0\x00\x00\u07d4\xc57I(\xcd\xf1\x93pTC\xb1L\xc2\r\xa4#G<\xd9\u03c9\a}\x10P\x9b\xb3\xaf\x80\x00\u07d4\xc58\xa0\xff(*\xaa_Ku\u03f6,p\x03~\xe6}O\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5;P\xfd;+r\xbclC\v\xaf\x19JQU\x85\u04d8m\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xc5=y\xf7\u02dbp\x95/\xd3\x0f\xceX\xd5K\x9f\vY\xf6G\x8a\x01\x13\xe2\xd6tCE\xf8\x00\x00\u07d4\xc5I\u07c3\xc6\xf6^\xec\x0f\x1d\u0260\x93J\\_:P\xfd\x88\x89\x9d\xc0\\\xce(\u00b8\x00\x00\u07d4\xc5P\x05\xa6\xc3~\x8c\xa7\xe5C\xce%\x99s\xa3\xca\u0396\x1aJ\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5U\xb91V\xf0\x91\x01#\x80\x00\xe0\x94\u0166)\xa3\x96%R\u02ce\xde\u0609cj\xaf\xbd\f\x18\xcee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u016e\x86\xb0\xc6\xc7\xe3\x90\x0f\x13h\x10\\VS\u007f\xaf\x8dt>\x89\n1\x06+\xee\xedp\x00\x00\u07d4\u0170\t\xba\xea\xf7\x88\xa2v\xbd5\x81:\xd6[@\v\x84\x9f;\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0175l\xd24&|(\xe8\x9cok\"f\xb0\x86\xa1/\x97\f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xc5\u01a4\x99\x8a3\xfe\xb7dCz\x8b\xe9)\xa7;\xa3J\ad\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\xe0\x94\xc5\xc7=a\xcc\xe7\xc8\xfeL\x8f\xce)\xf3\x90\x92\xcd\x19>\x0f\xff\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xc5\xc7Y\vV!\xec\xf85\x85\x88\u079bh\x90\xf2baC\U000498a1]\tQ\x9b\xe0\x00\x00\u07d4\xc5\xcd\xce\xe0\xe8]\x11}\xab\xbfSj?@i\xbfD?T\xe7\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xc5\u050c\xa2\xdb/\x85\xd8\xc5U\xcb\x0e\x9c\xfe\x82i6x?\x9e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc5\xde\x12\x03\xd3\xcc,\xea1\xc8.\xe2\xdeY\x16\x88\a\x99\xea\xfd\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xc5\xe4\x88\xcf+Vw\x939q\xf6L\xb8 -\xd0WR\xa2\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc5\xe8\x12\xf7o\x15\xf2\xe1\xf2\xf9\xbcH#H<\x88\x04cog\x89\x03\xf5\x14\x19:\xbb\x84\x00\x00\u07d4\xc5\u94d34\xf1%.\u04ba&\x81D\x87\xdf\u0498+1(\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\xc5\xebB)^\x9c\xad\xea\xf2\xaf\x12\xde\u078a\x8dS\xc5y\xc4i\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\xc5\xed\xbb\xd2\xca\x03WeJ\xd0\xeaG\x93\xf8\xc5\xce\xcd0\xe2T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc5\xf6K\xab\xb7\x031B\xf2\x0eF\u05eab\x01\xed\x86\xf6q\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5\xf6\x87qrF\u068a \r \xe5\u9f2c`\xb6\u007f8a\x89\x01\x8d\x99?4\xae\xf1\x00\x00\u07d4\xc6\x04[<5\vL\xe9\xca\fkuO\xb4\x1ai\xb9~\x99\x00\x892$\xf4'#\xd4T\x00\x00\u07d4\xc6\v\x04eN\x00;F\x83\x04\x1f\x1c\xbdk\u00cf\xda|\xdb\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\x14F\xb7T\xc2N;\x16B\xd9\xe5\x17e\xb4\xd3\xe4k4\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\x18R\x13!\xab\xaf[&Q:J\x95(\bo\"\n\xdco\x89\x01v\xb3D\xf2\xa7\x8c\x00\x00\u07d4\xc6#FW\xa8\a8A&\xf8\x96\x8c\xa1p\x8b\xb0{\xaaI<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc6%\xf8\u024d'\xa0\x9a\x1b\u02bdQ(\xb1\u00a9HV\xaf0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc65^\xc4v\x8cp\xa4\x9a\xf6\x95\x13\u0343\xa5\xbc\xa7\xe3\xb9\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc6:\xc4\x17\x99.\x9f\x9b`8n\xd9S\xe6\xd7\xdf\xf2\xb0\x90\xe8\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xc6<\u05c8!\x18\xb8\xa9\x1e\aML\x8fK\xa9\x18Q0;\x9a\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xc6R\x87\x1d\x19$\"\u01bc#_\xa0c\xb4J~\x1dC\u3149\bg\x0e\x9e\xc6Y\x8c\x00\x00\xe0\x94\xc6gD\x1e\u007f)y\x9a\xbaadQ\xd5;?H\x9f\x9e\x0fH\x8a\x02\xf2\x9a\xceh\xad\u0740\x00\x00\u07d4\xc6j\xe4\xce\xe8\u007f\xb352\x19\xf7\u007f\x1dd\x86\u0140(\x032\x89\x01\x9a\x16\xb0o\xf8\xcb\x00\x00\u07d4\xc6t\xf2\x8c\x8a\xfd\a?\x8by\x96\x91\xb2\xf0XM\xf9B\xe8D\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0197\xb7\x04w\u02b4.+\x8b&f\x81\xf4\xaesu\xbb%A\x8a\x01.W2\xba\xba\\\x98\x00\x00\u07d4\u019b\x85U9\xce\x1b\x04qG(\xee\xc2Z7\xf3g\x95\x1d\xe7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u019b\xe4@\x13Mb\x80\x98\x01D\xa9\xf6M\x84t\x8a7\xf3I\x89&\u009eG\u0104L\x00\x00\u07d4\u019df<\x8d`\x90\x83\x91\xc8\xd26\x19\x153\xfd\xf7wV\x13\x89\x1aJ\xba\"\\ t\x00\x00\u0794\u01a2\x86\xe0e\xc8_:\xf7H\x12\xed\x8b\u04e8\xce]%\xe2\x1d\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\u01a3\x0e\xf5\xbb3 \xf4\r\xc5\xe9\x81#\rR\xae:\xc1\x93\"\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\u01ae(}\xdb\xe1\x14\x9b\xa1m\xdc\xcaO\xe0j\xa2\uaa48\xa9\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc6\xc7\xc1\x917\x98\x97\u075c\x9d\x9a3\x83\x9cJ_b\xc0\x89\r\x89\xd8\xd8T\xb2$0h\x80\x00\xe0\x94\xc6\xcdh\xec56,Z\xd8L\x82\xadN\xdc#!%\x91-\x99\x8a\x05\xe0T\x9c\x962\xe1\xd8\x00\x00\u07d4\xc6\u0615N\x8f?\xc53\xd2\xd20\xff\x02\\\xb4\xdc\xe1O4&\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc6\xdb\u06de\xfd^\xc1\xb3xn\x06q\xeb\"y\xb2S\xf2\x15\xed\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc6\xdf u\xeb\xd2@\xd4Hi\u00bek\u07c2\xe6=N\xf1\xf5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc6\xe2\xf5\xaf\x97\x9a\x03\xfdr:\x1bn\xfar\x83\x18\u03dc\x18\x00\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xc6\xe3$\xbe\xeb[6v^\xcdFB`\xf7\xf2`\x06\xc5\xc6.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\xe4\xcc\fr\x83\xfc\x1c\x85\xbcH\x13\xef\xfa\xafr\xb4\x98#\xc0\x89\x0f\x03\x1e\xc9\xc8}\xd3\x00\x00\xe0\x94\xc6\xee5\x93B)i5)\xdcA\u067bq\xa2IfX\xb8\x8e\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xc6\xfb\x1e\xe3t\x17\u0400\xa0\xd0H\x92;\u06ba\xb0\x95\xd0w\u0189\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc7\x05'\xd4D\u0110\xe9\xfc?\\\xc4Nf\xebO0k8\x0f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7\r\x85mb\x1e\xc1E0<\nd\x00\xcd\x17\xbb\xd6\xf5\xea\xf7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc7\x0f\xa4Uv\xbf\x9c\x86_\x988\x93\x00,AI&\xf6\x10)\x89\x15\xb4\xaa\x8e\x97\x02h\x00\x00\u07d4\xc7\x11E\xe5)\u01e7\x14\xe6y\x03\xeeb\x06\xe4\xc3\x04+g'\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xc7\x1b*=q5\u04a8_\xb5\xa5q\u073ei^\x13\xfcC\u034965\u026d\xc5\u07a0\x00\x00\u07d4\xc7\x1f\x1du\x87?3\u0732\xddK9\x87\xa1-\a\x91\xa5\xce'\x897\b\xba\xed=h\x90\x00\x00\u07d4\xc7\x1f\x92\xa3\xa5J{\x8c/^\xa4C\x05\xfc\u02c4\xee\xe21H\x89\x02\xb5\x9c\xa11\xd2\x06\x00\x00\u07d4\xc7!\xb2\xa7\xaaD\xc2\x12\x98\xe8P9\xd0\x0e.F\x0eg\v\x9c\x89\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\xc7,\xb3\x01%\x8e\x91\xbc\b\x99\x8a\x80]\u0452\xf2\\/\x9a5\x89 \t\xc5\u023fo\xdc\x00\x00\xe0\x94\xc76\x8b\x97\t\xa5\xc1\xb5\x1c\n\xdf\x18ze\xdf\x14\xe1+}\xba\x8a\x02\x02o\xc7\u007f\x03\u5b80\x00\u07d4\xc79%\x9e\u007f\x85\xf2e\x9b\xef_`\x9e\xd8k=Yl \x1e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc7>!\x12(\"\x15\xdc\ab\xf3+~\x80}\xcd\x1az\xae>\x8a\x01v\f\xbcb;\xb3P\x00\x00\xe0\x94\xc7If\x80B\xe7\x11#\xa6H\x97^\b\xedc\x82\xf8>\x05\xe2\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xc7J9\x95\xf8\a\xde\x1d\xb0\x1a.\xb9\xc6.\x97\xd0T\x8fio\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc7Pl\x10\x19\x12\x1f\xf0\x8a,\x8c\x15\x91\xa6^\xb4\xbd\xfbJ?\x89 \x86\xac5\x10R`\x00\x00\u07d4\xc7\\7\xce-\xa0k\xbc@\b\x11Y\u01ba\x0f\x97n9\x93\xb1\x89:y#\x15\x1e\xcfX\x00\x00\u07d4\xc7]\"Y0j\xec}\xf0\"v\x8ci\x89\x9ae!\x85\xdb\u0109\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7`\x97\x1b\xbc\x18\x1cj|\xf7tA\xf2BG\u045c\xe9\xb4\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xc7a0\xc7<\xb9!\x028\x02\\\x9d\xf9]\v\xe5J\xc6\u007f\xbe\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xc7e\xe0\x04v\x81\tG\x81j\xf1B\xd4m.\u7f28\xccO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc7g^VG\xb9\xd8\xda\xf4\xd3\xdf\xf1\xe5R\xf6\xb0qT\xac8\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xc7{\x01\xa6\xe9\x11\xfa\x98\x8d\x01\xa3\xab3dk\xee\xf9\xc18\xf3\x89'\x1bo\xa5\xdb\xe6\xcc\x00\x00\u07d4\u01c3z\u0420\xbf\x14\x18i7\xac\xe0lUF\xa3j\xa5OF\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u01d8\x06\x03+\xc7\xd8(\xf1\x9a\u01a6@\u018e=\x82\x0f\xa4B\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u01d9\xe3N\x88\xff\x88\xbe}\xe2\x8e\x15\xe4\xf2\xa6=\v3\xc4\u02c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\u01ddPb\u01d6\xddwa\xf1\xf1>U\x8ds\xa5\x9f\x82\xf3\x8b\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\u01e0\x18\xf0\x96\x8aQ\xd1\xf6`<\\I\xdcT[\xcb\x0f\xf2\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u01ef\xf9\x19)yt\x89UZ/\xf1\xd1M\\iZ\x10\x83U\x8965\u026d\xc5\u07a0\x00\x00\u0794\u01f1\xc8>c ?\x95G&>\xf6(.}\xa3;n\xd6Y\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\u01f3\x9b\x06\x04Q\x00\f\xa1\x04\x9b\xa1T\xbc\xfa\x00\xff\x8a\xf2b\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\u01ff\x17\xc4\xc1\x1f\x98\x94\x1fP~w\bO\xff\xbd-\xbd=\xb5\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u01ff.\xd1\xed1)@\xeej\xde\xd1Qn&\x8eJ`HV\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc7\xd4O\xe3,\u007f\x8c\xd5\xf1\xa9t'\xb6\xcd:\xfc\x9eE\x02>\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xc7\xd5\xc7\x05@\x81\xe9\x18\xech{Z\xb3n\x97=\x18\x13)5\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xc7\xde^\x8e\xaf\xb5\xf6+\x1a\n\xf2\x19\\\xf7\x93\u01c9L\x92h\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc7\xe30\xcd\f\x89\n\u025f\xe7q\xfc\xc7\xe7\xb0\t\xb7A=\x8a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7\xea\xc3\x1a\xbc\xe6\xd5\xf1\u07a4\"\x02\xb6\xa6t\x15=\xb4z)\x89 \t\xc5\u023fo\xdc\x00\x00\xe0\x94\xc7\xecb\xb8\x04\xb1\xf6\x9b\x1e0p\xb5\xd3b\xc6/\xb3\t\xb0p\x8a\x02\xc4k\xf5A`f\x11\x00\x00\u07d4\xc7\xf7+\xb7X\x01k7G\x14\u0509\x9b\xce\"\xb4\xae\xc7\n1\x89:&\xc9G\x8f^-\x00\x00\u0794\xc8\v6\u047e\xaf\xba_\xccdM`\xacnF\xed)'\xe7\u0708\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xc8\x11\xc2\xe9\xaa\x1a\xc3F.\xba^\x88\xfc\xb5\x12\x0e\x9fn,\xa2\x89K\xe6\u0607\xbd\x87n\x00\x00\u07d4\xc8\x17\xdf\x1b\x91\xfa\xf3\x0f\xe3%\x15qr|\x97\x11\xb4]\x8f\x06\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xc8\x1f\xb7\xd2\x0f\u0480\x01\x92\xf0\xaa\xc1\x98\xd6\u05a3}?\xcb}\x89\x0e\x11I3\x1c-\xde\x00\x00\u07d4\xc8 \xc7\x11\xf0w\x05'8\a\xaa\xaam\xe4M\x0eKH\xbe.\x89\bg\x0e\x9e\xc6Y\x8c\x00\x00\u07d4\xc8#\x1b\xa5\xa4\x11\xa1>\"+)\xbf\xc1\b?v1X\xf2&\x8967\tlK\xcci\x00\x00\u07d4\xc86\xe2Jo\xcf)\x94;6\b\xe6b)\n!_e)\xea\x89\x0f\xd4Pd\xea\xee\x10\x00\x00\xe0\x94\xc8;\xa6\u0755I\xbe\x1d2\x87\xa5\xa6T\xd1\x06\xc3Lk]\xa2\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xc8>\x9djX%;\uefb7\x93\xe6\xf2\x8b\x05JXI\x1bt\x89\x0fF\u00b6\xf5\xa9\x14\x00\x00\u07d4\xc8A\x88O\xa4x_\xb7s\xb2\x8e\x97\x15\xfa\xe9\x9aQ40]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc8M\x9b\xea\n{\x9f\x14\x02 \xfd\x8b\x90\x97\u03ff\xd5\xed\xf5d\x89\x06\xab\x9e\u0091\xad}\x80\x00\u07d4\xc8RB\x8d+Xd\x97\xac\xd3\fV\xaa\x13\xfbU\x82\xf8D\x02\x893B\xd6\r\xff\x19`\x00\x00\u07d4\xc8S![\x9b\x9f-,\xd0t\x1eX^\x98{_\xb8\f!.\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xc8S%\uaca5\x9b>\xd8c\xc8j_)\x06\xa0B)\xff\xa9\x89\x19=\u007f}%=\xe0\x00\x00\u07d4\xc8^\xf2}\x82\x04\x03\x80_\xc9\xed%\x9f\xffd\xac\xb8\xd64j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc8akN\xc0\x91(\xcd\xff9\xd6\u4e6c\x86\xee\xc4q\xd5\xf2\x89\x01\r:\xa56\xe2\x94\x00\x00\xe0\x94\xc8a\x90\x90K\x8d\a\x9e\xc0\x10\xe4b\xcb\xff\xc9\b4\xff\xaa\\\x8a\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4\xc8q\r~\x8bZ;\u059aB\xfe\x0f\xa8\xb8|5\u007f\xdd\xcd\u0209\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc8sR\u06e5\x82\xee f\xb9\xc0\x02\xa9b\xe0\x03\x13Ox\xb1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xc8|w\xe3\xc2J\xde\xcd\xcd\x108\xa3\x8bV\xe1\x8d\xea\u04f7\x02\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\xc8}:\xe3\u0607\x04\u066b\x00\t\xdc\xc1\xa0\x06q1\xf8\xba<\x89j\xc5\xc6-\x94\x86\a\x00\x00\xe0\x94\u0201N4R>8\xe1\xf9'\xa7\xdc\xe8FjDz\t6\x03\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0202U\xed\xdc\xf5!\xc6\xf8\x1d\x97\xf5\xa4!\x81\xc9\a=N\xf1\x89\x0f\u00d0D\xd0\n*\x80\x00\u07d4\u0205\xa1\x8a\xab\xf4T\x1b{{~\xcd0\xf6\xfa\u619d\x95i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u020c\xa1\xe6\xe5\xf4\xd5X\xd17\x80\xf4\x88\xf1\rJ\xd3\x13\r4\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u020e\xecT\xd3\x05\xc9(\xcc(H\xc2\xfe\xe251\xac\xb9mI\x89lj\u04c2\xd4\xfba\x00\x00\xe0\x94\u021c\xf5\x04\xb9\xf3\xf85\x18\x1f\xd8BO\\\xcb\xc8\xe1\xbd\xdf}\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0222\xc4\xe5\x9e\x1c\u007f\xc5H\x05X\x048\xae\xd3\xe4J\xfd\xf0\x0e\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4\u022aI\u301f\b\x99\xf2\x8a\xb5~gCp\x9dXA\x903\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\u022b\x1a<\xf4l\xb8\xb0d\xdf.\"-9`s\x94 2w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0231\x85\x05%\xd9F\xf2\xae\x84\xf3\x17\xb1Q\x88\xc56\xa5\u0706\x89\x91\x8d\xdc:B\xa3\xd4\x00\x00\u07d4\xc8\xd4\xe1Y\x9d\x03\xb7\x98\t\xe0\x13\n\x8d\u00c4\b\xf0^\x8c\u04c9\x9f\xad\x06$\x12y\x16\x00\x00\u07d4\xc8\xdd'\xf1k\xf2$P\xf5w\x1b\x9f\xe4\xedO\xfc\xb3\t6\xf4\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xc8\xdezVL\u007f@\x12\xa6\xf6\xd1\x0f\u040fG\x89\x0f\xbf\a\u0509\x10CV\x1a\x88)0\x00\x00\u07d4\xc8\xe2\xad\xebT^I\x9d\x98,\f\x11sc\u03b4\x89\u0171\x1f\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\xc8\xe5X\xa3\xc5i~o\xb2:%\x94\u0200\xb7\xa1\xb6\x8f\x98`\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc8\xf2\xb3 \xe6\xdf\xd7\t\x06\u0157\xba\xd2\xf9P\x13\x12\u01c2Y\x89Q\x93K\x8b:W\xd0\x00\x00\u07d4\xc9\x03\x00\xcb\x1d@w\xe6\xa6\xd7\xe1i\xa4`F\x8c\xf4\xa4\x92\u05c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9\f7e\x15k\u028eH\x97\xab\x80$\x19\x15<\xbeR%\xa9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc9\x10\xa9pUl\x97\x16\xeaS\xaff\xdd\xef\x93\x141$\x91=\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\xe0\x94\xc9\x12{\u007ff)\xee\x13\xfc?`\xbc/Dg\xa2\aE\xa7b\x8a\x03|\x9a\xa4\xe7\xceB\x1d\x80\x00\u07d4\xc9\x1b\xb5b\xe4+\xd4a0\xe2\u04eeFR\xb6\xa4\ub1bc\x0f\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\xe0\x94\xc90\x88y\x05m\xfe\x13\x8e\xf8 \x8fy\xa9\x15\u01bc~p\xa8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xc94\xbe\xca\xf7\x1f\"_\x8bJK\xf7\xb1\x97\xf4\xac\x9604\\\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc9?\xbd\xe8\xd4m+\xcc\x0f\xa9\xb3;\u063a\u007f\x80B\x12Ue\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xc9@\x89U:\xe4\xc2,\xa0\x9f\xbc\x98\xf5pu\xcf.\u0155\x04\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc9A\x10\xe7\x1a\xfeW\x8a\xa2\x18\xe4\xfc(d\x03\xb03\n\u038d\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc9F\u056c\xc14n\xba\nry\xa0\xac\x1dF\\\x99m\x82~\x8a\x03x=T_\xdf\n\xa4\x00\x00\u07d4\xc9J(\xfb20\xa9\xdd\xfa\x96Nw\x0f,\xe3\xc2S\xa7\xbeO\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc9JXR\x03\xda{\xba\xfd\x93\xe1X\x84\xe6`\u0531\xea\xd8T\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xc9O|5\xc0'\xd4}\xf8\xefO\x9d\xf8Z\x92H\xa1}\xd2;\x89\x01\x9f\x8euY\x92L\x00\x00\u07d4\xc9Q\x90\f4\x1a\xbb\xb3\xba\xfb\xf7\xee )7pq\xdb\xc3j\x89\x11\xc2]\x00M\x01\xf8\x00\x00\u07d4\xc9S\xf94\xc0\xeb-\x0f\x14K\u06b0\x04\x83\xfd\x81\x94\x86\\\xe7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9f&r\x8a\xaaLO\xb3\xd3\x1c&\xdf:\xf3\x10\b\x17\x10\u0449\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\xc9gQel\n\x8e\xf45{sD2!4\xb9\x83PJ\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\u0240Hh\u007f+\xfc\u027d\x90\xed\x18slW\xed\xd3R\xb6]\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0241\xd3\x12\u0487\xd5X\x87\x1e\u0757:\xbbv\xb9y\xe5\xc3^\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0242Xmc\xb0\xd7L \x1b\x1a\xf8A\x83r\xe3\fv\x16\xbe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u0249CO\x82Z\xaf\x9cU/h^\xba|\x11\xdbJ_\xc7:\x89\x1b(\u014d\x96\x96\xb4\x00\x00\u07d4\u0249\xee\xc3\a\u80db\x9dr7\xcf\xda\b\x82)b\xab\u41c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u0252\xbeY\xc6r\x1c\xafN\x02\x8f\x9e\x8f\x05\xc2\\UQ[\u0509\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0255{\xa9L\x1b)\xe5'~\xc3f\"pI\x04\xc6=\xc0#\x89h>\xfcg\x82d,\x00\x00\xe0\x94\u025a\x9c\xd6\xc9\xc1\xbe54\xee\u0352\xec\xc2/\\8\xe9Q[\x8a\x01\x05Y;:\x16\x9dw\x00\x00\xe0\x94\u026c\x01\xc3\xfb\t)\x03?\f\xcc~\x1a\xcf\uaae7\x94]G\x8a\x02\xa3j\x9e\x9c\xa4\xd2\x03\x80\x00\u07d4\u0276\x98\xe8\x98\xd2\rMO@\x8eNM\x06\x19\"\xaa\x85c\a\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u0276\xb6\x86\x11\x16\x91\xeej\xa1\x97\xc7#\x1a\x88\xdc`\xbd)]\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc9\u01ec\v\u0753B\xb5\xea\xd46\t#\xf6\x8cr\xa6\xbac:\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc9\xc8\r\xc1.{\xab\x86\xe9I\xd0\x1eL>\xd3_+\x9b\xba_\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9\xd7dF\u056a\xdf\xf8\vh\xb9\x1b\b\u035b\xc8\xf5U\x1a\xc1\x89&\xb4\xbd\x91\x10\xdc\xe8\x00\x00\xe0\x94\xc9\u073b\x05oM\xb7\xd9\xda9\x93b\x02\u017d\x820\xb3\xb4w\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc9\xe0&\b\x06h(\x84\x8a\xeb(\xc76r\xa1)%\x18\x1fM\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xca\x042\xcb\x15{Qy\xf0.\xbb\xa5\xc9\u0475O\xecM\x88\u028965\u026d\xc5\u07a0\x00\x00\u07d4\xca\x12,\xf0\U00094216\xb7HC\xf4\x9a\xfe\u043a\x16\x18\xee\u05c9\x1e[\x8f\xa8\xfe*\xc0\x00\x00\xe0\x94\xca\"\u0363`m\xa5\xca\xd0\x13\xb8\aG\x06\xd7\xe9\xe7!\xa5\f\x8a\x01q\x81\xc6\xfa9\x81\x94\x00\x00\u07d4\xca#\xf6-\xff\rd`\x03lb\xe8@\xae\xc5W~\v\xef\u0489\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\xca%\xff4\x93L\x19B\xe2*N{\xd5o\x14\x02\x1a\x1a\xf0\x88\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xca7?\xe3\xc9\x06\xb8\xc6U\x9e\xe4\x9c\xcd\a\xf3|\xd4\xfbRf\x89a\t=|,m8\x00\x00\u07d4\xcaA\u032c0\x17 R\xd5\"\xcd//\x95}$\x81S@\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xcaB\x88\x01N\xdd\xc5c/_\xac\xb5\xe3\x85\x17\xa8\xf8\xbc]\x98\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xcaB\x88c\xa5\xca06\x98\x92\xd6\x12\x18>\xf9\xfb\x1a\x04\xbc\xea\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\xcaI\xa5\xf5\x8a\xdb\xef\xae#\xeeY\xee\xa2A\xcf\x04\x82b.\xaa\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xcaL\xa9\xe4w\x9dS\x0e\u02ec\xd4~j\x80X\xcf\xdee\u064f\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xcae~\xc0o\xe5\xbc\t\xcf#\xe5*\xf7\xf8\f\xc3h\x9en\u07890\xca\x02O\x98{\x90\x00\x00\u07d4\xcaf\xb2(\x0f\xa2\x82\u0176v1\xceU+b\xeeU\xad\x84t\x89j\xc4\"\xf54\x92\x88\x00\x00\xe0\x94\xcal\x81\x8b\xef\xd2Q6\x1e\x02t@h\xbe\x99\u062a`\xb8J\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcap\xf4\u077f\x06\x9d!C\xbdk\xbc\u007fikRx\x9b2\u7262\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xcatuvDjL\x8f0\xb0\x83@\xfe\xe1\x98\xdec\xec\x92\u03ca\x01|\x8e\x12\x06r*0\x00\x00\u07d4\xca{\xa3\xffSl~_\x0e\x158\x00\xbd8=\xb81)\x98\xe0\x89\t1\xac=k\xb2@\x00\x00\xe0\x94\u0282v\xc4w\xb4\xa0{\x80\x10{\x845\x94\x18\x96\a\xb5;\xec\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u0284\t\b>\x01\xb3\x97\xcf\x12\x92\x8a\x05\xb6\x84U\xceb\x01\u07c9V\xbcu\xe2\xd61\x00\x00\x00\u07d4\u0298\u01d8\x8e\xfa\b\xe9%\uf719ER\x03&\xe9\xf4;\x99\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u029a\x04*j\x80o\xfc\x92\x17\x95\x00\xd2D)\xe8\xabR\x81\x17\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\u029d\xec\x02\x84\x1a\xdf\\\xc9 WjQ\x87\xed\u04bdCJ\x18\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u029f\xaa\x17T/\xaf\xbb8\x8e\xab!\xbcL\x94\u89f3G\x88\x89lk\x8f\xce\r\x18y\x80\x00\xe0\x94\u02aah\xeel\xdf\r4EJv\x9b\r\xa1H\xa1\xfa\xaa\x18e\x8a\x01\x87.\x1d\xe7\xfeR\xc0\x00\x00\u07d4\u02ad\x9d\xc2\rX\x9c\xe4(\xd8\xfd\xa3\xa9\xd5:`{y\x88\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u02b0\xd3,\xf3v\u007f\xa6\xb3S|\x842\x8b\xaa\x9fPE\x816\x8a\x01\xe5\xb8\xfa\x8f\xe2\xac\x00\x00\x00\u07d4\u02b9\xa3\x01\xe6\xbdF\xe9@5P(\xec\xcd@\xceMZ\x1a\u00c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u02b9\xa9z\xda\x06\\\x87\x81nh`\xa8\xf1Bo\xe6\xb3\xd7u\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u02ba\xb6'N\xd1P\x89s~({\xe8x\xb7W\x93Hd\xe2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\u02bd\xaf5OG \xa4f\xa7d\xa5(\xd6\x0e:H*9<\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xca\xcbg^\t\x96#T\x04\ufbfb.\u02c1R'\x1bU\xe0\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\xca\xd1O\x9e\xbb\xa7f\x80\xeb\x83k\a\x9c\u007f{\xaa\xf4\x81\xedm\x89\f\xef={\xd7\xd04\x00\x00\xe0\x94\xca\xe3\xa2S\xbc\xb2\xcfN\x13\xba\x80\u0098\xab\x04\x02\xda|*\xa0\x8a\x01$\xbc\r\u0752\xe5`\x00\x00\u07d4\xca\xef\x02{\x1a\xb5\x04\xc7?A\xf2\xa1\ty\xb4t\xf9~0\x9f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xca\xf4H\x1d\x9d\xb7\x8d\xc4\xf2_{J\u023d;\x1c\xa0\x10k1\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xca\xfd\xe8U\x86L%\x98\xda<\xaf\xc0Z\u064d\U00089380H\x8a\x03\x00\xa8\xed\x96\xffJ\x94\x00\x00\xe0\x94\xcb\r\xd7\xcfN]\x86a\xf6\x02\x89C\xa4\xb9\xb7\\\x91D6\xa7\x8a\x19i6\x89t\xc0[\x00\x00\x00\u07d4\xcb\x1b\xb6\xf1\xda^\xb1\rH\x99\xf7\xe6\x1d\x06\xc1\xb0\x0f\u07f5-\x898E$\xccp\xb7x\x00\x00\u07d4\xcb=vl\x98?\x19+\xce\xca\xc7\x0fN\xe0=\xd9\xffqMQ\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xcbB\xb4N\xb5\xfd`\xb5\x83~O\x9e\xb4rgR=\x1a\"\x9c\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xcbG\xbd0\u03e8\xecTh\xaa\xa6\xa9FB\xce\xd9\xc8\x19\xc8\u0509\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcbH\xfe\x82e\u066fU\xebp\x06\xbc3VE\xb0\xa3\xa1\x83\xbe\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xcbJ\x91M+\xb0)\xf3._\xef\\#LO\xec--\xd5w\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xcbJ\xbf\u0082\xae\xd7n]W\xaf\xfd\xa5B\xc1\xf3\x82\xfc\xac\xf4\x8a\x01\xb9\x0f\x11\xc3\x18?\xaa\x00\x00\u07d4\xcbJ\xd0\xc7#\xdaF\xabV\xd5&\xda\f\x1d%\xc7=\xaf\xf1\n\x89\x1b\xa5\xab\xf9\xe7y8\x00\x00\u07d4\xcbK\xb1\xc6#\xba(\xdcB\xbd\xaa\xa6\xe7N\x1d*\xa1%l*\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xcbPXt\x12\x82#\x04\xeb\u02e0}\xab:\x0f\t\xff\xfe\u4189JD\x91\xbdm\xcd(\x00\x00\u07d4\xcbX\x99\v\u0350\u03ffm\x8f\t\x86\xf6\xfa`\x02v\xb9N-\x8964\xbf9\xab\x98x\x80\x00\u07d4\xcbh\xaeZ\xbe\x02\xdc\xf8\xcb\u016aq\x9c%\x81FQ\xaf\x8b\x85\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xcbty\x10\x9bC\xb2fW\xf4F_M\x18\xc6\xf9t\xbe_B\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xcb}+\x80\x89\xe91,\u026e\xaa's\xf3S\b\xecl*{\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u02c6\xed\xbc\x8b\xbb\x1f\x911\x02+\xe6IV^\xbd\xb0\x9e2\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u02d3\x19\x9b\x9c\x90\xbcI\x15\xbd\x85\x9e=B\x86m\xc8\xc1\x87I\x89\f\x90\xdf\a\xde\xf7\x8c\x00\x00\u07d4\u02d4\xe7o\xeb\xe2\b\x11g3\xe7n\x80]H\xd1\x12\xec\x9f\u028965\u026d\xc5\u07a0\x00\x00\u07d4\u02dbQ\x03\xe4\u0389\xafOd\x91aP\xbf\xf9\xee\u02df\xaa\\\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u02e2\\zP<\xc8\xe0\xd0Iq\xca\x05\xc7b\xf9\xb7b\xb4\x8b\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u02e2\x88\xcd<\x1e\xb4\u055d\xdb\x06\xa6B\x1c\x14\xc3E\xa4{$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u02f3\x18\x9eK\xd7\xf4_\x17\x8b\x1c0\xc7n&1MJK\n\x89\x0f\xfe\vg|e\xa9\x80\x00\xe0\x94\u02f7\xbe\x17\x95?,\u0313\u1f19\x80[\xf4U\x11CNL\x8a\n\xae[\x9d\xf5m/ \x00\x00\xe0\x94\xcb\xc0KM\x8b\x82\xca\xf6p\x99o\x16\f6)@\xd6o\xcf\x1a\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcb\u07974\xb8\xe6\xaaS\x8c)\x1dm\u007f\xac\xed\xb0\xf38\xf8W\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xcb\xe1\xb9H\x86M\x84t\xe7e\x14XX\xfc\xa4U\x0fxK\x92\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xe5/\xc53\xd7\xdd`\x8c\x92\xa2`\xb3|?E\u07b4\xeb3\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcb\xe8\x10\xfe\x0f\xec\xc9dGJ\x1d\xb9w(\xbc\x87\xe9s\xfc\xbd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xf1j\x0f\xe2tRX\xcdR\xdb+\xf2\x19T\xc9u\xfcj\x15\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\xcb\xf3\u007f\xf8T\xa2\xf1\xceS\x93D\x94wx\x92\xd3\xeceW\x82\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xfaj\xf6\u0083\xb0F\xe2w,`c\xb0\xb2\x15S\xc4\x01\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcb\xfav\xdb\x04\xce8\xfb ]7\xb8\xd3w\xcf\x13\x80\xda\x03\x17\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xcc\x03I\x85\xd3\xf2\x8c-9\xb1\xa3K\xce\xd4\u04f2\xb6\xca#N\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xcc\x04\x8d\u01f9]\xca%\xdf&\xee\xfac\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcc+_D\x8f5(\xd3\xfeA\xcc}\x1f\xa9\xc0\xdcv\xf1\xb7v\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xcc-\x04\xf0\xa4\x01q\x89\xb3@\xcaw\x19\x86A\xdc\xf6Ek\x91\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xccA\x9f\u0651+\x85\x13VY\xe7z\x93\xbc=\xf1\x82\xd4Q\x15\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xccE\xfb:U[\xad\x80{8\x8a\x03W\xc8U _|u\xe8\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xccHAM*\xc4\xd4*Yb\xf2\x9e\xeeD\x97\t/C\x13R\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\xccJ/,\xf8l\xf3\xe43u\xf3`\xa4sF\x91\x19_\x14\x90\x89I\x15\x05;\xd1)\t\x80\x00\u07d4\xccO\x0f\xf2\xae\xb6}T\xce;\xc8\xc6Q\v\x9a\xe8>\x9d2\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xccO\xaa\xc0\v\xe6b\x8f\x92\xefk\x8c\xb1\xb1\xe7j\xac\x81\xfa\x18\x89\v\"\xa2\xea\xb0\xf0\xfd\x00\x00\xe0\x94\xccO\xebr\u07d8\xff5\xa18\xe0\x17a\xd1 ?\x9b~\xdf\n\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xcc`oQ\x13\x97\xa3\x8f\u01c7+\u04f0\xbd\x03\xc7\x1b\xbdv\x8b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xcc`\xf86\xac\xde\xf3T\x8a\x1f\xef\u0321>\u01a97\xdbD\xa0\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4\xccl\x03\xbd`>\t\xdeT\xe9\xc4\u056cmA\xcb\xceqW$\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xccl-\xf0\x0e\x86\xec\xa4\x0f!\xff\xda\x1ag\xa1i\x0fG|e\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94\xccm{\x12\x06\x1b\xc9m\x10M`me\xff\xa3+\x006\xeb\a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xccs\xdd5kIy\xb5y\xb4\x01\xd4\xccz1\xa2h\xdd\xceZ\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xccu\x8d\a\x1d%\xa62\n\xf6\x8c]\xc9\xc4\xf6\x95[\xa9E \x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcc{\x04\x81\xcc2\xe6\xfa\xef#\x86\xa0p\"\xbc\xb6\xd2\u00f4\xfc\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94\u0314;\xe1\",\xd1@\n#\x99\xdd\x1bE\x94E\xcfmT\xa9\x8a\x02\xa7@\xaee6\xfc\x88\x00\x00\u07d4\u0315\x19\xd1\xf3\x98_k%^\xad\xed\x12\xd5bJ\x97'!\xe1\x8965\u026d\xc5\u07a0\x00\x00\u0794\u031a\xc7\x15\xcdo&\x10\xc5+XgdV\x88B\x97\x01\x8b)\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\u0320{\xb7\x94W\x1dJ\xcf\x04\x1d\xad\x87\xf0\xd1\xef1\x85\xb3\x19\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u032b\xc6\x04\x8aSFD$\xfc\xf7n\xeb\x9en\x18\x01\xfa#\u0509\x02\xab{&\x0f\xf3\xfd\x00\x00\u07d4\u032e\r=\x85*}\xa3\x86\x0f\x066\x15L\nl\xa3\x16(\u0509\x05\xc6\xd1+k\xc1\xa0\x00\x00\u07d4\xcc\xca$\xd8\xc5mn,\a\xdb\bn\xc0~X[\xe2g\xac\x8d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xcc\xd5!\x13-\x98l\xb9hi\x84&\"\xa7\u0762l>\xd0W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcc\xf49u\xb7k\xfes_\xec<\xb7\xd4\xdd$\xf8\x05\xba\tb\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xcc\xf6*f?\x13S\xba.\xf8\xe6R\x1d\xc1\xec\xb6s\xec\x8e\xf7\x89\b=lz\xabc`\x00\x00\u07d4\xcc\xf7\x11\r\x1b\u0667K\xfd\x1d}}-\x9dU`~{\x83}\x890\xca\x02O\x98{\x90\x00\x00\u07d4\xcc\xfdrW`\xa6\x88#\xff\x1e\x06/L\xc9~\x13`\xe8\u0657\x89\x15\xacV\xed\xc4\xd1,\x00\x00\u07d4\xcd\x02\x0f\x8e\xdf\xcfRG\x98\xa9\xb7:d\x034\xbb\xf7/\x80\xa5\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xcd\x06\xf8\xc1\xb5\u037d(\xe2\xd9kcF\xc3\xe8Z\x04\x83\xba$\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcd\a.n\x183\x13y\x95\x19m{\xb1r_\xef\x87a\xf6U\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcd\n\x16\x1b\xc3g\xae\t'\xa9*\xac\x9c\xf6\xe5\bg\x14\xef\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\xcd\n\xf3GN\"\xf0i\xec4\a\x87\r\xd7pD=[\x12\xb0\x89\x8e^\xb4\xeew\xb2\xef\x00\x00\u07d4\xcd\v\x02W\u70e3\xd2\xc2\u3e9dny\xb7^\xf9\x80$\u0509\x9f\xad\x06$\x12y\x16\x00\x00\u07d4\xcd\x10,\xd6\xdb=\xf1J\u05af\x0f\x87\xc7$y\x86\x1b\xfc=$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcd\x1ef\xedS\x9d\xd9/\xc4\v\xba\xa1\xfa\x16\u078c\x02\xc1ME\x89\fw\xe4%hc\xd8\x00\x00\u07d4\xcd\x1e\xd2c\xfb\xf6\xf6\xf7\xb4\x8a\xef\x8fs=2\x9dC\x82\xc7\u01c9\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4\xcd*6\xd7S\xe9\xe0\xed\x01*XMqh\aX{A\xd5j\x89\x0e+\xa7[\v\x1f\x1c\x00\x00\u07d4\xcd2\xa4\xa8\xa2\u007f\x1c\xc69T\xaacOxW\x05s4\u01e3\x89:\xd1fWlr\xd4\x00\x00\u07d4\xcd5\xff\x01\x0e\xc5\x01\xa7!\xa1\xb2\xf0z\x9c\xa5\x87}\xfc\xf9Z\x89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4\xcdC\x06\xd7\xf6\x94z\xc1tMN\x13\xb8\xef2\xcbe~\x1c\x00\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4\xcdC%\x8bs\x92\xa90\x83\x9aQ\xb2\xef\x8a\xd24\x12\xf7Z\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcdI\xbf\x18^p\xd0E\a\x99\x9f\x92\xa4\xdeDU1('\u040965\u026d\xc5\u07a0\x00\x00\u07d4\xcdU\x10\xa2B\u07f0\x18=\xe9%\xfb\xa8f\xe3\x12\xfa\xbc\x16W\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xcdVj\u05f8\x83\xf0\x1f\u04d9\x8a\x9aX\xa9\xde\xe4rM\u0725\x89\x030\xae\x185\xbe0\x00\x00\xe0\x94\xcdY\xf3\xdd\xe7~\t\x94\v\xef\xb6\xeeX\x03\x19e\xca\xe7\xa36\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcdr]p\xbe\x97\xe6w\xe3\xc8\xe8\\\v&\xef1\xe9\x95PE\x89Hz\x9a0E9D\x00\x00\xe0\x94\xcd~G\x90\x94d\xd8q\xb9\xa6\xdcv\xa8\xe9\x19]\xb3H^z\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xcd~\xce\bkKa\x9b;6\x93R\xee8\xb7\x1d\xdb\x06C\x9a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xcd\u007f\t\xd7\xedf\xd0\u00cb\u016dN2\xb7\xf2\xb0\x8d\xc1\xb3\r\x89>;\xb3M\xa2\xa4p\x00\x00\u07d4\u0355)I+\\)\xe4u\xac\xb9A@+=;\xa5\x06\x86\xb0\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0355\xfaB=o\xc1 'J\xac\xde\x19\xf4\xee\xb7f\xf1\x04 \x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u035bL\xefs9\f\x83\xa8\xfdq\u05f5@\xa7\xf9\u03cb\x8c\x92\x89\x04\xe1\x00;(\xd9(\x00\x00\u07d4\u0361t\x11\t\xc0&[?\xb2\xbf\x8d^\xc9\u00b8\xa34kc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0361\xb8\x86\u39d5\u027aw\x91N\n/\xe5go\x0f\\\u03c9\x05\xbf`\xeaB\xc2\x04\x00\x00\u07d4\u0364S\x0fK\x9b\xc5\t\x05\xb7\x9d\x17\u008f\xc4o\x954\x9b\u07c93\x10\xe0I\x11\xf1\xf8\x00\x00\u07d4\u036bF\xa5\x90 \x80do\xbf\x95B\x04 J\xe8\x84\x04\x82+\x89\x1d\x8a\x96\xe5\xc6\x06\xeb\x00\x00\u07d4\u0375\x97)\x900\x18?n-#\x853\xf4d*\xa5\x87T\xb6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xcd\xd5\u0601\xa76,\x90p\a;\u07fcu\xe7$S\xacQ\x0e\x89-\xa5\x18\xea\xe4\x8e\xe8\x00\x00\u07d4\xcd\xd6\rs\xef\xaa\xd8s\u027b\xfb\x17\x8c\xa1\xb7\x10Z\x81\xa6\x81\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xcd\xd9\xef\xacMm`\xbdq\xd9U\x85\xdc\xe5\u0557\x05\xc15d\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xcd\xe3m\x81\xd1(\u015d\xa1Ee!\x93\xee\u00bf\xd9e\x86\xef\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcd\xea8o\x9d\x0f\xd8\x04\xd0(\x18\xf27\xb7\xd9\xfavF\xd3^\x89\xa3I\xd3m\x80\xecW\x80\x00\u07d4\xcd\xec\xf5gT3\u0370\xc2\xe5Zh\xdb]\x8b\xbexA\x9d\u0489\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xcd\xfd\x82\x173\x97%\xd7\xeb\xac\x11\xa66U\xf2e\xef\xf1\xcc=\x8a\x01\x0f\fid\x10\xe3\xa9\x00\x00\u07d4\xce\a\x9fQ\x88wt\xd8\x02\x1c\xb3\xb5u\xf5\x8f\x18\xe9\xac\xf9\x84\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xce\x18\x84\u077b\xb8\xe1\x0eM\xbanD\xfe\xee\u00a7\xe5\xf9/\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xce\x1b\f\xb4j\xae\xcf\u05db\x88\f\xad\x0f-\u068a\x8d\xed\u0431\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xce&\xf9\xa50_\x83\x81\tCT\xdb\xfc\x92fN\x84\xf9\x02\xb5\x89\fz\xaa\xb0Y\x1e\xec\x00\x00\u07d4\xce-\xea\xb5\x1c\n\x9a\xe0\x9c\xd2\x12\xc4\xfaL\xc5+S\xcc\r\xec\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xce.\r\xa8\x93F\x99\xbb\x1aU>U\xa0\xb8\\\x16\x945\xbe\xa3\x8a\x01\x0f\fid\x10\xe3\xa9\x00\x00\u07d4\xce:a\xf0F\x1b\x00\x93^\x85\xfa\x1e\xad\x82\xc4^Zd\u0508\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xceK\x06]\xbc\xb20G 2b\xfbH\xc1\x18\x83d\x97tp\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xceS\xc8\xcd\xd7B\x96\xac\xa9\x87\xb2\xbc\x19\u00b8u\xa4\x87I\u0409\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xce^\x04\xf0\x18Ci\xbc\xfa\x06\xac\xa6o\xfa\x91\xbfY\xfa\x0f\xb9\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xce^\xb6:{\xf4\xfb\xc2\xf6\u4ea0\u018a\xb1\xcbL\xf9\x8f\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xceb\x12Z\xde\xc37\n\xc5!\x10\x95:Nv\v\xe9E\x1e;\x89\b=lz\xabc`\x00\x00\xe0\x94\xceq\bmL`%T\xb8-\xcb\xfc\xe8\x8d cMS\xccM\x8a\t(\x96R\x9b\xad\u0708\x00\x00\u07d4\u038akmP3\xb1I\x8b\x1f\xfe\xb4\x1aAU\x04\x05\xfa\x03\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0397\x86\xd3q/\xa2\x00\xe9\xf6\x857\xee\xaa\x1a\x06\xa6\xf4ZK\x89a\t=|,m8\x00\x00\u07d4\u039d!\u0192\xcd<\x01\xf2\x01\x1fP_\x87\x006\xfa\x8fl\u0489\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\u03a2\x89f#\xf4\x91\x02\x87\xa2\xbd\u017e\x83\xae\xa3\xf2\xe6\xde\b\x8a\x01\xfbZ7Q\xe4\x90\xdc\x00\x00\u07d4\u03a3JM\xd9=\u066e\xfd9\x90\x02\xa9}\x99z\x1bK\x89\u0349QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\u03a4?pu\x81k`\xbb\xfc\u62d9:\xf0\x88\x12p\xf6\u0109lk\x93[\x8b\xbd@\x00\x00\u07d4\u03a8t3AS<\xb2\xf0\xb9\xc6\xef\xb8\xfd\xa8\rw\x16(%\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u03b0\x89\xec\x8ax3~\x8e\xf8\x8d\xe1\x1bI\xe3\u0751\x0ft\x8f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u03b3=x\xe7Tz\x9d\xa2\xe8}Q\xae\xc5\xf3D\x1c\x87\x92:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u03b3\x898\x1dH\xa8\xaeO\xfcH:\u043b^ L\xfd\xb1\xec\x89('\xe6\xe4\xddb\xba\x80\x00\u07d4\xce\xc6\xfce\x85?\x9c\xce_\x8e\x84Fv6.\x15y\x01_\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xce\xd3\u01fe\x8d\xe7XQ@\x95*\xebP\x1d\xc1\xf8v\ucbf0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xce\xd8\x1e\xc3S?\xf1\xbf\xeb\xf3\xe3\x84>\xe7@\xad\x11u\x8d>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xce\u0733\xa1\u0584?\xb6\xbe\xf6Ca}\xea\U000cf398\xdd_\x89\x19\xe2\xa4\xc8\x18\xb9\x06\x00\x00\u07d4\xce\xe6\x99\xc0pzx6%+)/\x04|\xe8\xad(\x9b/U\x89\x11\x9a\x1e!\xaaiV\x00\x00\u07d4\xce\xedG\xca[\x89\x9f\xd1b?!\xe9\xbdM\xb6Z\x10\u5c1d\x89\a8w@L\x1e\xee\x00\x00\u07d4\xce\xf7tQ\u07e2\xc6C\xe0\v\x15mlo\xf8N#s\xebf\x89\n1\x06+\xee\xedp\x00\x00\u07d4\xcf\x11i\x04\x1c\x17E\xe4[\x17$5\xa2\xfc\x99\xb4\x9a\xce+\x00\x89\x01\xbb\x88\xba\xab-|\x00\x00\xe0\x94\xcf\x15v\x12vN\x0f\u0596\xc8\xcb_\xba\x85\xdfL\r\xdc<\xb0\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u0794\xcf\x1b\xdby\x9b.\xa6<\xe14f\x8b\xdc\x19\x8bT\x84\x0f\x18\v\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xcf\"\x88\xefN\xbf\x88\xe8m\xb1=\x8a\x0e\v\xf5*\x05e\x82\u00c9\x89Po\xbf\x97@t\x00\x00\u07d4\xcf&Ni%\x13\t\x06\xc4\xd7\xc1\x85\x91\xaaA\xb2\xa6\u007foX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf&\xb4{\xd04\xbcP\x8elK\xcf\xd6\xc7\xd3\x004\x92Wa\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xcf.*\xd65\xe9\x86\x1a\xe9\\\xb9\xba\xfc\xca\x03kR\x81\xf5\u038a\at2!~h6\x00\x00\x00\u07d4\xcf.s@B\xa3U\xd0_\xfb.9\x15\xb1h\x11\xf4Zi^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf4\x8f/\xe4{~A<\az{\xaf:u\xfb\xf8B\x86\x92\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xcf?\x91(\xb0r\x03\xa3\xe1\r}WU\xc0\u012b\xc6\xe2\xca\u008a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xcf?\xbf\xa1\xfd2\u05e6\xe0\xe6\xf8\xefN\xabW\xbe4\x02\\L\x899\xa1\xc0\xf7YMH\x00\x00\u07d4\xcfAftn\x1d;\xc1\xf8\xd0qK\x01\xf1~\x8ab\xdf\x14d\x896w\x03n\xdf\n\xf6\x00\x00\u07d4\xcfO\x118\xf1\xbdk\xf5\xb6\u0505\xcc\xe4\xc1\x01\u007f\u02c5\xf0}\x89/\u043cw\xc3+\xff\x00\x00\u07d4\xcfZo\x9d\xf7Uy\xc6D\xf7\x94q\x12\x15\xb3\rw\xa0\xce@\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf^\x0e\xac\u0473\x9d\x06U\xf2\xf7u5\xeff\b\xeb\x95\v\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcfhM\xfb\x83\x04r\x93U\xb5\x83\x15\xe8\x01\x9b\x1a\xa2\xad\x1b\xac\x89\x17r$\xaa\x84Lr\x00\x00\u07d4\xcfi@\x81\xc7m\x18\xc6L\xa7\x13\x82\xbe\\\xd6;<\xb4v\xf8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xcfnR\xe6\xb7t\x80\xb1\x86~\xfe\xc6Dm\x9f\xc3\xcc5w\xe8\x89\f\t\x01\xf6\xbd\x98y\x00\x00\u07d4\u03c8: 2\x96g\xea\"j\x1e\x9a\x92*\x12\xf2\x1f\xaa\x03\x81V\x91\x8cO\u02dc\x89\x04E\x91\xd6\u007f\xec\xc8\x00\x00\u07d4\xcf\xf7\xf8\x9aMB\x19\xa3\x82\x95%\x131V\x82\x10\xff\xc1\xc14\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xcf\xf8\xd0k\x00\xe3\xf5\f\x19\x10\x99\xadV\xbaj\xe2eq\u0348\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcf\xfcI\xc1x~\ubcb5l\xab\xe9$\x04\xb66\x14}EX\x8a\x013\xe00\x8f@\xa3\u0680\x00\u07d4\xd0\bQ;'`J\x89\xba\x17c\xb6\xf8L\u6233F\x94[\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\x0f\x06r\x86\xc0\xfb\u0402\xf9\xf4\xa6\x10\x83\xecv\u07b3\xce\xe6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\x15\xf6\xfc\xb8M\xf7\xbbA\x0e\x8c\x8f\x04\x89J\x88\x1d\xca\xc27\x898E$\xccp\xb7x\x00\x00\u07d4\xd0\x1a\xf9\x13O\xafRW\x17N\x8by\x18oB\xee5Nd-\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0!\b\u04ae<\xab\x10\xcb\xcf\x16W\xaf\">\x02|\x82\x10\xf6\x89lm\x84\xbc\xcd\xd9\xce\x00\x00\u07d4\xd0*\xfe\u03ce.\u00b6*\u022d Aa\xfd\x1f\xaew\x1d\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd01\x919\xfb\xab.\x8e*\xcc\xc1\xd9$\u0531\x1d\xf6ilZ\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd07\xd2\x15\xd1\x1d\x1d\xf3\xd5O\xbd2\x1c\u0495\xc5F^';\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xd0:-\xa4\x1e\x86\x8e\xd3\xfe\xf5t[\x96\xf5\xec\xa4b\xffo\u0689\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xd0?\xc1eWj\xae\xd5%\xe5P,\x8e\x14\x0f\x8b.\x86\x969\x8a\x01sV\u0633%\x01\xc8\x00\x00\u07d4\xd0C\xa0\x11\xecBp\xee~\u0239hsu\x15\xe5\x03\xf80(\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd0K\x86\x1b=\x9a\xccV:\x90\x16\x89\x94\x1a\xb1\xe1\x86\x11a\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd0ZD|\x91\x1d\xbb'[\xfb.Z7\xe5\xa7\x03\xa5o\x99\x97\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd0_\xfb+t\xf8g O\xe51e;\x02H\xe2\x1c\x13TN\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0bX\x81q\u03d9\xbb\xebX\xf1&\xb8p\xf9\xa3r\x8da\xec\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\u07d4\xd0c\x8e\xa5q\x89\xa6\xa6\x99\x02J\u05ccq\xd99\xc1\xc2\xff\x8c\x89\x8e\xaeVg\x10\xfc \x00\x00\xe0\x94\xd0d\x8aX\x1b5\b\xe15\xa2\x93]\x12\xc9epE\xd8q\u028a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4\xd0q\x19)f\xebi\xc3R\x0f\xca:\xa4\xdd\x04)~\xa0KN\x89\x05\xf6\x8e\x811\xec\xf8\x00\x00\u07d4\xd0q\x85 \xea\xe0\xa4\xd6-p\xde\x1b\xe0\xcaC\x1c^\xea$\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd0w]\xba*\xf4\xc3\n:x6Y9\xcdq\xc2\xf9\u0795\u0489i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xd0{\xe0\xf9\t\x97\xca\xf9\x03\u022c\x1dS\xcd\xe9\x04\xfb\x19\aA\x8968\x908\xb6\x99\xb4\x00\x00\u07d4\xd0~Q\x18d\xb1\u03d9i\xe3V\x06\x02\x82\x9e2\xfcNq\xf5\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\u0400\x94\x98\xc5H\x04z\x1e**\xa6\xa2\x9c\xd6\x1a\x0e\xe2h\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0402'_tZ,\xac\x02v\xfb\xdb\x02\u0532\xa3\xab\x17\x11\xfe\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\u040f\xc0\x9a\x000\xfd\t(\xcd2\x11\x98X\x01\x82\xa7j\xae\x9f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0413\xe8)\x81\x9f\xd2\xe2[\x978\x00\xbb=XA\xdd\x15-\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0414J\xa1\x85\xa13pa\xae \u071d\xd9l\x83\xb2\xbaF\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u0416V[|t\a\xd0e6X\x03U\xfd\xd6\xd29\x14J\xa1\x89\r\x8drkqw\xa8\x00\x00\u07d4\u041c\xb2\xe6\b-i:\x13\xe8\xd2\xf6\x8d\xd1\u0744a\xf5X@\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0426\xc6\xf9\xe9\u0133\x83\xd7\x16\xb3\x1d\xe7\x8dVAM\xe8\xfa\x91\x89\x10CV\x1a\x88)0\x00\x00\u07d4\u0427 \x9b\x80\xcf`\xdbb\xf5}\n]}R\x1ai`fU\x89\b\xacr0H\x9e\x80\x00\x00\xe0\x94\u0428\xab\xd8\n\x19\x9bT\xb0\x8be\xf0\x1d \x9c'\xfe\xf0\x11[\x8a\x01a\xc6&\xdca\xa2\xef\x80\x00\xe0\x94\u042b\xccp\xc0B\x0e\x0e\x17/\x97\xd4;\x87\xd5\xe8\f3n\xa9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u042es]\x91^\x94hf\xe1\xfe\xa7~^\xa4f\xb5\xca\xdd\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0431\x1do+\u0394^\fjP \u00f5'S\xf8\x03\xf9\u0449\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xd0\xc1\x01\xfd\x1f\x01\xc6?k\x1d\x19\xbc\x92\r\x9f\x93#\x14\xb16\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd0\xc5Z\xbf\x97o\xdc=\xb2\xaf\u9f99\u0519HMWl\x02\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\u0422\xadE\xf5\x9a\x9d\xcc\u0195\xd8_%\xcaF\xed1\xa5\xa3\x89-\x89W}}@ \x00\x00\u07d4\xd0\xd6,G\xea`\xfb\x90\xa3c\x92\t\xbb\xfd\xd4\xd93\x99\x1c\u0189\n\x84Jt$\xd9\xc8\x00\x00\u07d4\xd0\xdbEax o\\D0\xfe\x00Pc\x90<=zI\xa7\x89&I\x1eE\xa7S\xc0\x80\x00\u07d4\xd0\xe1\x94\xf3K\x1d\xb6\t(\x85\t\xcc\xd2\xe7;a1\xa2S\x8b\x8965f3\xeb\xd8\xea\x00\x00\u07d4\xd0\xe3^\x04vF\xe7Y\xf4Qp\x93\xd6@\x86BQ\u007f\bM\x89\u054f\xa4h\x18\xec\u02c0\x00\u07d4\xd0\xeeM\x02\xcf$8,0\x90\xd3\xe9\x95`\xde6xs\\\u07c9\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xd0\xf0OR\x10\x9a\xeb\xec\x9a{\x1e\x932v\x1e\x9f\xe2\xb9{\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd0\xf9Yx\x11\xb0\xb9\x92\xbb}7W\xaa%\xb4\xc2V\x1d2\xe2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\x03\x02\xfa\xa1\x92\x9a2i\x04\xd3v\xbf\v\x8d\xc9:\xd0LL\x89a\t=|,m8\x00\x00\xe0\x94\xd1\x10\r\xd0\x0f\xe2\xdd\xf1\x81c\xad\x96M\vi\xf1\xf2\xe9e\x8a\x8a\x01C\x12\tU\xb2Pk\x00\x00\u07d4\xd1\x16\xf3\xdc\xd5\xdbtK\xd0\b\x88v\x87\xaa\x0e\xc9\xfdr\x92\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd1\x19A|Fs,\xf3M\x1a\x1a\xfby\xc3\xe7\xe2\u034e\xec\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1-w\xae\x01\xa9-5\x11{\xacpZ\xac\u0642\xd0.t\xc1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd15yK\x14\x9a\x18\xe1G\xd1nb\x1ai1\xf0\xa4\n\x96\x9a\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xd1C%8\xe3[vd\x95j\u4563*\xbd\xf0A\xa7\xa2\x1c\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xd1C\x82g#\x17\x04\xfcr\x80\xd5c\xad\xf4v8D\xa8\a\"\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd1S\x8e\x9a\x87\u5729\xec\x8eX&\xa5\xb7\x93\xf9\x9f\x96\xc4\u00c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd1d\x85\x03\xb1\xcc\u0178\xbe\x03\xfa\x1e\xc4\xf3\xee&~j\xdf{\x8a\x01;\xef\xbfQ\xee\xc0\x90\x00\x00\xe0\x94\xd1h,!Y\x01\x8d\xc3\xd0\u007f\b$\n\x8c`m\xafe\xf8\xe1\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xd1q\xc3\xf2%\x8a\xef5\xe5\x99\xc7\xda\x1a\xa0s\x00#M\xa9\xa6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1w\x8c\x13\xfb\xd9h\xbc\b<\xb7\xd1\x02O\xfe\x1fI\xd0,\xaa\x89\xd9\xec\xb4\xfd \x8eP\x00\x00\u07d4\xd1\u007f\xbe\"\xd9\x04b\xed7(\x06p\xa2\xea\v0\x86\xa0\xd6\u0589\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\u0441\x1cU\x97i\x80\xf0\x83\x90\x1d\x8a\r\xb2i\"-\xfb\\\xfe\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u044e\xb9\xe1\u0485\u06be\x93\xe5\u053a\xe7k\xee\xfeC\xb5!\xe8\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\u0453\xe5\x83\xd6\a\x05c\xe7\xb8b\xb9aJG\u9509\xf3\xe5\x8965f3\xeb\xd8\xea\x00\x00\u07d4\u0457\x8f.4@\u007f\xab\x1d\xc2\x18=\x95\xcf\xdab`\xb3Y\x82\x89*\xb7\xb2`\xff?\xd0\x00\x00\u07d4\u045c\xaf9\xbb7\u007f\xdf,\xf1\x9b\xd4\xfbRY\x1c&1\xa6<\x8965\u026d\xc5\u07a0\x00\x00\u0794\u0463\x96\xdc\u06b2\xc7IA0\xb3\xfd0x 4\r\xfd\x8c\x1f\x88\xf9\"P\xe2\xdf\xd0\x00\x00\xe0\x94\u0467\x1b-\bX\xe82p\b]\x95\xa3\xb1T\x96P\x03^#\x8a\x03'\xbb\t\xd0j\xa8P\x00\x00\u07d4\u046c\xb5\xad\xc1\x189s%\x8dk\x85$\xff\xa2\x8f\xfe\xb2=\xe3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0473\u007f\x03\xcb\x10t$\xe9\xc4\xddW\\\xcdOL\xeeW\xe6\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4\u0475\xa4T\xac4\x05\xbbAy \x8cl\x84\xde\x00k\u02db\xe9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\xc4YT\xa6+\x91\x1a\xd7\x01\xff.\x90\x13\x1e\x8c\xeb\x89\xc9\\\x89K\x91\xa2\xdeE~\x88\x00\x00\u07d4\xd1\xc9np\xf0Z\xe0\xe6\xcd`!\xb2\b7P\xa7q|\xdeV\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\u0571\u007f\xfe-{\xbby\xcc}y0\xbc\xb2\xe5\x18\xfb\x1b\xbf\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd1\xda\f\x8f\xb7\xc2\x10\xe0\xf2\xeca\x8f\x85\xbd\xae}>sK\x1c\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd1\xddy\xfb\x15\x81`\xe5\xb4\xe8\xe2?1.j\x90\u007f\xbcMN\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\xdeZ\xad:_\xd8\x03\U00071bb6\x10<\xb8\xe1O\xe7#\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd1\xe1\xf2\xb9\xc1l0\x98t\xde\xe7\xfa\xc3&u\xaf\xf1)\u00d8\x89\x03\xf2M\x8eJ\x00p\x00\x00\xe0\x94\xd1\xe5\xe24\xa9\xf4Bf\xa4\xa6$\x1a\x84\u05e1\xa5Z\u0567\xfe\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd1\xeaMr\xa6{[>\x0f1UY\xf5+\xd0aMq0i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1\xee\x90YW\xfe|\xc7\x0e\xc8\xf2\x86\x8bC\xfeG\xb1?\xeb\xff\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4\xd1\xf1iM\"g\x1bZ\xadj\x94\x99\\6\x9f\xbea3go\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd1\xf4\xdc\x1d\u06ca\xbb\x88H\xa8\xb1N%\xf3\xb5Z\x85\x91\xc2f\x89\r\x8drkqw\xa8\x00\x00\u07d4\xd1\xfe\u042e\xe6\xf5\xdf\xd7\xe2Wi%L<\xfa\xd1Z\xde\u032a\x89'\x92\xc8\xfcKS(\x00\x00\u07d4\xd2\x05\x1c\xb3\xcbg\x04\xf0T\x8c\u0210\xab\n\x19\xdb4\x15\xb4*\x89\x12\x1b.^ddx\x00\x00\u07d4\xd2\x06\xaa\u07736\xd4^yr\xe9<\xb0uG\x1d\x15\x89{]\x89 \x86\xac5\x10R`\x00\x00\u07d4\xd2\tH+\xb5I\xab\xc4w{\xeam\u007fe\x00b\xc9\xc5z\x1c\x89\x11e\x1a\xc3\xe7\xa7X\x00\x00\u07d4\xd2\r\xcb\vxh+\x94\xbc0\x00(\x14H\xd5W\xa2\v\xfc\x83\x890\x84\x9e\xbe\x166\x9c\x00\x00\u07d4\xd2\x10{57&\u00e2\xb4ef\xea\xa7\xd9\xf8\v]!\xdb\xe3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd2\x11\xb2\x1f\x1b\x12\xb5\ta\x81Y\r\xe0~\xf8\x1a\x89S~\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd2\x18\xef\xb4\u06d8\x1c\xddjy\u007fK\u050c|&)<\xeb@\x89\xa1Fk1\xc6C\x1c\x00\x00\xe0\x94\xd2\x1asA\xeb\x84\xfd\x15\x10T\xe5\u31fb%\xd3nI\x9c\t\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xd2$\xf8\x80\xf9G\x9a\x89\xd3/\t\xe5+\u9432\x88\x13\\\xef\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xd2/\f\xa4\xcdG\x9ef\x17u\x05;\xccI\xe3\x90\xf6p\u074a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd21\x92\x975\x13!\x02G\x1b\xa5\x90\a\xb6dL\xc0\xc1\xde>\x8967\tlK\xcci\x00\x00\u07d4\xd25\xd1\\\xb5\xec\xee\xbba)\x9e\x0e\x82\u007f\xa8'H\x91\x1d\x89\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd2:$\xd7\xf9F\x83C\xc1C\xa4\x1ds\xb8\x8f|\xbec\xbe^\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd2=z\xff\xac\xdc>\x9f=\xaez\xfc\xb4\x00oX\xf8\xa4F\x00\x89\xc3(\t>a\xee@\x00\x00\u07d4\xd2C\x18L\x80\x1e]y\xd2\x06?5x\u06ee\x81\u7ce9\u02c9k\u0722h\x1e\x1a\xba\x00\x00\u07d4\xd2KfD\xf49\xc8\x05\x1d\xfcd\u04c1\xb8\xc8lu\xc1u8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd2K\xf1--\xdfE}\xec\xb1xt\xef\xde R\xb6\\\xbbI\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xd2Q\xf9\x03\xae\x18rrY\xee\xe8A\xa1\x89\xa1\xf5i\xa5\xfdv\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd2R\x96\v\v\xf6\xb2\x84\x8f\u07ad\x80\x13m\xb5\xf5\a\xf8\xbe\x02\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd2X\x1aU\xce#\xab\x10\u062d\x8cD7\x8fY\a\x9b\xd6\xf6X\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\xd2Z\xec\xd7\xeb\x8b\xd64[\x06;]\xbd'\x1cw\xd3QD\x94\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xd2|#O\xf7\xac\xca\xce=\x99g\b\xf8\xf9\xb0Ip\xf9}6\x89Hz\x9a0E9D\x00\x00\u07d4\u0482\x98RM\xf5\xecK$\xb0\xff\xb9\u07c5\x17\n\x14Z\x9e\xb5\x89\x0f\x98\xa3\xb9\xb37\xe2\x00\x00\xe0\x94\u0483\xb8\xed\xb1\n%R\x8aD\x04\xde\x1ce\xe7A\r\xbc\xaag\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\u0484\xa5\x03\x82\xf8:am9\xb8\xa9\xc0\xf3\x96\xe0\ubfe9]\x8966\xc2^f\xec\xe7\x00\x00\u07d4\u0488\xe7\xcb{\xa9\xf6 \xab\x0ftR\xe5\bc=\x1cZ\xa2v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u049d\xc0\x8e\xfb\xb3\xd7.&?x\xabv\x10\xd0\"m\xe7k\x00\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\u04a00\xac\x89R2_\x9e\x1d\xb3x\xa7\x14\x85\xa2N\x1b\a\xb2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u04a4y@CG\xc5T:\xab)*\xe1\xbbJo\x15\x83W\xfa\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u04a5\xa0$#\nW\xcc\xc6fv\v\x89\xb0\xe2l\xaf\u0449\u01ca\n\x96YZ\\n\x8a?\x80\x00\u07d4\u04a8\x03'\xcb\xe5\\L{\xd5\x1f\xf9\xdd\xe4\xcad\x8f\x9e\xb3\xf8\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\u04a8Oug\\b\xd8\f\x88ulB\x8e\xee+\xcb\x18T!\x89A\rXj \xa4\xc0\x00\x00\u07d4\u04ab\xd8J\x18\x10\x93\xe5\xe2)\x13oB\xd85\xe8#]\xe1\t\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\u04ac\r:X`^\x1d\x0f\x0e\xb3\xde%\xb2\xca\xd1)\xed`X\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u04bfg\xa7\xf3\xc6\xceV\xb7\xbeAg]\xbb\xad\xfe~\xa9:3\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd2\xdb\xeb\xe8\x9b\x03W\xae\xa9\x8b\xbe\x8eIc8\u07bb(\xe8\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd2\xe2\x1e\xd5hh\xfa\xb2\x8e\tG\x92z\xda\xf2\x9f#\xeb\xadl\x89l\x18O\x13U\xd0\xe8\x00\x00\u07d4\xd2\xe8\x17s\x8a\xbf\x1f\xb4\x86X?\x80\xc3P1\x8b\xed\x86\f\x80\x89\r\x02\xce\xcf_]\x81\x00\x00\u07d4\xd2\xed\xd1\xdd\xd6\xd8m\xc0\x05\xba\xebT\x1d\"\xb6@\xd5\xc7\xca\xe5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd2\xf1\x99\x8e\x1c\xb1X\f\xecOl\x04}\xcd=\xce\xc5L\xf7<\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd2\xf2A%]\xd7\xc3\xf7<\a\x040q\xec\b\xdd\xd9\xc5\xcd\xe5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd2\xffg \x16\xf6;/\x859\x8fJo\xed\xbb`\xa5\r<\u0389\x12\x91$o[sJ\x00\x00\u07d4\xd3\rLC\xad\xcfU\xb2\xcbS\u0583#&A4I\x8d\x89\u038965\u026d\xc5\u07a0\x00\x00\u07d4\xd3\x0e\xe9\xa1+Mh\xab\xac\xe6\xba\u029a\u05ff\\\xd1\xfa\xf9\x1c\x89QO\xcb$\xff\x9cP\x00\x00\u07d4\xd3\x11\x8e\xa3\xc85\x05\xa9\u0613\xbbg\xe2\xde\x14-Sz>\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd3\x11\xbc\u05eaN\x9bO8?\xf3\xd0\u05b6\xe0~!\xe3p]\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd3\x15\xde\xea\x1d\x8c\x12q\xf9\xd11\x12c\xabG\xc0\a\xaf\xb6\xf5\x89\x03\xc8\x1dNeK@\x00\x00\u07d4\xd3+,y\xc3dx\xc5C\x19\x01\xf6\xd7\x00\xb0M\xbe\x9b\x88\x10\x89\x15w\x9a\x9d\xe6\xee\xb0\x00\x00\u07d4\xd3+EVF\x14Ql\x91\xb0\u007f\xa9\xf7-\xcfx|\xceN\x1c\x89\x0f\xc6o\xae7F\xac\x00\x00\u07d4\xd30r\x811\xfe\x8e:\x15Hz4W<\x93E~*\xfe\x95\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd31\xc8#\x82Z\x9eRc\xd0R\u0611]M\xcd\xe0z\\7\x89\x1e\x93\x12\x83\xcc\xc8P\x00\x00\u07d4\xd33btE\xf2\u05c7\x90\x1e\xf3;\xb2\xa8\xa3g^'\xff\xec\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd3<\xf8+\xf1LY&@\xa0\x86\b\x91L#py\u057e4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd3Mp\x8ds\x98\x02E3\xa5\xa2\xb20\x9b\x19\xd3\xc5Qq\xbb\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd3N\x03\xd3j+\xd4\u045a_\xa1b\x18\xd1\xd6\x1e?\xfa\v\x15\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\xd3Pu\xcaa\xfeY\xd1#\x96\x9c6\xa8-\x1a\xb2\xd9\x18\xaa8\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xd3g\x00\x9a\xb6X&;b\xc23:\x1c\x9eA@I\x8e\x13\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd3g\x9aG\xdf-\x99\xa4\x9b\x01\u024d\x1c>\f\x98|\xe1\xe1X\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94\u04cf\xa2\xc4\xcc\x14z\xd0j\u0562\xf7Uy(\x1f\"\xa7\xcc\x1f\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\u04da]\xa4`9+\x94\v\u01ee8\xf1e\u007f\x8a\x01f\xc5H\b\x89\xdbw\x00\x00\xe0\x94\xd3\xd6\xe9\xfb\x82T/\u049e\xd9\xea6\t\x89\x1e\x15\x13\x96\xb6\xf7\x8a\voX\x8a\xa7\xbc\xf5\xc0\x00\x00\xe0\x94\xd3\xda\u0476\u040dE\x81\u032ee\xa8s-\xb6\xaci\xf0\u019e\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xd3\xdf;S\xcb;GU\xdeT\xe1\x80E\x1c\xc4L\x9e\x8a\u0a89#\u0114\t\xb9w\x82\x80\x00\u07d4\xd3\xf8s\xbd\x99V\x13W\x89\xab\x00\xeb\xc1\x95\xb9\"\xe9K%\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\x02\xb4\xf6\xa0\x99\xeb\xe7\x16\xcb\x14\xdfOy\xc0\xcd\x01\xc6\a\x1b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd4\r\x00U\xfd\x9a8H\x8a\xff\x92?\xd0=5\xecF\xd7\x11\xb3\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\xd4\x0e\xd6j\xb3\xce\xff$\xca\x05\xec\xd4q\ufd12\xc1__\xfa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd4\x18\x87\v\xc2\xe4\xfa{\x8aa!\xae\br\xd5RG\xb6%\x01\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xd4\x1d\u007f\xb4\x9f\xe7\x01\xba\xac%qpBl\u0273\x8c\xa3\xa9\xb2\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4\xd4 U\x92\x84@U\xb3\u01e1\xf8\f\xef\xe3\xb8\xebP\x9b\xcd\xe7\x89\t\xb3\xbf\xd3B\xa9\xfc\x80\x00\u07d4\xd4+ \xbd\x03\x11`\x8bf\xf8\xa6\xd1[*\x95\xe6\xde'\u017f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd44O}\\\xade\xd1~\\-\x0es#\x94=ob\xfe\x92\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xd4>\xe48\xd8=\xe9\xa3ub\xbbN(l\xb1\xbd\x19\xf4\x96M\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd4C4\xb4\xe2:\x16\x9a\f\x16\xbd!\xe8f\xbb\xa5-\x97\x05\x87\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\xe0\x94\xd4M\x81\xe1\x8fF\xe2\u03f5\xc1\xfc\xf5\x04\x1b\xc8V\x97g\xd1\x00\x8a\a\xb4B\xe6\x84\xf6Z\xa4\x00\x00\u07d4\xd4OJ\xc5\xfa\xd7k\xdc\x157\xa3\xb3\xafdr1\x9bA\r\x9d\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\xd4O^\xdf+\xcf$3\xf2\x11\xda\xdd\f\xc4P\xdb\x1b\x00\x8e\x14\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xd4Oj\u00d2;_\xd71\xa4\xc4YD\xecO~\xc5*j\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd4[3A\xe8\xf1\\\x802\x93 \u00d7~;\x90\xe7\x82j~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd4]]\xaa\x13\x8d\xd1\xd3t\xc7\x1b\x90\x19\x91h\x11\xf4\xb2\nN\x89\x1f9\x9b\x148\xa1\x00\x00\x00\u07d4\xd4`\xa4\xb9\b\xdd+\x05gY\xb4\x88\x85\vf\xa88\xfcw\xa8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd4g\xcf\x06L\bq\x98\x9b\x90\u0632\xeb\x14\xcc\xc6;6\b#\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd4k\xaea\xb0'\xe5\xbbB.\x83\xa3\xf9\xc9?<\x8f\xc7}'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4o\x82#E)\x82\xa1\xee\xa0\x19\xa8\x81n\xfc-o\xc0\ah\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xd4uG\u007f\xa5c\x90\xd30\x17Q\x8dg\x11\x02\u007f\x05\U0008dfc9k\x11\x133\xd4\xfdL\x00\x00\u07d4\xd4|$.\xdf\xfe\xa0\x91\xbcT\xd5}\xf5\xd1\xfd\xb91\x01Gl\x89\x9d\xf7\u07e8\xf7`H\x00\x00\u07d4\xd4}\x86\x85\xfa\xee\x14|R\x0f\u0646p\x91u\xbf/\x88k\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\u007fP\u07c9\xa1\xcf\xf9e\x13\xbe\xf1\xb2\xae:)q\xac\xcf,\x89-\x89W}}@ \x00\x00\u07d4\u0502\xe7\xf6\x8eA\xf28\xfeQx)\xde\x15G\u007f\xe0\xf6\xdd\x1d\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u0507\x9f\xd1+\x1f:'\xf7\xe1\tv\x1b#\xca4\xfa#\x06K\x1c\xaf\x00Qn(pJ\x82\xa4\xf8\x89Hz\x9a0E9D\x00\x00\u07d4\xd5\x00\xe4\xd1\u0242K\xa9\xf5\xb65\u03e3\xa8\xc2\u00cb\xbdL\xed\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd5\b\u04dcp\x91oj\xbcL\xc7\xf9\x99\xf0\x11\xf0w\x10X\x02\x89\x05rM$\xaf\xe7\u007f\x00\x00\u07d4\xd5\x0f\u007f\xa0>8\x98v\u04d0\x8b`\xa57\xa6pc\x04\xfbV\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd5\x13\xa4P\x80\xff/\xeb\xe6,\u0545J\xbe)\xeeDg\xf9\x96\x89\bN\x13\xbcO\xc5\xd8\x00\x00\u07d4\xd5'o\f\xd5\xff\xd5\xff\xb6?\x98\xb5p=U\x94\xed\xe0\x83\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd5)KfbB0;m\xf0\xb1\u020d7B\x9b\xc8\xc9e\xaa\x89\x10M\r\x00\u04b7\xf6\x00\x00\u07d4\xd5*\xec\xc6I98\xa2\x8c\xa1\xc3g\xb7\x01\xc2\x15\x98\xb6\xa0.\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\xd5\x99x\xee \xa3\x8c?I\x8dc\xd5\u007f1\xa3\x9fj\x06\x8a\x022\xb3o\xfcg*\xb0\x00\x00\u07d4\u05568\xd3\xc5\xfa\xa7q\x1b\xf0\x85t_\x9d[\xdc#\u0518\u0609lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u055d\x92\xd2\xc8p\x19\x80\xcc\a<7]r\n\xf0dt<\f\x8a\x04\x05\xfd\xf7\u5bc5\xe0\x00\x00\u07d4\u0567\xbe\xc32\xad\xde\x18\xb3\x10KW\x92Tj\xa5\x9b\x87\x9bR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0571\x17\xec\x11n\xb8FA\x89a\xeb~\xdbb\x9c\xd0\xddi\u007f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\u0572\x84\x04\x010\xab\xf7\xc1\xd1cq#q\xcc~(\xadf\u0689j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0579\xd2w\u062a\xd2\x06\x97\xa5\x1fv\xe2\tx\x99k\xff\xe0U\x89\a\xc3\xfe<\aj\xb5\x00\x00\u07d4\u057d^\x84U\xc10\x16\x93W\xc4q\xe3\u6077\x99jrv\x89-\x9e(\x8f\x8a\xbb6\x00\x00\u07d4\xd5\u02e5\xb2k\xea]s\xfa\xbb\x1a\xba\xfa\xcd\xef\x85\xde\xf3h\u0309\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd5\xceU\u0476/YC\xc0?\x89\b\xe3\x1f\xe1h\x9d\x8a\x00\x00\u07d4\xd6\x06Q\xe3\x93x4#\xe5\xcc\x1b\xc5\xf8\x89\xe4N\xf7\xea$>\x89\x15\x9ev7\x11)\xc8\x00\x00\u07d4\xd6\t\xbfO\x14n\xeak\r\xc8\xe0m\xdc\xf4D\x8a\x1f\xcc\xc9\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\t\xec\v\xe7\r\n\xd2ong\xc9\xd4v+R\xeeQ\x12,\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd6\nRX\a(R\r\xf7Tk\xc1\xe2\x83)\x17\x88\u06ee\f\x8964\x89\xef?\xf0\xd7\x00\x00\u07d4\xd6\v$s!\xa3*Z\xff\xb9k\x1e'\x99'\xccXM\xe9C\x89z\xd0 \xd6\xdd\xd7v\x00\x00\u07d4\xd6\x11\x02v\xcf\xe3\x1eB\x82ZW\u007fkC]\xbc\xc1\f\xf7d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd6\x12Y{\xc3\x17C\u01c63\xf63\xf29\xb1\xe9Bk\xd9%\x8a\x10\x17\xf7\u07d6\xbe\x17\x80\x00\x00\u07d4\xd6#J\xafE\xc6\xf2.f\xa2%\xff\xb9:\xddb\x9bN\xf8\x0f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd6.\u06d6\xfc\u259a\xaflT^\x96|\xf1\xc0\xbc\x80R\x05\x89\x04\xa5eSjZ\u0680\x00\u07d4\xd60\v2\x15\xb1\x1d\xe7b\xec\xdeKp\xb7\x92}\x01)\x15\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd69]\xb5\xa4\xbbf\xe6\x0fL\xfb\xcd\xf0\x05{\xb4\xd9xb\xe2\x891T\xc9r\x9d\x05x\x00\x00\xe0\x94\xd6J-P\xf8\x85\x857\x18\x8a$\xe0\xf5\r\xf1h\x1a\xb0~\u05ca\b7Z*\xbc\xca$@\x00\x00\u07d4\xd6X\n\xb5\xedL}\xfaPo\xa6\xfed\xad\\\xe1)pw2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd6Y\x8b\x13\x86\xe9<\\\u02d6\x02\xffK\xbb\xec\xdb\xd3p\x1d\u0109\f%\xf4\xec\xb0A\xf0\x00\x00\u07d4\xd6dM@\xe9\v\xc9\u007f\xe7\xdf\xe7\u02bd2i\xfdW\x9b\xa4\xb3\x89\b\x9e\x91y\x94\xf7\x1c\x00\x00\xe0\x94\xd6g\f\x03m\xf7T\xbeC\xda\u074fP\xfe\xea(\x9d\x06\x1f\u058a\x01D\xa2\x904H\xce\xf7\x80\x00\u07d4\xd6hR:\x90\xf0)=e\xc58\xd2\xddlWg7\x10\x19n\x89\x02$,0\xb8S\xee\x00\x00\u07d4\xd6j\xb7\x92\x94\aL\x8bb}\x84-\xabA\xe1}\xd7\f]\xe5\x8965\u026d\xc5\u07a0\x00\x00\u0794\xd6j\xcc\r\x11\xb6\x89\u03a6\xd9\xea_\xf4\x01L\"J]\xc7\u0108\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xd6m\xdf\x11Y\xcf\"\xfd\x8czK\xc8\u0540wV\xd43\xc4>\x89wC\"\x17\xe6\x83`\x00\x00\u07d4\u0587\xce\xc0\x05\x90\x87\xfd\xc7\x13\xd4\xd2\xd6^w\xda\xef\xed\xc1_\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\u0588\xe7\x85\u024f\x00\xf8K:\xa1S3U\u01e2X\xe8yH\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u05a2.Y\x8d\xab\u04ce\xa6\xe9X\xbdy\u050d\u0756\x04\xf4\u07c965\u026d\xc5\u07a0\x00\x00\u07d4\u05a7\xacM\xe7\xb5\x10\xf0\xe8\xdeQ\x9d\x97?\xa4\xc0\x1b\xa84\x00\x89e\xea=\xb7UF`\x00\x00\u07d4\u05ac\xc2 \xba.Q\xdf\xcf!\xd4C6\x1e\xeav\\\xbd5\u0609\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u05ac\xff\u043f\u065c8.{\xd5o\xf0\xe6\x14J\x9eR\xb0\x8e\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xd6\xc0\u043c\x93\xa6.%qtp\x0e\x10\xf0$\u0232?\x1f\x87\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd6\xcf\\\x1b\u03dd\xa6b\xbc\xea\"U\x90P\x99\xf9\xd6\xe8M\u030a\x01\u011eB\x01W\xd9\xc2\x00\x00\u07d4\xd6\xd05r\xa4RE\xdb\xd46\x8cO\x82\xc9W\x14\xbd!g\xe2\x89?\x00\xc3\xd6f\x86\xfc\x00\x00\u07d4\xd6\xd6wiX\xee#\x14:\x81\xad\xad\xeb\b8 \t\xe9\x96\u0089\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd6\xd9\xe3\x0f\bB\x01*qv\xa9\x17\xd9\xd2\x04\x8c\xa0s\x87Y\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd6\xe0\x9e\x98\xfe\x13\x003!\x04\xc1\xca4\xfb\xfa\xc5T6N\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\xe8\xe9z\u90db\x9e\xe5\a\xee\xdb(\xed\xfbtw\x03\x149\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\uea18\u052e+q\x80'\xa1\x9c\xe9\xa5\xebs\x00\xab\xe3\u0289\x01}J\xce\xeec\u06c0\x00\xe0\x94\xd6\xf1\xe5[\x16\x94\b\x9e\xbc\xb4\xfe}x\x82\xaaf\u0217av\x8a\x04<#\xbd\xbe\x92\x9d\xb3\x00\x00\u07d4\xd6\xf4\xa7\xd0N\x8f\xaf \xe8\xc6\ub15c\xf7\xf7\x8d\xd2=z\x15\x89\a$\xde\xd1\xc7H\x14\x00\x00\u07d4\xd6\xfc\x04F\u01a8\xd4\n\xe3U\x1d\xb7\xe7\x01\xd1\xfa\x87nJI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\x03\u01a4\xf1\x1d`\x19Ey\u054c'f\xa7\xef\x16\xc3\n)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\x05%\x19uj\xf4%\x90\xf1S\x91\xb7#\xa0?\xa5d\xa9Q\x89\xfa61H\r\x01\xfd\x80\x00\u07d4\xd7\na+\xd6\u0769\xea\xb0\xdd\xdc\xffJ\xafA\"\u04cf\xea\xe4\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\xd7\n\xd2\xc4\xe9\uefe67\xefV\xbdHj\u04a1\xe5\xbc\xe0\x93\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\x14\f\x8eZC\a\xfa\xb0\xcc'\xba\u0752\x95\x01\x8b\xf8yp\x89\x05\xf1\x01kPv\xd0\x00\x00\u07d4\xd7\x16J\xa2a\xc0\x9a\u0672\xb5\x06\x8dE>\xd8\xebj\xa10\x83\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd7\x1eC\xa4Qw\xadQ\xcb\xe0\xf7!\x84\xa5\xcbP9\x17(Z\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\x1f\xb10\xf0\x15\fVRi\xe0\x0e\xfbC\x90+R\xa4U\xa6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\"W8\xdc\xf3W\x848\xf8\xe7\u0233\x83~B\xe0J&/\x89\x18+\x8c\ubec3\xaa\x00\x00\u07d4\xd7'MP\x80M\x9cw\u0693\xfaH\x01V\xef\xe5{\xa5\x01\u0789i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xd71\xbbk_<79^\t\u03ac\xcd\x14\xa9\x18\xa6\x06\a\x89\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xd7>\xd2\u0645\xb5\xf2\x1bU\xb2td;\xc6\xda\x03\x1d\x8e\u074d\x8a\nm\xd9\f\xaeQ\x14H\x00\x00\u07d4\xd7D\xac~S\x10\xbeijc\xb0\x03\xc4\v\xd097\x05a\u0189Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\xd7Jn\x8dj\xab4\u0385\x97h\x14\xc12{\xd6\xea\a\x84\u048a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xd7ZP*[gr\x87G\x0fe\u016aQ\xb8|\x10\x15\x05r\x8910\xb4dc\x85t\x00\x00\u07d4\xd7m\xba\xeb\xc3\rN\xf6{\x03\xe6\xe6\xec\xc6\xd8N\x00MP-\x89mv\xb9\x18\x8e\x13\x85\x00\x00\u07d4\xd7q\xd9\xe0\u028a\b\xa1\x13wW1CN\xb3'\x05\x99\xc4\r\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd7x\x8e\xf2\x86X\xaa\x06\xccS\xe1\xf3\xf0\xdeX\xe5\xc3q\xbex\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xd7x\x92\xe2';#]v\x89\xe40\xe7\xae\ud73c\xe8\xa1\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u05c1\xf7\xfc\t\x18F\x11V\x85p\xb4\x98n,r\x87+~\u0409\x01\x15\x95a\x06]]\x00\x00\u07d4\u05c5\xa8\xf1\x8c8\xb9\xbcO\xfb\x9b\x8f\xa8\xc7r{\xd6B\xee\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u05ce\xcd%\xad\xc8k\xc2\x05\x1d\x96\xf6Sd\x86kB\xa4&\xb7\x89\xd20X\xbf/&\x12\x00\x00\xe0\x94\u05cf\x84\xe3\x89D\xa0\xe0%_\xae\xceH\xbaIP\u053d9\u048a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\u05d4\x83\xf6\xa8DO%I\xd6\x11\xaf\xe0,C-\x15\xe1\x10Q\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u05d85\xe4\x04\xfb\x86\xbf\x84_\xba\t\rk\xa2^\f\x88f\xa6\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\u05da\xff\x13\xba-\xa7]F$\f\xac\n$g\xc6V\x94\x98#\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\u05dd\xb5\xabCb\x1az=\xa7\x95\xe5\x89)\xf3\xdd%\xafg\u0649lj\xccg\u05f1\xd4\x00\x00\u07d4\u05e1C\x1e\xe4S\xd1\xe4\x9a\x05P\xd1%hy\xb4\xf5\xd1\x02\x01\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\u05ed\t\xc6\xd3&WhSU\xb5\xc6\uc39fW\xb4\ube42\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u05f7@\xdf\xf8\xc4Wf\x8f\xdft\xf6\xa2f\xbf\xc1\u0737#\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd7\u0080>\u05f0\xe0\x83sQA\x1a\x8ef7\xd1h\xbc[\x05\x8a\x06A\xda\xf5\xc9\x1b\xd95\x80\x00\u07d4\xd7\xc6&]\xea\x11\x87l\x90;q\x8eL\u062b$\xfe&[\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xca\u007f\xdc\xfe\xbeE\x88\xef\xf5B\x1d\x15\"\xb6\x13(\xdf{\xf3\x89\xd8\xe6\x00\x1el0+\x00\x00\u07d4\xd7\u037dA\xff\xf2\r\xf7'\xc7\vbU\xc1\xbav\x06\x05Th\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\xd1W\xe4\xc0\xa9d7\xa6\u0485t\x1d\xd2>\xc46\x1f\xa3k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xd2\xc6\xfc\xa8\xad\x1fu9R\x10\xb5}\xe5\xdf\xd6s\x939\t\x89\x12nr\xa6\x9aP\xd0\x00\x00\xe0\x94\xd7\xd3\xc7Y Y\x048\xb8,>\x95\x15\xbe.\xb6\xedz\x8b\x1a\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\xd7\xd7\xf2\u02a4b\xa4\x1b;0\xa3J\xeb;\xa6\x10\x10\xe2bo\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xe7J\xfd\xba\xd5^\x96\u03bcZ7O,\x8bv\x86\x80\xf2\xb0\x89\x05]\xe6\xa7y\xbb\xac\x00\x00\xe0\x94\xd7\xeb\x901b'\x1c\x1a\xfa5\xfei\xe3s\"\u0224\u049b\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd7\xeb\u0779\xf99\x87w\x9bh\x01U7T8\xdbe\xaf\xcbj\x89\x05t\x1a\xfe\xff\x94L\x00\x00\u07d4\xd7\xef4\x0ef\xb0\u05ef\xcc\xe2\n\x19\xcb{\xfc\x81\xda3\xd9N\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd7\xf3p\u053e\xd9\xd5|oI\u0259\xder\x9e\xe5i\xd3\xf4\xe4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\xfa_\xfb`H\xf9o\xb1\xab\xa0\x9e\xf8{\x1c\x11\xddp\x05\xe4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd8\x06\x9f\x84\xb5!I?G\x15\x03\u007f2&\xb2_3\xb6\x05\x86\x89g\x8a\x93 b\xe4\x18\x00\x00\u0794\xd8\x15\xe1\xd9\xf4\xe2\xb5\xe5~4\x82k|\xfd\x88\x81\xb8Th\x90\x88\xf0\x15\xf2W6B\x00\x00\u07d4\xd8\x1b\xd5K\xa2\xc4Jok\xeb\x15a\u058b\x80\xb5DNm\u0189?\x17\r~\xe4\"\xf8\x9c\x80-1({\x96q\xe8\x1c\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xd8K\x92/xA\xfcWt\xf0\x0e\x14`J\xe0\xdfB\xc8U\x1e\x89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4\xd8U\xb0<\xcb\x02\x9awG\xb1\xf0s\x03\xe0\xa6dy59\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\xd8_\u07af*a\xf9]\xb9\x02\xf9\xb5\xa5<\x9b\x8f\x92f\u00ec\x89l\xf6Z~\x90G(\x00\x00\u07d4\xd8q^\xf9\x17o\x85\v.0\xeb\x8e8'\a\xf7w\xa6\xfb\xe9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd8t\xb9\u07eeEj\x92\x9b\xa3\xb1\xa2~W,\x9b,\xec\u07f3\x89\t79SM(h\x00\x00\u07d4\u0613\n9\xc7sW\xc3\n\u04e0`\xf0\v\x06\x04c1\xfdb\x89,s\xc97t,P\x00\x00\u07d4\u061b\xc2q\xb2{\xa3\xabib\xc9JU\x90\x06\xae8\xd5\xf5j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0637}\xb9\xb8\x1b\xbe\x90B{b\xf7\x02\xb2\x01\xff\u009f\xf6\x18\x892m\x1eC\x96\xd4\\\x00\x00\u07d4\xd8\xcdd\xe0(N\xecS\xaaF9\xaf\xc4u\b\x10\xb9\u007f\xabV\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd8\xd6C\x84$\x9bwg\x94\x06;V\x98x\xd5\xe3\xb50\xa4\xb2\x89\t\xa0C\u0432\xf9V\x80\x00\u07d4\xd8\xd6T \xc1\x8c#'\xccZ\xf9t%\xf8W\xe4\xa9\xfdQ\xb3\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xd8\xe5\xc9g^\xf4\xde\xed&k\x86\x95o\xc4Y\x0e\xa7\u0522}\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd8\xe8GB\x92\xe7\xa0Q`L\xa1d\xc0pw\x83\xbb(\x85\xe8\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\u07d4\xd8\xebxP>\xc3\x1aT\xa9\x016x\x1a\xe1\t\x00Lt2W\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd8\xee\xf4\xcfK\xeb\x01\xee \xd1\x11t\x8ba\xcbM?d\x1a\x01\x89\x94\x89#z\u06daP\x00\x00\u07d4\xd8\xf4\xba\xe6\xf8M\x91\rm}Z\xc9\x14\xb1\xe6\x83r\xf9A5\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd8\xf6 6\xf0;v5\xb8X\xf1\x10?\x8a\x1d\x90\x19\xa8\x92\xb6\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xd8\xf6e\xfd\x8c\xd5\u00bc\xc6\xdd\xc0\xa8\xaeR\x1eM\u01aa``\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xd8\xf9$\fU\xcf\xf05RB\x80\xc0\x9e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd8\xfe\b\x8f\xff\u0394\x8fQ7\xee#\xb0\x1d\x95\x9e\x84\xacB#\x89\f[T\xa9O\xc0\x17\x00\x00\u07d4\xd9\x0f0\t\xdbC~N\x11\u01c0\xbe\u0209os\x8de\xef\r\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd9\x10;\xb6\xb6zU\xa7\xfe\xce-\x1a\xf6-E|!x\x94m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd9\x13\xf0w\x19Iu\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd9D\u0226\x9f\xf2\xca\x12Ii\f\x12)\xc7\x19/6%\x10b\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd9JW\x88*Rs\x9b\xbe*\x06G\xc8\f$\xf5\x8a+O\x1c\x89H\xb5N*\xdb\xe1+\x00\x00\xe0\x94\xd9SB\x95<\x8a!\xe8\xb65\xee\xfa\u01c1\x9b\xea0\xf1pG\x8a\x13\xf0l\u007f\xfe\xf0]@\x00\x00\u07d4\xd9\\\x90\xff\xbeT\x84\x86G\x80\xb8gIJ\x83\u0212V\xd6\xe4\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\xd9g\x11T\x0e.\x99\x83C\xd4\xf5\x90\xb6\xfc\x8f\xac;\xb8\xb3\x1d\x89_Z@h\xb7\x1c\xb0\x00\x00\u07d4\xd9j\xc2Pt\t\u01e3\x83\xab.\xee\x18\"\xa5\xd78\xb3kV\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd9m\xb3;{Z\x95\f>\xfa-\xc3\x1b\x10\xba\x10\xa52\uf1c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd9wYe\xb7\x16Gfu\xa8\xd5\x13\xeb\x14\xbb\xf7\xb0|\xd1J\x8a\x01\x13.m-#\xc5\xe4\x00\x00\u07d4\xd9{\xc8J\xbdG\xc0[\xbfE{.\xf6Y\xd6\x1c\xa5\xe5\u43c9\x06\x9d\x17\x11\x9d\u0168\x00\x00\u07d4\xd9\u007fE&\u07a9\xb1c\xf8\xe8\xe3:k\u03d2\xfb\x90}\xe6\xec\x89\x0feJ\xafM\xb2\xf0\x00\x00\u07d4\xd9\u007f\xe6\xf5?*X\xf6\xd7mu*\xdft\xa8\xa2\xc1\x8e\x90t\x89\x10\xcd\xf9\xb6\x9aCW\x00\x00\u07d4\u0659\x99\xa2I\r\x94\x94\xa50\xca\xe4\xda\xf3\x85T\xf4\xddc>\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\u065d\xf7B\x1b\x93\x82\xe4,\x89\xb0\x06\xc7\xf0\x87p*\aW\xc0\x89\x1a\x05V\x90\xd9\u06c0\x00\x00\xe0\x94\u0677\x83\xd3\x1d2\xad\xc5\x0f\xa3\xea\u02a1]\x92\xb5h\xea\xebG\x8a\a3\xaf\x907L\x1b(\x00\x00\u07d4\xd9\xd3p\xfe\xc65v\xab\x15\xb3\x18\xbf\x9eX6M\u00a3U*\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xd9\xd4/\xd1>\xbdK\xf6\x9c\xac^\x9c~\x82H:\xb4m\xd7\xe9\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xd9\xe2~\xb0}\xfcq\xa7\x06\x06\f\u007f\a\x928\u0293\xe8\x859\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd9\xe3\x85~\xfd\x1e *D\x17p\xa7w\xa4\x9d\xccE\xe2\xe0\u04c9\f\x1d\xaf\x81\u0623\xce\x00\x00\u07d4\xd9\xec.\xfe\x99\xff\\\xf0\r\x03\xa81{\x92\xa2J\xefD\x1f~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd9\xec\x8f\xe6\x9bw\x16\xc0\x86Z\xf8\x88\xa1\x1b+\x12\xf7 \xed3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd9\xf1\xb2d\b\xf0\xecg\xad\x1d\ro\xe2.\x85\x15\xe1t\x06$\x89\x01M\x11 \u05f1`\x00\x00\u07d4\xd9\xf5G\xf2\xc1\xde\x0e\u064aS\xd1a\xdfWc]\xd2\x1a\x00\xbd\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xd9\xff\x11]\x01&l\x9fs\xb0c\xc1\xc28\xef5e\xe6;6\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\xda\x06\x04N)#&\xffil\x0091h\xceF\xff\xac9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda*\x14\xf9r@\x15\u05d0\x14\xed\x8eY\th\x1dYaH\xf1\x89\x02\xa1\x0f\x0f\x8a\x91\xab\x80\x00\u07d4\xda*\u054ew\xde\xdd\xed\xe2\x18vF\xc4e\x94Z\x8d\xc3\xf6A\x89#\xc7W\a+\x8d\xd0\x00\x00\u07d4\xda0\x17\xc1P\xdd\r\xce\u007f\u03c8\x1b\nH\xd0\xd1\xc7V\xc4\u01c9\x05k\xf9\x1b\x1ae\xeb\x00\x00\u07d4\xda4\xb2\xea\xe3\v\xaf\xe8\xda\xec\xcd\xe8\x19\xa7\x94\u0349\xe0\x95I\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdaJ_U\u007f;\xab9\n\x92\xf4\x9b\x9b\x90\n\xf3\fF\xae\x80\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdaPU7S\u007f\xfb3\xc4\x15\xfe\xc6Ni\xba\xe0\x90\xc5\xf6\x0f\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xdai\x8dd\xc6\\\u007f+,rS\x05\x9c\xd3\u0441\u0619\xb6\xb7\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\u07d4\xdaw2\xf0/.'.\xaf(\u07d7.\xcc\r\xde\xed\x9c\xf4\x98\x89\v \xbf\xbfig\x89\x00\x00\u07d4\xdaz\xd0%\xeb\xde%\xd2\"C\u02c3\x0e\xa1\xd3\xf6JVc#\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\u0685]SG\u007fP^\xc4\xc8\xd5\u8ed1\x80\u04c6\x81\x11\x9c\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\u0687^N/<\xab\xe4\xf3~\x0e\xae\xd7\xd1\xf6\xdc\xc6\xff\xefC\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u068b\xbe\xe1\x82\xe4U\xd2\t\x8a\xcb3\x8amE\xb4\xb1~\u0636\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0698.\x96C\xff\xec\xe7#\aZ@\xfewnZ\xce\x04\xb2\x9b\x89\b\xb8\xb6\u0259\x9b\xf2\x00\x00\u07d4\u069fUF\tF\u05ff\xb5p\xdd\xecu|\xa5w;XB\x9a\x89\x1b\x84]v\x9e\xb4H\x00\x00\u07d4\u06a1\xbdz\x91H\xfb\x86\\\xd6\x12\xdd5\xf1b\x86\x1d\x0f;\u0709\xa68\xabr\xd9,\x13\x80\x00\xe0\x94\u06a6<\xbd\xa4]\u0507\xa3\xf1\xcdJtj\x01\xbb^\x06\v\x90\x8a\x01\x04\x16\u0670*\x89$\x00\x00\u07d4\u06a7v\xa6uDi\u05f9&z\x89\xb8g%\xe7@\xda\x0f\xa0\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u06ac\x91\xc1\xe8Y\xd5\xe5~\xd3\bKP \x0f\x97f\xe2\xc5+\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u06ac\xda\xf4\"&\xd1\\\xb1\u03d8\xfa\x15\x04\x8c\u007fL\xee\xfei\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\u06b6\xbc\u06c3\xcf$\xa0\xae\x1c\xb2\x1b;[\x83\xc2\xf3\x82I'\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\u06bb\b\x89\xfc\x04)&\xb0^\xf5{% \x91\n\xbcKAI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06bc\"PB\xa6Y,\xfa\x13\xeb\xe5N\xfaA\x04\bx\xa5\xa2\x89\x0e\x11\xfa\xd5\xd8\\\xa3\x00\x00\u07d4\xda\xc0\xc1w\xf1\x1c\\>>x\xf2\xef\xd6c\xd12!H\x85t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\xd16\xb8\x81x\xb4\x83zlx\x0f\xeb\xa2&\xb9\x85i\xa9L\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xda\xdb\xfa\xfd\x8bb\xb9*$\xef\xd7RV\u0743\xab\xdb\u05fb\u06c9\x01\x11du\x9f\xfb2\x00\x00\u07d4\xda\xdc\x00\xaby'`\xaa4\x15i\xfa\x9f\xf5\x98&\x84\x85JJ2\x8an\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\xe7 \x1e\xab\x8c\x063\x02\x93\ri9)\xd0\u007f\x95\xe7\x19b\x89\x91\xae\xc0(\xb4\x19\x81\x00\x00\u07d4\xda\xed\u052d\x10{'\x1e\x89Hl\xbf\x80\xeb\xd6!\u0757Ex\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x04\xfa\xd9\u011f\x9e\x88\v\xeb\x8f\xcf\x1d:8\x90\u4cc4o\x89CZ\xe6\xcc\fX\xe5\x00\x00\u07d4\xdb\f\u01cft\u0642{\u070ads'n\xb8O\u0717b\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x12\x93\xa5\x06\xe9\f\xad*Y\xe1\xb8V\x1f^f\x96\x1ag\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x19\xa3\x98\"06\x8f\x01w!\x9c\xb1\f\xb2Y\u0372%|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb#\xa6\xfe\xf1\xaf{X\x1ew,\xf9\x18\x82\u07b2Qo\xc0\xa7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb$O\x97\xd9\xc4K\x15\x8a@\xed\x96\x06\xd9\xf7\xbd8\x9131\x89\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xdb(\x8f\x80\xff\xe22\u00baG\u0314\xc7c\xcfo\u0278+\r\x89\x04\x9b\x9c\xa9\xa6\x944\x00\x00\u07d4\xdb*\f\x9a\xb6M\xf5\x8d\u07f1\u06ec\xf8\xba\r\x89\xc8[1\xb4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdb4t^\u0785v\xb4\x99\xdb\x01\xbe\xb7\xc1\xec\u0685\xcfJ\xbe\x89\x04V9\x18$O@\x00\x00\u07d4\xdb?%\x8a\xb2\xa3\xc2\xcf3\x9cD\x99\xf7ZK\xd1\xd3G.\x9e\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xdbK\xc8;\x0ek\xaa\xdb\x11V\xc5\xcf\x06\xe0\xf7!\x80\x8cR\u01c9/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xdbc\x12-\xe7\x03}\xa4\x97\x151\xfa\u9bc5\x86x\x86\u0192\x89\x0f\x04%\xb0d\x1f4\x00\x00\u07d4\xdbl*s\xda\xc7BJ\xb0\xd01\xb6ga\x12%f\xc0\x10C\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xdbnV\f\x9b\xc6 \u053e\xa3\xa9MG\xf7\x88\v\xf4\u007f-_\x89\x04\xda\x0f\xdf\xcf\x05v\x00\x00\u07d4\xdbo\xf7\x1b=\xb0\x92\x8f\x83\x9e\x05\xa72;\xfbW\u049c\x87\xaa\x891T\xc9r\x9d\x05x\x00\x00\u07d4\xdbsF\vY\xd8\xe8PE\xd5\xe7R\xe6%Y\x87^BP.\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\xdbw\xb8\x8d\xcbq/\xd1~\xe9\x1a[\x94t\x8dr\f\x90\xa9\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb}@7\b\x1fle\xf9Gk\x06\x87\xd9\u007f\x1e\x04M\n\x1d\x89#\xc7W\a+\x8d\xd0\x00\x00\xe0\x94\u06c8.\xac\xed\xd0\xef\xf2cQ\x1b1*\u06fcY\u01b8\xb2[\x8a\x01\xedO\xdez\"6\xb0\x00\x00\u07d4\u06d3q\xb3\fL\x84NY\xe0>\x92K\xe6\x06\xa98\xd1\xd3\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06e4ym\f\xebM:\x83k\x84\xc9o\x91\n\xfc\x10?[\xa0\x89\t\b\xf4\x93\xf77A\x00\x00\u07d4\u06ed\xc6\x1e\xd5\xf0F\n\u007f\x18\xe5\x1b/\xb2aM\x92d\xa0\xe0\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u06f6\xacH@'\x04\x16B\xbb\xfd\x8d\x80\xf9\xd0\xc1\xcf3\xc1\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06fc\xbby\xbfG\x9aB\xadq\xdb\u02b7{Z\u07ea\x87,X\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xdb\xc1\xce\x0eI\xb1\xa7\x05\xd2. 7\xae\xc8x\xee\ru\xc7\x03\x89\r\x8drkqw\xa8\x00\x00\u07d4\xdb\xc1\xd0\xee+\xabS\x11@\xde\x13w\"\xcd6\xbd\xb4\xe4q\x94\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb\u015e\u0609s\u07ad1\b\x84\":\xf4\x97c\xc0P0\xf1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\xdb\xc6ie\xe4&\xff\x1a\xc8z\xd6\xebx\xc1\xd9Rq\x15\x8f\x9f\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xdb\xcb\xcdzW\ua7724\x9b\x87\x8a\xf3K\x1a\xd6B\xa7\xf1\u0449\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb\xd5\x1c\xdf,;\xfa\xcd\xff\x10b!\xde.\x19\xadmB\x04\x14\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xdb\xd7\x1e\xfaK\x93\u0209\xe7e\x93\xde`\x9c;\x04\u02ef\xbe\b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xdb\xf5\xf0a\xa0\xf4\x8e^ia\x879\xa7}.\xc1\x97h\xd2\x01\x89\b=lz\xabc`\x00\x00\u07d4\xdb\xf8\xb19g\xf5Q%'-\xe0V%6\xc4P\xbaVU\xa0\x89n\xf5x\xf0n\f\xcb\x00\x00\u07d4\xdb\xfb\x1b\xb4d\xb8\xa5\x8eP\r.\xd8\u0797,E\xf5\xf1\xc0\xfb\x89V\xbcu\xe2\xd61\x00\x00\x00\xe0\x94\xdc\x06~\xd3\xe1-q\x1e\xd4u\xf5\x15n\xf7\xe7\x1a\x80\xd94\xb9\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xdc\b\u007f\x93\x90\xfb\x9e\x97j\xc2:\xb6\x89TJ\tB\xec !\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xdc\x1e\xb9\xb6\xe6CQ\xf5d$P\x96E\xf8>y\xee\xe7l\xf4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdc\x1f\x19ya_\b!@\xb8\xbbx\xc6{'\xa1\x94'\x13\xb1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xdc#\xb2`\xfc\xc2n}\x10\xf4\xbd\x04J\xf7\x94W\x94`\xd9\u0689\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xdc)\x11\x97E\xd23s \xdaQ\xe1\x91\x00\xc9H\u0640\xb9\x15\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xdc-\x15\xa6\x9fk\xb3;$j\xef@E\aQ\xc2\xf6uj\u0489l4\x10\x80\xbd\x1f\xb0\x00\x00\u07d4\xdc=\xaeY\xed\x0f\xe1\x8bXQ\x1eo\xe2\xfbi\xb2\x19h\x94#\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xdc?\x0evr\xf7\x1f\xe7R[\xa3\v\x97U\x18: \xb9\x16j\x8a\x02\b\x9c\xf5{[>\x96\x80\x00\xe0\x94\xdcCE\u0581.\x87\n\xe9\fV\x8cg\xd2\xc5g\u03f4\xf0<\x8a\x01k5-\xa5\xe0\xed0\x00\x00\u07d4\xdcD'[\x17\x15\xba\xea\x1b\x03EsZ)\xacB\xc9\xf5\x1bO\x89?\x19\xbe\xb8\xdd\x1a\xb0\x00\x00\u07d4\xdcF\xc13%\u034e\xdf\x020\xd0h\x89d\x86\xf0\a\xbfN\xf1\x89Hz\x9a0E9D\x00\x00\u07d4\xdcQ\xb2\u071d$z\x1d\x0e[\xc3l\xa3\x15oz\xf2\x1f\xf9\xf6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xdcS\x05\xb4\x02\n\x06\xb4\x9de||\xa3L5\xc9\x1c_,V\x8a\x01}\xf6\xc1\r\xbe\xba\x97\x00\x00\u07d4\xdcW4[8\xe0\xf0g\u0263\x1d\x9d\xea\xc5'Z\x10\x94\x93!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdcWG}\xaf\xa4/p\\\u007f\xe4\x0e\xae\x9c\x81un\x02%\xf1\x89\x1b\x1b\x81(\xa7An\x00\x00\u07d4\xdc_Z\xd6c\xa6\xf2c2}d\xca\xc9\xcb\x13=,\x96\x05\x97\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdcp:_7\x94\xc8Ml\xb3TI\x18\xca\xe1J5\u00fdO\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94\xdcs\x8f\xb2\x17\u03ad/iYL\b\x17\r\xe1\xaf\x10\xc4\x19\xe3\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xdcv\xe8[\xa5\v\x9b1\xec\x1e& \xbc\xe6\xe7\xc8\x05\x8c\x0e\xaf\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0703\xb6\xfd\rQ!1 G\a\xea\xf7.\xa0\xc8\u027e\xf9v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u070c)\x12\xf0\x84\xa6\u0444\xaasc\x85\x13\u033c2n\x01\x02\x89F3\xbc6\xcb\xc2\xdc\x00\x00\u07d4\u0711\x1c\xf7\xdc]\u04016Vg\x05(\xe93\x8eg\x03G\x86\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0730;\xfal\x111#NV\xb7\xea|Or\x14\x87Tkz\x89Hz\x9a0E9D\x00\x00\xe0\x94\u0736M\xf47X\xc7\u03d7O\xa6`HO\xbbq\x8f\x8cg\xc1\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xdc\xc5-\x8f\x8d\x9f\xc7B\xa8\xb8'g\xf0US\x87\xc5c\xef\xff\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xdc\xcb7\x0e\u058a\xa9\"(0C\xef|\xad\x1b\x9d@?\xc3J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdc\u0324 E\xec>\x16P\x8b`?\xd96\xe7\xfd}\xe5\xf3j\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xdc\xd1\fU\xbb\x85OuD4\xf1!\x9c,\x9a\x98\xac\xe7\x9f\x03\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xdc\u057c\xa2\x00S\x95\xb6u\xfd\xe5\x03VY\xb2k\xfe\xfcI\xee\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xdc\u06fdN&\x04\xe4\x0e\x17\x10\xccg0(\x9d\xcc\xfa\u04c9-\x89\xf9]\xd2\xec'\xcc\xe0\x00\x00\u07d4\xdc\xe3\f1\xf3\xcafr\x1e\xcb!<\x80\x9a\xabV\x1d\x9bR\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdc\xf39eS\x13\x80\x161h\xfc\x11\xf6~\x89\xc6\xf1\xbc\x17\x8a\x89\x12'v\x854\x06\xb0\x80\x00\u07d4\xdc\xf6\xb6W&n\x91\xa4\xda\xe6\x03=\xda\xc1S2\u074d+4\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xdc\xf9q\x9b\xe8|oFum\xb4\x89\x1d\xb9\xb6\x11\xd2F\x9cP\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xdc\xff\xf3\xe8\xd2<*4\xb5k\u0473\xbdE\u01d3tC\"9\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xdd\x04\xee\xe7N\v\xf3\f?\x8dl,\u007fR\xe0Q\x92\x10\u07d3\x89\x04V9\x18$O@\x00\x00\xe0\x94\xdd&\xb4)\xfdC\xd8N\xc1y\x82S$\xba\u057f\xb9\x16\xb3`\x8a\x01\x16\xbf\x95\xbc\x842\x98\x00\x00\u07d4\xdd*#:\xde\xdef\xfe\x11&\xd6\xc1h#\xb6*\x02\x1f\xed\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xdd+\u07e9\x17\xc1\xf3\x10\xe6\xfa5\xaa\x8a\xf1i9\xc23\xcd}\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xdd5\xcf\xdb\u02d93\x95Sz\xec\xc9\xf5\x90\x85\xa8\xd5\u0776\xf5\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xddG\x18\x9a>d9qg\xf0b\x0eHEe\xb7b\xbf\xbb\xf4\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xddM\xd6\xd3`3\xb0co\u030d\t8`\x9fM\xd6OJ\x86\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xddO_\xa2\x11\x1d\xb6\x8fk\xde5\x89\xb60)9[i\xa9-\x89\b\x96=\xd8\xc2\xc5\xe0\x00\x00\xe0\x94\xddc\x04/%\xed2\x88J\xd2n:\xd9Y\xeb\x94\xea6\xbfg\x8a\x04\x84\xd7\xfd\xe7\u0553\xf0\x00\x00\u07d4\xdde\xf6\xe1qc\xb5\xd2\x03d\x1fQ\xcc{$\xb0\x0f\x02\xc8\xfb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xddl\x06!\x93\xea\xc2=/\xdb\xf9\x97\xd5\x06:4k\xb3\xb4p\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdd{\u0366Y$\xaa\xa4\x9b\x80\x98J\xe1su\x02X\xb9(G\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdd\u007f\xf4A\xbao\xfe6q\xf3\xc0\u06bb\xff\x18#\xa5\x043p\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0742T\x12\x1an\x94/\xc9\b(\xf2C\x1fQ\x1d\xad\u007f2\u6263\x9b)\xe1\xf3`\xe8\x00\x00\xe0\x94\u074a\xf9\xe7vR#\xf4DoD\xd3\xd5\t\x81\x9a==\xb4\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\u0755\xdb\xe3\x0f\x1f\x18w\xc5\xddv\x84\xae\xef0*\xb6\x88Q\x92\x8a\x01\xc5\xd8\xd6\xeb>2P\x00\x00\xe0\x94\u0756|L_\x8a\xe4~&o\xb4\x16\xaa\u0456N\xe3\xe7\xe8\u00ca\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\u075bHZ;\x1c\xd3:j\x9cb\xf1\xe5\xbe\xe9'\x01\x85m%\x89\f3\x83\xed\x03\x1b~\x80\x00\xe0\x94\u0763q\xe6\x00\xd3\x06\x88\xd4q\x0e\b\x8e\x02\xfd\xf2\xb9RM_\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4\u0764\xed*X\xa8\xdd \xa72u4{X\rq\xb9[\xf9\x9a\x89\x15\xa1<\xc2\x01\xe4\xdc\x00\x00\xe0\x94\u0764\xff}\xe4\x91\u0187\xdfEt\xdd\x1b\x17\xff\x8f$k\xa3\u044a\x04&\x84\xa4\x1a\xbf\xd8@\x00\x00\u07d4\u076bkQ\xa9\x03\v@\xfb\x95\xcf\vt\x8a\x05\x9c$\x17\xbe\u01c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u076bu\xfb/\xf9\xfe\u02c8\xf8\x94vh\x8e+\x00\xe3g\xeb\xf9\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\xe0\x94\u076b\xf1<<\x8e\xa4\xe3\xd7=x\xecqz\xfa\xfaC\x0eTy\x8a\b\xcf#\xf9\t\xc0\xfa\x00\x00\x00\u07d4\u076c1*\x96UBj\x9c\f\x9e\xfa?\xd8%Y\xefE\x05\xbf\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\u076ck\xf4\xbb\xdd}Y}\x9chm\x06\x95Y;\xed\xcc\xc7\xfa\x89.\xe4IU\b\x98\xe4\x00\x00\xe0\x94\u077d+\x93,v;\xa5\xb1\xb7\xae;6.\xac>\x8d@\x12\x1a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u077d\xdd\x1b\xbd8\xff\xad\xe00]0\xf0 (\xd9.\x9f:\xa8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u077e\xe6\xf0\x94\xea\xe64 \xb0\x03\xfbGW\x14*\xeal\xd0\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdd\u059c[\x9b\xf5\xebZ9\xce\xe7\xc34\x1a\x12\r\x97?\xdb4\x89k\xc1K\x8f\x8e\x1b5\x00\x00\xe0\x94\xdd\xdd{\x9en\xab@\x9b\x92&:\xc2r\u0680\x1bfO\x8aW\x8ai\xe1\r\xe7fv\u0400\x00\x00\u07d4\xdd\xe6p\xd0\x169fuv\xa2-\xd0]2F\xd6\x1f\x06\xe0\x83\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94\xdd\xe7zG@\xba\b\xe7\xf7?\xbe:\x16t\x91)1t.\xeb\x8a\x044\xfeMC\x82\xf1\u0500\x00\u07d4\xdd\xe8\xf0\xc3\x1bt\x15Q\x1d\xce\xd1\xcd}F2>K\xd1\"2\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\xdd\xe9i\xae\xf3N\xa8z\u0099\xb7Y~)+J\x01U\u030a\x89\x102\xf2YJ\x01s\x80\x00\u07d4\xdd\xf0\xcc\xe1\xfe\x99m\x91v5\xf0\a\x12\xf4\x05 \x91\xdf\xf9\xea\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdd\xf3\xadv58\x10\xbej\x89\xd71\xb7\x87\xf6\xf1q\x88a+\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xdd\xf5\x81\n\x0e\xb2\xfb.22;\xb2\u0255\t\xab2\x0f$\xac\x8a\x03\xca\\f\u067cD0\x00\x00\xe0\x94\xdd\xf9\\\x1e\x99\xce/\x9fV\x98\x05|\x19\xd5\xc9@'\xeeJn\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\xdd\xfa\xfd\xbc|\x90\xf12\x0eT\xb9\x8f7F\x17\xfb\xd0\x1d\x10\x9f\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xdd\xfc\xca\x13\xf94\xf0\u03fe#\x1d\xa109\xd7\x04u\xe6\xa1\u040968\"\x16`\xa5\xaa\x80\x00\u07d4\xde\x02~\xfb\xb3\x85\x03\"n\xd8q\t\x9c\xb3\v\xdb\x02\xaf\x135\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xde\x06\xd5\xeawzN\xb1G^`]\xbc\xbfCDN\x807\xea\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xde\a\xfb[zFN;\xa7\xfb\xe0\x9e\x9a\xcb'\x1a\xf53\x8cX\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xde\x11!\x82\x9c\x9a\b(@\x87\xa4?\xbd/\xc1\x14*23\xb4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xde\x17kR\x84\xbc\xee:\x83\x8b\xa2Og\xfc|\xbfg\u05ce\xf6\x89\x02\t\xce\b\xc9b\xb0\x00\x00\u07d4\xde!\"\x93\xf8\xf1\xd21\xfa\x10\xe6\tG\rQ,\xb8\xff\xc5\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde0\xe4\x9eZ\xb3\x13!M/\x01\u072b\u0389@\xb8\x1b\x1cv\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xde3\xd7\b\xa3\xb8\x9e\x90\x9e\xafe;0\xfd\u00e5\xd5\u0334\xb3\x89\t\x9c\x88\"\x9f\xd4\xc2\x00\x00\u07d4\xde7B\x99\xc1\xd0}ySs\x85\x19\x0fD.\xf9\xca$\x06\x1f\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xdeB\xfc\xd2L\xe4#\x93\x830CgY_\x06\x8f\fa\a@\x89\x02r*p\xf1\xa9\xa0\x00\x00\u07d4\xdeP\x86\x8e\xb7\xe3\xc7\x197\xecs\xfa\x89\u074b\x9e\xe1\rE\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xdeU\xde\x04X\xf8P\xb3~Mx\xa6A\xdd.\xb2\u074f8\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xde[\x00_\xe8\u06ae\x8d\x1f\x05\xde>\xda\x04 f\xc6\xc4i\x1c\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\xdea-\a$\xe8N\xa4\xa7\xfe\xaa=!B\xbd^\xe8-2\x01\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdem61\x06\xccb8\xd2\xf0\x92\xf0\xf07!6\xd1\xcdP\u018a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xde}\xee\"\x0f\x04W\xa7\x18}V\xc1\xc4\x1f.\xb0\n\xc5`!\x89\"%\xf3\x9c\x85\x05*\x00\x00\u07d4\u0782\u030dJ\x1b\xb1\xd9CC\x92\x96[>\x80\xba\xd3\xc0=O\x89P\x18nu\u0797\xa6\x00\x00\u07d4\u0797\xf43\a\x00\xb4\x8cImC|\x91\xca\x1d\xe9\u0130\x1b\xa4\x89\x9d\xcc\x05\x15\xb5n\f\x00\x00\u07d4\u079e\xffLy\x88\x11\xd9h\xdc\xcbF\r\x9b\x06\x9c\xf3\x02x\xe0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u07b1\xbc4\xd8mJM\xde%\x80\u063e\xaf\aN\xb0\xe1\xa2D\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\u07b2I]j\xca{*j-\x13\x8bn\x1aB\xe2\xdc1\x1f\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\u07b9rTGL\r/Zyp\xdc\xdb/R\xfb\x10\x98\xb8\x96\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u07b9\xa4\x9aC\x870 \xf0u\x91\x85\xe2\v\xbbL\U000c1ecf\x89\vx\xed\xb0\xbf.^\x00\x00\u07d4\u07bb\u0743\x1e\x0f \xaen7\x82R\xde\xcd\xf9/|\xf0\xc6X\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde\xc3\xee\xc2d\nu,Fn+~~\u616f\xe9\xacA\xf4\x89G\u0257SYk(\x80\x00\u07d4\xde\xc8#s\xad\xe8\xeb\xcf*\xcbo\x8b\xc2AM\u05eb\xb7\rw\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xde\u0221\xa8\x98\xf1\xb8\x95\xd80\x1f\xe6J\xb3\xad]\xe9A\xf6\x89\x89*\xb4\xf6~\x8as\x0f\x80\x00\u07d4\xde\u025e\x97/\xcaqwP\x8c\x8e\x1aG\xac\"\xd7h\xac\xab|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde\xd8w7\x84\a\xb9Nx\x1cN\xf4\xaf|\xfc[\xc2 \xb5\x16\x89\x141y\xd8i\x11\x02\x00\x00\u07d4\xde\xe9B\xd5\xca\xf5\xfa\xc1\x14!\xd8k\x01\vE\x8e\\9)\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xde\xee&\x89\xfa\x90\x06\xb5\x9c\xf2\x85#}\xe5;:\u007f\xd0\x148\x89\x18ey\xf2\x9e %\x00\x00\u07d4\xde\xfd\xdf\u055b\x8d,\x15N\xec\xf5\xc7\xc1g\xbf\v\xa2\x90]>\x89\x05\x12\xcb^&GB\x00\x00\u07d4\xde\xfe\x91A\xf4pE\x99\x15\x9d{\"=\xe4+\xff\xd8\x04\x96\xb3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xdf\t\x8f^N=\xff\xa5\x1a\xf27\xbd\xa8e,Os\ud726\x89\x1b6\xa6DJ>\x18\x00\x00\xe0\x94\xdf\r\ba{\xd2R\xa9\x11\u07cb\xd4\x1a9\xb8=\u07c0\x96s\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdf\x0f\xf1\xf3\xd2z\x8e\xc9\xfb\x8fk\f\xb2T\xa6;\xba\x82$\xa5\x89\xec\xc5 )E\xd0\x02\x00\x00\u07d4\xdf\x1f\xa2\xe2\x0e1\x98^\xbe,\x0f\f\x93\xb5L\x0f\xb6z&K\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdf!\x1c\xd2\x12\x88\xd6\xc5o\xaef\xc3\xffTb]\u0531T'\x89\x87\x86\xcdvN\x1f,\x00\x00\u07d4\xdf#k\xf6\xab\xf4\xf3)7\x95\xbf\f(q\x8f\x93\u3c73k\x89Hz\x9a0E9D\x00\x00\u07d4\xdf1\x02_VI\xd2\xc6\xee\xa4\x1e\u04fd\xd3G\x1ay\x0fu\x9a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdf7\xc2.`:\xed\xb6\nbrS\xc4}\x8b\xa8f\xf6\xd9r\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4\xdf;r\u017dq\u0501N\x88\xa6#!\xa9=@\x11\xe3W\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdf?W\xb8\xeed4\xd0G\"=\xeft\xb2\x0fc\xf9\xe4\xf9U\x89\r\x94b\xc6\xcbKZ\x00\x00\u07d4\xdfD\xc4\u007f\xc3\x03\xacv\xe7O\x97\x19L\xcag\xb5\xbb<\x02?\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\xdfG\xa6\x1brSQ\x93\xc5a\xcc\xccu\xc3\xf3\xce\b\x04\xa2\x0e\x89\x15\x93\\\vN=x\x00\x00\u07d4\xdfG\xa8\xef\x95\xf2\xf4\x9f\x8eoX\x18AT\x14]\x11\xf7'\x97\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xdfS\x003F\xd6\\^zdk\xc04\xf2\xb7\xd3/\xcb\xe5j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdfW5:\xaf\xf2\xaa\xdb\n\x04\xf9\x01N\x8d\xa7\x88N\x86X\x9c\x89\bH\x86\xa6nO\xb0\x00\x00\u07d4\xdf`\xf1\x8c\x81*\x11\xedN'v\xe7\xa8\x0e\xcf^S\x05\xb3\u05890\xca\x02O\x98{\x90\x00\x00\u07d4\xdfd\x85\xc4)z\xc1R\xb2\x89\xb1\x9d\xde2\xc7~\xc4\x17\xf4}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xdff\n\x91\u06b9\xf70\xf6\x19\rP\xc89\x05aP\aV\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\xdfn\xd6\x00jj\xbe\x88n\xd3=\x95\xa4\xde(\xfc\x12\x189'\x891T\xc9r\x9d\x05x\x00\x00\u07d4\u07c5\x10y>\xee\x81\x1c-\xab\x1c\x93\xc6\xf4G?0\xfb\xef[\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u07cdH\xb1\xeb\a\xb3\xc2\x17y\x0el-\xf0M\xc3\x19\xe7\xe8H\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u07e6\xb8\xb8\xad1\x84\xe3W\xda()Q\u05d1a\u03f0\x89\xbc\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\u07ef1\xe6\"\xc0=\x9e\x18\xa0\u0778\xbe`\xfb\xe3\xe6a\xbe\n\x8a\x02\x1e\x17\x1a>\xc9\xf7,\x00\x00\u07d4\u07f1bn\xf4\x8a\x1d}uR\xa5\xe0)\x8f\x1f\xc2:;H-\x89\\\xe8\x95\u0754\x9e\xfa\x00\x00\xe0\x94\u07f4\u052d\xe5/\u0301\x8a\xccz,k\xb2\xb0\x02$e\x8fx\x8a\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\u07fdB2\xc1|@z\x98\r\xb8\u007f\xfb\u036060\xe5\xc4Y\x89\x1d\xfc\u007f\x92I#S\x00\x00\u07d4\xdf\xcb\xdf\tEN\x1a^J@\xd3\xee\xf7\xc5\xcf\x1c\xd3\u0794\x86\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdf\xdb\xce\xc1\x01K\x96\xda!X\xcaQ>\x9c\x8d;\x9a\xf1\xc3\u0409lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdf\xde\xd2WK'\xd1a:}\x98\xb7\x15\x15\x9b\r\x00\xba\xab(\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xdf\xdfC9P\x8b\x0fnZ\xb1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe0\x06\x04b\xc4\u007f\xf9g\x9b\xae\xf0qY\xca\xe0\x8c)\xf2t\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\r\x15;\x106\x91C\xf9\u007fT\xb8\xd4\xca\"\x9e\xb3\xe8\xf3$\x89\b=lz\xabc`\x00\x00\u07d4\xe0\x12\xdbE8'\xa5\x8e\x16\xc16V\b\xd3n\xd6Xr\x05\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x15G\xbaB\xfc\xaf\xaf\x93\x93\x8b\xec\xf7i\x9ft)\n\xf7O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x16\xdc\x13\x8e%\x81[\x90\xbe?\xe9\xee\xe8\xff\xb2\xe1\x05bO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0\x18Y\xf2B\xf1\xa0\xec`/\xa8\xa3\xb0\xb5v@\xec\x89\a^\x89\x1e\x16,\x17{\xe5\xcc\x00\x00\xe0\x94\xe0 \xe8cb\xb4\x87u(6\xa6\xde\v\xc0,\xd8\u061a\x8bj\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xe0#\xf0\x9b(\x87a,|\x9c\xf1\x98\x8e::`+3\x94\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0'\"\x13\xe8\xd2\xfd>\x96\xbdb\x17\xb2KK\xa0\x1bapy\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe0+t\xa4v(\xbe1[\x1fv\xb3\x15\x05J\xd4J\xe9qo\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xe02 \u0197\xbc\u048f&\xef\vt@J\x8b\xeb\x06\xb2\xba{\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe05/\u07c1\x9b\xa2e\xf1L\x06\xa61\\J\xc1\xfe\x13\x1b.\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe08\x8a\xed\xdd?\xe2\xadV\xf8WH\xe8\x0eq\n4\xb7\xc9.\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0<\x00\xd0\x03\x88\xec\xbfO&=\n\xc7x\xbbA\xa5z@\u064966\xc9yd6t\x00\x00\u07d4\xe0I \xdcn\xcc\x1dn\xcc\bO\x88\xaa\n\xf5\u06d7\xbf\x89:\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xe0Ir\xa8<\xa4\x11+\xc8q\xc7-J\xe1al/\a(\u06c9\x0e\x81\xc7\u007f)\xa3/\x00\x00\u07d4\xe0O\xf5\xe5\xa7\u2bd9]\x88W\xce\x02\x90\xb5:+\x0e\xda]\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xe0P)\xac\xeb\axg[\xef\x17A\xab,\u0493\x1e\xf7\xc8K\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4\xe0V\xbf?\xf4\x1c&%o\xefQqf\x12\xb9\u04da\u0799\x9c\x89\x05k\xe7W\xa1.\n\x80\x00\u07d4\xe0a\xa4\xf2\xfcw\xb2\x96\u045a\xda#\x8eI\xa5\u02ce\xcb\xfap\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe0f>\x8c\xd6g\x92\xa6A\xf5nP\x03f\x01G\x88\x0f\x01\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0f\x8f\xa8,\x14\xd6\xe8\xd9:S\x11>\xf2\x86/\xa8\x15\x81\xbc\x89//9\xfclT\x00\x00\x00\u07d4\xe0i\xc0\x173R\xb1\v\xf6\x83G\x19\xdb[\xed\x01\xad\xf9{\xbc\x89\x01\x064\xf8\xe52;\x00\x00\u07d4\xe0l)\xa8\x15\x17\xe0\u0507\xb6\u007f\xb0\xb6\xaa\xbcOW6\x83\x88\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xe0l\xb6)G\x04\xee\xa7C|/\xc3\xd3\as\xb7\xbf8\x88\x9a\x89\x01\x16\xdc:\x89\x94\xb3\x00\x00\u07d4\xe0q7\xae\r\x11m\x0353\xc4\uad16\xf8\xa9\xfb\tV\x9c\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\xe0v\xdb0\xabHoy\x19N\xbb\xc4]\x8f\xab\x9a\x92B\xf6T\x8a\x01\x06`~4\x94\xba\xa0\x00\x00\u07d4\xe0~\xbb\xc7\xf4\xdaAnB\xc8\xd4\xf8B\xab\xa1b3\xc1%\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x81\xca\x1fH\x82\xdb`C\u0569\x19\a\x03\xfd\xe0\xab;\xf5m\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\x83\xd3Hc\xe0\xe1\u007f\x92ky(\xed\xff1~\x99\x8e\x9cK\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\x8b\x9a\xbak\xd9\u048b\xc2\x05gy\xd2\xfb\xf0\xf2\x85Z=\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x8b\u009c+H\xb1i\xff+\xdc\x16qLXnl\xb8\\\u03c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe0\x8c`11\x06\xe3\xf93O\xe6\xf7\xe7bM!\x110\xc0w\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xe0\x9ch\xe6\x19\x98\xd9\xc8\x1b\x14\xe4\xee\x80+\xa7\xad\xf6\xd7L\u06c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe0\x9f\xeauZ\xee\x1aD\xc0\xa8\x9f\x03\xb5\u07b7b\xba3\x00o\x89;\xa2\x89\xbc\x94O\xf7\x00\x00\xe0\x94\xe0\xa2T\xac\t\xb9r[\xeb\xc8\xe4`C\x1d\xd0s.\xbc\xab\xbf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xe0\xaai6UU\xb7?(#3\xd1\xe3\f\x1b\xbd\a(T\xe8\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xe0\xba\u064e\ue598\xdb\xf6\xd7`\x85\xb7\x92=\xe5uN\x90m\x89\t\r\x97/22<\x00\x00\u07d4\xe0\u012b\x90r\xb4\xe6\xe3eJI\xf8\xa8\xdb\x02jK3\x86\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\u0380\xa4a\xb6H\xa5\x01\xfd\v\x82F\x90\u0206\x8b\x0eM\xe8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0\xcfi\x8a\x053'\xeb\xd1k}w\x00\t/\xe2\xe8T$F\x89\x05*4\u02f6\x1fW\x80\x00\xe0\x94\xe0\xd21\xe1D\xec\x91\a8l|\x9b\x02\xf1p,\xea\xa4\xf7\x00\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4\xe0\xd7kqf\xb1\xf3\xa1+@\x91\xee+)\u078c\xaa}\a\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\xe0\xb2\xe2\x9d\xdes\xafu\x98~\xe4Dl\x82\x9a\x18\x9c\x95\xbc\x89\b\x13\xcaV\x90m4\x00\x00\xe0\x94\xe0\xe9xu=\x98/\u007f\x9d\x1d#\x8a\x18\xbdH\x89\xae\xfeE\x1b\x8a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4\xe0\xf3r4|\x96\xb5_}C\x06\x03K\xeb\x83&o\xd9\tf\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\xf9\x03\xc1\xe4\x8a\xc4!\xabHR\x8f=J&H\b\x0f\xe0C\x897\b\xba\xed=h\x90\x00\x00\u07d4\xe0\xff\v\xd9\x15D9\u0125\xb7#>)\x1d}\x86\x8a\xf5?3\x89\x15y!jQ\xbb\xfb\x00\x00\xe0\x94\xe1\n\xc1\x9cTo\xc2T|a\xc19\xf5\xd1\xf4Zff\u0570\x8a\x01\x02\xdao\xd0\xf7:<\x00\x00\xe0\x94\xe1\fT\x00\x88\x11?\xa6\xec\x00\xb4\xb2\u0202O\x87\x96\xe9n\u010a2\x0fE\t\xab\x1e\xc7\xc0\x00\x00\xe0\x94\xe1\x17:$})\xd8#\x8d\xf0\x92/M\xf2Z\x05\xf2\xafw\u00ca\bx\xc9]V\x0f0G\x80\x00\xe0\x94\xe1 >\xb3\xa7#\xe9\x9c\" \x11|\xa6\xaf\xebf\xfaBOa\x8a\x02\x00\uf49e2V\xfe\x00\x00\xe0\x94\xe11\xf8~\xfc^\xf0~C\xf0\xf2\xf4\xa7G\xb5Q\xd7P\xd9\xe6\x8a\x04<%\xe0\xdc\xc1\xbd\x1c\x00\x00\u07d4\xe13N\x99\x83y\xdf\xe9\x83\x17pby\x1b\x90\xf8\x0e\xe2-\x8d\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe15@\xec\xee\x11\xb2\x12\xe8\xb7u\u070eq\xf3t\xaa\xe9\xb3\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1;=+\xbf\u073c\x87r\xa23\x15rL\x14%\x16|V\x88\x897\xf3y\x14\x1e\xd0K\x80\x00\u07d4\xe1D=\xbd\x95\xccA#\u007fa:HEi\x88\xa0Oh2\x82\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xe1F\x17\xf6\x02%\x01\xe9~{>-\x886\xaaa\xf0\xff-\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe1I\xb5rl\xafm^\xb5\xbf*\xccA\xd4\xe2\xdc2\x8d\u1089i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xe1T\xda\xea\xdbTX8\xcb\u01aa\fUu\x19\x02\xf5(h*\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4\xe1l\xe3Ya\xcdt\xbdY\r\x04\u012dJ\x19\x89\xe0V\x91\u0189\a\xea(2uw\b\x00\x00\u07d4\xe1r\xdf\xc8\xf8\f\xd1\xf8\u03459\xdc&\b \x14\xf5\xa8\xe3\u8262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xe1w\xe0\xc2\x01\xd35\xba9V\x92\x9cW\x15\x88\xb5\x1cR#\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1x\x12\xf6l^e\x94\x1e\x18lF\x92+n{/\x0e\xebF\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xe1\x80\u079e\x86\xf5{\xaf\xac\u05d0O\x98&\xb6\xb4\xb2c7\xa3\x89-\x04\x1dpZ,`\x00\x00\xe0\x94\xe1\x92H\x9b\x85\xa9\x82\xc1\x882F\xd9\x15\xb2)\xcb\x13 \u007f8\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xe1\x95\xbb\xc6,{tD\x04\x0e\xb9\x96#\x96Ovg\xb3v\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe2\x06\xfbs$\xe9\u07b7\x9e\x19\x904\x96\u0596\x1b\x9b\xe5f\x03\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe2\aW\x8e\x1fM\u06cf\xf6\u0546{9X-q\xb9\x81*\u0149\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xe2\b\x81*h@\x98\xf3\xdaN\xfej\xba%bV\xad\xfe?\xe6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe2\tT\xd0\xf4\x10\x8c\x82\xd4\u0732\x14\x8d&\xbb\xd9$\xf6\xdd$\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe2\v\xb9\xf3\x96d\x19\xe1K\xbb\xaa\xaag\x89\xe9$\x96\u03e4y\x89\xbb\xd8%\x03\aRv\x00\x00\u07d4\xe2\r\x1b\xcbq(m\xc7\x12\x8a\x9f\xc7\xc6\xed\u007fs8\x92\xee\xf5\x896d\xf8\xe7\xc2J\xf4\x00\x00\u0794\xe2\x19\x12\x15\x98?3\xfd3\xe2,\u0522I\x00T\xdaS\xfd\u0708\xdbD\xe0I\xbb,\x00\x00\u07d4\xe2\x19\x8c\x8c\xa1\xb3\x99\xf7R\x15a\xfdS\x84\xa7\x13/\xbaHk\x897\b\xba\xed=h\x90\x00\x00\xe0\x94\xe2\x1cw\x8e\xf2\xa0\xd7\xf7Q\xea\x8c\aM\x1f\x81\"C\x86>N\x8a\x01\x1f\xc7\x0e,\x8c\x8a\xe1\x80\x00\xe0\x94\xe2)\xe7F\xa8?,\xe2S\xb0\xb0>\xb1G$\x11\xb5~W\x00\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4\xe2+ \xc7x\x94F;\xafwL\xc2V\u057d\u06ff}\xdd\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe20\xfe\x1b\xff\x03\x18m\x02\x19\xf1]LH\x1b}Y\xbe(j\x89\x01\xfdt\x1e\x80\x88\x97\x00\x00\u07d4\xe27\xba\xa4\xdb\u0252n2\xa3\xd8]\x12d@-T\xdb\x01/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2A\t\xbe/Q=\x87I\x8e\x92j(d\x99uO\x9e\u051e\x890\x0e\xa8\xad\x1f'\xca\x00\x00\u07d4\xe2Fh<\u025d\xb7\u0125+\u02ec\xaa\xb0\xb3/k\xfc\x93\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe2Z\x16{\x03\x1e\x84am\x0f\x01?1\xbd\xa9]\xcccP\xb9\x8a\x02\x8c*\xaa\u0243\xd0]\u0187st\xa8\xf4F\xee\xe9\x89\n\xb6@9\x12\x010\x00\x00\u07d4\xe2\x8b\x06\"Y\xe9n\xeb<\x8dA\x04\x94?\x9e\xb3%\x89<\xf5\x89Hz\x9a0E9D\x00\x00\xe0\x94\u237c\x8e\xfd^Ajv.\xc0\xe0\x18\x86K\xb9\xaa\x83({\x8a\x051\xf2\x00\xab>\x03\n\x80\x00\u07d4\xe2\x90K\x1a\xef\xa0V9\x8bb4\xcb5\x81\x12\x88\xd76\xdbg\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u274a\xe4R\xdc\xf3\xb6\xacd^c\x04\t8UQ\xfa\xae\n\x89\x04Z\r\xa4\xad\xf5B\x00\x00\u07d4\xe2\xbb\xf8FA\xe3T\x1fl3\xe6\xedh:cZp\xbd\xe2\xec\x89\x1bA<\xfc\xbfY\xb7\x80\x00\u07d4\xe2\xcf6\n\xa22\x9e\xb7\x9d+\xf7\xca\x04\xa2z\x17\xc52\xe4\u0609\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xe2\xdf#\xf6\xea\x04\xbe\xcfJ\xb7\x01t\x8d\xc0\x961\x84U\\\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2\xe1\\`\xdd8\x1e:K\xe2Pq\xab$\x9aL\\Rd\u0689\u007fk\u011b\x81\xb57\x00\x00\u07d4\xe2\xe2nN\x1d\xcf0\xd0H\xccn\u03ddQ\xec\x12\x05\xa4\xe9&\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xe2\xeei\x1f#~\xe6R\x9beW\xf2\xfc\xdd=\xcf\fY\xecc\x8a\x01'r\x9c\x14h| \x00\x00\u07d4\xe2\xef\xa5\xfc\xa7\x958\xce`h\xbf1\xd2\xc5\x16\xd4\xd5<\b\xe5\x89\a\x1c\xc4\b\xdfc@\x00\x00\xe0\x94\xe2\xef\u0429\xbc@~\xce\x03\xd6~\x8e\xc8\xe9\u0483\xf4\x8d*I\x8a\x02\x99\xb3;\xf9\u0144\xe0\x00\x00\u07d4\xe2\xf4\r5\x8f^?\xe7F>\xc7\x04\x80\xbd.\u04d8\xa7\x06;\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe2\xf98=X\x10\xea{C\x18+\x87\x04\xb6+'\xf5\x92]9\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe2\xff\x9e\xe4\xb6\xec\xc1AA\xcct\xcaR\xa9\xe7\xa2\xee\x14\xd9\b\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\xe3\x02\x12\xb2\x01\x1b\xb5k\xdb\xf1\xbc5i\x0f:N\x0f\xd9\x05\xea\x8a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4\xe3\x03\x16\u007f=I`\xfe\x88\x1b2\x80\n+J\xef\xf1\xb0\x88\u0509lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3\x04\xa3/\x05\xa87btJ\x95B\x97o\xf9\xb7#\xfa1\xea\x89Ur\xf2@\xa3F \x00\x00\u07d4\xe3\bCR\x04y7d\xf5\xfc\xbee\xebQ\x0fZtJeZ\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3\t\x97L\xe3\x9d`\xaa\xdf.ig2Q\xbf\x0e\x04v\n\x10\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xe3\x1bN\xef\x18L$\xab\t\x8e6\xc8\x02qK\xd4t=\xd0\u0509\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3!\xbbJ\x94j\xda\xfd\xad\xe4W\x1f\xb1\\\x00C\u04de\xe3_\x89Udu8+L\x9e\x00\x00\u07d4\xe3&<\xe8\xafm\xb3\xe4gXE\x02\xedq\t\x12^\xae\"\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3+\x1cG%\xa1\x87TI\u93d7\x0e\xb3\xe5@b\xd1X\x00\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3/\x95vmW\xb5\xcdK\x172\x89\u0587o\x9edU\x81\x94\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe38@\u063c\xa7\u0698\xa6\xf3\u0416\xd8=\xe7\x8bp\xb7\x1e\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe38\xe8Y\xfe.\x8c\x15UHH\xb7\\\xae\u0368w\xa0\xe82\x89a\xac\xff\x81\xa7\x8a\xd4\x00\x00\u07d4\xe3=\x98\x02 \xfa\xb2Y\xafj\x1fK8\xcf\x0e\xf3\xc6\xe2\xea\x1a\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe3=\xf4\u0380\u0336*v\xb1+\xcd\xfc\xec\xc4b\x89\x97:\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xe3?\xf9\x87T\x1d\xde\\\xde\u0a29m\xcc?3\xc3\xf2L\u008a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xe3A\v\xb7U|\xf9\x1dy\xfai\xd0\xdf\xea\n\xa0u@&Q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3Ad-@\u04af\xce.\x91\a\xc6py\xacz&`\bl\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe3TS\xee\xf2\xcc2\x89\x10CR\x8d\t\x84i\x80\x00\xe0\x94\xe5\x10\xd6y\u007f\xba=f\x93\x83Z\x84N\xa2\xadT\x06\x91\x97\x1b\x8a\x03\xae9\xd4s\x83\xe8t\x00\x00\u07d4\xe5\x14!\xf8\xee\"\x10\xc7\x1e\xd8p\xfea\x82v\u0215J\xfb\xe9\x89Hz\x9a0E9D\x00\x00\u07d4\xe5\x1e\xb8~\u007f\xb71\x1fR(\xc4y\xb4\x8e\u0247\x881\xacL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5!V1\xb1BH\xd4Z%R\x96\xbe\xd1\xfb\xfa\x030\xff5\x89G\x03\xe6\xebR\x91\xb8\x00\x00\xe0\x94\xe5(\xa0\xe5\xa2g\xd6g\xe99:e\x84\xe1\x9b4\u071b\xe9s\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xe54%\xd8\xdf\x1f\x11\xc3A\xffX\xae_\x148\xab\xf1\xcaS\u03c9\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\u07d4\xe5No\x9c\xffV\xe1\x9cF\x1e\xb4T\xf9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5A\x02SM\xe8\xf2>\xff\xb0\x93\xb3\x12B\xad;#?\xac\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe5E\xee\x84\xeaH\xe5d\x16\x1e\x94\x82\u055b\xcf@j`,\xa2\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xe5H\x1a\u007f\xedB\xb9\x01\xbb\xed x\x9b\u052d\xe5\r_\x83\xb9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5Y\xb5\xfd3{\x9cUr\xa9\xbf\x9e\x0f%!\xf7\xd4F\xdb\xe4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe5\\\x80R\n\x1b\x0fu[\x9a,\xd3\xce!Ov%e>\x8a\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe5mC\x13$\xc9)\x11\xa1t\x9d\xf2\x92p\x9c\x14\xb7ze\u034a\x01\xbc\x85\xdc*\x89\xbb \x00\x00\u07d4\xe5})\x95\xb0\xeb\xdf?<\xa6\xc0\x15\xeb\x04&\r\xbb\x98\xb7\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\u51f1j\xbc\x8at\b\x1e6\x13\xe1CB\xc03u\xbf\bG\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\x89\xfav\x98M\xb5\xec@\x04\xb4n\u8954\x92\xc3\aD\u0389\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xe5\x8d\xd228\xeen\xa7\xc2\x13\x8d8]\xf5\x00\xc3%\xf3v\xbe\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xe5\x95?\xeaIq\x04\xef\x9a\xd2\xd4\xe5\x84\x1c'\x1f\a5\x19\u0089&)\xf6n\fS\x00\x00\x00\xe0\x94\u5587\x97F\x8e\xf7g\x10\x1bv\x1dC\x1f\xce\x14\xab\xff\u06f4\x8a\x01\xb3\xd9i\xfaA\x1c\xa0\x00\x00\u07d4\xe5\x97\xf0\x83\xa4i\xc4Y\x1c=+\x1d,w'\x87\xbe\xfe'\xb2\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xe5\x9b;\xd3\x00\x89?\x97#>\xf9G\xc4or\x17\xe3\x92\xf7\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5\xa3e4<\xc4\xeb\x1ew\x03h\xe1\xf1\x14Jw\xb82\xd7\xe0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe5\xa3\xd7\xeb\x13\xb1\\\x10\x01w#m\x1b\xeb0\xd1~\xe1T \x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xaa\v\x83;\xb9\x16\xdc\x19\xa8\xddh?\x0e\xde$\x1d\x98\x8e\xba\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\u5def\x14i\x86\xc0\xff\x8f\x85\xd2.l\xc34\a}\x84\xe8$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xb8&\x19l\x0e\x1b\xc1\x11\x9b\x02\x1c\xf6\xd2Y\xa6\x10\u0256p\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe5\xb9o\u026c\x03\xd4H\xc1a:\xc9\x1d\x15\x97\x81E\xdb\xdf\u0449\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u5e40\u048e\xec\xe2\xc0o\xcal\x94s\x06\x8b7\u0526\xd6\xe9\x89%\xaf\u058c\xac+\x90\x00\x00\u07d4\u5eb4\xf0\xaf\u0629\u0463\x81\xb4Wa\xaa\x18\xf3\xd3\xcc\xe1\x05\x89Q\xbf\xd7\xc18x\xd1\x00\x00\u07d4\xe5\xbc\u020c;%on\xd5\xfeU\x0eJ\x18\x19\x8b\x943V\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xbd\xf3OL\xccH>L\xa50\xcc|\xf2\xbb\x18\xfe\xbe\x92\xb3\x89\x06\xd85\xa1\v\xbc\xd2\x00\x00\u07d4\xe5\u0713I\xcbR\xe1a\x19a\"\u03c7\xa3\x896\xe2\xc5\u007f4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xe38\x00\xa1\xb2\xe9k\xde\x101c\n\x95\x9a\xa0\a\xf2nQ\x89Hz\x9a0E9D\x00\x00\u07d4\xe5\xe3~\x19@\x8f,\xfb\xec\x834\x9d\u0501S\xa4\xa7\x95\xa0\x8f\x89\u3bb5sr@\xa0\x00\x00\u07d4\xe5\xed\xc7>bo]4A\xa4U9\xb5\xf7\xa3\x98\u0153\xed\xf6\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xe5\xed\xf8\x12?$\x03\xce\x1a\x02\x99\xbe\xcfz\xactM\a_#\x89\n\xdaUGK\x814\x00\x00\u07d4\xe5\xf8\xefm\x97\x066\xb0\u072aO \x0f\xfd\xc9\xe7Z\xf1t\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xfb1\xa5\xca\xeej\x96\xde9;\xdb\xf8\x9f\xbee\xfe\x12[\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5\xfb\xe3I\x84\xb67\x19o3\x1cg\x9d\f\fG\xd84\x10\xe1\x89llD\xfeG\xec\x05\x00\x00\u07d4\xe6\tU\xdc\v\xc1V\xf6\xc4\x18I\xf6\xbdwk\xa4K\x0e\xf0\xa1\x89\x10C\x16'\xa0\x93;\x00\x00\u07d4\xe6\nU\xf2\u07d9m\u00ee\xdbil\b\xdd\xe09\xb2d\x1d\xe8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe6\x11[\x13\xf9y_~\x95e\x02\xd5\aEg\u06b9E\xcek\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xe6\x1f(\t\x15\xc7t\xa3\x1d\"<\xf8\f\x06\x92f\xe5\xad\xf1\x9b\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xe6/\x98e\a\x12\xeb\x15\x87S\xd8)r\xb8\u9723\xf6\x18w\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe6/\x9d|d\xe8\xe2cZ\xeb\x88=\xd7;\xa6\x84\xee|\x10y\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe6>xt\x14\xb9\x04\x84x\xa5\a35\x9e\xcd\xd7\xe3dz\xa6\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xe6FfXr\xe4\v\rz\xa2\xff\x82r\x9c\xaa\xba[\xc3\u8789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe6N\xf0\x12e\x8dT\xf8\xe8`\x9cN\x90#\xc0\x9f\xe8e\xc8;\x89\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\xe6On\x1dd\x01\xb5l\akd\xa1\xb0\x86}\v/1\rN\x89\x02\u02edq\xc5:\xe5\x00\x00\u07d4\xe6g\xf6R\xf9W\u008c\x0ef\u04364\x17\xc8\f\x8c\x9d\xb8x\x89 \x9d\x92/RY\xc5\x00\x00\xe0\x94\xe6w\xc3\x1f\xd9\xcbr\x00u\u0724\x9f\x1a\xbc\xcdY\xec3\xf74\x8a\x01\xa6\u05be\xb1\xd4.\xe0\x00\x00\u07d4\xe6|,\x16e\u02038h\x81\x87b\x9fI\xe9\x9b`\xb2\u04fa\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xe6\x9al\xdb:\x8a}\xb8\xe1\xf3\f\x8b\x84\xcds\xba\xe0+\xc0\xf8\x8a\x03\x94\xfd\xc2\xe4R\xf6q\x80\x00\u07d4\xe6\x9d\x1c7\x8bw\x1e\x0f\xef\xf0Q\xdbi\xd9f\xacgy\xf4\xed\x89\x1d\xfaj\xaa\x14\x97\x04\x00\x00\u07d4\xe6\x9f\xcc&\xed\"_{.7\x984\xc5$\xd7\f\x175\u5f09lk\x93[\x8b\xbd@\x00\x00\u07d4\xe6\xa3\x01\x0f\x02\x01\xbc\x94\xffg\xa2\xf6\x99\xdf\xc2\x06\xf9\xe7gB\x89/\xa7\xcb\xf6dd\x98\x00\x00\u07d4\xe6\xa6\xf6\xddop\xa4V\xf4\xec\x15\xefz\xd5\xe5\u06f6\x8b\xd7\u0709\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe6\xb2\x0f\x98\n\xd8S\xad\x04\xcb\xfc\x88|\xe6`\x1ck\xe0\xb2L\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u6cec?]M\xa5\xa8\x85}\v?0\xfcK+i+w\u05c9O%\x91\xf8\x96\xa6P\x00\x00\u07d4\xe6\xb9T_~\u0406\xe5R\x92F9\xf9\xa9\xed\xbb\xd5T\v>\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\xe0\x94\xe6\xbc\xd3\n\x8f\xa18\xc5\xd9\xe5\xf6\xc7\xd2\u0680i\x92\x81-\u034a7\x0e\xa0\xd4|\xf6\x1a\x80\x00\x00\u07d4\xe6\xc8\x1f\xfc\xec\xb4~\xcd\xc5\\\vq\xe4\x85_>^\x97\xfc\x1e\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4\xe6\xcb&\vqmL\n\xb7&\xee\xeb\a\xc8pr\x04\xe2v\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe6\xcb?1$\xc9\xc9\xcc84\xb1'K\xc33dV\xa3\x8b\xac\x89\x17+\x1d\xe0\xa2\x13\xff\x00\x00\xe0\x94\xe6\xd2\"\t\xff\u0438u\t\xad\xe3\xa8\xe2\xefB\x98y\u02c9\xb5\x8a\x03\xa7\xaa\x9e\x18\x99\xca0\x00\x00\u07d4\xe6\u051f\x86\xc2(\xf4sg\xa3^\x88l\xaa\xcb'\x1eS\x94)\x89\x16^\xc0\x9d\xa7\xa1\x98\x00\x00\u07d4\xe6\xe6!\xea\xab\x01\xf2\x0e\xf0\x83k|\xadGFL\xb5\xfd<\x96\x89\x11!\x93B\xaf\xa2K\x00\x00\u07d4\xe6\xe8\x861{jf\xa5\xb4\xf8\x1b\xf1d\xc58\xc2d5\x17e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe6\u98ddu\x0f\xe9\x949N\xb6\x82\x86\xe5\xeab\xa6\x99x\x82\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xe6\xec\\\xf0\u011b\x9c1~\x1epc\x15\uf7b7\xc0\xbf\x11\xa7\x8a\x03\xa4i\xf3F~\x8e\xc0\x00\x00\u07d4\xe6\xf5\xebd\x9a\xfb\x99Y\x9cAK'\xa9\xc9\xc8U5\u007f\xa8x\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xe6\xfe\n\xfb\x9d\xce\xdd7\xb2\xe2,E\x1b\xa6\xfe\xabg4\x803\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe7\x10\xdc\u041b\x81\x01\xf9C{\xd9}\xb9\ns\xef\x99=\v\xf4\x89\x14\xee6\xc0Z\xc2R\x00\x00\u07d4\xe7'\xe6~\xf9\x11\xb8\x1fl\xf9\xc7?\xcb\xfe\xbc+\x02\xb5\xbf\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7.\x1d3\\\u009a\x96\xb9\xb1\xc0/\x00:\x16\xd9q\xe9\v\x9d\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xe71\x1c\x953\xf0\t,rH\xc9s\x9b[,\x86J4\xb1\u0389\x97\xf9}l\xc2m\xfe\x00\x00\u07d4\xe7;\xfe\xad\xa6\xf0\xfd\x01o\xbc\x84>\xbc\xf6\xe3p\xa6[\xe7\f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xe7<\xcfCg%\xc1Q\xe2U\xcc\xf5!\f\xfc\xe5\xa4?\x13\xe3\x89\x01\x15NS!}\xdb\x00\x00\u07d4\xe7B\xb1\xe6\x06\x9a\x8f\xfc'\f\xc6\x1f\xa1d\xac\x15SE\\\x10]\x04\x88~\x14\x89\x06\x96\xd8Y\x00 \xbb\x00\x00\u07d4\xe7\\\x1f\xb1w\b\x9f>X\xb1\x06y5\xa6Yn\xf1s\u007f\xb5\x89\x05j\x87\x9f\xa7uG\x00\x00\u07d4\xe7\\;8\xa5\x8a?3\xd5V\x90\xa5\xa5\x97f\xbe\x18^\x02\x84\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe7a\xd2\u007f\xa3P,\xc7k\xb1\xa6\bt\x0e\x14\x03\u03dd\xfci\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xe7f\xf3O\xf1o<\xfc\xc9s!r\x1fC\xdd\xf5\xa3\x8b\f\xf4\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xe7m\x94Z\xa8\x9d\xf1\xe4W\xaa4+1\x02\x8a^\x910\xb2\u03897\b\xba\xed=h\x90\x00\x00\u07d4\xe7s^\xc7e\x18\xfcj\xa9-\xa8qZ\x9e\xe3\xf6%x\x8f\x13\x89lM\x16\v\xaf\xa1\xb7\x80\x00\xe0\x94\xe7z\x89\xbdE\xdc\x04\xee\xb4\xe4\x1d{Ykp~nQ\xe7L\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xe7}}\uac96\u0234\xfa\a\xca;\xe1\x84\x16=Zm`l\x89\x05\x049\x04\xb6q\x19\x00\x00\u07d4\xe7\u007f\xeb\xab\xdf\b\x0f\x0f]\xca\x1d?Wf\xf2\xa7\x9c\x0f\xfa|\x89K\"\x9d(\xa8Ch\x00\x00\xe0\x94\u7025c\x06\xba\x1ek\xb31\x95,\"S\x9b\x85\x8a\xf9\xf7}\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xe7\x81\xecs-@\x12\x02\xbb\x9b\xd18`\x91\r\xd6\u009a\xc0\xb6\x89C8t\xf62\xcc`\x00\x00\u07d4\xe7\x84\xdc\xc8s\xaa\x8c\x15\x13\xec&\xff6\xbc\x92\xea\xc6\xd4\xc9h\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe7\x91-L\xf4V,W=\xdc[q\xe3s\x10\xe3x\xef\x86\u0249\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xe7\x91\u0545\xb8\x996\xb2])\x8f\x9d5\xf9\xf9\xed\xc2Z)2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7\x924\x9c\xe9\xf6\xf1O\x81\xd0g@\x96\xbe\xfa\x1f\x92!\xcd\xea\x89[]#J\r\xb48\x80\x00\u07d4\xe7\x96\xfdN\x83\x9bL\x95\xd7Q\x0f\xb7\xc5\xc7+\x83\xc6\xc3\xe3\u01c9\x1b\xc43\xf2?\x83\x14\x00\x00\xe0\x94\xe7\xa4/Y\xfe\xe0t\xe4\xfb\x13\xea\x9eW\xec\xf1\xccH(\"I\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xe7\xa4V\f\x84\xb2\x0e\x0f\xb5LIg\f)\x03\xb0\xa9lB\xa4\x89 j\xea\u01e9\x03\x98\x00\x00\u07d4\xe7\xa8\xe4q\xea\xfby\x8fET\xccnRg0\xfdV\xe6,}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u7f82\xc6Y<\x1e\xed\xdd*\xe0\xb1P\x01\xff \x1a\xb5{/\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4\xe7\u01b5\xfc\x05\xfct\x8e[C\x81rdI\xa1\xc0\xad\x0f\xb0\xf1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7\xd1u$\xd0\v\xad\x82I|\x0f'\x15jd\u007f\xf5\x1d'\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe7\xd2\x13\x94\u007f\u02d0J\xd78H\v\x1e\xed/\\2\x9f'\xe8\x89\x01\x03\u00f1\xd3\xe9\xc3\x00\x00\u07d4\xe7\xd6$\x06 \xf4,^\u06f2\xed\xe6\xae\xc4=\xa4\xed\x9bWW\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe7\xda`\x9d@\xcd\xe8\x0f\x00\xce[O\xfbj\xa9\u04304\x94\xfc\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe7\xf0oi\x9b\xe3\x1cD\vC\xb4\xdb\x05\x01\xec\x0e%&\x16D\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe7\xf4\xd7\xfeoV\x1f\u007f\xa1\xda0\x05\xfd6TQ\xad\x89\u07c9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe7\xfd\x8f\xd9Y\xae\xd2v~\xa7\xfa\x96\f\xe1\xdbS\xaf\x80%s\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe8\x0e\u007f\xef\x18\xa5\xdb\x15\xb0\x14s\xf3\xadkx\xb2\xa2\xf8\xac\u0649\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe8\x13\u007f\xc1\xb2\xec|\xc7\x10:\xf9!\x89\x9bJ9\xe1\xd9Y\xa1\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\xe8\x1c-4l\n\xdfL\xc5g\b\xf69K\xa6\xc8\u0226J\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8,X\xc5yC\x1bg5F\xb5:\x86E\x9a\xca\xf1\u079b\x93\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe84\xc6C\x18 \\\xa7\xddJ!\xab\xcb\b&l\xb2\x1f\xf0,\x8965\xc6 G9\u0640\x00\u07d4\xe86\x04\xe4\xffk\xe7\xf9o`\x18\xd3\xec0r\xecR]\xffk\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\xe0\x94\xe8E\xe3\x87\xc4\xcb\u07d8\"\x80\xf6\xaa\x01\xc4\x0eK\xe9X\u0772\x8a\x05K@\xb1\xf8R\xbd\xa0\x00\x00\u07d4\xe8H\xca~\xbf\xf5\xc2O\x9b\x9c1g\x97\xa4;\xf7\xc3V)-\x89\x06.\x11\\\x00\x8a\x88\x00\x00\u07d4\xe8KU\xb5%\xf1\x03\x9etK\x91\x8c\xb33$\x92\xe4^\xcaz\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe8O\x80v\xa0\xf2\x96\x9e\xcd3>\xef\x8d\xe4\x10B\x98b\x91\xf2\x89\x17k4O*x\xc0\x00\x00\u07d4\xe8d\xfe\xc0~\xd1!Je1\x1e\x11\xe3)\xde\x04\r\x04\xf0\xfd\x89Y\u0283\xf5\xc4\x04\x96\x80\x00\u07d4\xe8}\xba\xc66\xa3w!\xdfT\xb0\x8a2\xefIY\xb5\xe4\xff\x82\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe8~\x9b\xbf\xbb\xb7\x1c\x1at\ft\xc7#Bm\xf5]\x06=\u064a\x01\xb1\x92\x8c\x00\u01e68\x00\x00\u07d4\xe8~\xacm`+A\t\xc9g\x1b\xf5{\x95\f,\xfd\xb9\x9dU\x89\x02\xb4\xf2\x19r\xec\xce\x00\x00\xe0\x94\u807b\xbeir-\x81\xef\xec\xaaH\u0455*\x10\xa2\xbf\xac\x8f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xe8\x92Is\x8b~\xce\xd7\xcbfjf\xe4s\xbcv\x82/U\t\x8d\x89\xb9\x1c\u0149lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\xc3\u04f0\xe1\u007f\x97\xd1\xe7V\xe6\x84\xf9N\x14p\xf9\x9c\x95\xa1\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xe8\xc3\xf0E\xbb}8\xc9\xd2\U000d5c3a\x84\x92\xb2S#\t\x01\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4\xe8\xccC\xbcO\x8a\xcf9\xbf\xf0N\xbf\xbfB\xaa\xc0j2\x84p\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe8\xd9B\xd8/\x17^\xcb\x1c\x16\xa4\x05\xb1\x01C\xb3\xf4k\x96:\x89\x1e\xd2\xe8\xffm\x97\x1c\x00\x00\u07d4\xe8\u077e\xd72\xeb\xfeu@\x96\xfd\xe9\bk\x8e\xa4\xa4\xcd\xc6\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\xder^\xca]\xef\x80_\xf7\x94\x1d1\xac\x1c.4-\xfe\x95\x89\x85~\ro\x1d\xa7j\x00\x00\u07d4\xe8\xe9\x85\x05\x86\xe9OR\x99\xabIK\xb8!\xa5\xf4\f\x00\xbd\x04\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\xe8\xea\u047b\x90\xcc\u00ee\xa2\xb0\xdc\u0175\x80VUFU\xd1\u054a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\u07d4\xe8\xea\xf1)D\t-\xc3Y\x9b9S\xfa|\xb1\xc9v\x1c\xc2F\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xe8\xedQ\xbb\xb3\xac\xe6\x9e\x06\x02K3\xf8hD\xc4sH\u06de\x8a\"\xf9\xea\x89\xf4\xa7\xd6\xc4\x00\x00\u07d4\xe8\xef\x10\r|\xe0\x89X2\xf2g\x8d\xf7-J\u03cc(\xb8\xe3\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xe8\xf2\x99i\xe7\\e\xe0\x1c\xe3\xd8aT }\n\x9e|v\xf2\x89\xa2/\xa9\xa7:'\x19\x80\x00\u07d4\xe8\xfc6\xb0\x13\x1e\xc1 \xac\x9e\x85\xaf\xc1\f\xe7\vV\u0636\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9\n5L\xec\x04\u059e]\x96\xdd\xc0\xc5\x13\x8d=3\x15\n\xa0\x89\x1b\x1a}\u03caD\u04c0\x00\xe0\x94\xe9\x13>}1\x84]_+f\xa2a\x87\x92\xe8i1\x1a\xcff\x8a\x05\x17\xc0\xcb\xf9\xa3\x90\x88\x00\x00\u07d4\xe9\x1d\xac\x01\x95\xb1\x9e7\xb5\x9bS\xf7\xc0\x17\xc0\xb29[\xa4L\x89e\xea=\xb7UF`\x00\x00\u07d4\xe9\x1f\xa0\xba\xda\u0779\xa9~\x88\xd3\xf4\xdb|U\u05bbt0\xfe\x89\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\xe9#\xc0aw\xb3B~\xa4H\xc0\xa6\xff\x01\x9bT\xccT\x8d\x95\x89\x01\xf7\x80\x01Fg\xf2\x80\x00\xe0\x94\xe9=G\xa8\u0288]T\fNRo%\xd5\xc6\xf2\xc1\b\u0138\x8a\x17\xda:\x04\u01f3\xe0\x00\x00\x00\u07d4\xe9E\x8fh\xbb',\xb5g:\x04\xf7\x81\xb4\x03Uo\u04e3\x87\x89\x03N\x8b\x88\xce\xe2\xd4\x00\x00\u07d4\xe9IA\xb6\x03`\x19\xb4\x01j0\xc1\x03}Zi\x03\xba\xba\xad\x89*H\xac\xabb\x04\xb0\x00\x00\u07d4\xe9I[\xa5\x84'(\xc0\ud5fe7\xd0\xe4\"\xb9\x8di ,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe9M\xed\x99\u0735r\xb9\xbb\x1d\u02e3/m\xee\x91\xe0W\x98N\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\xe0\x94\xe9QyR}\uc951l\xa9\xa3\x8f!\\\x1e\x9c\xe77\xb4\u024a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xe9U\x91\x85\xf1f\xfc\x95\x13\xccq\x11aD\xce-\xeb\x0f\x1dK\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xe9^\x92\xbb\xc6\xde\a\xbf:f\x0e\xbf_\xeb\x1c\x8a5'\xe1\u0148\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\xe9e\u06a3@9\xf7\xf0\xdfb7Z7\u5acar\xb3\x01\xe7\x8a\x01\x03\xfd\xde\u0373\xf5p\x00\x00\u07d4\xe9i\xea\x15\x95\xed\xc5\u0127\a\xcf\xde8\t)c2Q\xa2\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9k\x18N\x1f\x0fT\x92J\xc8t\xf6\v\xbfDptF\xb7+\x89\x9d\xcc\x05\x15\xb5n\f\x00\x00\xe0\x94\xe9m}L\xdd\x15U:NM1mmd\x80\xca<\xea\x1e8\x8a\x02\x95]\x02\xe1\xa15\xa0\x00\x00\u07d4\xe9n-8\x13\xef\xd1\x16_\x12\xf6\x02\xf9\u007fJb\x90\x9d\x1b;\xc0\xe9\xaa\"\u007f\x90\x89'\xcaK\xd7\x19\xf0\xb8\x00\x00\u07d4\xea,\x19}&\xe9\x8b\r\xa8>\x1br\u01c7a\x8c\x97\x9d=\xb0\x89\x01\x11du\x9f\xfb2\x00\x00\xe0\x94\xea7y\xd1J\x13\xf6\u01c5f\xbc\xde@5\x91A:b9\u06ca)\xb7d2\xb9DQ \x00\x00\u07d4\xeaN\x80\x9e&j\xe5\xf1<\xdb\u33dd\x04V\xe68m\x12t\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\xe0\x94\xeaS\xc9T\xf4\xed\x97\xfdH\x10\x11\x1b\u06b6\x9e\xf9\x81\xef%\xb9\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xeaS\xd2ed\x85\x9d\x9e\x90\xbb\x0eS\xb7\xab\xf5`\xe0\x16,8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xea`Ci\x12\xdek\xf1\x87\u04e4r\xff\x8fS3\xa0\xf7\xed\x06\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xea`T\x9e\xc7U?Q\x1d!I\xf2\xd4fl\xbd\x92C\xd9<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xeaf\xe7\xb8M\u037f6\xee\xa3\xe7[\x858*u\xf1\xa1]\x96\x89]\xbc\x91\x91&o\x11\x80\x00\u07d4\xeahlPW\t<\x17\x1cf\u06d9\xe0\x1b\x0e\xce\xcb0\x86\x83\x89\x14\u0768],\xe1G\x80\x00\u07d4\xeaj\xfe,\xc9(\xac\x83\x91\xeb\x1e\x16_\xc4\x00@\xe3t!\u7262\u007f\xa0c\xb2\xe2\xe6\x80\x00\u07d4\xeay\x05}\xab\xef^d\xe7\xb4O\u007f\x18d\x8e~S7\x18\u0489\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xea|Mm\xc7)\xcdk\x15|\x03\xad#|\xa1\x9a \x93F\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\x81h\xfb\xf2%\xe7\x86E\x9c\xa6\xbb\x18\xd9c\xd2kPS\t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xea\x81\u02868T\f\xd9\xd4\xd7=\x06\x0f,\xeb\xf2$\x1f\xfc>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xea\x83\x17\x19yYB@A\xd9\xd7\xc6z>\xce\x1d\xbbx\xbbU\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xea\x85'\xfe\xbf\xa1\xad\xe2\x9e&A\x93)\u04d3\xb9@\xbb\xb7\u0709lj\xccg\u05f1\xd4\x00\x00\u07d4\xea\x8f0\xb6\xe4\xc5\xe6R\x90\xfb\x98d%\x9b\u0159\x0f\xa8\ue289\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xea\x94\xf3(\b\xa2\uf29b\xf0\x86\x1d\x1d$\x04\xf7\xb7\xbe%\x8a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xea\xa4\\\xea\x02\xd8},\xc8\xfd\xa9CN-\x98[\xd4\x03\x15\x84\x89h\x1f\xc2\xccn+\x8b\x00\x00\xe0\x94\uac3d\x14\x83\t\x18l\xf8\xcb\xd1;r2\xd8\tZ\u02c3:\x8a\x02C\x9a\x88\x1cjq|\x00\x00\u07d4\uaed0\xd3y\x89\xaa\xb3\x1f\xea\xe5G\xe0\xe6\xf3\x99\x9c\xe6\xa3]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xc0\x82~\xff\fn?\xf2\x8a}JT\xf6\\\xb7h\x9d{\x99\x89\x9a\xd9\u67ddGR\x00\x00\u07d4\xea\xc1H(&\xac\xb6\x11\x1e\x19\xd3@\xa4_\xb8QWk\xed`\x89\x01\xbe\x8b\xab\x04\u067e\x80\x00\xe0\x94\xea\xc1{\x81\xedQ\x91\xfb\b\x02\xaaT3s\x13\x83A\a\xaa\xa4\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xea\u00efW\x84\x92\u007f\u9958\xfcN\xec8\xb8\x10/7\xbcX\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xea\u01b9\x88BT.\xa1\v\xb7O&\xd7\xc7H\x8fi\x8bdR\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xea\xc7h\xbf\x14\xb8\xf9C.i\xea\xa8*\x99\xfb\xeb\x94\xcd\f\x9c\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4\xea\xd2\x1c\x1d\xec\u03ff\x1c\\\xd9f\x88\xa2Gki\xba\a\xceJ\x89\x03\xf2M\x8eJ\x00p\x00\x00\u07d4\xea\xd4\xd2\xee\xfbv\xab\xaeU3\x96\x1e\xdd\x11@\x04\x06\xb2\x98\xfc\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xea\xd6Rb\xed]\x12-\xf2\xb2u\x14\x10\xf9\x8c2\xd1#\x8fQ\x89\x05\x83\x17\xedF\xb9\xb8\x00\x00\u07d4\xea\xd7P\x16\u3801Pr\xb6\xb1\b\xbc\xc1\xb7\x99\xac\xf08>\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xea#\xaa\x05r\x00\xe7\xc9\xc1^\x8f\xf1\x90\xd0\xe6l\f\x0e\x83\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xed\x16\xea\xf5\u06ab[\xf0)^^\a\u007fY\xfb\x82U\x90\v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xea\xed\xcck\x8bib\xd5\xd9(\x8c\x15lW\x9dG\xc0\xa9\xfc\xff\x89\x04\x9b\x9c\xa9\xa6\x944\x00\x00\u07d4\xea\xf5#\x88Tn\xc3Z\xcaolc\x93\xd8\xd6\t\xde:K\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xeb\x10E\x8d\xac\xa7\x9eJk$\xb2\x9a\x8a\x8a\xdaq\x1b\u007f.\xb6\x89\u063beI\xb0+\xb8\x00\x00\u07d4\xeb\x1c\xea{E\u047dM\x0e*\x00{\u04ff\xb3Tu\x9e,\x16\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\xeb%H\x1f\u035c\"\x1f\x1a\xc7\xe5\xfd\x1e\u0353\a\xa1b\x15\xb8\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\xeb.\xf3\u04cf\xe6R@<\xd4\xc9\xd8^\xd7\xf0h,\xd7\xc2\u078a\t\x0fSF\b\xa7(\x80\x00\x00\xe0\x94\xeb;\xddY\xdc\u0765\xa9\xbb*\xc1d\x1f\xd0!\x80\xf5\xf3e`\x8a\x01e\xc9fG\xb3\x8a \x00\x00\u07d4\xeb<\xe7\xfc8\x1cQ\xdb}_\xbdi/\x8f\x9e\x05\x8aLp=\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xebE?Z:\xdd\u074a\xb5gP\xfa\xdb\x0f\xe7\xf9M\x9c\x89\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xebO\x00\xe2\x836\xea\t\x94%\x88\ueb12\x18\x11\xc5\"\x14<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xebR\xab\x10U4\x922\x9c\x1cT\x83:\xe6\x10\xf3\x98\xa6[\x9d\x89\b=lz\xabc`\x00\x00\u07d4\xebW\r\xba\x97R'\xb1\xc4-n\x8d\xea,V\u026d\x96\x06p\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xebc\x94\xa7\xbf\xa4\u0489\x11\u0565\xb2>\x93\xf3^4\f\"\x94\x89\x04:w\xaa\xbd\x00x\x00\x00\u07d4\xebh\x10i\x1d\x1a\xe0\u045eG\xbd\"\u03be\u0cfa'\xf8\x8a\x89\x87\x85c\x15\xd8x\x15\x00\x00\u07d4\xebvBL\x0f\u0557\xd3\xe3A\xa9d*\xd1\xee\x11\x8b+W\x9d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xeb| +F+|\u0145]t\x84u_n&\xefC\xa1\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xeb\x83\\\x1a\x91\x18\x17\x87\x8a3\xd1gV\x9e\xa3\xcd\u04c7\xf3(\x8965\u026d\xc5\u07a0\x00\x00\u07d4\ub268\x82g\t\t\xcf7~\x9ex(n\xe9{\xa7\x8dF\u0089+|\xc2\xe9\xc3\"\\\x00\x00\xe0\x94\xeb\x90\u01d3\xb3S\x97a\xe1\xc8\x14\xa2\x96q\x14\x86\x92\x19>\xb4\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xeb\x9c\xc9\xfe\bi\xd2\u06b5,\u01ea\xe8\xfdW\xad\xb3_\x9f\xeb\x89j\x93\xbb\x17\xaf\x81\xf8\x00\x00\xe0\x94\ub8c8\xb0\xda'\xc8{\x1c\xc0\xea\xc6\xc5{,Z\vE\x9c\x1a\x8a\x01p\xa0\xf5\x04\x0eP@\x00\x00\u07d4\xeb\xaa!m\xe9\xccZC\x03\x17\a\xd3o\xe6\u057e\xdc\x05\xbd\xf0\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xeb\xac+D\b\xefT1\xa1;\x85\b\xe8bP\x98!\x14\xe1E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xeb\xb6,\xf8\xe2,\x88K\x1b(\xc6\xfa\x88\xfb\xbc\x17\x93\x8a\xa7\x87\x89+By\x84\x03\u0278\x00\x00\u07d4\xeb\xb7\xd2\xe1\x1b\u01b5\x8f\n\x8dE\xc2\xf6\xde0\x10W\n\u0211\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4\xeb\xbbO,=\xa8\xbe>\xb6-\x1f\xfb\x1f\x95\x02a\u03d8\xec\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4\xeb\xbdM\xb9\x01\x99R\u058b\x1b\x0fm\x8c\xf0h<\x008{\xb5\x89\x12\x04\x01V=}\x91\x00\x00\u07d4\xeb\xbe\xeb%\x91\x84\xa6\xe0\x1c\xcc\xfc\"\a\xbb\u0603xZ\xc9\n\x89!\x9b\xc1\xb0G\x83\xd3\x00\x00\u07d4\xeb\xd3V\x15j81#4=H\x84;\xff\xeda\x03\xe8f\xb3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xeb\xd3{%ec\xe3\fo\x92\x89\xa8\xe2p/\bR\x88\b3\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xeb\xe4l\xc3\xc3L2\xf5\xad\xd6\xc3\x19[\xb4\x86\xc4q>\xb9\x18\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xeb\xff\x84\xbb\xefB0q\xe6\x04\xc3a\xbb\xa6w\xf5Y=\xefN\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xec\t'\xba\xc7\xdc6f\x9c(5J\xb1\xbe\x83\xd7\xee\xc3\t4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\x0e\x18\xa0\x1d\xc4\xdc]\xaa\xe5g\xc3\xfaL\u007f\x8f\x9bY\x02\x05\x89\x11\x1f\xfe@JA\xe6\x00\x00\xe0\x94\xec\x116,\xec\x81\t\x85\xd0\xeb\xbd{sE\x14D\x98[6\x9f\x8a\x06ZNIWpW1\x80\x00\u07d4\xec,\xb8\xb97\x8d\xff1\xae\xc3\xc2.\x0em\xad\xff1J\xb5\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\xec0\xad\u0749[\x82\xee1\x9eT\xfb\x04\xcb+\xb09q\xf3k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec;\x8bX\xa1'\x03\xe5\x81\xce_\xfd~!\xc5}\x1e\\f?\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xecHg\xd2\x17Z\xb5\xb9F\x93aYUFUF\x84\u0364`\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xecM\b\xaa.GIm\u0287\"]\xe3?+@\xa8\xa5\xb3o\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\xecX\xbc\r\f \xd8\xf4\x94efAS\xc5\xc1\x96\xfeY\u6f89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xec[\x19\x8a\x00\u03f5Z\x97\xb5\xd56D\xcf\xfa\x8a\x04\u04abE\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec]\xf2'\xbf\xa8]z\xd7kBn\x1c\xee\x96;\xc7\xf5\x19\u074965\u026d\xc5\u07a0\x00\x00\xe0\x94\xec_\xea\xfe!\f\x12\xbf\u0265\xd0Y%\xa1#\xf1\xe7?\xbe\xf8\x8a`\x8f\xcf=\x88t\x8d\x00\x00\x00\u07d4\xeci\x04\xba\xe1\xf6\x97\x90Y\x17\t\xb0`\x97\x83s?%s\xe3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xecs\x11L^@o\u06fe\t\xb4\xfab\x1b\xd7\x0e\xd5N\xa1\xef\x8a\x050%\xcd!o\xceP\x00\x00\u07d4\xecs\x83=\xe4\xb8\x10\xbb\x02x\x10\xfc\x8fi\xf5D\xe8<\x12\u044965\u026d\xc5\u07a0\x00\x00\u07d4\xecu\xb4\xa4u\x13\x12\v\xa5\xf8`9\x81O\x19\x98\xe3\x81z\u00c9\t\xb0\xbc\xe2\xe8\xfd\xba\x00\x00\u07d4\xecv\xf1.W\xa6U\x04\x03?,\v\xceo\xc0;\xd7\xfa\n\u0109\xc2\x12z\xf8X\xdap\x00\x00\u0794\xec\x80\x14\xef\xc7\xcb\xe5\xb0\xceP\xf3V,\xf4\xe6\u007f\x85\x93\xcd2\x88\xf0\x15\xf2W6B\x00\x00\u07d4\xec\x82\xf5\r\x06G_hM\xf1\xb3\x92\xe0\r\xa3A\xaa\x14TD\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xec\x83\xe7\x98\u00d6\xb7\xa5^*\"$\xab\u0343K'\xeaE\x9c\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xec\x89\xf2\xb6x\xa1\xa1[\x914\xec^\xb7\fjb\a\x1f\xba\xf9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xec\x8c\x1d{j\xac\xcdB\x9d\xb3\xa9\x1e\xe4\xc9\xeb\x1c\xa4\xf6\xf7<\x89\xe6d\x99\"\x88\xf2(\x00\x00\xe0\x94\xec\x98Q\xbd\x91rpa\x02g\xd6\x05\x18\xb5M<\xa2\xb3[\x17\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xec\x99\xe9]\xec\xe4o\xff\xfb\x17^\xb6@\x0f\xbe\xbb\b\ue6d5\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xec\xa5\xf5\x87\x92\xb8\xc6-*\xf5Vq~\xe3\xee0(\xbeM\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\xabZ\xba[\x82\x8d\xe1pS\x81\xf3\x8b\xc7D\xb3+\xa1\xb47\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\xec\xaf3P\xb7\xce\x14M\x06\x8b\x18`\x10\x85,\x84\xdd\f\xe0\xf0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\xb9LV\x8b\xfeY\xad\xe6Pd_O&0lsl\xac\xe4\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xec\xbeB^g\r9\tN \xfbVC\xa9\xd8\x18\xee\xd26\u078a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xec\xbe^\x1c\x9a\u04b1\xdc\xcf\n0_\xc9R/Fi\xdd:\xe7\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xec\xcfz\x04W\xb5f\xb3F\xcag:\x18\x0fDA0!j\u00c9\x05k\xc7^-c\x10\x00\x00\u07d4\xec\u0466(\x025\x1aAV\x8d#\x030\x04\xac\xc6\xc0\x05\xa5\u04c9\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xec\xd2v\xafd\u01dd\x1b\u0669+\x86\xb5\u835a\x95\xeb\x88\xf8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xec\u0506\xfc\x19g\x91\xb9,\xf6\x12\xd3HaO\x91VH\x8b~\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xec\xda\xf92)\xb4^\xe6r\xf6]\xb5\x06\xfb^\xca\x00\xf7\xfc\xe6\x89W\x01\xf9m\xcc@\xee\x80\x00\u07d4\xec\xe1\x11g\vV<\u037e\xbc\xa5#\x84)\x0e\xcdh\xfe\\\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xec\xe1\x15&\x82\xb7Y\x8f\xe2\xd1\xe2\x1e\xc1U3\x88T5\xac\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xec\xe1)\bw\xb5\x83\xe3a\xa2\xd4\x1b\x00\x93F\xe6'N%8\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xec\xf0]\a\xea\x02n~\xbfIA\x00#5\xba\xf2\xfe\xd0\xf0\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xec\xf2L\xdd|\"\x92\x8cD\x1eiM\xe4\xaa1\xb0\xfa\xb5\x97x\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xec\xfd\x00M\x02\xf3l\xd4\u0634\xa8\xc1\xa9S;j\xf8\\\xd7\x16\x8a\x01\x0fA\xac\xb4\xbb;\x9c\x00\x00\xe0\x94\xed\x02\x06\xcb#1Q(\xf8\xca\xff&\xf6\xa3\v\x98Tg\xd0\"\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xed\x10e\xdb\u03dds\xc0O\xfcy\b\x87\r\x88\x14h\xc1\xe12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xed\x12vQ;o\u0186(\xa7A\x85\xc2\xe2\f\xbb\xcax\x17\xbf\x89\nZ\xa8P\t\xe3\x9c\x00\x00\xe0\x94\xed\x12\xa1\xba\x1f\xb8\xad\xfc\xb2\r\xfa\x19X.RZ\xa3\xb7E$\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xed\x16\xce9\xfe\xef;\xd7\xf5\xd1b\x04^\x0fg\xc0\xf0\x00F\xbb\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xed\x1a\\C\xc5t\xd4\xe94)\x9b$\xf1G,\u071f\xd6\xf0\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xed\x1b$\xb6\x91-Q\xb34\xac\r\xe6\xe7q\xc7\xc0EF\x95\xea\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xed\x1f\x1e\x11Z\r`\xce\x02\xfb%\xdf\x01M(\x9e:\f\xbe}\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xed10\\1\x9f\x92s\u04d3m\x8f[/q\u9c72)c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xed2z\x14\xd5\u03ed\u0641\x03\xfc\t\x99q\x8d~\xd7\x05(\xea\x89N\x10\x03\xb2\x8d\x92\x80\x00\x00\u07d4\xed<\xbc7\x82\u03bdg\x98\x9b0\\A3\xb2\xcd\xe3\"\x11\xeb\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xed@\x14S\x8c\xeefJ/\xbc\xb6\xdcf\x9fz\xb1m\v\xa5|\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xedA\u188f\\\xaa\x848\x80\xefN\x8b\b\xbdl3\x14\x1e\u07c9*\xd5\xdd\xfaz\x8d\x83\x00\x00\xe0\x94\xedK\xe0J\x05-z\u0333\xdc\u03901\x9d\xba@ \xab,h\x8a\a\xf3zp\xea\xf3b\x17\x80\x00\xe0\x94\xedR\xa2\xcc\bi\u071e\x9f\x84+\u0415|G\xa8\xe9\xb0\xc9\xff\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xed[LA\xe7b\xd9B@Cs\xca\xf2\x1e\xd4a]%\xe6\xc1\x89m-O=\x95%\xb4\x00\x00\u07d4\xed`\u012bnT\x02\x061~5\x94zc\xa9\xcak\x03\xe2\u02c9\x03\x1a\u066d\vF\u007f\x80\x00\u07d4\xedd\x1e\x066\x8f\xb0\xef\xaa\x17\x03\xe0\x1f\xe4\x8fJhS\t\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xedfC\xc0\xe8\x88K-2\x11\x857\x85\xa0\x8b\xf8\xf3>\u049f\x89Hz\x9a0E9D\x00\x00\xe0\x94\xedp\xa3|\xdd\x1c\xbd\xa9tm\x93\x96X\xae*a\x81(\x85x\x8a\x02\bj\xc3Q\x05&\x00\x00\x00\u07d4\xedsFvn\x1agm\r\x06\xec\x82\x18g\xa2v\xa0\x83\xbf1\x89\u064a\t1\xcc-I\x00\x00\u07d4\xed\x86&\x16\xfc\xbf\xb3\xbe\xcbt\x06\xf7<\\\xbf\xf0\f\x94\aU\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xed\x9e\x03\f\xa7\\\xb1\u049e\xa0\x1d\rL\xdf\xdc\xcd8D\xb6\xe4\x89\x01\xac\xc1\x16\u03ef\xb1\x80\x00\xe0\x94\ud7bc\u02e4/\x98\x15\xe7\x823&m\xd6\xe85\xb6\xaf\xc3\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\ud7f1\xf5\xaf/\xbf\u007f\xfcP)\xce\xe4+p\xff\\'[\xf5\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xed\xa4\xb2\xfaY\u0584\xb2z\x81\r\xf8\x97\x8as\xdf0\x8ac\u0089\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xed\xb4s59y\xa2\x06\x87\x9d\xe1D\xc1\n:\xcf\x12\xa7'OV9a\xf57R\x9d\x89\xc7\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xeer\x88\xd9\x10\x86\xd9\xe2\xeb\x91\x00\x14\u066b\x90\xa0-x\u00a0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xee|=\xed|(\xf4Y\xc9/\xe1;M\x95\xba\xfb\xab\x026}\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\xee\x86} \x91k\xd2\xe9\xc9\xec\xe0\x8a\xa0C\x85\xdbf|\x91.\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\ue25b\x02\xcb\xcb99\xcda\xde\x13B\xd5\x04\x82\xab\xb6\x852\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xee\x90m}_\x17H%\x81t\xbeL\xbc8\x93\x03\x02\xab{B\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\ue5ea\x8a\u019e\xdfz\x98}mp\x97\x9f\x8e\xc1\xfb\xcaz\x94\x89\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\xee\xa1\xe9y\x88\xdeu\xd8!\xcd(\xadh\"\xb2,\u0398\x8b1\x89\x1c0s\x1c\xec\x03 \x00\x00\xe0\x94\xee\u048c?\x06\x8e\tJ0K\x85<\x95\nh\t\xeb\xcb\x03\xe0\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xee\u04c4\xef-A\xd9\xd2\x03\x97NW\xc1#(\xeav\x0e\b\xea\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xee\xdflB\x80\xe6\xeb\x05\xb94\xac\xe4(\xe1\x1dB1\xb5\x90[\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xee\xe7a\x84~3\xfda\u0653\x87\xee\x14b\x86\x94\u047f\xd5%\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xee\xe9\xd0Rn\xda\x01\xe41\x16\xa3\x952-\u0689pW\x8f9\x8a\x02\x1e\x19\x99\xbb\xd5\u04be\x00\x00\u07d4\xee\xf1\xbb\xb1\xe5\xa8?\u0782H\xf8\x8e\xe3\x01\x8a\xfa-\x132\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xee\xfb\xa1-\xfc\x99gB\xdby\x04d\xca}';\xe6\xe8\x1b>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xee\xfd\x05\xb0\xe3\xc4\x17\xd5[3C\x06\x04\x86\xcd\xd5\xe9*\xa7\xa6\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xef\r\xc7\xddzS\xd6\x12r\x8b\xcb\u04b2|\x19\xddM}fo\x89&A\x1c[5\xf0Z\x00\x00\u07d4\xef\x11RR\xb1\xb8E\u0345\u007f\x00-c\x0f\x1bo\xa3zNP\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xef\x1c\x04w\xf1\x18M`\xac\u02b3t\xd3tUz\n>\x10\xf3\x89\b=lz\xabc`\x00\x00\u07d4\xef,4\xbbH}7b\xc3\u0327\x82\xcc\xddz\x8f\xbb\n\x991\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xef5\xf6\u0531\a^j\xa19\x15\x1c\x97K/FX\xf7\x058\x89<;\xc3?\x94\xe5\r\x80\x00\u07d4\xef9\u0291s\xdf\x15S\x1ds\xe6\xb7*hKQ\xba\x0f+\xb4\x89V\xa0\xb4un\xe28\x00\x00\u07d4\xefF<&y\xfb'\x91d\xe2\f=&\x915\x87s\xa0\xad\x95\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xefG\xcf\a>6\xf2q\xd5\"\xd7\xfaNq \xadP\a\xa0\xbc\x89\x87\x86x2n\xac\x90\x00\x00\u07d4\xefa\x15[\xa0\t\xdc\u07be\xf1\v(\xd9\xda=\x1b\xc6\xc9\xce\u0509\x034-`\xdf\xf1\x96\x00\x00\u0794\xefix\x1f2\xff\xce34o,\x9a\xe3\xf0\x84\x93\xf3\xe8/\x89\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xefv\xa4\u034f\xeb\xcb\u0278\x18\xf1x(\xf8\xd94s\xf3\xf3\u02c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\uf4c1\x8fhM\xb0\xc3g^\xc8\x132\xb3\x18>\xcc(\xa4\x95\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94\xef\x9fY\xae\xdaA\x8c\x14\x94h-\x94\x1a\xabI$\xb5\xf4\x92\x9a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\uf9b1\xf0\xdb`57\x82h\x91\xb8\xb4\xbc\x169\x84\xbb@\u03495e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xef\xbdR\xf9}\xa5\xfd:g:F\xcb\xf30D{~\x8a\xad\\\x89\x05l<\x9b\x80\xa0\xa6\x80\x00\xe0\x94\xef\xc8\xcf\x19c\u0269Rg\xb2(\xc0\x86#\x98\x89\xf4\xdf\xd4g\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xef\u02ae\x9f\xf6M,\xd9[RI\xdc\xff\xe7\xfa\xa0\xa0\xc0\xe4M\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xef\xcc\xe0k\xd6\b\x9d\x0eE\x8e\xf5a\xf5\xa6\x89H\n\xfep\x00\x89 \x86\xac5\x10R`\x00\x00\u07d4\xef\xe0g]\xa9\x8a]\xdap\u0356\x19k\x87\xf4\xe7&\xb43H\x89?\x19\xbe\xb8\xdd\x1a\xb0\x00\x00\u07d4\xef\xe8\xff\x87\xfc&\x0e\agc\x8d\xd5\xd0/\xc4g.\x0e\xc0m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xef\xeb\x19\x97\xaa\xd2w\xcc3C\x0ea\x11\xed\tCY@H\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xef\xee\xa0\x10uo\x81\xdaK\xa2[r\x17\x87\xf0X\x17\v\uff49\x01\u009c\x9c\xf7p\xef\x00\x00\u07d4\xef\xf5\x1dr\xad\xfa\xe1C\xed\xf3\xa4+\x1a\xecU\xa2\xcc\xdd\v\x90\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xef\xf8kQ#\xbc\xdc\x17\xedL\xe8\xe0[~\x12\xe5\x13\x93\xa1\xf7\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xef\xfc\x15\u41f1\xbe\xda\n\x8d\x13%\xbd\xb4\x17\"@\xdcT\n\x89\x03\x8599\xee\xe1\xde\x00\x00\xe0\x94\xf0\x11\x95\xd6W\xef<\x94.l\xb89I\xe5\xa2\v\\\xfa\x8b\x1e\x8a\x05ts\xd0]\xab\xae\x80\x00\x00\u07d4\xf0'\x96)Q\x01gB\x88\xc1\xd94g\x05=\x04\"\x19\xb7\x94\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\u07d4\xf09h={=\"[\xc7\xd8\u07ed\xefc\x164A\xbeA\xe2\x89\x01\xdd\x1eK\xd8\xd1\xee\x00\x00\u07d4\xf0Jj7\x97\b\xb9B\x8dr*\xa2\xb0kw\xe8\x895\u03c9\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xf0M,\x91\xef\xb6\xe9\xc4_\xfb\xe7KCL\x8c_+\x02\x8f\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf0W\xaaf\xcav~\xde\x12J\x1c[\x9c\xc5\xfc\x94\xef\v\x017\x89p\xa2K\u02b6\xf4]\x00\x00\u07d4\xf0[\xa8\u05f6\x859\xd930\v\xc9(\x9c=\x94t\xd0A\x9e\x89\x06\xda'\x02M\xd9`\x00\x00\u07d4\xf0\\\xee\xabeA\x05dp\x99Qw<\x84E\xad\x9fN\u01d7\x89\x10C\x16'\xa0\x93;\x00\x00\xe0\x94\xf0_\xcdL\rs\xaa\x16~US\xc8\xc0\xd6\xd4\xf2\xfa\xa3\x97W\x8a\x02\xd2\xd6l1p\xb2\x98\x00\x00\u07d4\xf0g\xe1\xf1\u0583UjL\xc4\xfd\f\x03\x13#\x9f2\xc4\xcf\u060965\u026d\xc5\u07a0\x00\x00\u07d4\xf0g\xfb\x10\u07f2\x93\u962b\xe5d\xc0U\xe34\x8f\x9f\xbf\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0h\xdf\xe9]\x15\xcd:\u007f\x98\xff\xa6\x88\xb44hB\xbe&\x90\x89D\n\xd8\x19\xe0\x97L\x00\x00\xe0\x94\xf0j\x85J<]\xc3m\x1cI\xf4\xc8}m\xb33\xb5~J\u074a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf0y\xe1\xb1&_P\xe8\u0229\x8e\xc0\u01c1^\xb3\xae\xac\x9e\xb4\x89\x01\x16\xdc:\x89\x94\xb3\x00\x00\xe0\x94\xf0{\xd0\xe5\xc2\xcei\xc7\u0127$\xbd&\xbb\xfa\x9d*\x17\xca\x03\x8a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\xf0\x83*k\xb2U\x03\xee\xcaC[\xe3\x1b\v\xf9\x05\xca\x1f\xcfW\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf0\x9b>\x87\xf9\x13\xdd\xfdW\xae\x80I\xc71\u06e9\xb66\xdf\u00c9 \xf5\xb1\uab4d\x80\x00\x00\u07d4\xf0\xb14\v\x99oo\v\xf0\xd9V\x1c\x84\x9c\xaf\u007fD0\xbe\xfa\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf0\xb1\xf9\xe2x2\xc6\xdei\x14\xd7\n\xfc#\x8ct\x99\x95\xac\xe4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf0\xb4i\xea\xe8\x9d@\f\xe7\xd5\xd6j\x96\x95\x03p6\xb8\x89\x03\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf0\xb9\u0583\u03a1+\xa6\x00\xba\xac\xe2\x19\xb0\xb3\xc9~\x8c\x00\xe4\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf0\xbe\x0f\xafMy#\xfcDF\"\u0458\f\xf2\u0650\xaa\xb3\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0\xc0\x81\xdaR\xa9\xae6d*\xdf^\b _\x05\xc5Ah\xa6\x89\x06\x04o7\xe5\x94\\\x00\x00\u07d4\xf0\xc7\r\rm\xabvc\xaa\x9e\xd9\xce\xeaV~\xe2\u01b0'e\x89qC\x8a\u0167\x91\xa0\x80\x00\u07d4\xf0\xcb\xef\x84\xe1ic\x00\x98\xd4\xe3\x01\xb2\x02\b\xef\x05\x84j\u0249\x0e\v\x83EPkN\x00\x00\u07d4\xf0\xd2\x16c\u0630\x17n\x05\xfd\xe1\xb9\x0e\xf3\x1f\x850\xfd\xa9_\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xf0\xd5\xc3\x1c\xcbl\xbe0\xc7\xc9\xea\x19\xf2h\xd1Y\x85\x1f\x8c\x9c\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\xf0\xd6L\xf9\xdf\tt\x113\xd1pH_\xd2K\x00P\x11\xd5 \x89\x1b\b\x93A\xe1O\xcc\x00\x00\u07d4\xf0\xd8X\x10^\x1bd\x81\x01\xac?\x85\xa0\xf8\"+\xf4\xf8\x1dj\x89 \x86\xac5\x10R`\x00\x00\u07d4\xf0\xdcC\xf2\x05a\x91'P{+\x1c\x1c\xfd\xf3-(1\t \x89\x10^\xb7\x9b\x94\x17\b\x80\x00\u07d4\xf0\xe1\u07e4*\u07ac/\x17\xf6\xfd\xf5\x84\xc9Hb\xfdV3\x93\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf0\xe2d\x9c~j?,]\xfe3\xbb\xfb\xd9'\xca<5\nX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0\xe7\xfb\x9eB\nS@\xd56\xf4\x04\b4O\xea\xef\xc0j\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf1\x04b\xe5\x8f\xcc\a\U000d5121\x87c\x94Q\x16~\x85\x92\x01\x89\t4\xdd]3\xbc\x97\x00\x00\xe0\x94\xf1\x06a\xff\x94\x14\x0f >zH%rCy8\xbe\xc9\xc3\xf7\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xf1\x14\xff\r\x0f$\xef\xf8\x96\xed\xdeTq\u07a4\x84\x82J\x99\xb3\x88\xbe -j\x0e\xda\x00\x00\u07d4\xf1\x16\xb0\xb4h\x0fS\xabr\xc9h\xba\x80.\x10\xaa\x1b\xe1\x1d\u0209\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xf1\x1c\xf5\xd3cto\xeehd\xd3\xca3m\xd8\x06y\xbb\x87\xae\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xf1\x1e\x01\u01e9\xd1$\x99\x00_M\xaew\x16\tZ4\x17bw\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf1;\b0\x93\xbaVN-\xc61V\x8c\xf7T\r\x9a\x0e\xc7\x19\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf1O\x0e\xb8m\xb0\xebhu?\x16\x91\x8e]K\x80t7\xbd>\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf1Qx\xff\xc4:\xa8\a\x0e\xce2~\x93\x0f\x80\x9a\xb1\xa5O\x9d\x89\n\xb6@9\x12\x010\x00\x00\u07d4\xf1V\xdc\v*\x98\x1e[U\xd3\xf2\xf0;\x814\xe31\u06ed\xb7\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf1]\x9dZ!\xb1\x92\x9ey\x03q\xa1\u007f\x16\xd9_\fie\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1^\x18,O\xbb\xady\xbd\x934\"B\xd4\xdc\xcf+\xe5\x89%\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf1bM\x98\ve3o\xea\u0166\xd5A%\x00\\\xfc\xf2\xaa\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1g\xf5\x86\x8d\xcfB3\xa7\x83\x06\th,\xaf-\xf4\xb1\xb8\a\x89\x81\xe5B\xe1\xa78?\x00\x00\u07d4\xf1m\xe1\x89\x1d\x81\x96F\x13\x95\xf9\xb16&[;\x95F\xf6\xef\x89\x01\xb2\x8e\x1f\x98\xbb\u0380\x00\u07d4\xf1z\x92\xe06\x1d\xba\xce\xcd\xc5\xde\r\x18\x94\x95Z\xf6\xa9\xb6\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1z\xdbt\x0fE\u02fd\xe3\tN~\x13qo\x81\x03\xf5c\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1\x8b\x14\xcb\xf6iC6\xd0\xfe\x12\xac\x1f%\xdf-\xa0\xc0]\xbb\x89\xd8\xd4`,&\xbfl\x00\x00\u07d4\xf1\x9b98\x9dG\xb1\x1b\x8a,?\x1d\xa9\x12M\xec\xff\xbe\xfa\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1\x9f\x195\b9>M*\x12{ \xb2\x03\x1f9\xc8%\x81\u0189\xbd\xbdz\x83\xbd/l\x00\x00\u07d4\xf1\xa1\xf3 @yd\xfd<\x8f.,\u0224X\r\xa9O\x01\xea\x89ll!wU|D\x00\x00\u07d4\xf1\xb4\xec\xc65%\xf7C,=\x83O\xfe+\x97\x0f\xbe\xb8r\x12\x89\xa2\xa2@h\xfa\u0340\x00\x00\u07d4\U000753ef\xfa\x87\x94\xf5\n\xf8\xe8\x83\t\u01e6&TU\xd5\x1a\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\xf1\xc8\u0129A\xb4b\x8c\rl0\xfd\xa5dR\u065c~\x1bd\x89N\x8c\xea\x1e\xdeu\x04\x00\x00\u07d4\xf1\xda@so\x99\xd5\xdf;\x06\x8a]t_\xaf\xc6F?\u0271\x89\x06\x96\xca#\x05\x8d\xa1\x00\x00\u07d4\xf1\u070a\xc8\x10B\xc6z\x9c\\c2!\xa8\xf76>e\x87\f\x9f(t$\u04a9`\x89J\xcfX\xe0rW\x10\x00\x00\u07d4\xf2B\u0684]B\u053fw\x9a\x00\xf2\x95\xb4\aP\xfeI\xea\x13\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf2RY\xa5\xc99\xcd%\x96l\x9bc\x03\xd3s\x1cS\u077cL\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf2^Lp\xbcFV2\u021eV%\xa82\xa7r/k\xff\xab\x89\xf3K\x82\xfd\x8e\x91 \x00\x00\u07d4\xf2k\xce\xdc\xe3\xfe\xad\u03a3\xbc>\x96\xeb\x10@\xdf\xd8\xff\u1809*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xf2py%v\xf0]QD\x93\xff\xd1\xf5\xe8K\xecK-\xf8\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf2s,\xf2\xc1;\x8b\xb8\xe7I*\x98\x8f_\x89\xe3\x82s\xdd\u0209 \x86\xac5\x10R`\x00\x00\xe0\x94\xf2t.hY\xc5i\xd5\xf2\x10\x83Q\xe0\xbfM\xca5*H\xa8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf2\x81:d\xc5&]\x02\x025\u02dc1\x9bl\x96\xf9\x06\xc4\x1e\x89\x12\xf99\u025e\u06b8\x00\x00\u07d4\xf2\x87\xffR\xf4a\x11z\xdb>\x1d\xaaq\x93-\x14\x93\xc6_.\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\xf2\xab\x11au\x02D\xd0\xec\xd0H\xee\r>Q\xab\xb1C\xa2\xfd\x89B\xfe+\x90ss\xbc\x00\x00\u07d4\xf2\xb4\xab,\x94'\xa9\x01^\xf6\xee\xff\xf5\xed\xb6\x019\xb7\x19\u0449&\u06d9*;\x18\x00\x00\x00\u07d4\xf2\xc0>*8\x99\x8c!d\x87`\xf1\xe5\xae~\xa3\a}\x85\"\x89\x8f?q\x93\xab\a\x9c\x00\x00\u0794\xf2\u0090N\x9f\xa6d\xa1\x1e\xe2VV\xd8\xfd,\xc0\u0665\"\xa0\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xf2\xc3b\xb0\xef\x99\x1b\xc8/\xb3nf\xffu\x93*\xe8\u0742%\x89\x04\x02\xf4\xcf\xeeb\xe8\x00\x00\u07d4\xf2\xd0\xe9\x86\xd8\x14\xea\x13\xc8\xf4f\xa0S\x8cS\u0712&Q\xf0\x89J\xcfX\xe0rW\x10\x00\x00\xe0\x94\xf2\u04775w$\xecL\x03\x18[\x87\x9bc\xf5~&X\x91S\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf2\xd5v<\xe0s\x12~,\xed\xdeo\xab\xa7\x86\xc7<\xa9AA\x8a\x01\xacB\x86\x10\x01\x91\xf0\x00\x00\xe0\x94\xf2\u055c\x89#u\x90s\xd6\xf4\x15\xaa\xf8\xeb\x06_\xf2\U000f614a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4\xf2\xe9\x9f\\\xbb\x83kz\xd3bGW\x1a0,\xbeKH\x1ci\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf2\xed>w%J\u02c3#\x1d\xc0\x86\x0e\x1a\x11$+\xa6'\u06c9kV\x05\x15\x82\xa9p\x00\x00\xe0\x94\xf2\xed\xde7\xf9\xa8\u00dd\u07a2My\xf4\x01WW\xd0k\xf7\x86\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xf2\xef\xe9e`\xc9\xd9{r\xbd6DxC\x88\\\x1d\x90\xc21\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf2\xfb\xb6\u0607\xf8\xb8\xcc:\x86\x9a\xba\x84\u007f=\x1fd\xfcc\x97\xaae\xfbS\xa8\xf0z\x0f\x89:\xae0\xe8\xbc\xee\x89|\xf28\x1fa\x9f\x15\x00\x00\u07d4\xf3@\x83\xec\xea8P\x17\xaa@\xbd\xd3^\xf7\xef\xfbL\xe7v-\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf3F\xd7\u0792t\x1c\b\xfcX\xa6M\xb5[\x06-\xde\x01-\x14\x89\x0f\xffk\x1fv\x1em\x00\x00\xe0\x94\xf3U\xd3\xec\f\xfb\x90}\x8d\xbb\x1b\xf3FNE\x81(\x19\v\xac\x8a\x01\v\x04n\u007f\r\x80\x10\x00\x00\u07d4\xf3m\xf0/\xbd\x89`sG\xaf\xce)i\xb9\xc4#jX\xa5\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3s\xe9\u06ac\f\x86u\xf5;yz\x16\x0fo\xc04\xaek#\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf3{BeG\xa1d-\x8032H\x14\xf0\xed\xe3\x11O\xc2\x12\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xf3{\xf7\x8cXu\x15G\x11\xcbd\r7\xeam(\xcf\xcb\x12Y\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf3\x82\xdfX1U\xd8T\x8f?\x93D\f\xd5\xf6\x8c\xb7\x9d`&\x8a8u}\x02\u007f\xc1\xfd\\\x00\x00\xe0\x94\xf3\x82\xe4\xc2\x04\x10\xb9Q\b\x9e\x19\xba\x96\xa2\xfe\xe3\xd9\x1c\xce~\x8a\x01\x11\xfaV\xee\u00a88\x00\x00\xe0\x94\xf3\x8al\xa8\x01hS~\x97M\x14\xe1\xc3\xd19\x90\xa4L,\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xf3\x9a\x9dz\xa3X\x1d\xf0~\xe4'\x9a\xe6\xc3\x12\xef!\x036X\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf3\xb6h\xb3\xf1M\x92\x0e\xbc7\x90\x92\u06d8\x03\x1bg\xb2\x19\xb3\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\U000fe679\x10<\xe7U\n\xa7O\xf1\xdb\x18\xe0\x9d\xfe2\xe0\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xc1\xab\u049d\xc5{A\xdc\x19-\x0e8M\x02\x1d\xf0\xb4\xf6\u0509\x97\xae\f\u07cf\x86\xf8\x00\x00\u07d4\xf3\xc4qm\x1e\xe5'\x9a\x86\xd0\x16:\x14a\x81\x81\xe1a6\u01c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf3\u030b\xcbU\x94e\xf8\x1b\xfeX;\u05eb\n#\x06E;\x9e\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf3\u0588\xf0k\xbd\xbfP\xf9\x93,AE\xcb\xe4\x8e\xcd\xf6\x89\x04\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf3\xdb\xcf\x13Z\u02dd\xee\x1aH\x9cY<\x02O\x03\u00bb\xae\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xde_&\xefj\xde\xd6\xf0m;\x91\x13F\xeep@\x1d\xa4\xa0\x89\x13:\xb3}\x9f\x9d\x03\x00\x00\u07d4\xf3\xdfc\xa9q\x99\x93308;>\xd7W\v\x96\u0101#4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xe7OG\f}:?\x003x\x0fv\xa8\x9f>\xf6\x91\xe6\u02c9\xa3\xcf\xe61\xd1Cd\x00\x00\u07d4\xf3\xeb\x19H\xb9Q\xe2-\xf1ax)\xbf;\x8d\x86\x80\xeckh\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf3\xf1\xfa9\x18\xca4\xe2\xcf~\x84g\v\x1fM\x8e\xca\x16\r\xb3\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\xf3\xf2O\u009e @?\xc0\xe8\xf5\xeb\xbbU4&\xf7\x82p\xa2\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf3\xfar5R\xa5\xd0Q.+b\xf4\x8d\xca{+\x81\x050[\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xf3\xfeQ\xfd\xe3D\x13\xc73\x18\xb9\xc8T7\xfe~\x82\x0fV\x1a\x896b2\\\u044f\xe0\x00\x00\u07d4\xf4\x00\xf9=_\\~?\xc3\x03\x12\x9a\xc8\xfb\f/xd\a\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\v\x13O\xea\"\u01b2\x9c\x84W\xf4\x9f\x00\x0f\x9c\xdax\x9a\u06c9 \x86\xac5\x10R`\x00\x00\u07d4\xf4\x15W\xdf\u07f1\xa1\xbd\xce\xfe\xfe.\xba\x1e!\xfe\nJ\x99B\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\x17z\r\x85\u050b\x0e&B\x11\xce*\xa2\xef\xd3\xf1\xb4\u007f\b\x89\xc2\xcc\xca&\xb7\xe8\x0e\x80\x00\u07d4\xf4/\x90R1\xc7p\xf0\xa4\x06\xf2\xb7h\x87\u007f\xb4\x9e\xee\x0f!\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xf42\xb9\u06ef\x11\xbd\xbds\xb6Q\x9f\xc0\xa9\x04\x19\x87q\xaa\u0189\b=lz\xabc`\x00\x00\u07d4\xf4=\xa3\xa4\xe3\xf5\xfa\xb1\x04\u029b\xc1\xa0\xf7\xf3\xbbJV\xf3Q\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xf4G\x10\x8b\x98\xdfd\xb5~\x87\x103\x88\\\x1a\xd7\x1d\xb1\xa3\xf9\x8a\x01v\xf4\x9e\xad4\x83P\x80\x00\u07d4\xf4O\x85Q\xac\xe93r\a\x12\xc5\u0111\u0376\xf2\xf9Qsl\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\xf4V\x05Z\x11\xab\x91\xfff\x8e.\xc9\"\x96\x1f*#\xe3\xdb%\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xf4V\xa7[\xb9\x96U\xa7A,\xe9}\xa0\x81\x81m\xfd\xb2\xb1\xf2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf4[\x1d\xcb.A\xdc'\xff\xa0$\u06ad\xf6\x19\xc1\x11u\xc0\x87\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xf4c\xa9\f\xb3\xf1>\x1f\x06CB66\xbe\xab\x84\xc1#\xb0m\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf4h\x90n~\xdffJ\xb0\u063e=\x83\xebz\xb3\xf7\xff\xdcx\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xf4i\x80\u3929\u049ajn\x90`E7\xa3\x11K\xcb(\x97\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf4kk\x9c|\xb5R\x82\x9c\x1d=\xfd\x8f\xfb\x11\xaa\xba\xe7\x82\xf6\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4\xf4v\xe1&\u007f\x86$|\xc9\b\x81o.z\xd58\x8c\x95-\xb0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf4v\xf2\xcbr\b\xa3.\x05\x1f\xd9N\xa8f)\x92c\x82\x87\xa2\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xf4{\xb14\xda0\xa8\x12\xd0\x03\xaf\x8d\u0338\x88\xf4K\xbfW$\x8a\x01\x19Y\xb7\xfe3\x95X\x00\x00\u07d4\xf4\x83\xf6\a\xa2\x1f\xcc(\x10\n\x01\x8cV\x8f\xfb\xe1@8\x04\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf4\x8e\x1f\x13\xf6\xafM\x84\xb3q\xd7\xdeK'=\x03\xa2c'\x8e\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xf4\x9cG\xb3\xef\xd8knj[\xc9A\x8d\x1f\x9f\xec\x81Ki\xef\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xf4\x9fo\x9b\xaa\xbc\x01\x8c\x8f\x8e\x11\x9e\x01\x15\xf4\x91\xfc\x92\xa8\xa4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf4\xa3g\xb1f\u0499\x1a+\xfd\xa9\xf5dc\xa0\x9f%,\x1b\x1d\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\xa5\x1f\xceJ\x1d[\x94\xb0q\x83\x89\xbaNx\x14\x13\x9c\xa78\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xf4\xa9\xd0\f\xef\xa9{zX\xef\x94\x17\xfcbg\xa5\x06\x909\xee\x89\x01.\x89(\u007f\xa7\x84\x00\x00\u07d4\xf4\xaa\xa3\xa6\x16>7\x06W{I\xc0v~\x94\x8ah\x1e\x16\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\xb1bn$\xf3\v\xca\xd9'!\xb2\x93r\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xf5U\xa2{\xb1\xe2\xfdN,\u01c4\xca\ue493\x9f\xc0n/\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\xf5X\xa2\xb2\xdd&\u0755\x93\xaa\xe0E1\xfd<<\u00c5Kg\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\xf5`H\xdd!\x81\u0523od\xfc\xec\xc6!T\x81\xe4*\xbc\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf5dB\xf6\x0e!i\x13\x95\u043f\xfa\xa9\x19M\xca\xff\x12\u2dc9\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xf5yqJE\xeb\x8fR\xc3\xd5{\xbd\xef\xd2\xc1[./\x11\u07c9T\x91YV\xc4\t`\x00\x00\u07d4\xf5\x93\xc6R\x85\xeek\xbdf7\U000fe3c9\xad@\u0509\xf6U\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf5\x98\xdb.\t\xa8\xa5\xee}r\r+\\C\xbb\x12m\x11\xec\u0089\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf5\x9d\xab\x1b\xf8\xdf\x112~a\xf9\xb7\xa1KV:\x96\xec5T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf5\x9f\x9f\x02\xbb\u024e\xfe\t~\xab\xb7\x82\x10\x97\x90!\x89\x8b\xfd\x8a\x02\x1e\x17\x1a>\xc9\xf7,\x00\x00\u07d4\xf5\xa5E\x9f\xcd\xd5\xe5\xb2s\x83\r\xf8\x8e\xeaL\xb7}\xda\u07f9\x89\x04\t\xe5+H6\x9a\x00\x00\u07d4\xf5\xa7gj\xd1H\xae\x9c\x1e\xf8\xb6\xf5\xe5\xa0\xc2\xc4s\xbe\x85\v\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf5\xb0h\x98\x9d\xf2\x9c%5w\xd0@Z\xden\x0eu(\xf8\x9e\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\xf5\xb6\xe9\x06\x1aN\xb0\x96\x16\aw\xe2gb\xcfH\xbd\u0635]\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xf5\xcf\xfb\xbabN~\xb3!\xbc\x83\xc6\f\xa6\x81\x99\xb4\xe3fq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf5\xd1ER\xb1\xdc\xe0\xd6\xdc\x1f2\r\xa6\xff\u02231\xcdo\f\x89Hz\x9a0E9D\x00\x00\xe0\x94\xf5\xd6\x1a\xc4\u0295G^[{\xff\xd5\xf2\xf6\x90\xb3\x16u\x96\x15\x8a\x06\x92\xae\x88\x97\b\x1d\x00\x00\x00\u07d4\xf5\xd9\xcf\x00\xd6X\xddEQzH\xa9\xd3\xf5\xf63T\x1aS=\x89\x06O_\xdfIOx\x00\x00\u07d4\xf5\xea\xdc\xd2\u0478ez\x12\x1f3\xc4X\xa8\xb1>v\xb6U&\x89\r\x8b\x0fZZ\xc2J\x00\x00\u07d4\xf6\a\xc2\x15\r>\x1b\x99\xf2O\xa1\xc7\xd5@\xad\xd3\\N\xbe\x1e\x89\xa7\xf1\xaa\a\xfc\x8f\xaa\x00\x00\u07d4\xf6\v\xd75T>k\xfd.\xa6\xf1\x1b\xffbs@\xbc\x03Z#\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf6\f\x1bE\xf1d\xb9X\x0e 'Z\\9\xe1\xd7\x1e5\xf8\x91\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf6\x0fb\xd797\x95?\xef5\x16\x9e\x11\xd8r\xd2\xea1~\xec\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xf6\x12\x83\xb4\xbd\x85\x04\x05\x8c\xa3`\u94d9\x9bb\xcb\xc8\xcdg\x89\r\xd2\xd5\xfc\xf3\xbc\x9c\x00\x00\u07d4\xf6\x17\xb9g\xb9\xbdH_v\x95\xd2\xefQ\xfbw\x92\u0618\xf5\x00\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf6\x18\u0671\x04A\x14\x80\xa8c\xe6#\xfcU#-\x1aOH\xaa\x89\x0eh\x9emD\xb1f\x80\x00\u07d4\xf6\"\u5126b>\xaa\xf9\x9f+\xe4\x9eS\x80\xc5\xcb\xcf\\\u0609\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf62\xad\xffI\r\xa4\xb7-\x126\xd0KQ\x0ft\xd2\xfa\xa3\u0349K\xe4\xe7&{j\xe0\x00\x00\u07d4\xf69\xac1\u069fg'\x1b\xd1\x04\x02\xb7eN\\\xe7c\xbdG\x89\x15\xaf\x0fB\xba\xf9&\x00\x00\u07d4\xf6:W\x9b\xc3\xea\u00a9I\x04\x10\x12\x8d\xbc\xeb\xe6\xd9\u0782C\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\xf6E\xdd|\x89\x00\x93\xe8\xe4\u022a\x92\xa6\xbb55\"\xd3\u0718\x89\aC\x9f\xa2\t\x9eX\x00\x00\xe0\x94\xf6H\xea\x89\xc2u%q\x01r\x94Ny\xed\xff\x84x\x03\xb7u\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xf6JJ\xc8\xd5@\xa9(\x9ch\xd9`\xd5\xfb|\xc4Zw\x83\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf6N\xcf!\x17\x93\x1cmSZ1\x1eO\xfe\xae\xf9\u0514\x05\xb8\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xf6O\xe0\x93\x9a\x8d\x1e\xea*\x0e\u035a\x970\xfdyX\xe31\t\x89\x01\x1d\xe1\xe6\xdbE\f\x00\x00\u07d4\xf6V\x16\xbe\x9c\x8by~t\x15\"|\x918\xfa\xa0\x89\x17B\u05c9*\xd3s\xcef\x8e\x98\x00\x00\u07d4\xf6W\xfc\xbeh.\xb4\xe8\xdb\x15.\u03c9$V\x00\vQ=\x15\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf6X\x19\xacL\xc1L\x13\u007f\x05\xddyw\xc7\xda\xe0\x8d\x1aJ\xb5\x89\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xf6{\xb8\xe2\x11\x8b\xbc\u0550'fn\xed\xf6\x94>\xc9\xf8\x80\xa5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf6\x84d\xbfd\xf2A\x13V\xe4\xd3%\x0e\xfe\xfe\\P\xa5\xf6[\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf6\x86x[\x89r\va\x14_\ua017\x8dj\u030e\v\xc1\x96\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf6\x8c^3\xfa\x97\x13\x9d\xf5\xb2\xe68\x86\xce4\xeb\xf3\u45dc\x89\xb3\xfaAi\xe2\xd8\xe0\x00\x00\u07d4\xf6\xa8cWW\xc5\xe8\xc14\xd2\r\x02\x8c\xf7x\u03c6\t\xe4j\x89O\x1dw/\xae\xc1|\x00\x00\u07d4\xf6\xb7\x82\xf4\xdc\xd7E\xa6\xc0\xe2\xe00`\x0e\x04\xa2K%\xe5B\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xf6\xbc7\xb1\u04a3x\x8dX\x9bm\xe2\x12\xdc\x17\x13\xb2\xf6\u738a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xf6\xc3\u010a\x1a\xc0\xa3G\x99\xf0M\xb8n\u01e9u\xfewh\xf3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf6\xd2]?=\x84m#\x9fR_\xa8\xca\xc9{\xc45x\u06ec\x890\x92\u007ft\xc9\xde\x00\x00\x00\u07d4\xf6\xea\xacp2\u0512\xef\x17\xfd`\x95\xaf\xc1\x1dcOV\xb3\x82\x89\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\xf6\xea\xd6}\xbf[~\xb13X\xe1\x0f6\x18\x9dS\xe6C\xcf\u03ca\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xf6\xf1\xa4C\t\x05\x1ck%\xe4}\xff\x90\x9b\x17\x9b\xb9\xabY\x1c\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf7\x03(\xef\x97b_\xe7E\xfa\xa4\x9e\xe0\xf9\u052a;\r\xfbi\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf7\n\x99\x8aq{3\x8d\x1d\u0658T@\x9b\x1a3\x8d\ue930\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7\rcz\x84\\\x06\xdbl\u0711\xe67\x1c\xe7\xc48\x8ab\x8e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\x15R\x13D\x98\x92tK\xc6\x0f.\x04@\a\x88\xbd\x04\x1f\u0749\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\xe0\x94\xf7\x1bE4\xf2\x86\xe40\x93\xb1\xe1^\xfe\xa7I\xe7Y{\x8bW\x8a\x16\x1c\x13\xd34\x1c\x87(\x00\x00\u07d4\xf74\xec\x03rM\xde\xe5\xbbRy\xaa\x1a\xfc\xf6\x1b\f\xb4H\xa1\x89\xe5\xbf,\u0270\x97\x80\x00\x00\u07d4\xf76\u0716v\x00\x128\x8f\xe8\x8bf\xc0n\xfeW\xe0\xd7\xcf\n\x89q\xd7Z\xb9\xb9 P\x00\x00\u07d4\xf7:\xc4l ;\xe1S\x81\x11\xb1Q\xec\x82 \u01c6\xd8AD\x89\x0f\xf77x\x17\xb8+\x80\x00\u07d4\xf7=\xd9\xc1B\xb7\x1b\xce\x11\xd0n0\xe7\xe7\xd02\xf2\uc71e\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf7A\x8a\xa0\xe7\x13\xd2H\"\x87v\xb2\xe7CB\"\xaeu\u3949lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7Nn\x14S\x82\xb4\u06c2\x1f\xe0\xf2\u0643\x88\xf4V\t\u019f\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf7P\f\x16o\x8b\xea/\x824v\x06\xe5\x02K\xe9\xe4\xf4\u0399\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7W\xfc\x87 \xd3\xc4\xfaRw\a^`\xbd\\A\x1a\xeb\xd9w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7[\xb3\x9cy\x97y\xeb\xc0J3m&\r\xa61F\xed\x98\u0409\x01Z\xf1\u05cbX\xc4\x00\x00\xe0\x94\xf7h\xf3!\xfdd3\xd9kO5M<\xc1e,\x172\xf5\u007f\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf7oi\xce\xe4\xfa\xa0\xa6;0\xae\x1ex\x81\xf4\xf7\x15ep\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf7w6\x1a=\u062bb\xe5\xf1\xb9\xb0GV\x8c\xc0\xb5UpL\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf7|{\x84QI\xef\xba\x19\xe2a\xbc|u\x15y\b\xaf\xa9\x90\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7\u007f\x95\x87\xffz-r\x95\xf1\xf5q\u0206\xbd3\x92jR|\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xf7\x82X\xc1$\x81\xbc\xdd\u06f7*\x8c\xa0\xc0C\tra\xc6\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\x98\xd1m\xa4\xe4`\xc4`\xcdH_\xae\x0f\xa0Y\x97\b\ub08965\u026d\xc5\u07a0\x00\x00\u07d4\xf7\xa1\xad\xe2\xd0\xf5)\x12=\x10U\xf1\x9b\x17\x91\x9fV!Ng\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf7\xac\xff\x93K\x84\xda\ti\xdc7\xa8\xfc\xf6C\xb7\xd7\xfb\xedA\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf7\xb1Q\xcc^W\x1c\x17\xc7e9\xdb\xe9\x96L\xbbo\xe5\xdey\x89tq|\xfbh\x83\x10\x00\x00\u07d4\xf7\xb2\x9b\x82\x19\\\x88-\xabx\x97\u00ae\x95\xe7w\x10\xf5xu\x89w5Aa2\xdb\xfc\x00\x00\u07d4\xf7\xbcLD\x91\rZ\xed\xd6n\xd25U8\xa6\xb1\x93\xc3a\xec\x89\x05A\xde,-\x8db\x00\x00\u07d4\xf7\xc0\f\xdb\x1f\x02\x03\x10\u056c\xab{Ij\xaaD\xb7y\b^\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xf7\xc1\xb4C\x96\x8b\x11{]\u0677UW/\xcd9\xca^\xc0K\x89\x18\xb9h\u0092\xf1\xb5\x00\x00\xe0\x94\xf7\xc5\x0f\x92*\xd1ka\xc6\u047a\xa0E\xed\x81h\x15\xba\u010f\x8a\x02\xa99j\x97\x84\xad}\x00\x00\u07d4\xf7\xc7\b\x01Pq\xd4\xfb\n:*\t\xa4]\x15c\x96\xe34\x9e\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf7\xcb\u06e6\xbel\xfeh\xdb\xc2<+\x0f\xf50\xee\x05\"o\x84\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\xd0\xd3\x10\xac\xea\x18@a8\xba\xaa\xbb\xfe\x05q\xe8\r\xe8_\x89Hz\x9a0E9D\x00\x00\u07d4\xf7\u05ef LV\xf3\x1f\xd9C\x98\xe4\r\xf1\x96K\u063f\x12<\x89\b!\xd2!\xb5)\x1f\x80\x00\u07d4\xf7\xdc%\x11\x96\xfb\u02f7|\x94}|\x19F\xb0\xffe\x02\x1c\xea\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf7\xe4Z\x12\xaaq\x1cp\x9a\xce\xfe\x95\xf3;xa-*\xd2*\x8a\x0e\x06U\xe2\xf2k\xc9\x18\x00\x00\u07d4\xf7\xf4\x89\x8cLRm\x95_!\xf0U\xcbnG\xb9\x15\xe5\x19d\x89|\b`\xe5\xa8\r\xc0\x00\x00\u07d4\xf7\xf9\x1ez\xcb[\x81)\xa3\x06\x87|\xe3\x16\x8eoC\x8bf\xa1\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4\xf7\xfcE\xab\xf7oP\x88\xe2\u5d68\xd12\xf2\x8aMN\xc1\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\x06:\xf4\xcc\x1d\xd9a\x9a\xb5\u063f\xf3\xfc\xd1\xfa\xa8H\x82!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\bnBf\x1e\xa9)\xd2\u0761\xablt\x8c\xe3\x05]\x11\x1e\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf8\bw\x86\xb4-\xa0N\xd6\xd1\xe0\xfe&\xf6\xc0\xee\xfe\x1e\x9fZ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf8\r6\x19p/\xa5\x83\x8cH9\x18Y\xa89\xfb\x9c\xe7\x16\x0f\x89l\a\xa7\u0471np\x00\x00\u07d4\xf8\x14y\x9fm\xdfM\xcb)\xc7\xee\x87\x0eu\xf9\xcc-52m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf8\x15\xc1\n\x03-\x13\xc3K\x89v\xfan;\xd2\xc9\x13\x1a\x8b\xa9\x89Hz\x9a0E9D\x00\x00\u07d4\xf8\x16\"\xe5WW\xda\xeafu\x97]\xd958\xda}\x16\x99\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8$\xee3\x1eJ\xc3\xccXv\x939[W\xec\xf6%\xa6\xc0\u0089V\xc9]\xe8\xe8\xca\x1d\x00\x00\u07d4\xf8'\xd5n\xd2\xd3' \u052b\xf1\x03\xd6\xd0\xefM;\xcdU\x9b\x89\x01l\x80\x06W\x91\xa2\x80\x00\u07d4\xf8)\x85\x91R>P\xb1\x03\xf0\xb7\x01\xd6#\xcb\xf0\xf7EV\xf6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf8H\xfc\xe9\xaba\x1c}\x99 n#\xfa\u019a\u0508\xb9O\xe1\x89\x02\xa1\x12\x9d\t6r\x00\x00\u07d4\xf8O\t\n\xdf?\x8d\xb7\u1533P\xfb\xb7u\x00i\x9ff\xfd\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf8Q\xb0\x10\xf63\xc4\n\xf1\xa8\xf0js\ubeabe\az\xb5\x89\xee\x86D/\xcd\x06\xc0\x00\x00\u07d4\xf8X\x17\x1a\x04\xd3W\xa1;IA\xc1n~U\xdd\u0514\x13)\x89\x02F\xa5!\x8f*\x00\x00\x00\u07d4\xf8[\xab\x1c\xb3q\x0f\xc0_\xa1\x9f\xfa\xc2.gR\x1a\v\xa2\x1d\x89l\x955\u007f\xa6\xb3l\x00\x00\u07d4\xf8j>\xa8\a\x1fp\x95\xc7\u06ca\x05\xaePz\x89)\u06f8v\x89\x126\xef\xcb\u02f3@\x00\x00\u07d4\xf8pL\x16\xd2\xfd[\xa3\xa2\xc0\x1d\x0e\xb2\x04\x84\xe6\xec\xfa1\t\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf8p\x99_\xe1\xe5\"2\x1duC7\xa4\\\f\x9d{8\x95\x1c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf8s\xe5ze\xc9;n\x18\xcbu\xf0\xdc\a}[\x893\xdc\\\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xf8ua\x9d\x8a#\xe4]\x89\x98\u0444\u0500\xc0t\x89p\x82*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf8{\xb0{(\x9d\xf70\x1eT\xc0\xef\xdaj,\xf2\x91\xe8\x92\x00\x89K\xe4\xe7&{j\xe0\x00\x00\u0794\xf8\x89\x00\xdbsyU\xb1Q\x9b\x1a}\x17\n\x18\x86L\xe5\x90\xeb\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xf8\x8bX\xdb7B\vFL\v\xe8\x8bE\xee+\x95)\x0f\x8c\xfa\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf8\x96+u\xdb]$\xc7\xe8\xb7\xce\xf1\x06\x8c>g\u03bb0\xa5\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xf8\xa0e\xf2\x87\xd9\x1dw\xcdbj\xf3\x8f\xfa\"\r\x9bU*+\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xf8\xa4\x9c\xa29\f\x1fm\\\x0ebQ;\a\x95qt?|\u0189\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf8\xa5\f\xee.h\x8c\xee\u3b24\u0522\x97%\xd4\a,\u0103\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xacJ9\xb5<\x110x \x97;D\x13e\xcf\xfeYof\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xae\x85{g\xa4\xa2\x89:?\xbe|z\x87\xff\x1c\x01\u01a6\xe7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf8\xbf\x9c\x04\x87NZw\xf3\x8fL8R~\x80\xc6v\xf7\xb8\x87\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xc7\xf3J8\xb3\x18\x01\xdaC\x064w\xb1+'\xd0\xf2\x03\xff\x89\x1a\u04ba\xbao\xefH\x00\x00\u07d4\xf8\xca3l\x8e\x91\xbd \xe3\x14\xc2\v-\xd4`\x8b\x9c\x8b\x94Y\x89-\u071b\u0173,x\x00\x00\u07d4\xf8\xd1t$\xc7g\xbe\xa3\x12\x05s\x9a+W\xa7'r\x14\uef89\x02F\xdd\xf9yvh\x00\x00\u07d4\xf8\xd5-\xcc_\x96\xcc(\x00{>\u02f4\t\xf7\xe2*dl\xaa\x89\b\x16\x90\xe1\x81(H\x00\x00\u07d4\xf8\xdc\xe8g\xf0\xa3\x9c[\xef\x9e\xeb\xa6\t\"\x9e\xfa\x02g\x8bl\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xf2&\x14*B\x844\xab\x17\xa1\x86J%\x97\xf6J\xab/\x06\x89\tY\x8b/\xb2\xe9\xf2\x80\x00\u07d4\xf8\xf6d^\r\xeedK=\xad\x81\xd5q\uf6ef\x84\x00!\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\x01\xc0\x0f\xc1\u06c8\xb6\x9cK\xc3%+\\\xa7\x0e\xa6\xee\\\xf6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9=[\xcb\x06D\xb0\xcc\xe5\xfc\u0763C\xf5\x16\x8f\xfa\xb2\x87}\x89\vb\a\xb6}&\xf9\x00\x00\u07d4\xf9W\x0e\x92L\x95\u07bbpa6\x97\x92\xcf.\xfe\u00a8-^\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf9d \x86\xb1\xfb\xaea\xa6\x80M\xbe_\xb1^\xc2\u04b57\xf4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9d\x88i\x85\x90\xdc;,UVB\xb8q4\x8d\xfa\x06z\u0549\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9d\u064d(\x170\xba5\xb2\xe3\xa3\x14yn{B\xfe\xdfg\x89S\xb0\x87`\x98\xd8\f\x00\x00\u07d4\xf9e\ri\x89\xf1\x99\xab\x1c\xc4ycm\xed0\xf2A\x02\x1fe\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\xe0\x94\xf9h\x83X$Y\x90\x8c\x82v'\xe8o(\xe6F\xf9\xc7\xfcz\x8a\x01\u0127\x877\xcd\u03f8\x00\x00\u07d4\xf9kL\x00voSsj\x85t\xf8\"\xe6GL/!\xda-\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9r\x9dH(,\x9e\x87\x16m^\xef-\x01\xed\xa9\xdb\xf7\x88!\x89\x05k\x83\xdd\xc7(T\x80\x00\u07d4\xf9v~N\xcbJY\x80Ru\b\u05fe\xc3\xd4^Ld\x9c\x13\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xf9x\xb0%\xb6B3U\\\xc3\xc1\x9a\xda\u007fA\x99\xc94\x8b\xf7\x8aT\xb4\v\x1f\x85+\xda\x00\x00\x00\u07d4\xf9{V\xeb\u0577z\xbc\x9f\xba\u02eb\u0514\xb9\xd2\xc2!\xcd\x03\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf9\x81\x1f\xa1\x9d\xad\xbf\x02\x9f\x8b\xfeV\x9a\xdb\x18\"\x8c\x80H\x1a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf9\x82Ps\fLa\xc5\u007f\x12\x985\xf2h\b\x94yEB\xf3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xf9\x894gr\x99^\xc1\x90o\xaf\xfe\xba*\u007f\xe7\u079ck\xab\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf9\x98\xca4\x11s\nl\xd1\x0etU\xb0A\x0f\xb0\xf6\xd3\xff\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\x9a\xeeDKW\x83\xc0\x93\xcf\xff\xd1\xc4c,\xf9\x90\x9f\xbb\x91\x1d/\x81\x92\xf8B\t\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xf9\xbf\xb5\x9dS\x8a\xfcHt\xd4\xf5\x94\x1b\b\xc9s\x0e8\xe2K\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf9\xdd#\x90\b\x18/\xb5\x19\xfb0\xee\xdd \x93\xfe\xd1c\x9b\xe8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9\u07ba\xec\xb5\xf39\xbe\xeaH\x94\xe5 K\xfa4\r\x06\u007f%\x89ZB\x84Fs\xb1d\x00\x00\xe0\x94\xf9\xe3tG@lA!\x97\xb2\u2bbc\x00\x1dn0\u024c`\x8a\x01\xc4y\xbbCI\xc0\xee\x00\x00\u07d4\xf9\xe7\"/\xaa\xf0\xf4\xda@\xc1\u0124\x0607:\t\xbe\u05f6\x89\x9bO\u0730\x94V$\x00\x00\u07d4\xf9\xec\xe0\"\xbc\xcd,\x924i\x11\xe7\x9d\xd5\x03\x03\xc0\x1e\x01\x88\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfa\x00\xc3v\xe8\x9c\x05\u81c1z\x9d\xd0t\x8d\x96\xf3A\xaa\x89\x89\x10M\r\x00\u04b7\xf6\x00\x00\u07d4\xfa\f\x1a\x98\x8c\x8a\x17\xad5(\xeb(\xb3@\x9d\xaaX\"_&\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xfa\x10_\x1a\x11\xb6\xe4\xb1\xf5`\x12\xa2y\"\xe2\xac-\xa4\x81/\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xfa\x14/\xe4~\u0697\xe6P;8k\x18\xa2\xbe\xdds\u0335\xb1\x89.\x15:\xd8\x15H\x10\x00\x00\u07d4\xfa\x14\xb5f#J\xbe\xe70B\xc3\x1d!qq\x82\u02e1J\xa1\x89\x11\xc7\xea\x16.x \x00\x00\u07d4\xfa\x19\xd6\xf7\xa5\x0fO\a\x98\x93\xd1g\xbf\x14\xe2\x1d\x00s\u0456\x89\x1c\xbb:?\xf0\x8d\b\x00\x00\u07d4\xfa\x1f\x19q\xa7u\xc3PO\xefPy\xf6@\xc2\u013c\xe7\xac\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfa'\x9b\xfd\x87g\xf9V\xbf\u007f\xa0\xbdV`\x16\x8d\xa7V\x86\xbd\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xfa'\xccI\xd0\vl\x98s6\xa8u\xae9\xdaX\xfb\x04\x1b.\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xfa(2\x99`=\x87X\xe8\u02b0\x82\x12],\x8f}DT)\x8a\x01[\xca\xcb\x1e\x05\x01\xae\x80\x00\u07d4\xfa+\xbc\xa1]?\u37ca2\x8e\x91\xf9\r\xa1Oz\xc6%=\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xfa/\u049d\x03\xfe\xe9\xa0x\x93\xdf:&\x9fV\xb7/.\x1ed\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xfa3U2\x85\xa9sq\x9a\r_\x95o\xf8a\xb2\u061e\xd3\x04\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfa:\fK\x90?n\xa5.\xa7\xab{\x88c\xb6\xa6\x16\xadfP\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfa:\x1a\xa4H\x8b5\x1a\xa7V\f\xf5\xeec\n/\xd4\\2\"\x89/\xa4~j\xa74\r\x00\x00\u07d4\xfaA\tq\xad\"\x9c06\xf4\x1a\u03c5/*\u0259(\x19P\x89\u0633\x11\xa8\xdd\xfa|\x00\x00\u07d4\xfaD\xa8U\xe4\x04\xc8m\f\xa8\xef3$%\x1d\xfb4\x9cS\x9e\x89T\"S\xa1&\xce@\x00\x00\xe0\x94\xfaR\x01\xfe\x13B\xaf\x110{\x91B\xa0A$<\xa9./\t\x8a 8\x11j:\xc0C\x98\x00\x00\xe0\x94\xfa`\x86\x8a\xaf\xd4\xffL\\W\x91K\x8e\u054bBWs\u07e9\x8a\x01\xcf\xe5\xc8\b\xf3\x9f\xbc\x00\x00\u07d4\xfag\xb6{O7\xa0\x15\t\x15\x11\x0e\xde\a;\x05\xb8S\xbd\xa2\x89#\x19\xba\x94sq\xad\x00\x00\u07d4\xfah\xe0\xcb>\xdfQ\xf0\xa6\xf2\x11\u0272\xcb^\a<\x9b\xff\xe6\x89\x0f\xc969(\x01\xc0\x00\x00\xe0\x94\xfaj7\xf0\x18\xe9yg\x93\u007f\xc5\xe8a{\xa1\u05c6\xdd_w\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\xfav\x06C[5l\xee%{\xd2\xfc\xd3\xd9\xea\xcb<\xd1\xc4\xe1\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xfaz\xdff\v\x8d\x99\xce\x15\x93=|_\a/<\xbe\xb9\x9d3\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\xfa\x86\xca'\xbf(T\u0648p\x83\u007f\xb6\xf6\xdf\xe4\xbfdS\xfc\x89\x11u~\x85%\xcf\x14\x80\x00\u07d4\xfa\x8c\xf4\xe6'i\x8c]W\x88\xab\xb7\x88\x04\x17\xe7P#\x13\x99\x89\xe6\x1a6\x96\xee\xf6\x10\x00\x00\u07d4\xfa\x8e;\x1f\x13C9\x00s}\xaa\xf1\xf6)\x9cH\x87\xf8[_\x89&\u009eG\u0104L\x00\x00\u07d4\xfa\x9e\xc8\xef\xe0\x86\x86\xfaX\xc1\x813Xr\xbai\x85`\ucac9lj\xccg\u05f1\xd4\x00\x00\u07d4\xfa\xad\x90]\x84|{#A\x8a\xee\xcb\xe3\xad\u06cd\xd3\xf8\x92J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xfa\xae\xba\x8f\xc0\xbb\xdaU<\xa7.0\xef=s.&\xe8 A\x89H\x8d(*\xaf\xc9\xf6\x80\x00\u07d4\xfa\xb4\x87P\r\xf2\x0f\xb8>\xbe\xd9\x16y\x1dV\x17r\xad\xbe\xbf\x89lkLM\xa6\u077e\x00\x00\u07d4\xfa\xc5\u0294u\x80x\xfb\xfc\xcd\x19\xdb5X\xda~\u8827h\x897(\xa6+\r\xcf\xf6\x00\x00\u07d4\xfa\xd9j\xb6\xacv\x8a\xd5\t\x94R\xacGw\xbd\x1aG\xed\u010f\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xfa\xe7g\x19\xd9~\xacA\x87\x04(\xe9@'\x9d\x97\xddW\xb2\xf6\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4\xfa\u8053pG\x89Zf\f\xf2)v\x0f'\xe6h(\xd6C\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xfa\xe9,\x13p\xe9\u115a]\xf8;V\xd0\xf5\x86\xaa;@L\x89\x05\u0174\xf3\xd8C\x98\x00\x00\xe0\x94\xfa\xf5\xf0\xb7\xb6\xd5X\xf5\t\r\x9e\xa1\xfb-B%\x9cX`x\x8a\x01Z\xff\xb8B\fkd\x00\x00\xe0\x94\xfb\x12o\x0e\xc7i\xf4\x9d\xce\xfc\xa2\xf2\x00(dQX0\x84\xb8\x8a\x01\x0f\xcb\xc25\x03\x96\xbf\x00\x00\xe0\x94\xfb\x13^\xb1Z\x8b\xacr\xb6\x99\x154*`\xbb\xc0k~\a|\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xfb\"<\x1e\"\xea\xc1&\x9b2\xee\x15jS\x85\x92.\xd3o\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb7\xcfkO\x81\xa9\xe2\"\xfb\xa2.\x9b\xd2KP\x98\xb73\u03c9\x02\x1auJm\xc5(\x00\x00\u07d4\xfb8`\xf4\x12\x1cC.\xbd\xc8\xecj\x031\xb1\xb7\ty.\x90\x89 \x8c9J\xf1\u0208\x00\x00\u07d4\xfb9\x18\x9a\xf8v\xe7b\xc7\x1dl>t\x18\x93\xdf\"l\xed\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfb:\v\rkjq\x8fo\xc0)*\x82]\xc9$z\x90\xa5\u0409\n\xd6\xdd\x19\x9e\x97[\x00\x00\xe0\x94\xfb?\xa1\xac\b\xab\xa9\xcc;\xf0\xfe\x9dH8 h\x8fe\xb4\x10\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xfb?\xe0\x9b\xb86\x86\x15)\xd7Q\x8d\xa2v5\xf58PV\x15\x89K\xe3\x92\x16\xfd\xa0p\x00\x00\xe0\x94\xfbQ%\xbf\x0f^\xb0\xb6\xf0 \xe5k\xfc/\xdf=@,\t~\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\xfbU\x18qL\xef\xc3m\x04\x86]\xe5\x91^\xf0\xffG\xdf\xe7C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb_\xfa\xa0\xf7aW&5x\x91GX\x18\x93\x9d 7\u03d6\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfbh\\\x15\xe49\x96^\xf6&\xbf\r\x83L\u0468\x9f+V\x95\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xfbtK\x95\x1d\tK1\x02b\xc8\xf9\x86\xc8`\u07da\xb1\xdee\x89\x02\xd1\xc5\x15\xf1\xcbJ\x80\x00\u07d4\xfby\xab\u06d2\\U\xb9\xf9\x8e\xfe\xefd\xcf\xc9\xeba\xf5\x1b\xb1\x89a@\xc0V\xfb\n\xc8\x00\x00\u07d4\xfb\x81\x13\xf9M\x91s\xee\xfdZ0s\xf5\x16\x80:\x10\xb2\x86\xae\x89\x04V9\x18$O@\x00\x00\u07d4\xfb\x84,\xa2\xc5\xef\x139\x17\xa26\xa0\u052c@i\x01\x10\xb08\x89\x10\x96\x9ab\xbe\x15\x88\x00\x00\u07d4\xfb\x91\xfb\x1aiUS\xf0\u018e!'m\xec\xf0\xb89\t\xb8m\x89\x05l\x006\x17\xafx\x00\x00\u07d4\xfb\x94s\xcfw\x125\n\x1f\xa09Rs\xfc\x80V\aR\xe4\xfb\x89\x06\xaf!\x98\xba\x85\xaa\x00\x00\xe0\x94\xfb\x94\x9cd\u007f\xdc\xfd%\x14\xc7\u054e1\xf2\x8aS-\x8cX3\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xfb\xa5HmS\xc6\xe2@IBA\xab\xf8~C\xc7`\rA:\x89k\xbfaIIH4\x00\x00\u07d4\xfb\xb1a\xfe\x87_\t)\nK&+\xc6\x01\x10\x84\x8f\r\"&\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb\xbb\xeb\u03fe#^W\xdd#\x06\xad\x1a\x9e\u0141\xc7\xf9\xf4\x8f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xfb\xc0\x1d\xb5NG\xcd\xc3\xc48iJ\xb7\x17\xa8V\xc2?\xe6\xe9\x8a\x01\xcaqP\xab\x17OG\x00\x00\xe0\x94\xfb\xcf\xccJ{\x0f&\xcf&\xe9\xf33!2\xe2\xfcj#\af\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfb\xe7\x16\"\xbc\xbd1\xc1\xa3iv\xe7\xe5\xf6p\xc0\u007f\xfe\x16\u0789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfb\xed\xe3,4\x9f3\x00\xefL\xd3;M\xe7\xdc\x18\xe4C\xd3&\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\xfb\xf2\x04\xc8\x13\xf86\xd89b\u01c7\fx\b\xca4\u007f\xd3>\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xfb\xf7Y3\xe0\x1bu\xb1T\xef\x06i\ak\xe8\u007fb\xdf\xfa\xe1\x8a\x10\x84cr\xf2I\xd4\xc0\x00\x00\u07d4\xfc\x00\x96\xb2\x1e\x95\xac\xb8\xd6\x19\xd1v\xa4\xa1\xd8\xd5)\xba\xdb\xef\x89\x14\xd9i;\xcb\xec\x02\x80\x00\xe0\x94\xfc\x00\xa4 \xa3a\a\xdf\xd5\xf4\x95\x12\x8a_\u5af2\xdb\x0f4\x8a\x01C\x17\x9d\x86\x91\x10 \x00\x00\xe0\x94\xfc\x01\x8ai\n\xd6tm\xbe:\u03d7\x12\xdd\xcaR\xb6%\x009\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xfc\x02s@3\xe5\u007fpQ~\n\xfc~\xe6$a\xf0o\xad\x8e\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xfc\x0e\xe6\xf7\u00b3qJ\xe9\x91lEVf\x05\xb6V\xf3$A\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xfc\x10\xb7\xa6{2h\xd53\x1b\xfbj\x14\xde\xf5\xeaJ\x16,\xa3\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfc\x15\u02d9\xa8\xd1\x03\v\x12w\n\xdd\x03:y\xee\r\f\x90\x8c\x89\x12\xfa\x00\xbdR\xe6$\x00\x00\u07d4\xfc)R\xb4\u011f\xed\u043c\x05(\xa3\bI^mj\x1cq\u0589lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xfc,\x1f\x88\x96\x1d\x01\x9c>\x9e\xa30\t\x15.\x06\x93\xfb\xf8\x8a\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\xfc6\x11\x05\u0750\xf9\xed\xe5fI\x9di\xe9\x13\x03\x95\xf1*\u020aS\xa4\xfe/ N\x80\xe0\x00\x00\u07d4\xfc7/\xf6\x92|\xb3\x96\xd9\xcf)\x805\x00\x11\r\xa62\xbcR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc9\xbeA\tK\x19\x97\xd2\x16\x9e\x82d\xc2\u00fa\xa6\u025b\u0109lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc=\"k\xb3jX\xf5&V\x88W\xb0\xbb\x12\xd1\t\xec\x93\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfcC\x82\x9a\u01c7\xff\x88\xaa\xf1\x83\xba5*\xad\xbfZ\x15\xb1\x93\x89\u05ac\n+\x05R\xe0\x00\x00\u07d4\xfcI\xc1C\x9aA\u05b3\xcf&\xbbg\xe06R$\xe5\xe3\x8f_\x8966\u05ef^\u024e\x00\x00\u07d4\xfcU\x00\x82Q\x05\xcfq*1\x8a^\x9c;\xfci\u021d\f\x12\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xfcf\xfa\xba'\u007fK]\xe6J\xd4^\xb1\x9c1\xe0\f\xed>\u054a\x011\xbe\xb9%\xff\xd3 \x00\x00\xe0\x94\xfc~\"\xa5\x03\xecZ\xbe\x9b\b\xc5\v\xd1I\x99\xf5 \xfaH\x84\x8a\x01ZG}\xfb\xe1\xea\x14\x80\x00\u07d4\xfc\x82\x15\xa0\xa6\x99\x13\xf6*C\xbf\x1c\x85\x90\xb9\xdd\xcd\r\x8d\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc\x98\x9c\xb4\x87\xbf\x1a}\x17\xe4\xc1\xb7\u0137\xaa\xfd\xdak\n\x8d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xfc\x9b4td\xb2\xf9\x92\x9d\x80~\x03\x9d\xaeH\xd3\u064d\xe3y\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xfc\xa4;\xbc#\xa0\xd3!\xba\x9eF\xb9)s\\\xe7\xd8\xef\f\x18\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfc\xa7>\xff\x87q\xc0\x10;\xa3\xcc\x1a\x9c%\x94H\xc7*\xbf\v\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfc\xad\xa3\x00(?k\xcc\x13J\x91Eg`\xb0\xd7}\xe4\x10\xe0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xfc\xbc\\q\xac\xe7\x97AE\v\x01,\xf6\xb8\xd3\xf1}\xb6\x8ap\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xfc\xbd\x85\xfe\xeajuO\xcf4ID\x9e7\xff\x97\x84\xf7w<\x89\xa7J\xdai\xab\xd7x\x00\x00\xe0\x94\xfc\xc9\u0524&.z\x02z\xb7Q\x91\x10\xd8\x02\u0115\xce\xea9\x8a\x01YQ\x82\"K&H\x00\x00\xe0\x94\xfc\xcd\r\x1e\xce\xe2z\xdd\xea\x95\xf6\x85z\xee\xc8\u01e0K(\xee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xfc\u0434\x82|\xd2\b\xff\xbf^u\x9d\xba\x8c<\xc6\x1d\x8c,<\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfc\xe0\x89c\\\xe9z\xba\xc0kD\x81\x9b\xe5\xbb\n>.\v7\x89\x05\x03\x92\nv0\xa7\x80\x00\u07d4\xfc\xf1\x99\xf8\xb8T\"/\x18.N\x1d\t\x9dN2>*\xae\x01\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfc\xfc:P\x04\xd6xa?\v6\xa6B&\x9a\u007f7\x1c?j\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfd\x19\x1a5\x15}x\x13s\xfbA\x1b\xf9\xf2R\x90\x04|^\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfd\x1f\xaa4{\x0f\u0300L-\xa8l6\xd5\xf1\u044bp\x87\xbb\x89\x02\xd6\xeb$z\x96\xf6\x00\x00\u07d4\xfd\x1f\xb5\xa8\x9a\x89\xa7!\xb8yph\xfb\xc4\u007f>\x9dR\xe1I\x89\f\u0435\x83\u007f\xc6X\x00\x00\u07d4\xfd OOJ\xba%%\xbar\x8a\xfd\xf7\x87\x92\xcb\u07b75\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd'W\xcc5Q\xa0\x95\x87\x8d\x97\x87V\x15\xfe\fj2\xaa\x8a\x89 m\xb1R\x99\xbe\xac\x00\x00\u07d4\xfd(r\u045eW\x85<\xfa\x16\xef\xfe\x93\u0431\xd4{O\x93\xfb\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfd))'\x1e\x9d \x95\xa2dv~{\r\xf5.\xa0\xd1\xd4\x00\x89\xa2\xa1\xeb%\x1bZ\xe4\x00\x00\u07d4\xfd7z8Rr\x90\f\xb46\xa3\xbbyb\xcd\xff\xe9?]\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd@$+\xb3Jp\x85^\xf0\xfd\x90\xf3\x80-\xec!6\xb3'\x89h\xa8u\a>)$\x00\x00\xe0\x94\xfdE,9i\xec\xe3\x80\x1cT \xf1\xcd\u02a1\xc7\x1e\xd2=\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xfdKU\x1fo\xdb\u0366\xc5\x11\xb5\xbb7\"P\xa6\xb7\x83\xe54\x89\x01\x1d\xe1\xe6\xdbE\f\x00\x00\u07d4\xfdK\x98\x95X\xae\x11\xbe\f;6\xe2\xd6\xf2\xa5J\x93C\xca.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfdM\xe8\xe3t\x8a(\x9c\xf7\xd0`Q}\x9d88\x8d\xb0\x1f\xb8\x89\r\x8drkqw\xa8\x00\x00\u07d4\xfdZc\x15\u007f\x91O\u04d8\uac5c\x13}\xd9U\v\xb7q\\\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xfd`\u04b5\xaf=5\xf7\xaa\xf0\u00d3\x05.y\xc4\xd8#\u0645\x89\x03\x0e\xb5\r.\x14\b\x00\x00\u07d4\xfdhm\xe5?\xa9\u007f\x99c\x9e%hT\x97 \xbcX\x8c\x9e\xfc\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xfd~\u078fR@\xa0eA\xebi\x9dx,/\x9a\xfb!p\xf6\x89Hz\x9a0E9D\x00\x00\u07d4\xfd\x81+\u019f\xb1p\xefW\xe22~\x80\xaf\xfd\x14\xf8\xe4\xb6\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\x88\xd1\x14\"\x0f\b\x1c\xb3\xd5\xe1[\xe8\x15*\xb0sfWj\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xfd\x91\x856\xa8\xef\xa6\xf6\xce\xfe\x1f\xa1\x159\x95\xfe\xf5\xe3=;\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xfd\x92\x0fr&\x82\xaf\xb5\xafE\x1b\x05D\xd4\xf4\x1b;\x9dWB\x89~R\x05j\x12?<\x00\x00\u07d4\xfd\x95y\xf1\x19\xbb\xc8\x19\xa0+a\u3348\x03\xc9B\xf2M2\x89\x05\xb9~\x90\x81\xd9@\x00\x00\u07d4\xfd\xa0\xce\x153\a\a\xf1\v\xce2\x01\x17- \x18\xb9\xdd\xeat\x89\x02\xd0A\xd7\x05\xa2\xc6\x00\x00\xe0\x94\xfd\xa3\x04(\x19\xaf>f)\x00\xe1\xb9+CX\xed\xa6\xe9%\x90\x8a\x19\a\xa2\x84\u054fc\xe0\x00\x00\u07d4\xfd\xa6\x81\x0e\xa5\xac\x98]o\xfb\xf1\xc5\x11\xf1\xc1B\xed\xcf\xdd\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfd\xb39D\xf26\x06\x15\xe5\xbe#\x95w\u0221\x9b\xa5-\x98\x87\x89 \x9d\x92/RY\xc5\x00\x00\u07d4\xfd\xbaSY\xf7\xec;\xc7p\xacI\x97]\x84N\xc9qbV\xf1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xfd\xc4\xd4vZ\x94/[\xf9i1\xa9\xe8\xccz\xb8\xb7W\xffL\x8a\x12lG\x8a\x0e>\xa8`\x00\x00\xe0\x94\xfd\xcd]\x80\xb1\x05\x89zW\xab\xc4xev\x8b)\x00RB\x95\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u0794\xfd\xd1\x19_y}O5q}\x15\xe6\xf9\x81\n\x9a?\xf5T`\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xfd\xd5\x02\xa7N\x81;\u03e3U\xce\xda<\x17ojhq\xaf\u007f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfd\u357c\vm\\\xbbL\x1d\x8f\xea>\vK\xffc^\x9d\xb7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\xea\xac*\xcf\x1d\x13\x8e\x19\xf2\xfc?\x9f\xb7E\x92\xe3\ud04a\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xfd\xec\xc8-\xdf\xc5a\x92\xe2oV<=h\xcbTJ\x96\xbf\xed\x89\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4\xfd\xf4#C\x01\x9b\v\fk\xf2`\xb1s\xaf\xab~E\xb9\xd6!\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xfd\xf4I\xf1\b\xc6\xfbOZ+\b\x1e\xed~E\u645eM%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\xfda4\xc0J\x8a\xb7\xeb\x16\xf0\x06C\xf8\xfe\xd7\u06aa\ucc89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfe\x00\xbfC\x99\x11\xa5S\x98-\xb68\x03\x92E\xbc\xf02\xdb\u0709\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xfe\x01n\xc1~\xc5\xf1\x0e;\xb9\x8f\xf4\xa1\xed\xa0E\x15v\x82\xab\x89\x14_T\x02\xe7\xb2\xe6\x00\x00\u07d4\xfe\x0e0\xe2\x14)\rt=\xd3\x0e\xb0\x82\xf1\xf0\xa5\"Z\xdea\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfe!\v\x8f\x04\xdcmOv!j\xcf\xcb\u055b\xa8;\xe9\xb60\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfe\"\xa0\xb3\x88f\x8d\x1a\xe2d>w\x1d\xac\xf3\x8aCB#\u0309\xd8\xdb^\xbd{&8\x00\x00\u07d4\xfe6&\x88\x84_\xa2D\u0300~K\x110\xeb7A\xa8\x05\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfe8'\xd5v0\u03c7a\xd5\x12y{\v\x85\x8eG\x8b\xbd\x12\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfeA\x8bB\x1a\x9cm76\x02y\x04u\xd20>\x11\xa7Y0\x897\b\xba\xed=h\x90\x00\x00\u07d4\xfeBI\x12yP\xe2\xf8\x96\xec\x0e~.=\x05Z\xab\x10U\x0f\x89$=M\x18\"\x9c\xa2\x00\x00\xe0\x94\xfeM\x84\x03!o\xd5qW+\xf1\xbd\xb0\x1d\x00W\x89x\u0588\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xfeS\xb9I\x89\u0619d\xda aS\x95&\xbb\xe9y\xdd.\xa9\x89h\xa8u\a>)$\x00\x00\u07d4\xfeT\x9b\xbf\xe6G@\x18\x98\x92\x93%8\u06afF\u04b6\x1dO\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xfea]\x97\\\b\x87\xe0\xc9\x11>\xc7)\x84 \xa7\x93\xaf\x8b\x96\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfee\xc4\x18\x8dy\"Wi\td D\xfd\xc5#\x95V\x01e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfei\u007f\xf2,\xa5G\xbf\xc9^3\xd9`\xda`\\gc\xf3[\x89G\xd4\x11\x9f\xd9`\x94\x00\x00\u07d4\xfej\x89[y\\\xb4\xbf\x85\x90=<\xe0\x9cZ\xa49S\u04ff\x89\xb8Pz\x82\a( \x00\x00\u07d4\xfeo_B\xb6\x19;\x1a\xd1b\x06\u4bf5#\x9dM}\xb4^\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xfep\x11\xb6\x98\xbf3q\x13-tE\xb1\x9e\xb5\xb0\x945j\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x80\xe9#-\xea\xff\x19\xba\xf9\x98i\x88:K\xdf\x00\x04\xe5<\x89.b\xf2\ni\xbe@\x00\x00\u07d4\xfe\x8en6eW\r\xffz\x1b\xdaiz\xa5\x89\xc0\xb4\xe9\x02J\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x8f\x1f\u072b\u007f\xbe\u0266\xa3\xfc\xc5\aa\x96\x00P\\6\xa3\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xfe\x91\xec\xcf+\xd5f\xaf\xa1\x16\x96\xc5\x04\x9f\xa8Lic\nR\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xfe\x96\xc4\xcd8\x15b@\x1a\xa3*\x86\xe6[\x9dR\xfa\x8a\xee'\x89\x8f\x1d\\\x1c\xae7@\x00\x00\u07d4\xfe\x98\xc6d\xc3\xe4G\xa9^i\xbdX!q\xb7\x17n\xa2\xa6\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfe\x9a\xd1.\xf0]m\x90&\x1f\x96\xc84\n\x03\x81\x97M\xf4w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x9c\x0f\xff\xef\xb8\x03\b\x12V\xc0\xcfMfY\xe6\xd3>\xb4\xfb\x89R\xd5B\x80O\x1c\xe0\x00\x00\u07d4\xfe\x9c\xfc;\xb2\x93\u0772\x85\xe6%\xf3X/t\xa6\xb0\xa5\xa6\u0349j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xfe\x9e\x11\x97\u05d7JvH\xdc\u01e01\x12\xa8\x8e\xdb\xc9\x04]\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\xe0\x94\xfe\xac\xa2\xactbK\xf3H\xda\u0258QC\xcf\xd6R\xa4\xbeU\x8a\x05\x89\u007f\u02f0)\x14\b\x80\x00\u07d4\xfe\xad\x18\x03\xe5\xe77\xa6\x8e\x18G-\x9a\xc7\x15\xf0\x99L\u00be\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xfe\xb8\xb8\xe2\xafqj\xe4\x1f\xc7\xc0K\xcf)T\x01VF\x1ek\x89TQt\xa5(\xa7z\x00\x00\u07d4\xfe\xb9-0\xbf\x01\xff\x9a\x19\x01flUsS+\xfa\a\xee\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfe\xbc1s\xbc\x90r\x13cT\x00+{O\xb3\xbf\xc5?\"\xf1\x89\x14\x0e\xc8\x0f\xa7\xee\x88\x00\x00\u07d4\xfe\xbdH\xd0\xff\xdb\xd5el\xd5\xe6\x866:a\x14R(\xf2y\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xfe\xbd\x9f\x81\xcfx\xbd_\xb6\u0139\xa2K\xd4\x14\xbb\x9b\xfaLN\x89k\xe1\x0f\xb8\xedn\x13\x80\x00\u07d4\xfe\xc0o\xe2{D\u01c4\xb29n\xc9/{\x92:\xd1~\x90w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\xc1NT\x85\xde+>\xef^t\xc4aF\u06ceEN\x035\x89\t\xb4\x1f\xbf\x9e\n\xec\x00\x00\u07d4\xfe\xd8Gm\x10\u0544\xb3\x8b\xfag7`\x0e\xf1\x9d5\xc4\x1e\u0609b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xfe\xef;n\xab\xc9J\xff\xd31\f\x1cM\x0ee7^\x13\x11\x19\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfe\xf0\x9dp$?9\xed\x8c\xd8\x00\xbf\x96QG\x9e\x8fJ\xca<\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfe\xf3\xb3\u07ad\x1ai&\u051a\xa3+\x12\xc2*\xf5M\x9f\xf9\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xff\v|\xb7\x1d\xa9\xd4\xc1\xean\xcc(\xeb\xdaPLc\xf8/\u04498\x8a\x88]\xf2\xfcl\x00\x00\u07d4\xff\f\xc6\u73c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xff'&)AH\xb8lx\xa97$\x97\xe4Y\x89\x8e\xd3\xfe\xe3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xff=\xedz@\u04ef\xf0\u05e8\xc4_\xa6\x13j\xa0C=\xb4W\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xff>\xeeW\xc3Mm\xae\x97\r\x8b1\x11\x17\xc55\x86\xcd5\x02\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xff>\xf6\xba\x15\x1c!\xb5\x99\x86\xaed\xf6\xe8\"\x8b\u0262\xc73\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xffA\xd9\xe1\xb4\xef\xfe\x18\u0630\xd1\xf6?\xc4%_\xb4\xe0l=\x89Hz\x9a0E9D\x00\x00\u07d4\xffE\xcb4\xc9(6M\x9c\xc9\u063b\x0074ta\x8f\x06\xf3\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xffI\xa7u\x81N\xc0\x00Q\xa7\x95\xa8u\xde$Y.\xa4\x00\u050a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xffJ@\x8fP\xe9\xe7!F\xa2\x8c\xe4\xfc\x8d\x90'\x1f\x11n\x84\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xffM\x9c\x84\x84\xc4\x10T\x89H\xa4\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 rinkebyAllocData = "\xf9\x03\xb7\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x941\xb9\x8d\x14\x00{\xde\xe67)\x80\x86\x98\x8a\v\xbd1\x18E#\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ++ const goerliAllocData = "\xf9\x04\x06\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xe0\x94L*\xe4\x82Y5\x05\xf0\x16<\xde\xfc\a>\x81\xc6<\xdaA\a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xa8\xe8\xf1G2e\x8eKQ\xe8q\x191\x05:\x8ai\xba\xf2\xb1\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe1\x94\u0665\x17\x9f\t\x1d\x85\x05\x1d<\x98'\x85\xef\xd1E\\\uc199\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe1\x94\u08bdBX\xd2v\x887\xba\xa2j(\xfeq\xdc\a\x9f\x84\u01cbJG\xe3\xc1$H\xf4\xad\x00\x00\x00" ++ const yoloV3AllocData = "\xf9\x05o\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x94\x0e\x89\xe2\xae\xdb\x1c\xfc\u06d4$\xd4\x1a\x1f!\x8fA2s\x81r\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x10A\xaf\xbc\xb3Y\u0568\xdcX\xc1[/\xf5\x13T\xff\x8a!}\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94`\xad\xc0\xf8\x9aA\xaf#|\xe75T\xed\xe1p\xd73\xec\x14\xe0\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94y\x9d2\x9e_X4\x19\x16|\xd7\"\x96$\x85\x92n3\x8fJ\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94|\xf5\xb7\x9b\xfe)\x1ag\xab\x02\xb3\x93\xe4V\xcc\xc4\xc2f\xf7S\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x8a\x8e\xaf\xb1\xcfb\xbf\xbe\xb1t\x17i\xda\xe1\xa9\xddG\x99a\x92\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x8b\xa1\xf1\tU\x1b\xd42\x800\x12dZ\xc16\xdd\xd6M\xbar\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\xb0*.\xda\x1b1\u007f\xbd\x16v\x01(\x83k\n\u015bV\x0e\x9d\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\xdf\n\x88\xb2\xb6\x8cg7\x13\xa8\xec\x82`\x03go'.5s\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +++>>>>>>> c2d2f4ed8f:core/genesis_alloc.go +diff --cc params/config.go +index 51fa97c6fd,8f8b36b57d..0000000000 +--- a/params/config.go ++++ b/params/config.go +@@@ -30,8 -31,7 +30,12 @@@ var + RopstenGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") + RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177") + GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a") +++<<<<<<< HEAD + + // TODO: update with yolov2 values + + YoloV2GenesisHash = common.HexToHash("0xc3fd235071f24f93865b0850bd2a2119b30f7224d18a0e34c7bbf549ad7e3d36") +++======= ++ YoloV3GenesisHash = common.HexToHash("0x374f07cc7fa7c251fc5f36849f574b43db43600526410349efdca2bcea14101a") +++>>>>>>> c2d2f4ed8f + ) + + // TrustedCheckpoints associates each known checkpoint with the genesis hash of +@@@ -54,31 -54,30 +58,58 @@@ var CheckpointOracles = map[common.Hash + + var ( + // MainnetChainConfig is the chain parameters to run a node on the main network. +++<<<<<<< HEAD + + MainnetChainConfig = &goethereum.ChainConfig{ + + ChainID: big.NewInt(1), + + HomesteadBlock: big.NewInt(1150000), + + DAOForkBlock: big.NewInt(1920000), + + DAOForkSupport: true, + + EIP150Block: big.NewInt(2463000), + + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + + EIP155Block: big.NewInt(2675000), + + EIP158Block: big.NewInt(2675000), + + ByzantiumBlock: big.NewInt(4370000), + + ConstantinopleBlock: big.NewInt(7280000), + + PetersburgBlock: big.NewInt(7280000), + + IstanbulBlock: big.NewInt(9069000), + + MuirGlacierBlock: big.NewInt(9200000), + + Ethash: new(ctypes.EthashConfig), + + TrustedCheckpoint: MainnetTrustedCheckpoint, + + TrustedCheckpointOracle: MainnetCheckpointOracle, + + } + + + + // MainnetTrustedCheckpoint contains the light client trusted checkpoint for the main network. + + MainnetTrustedCheckpoint = &ctypes.TrustedCheckpoint{ + + SectionIndex: 336, + + SectionHead: common.HexToHash("0xd42b78902b6527a80337bf1bc372a3ccc3db97e9cc7cf421ca047ae9076c716b"), + + CHTRoot: common.HexToHash("0xd97f3b30f7e0cb958e4c67c53ec27745e5a165e33e56821b86523dfee62b783a"), + + BloomRoot: common.HexToHash("0xf3cbfd070fababfe2adc9b23fc02c731f6ca2cce6646b3ede4ef2db06092ccce"), +++======= ++ MainnetChainConfig = &ChainConfig{ ++ ChainID: big.NewInt(1), ++ HomesteadBlock: big.NewInt(1_150_000), ++ DAOForkBlock: big.NewInt(1_920_000), ++ DAOForkSupport: true, ++ EIP150Block: big.NewInt(2_463_000), ++ EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), ++ EIP155Block: big.NewInt(2_675_000), ++ EIP158Block: big.NewInt(2_675_000), ++ ByzantiumBlock: big.NewInt(4_370_000), ++ ConstantinopleBlock: big.NewInt(7_280_000), ++ PetersburgBlock: big.NewInt(7_280_000), ++ IstanbulBlock: big.NewInt(9_069_000), ++ MuirGlacierBlock: big.NewInt(9_200_000), ++ BerlinBlock: big.NewInt(12_244_000), ++ Ethash: new(EthashConfig), ++ } ++ ++ // MainnetTrustedCheckpoint contains the light client trusted checkpoint for the main network. ++ MainnetTrustedCheckpoint = &TrustedCheckpoint{ ++ SectionIndex: 364, ++ SectionHead: common.HexToHash("0x3fd20ff221f5e962bb66f57a61973bfc2ba959879a6509384a80a45d208b5afc"), ++ CHTRoot: common.HexToHash("0xe35b3b807f4e9427fb4e2929961c78a9dc10f503a538319031cc7d00946a0591"), ++ BloomRoot: common.HexToHash("0x340553b378b2db214b898be15c80ac5be7caffc2e6448fd6f7aff23290d89296"), +++>>>>>>> c2d2f4ed8f + } + + // MainnetCheckpointOracle contains a set of configs for the main network oracle. +@@@ -104,20 -103,21 +135,38 @@@ + EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"), + EIP155Block: big.NewInt(10), + EIP158Block: big.NewInt(10), +++<<<<<<< HEAD + + ByzantiumBlock: big.NewInt(1700000), + + ConstantinopleBlock: big.NewInt(4230000), + + PetersburgBlock: big.NewInt(4939394), + + IstanbulBlock: big.NewInt(6485846), + + MuirGlacierBlock: big.NewInt(7117117), + + Ethash: new(ctypes.EthashConfig), + + } + + + + // RopstenTrustedCheckpoint contains the light client trusted checkpoint for the Ropsten test network. + + RopstenTrustedCheckpoint = &ctypes.TrustedCheckpoint{ + + SectionIndex: 269, + + SectionHead: common.HexToHash("0x290a9eb65e65c64601d1b05522533ed502098a246736b348502a170818a33d64"), + + CHTRoot: common.HexToHash("0x530ebac02264227277d0a16b0819ef96a2011a6e1e66523ebff8040f4a3437ca"), + + BloomRoot: common.HexToHash("0x480cd5b3198a0767022902130546854a2e8867cce573c1cf0ce54e67a7bf5efb"), +++======= ++ ByzantiumBlock: big.NewInt(1_700_000), ++ ConstantinopleBlock: big.NewInt(4_230_000), ++ PetersburgBlock: big.NewInt(4_939_394), ++ IstanbulBlock: big.NewInt(6_485_846), ++ MuirGlacierBlock: big.NewInt(7_117_117), ++ BerlinBlock: big.NewInt(9_812_189), ++ Ethash: new(EthashConfig), ++ } ++ ++ // RopstenTrustedCheckpoint contains the light client trusted checkpoint for the Ropsten test network. ++ RopstenTrustedCheckpoint = &TrustedCheckpoint{ ++ SectionIndex: 279, ++ SectionHead: common.HexToHash("0x4a4912848d4c06090097073357c10015d11c6f4544a0f93cbdd584701c3b7d58"), ++ CHTRoot: common.HexToHash("0x9053b7867ae921e80a4e2f5a4b15212e4af3d691ca712fb33dc150e9c6ea221c"), ++ BloomRoot: common.HexToHash("0x3dc04cb1be7ddc271f3f83469b47b76184a79d7209ef51d85b1539ea6d25a645"), +++>>>>>>> c2d2f4ed8f + } + + // RopstenCheckpointOracle contains a set of configs for the Ropsten test network oracle. +@@@ -134,34 -134,33 +183,61 @@@ + } + + // RinkebyChainConfig contains the chain parameters to run a node on the Rinkeby test network. +++<<<<<<< HEAD + + RinkebyChainConfig = &goethereum.ChainConfig{ + + ChainID: big.NewInt(4), + + HomesteadBlock: big.NewInt(1), + + DAOForkBlock: nil, + + DAOForkSupport: true, + + EIP150Block: big.NewInt(2), + + EIP150Hash: common.HexToHash("0x9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9"), + + EIP155Block: big.NewInt(3), + + EIP158Block: big.NewInt(3), + + ByzantiumBlock: big.NewInt(1035301), + + ConstantinopleBlock: big.NewInt(3660663), + + PetersburgBlock: big.NewInt(4321234), + + IstanbulBlock: big.NewInt(5435345), + + MuirGlacierBlock: nil, + + TrustedCheckpoint: RinkebyTrustedCheckpoint, + + TrustedCheckpointOracle: RinkebyCheckpointOracle, + + Clique: &ctypes.CliqueConfig{ +++======= ++ RinkebyChainConfig = &ChainConfig{ ++ ChainID: big.NewInt(4), ++ HomesteadBlock: big.NewInt(1), ++ DAOForkBlock: nil, ++ DAOForkSupport: true, ++ EIP150Block: big.NewInt(2), ++ EIP150Hash: common.HexToHash("0x9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9"), ++ EIP155Block: big.NewInt(3), ++ EIP158Block: big.NewInt(3), ++ ByzantiumBlock: big.NewInt(1_035_301), ++ ConstantinopleBlock: big.NewInt(3_660_663), ++ PetersburgBlock: big.NewInt(4_321_234), ++ IstanbulBlock: big.NewInt(5_435_345), ++ MuirGlacierBlock: nil, ++ BerlinBlock: big.NewInt(8_290_928), ++ Clique: &CliqueConfig{ +++>>>>>>> c2d2f4ed8f + Period: 15, + Epoch: 30000, + }, + } + + // RinkebyTrustedCheckpoint contains the light client trusted checkpoint for the Rinkeby test network. +++<<<<<<< HEAD + + RinkebyTrustedCheckpoint = &ctypes.TrustedCheckpoint{ + + SectionIndex: 223, + + SectionHead: common.HexToHash("0x03ca0d5e3a931c77cd7a97bbaa2d9e4edc4549c621dc1d223a29f10c86a4a16a"), + + CHTRoot: common.HexToHash("0x6573dbdd91b2958b446bd04d67c23e5f14b2510ac96e8df1b6a894dc49e37c6c"), + + BloomRoot: common.HexToHash("0x28a35042a4e88efbac55fe566faf7fce000dc436f17fd4cb4b081c9cd793e1a7"), +++======= ++ RinkebyTrustedCheckpoint = &TrustedCheckpoint{ ++ SectionIndex: 248, ++ SectionHead: common.HexToHash("0x26874cf023695778cc3175d1bec19894204d8d0b756b587e81e35f300dc5b33c"), ++ CHTRoot: common.HexToHash("0xc129d1ed6673c5d3e1068e9d97244e72952b7ca08acbd7b3bfa58bc3085c442c"), ++ BloomRoot: common.HexToHash("0x1dafe79dcd7d348782aa834a4a4397890d9ad90643736791132ed5c16879a037"), +++>>>>>>> c2d2f4ed8f + } + + // RinkebyCheckpointOracle contains a set of configs for the Rinkeby test network oracle. +@@@ -177,33 -176,32 +253,59 @@@ + } + + // GoerliChainConfig contains the chain parameters to run a node on the Görli test network. +++<<<<<<< HEAD + + GoerliChainConfig = &goethereum.ChainConfig{ + + ChainID: big.NewInt(5), + + HomesteadBlock: big.NewInt(0), + + DAOForkBlock: nil, + + DAOForkSupport: true, + + EIP150Block: big.NewInt(0), + + EIP155Block: big.NewInt(0), + + EIP158Block: big.NewInt(0), + + ByzantiumBlock: big.NewInt(0), + + ConstantinopleBlock: big.NewInt(0), + + PetersburgBlock: big.NewInt(0), + + IstanbulBlock: big.NewInt(1561651), + + MuirGlacierBlock: nil, + + TrustedCheckpoint: GoerliTrustedCheckpoint, + + TrustedCheckpointOracle: GoerliCheckpointOracle, + + Clique: &ctypes.CliqueConfig{ +++======= ++ GoerliChainConfig = &ChainConfig{ ++ ChainID: big.NewInt(5), ++ HomesteadBlock: big.NewInt(0), ++ DAOForkBlock: nil, ++ DAOForkSupport: true, ++ EIP150Block: big.NewInt(0), ++ EIP155Block: big.NewInt(0), ++ EIP158Block: big.NewInt(0), ++ ByzantiumBlock: big.NewInt(0), ++ ConstantinopleBlock: big.NewInt(0), ++ PetersburgBlock: big.NewInt(0), ++ IstanbulBlock: big.NewInt(1_561_651), ++ MuirGlacierBlock: nil, ++ BerlinBlock: big.NewInt(4_460_644), ++ Clique: &CliqueConfig{ +++>>>>>>> c2d2f4ed8f + Period: 15, + Epoch: 30000, + }, + } + + // GoerliTrustedCheckpoint contains the light client trusted checkpoint for the Görli test network. +++<<<<<<< HEAD + + GoerliTrustedCheckpoint = &ctypes.TrustedCheckpoint{ + + SectionIndex: 107, + + SectionHead: common.HexToHash("0xff3ae39199fa191894de419e7f673c8627aa8cc7af924b90f36635b6add375f2"), + + CHTRoot: common.HexToHash("0x27d59d60c652425b6b593a882f55a4ff57f24e470a810a6e3c8ba71833a20220"), + + BloomRoot: common.HexToHash("0x3c14066d8bb3733780c06b8165768dbb9dd23b75f56012fe5f2fb3c2fb70cadb"), +++======= ++ GoerliTrustedCheckpoint = &TrustedCheckpoint{ ++ SectionIndex: 132, ++ SectionHead: common.HexToHash("0x29fa240c97b47ecbfef3fea8b3cff035d93154d1d48b25e3333cf2f7067c5324"), ++ CHTRoot: common.HexToHash("0x85e5c59e5b202284291405dadc40dc36ab6417bd189fb18be24f6dcab6b80511"), ++ BloomRoot: common.HexToHash("0x0b7afdd200477f46e982e2cabc822ac454424986fa50d899685dfaeede1f882d"), +++>>>>>>> c2d2f4ed8f + } + + // GoerliCheckpointOracle contains a set of configs for the Goerli test network oracle. +@@@ -219,9 -217,9 +321,15 @@@ + Threshold: 2, + } + +++<<<<<<< HEAD + + // YoloV2ChainConfig contains the chain parameters to run a node on the YOLOv2 test network. + + YoloV2ChainConfig = &goethereum.ChainConfig{ + + ChainID: big.NewInt(133519467574834), +++======= ++ // YoloV3ChainConfig contains the chain parameters to run a node on the YOLOv3 test network. ++ YoloV3ChainConfig = &ChainConfig{ ++ ChainID: new(big.Int).SetBytes([]byte("yolov3x")), +++>>>>>>> c2d2f4ed8f + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, +@@@ -233,8 -231,9 +341,14 @@@ + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: nil, +++<<<<<<< HEAD + + YoloV2Block: big.NewInt(0), + + Clique: &ctypes.CliqueConfig{ +++======= ++ BerlinBlock: nil, // Don't enable Berlin directly, we're YOLOing it ++ YoloV3Block: big.NewInt(0), ++ Clique: &CliqueConfig{ +++>>>>>>> c2d2f4ed8f + Period: 15, + Epoch: 30000, + }, +@@@ -245,133 -244,404 +359,495 @@@ + // + // This configuration is intentionally not using keyed fields to force anyone + // adding flags to the config to also have to set these fields. +++<<<<<<< HEAD + + AllEthashProtocolChanges = &goethereum.ChainConfig{ + + ChainID: big.NewInt(1137), + + HomesteadBlock: big.NewInt(0), + + DAOForkBlock: nil, + + DAOForkSupport: false, + + EIP150Block: big.NewInt(0), + + EIP150Hash: common.Hash{}, + + EIP155Block: big.NewInt(0), + + EIP158Block: big.NewInt(0), + + ByzantiumBlock: big.NewInt(0), + + ConstantinopleBlock: big.NewInt(0), + + PetersburgBlock: big.NewInt(0), + + IstanbulBlock: big.NewInt(0), + + MuirGlacierBlock: nil, + + YoloV2Block: nil, + + EWASMBlock: nil, + + Ethash: new(ctypes.EthashConfig), + + Clique: nil, + + TrustedCheckpoint: nil, + + TrustedCheckpointOracle: nil, + + } + + + + /* + + https://github.com/ethereum/go-ethereum/blob/master/params/config.go#L242 + + + + AllEthashProtocolChanges = &ChainConfig{ + + big.NewInt(1337), + + big.NewInt(0), + + nil, + + false, + + big.NewInt(0), + + common.Hash{}, + + big.NewInt(0), + + big.NewInt(0), + + big.NewInt(0), + + big.NewInt(0), + + big.NewInt(0), + + big.NewInt(0), + + nil, + + nil, + + nil, + + new(EthashConfig), + + nil + + } +++======= ++ AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil} ++ ++ // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced ++ // and accepted by the Ethereum core developers into the Clique consensus. ++ // ++ // This configuration is intentionally not using keyed fields to force anyone ++ // adding flags to the config to also have to set these fields. ++ AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} ++ ++ TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil} ++ TestRules = TestChainConfig.Rules(new(big.Int)) ++ ) ++ ++ // TrustedCheckpoint represents a set of post-processed trie roots (CHT and ++ // BloomTrie) associated with the appropriate section index and head hash. It is ++ // used to start light syncing from this checkpoint and avoid downloading the ++ // entire header chain while still being able to securely access old headers/logs. ++ type TrustedCheckpoint struct { ++ SectionIndex uint64 `json:"sectionIndex"` ++ SectionHead common.Hash `json:"sectionHead"` ++ CHTRoot common.Hash `json:"chtRoot"` ++ BloomRoot common.Hash `json:"bloomRoot"` ++ } ++ ++ // HashEqual returns an indicator comparing the itself hash with given one. ++ func (c *TrustedCheckpoint) HashEqual(hash common.Hash) bool { ++ if c.Empty() { ++ return hash == common.Hash{} ++ } ++ return c.Hash() == hash ++ } ++ ++ // Hash returns the hash of checkpoint's four key fields(index, sectionHead, chtRoot and bloomTrieRoot). ++ func (c *TrustedCheckpoint) Hash() common.Hash { ++ buf := make([]byte, 8+3*common.HashLength) ++ binary.BigEndian.PutUint64(buf, c.SectionIndex) ++ copy(buf[8:], c.SectionHead.Bytes()) ++ copy(buf[8+common.HashLength:], c.CHTRoot.Bytes()) ++ copy(buf[8+2*common.HashLength:], c.BloomRoot.Bytes()) ++ return crypto.Keccak256Hash(buf) ++ } ++ ++ // Empty returns an indicator whether the checkpoint is regarded as empty. ++ func (c *TrustedCheckpoint) Empty() bool { ++ return c.SectionHead == (common.Hash{}) || c.CHTRoot == (common.Hash{}) || c.BloomRoot == (common.Hash{}) ++ } ++ ++ // CheckpointOracleConfig represents a set of checkpoint contract(which acts as an oracle) ++ // config which used for light client checkpoint syncing. ++ type CheckpointOracleConfig struct { ++ Address common.Address `json:"address"` ++ Signers []common.Address `json:"signers"` ++ Threshold uint64 `json:"threshold"` ++ } ++ ++ // ChainConfig is the core config which determines the blockchain settings. ++ // ++ // ChainConfig is stored in the database on a per block basis. This means ++ // that any network, identified by its genesis block, can have its own ++ // set of configuration options. ++ type ChainConfig struct { ++ ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection ++ ++ 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,omitempty"` // EIP150 HF block (nil = no fork) ++ EIP150Hash common.Hash `json:"eip150Hash,omitempty"` // EIP150 HF hash (needed for header only clients as only gas pricing changed) ++ ++ EIP155Block *big.Int `json:"eip155Block,omitempty"` // EIP155 HF block ++ EIP158Block *big.Int `json:"eip158Block,omitempty"` // EIP158 HF block ++ ++ ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium) ++ ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated) ++ PetersburgBlock *big.Int `json:"petersburgBlock,omitempty"` // Petersburg switch block (nil = same as Constantinople) ++ IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul) ++ MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated) ++ BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin) ++ ++ YoloV3Block *big.Int `json:"yoloV3Block,omitempty"` // YOLO v3: Gas repricings TODO @holiman add EIP references ++ EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated) ++ ++ // 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 ++ } ++ ++ // String implements the stringer interface, returning the consensus engine details. ++ func (c *CliqueConfig) String() string { ++ return "clique" ++ } ++ ++ // String implements the fmt.Stringer interface. ++ func (c *ChainConfig) String() string { ++ 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 Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, YOLO v3: %v, Engine: %v}", ++ c.ChainID, ++ c.HomesteadBlock, ++ c.DAOForkBlock, ++ c.DAOForkSupport, ++ c.EIP150Block, ++ c.EIP155Block, ++ c.EIP158Block, ++ c.ByzantiumBlock, ++ c.ConstantinopleBlock, ++ c.PetersburgBlock, ++ c.IstanbulBlock, ++ c.MuirGlacierBlock, ++ c.BerlinBlock, ++ c.YoloV3Block, ++ engine, ++ ) ++ } ++ ++ // IsHomestead returns whether num is either equal to the homestead block or greater. ++ func (c *ChainConfig) IsHomestead(num *big.Int) bool { ++ return isForked(c.HomesteadBlock, num) ++ } ++ ++ // IsDAOFork 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) ++ } +++>>>>>>> c2d2f4ed8f + + -// IsEIP150 returns whether num is either equal to the EIP150 fork block or greater. + -func (c *ChainConfig) IsEIP150(num *big.Int) bool { + - return isForked(c.EIP150Block, num) + -} + + -// IsEIP155 returns whether num is either equal to the EIP155 fork block or greater. + -func (c *ChainConfig) IsEIP155(num *big.Int) bool { + - return isForked(c.EIP155Block, num) + -} + + // ChainConfig is the core config which determines the blockchain settings. + + // + + // ChainConfig is stored in the database on a per block basis. This means + + // that any network, identified by its genesis block, can have its own + + // set of configuration options. + + type ChainConfig struct { + + ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection + + -// IsEIP158 returns whether num is either equal to the EIP158 fork block or greater. + -func (c *ChainConfig) IsEIP158(num *big.Int) bool { + - return isForked(c.EIP158Block, num) + -} + + HomesteadBlock *big.Int `json:"homesteadBlock,omitempty"` // Homestead switch block (nil = no fork, 0 = already homestead) + + -// IsByzantium returns whether num is either equal to the Byzantium fork block or greater. + -func (c *ChainConfig) IsByzantium(num *big.Int) bool { + - return isForked(c.ByzantiumBlock, num) + -} + + 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 + + -// IsConstantinople returns whether num is either equal to the Constantinople fork block or greater. + -func (c *ChainConfig) IsConstantinople(num *big.Int) bool { + - return isForked(c.ConstantinopleBlock, num) + -} + + // EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150) + + EIP150Block *big.Int `json:"eip150Block,omitempty"` // EIP150 HF block (nil = no fork) + + EIP150Hash common.Hash `json:"eip150Hash,omitempty"` // EIP150 HF hash (needed for header only clients as only gas pricing changed) + + -// IsMuirGlacier returns whether num is either equal to the Muir Glacier (EIP-2384) fork block or greater. + -func (c *ChainConfig) IsMuirGlacier(num *big.Int) bool { + - return isForked(c.MuirGlacierBlock, num) + -} + + EIP155Block *big.Int `json:"eip155Block,omitempty"` // EIP155 HF block + + EIP158Block *big.Int `json:"eip158Block,omitempty"` // EIP158 HF block + + -// IsPetersburg returns whether num is either + -// - equal to or greater than the PetersburgBlock fork block, + -// - OR is nil, and Constantinople is active + -func (c *ChainConfig) IsPetersburg(num *big.Int) bool { + - return isForked(c.PetersburgBlock, num) || c.PetersburgBlock == nil && isForked(c.ConstantinopleBlock, num) + -} + + ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium) + + ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated) + + PetersburgBlock *big.Int `json:"petersburgBlock,omitempty"` // Petersburg switch block (nil = same as Constantinople) + + IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul) + + MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated) + + -// IsIstanbul returns whether num is either equal to the Istanbul fork block or greater. + -func (c *ChainConfig) IsIstanbul(num *big.Int) bool { + - return isForked(c.IstanbulBlock, num) + -} + + YoloV2Block *big.Int `json:"yoloV2Block,omitempty"` // YOLO v1: https://github.com/ethereum/EIPs/pull/2657 (Ephemeral testnet) + + EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated) + + +++<<<<<<< HEAD + + // Various consensus engines + + Ethash *EthashConfig `json:"ethash,omitempty"` + + Clique *CliqueConfig `json:"clique,omitempty"` + + } + + */ + + + // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced + + // and accepted by the Ethereum core developers into the Clique consensus. + + // + + // This configuration is intentionally not using keyed fields to force anyone + + // adding flags to the config to also have to set these fields. + + AllCliqueProtocolChanges = &goethereum.ChainConfig{ + + ChainID: big.NewInt(1337), + + HomesteadBlock: big.NewInt(0), + + DAOForkBlock: nil, + + DAOForkSupport: false, + + EIP150Block: big.NewInt(0), + + EIP150Hash: common.Hash{}, + + EIP155Block: big.NewInt(0), + + EIP158Block: big.NewInt(0), + + ByzantiumBlock: big.NewInt(0), + + ConstantinopleBlock: big.NewInt(0), + + PetersburgBlock: big.NewInt(0), + + IstanbulBlock: big.NewInt(0), + + EWASMBlock: nil, + + Ethash: nil, + + Clique: &ctypes.CliqueConfig{ + + Period: 0, + + Epoch: 30000, + + }, + + TrustedCheckpoint: nil, + + TrustedCheckpointOracle: nil, + + } + + + + // TestChainConfig is used for tests. + + TestChainConfig = &goethereum.ChainConfig{ + + ChainID: big.NewInt(1), + + HomesteadBlock: big.NewInt(0), + + DAOForkBlock: nil, + + DAOForkSupport: false, + + EIP150Block: big.NewInt(0), + + EIP150Hash: common.Hash{}, + + EIP155Block: big.NewInt(0), + + EIP158Block: big.NewInt(0), + + ByzantiumBlock: big.NewInt(0), + + ConstantinopleBlock: big.NewInt(0), + + PetersburgBlock: big.NewInt(0), + + IstanbulBlock: big.NewInt(0), + + EWASMBlock: nil, + + Ethash: new(ctypes.EthashConfig), + + Clique: nil, + + TrustedCheckpoint: nil, + + TrustedCheckpointOracle: nil, +++======= ++ // IsBerlin returns whether num is either equal to the Berlin fork block or greater. ++ func (c *ChainConfig) IsBerlin(num *big.Int) bool { ++ return isForked(c.BerlinBlock, num) || isForked(c.YoloV3Block, num) ++ } ++ ++ // IsEWASM returns whether num represents a block number after the EWASM fork ++ func (c *ChainConfig) IsEWASM(num *big.Int) bool { ++ return isForked(c.EWASMBlock, num) ++ } ++ ++ // 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 ++ } ++ ++ // CheckConfigForkOrder checks that we don't "skip" any forks, geth isn't pluggable enough ++ // to guarantee that forks can be implemented in a different order than on official networks ++ func (c *ChainConfig) CheckConfigForkOrder() error { ++ type fork struct { ++ name string ++ block *big.Int ++ optional bool // if true, the fork may be nil and next fork is still allowed ++ } ++ var lastFork fork ++ for _, cur := range []fork{ ++ {name: "homesteadBlock", block: c.HomesteadBlock}, ++ {name: "daoForkBlock", block: c.DAOForkBlock, optional: true}, ++ {name: "eip150Block", block: c.EIP150Block}, ++ {name: "eip155Block", block: c.EIP155Block}, ++ {name: "eip158Block", block: c.EIP158Block}, ++ {name: "byzantiumBlock", block: c.ByzantiumBlock}, ++ {name: "constantinopleBlock", block: c.ConstantinopleBlock}, ++ {name: "petersburgBlock", block: c.PetersburgBlock}, ++ {name: "istanbulBlock", block: c.IstanbulBlock}, ++ {name: "muirGlacierBlock", block: c.MuirGlacierBlock, optional: true}, ++ {name: "berlinBlock", block: c.BerlinBlock}, ++ } { ++ if lastFork.name != "" { ++ // Next one must be higher number ++ if lastFork.block == nil && cur.block != nil { ++ return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at %v", ++ lastFork.name, cur.name, cur.block) ++ } ++ if lastFork.block != nil && cur.block != nil { ++ if lastFork.block.Cmp(cur.block) > 0 { ++ return fmt.Errorf("unsupported fork ordering: %v enabled at %v, but %v enabled at %v", ++ lastFork.name, lastFork.block, cur.name, cur.block) ++ } ++ } ++ } ++ // If it was optional and not set, then ignore it ++ if !cur.optional || cur.block != nil { ++ lastFork = cur ++ } ++ } ++ return nil ++ } ++ ++ 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) ++ } ++ 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) ++ } ++ if isForkIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, head) { ++ return newCompatError("Byzantium fork block", c.ByzantiumBlock, newcfg.ByzantiumBlock) ++ } ++ if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) { ++ return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock) ++ } ++ if isForkIncompatible(c.PetersburgBlock, newcfg.PetersburgBlock, head) { ++ // the only case where we allow Petersburg to be set in the past is if it is equal to Constantinople ++ // mainly to satisfy fork ordering requirements which state that Petersburg fork be set if Constantinople fork is set ++ if isForkIncompatible(c.ConstantinopleBlock, newcfg.PetersburgBlock, head) { ++ return newCompatError("Petersburg fork block", c.PetersburgBlock, newcfg.PetersburgBlock) ++ } ++ } ++ if isForkIncompatible(c.IstanbulBlock, newcfg.IstanbulBlock, head) { ++ return newCompatError("Istanbul fork block", c.IstanbulBlock, newcfg.IstanbulBlock) ++ } ++ if isForkIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, head) { ++ return newCompatError("Muir Glacier fork block", c.MuirGlacierBlock, newcfg.MuirGlacierBlock) ++ } ++ if isForkIncompatible(c.BerlinBlock, newcfg.BerlinBlock, head) { ++ return newCompatError("Berlin fork block", c.BerlinBlock, newcfg.BerlinBlock) ++ } ++ if isForkIncompatible(c.YoloV3Block, newcfg.YoloV3Block, head) { ++ return newCompatError("YOLOv3 fork block", c.YoloV3Block, newcfg.YoloV3Block) ++ } ++ if isForkIncompatible(c.EWASMBlock, newcfg.EWASMBlock, head) { ++ return newCompatError("ewasm fork block", c.EWASMBlock, newcfg.EWASMBlock) ++ } ++ 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) ++ } ++ ++ // 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 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 ++ } ++ ++ 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 (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) ++ } ++ ++ // Rules wraps ChainConfig and is merely syntactic 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 ++ IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool ++ IsBerlin bool ++ } ++ ++ // Rules ensures c's ChainID is not nil. ++ func (c *ChainConfig) Rules(num *big.Int) Rules { ++ chainID := c.ChainID ++ if chainID == nil { ++ chainID = new(big.Int) ++ } ++ return Rules{ ++ ChainID: new(big.Int).Set(chainID), ++ IsHomestead: c.IsHomestead(num), ++ IsEIP150: c.IsEIP150(num), ++ IsEIP155: c.IsEIP155(num), ++ IsEIP158: c.IsEIP158(num), ++ IsByzantium: c.IsByzantium(num), ++ IsConstantinople: c.IsConstantinople(num), ++ IsPetersburg: c.IsPetersburg(num), ++ IsIstanbul: c.IsIstanbul(num), ++ IsBerlin: c.IsBerlin(num), +++>>>>>>> c2d2f4ed8f + } + -} + +) +diff --cc params/version.go +index b537e88cc9,e92ae13a1f..0000000000 +--- a/params/version.go ++++ b/params/version.go +@@@ -21,11 -21,10 +21,18 @@@ import + ) + + const ( +++<<<<<<< HEAD + + VersionMajor = 1 // Major version component of the current release + + VersionMinor = 11 // Minor version component of the current release + + VersionPatch = 23 // Patch version component of the current release + + VersionMeta = "unstable" // Version metadata to append to the version string + + VersionName = "CoreGeth" +++======= ++ VersionMajor = 1 // Major version component of the current release ++ VersionMinor = 10 // Minor version component of the current release ++ VersionPatch = 1 // Patch version component of the current release ++ VersionMeta = "stable" // Version metadata to append to the version string +++>>>>>>> c2d2f4ed8f + ) + + // Version holds the textual version string. +diff --cc tests/init.go +index 6732d23be9,67f706eb50..0000000000 +--- a/tests/init.go ++++ b/tests/init.go +@@@ -253,8 -141,7 +253,12 @@@ var Forks = map[string]ctypes.ChainConf + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(5), + }, +++<<<<<<< HEAD + + "YOLOv2": &goethereum.ChainConfig{ + + Clique: new(ctypes.CliqueConfig), +++======= ++ "YOLOv3": { +++>>>>>>> c2d2f4ed8f + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), +@@@ -264,11 -151,11 +268,11 @@@ + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), +- YoloV2Block: big.NewInt(0), ++ YoloV3Block: big.NewInt(0), + }, +- // This specification is subject to change, but is for now identical to YOLOv2 ++ // This specification is subject to change, but is for now identical to YOLOv3 + // for cross-client testing purposes + - "Berlin": { + + "Berlin": &goethereum.ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), +diff --cc tests/state_test_util.go +index 7825139d3b,46834de6da..0000000000 +--- a/tests/state_test_util.go ++++ b/tests/state_test_util.go +@@@ -215,29 -182,23 +215,39 @@@ func (t *StateTest) RunNoVerify(subtes + if err != nil { + return nil, nil, common.Hash{}, err + } +- context := core.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase) ++ ++ // Prepare the EVM. ++ txContext := core.NewEVMTxContext(msg) ++ context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) + context.GetHash = vmTestBlockHash +++<<<<<<< HEAD + + + + evm := vm.NewEVM(context, statedb, config, vmconfig) + + + + if config.IsEnabled(config.GetEIP2929Transition, block.Number()) { + + statedb.AddAddressToAccessList(msg.From()) + + if dst := msg.To(); dst != nil { + + statedb.AddAddressToAccessList(*dst) + + // If it's a create-tx, the destination will be added inside evm.create + + } + + for addr := range vm.PrecompiledContractsForConfig(config, block.Number()) { + + statedb.AddAddressToAccessList(addr) + + } + + } +++======= ++ evm := vm.NewEVM(context, txContext, statedb, config, vmconfig) ++ ++ // Execute the message. ++ snapshot := statedb.Snapshot() +++>>>>>>> c2d2f4ed8f + gaspool := new(core.GasPool) + gaspool.AddGas(block.GasLimit()) +- snapshot := statedb.Snapshot() + if _, err := core.ApplyMessage(evm, msg, gaspool); err != nil { + statedb.RevertToSnapshot(snapshot) + } ++ + // Commit block + - statedb.Commit(config.IsEIP158(block.Number())) + + statedb.Commit(config.IsEnabled(config.GetEIP161dTransition, block.Number())) + // Add 0-value mining reward. This only makes a difference in the cases + // where + // - the coinbase suicided, or +diff --cc tests/testdata +index 72bdca9504,c600d7795a..0000000000 +--- a/tests/testdata ++++ b/tests/testdata +diff --cc tests/transaction_test_util.go +index 8cb2287408,82ee01de15..0000000000 +--- a/tests/transaction_test_util.go ++++ b/tests/transaction_test_util.go +@@@ -56,7 -55,7 +56,11 @@@ func (tt *TransactionTest) Run(config c + return nil, nil, err + } + // Intrinsic gas +++<<<<<<< HEAD + + requiredGas, err := core.IntrinsicGas(tx.Data(), tx.To() == nil, isEIP2F, isEIP2028F) +++======= ++ requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isHomestead, isIstanbul) +++>>>>>>> c2d2f4ed8f + if err != nil { + return nil, nil, err + } +* Unmerged path SECURITY.md +* Unmerged path cmd/geth/retesteth.go +* Unmerged path cmd/puppeth/genesis.go +* Unmerged path cmd/puppeth/module_faucet.go +* Unmerged path cmd/puppeth/module_node.go +* Unmerged path cmd/puppeth/wizard_faucet.go +* Unmerged path cmd/puppeth/wizard_genesis.go +* Unmerged path eth/helper_test.go +* Unmerged path eth/protocol.go +* Unmerged path eth/protocol_test.go diff --git a/go.mod b/go.mod old mode 100755 new mode 100644 index 3939457f6d..427c411c1b --- a/go.mod +++ b/go.mod @@ -1,17 +1,20 @@ module github.com/ethereum/go-ethereum -go 1.13 +go 1.15 require ( github.com/Azure/azure-storage-blob-go v0.10.0 + github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/VictoriaMetrics/fastcache v1.5.7 github.com/alecthomas/jsonschema v0.0.0-20200530073317-71f438968921 - github.com/aristanetworks/goarista v0.0.0-20200812190859-4cb0e71f3c0e - github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 // indirect - github.com/aws/aws-sdk-go v1.34.21 + github.com/aws/aws-sdk-go-v2 v1.2.0 + github.com/aws/aws-sdk-go-v2/config v1.1.1 + github.com/aws/aws-sdk-go-v2/credentials v1.1.1 + github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1 github.com/btcsuite/btcd v0.21.0-beta github.com/cespare/cp v1.1.1 - github.com/cloudflare/cloudflare-go v0.13.2 + github.com/cloudflare/cloudflare-go v0.14.0 + github.com/consensys/gurvy v0.3.8 github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea github.com/dlclark/regexp2 v1.4.0 // indirect @@ -21,8 +24,9 @@ require ( github.com/etclabscore/go-openrpc-reflect v0.0.35 github.com/ethereum/evmc/v7 v7.3.0 github.com/fatih/color v1.7.0 - github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc + github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff + github.com/go-kit/kit v0.10.0 // indirect github.com/go-ole/go-ole v1.2.4 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-stack/stack v1.8.0 @@ -30,49 +34,52 @@ require ( github.com/golang/protobuf v1.4.3 github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3 github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa + github.com/google/uuid v1.1.5 + github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 // indirect github.com/gorilla/websocket v1.4.2 - github.com/graph-gophers/graphql-go v0.0.0-20200819123640-3b5ddcd884ae - github.com/hashicorp/golang-lru v0.5.4 + github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29 + github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d + github.com/holiman/bloomfilter/v2 v2.0.3 github.com/holiman/uint256 v1.1.1 - github.com/huin/goupnp v1.0.0 + github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88 github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 - github.com/influxdata/influxdb v1.8.2 + github.com/influxdata/influxdb v1.8.3 github.com/jackpal/go-nat-pmp v1.0.2 + github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e github.com/julienschmidt/httprouter v1.3.0 github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 - github.com/ledgerwatch/turbo-geth v0.0.0-20210308133656-9d397082536c + github.com/kr/pretty v0.2.0 // indirect github.com/mattn/go-colorable v0.1.7 github.com/mattn/go-isatty v0.0.12 github.com/mitchellh/go-homedir v1.1.0 github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 - github.com/olekukonko/tablewriter v0.0.4 + github.com/olekukonko/tablewriter v0.0.5 github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 - github.com/pborman/uuid v1.2.1 github.com/peterh/liner v1.2.0 github.com/prometheus/tsdb v0.10.0 github.com/rjeczalik/notify v0.9.1 github.com/rs/cors v1.7.0 - github.com/rs/xhandler v0.0.0-20170707052532-1eb70cf1520d // indirect github.com/shirou/gopsutil v2.20.5+incompatible + github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac // indirect github.com/spf13/cobra v1.1.1 github.com/spf13/viper v1.7.0 github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 - github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 github.com/tidwall/gjson v1.6.0 github.com/tidwall/pretty v1.0.0 github.com/tyler-smith/go-bip39 v1.0.2 - github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 github.com/xeipuuv/gojsonschema v1.2.0 - github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 // indirect golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 - golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e - golang.org/x/text v0.3.3 - golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e + golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c + golang.org/x/text v0.3.4 + golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 gonum.org/v1/gonum v0.8.1 gonum.org/v1/plot v0.8.0 + google.golang.org/protobuf v1.25.0 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 gopkg.in/urfave/cli.v1 v1.20.0 diff --git a/go.sum b/go.sum old mode 100755 new mode 100644 index 1288eb2bca..240b144d91 --- a/go.sum +++ b/go.sum @@ -1,5 +1,6 @@ -bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= @@ -21,18 +22,15 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20200628203458-851255f7a67b/go.mod h1:jiUwifN9cRl/zmco43aAqh0aV+s9GbhG13KcD+gEpkU= -github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZOMdj5HYo= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2 h1:6oiIS9yaG6XCCzhgAgKFfIWyo4LLCiDhZot6ltoThhY= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= -github.com/Azure/azure-storage-blob-go v0.7.0 h1:MuueVOYkufCxJw5YZzF842DY2MBsp+hLuh2apKY0mck= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs= github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE= github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.0 h1:CxTzQrySOxDnKpLjFJeZAS5Qrv/qFPkgLjx5bOAi//I= github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= github.com/Azure/go-autorest/autorest/adal v0.8.3 h1:O1AGG9Xig71FxdX9HO5pGNyZ7TbSyHaVg+5eJO/jSGw= github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= @@ -51,9 +49,6 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/DataDog/datadog-go v0.0.0-20180822151419-281ae9f2d895/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/JekaMas/notify v0.9.4 h1:Ns+DRf9kho8T0yQNSKoZqAnbvO/Hg3KmJCUaoRhL7MM= -github.com/JekaMas/notify v0.9.4/go.mod h1:KYZd45vBSOYP2/9lY38EjZtvKRZMfgWaJk8bvBxhIYk= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= @@ -64,16 +59,8 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= -github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI= -github.com/RoaringBitmap/roaring v0.4.18/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI= -github.com/RoaringBitmap/roaring v0.4.21/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= -github.com/RoaringBitmap/roaring v0.5.6-0.20201124195327-6ec715d630bc h1:8HwpW2Oc4fIDKj5eJPFLyT0Jykjjzv+vFuStyAvLXL8= -github.com/RoaringBitmap/roaring v0.5.6-0.20201124195327-6ec715d630bc/go.mod h1:WZ83fjBF/7uBHi6QoFyfGL4+xuV4Qn+xFkm4+vSzrhE= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/sarama v1.26.1/go.mod h1:NbSGBSSndYaIhRcBtY9V0U7AyH+x71bG668AuWys/yU= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= @@ -83,7 +70,6 @@ github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6Ro github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/airbrake/gobrake v3.6.1+incompatible/go.mod h1:wM4gu3Cn0W0K7GUuVWnlXZU11AGBXMILnrdOU8Kn00o= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af h1:wVe6/Ea46ZMeNkQjjBW6xcqyQA/j5e0D6GytH95g0gQ= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/jsonschema v0.0.0-20200530073317-71f438968921 h1:T3+cD5fYvuH36h7EZq+TDpm+d8a6FSD4pQsbmuGGQ8o= @@ -92,104 +78,48 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alexflint/go-arg v1.1.0/go.mod h1:3Rj4baqzWaGGmZA2+bVTV8zQOZEjBQAPBnL5xLT+ftY= -github.com/alexflint/go-arg v1.2.0/go.mod h1:3Rj4baqzWaGGmZA2+bVTV8zQOZEjBQAPBnL5xLT+ftY= -github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/anacrolix/dht v0.0.0-20180412060941-24cbf25b72a4/go.mod h1:hQfX2BrtuQsLQMYQwsypFAab/GvHg8qxwVi4OJdR1WI= -github.com/anacrolix/dht/v2 v2.0.1/go.mod h1:GbTT8BaEtfqab/LPd5tY41f3GvYeii3mmDUK300Ycyo= -github.com/anacrolix/dht/v2 v2.2.1-0.20191103020011-1dba080fb358/go.mod h1:d7ARx3WpELh9uOEEr0+8wvQeVTOkPse4UU6dKpv4q0E= -github.com/anacrolix/dht/v2 v2.3.2-0.20200103043204-8dce00767ebd/go.mod h1:cgjKyErDnKS6Mej5D1fEqBKg3KwFF2kpFZJp3L6/fGI= -github.com/anacrolix/dht/v2 v2.5.1-0.20200317023935-129f05e9b752/go.mod h1:7RLvyOjm+ZPA7vgFRP+1eRjFzrh27p/nF0VCk5LcjoU= -github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= -github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= -github.com/anacrolix/envpprof v1.0.1/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= -github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= -github.com/anacrolix/go-libutp v0.0.0-20180522111405-6baeb806518d/go.mod h1:beQSaSxwH2d9Eeu5ijrEnHei5Qhk+J6cDm1QkWFru4E= -github.com/anacrolix/go-libutp v1.0.2/go.mod h1:uIH0A72V++j0D1nnmTjjZUiH/ujPkFxYWkxQ02+7S0U= -github.com/anacrolix/log v0.0.0-20180412014343-2323884b361d/go.mod h1:sf/7c2aTldL6sRQj/4UKyjgVZBu2+M2z9wf7MmwPiew= -github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= -github.com/anacrolix/log v0.3.1-0.20190913000754-831e4ffe0174/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= -github.com/anacrolix/log v0.3.1-0.20191001111012-13cede988bcd/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= -github.com/anacrolix/log v0.4.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= -github.com/anacrolix/log v0.5.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= -github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= -github.com/anacrolix/log v0.7.0/go.mod h1:s5yBP/j046fm9odtUTbHOfDUq/zh1W8OkPpJtnX0oQI= -github.com/anacrolix/missinggo v0.0.0-20180522035225-b4a5853e62ff/go.mod h1:b0p+7cn+rWMIphK1gDH2hrDuwGOcbB6V4VXeSsEfHVk= -github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s= -github.com/anacrolix/missinggo v0.2.1-0.20190310234110-9fbdc9f242a8/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= -github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= -github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= -github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y= -github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ= -github.com/anacrolix/missinggo/v2 v2.2.0/go.mod h1:o0jgJoYOyaoYQ4E2ZMISVa9c88BbUBVQQW4QeRkNCGY= -github.com/anacrolix/missinggo/v2 v2.2.1-0.20191103010835-12360f38ced0/go.mod h1:ZzG3/cc3t+5zcYWAgYrJW0MBsSwNwOkTlNquBbP51Bc= -github.com/anacrolix/missinggo/v2 v2.3.0/go.mod h1:ZzG3/cc3t+5zcYWAgYrJW0MBsSwNwOkTlNquBbP51Bc= -github.com/anacrolix/missinggo/v2 v2.3.1/go.mod h1:3XNH0OEmyMUZuvXmYdl+FDfXd0vvSZhvOLy8CFx8tLg= -github.com/anacrolix/missinggo/v2 v2.4.1-0.20200227072623-f02f6484f997/go.mod h1:KY+ij+mWvwGuqSuecLjjPv5LFw5ICUc1UvRems3VAZE= -github.com/anacrolix/mmsg v0.0.0-20180515031531-a4a3ba1fc8bb/go.mod h1:x2/ErsYUmT77kezS63+wzZp8E3byYB0gzirM/WMBLfw= -github.com/anacrolix/mmsg v1.0.0/go.mod h1:x8kRaJY/dCrY9Al0PEcj1mb/uFHwP6GCJ9fLl4thEPc= -github.com/anacrolix/multiless v0.0.0-20191223025854-070b7994e841/go.mod h1:TrCLEZfIDbMVfLoQt5tOoiBS/uq4y8+ojuEVVvTNPX4= -github.com/anacrolix/stm v0.1.0/go.mod h1:ZKz7e7ERWvP0KgL7WXfRjBXHNRhlVRlbBQecqFtPq+A= -github.com/anacrolix/stm v0.1.1-0.20191106051447-e749ba3531cf/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg= -github.com/anacrolix/stm v0.2.0/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg= -github.com/anacrolix/sync v0.0.0-20171108081538-eee974e4f8c1/go.mod h1:+u91KiUuf0lyILI6x3n/XrW7iFROCZCG+TjgK8nW52w= -github.com/anacrolix/sync v0.0.0-20180611022320-3c4cb11f5a01/go.mod h1:+u91KiUuf0lyILI6x3n/XrW7iFROCZCG+TjgK8nW52w= -github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778/go.mod h1:s735Etp3joe/voe2sdaXLcqDdJSay1O0OPnM0ystjqk= -github.com/anacrolix/sync v0.2.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g= -github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= -github.com/anacrolix/tagflag v0.0.0-20180605133421-f477c8c2f14c/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= -github.com/anacrolix/tagflag v0.0.0-20180803105420-3a8ff5428f76/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= -github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= -github.com/anacrolix/tagflag v1.0.1/go.mod h1:gb0fiMQ02qU1djCSqaxGmruMvZGrMwSReidMB0zjdxo= -github.com/anacrolix/torrent v0.0.0-20180622074351-fefeef4ee9eb/go.mod h1:3vcFVxgOASslNXHdivT8spyMRBanMCenHRpe0u5vpBs= -github.com/anacrolix/torrent v1.7.1/go.mod h1:uvOcdpOjjrAq3uMP/u1Ide35f6MJ/o8kMnFG8LV3y6g= -github.com/anacrolix/torrent v1.9.0/go.mod h1:jJJ6lsd2LD1eLHkUwFOhy7I0FcLYH0tHKw2K7ZYMHCs= -github.com/anacrolix/torrent v1.11.0/go.mod h1:FwBai7SyOFlflvfEOaM88ag/jjcBWxTOqD6dVU/lKKA= -github.com/anacrolix/torrent v1.15.2/go.mod h1:sJtcAZtlGaZLo7wCXT/EZV+hATsq0Bg6pVhhzACY0E0= -github.com/anacrolix/upnp v0.1.1/go.mod h1:LXsbsp5h+WGN7YR+0A7iVXm5BL1LYryDev1zuJMWYQo= -github.com/anacrolix/utp v0.0.0-20180219060659-9e0e1d1d0572/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/aristanetworks/fsnotify v1.4.2/go.mod h1:D/rtu7LpjYM8tRJphJ0hUBYpjai8SfX+aSNsWDTq/Ks= -github.com/aristanetworks/glog v0.0.0-20191112221043-67e8567f59f3/go.mod h1:KASm+qXFKs/xjSoWn30NrWBBvdTTQq+UjkhjEJHfSFA= -github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847 h1:rtI0fD4oG/8eVokGVPYJEW1F88p1ZNgXiEIs9thEE4A= github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= -github.com/aristanetworks/goarista v0.0.0-20200812190859-4cb0e71f3c0e h1:tkEt0le4Lv5+VmcxZPIVSrP8LVPLhndIm/BOP7iPh/w= -github.com/aristanetworks/goarista v0.0.0-20200812190859-4cb0e71f3c0e/go.mod h1:QZe5Yh80Hp1b6JxQdpfSEEe8X7hTyTEZSosSrFf/oJE= -github.com/aristanetworks/splunk-hec-go v0.3.3/go.mod h1:1VHO9r17b0K7WmOlLb9nTk/2YanvOEnLMUgsFrxBROc= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.25.48 h1:J82DYDGZHOKHdhx6hD24Tm30c2C3GchYGfN0mf9iKUk= github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.34.21 h1:M97FXuiJgDHwD4mXhrIZ7RJ4xXV6uZVPvIC2qb+HfYE= -github.com/aws/aws-sdk-go v1.34.21/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/benbjohnson/immutable v0.2.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI= +github.com/aws/aws-sdk-go-v2 v1.2.0 h1:BS+UYpbsElC82gB+2E2jiCBg36i8HlubTB/dO/moQ9c= +github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= +github.com/aws/aws-sdk-go-v2/config v1.1.1 h1:ZAoq32boMzcaTW9bcUacBswAmHTbvlvDJICgHFZuECo= +github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= +github.com/aws/aws-sdk-go-v2/credentials v1.1.1 h1:NbvWIM1Mx6sNPTxowHgS2ewXCRp+NGTzUYb/96FZJbY= +github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2 h1:EtEU7WRaWliitZh2nmuxEXrN0Cb8EgPUFGIoTMeqbzI= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2 h1:4AH9fFjUlVktQMznF+YN33aWNXaR4VgDXyP28qokJC0= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= +github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1 h1:cKr6St+CtC3/dl/rEBJvlk7A/IN5D5F02GNkGzfbtVU= +github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4= +github.com/aws/aws-sdk-go-v2/service/sso v1.1.1 h1:37QubsarExl5ZuCBlnRP+7l1tNwZPBSTqpTBrPH98RU= +github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= +github.com/aws/aws-sdk-go-v2/service/sts v1.1.1 h1:TJoIfnIFubCX0ACVeJ0w46HEH5MwjwYN4iFhuYIhfIY= +github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= +github.com/aws/smithy-go v1.1.0 h1:D6CSsM3gdxaGaqXnPgOBCeL6Mophqzu7KJOu7zW78sU= +github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blend/go-sdk v1.1.1/go.mod h1:IP1XHXFveOXHRnojRJO7XvqWGqyzevtXND9AdSztAe8= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= -github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= -github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= -github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M= github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= @@ -204,12 +134,9 @@ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= -github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2 h1:t8KYCwSKsOEZBFELI4Pn/phbp38iJ1RRAkDFNin1aak= -github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= @@ -223,17 +150,17 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9 h1:J82+/8rub3qSy0HxEnoYD8cs+HDlHWYrqYXe2Vqxluk= github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= -github.com/cloudflare/cloudflare-go v0.13.2 h1:bhMGoNhAg21DuqJjU9jQepRRft6vYfo6pejT3NN4V6A= -github.com/cloudflare/cloudflare-go v0.13.2/go.mod h1:27kfc1apuifUmJhp069y0+hwlKDg4bd8LWlu7oKeZvM= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cloudflare/cloudflare-go v0.14.0 h1:gFqGlGl/5f9UGXAaKapCGUfaTCgRKKnzu2VvzMZlOFA= +github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= +github.com/consensys/bavard v0.1.8-0.20210105233146-c16790d2aa8b/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= +github.com/consensys/goff v0.3.10/go.mod h1:xTldOBEHmFiYS0gPXd3NsaEqZWlnmeWcRLWgD3ba3xc= +github.com/consensys/gurvy v0.3.8 h1:H2hvjvT2OFMgdMn5ZbhXqHt+F8DJ2clZW7Vmc0kFFxc= +github.com/consensys/gurvy v0.3.8/go.mod h1:sN75xnsiD593XnhbhvG2PkOy194pZBzqShWF/kwuW/g= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -243,7 +170,6 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= @@ -258,40 +184,27 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dop251/goja v0.0.0-20200219165308-d1232e640a87/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bacAU/eSnV3dAxVpepaghAdhGoQ= github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= -github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c h1:JHHhtb9XWJrGNMcrVP6vyzO4dusgi/HnceHTgxSejUM= github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= -github.com/elliotchance/orderedmap v1.2.0/go.mod h1:8hdSl6jmveQw8ScByd3AaNHNk51RhbTazdqtTty+NFw= -github.com/emicklei/dot v0.11.0 h1:Ase39UD9T9fRBOb5ptgpixrxfx8abVzNWZi2+lr53PI= -github.com/emicklei/dot v0.11.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/etclabscore/go-jsonschema-walk v0.0.6 h1:DrNzoKWKd8f8XB5nFGBY00IcjakRE22OTI12k+2LkyY= github.com/etclabscore/go-jsonschema-walk v0.0.6/go.mod h1:VdfDY72AFAiUhy0ZXEaWSpveGjMT5JcDIm903NGqFwQ= github.com/etclabscore/go-openrpc-reflect v0.0.35 h1:BHr8JJtMB46eVVc3sAUuLH3s9YFp8KsBEm3VkRF6mGY= @@ -299,42 +212,31 @@ github.com/etclabscore/go-openrpc-reflect v0.0.35/go.mod h1:XgVhDkb6BeOtc4qf04k4 github.com/ethereum/evmc/v7 v7.3.0 h1:4CsjJ+vSRrkzxOHeG1lFRGk4sG4/PgzXnWuRNgLGMJ0= github.com/ethereum/evmc/v7 v7.3.0/go.mod h1:q2Q0rCSUlIkngd+mZwfCzEUbvB0IIopH1+7hcs9QuDg= github.com/ethereum/go-ethereum v1.9.12/go.mod h1:PvsVkQmhZFx92Y+h2ylythYlheEDt/uBgFbl61Js/jo= +github.com/ethereum/go-ethereum v1.9.25/go.mod h1:vMkFiYLHI4tgPw4k2j4MHKoovchFE8plZ0M9VMk4/oM= github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fjl/gencodec v0.0.0-20191126094850-e283372f291f/go.mod h1:q+7Z5oyy8cvKF3TakcuihvQvBHFTnXjB+7UP1e2Q+1o= -github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc h1:jtW8jbpkO4YirRSyepBOH8E+2HEw6/hKkBvFPwhUN8c= github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= -github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-fonts/dejavu v0.1.0 h1:JSajPXURYqpr+Cu8U9bt8K+XcACIHWqWrvWCKyeFmVQ= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-gl/gl v0.0.0-20180407155706-68e253793080/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= -github.com/go-gl/glfw v0.0.0-20180426074136-46a8d530c326/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= @@ -342,7 +244,6 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO github.com/go-latex/latex v0.0.0-20200518072620-0806b477ea35 h1:uroDDLmuCK5Pz5J/Ef5vCL6F0sJmAtZFTm0/cF027F4= github.com/go-latex/latex v0.0.0-20200518072620-0806b477ea35/go.mod h1:PNI+CcWytn/2Z/9f1SGOOYn0eILruVyp0v2/iAs8asQ= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -353,36 +254,32 @@ github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDB github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg= github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/spec v0.19.7 h1:0xWSeMd35y5avQAThZR2PkEuqSosoS5t6gDH4L8n11M= github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/spec v0.19.11 h1:ogU5q8dtp3MMPn59a9VRrPKVxvJHEs5P7yNMR5sNnis= github.com/go-openapi/spec v0.19.11/go.mod h1:vqK/dIdLGCosfvYsQV3WfC7N3TiZSnGY2RZKoFK7X28= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.8 h1:vfK6jLhs7OI4tAXkvkooviaE1JEPcw3mutyegLHHjmk= github.com/go-openapi/swag v0.19.8/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= github.com/go-openapi/swag v0.19.11 h1:RFTu/dlFySpyVvJDfp/7674JY4SDglYWKztbiIGFpmc= github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY= -github.com/go-sourcemap/sourcemap v2.1.2+incompatible h1:0b/xya7BKGhXuqFESKM4oIiRo9WOt2ebz7KxfreD6ug= github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.5 h1:AKODKU3pDH1RzZzm6YZu77YWtEAq6uh1rLIAQlay2qc= github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= +github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= @@ -399,24 +296,19 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26 h1:lMm2hD9Fy0ynom5+85/pbdkiYcBqM1JWmhpAXLmy0fw= -github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3 h1:ur2rms48b3Ep1dxh7aUV2FZEQ8jEVO2F6ILKx8ofkAg= github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= @@ -424,7 +316,10 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -435,13 +330,12 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I= +github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -450,16 +344,11 @@ github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gosuri/uilive v0.0.0-20170323041506-ac356e6e42cd/go.mod h1:qkLSc0A5EXSP6B04TrN4oQoxqFI7A8XvoXSlJi8cwk8= -github.com/gosuri/uilive v0.0.3/go.mod h1:qkLSc0A5EXSP6B04TrN4oQoxqFI7A8XvoXSlJi8cwk8= -github.com/gosuri/uiprogress v0.0.0-20170224063937-d0567a9d84a1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0= -github.com/gosuri/uiprogress v0.0.1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0= github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= -github.com/graph-gophers/graphql-go v0.0.0-20200819123640-3b5ddcd884ae h1:TQuRfD07N7uHp+CW7rCfR579o6PDnwJacRBJH74RMq0= -github.com/graph-gophers/graphql-go v0.0.0-20200819123640-3b5ddcd884ae/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29 h1:sezaKhEfPFg8W0Enm61B9Gs911H8iesGY5R8NDPtd1M= +github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -477,31 +366,30 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.1.1 h1:4JywC80b+/hSfljFlEBLHrrh+CIONLDz9NuFl0af4Mw= github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= -github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= -github.com/huandu/xstrings v1.2.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag= -github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= +github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88 h1:bcAj8KroPf552TScjFPIakjH2/tdIrIH8F+cc4v4SRo= +github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88/go.mod h1:nNs7wvRfN1eKaMknBydLNQU6146XQim8t4h+q90biWo= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= github.com/iancoleman/orderedmap v0.1.0 h1:2orAxZBJsvimgEBmMWfXaFlzSG2fbQil5qzP3F6cCkg= @@ -512,12 +400,12 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/flux v0.65.0/go.mod h1:BwN2XG2lMszOoquQaFdPET8FRQfrXiZsWmcMO9rkaVY= +github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= -github.com/influxdata/influxdb v1.8.2 h1:f+vS/DtIjoWMm5DsJtZvwN7ZiTBpOEz463DsipNQRE8= -github.com/influxdata/influxdb v1.8.2/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ= +github.com/influxdata/influxdb v1.8.3 h1:WEypI1BQFTT4teLM+1qkEcvUi0dAvopAI/ir0vAiBg8= +github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxql v1.1.0/go.mod h1:KpVI7okXjK6PRi3Z5B+mtKZli+R1DnZgb3N+tzevNgo= +github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= @@ -526,26 +414,24 @@ github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1: github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e h1:UvSe12bq+Uj2hWd8aOlwPmoZ+CITRFrdit+sDGfAg8U= +github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= -github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= -github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -559,36 +445,31 @@ github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 h1:ZHuwnjpP8LsVsUYqTqeVAI+GfDfJ6UNPrExZF+vX/DQ= github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/kevinburke/go-bindata v3.21.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM= +github.com/kilic/bls12-381 v0.0.0-20201226121925-69dacb279461/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/ledgerwatch/lmdb-go v1.17.4/go.mod h1:NKRpCxksoTQPyxsUcBiVOe0135uqnJsnf6cElxmOL0o= -github.com/ledgerwatch/turbo-geth v0.0.0-20210308133656-9d397082536c/go.mod h1:9RNKH1PVtf16otlST54SEFvOSvQEyJamKvrJ5knlHIs= +github.com/leanovate/gopter v0.2.8/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/llgcode/draw2d v0.0.0-20200603164053-19660b984a28/go.mod h1:mVa0dA29Db2S4LVqDYLlsePDzRJLDfdhVZiI15uY0FA= -github.com/llgcode/ps v0.0.0-20150911083025-f1443b32eedb/go.mod h1:1l8ky+Ew27CMX29uG+a2hNOKpeNYEQjjtiALiBlFQbY= -github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/lukechampine/stm v0.0.0-20191022212748-05486c32d236/go.mod h1:wTLsd5FC9rts7GkMpsPGk64CIuea+03yaLAp19Jmlg8= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -607,20 +488,14 @@ github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HN github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-sqlite3 v1.7.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.13.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v2.0.2+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -640,7 +515,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0= @@ -653,6 +527,7 @@ github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= @@ -660,24 +535,21 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= -github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 h1:CznVS40zms0Dj5he4ERo+fRPtO0qxUk8lA8Xu3ddet0= github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333/go.mod h1:Ag6rSXkHIckQmjFBCweJEEt1mrTPBv8b9W4aU/NQWfI= -github.com/openconfig/gnmi v0.0.0-20190823184014-89b2bf29312c/go.mod h1:t+O9It+LKzfOAhKTT5O0ehDix+MTqbtT0T9t+7zzOvc= -github.com/openconfig/reference v0.0.0-20190727015836-8dfd928c9696/go.mod h1:ym2A+zigScwkSEb/cVQB0/ZMpU3rqiH6X7WRRsxgOGw= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -693,12 +565,9 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= -github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/petar/GoLLRB v0.0.0-20190514000832-33fb24c13b99/go.mod h1:HUpKUBZnpzkdx0kD/+Yfuft+uD3zHGtXF/XJB14TUr4= github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/peterh/liner v1.2.0 h1:w/UPXyl5GfahFxcTOz2j9wCIHNI+pUPr2laqpojKNCg= @@ -707,7 +576,6 @@ github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.4.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -722,38 +590,27 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.0.10/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic= github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE= github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= @@ -763,35 +620,28 @@ github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9Ac github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= -github.com/rs/xhandler v0.0.0-20170707052532-1eb70cf1520d/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v2.20.5+incompatible h1:tYH07UPoQt0OCQdgWWMgYHy3/a9bcxNpBIysykNIP7I= github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v3 v3.21.1/go.mod h1:igHnfak0qnw1biGeI2qKQvu0ZkwvEkUcCLlYhZzdr/4= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac h1:wbW+Bybf9pXxnCFAOWZTqkRjAc7rAIwo2e1ArUhiHxg= github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= -github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= @@ -810,9 +660,7 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 h1:Oo2KZNP70KE0+IUJSidPj/BFS/RXNHmKIJOdckzml2E= github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= -github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= -github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM= github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -820,23 +668,18 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/syncthing/syncthing v0.14.48-rc.4/go.mod h1:nw3siZwHPA6M8iSfjDCWQ402eqvEIasMQOE8nFOxy7M= github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= +github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= -github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= -github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= @@ -844,31 +687,16 @@ github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0 github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tinylib/msgp v1.1.1/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tjfoc/gmsm v1.3.0/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/tyler-smith/go-bip39 v1.0.2 h1:+t3w+KwLXO6154GNJY+qUtIxLTmFjfUmpguQT1OlOT8= github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= -github.com/ugorji/go v1.1.13/go.mod h1:jxau1n+/wyTGLQoCkjok9r5zFa/FxT6eI5HiHKQszjc= -github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU= -github.com/ugorji/go/codec/codecgen v1.1.13/go.mod h1:EhCxlc7Crov+HLygD4+hBCitXNrrGKRrRWj+pRsyJGg= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= -github.com/wcharczuk/go-chart v2.0.1+incompatible/go.mod h1:PF5tmL4EIx/7Wf+hEkpCqYi5He4u90sw+0+6FhrryuE= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bloom v0.0.0-20170505221640-54e3b963ee16/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8= -github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8= -github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk= github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= @@ -877,13 +705,8 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= -github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -906,12 +729,10 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -923,6 +744,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= @@ -945,15 +767,14 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -967,33 +788,25 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190318221613-d196dffd7c2b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191125084936-ffdde1057850/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY= golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d h1:1aflnvSoWWLI2k/dMUAl5lvU1YO4Mb4hz0gh+1rjcxU= +golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1004,18 +817,18 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1023,56 +836,49 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191126131656-8a8471f7e56d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs= -golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210105210732-16f7687f5001/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181205014116-22934f0fdb62/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1097,18 +903,16 @@ golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= @@ -1116,6 +920,7 @@ gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= gonum.org/v1/gonum v0.8.1 h1:wGtP3yGpc5mCLOLeTeBdjeui9oZSz5De0eOjMLC/QuQ= gonum.org/v1/gonum v0.8.1/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gonum.org/v1/plot v0.8.0 h1:dNgubmltsMoehfn6XgbutHpicbUfbkcGSxkICy1bC4o= @@ -1149,8 +954,6 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200218151345-dad8c97a84f5/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1161,13 +964,8 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.1/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1179,9 +977,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a/go.mod h1:KF9sEfUPAXdG8Oev9e99iLGnl2uJMjc5B+4y3O7x610= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -1189,18 +987,13 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= -gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= -gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= -gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= -gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUkSBlpnkWV1bJ+vv3mOgQEltEJ2rPxroVu0= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= -gopkg.in/redis.v4 v4.2.4/go.mod h1:8KREHdypkCEojGKQcjMqAODMICIVwZAONWq8RowTITA= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= @@ -1208,14 +1001,14 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1224,6 +1017,7 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/graphql/graphql.go b/graphql/graphql.go index ad0e4453d4..f7b67e4ccc 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -20,6 +20,8 @@ package graphql import ( "context" "errors" + "fmt" + "strconv" "time" "github.com/ethereum/go-ethereum" @@ -31,7 +33,6 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" ) @@ -39,6 +40,37 @@ var ( errBlockInvariant = errors.New("block objects must be instantiated with at least one of num or hash") ) +type Long int64 + +// ImplementsGraphQLType returns true if Long implements the provided GraphQL type. +func (b Long) ImplementsGraphQLType(name string) bool { return name == "Long" } + +// UnmarshalGraphQL unmarshals the provided GraphQL query data. +func (b *Long) UnmarshalGraphQL(input interface{}) error { + var err error + switch input := input.(type) { + case string: + // uncomment to support hex values + //if strings.HasPrefix(input, "0x") { + // // apply leniency and support hex representations of longs. + // value, err := hexutil.DecodeUint64(input) + // *b = Long(value) + // return err + //} else { + value, err := strconv.ParseInt(input, 10, 64) + *b = Long(value) + return err + //} + case int32: + *b = Long(input) + case int64: + *b = Long(input) + default: + err = fmt.Errorf("unexpected type %T for Long", input) + } + return err +} + // Account represents an Ethereum account at a particular block. type Account struct { backend ethapi.Backend @@ -77,7 +109,7 @@ func (a *Account) Code(ctx context.Context) (hexutil.Bytes, error) { if err != nil { return hexutil.Bytes{}, err } - return hexutil.Bytes(state.GetCode(a.address)), nil + return state.GetCode(a.address), nil } func (a *Account) Storage(ctx context.Context, args struct{ Slot common.Hash }) (common.Hash, error) { @@ -116,7 +148,21 @@ func (l *Log) Topics(ctx context.Context) []common.Hash { } func (l *Log) Data(ctx context.Context) hexutil.Bytes { - return hexutil.Bytes(l.log.Data) + return l.log.Data +} + +// AccessTuple represents EIP-2930 +type AccessTuple struct { + address common.Address + storageKeys *[]common.Hash +} + +func (at *AccessTuple) Address(ctx context.Context) common.Address { + return at.address +} + +func (at *AccessTuple) StorageKeys(ctx context.Context) *[]common.Hash { + return at.storageKeys } // Transaction represents an Ethereum transaction. @@ -157,7 +203,7 @@ func (t *Transaction) InputData(ctx context.Context) (hexutil.Bytes, error) { if err != nil || tx == nil { return hexutil.Bytes{}, err } - return hexutil.Bytes(tx.Data()), nil + return tx.Data(), nil } func (t *Transaction) Gas(ctx context.Context) (hexutil.Uint64, error) { @@ -213,12 +259,8 @@ func (t *Transaction) From(ctx context.Context, args BlockNumberArgs) (*Account, if err != nil || tx == nil { return nil, err } - var signer types.Signer = types.HomesteadSigner{} - if tx.Protected() { - signer = types.NewEIP155Signer(tx.ChainId()) - } + signer := types.LatestSigner(t.backend.ChainConfig()) from, _ := types.Sender(signer, tx) - return &Account{ backend: t.backend, address: from, @@ -259,30 +301,30 @@ func (t *Transaction) getReceipt(ctx context.Context) (*types.Receipt, error) { return receipts[t.index], nil } -func (t *Transaction) Status(ctx context.Context) (*hexutil.Uint64, error) { +func (t *Transaction) Status(ctx context.Context) (*Long, error) { receipt, err := t.getReceipt(ctx) if err != nil || receipt == nil { return nil, err } - ret := hexutil.Uint64(receipt.Status) + ret := Long(receipt.Status) return &ret, nil } -func (t *Transaction) GasUsed(ctx context.Context) (*hexutil.Uint64, error) { +func (t *Transaction) GasUsed(ctx context.Context) (*Long, error) { receipt, err := t.getReceipt(ctx) if err != nil || receipt == nil { return nil, err } - ret := hexutil.Uint64(receipt.GasUsed) + ret := Long(receipt.GasUsed) return &ret, nil } -func (t *Transaction) CumulativeGasUsed(ctx context.Context) (*hexutil.Uint64, error) { +func (t *Transaction) CumulativeGasUsed(ctx context.Context) (*Long, error) { receipt, err := t.getReceipt(ctx) if err != nil || receipt == nil { return nil, err } - ret := hexutil.Uint64(receipt.CumulativeGasUsed) + ret := Long(receipt.CumulativeGasUsed) return &ret, nil } @@ -314,6 +356,31 @@ func (t *Transaction) Logs(ctx context.Context) (*[]*Log, error) { return &ret, nil } +func (t *Transaction) Type(ctx context.Context) (*int32, error) { + tx, err := t.resolve(ctx) + if err != nil { + return nil, err + } + txType := int32(tx.Type()) + return &txType, nil +} + +func (t *Transaction) AccessList(ctx context.Context) (*[]*AccessTuple, error) { + tx, err := t.resolve(ctx) + if err != nil || tx == nil { + return nil, err + } + accessList := tx.AccessList() + ret := make([]*AccessTuple, 0, len(accessList)) + for _, al := range accessList { + ret = append(ret, &AccessTuple{ + address: al.Address, + storageKeys: &al.StorageKeys, + }) + } + return &ret, nil +} + func (t *Transaction) R(ctx context.Context) (hexutil.Big, error) { tx, err := t.resolve(ctx) if err != nil || tx == nil { @@ -410,18 +477,18 @@ func (b *Block) resolveReceipts(ctx context.Context) ([]*types.Receipt, error) { if err != nil { return nil, err } - b.receipts = []*types.Receipt(receipts) + b.receipts = receipts } return b.receipts, nil } -func (b *Block) Number(ctx context.Context) (hexutil.Uint64, error) { +func (b *Block) Number(ctx context.Context) (Long, error) { header, err := b.resolveHeader(ctx) if err != nil { return 0, err } - return hexutil.Uint64(header.Number.Uint64()), nil + return Long(header.Number.Uint64()), nil } func (b *Block) Hash(ctx context.Context) (common.Hash, error) { @@ -435,20 +502,20 @@ func (b *Block) Hash(ctx context.Context) (common.Hash, error) { return b.hash, nil } -func (b *Block) GasLimit(ctx context.Context) (hexutil.Uint64, error) { +func (b *Block) GasLimit(ctx context.Context) (Long, error) { header, err := b.resolveHeader(ctx) if err != nil { return 0, err } - return hexutil.Uint64(header.GasLimit), nil + return Long(header.GasLimit), nil } -func (b *Block) GasUsed(ctx context.Context) (hexutil.Uint64, error) { +func (b *Block) GasUsed(ctx context.Context) (Long, error) { header, err := b.resolveHeader(ctx) if err != nil { return 0, err } - return hexutil.Uint64(header.GasUsed), nil + return Long(header.GasUsed), nil } func (b *Block) Parent(ctx context.Context) (*Block, error) { @@ -490,7 +557,7 @@ func (b *Block) Nonce(ctx context.Context) (hexutil.Bytes, error) { if err != nil { return hexutil.Bytes{}, err } - return hexutil.Bytes(header.Nonce[:]), nil + return header.Nonce[:], nil } func (b *Block) MixHash(ctx context.Context) (common.Hash, error) { @@ -564,7 +631,7 @@ func (b *Block) ExtraData(ctx context.Context) (hexutil.Bytes, error) { if err != nil { return hexutil.Bytes{}, err } - return hexutil.Bytes(header.Extra), nil + return header.Extra, nil } func (b *Block) LogsBloom(ctx context.Context) (hexutil.Bytes, error) { @@ -572,7 +639,7 @@ func (b *Block) LogsBloom(ctx context.Context) (hexutil.Bytes, error) { if err != nil { return hexutil.Bytes{}, err } - return hexutil.Bytes(header.Bloom.Bytes()), nil + return header.Bloom.Bytes(), nil } func (b *Block) TotalDifficulty(ctx context.Context) (hexutil.Big, error) { @@ -777,20 +844,20 @@ type CallData struct { // CallResult encapsulates the result of an invocation of the `call` accessor. type CallResult struct { - data hexutil.Bytes // The return data from the call - gasUsed hexutil.Uint64 // The amount of gas used - status hexutil.Uint64 // The return status of the call - 0 for failure or 1 for success. + data hexutil.Bytes // The return data from the call + gasUsed Long // The amount of gas used + status Long // The return status of the call - 0 for failure or 1 for success. } func (c *CallResult) Data() hexutil.Bytes { return c.data } -func (c *CallResult) GasUsed() hexutil.Uint64 { +func (c *CallResult) GasUsed() Long { return c.gasUsed } -func (c *CallResult) Status() hexutil.Uint64 { +func (c *CallResult) Status() Long { return c.status } @@ -807,29 +874,29 @@ func (b *Block) Call(ctx context.Context, args struct { if err != nil { return nil, err } - status := hexutil.Uint64(1) + status := Long(1) if result.Failed() { status = 0 } return &CallResult{ data: result.ReturnData, - gasUsed: hexutil.Uint64(result.UsedGas), + gasUsed: Long(result.UsedGas), status: status, }, nil } func (b *Block) EstimateGas(ctx context.Context, args struct { Data ethapi.CallArgs -}) (hexutil.Uint64, error) { +}) (Long, error) { if b.numberOrHash == nil { _, err := b.resolveHeader(ctx) if err != nil { - return hexutil.Uint64(0), err + return 0, err } } gas, err := ethapi.DoEstimateGas(ctx, b.backend, args.Data, *b.numberOrHash, b.backend.RPCGasCap()) - return gas, err + return Long(gas), err } type Pending struct { @@ -877,23 +944,24 @@ func (p *Pending) Call(ctx context.Context, args struct { if err != nil { return nil, err } - status := hexutil.Uint64(1) + status := Long(1) if result.Failed() { status = 0 } return &CallResult{ data: result.ReturnData, - gasUsed: hexutil.Uint64(result.UsedGas), + gasUsed: Long(result.UsedGas), status: status, }, nil } func (p *Pending) EstimateGas(ctx context.Context, args struct { Data ethapi.CallArgs -}) (hexutil.Uint64, error) { +}) (Long, error) { pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) - return ethapi.DoEstimateGas(ctx, p.backend, args.Data, pendingBlockNr, p.backend.RPCGasCap()) + gas, err := ethapi.DoEstimateGas(ctx, p.backend, args.Data, pendingBlockNr, p.backend.RPCGasCap()) + return Long(gas), err } // Resolver is the top-level object in the GraphQL hierarchy. @@ -902,12 +970,15 @@ type Resolver struct { } func (r *Resolver) Block(ctx context.Context, args struct { - Number *hexutil.Uint64 + Number *Long Hash *common.Hash }) (*Block, error) { var block *Block if args.Number != nil { - number := rpc.BlockNumber(uint64(*args.Number)) + if *args.Number < 0 { + return nil, nil + } + number := rpc.BlockNumber(*args.Number) numberOrHash := rpc.BlockNumberOrHashWithNumber(number) block = &Block{ backend: r.backend, @@ -939,10 +1010,10 @@ func (r *Resolver) Block(ctx context.Context, args struct { } func (r *Resolver) Blocks(ctx context.Context, args struct { - From hexutil.Uint64 - To *hexutil.Uint64 + From *Long + To *Long }) ([]*Block, error) { - from := rpc.BlockNumber(args.From) + from := rpc.BlockNumber(*args.From) var to rpc.BlockNumber if args.To != nil { @@ -985,7 +1056,7 @@ func (r *Resolver) Transaction(ctx context.Context, args struct{ Hash common.Has func (r *Resolver) SendRawTransaction(ctx context.Context, args struct{ Data hexutil.Bytes }) (common.Hash, error) { tx := new(types.Transaction) - if err := rlp.DecodeBytes(args.Data, tx); err != nil { + if err := tx.UnmarshalBinary(args.Data); err != nil { return common.Hash{}, err } hash, err := ethapi.SubmitTransaction(ctx, r.backend, tx) @@ -1040,10 +1111,6 @@ func (r *Resolver) GasPrice(ctx context.Context) (hexutil.Big, error) { return hexutil.Big(*price), err } -func (r *Resolver) ProtocolVersion(ctx context.Context) (int32, error) { - return int32(r.backend.ProtocolVersion()), nil -} - func (r *Resolver) ChainID(ctx context.Context) (hexutil.Big, error) { return hexutil.Big(*r.backend.ChainConfig().GetChainID()), nil } diff --git a/graphql/graphql_test.go b/graphql/graphql_test.go index 5ba9c95537..6ba5400651 100644 --- a/graphql/graphql_test.go +++ b/graphql/graphql_test.go @@ -19,17 +19,36 @@ package graphql import ( "fmt" "io/ioutil" + "math/big" "net/http" "strings" "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/crypto" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/params/types/genesisT" + "github.com/stretchr/testify/assert" ) func TestBuildSchema(t *testing.T) { - stack, err := node.New(&node.DefaultConfig) + ddir, err := ioutil.TempDir("", "graphql-buildschema") + if err != nil { + t.Fatalf("failed to create temporary datadir: %v", err) + } + // Copy config + conf := node.DefaultConfig + conf.DataDir = ddir + stack, err := node.New(&conf) if err != nil { t.Fatalf("could not create new node: %v", err) } @@ -40,62 +59,167 @@ func TestBuildSchema(t *testing.T) { } // Tests that a graphQL request is successfully handled when graphql is enabled on the specified endpoint -func TestGraphQLHTTPOnSamePort_GQLRequest_Successful(t *testing.T) { - stack := createNode(t, true) +func TestGraphQLBlockSerialization(t *testing.T) { + stack := createNode(t, true, false) defer stack.Close() // start node if err := stack.Start(); err != nil { t.Fatalf("could not start node: %v", err) } - // create http request - body := strings.NewReader("{\"query\": \"{block{number}}\",\"variables\": null}") - gqlReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/graphql", "127.0.0.1:9393"), body) - if err != nil { - t.Error("could not issue new http request ", err) + + for i, tt := range []struct { + body string + want string + code int + }{ + { // Should return latest block + body: `{"query": "{block{number}}","variables": null}`, + want: `{"data":{"block":{"number":10}}}`, + code: 200, + }, + { // Should return info about latest block + body: `{"query": "{block{number,gasUsed,gasLimit}}","variables": null}`, + want: `{"data":{"block":{"number":10,"gasUsed":0,"gasLimit":11500000}}}`, + code: 200, + }, + { + body: `{"query": "{block(number:0){number,gasUsed,gasLimit}}","variables": null}`, + want: `{"data":{"block":{"number":0,"gasUsed":0,"gasLimit":11500000}}}`, + code: 200, + }, + { + body: `{"query": "{block(number:-1){number,gasUsed,gasLimit}}","variables": null}`, + want: `{"data":{"block":null}}`, + code: 200, + }, + { + body: `{"query": "{block(number:-500){number,gasUsed,gasLimit}}","variables": null}`, + want: `{"data":{"block":null}}`, + code: 200, + }, + { + body: `{"query": "{block(number:\"0\"){number,gasUsed,gasLimit}}","variables": null}`, + want: `{"data":{"block":{"number":0,"gasUsed":0,"gasLimit":11500000}}}`, + code: 200, + }, + { + body: `{"query": "{block(number:\"-33\"){number,gasUsed,gasLimit}}","variables": null}`, + want: `{"data":{"block":null}}`, + code: 200, + }, + { + body: `{"query": "{block(number:\"1337\"){number,gasUsed,gasLimit}}","variables": null}`, + want: `{"data":{"block":null}}`, + code: 200, + }, + { + body: `{"query": "{block(number:\"0xbad\"){number,gasUsed,gasLimit}}","variables": null}`, + want: `{"errors":[{"message":"strconv.ParseInt: parsing \"0xbad\": invalid syntax"}],"data":{}}`, + code: 400, + }, + { // hex strings are currently not supported. If that's added to the spec, this test will need to change + body: `{"query": "{block(number:\"0x0\"){number,gasUsed,gasLimit}}","variables": null}`, + want: `{"errors":[{"message":"strconv.ParseInt: parsing \"0x0\": invalid syntax"}],"data":{}}`, + code: 400, + }, + { + body: `{"query": "{block(number:\"a\"){number,gasUsed,gasLimit}}","variables": null}`, + want: `{"errors":[{"message":"strconv.ParseInt: parsing \"a\": invalid syntax"}],"data":{}}`, + code: 400, + }, + { + body: `{"query": "{bleh{number}}","variables": null}"`, + want: `{"errors":[{"message":"Cannot query field \"bleh\" on type \"Query\".","locations":[{"line":1,"column":2}]}]}`, + code: 400, + }, + // should return `estimateGas` as decimal + { + body: `{"query": "{block{ estimateGas(data:{}) }}"}`, + want: `{"data":{"block":{"estimateGas":53000}}}`, + code: 200, + }, + // should return `status` as decimal + { + body: `{"query": "{block {number call (data : {from : \"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b\", to: \"0x6295ee1b4f6dd65047762f924ecd367c17eabf8f\", data :\"0x12a7b914\"}){data status}}}"}`, + want: `{"data":{"block":{"number":10,"call":{"data":"0x","status":1}}}}`, + code: 200, + }, + } { + resp, err := http.Post(fmt.Sprintf("%s/graphql", stack.HTTPEndpoint()), "application/json", strings.NewReader(tt.body)) + if err != nil { + t.Fatalf("could not post: %v", err) + } + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("could not read from response body: %v", err) + } + if have := string(bodyBytes); have != tt.want { + t.Errorf("testcase %d %s,\nhave:\n%v\nwant:\n%v", i, tt.body, have, tt.want) + } + if tt.code != resp.StatusCode { + t.Errorf("testcase %d %s,\nwrong statuscode, have: %v, want: %v", i, tt.body, resp.StatusCode, tt.code) + } } - gqlReq.Header.Set("Content-Type", "application/json") - // read from response - resp := doHTTPRequest(t, gqlReq) - bodyBytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("could not read from response body: %v", err) +} + +func TestGraphQLBlockSerializationEIP2718(t *testing.T) { + stack := createNode(t, true, true) + defer stack.Close() + // start node + if err := stack.Start(); err != nil { + t.Fatalf("could not start node: %v", err) + } + + for i, tt := range []struct { + body string + want string + code int + }{ + { + body: `{"query": "{block {number transactions { from { address } to { address } value hash type accessList { address storageKeys } index}}}"}`, + want: `{"data":{"block":{"number":1,"transactions":[{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x64","hash":"0x4f7b8d718145233dcf7f29e34a969c63dd4de8715c054ea2af022b66c4f4633e","type":0,"accessList":[],"index":0},{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x32","hash":"0x9c6c2c045b618fe87add0e49ba3ca00659076ecae00fd51de3ba5d4ccf9dbf40","type":1,"accessList":[{"address":"0x0000000000000000000000000000000000000dad","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000000"]}],"index":1}]}}}`, + code: 200, + }, + } { + resp, err := http.Post(fmt.Sprintf("%s/graphql", stack.HTTPEndpoint()), "application/json", strings.NewReader(tt.body)) + if err != nil { + t.Fatalf("could not post: %v", err) + } + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("could not read from response body: %v", err) + } + if have := string(bodyBytes); have != tt.want { + t.Errorf("testcase %d %s,\nhave:\n%v\nwant:\n%v", i, tt.body, have, tt.want) + } + if tt.code != resp.StatusCode { + t.Errorf("testcase %d %s,\nwrong statuscode, have: %v, want: %v", i, tt.body, resp.StatusCode, tt.code) + } } - expected := "{\"data\":{\"block\":{\"number\":\"0x0\"}}}" - assert.Equal(t, expected, string(bodyBytes)) } // Tests that a graphQL request is not handled successfully when graphql is not enabled on the specified endpoint func TestGraphQLHTTPOnSamePort_GQLRequest_Unsuccessful(t *testing.T) { - stack := createNode(t, false) + stack := createNode(t, false, false) defer stack.Close() if err := stack.Start(); err != nil { t.Fatalf("could not start node: %v", err) } - - // create http request - body := strings.NewReader("{\"query\": \"{block{number}}\",\"variables\": null}") - gqlReq, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s/graphql", "127.0.0.1:9393"), body) - if err != nil { - t.Error("could not issue new http request ", err) - } - gqlReq.Header.Set("Content-Type", "application/json") - // read from response - resp := doHTTPRequest(t, gqlReq) - bodyBytes, err := ioutil.ReadAll(resp.Body) + body := strings.NewReader(`{"query": "{block{number}}","variables": null}`) + resp, err := http.Post(fmt.Sprintf("%s/graphql", stack.HTTPEndpoint()), "application/json", body) if err != nil { - t.Fatalf("could not read from response body: %v", err) + t.Fatalf("could not post: %v", err) } // make sure the request is not handled successfully - assert.Equal(t, 404, resp.StatusCode) - assert.Equal(t, "404 page not found\n", string(bodyBytes)) + assert.Equal(t, http.StatusNotFound, resp.StatusCode) } -func createNode(t *testing.T, gqlEnabled bool) *node.Node { +func createNode(t *testing.T, gqlEnabled bool, txEnabled bool) *node.Node { stack, err := node.New(&node.Config{ HTTPHost: "127.0.0.1", - HTTPPort: 9393, + HTTPPort: 0, WSHost: "127.0.0.1", - WSPort: 9393, + WSPort: 0, }) if err != nil { t.Fatalf("could not create node: %v", err) @@ -103,19 +227,44 @@ func createNode(t *testing.T, gqlEnabled bool) *node.Node { if !gqlEnabled { return stack } - - createGQLService(t, stack, "127.0.0.1:9393") - + if !txEnabled { + createGQLService(t, stack) + } else { + createGQLServiceWithTransactions(t, stack) + } return stack } -func createGQLService(t *testing.T, stack *node.Node, endpoint string) { +func createGQLService(t *testing.T, stack *node.Node) { // create backend - ethBackend, err := eth.New(stack, ð.DefaultConfig) + ethConf := ðconfig.Config{ + Genesis: &genesisT.Genesis{ + Config: params.AllEthashProtocolChanges, + GasLimit: 11500000, + Difficulty: big.NewInt(1048576), + }, + Ethash: ethash.Config{ + PowMode: ethash.ModeFake, + }, + NetworkId: 1337, + TrieCleanCache: 5, + TrieCleanCacheJournal: "triecache", + TrieCleanCacheRejournal: 60 * time.Minute, + TrieDirtyCache: 5, + TrieTimeout: 60 * time.Minute, + SnapshotCache: 5, + } + ethBackend, err := eth.New(stack, ethConf) if err != nil { t.Fatalf("could not create eth backend: %v", err) } - + // Create some blocks and import them + chain, _ := core.GenerateChain(params.AllEthashProtocolChanges, ethBackend.BlockChain().Genesis(), + ethash.NewFaker(), ethBackend.ChainDb(), 10, func(i int, gen *core.BlockGen) {}) + _, err = ethBackend.BlockChain().InsertChain(chain) + if err != nil { + t.Fatalf("could not create import blocks: %v", err) + } // create gql service err = New(stack, ethBackend.APIBackend, []string{}, []string{}) if err != nil { @@ -123,12 +272,86 @@ func createGQLService(t *testing.T, stack *node.Node, endpoint string) { } } -func doHTTPRequest(t *testing.T, req *http.Request) *http.Response { - client := &http.Client{} - resp, err := client.Do(req) +func createGQLServiceWithTransactions(t *testing.T, stack *node.Node) { + // create backend + key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address := crypto.PubkeyToAddress(key.PublicKey) + funds := big.NewInt(1000000000) + dad := common.HexToAddress("0x0000000000000000000000000000000000000dad") + + ethConf := ðconfig.Config{ + Genesis: &genesisT.Genesis{ + Config: params.AllEthashProtocolChanges, + GasLimit: 11500000, + Difficulty: big.NewInt(1048576), + Alloc: genesisT.GenesisAlloc{ + address: {Balance: funds}, + // The address 0xdad sloads 0x00 and 0x01 + dad: { + Code: []byte{ + byte(vm.PC), + byte(vm.PC), + byte(vm.SLOAD), + byte(vm.SLOAD), + }, + Nonce: 0, + Balance: big.NewInt(0), + }, + }, + }, + Ethash: ethash.Config{ + PowMode: ethash.ModeFake, + }, + NetworkId: 1337, + TrieCleanCache: 5, + TrieCleanCacheJournal: "triecache", + TrieCleanCacheRejournal: 60 * time.Minute, + TrieDirtyCache: 5, + TrieTimeout: 60 * time.Minute, + SnapshotCache: 5, + } + + ethBackend, err := eth.New(stack, ethConf) if err != nil { - t.Fatal("could not issue a GET request to the given endpoint", err) + t.Fatalf("could not create eth backend: %v", err) + } + signer := types.LatestSigner(ethConf.Genesis.Config) + legacyTx, _ := types.SignNewTx(key, signer, &types.LegacyTx{ + Nonce: uint64(0), + To: &dad, + Value: big.NewInt(100), + Gas: 50000, + GasPrice: big.NewInt(1), + }) + envelopTx, _ := types.SignNewTx(key, signer, &types.AccessListTx{ + ChainID: ethConf.Genesis.Config.GetChainID(), + Nonce: uint64(1), + To: &dad, + Gas: 30000, + GasPrice: big.NewInt(1), + Value: big.NewInt(50), + AccessList: types.AccessList{{ + Address: dad, + StorageKeys: []common.Hash{{0}}, + }}, + }) + + // Create some blocks and import them + chain, _ := core.GenerateChain(params.AllEthashProtocolChanges, ethBackend.BlockChain().Genesis(), + ethash.NewFaker(), ethBackend.ChainDb(), 1, func(i int, b *core.BlockGen) { + b.SetCoinbase(common.Address{1}) + b.AddTx(legacyTx) + b.AddTx(envelopTx) + }) + + _, err = ethBackend.BlockChain().InsertChain(chain) + if err != nil { + t.Fatalf("could not create import blocks: %v", err) + } + // create gql service + err = New(stack, ethBackend.APIBackend, []string{}, []string{}) + if err != nil { + t.Fatalf("could not create graphql service: %v", err) } - return resp } diff --git a/graphql/schema.go b/graphql/schema.go index d7b253f227..d792bb9153 100644 --- a/graphql/schema.go +++ b/graphql/schema.go @@ -69,6 +69,12 @@ const schema string = ` transaction: Transaction! } + #EIP-2718 + type AccessTuple{ + address: Address! + storageKeys : [Bytes32!] + } + # Transaction is an Ethereum transaction. type Transaction { # Hash is the hash of this transaction. @@ -118,6 +124,9 @@ const schema string = ` r: BigInt! s: BigInt! v: BigInt! + #Envelope transaction support + type: Int + accessList: [AccessTuple!] } # BlockFilterCriteria encapsulates log filter criteria for a filter applied @@ -300,7 +309,7 @@ const schema string = ` block(number: Long, hash: Bytes32): Block # Blocks returns all the blocks between two numbers, inclusive. If # to is not supplied, it defaults to the most recent known block. - blocks(from: Long!, to: Long): [Block!]! + blocks(from: Long, to: Long): [Block!]! # Pending returns the current pending state. pending: Pending! # Transaction returns a transaction specified by its hash. @@ -310,8 +319,6 @@ const schema string = ` # GasPrice returns the node's estimate of a gas price sufficient to # ensure a transaction is mined in a timely fashion. gasPrice: BigInt! - # ProtocolVersion returns the current wire protocol version number. - protocolVersion: Int! # Syncing returns information on the current synchronisation state. syncing: SyncState # ChainID returns the current chain ID for transaction replay protection. diff --git a/graphql/service.go b/graphql/service.go index ae962e5b36..bcb0a4990d 100644 --- a/graphql/service.go +++ b/graphql/service.go @@ -17,12 +17,44 @@ package graphql import ( + "encoding/json" + "net/http" + "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/node" "github.com/graph-gophers/graphql-go" - "github.com/graph-gophers/graphql-go/relay" ) +type handler struct { + Schema *graphql.Schema +} + +func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + var params struct { + Query string `json:"query"` + OperationName string `json:"operationName"` + Variables map[string]interface{} `json:"variables"` + } + if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + response := h.Schema.Exec(r.Context(), params.Query, params.OperationName, params.Variables) + responseJSON, err := json.Marshal(response) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if len(response.Errors) > 0 { + w.WriteHeader(http.StatusBadRequest) + } + + w.Header().Set("Content-Type", "application/json") + w.Write(responseJSON) + +} + // New constructs a new GraphQL service instance. func New(stack *node.Node, backend ethapi.Backend, cors, vhosts []string) error { if backend == nil { @@ -41,7 +73,7 @@ func newHandler(stack *node.Node, backend ethapi.Backend, cors, vhosts []string) if err != nil { return err } - h := &relay.Handler{Schema: s} + h := handler{Schema: s} handler := node.NewHTTPHandlerStack(h, cors, vhosts) stack.RegisterHandler("GraphQL UI", "/graphql/ui", GraphiQL{}) diff --git a/interfaces.go b/interfaces.go index 1ff31f96b6..afcdc17e58 100644 --- a/interfaces.go +++ b/interfaces.go @@ -119,6 +119,8 @@ type CallMsg struct { GasPrice *big.Int // wei <-> gas exchange ratio Value *big.Int // amount of wei sent along with the call Data []byte // input data, usually an ABI-encoded contract method invocation + + AccessList types.AccessList // EIP-2930 access list. } // A ContractCaller provides contract calls, essentially transactions that are executed by diff --git a/internal/cmdtest/test_cmd.go b/internal/cmdtest/test_cmd.go index 0edfccec5a..96f9cf3bd2 100644 --- a/internal/cmdtest/test_cmd.go +++ b/internal/cmdtest/test_cmd.go @@ -27,6 +27,7 @@ import ( "regexp" "strings" "sync" + "sync/atomic" "syscall" "testing" "text/template" @@ -36,16 +37,17 @@ import ( ) func NewTestCmd(t *testing.T, data interface{}) *TestCmd { - return &TestCmd{T: t, Data: data} + return &TestCmd{T: t, Data: data, KillTimeout: 5 * time.Second} } type TestCmd struct { // For total convenience, all testing methods are available. *testing.T - Func template.FuncMap - Data interface{} - Cleanup func() + Func template.FuncMap + Data interface{} + Cleanup func() + KillTimeout time.Duration cmd *exec.Cmd stdout *bufio.Reader @@ -55,10 +57,13 @@ type TestCmd struct { Err error } +var id int32 + // Run exec's the current binary using name as argv[0] which will trigger the // reexec init function for that name (e.g. "geth-test" in cmd/geth/run_test.go) func (tt *TestCmd) Run(name string, args ...string) { - tt.stderr = &testlogger{t: tt.T} + id := atomic.AddInt32(&id, 1) + tt.stderr = &testlogger{t: tt.T, name: fmt.Sprintf("%d", id)} tt.cmd = &exec.Cmd{ Path: reexec.Self(), Args: append([]string{name}, args...), @@ -227,7 +232,7 @@ func (tt *TestCmd) Kill() { } func (tt *TestCmd) withKillTimeout(fn func()) { - timeout := time.AfterFunc(5*time.Second, func() { + timeout := time.AfterFunc(tt.KillTimeout, func() { tt.Log("killing the child process (timeout)") tt.Kill() }) @@ -238,16 +243,17 @@ func (tt *TestCmd) withKillTimeout(fn func()) { // testlogger logs all written lines via t.Log and also // collects them for later inspection. type testlogger struct { - t *testing.T - mu sync.Mutex - buf bytes.Buffer + t *testing.T + mu sync.Mutex + buf bytes.Buffer + name string } func (tl *testlogger) Write(b []byte) (n int, err error) { lines := bytes.Split(b, []byte("\n")) for _, line := range lines { if len(line) > 0 { - tl.t.Logf("(stderr) %s", line) + tl.t.Logf("(stderr:%v) %s", tl.name, line) } } tl.mu.Lock() diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 3b077b6e08..bac5234b14 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -28,7 +28,7 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics/exp" "github.com/fjl/memsize/memsizeui" - colorable "github.com/mattn/go-colorable" + "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "gopkg.in/urfave/cli.v1" ) @@ -46,13 +46,17 @@ var ( Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4)", Value: "", } + logjsonFlag = cli.BoolFlag{ + Name: "log.json", + Usage: "Format logs with JSON", + } backtraceAtFlag = cli.StringFlag{ - Name: "backtrace", + Name: "log.backtrace", Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")", Value: "", } debugFlag = cli.BoolFlag{ - Name: "debug", + Name: "log.debug", Usage: "Prepends log messages with call-site location (file and line number)", } pprofFlag = cli.BoolFlag{ @@ -110,57 +114,116 @@ var ( Name: "cpuprofile", Usage: "Write CPU profile to the given file (deprecated, use --pprof.cpuprofile)", } + legacyBacktraceAtFlag = cli.StringFlag{ + Name: "backtrace", + Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\") (deprecated, use --log.backtrace)", + Value: "", + } + legacyDebugFlag = cli.BoolFlag{ + Name: "debug", + Usage: "Prepends log messages with call-site location (file and line number) (deprecated, use --log.debug)", + } ) // Flags holds all command-line flags required for debugging. var Flags = []cli.Flag{ - verbosityFlag, vmoduleFlag, backtraceAtFlag, debugFlag, - pprofFlag, pprofAddrFlag, pprofPortFlag, memprofilerateFlag, - blockprofilerateFlag, cpuprofileFlag, traceFlag, + verbosityFlag, + vmoduleFlag, + logjsonFlag, + backtraceAtFlag, + debugFlag, + pprofFlag, + pprofAddrFlag, + pprofPortFlag, + memprofilerateFlag, + blockprofilerateFlag, + cpuprofileFlag, + traceFlag, } +// This is the list of deprecated debugging flags. var DeprecatedFlags = []cli.Flag{ - legacyPprofPortFlag, legacyPprofAddrFlag, legacyMemprofilerateFlag, - legacyBlockprofilerateFlag, legacyCpuprofileFlag, + legacyPprofPortFlag, + legacyPprofAddrFlag, + legacyMemprofilerateFlag, + legacyBlockprofilerateFlag, + legacyCpuprofileFlag, + legacyBacktraceAtFlag, + legacyDebugFlag, } -var ( - ostream log.Handler - glogger *log.GlogHandler -) +var glogger *log.GlogHandler func init() { - usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" - output := io.Writer(os.Stderr) - if usecolor { - output = colorable.NewColorableStderr() - } - ostream = log.StreamHandler(output, log.TerminalFormat(usecolor)) - glogger = log.NewGlogHandler(ostream) + Flags = append(Flags, DeprecatedFlags...) + glogger = log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) + glogger.Verbosity(log.LvlInfo) + log.Root().SetHandler(glogger) } // Setup initializes profiling and logging based on the CLI flags. // It should be called as early as possible in the program. func Setup(ctx *cli.Context) error { + var ostream log.Handler + output := io.Writer(os.Stderr) + if ctx.GlobalBool(logjsonFlag.Name) { + ostream = log.StreamHandler(output, log.JSONFormat()) + } else { + usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" + if usecolor { + output = colorable.NewColorableStderr() + } + ostream = log.StreamHandler(output, log.TerminalFormat(usecolor)) + } + glogger.SetHandler(ostream) + // logging - log.PrintOrigins(ctx.GlobalBool(debugFlag.Name)) - glogger.Verbosity(log.Lvl(ctx.GlobalInt(verbosityFlag.Name))) - glogger.Vmodule(ctx.GlobalString(vmoduleFlag.Name)) - glogger.BacktraceAt(ctx.GlobalString(backtraceAtFlag.Name)) + verbosity := ctx.GlobalInt(verbosityFlag.Name) + glogger.Verbosity(log.Lvl(verbosity)) + vmodule := ctx.GlobalString(vmoduleFlag.Name) + glogger.Vmodule(vmodule) + + debug := ctx.GlobalBool(debugFlag.Name) + if ctx.GlobalIsSet(legacyDebugFlag.Name) { + debug = ctx.GlobalBool(legacyDebugFlag.Name) + log.Warn("The flag --debug is deprecated and will be removed in the future, please use --log.debug") + } + if ctx.GlobalIsSet(debugFlag.Name) { + debug = ctx.GlobalBool(debugFlag.Name) + } + log.PrintOrigins(debug) + + backtrace := ctx.GlobalString(backtraceAtFlag.Name) + if b := ctx.GlobalString(legacyBacktraceAtFlag.Name); b != "" { + backtrace = b + log.Warn("The flag --backtrace is deprecated and will be removed in the future, please use --log.backtrace") + } + if b := ctx.GlobalString(backtraceAtFlag.Name); b != "" { + backtrace = b + } + glogger.BacktraceAt(backtrace) + log.Root().SetHandler(glogger) // profiling, tracing + runtime.MemProfileRate = memprofilerateFlag.Value if ctx.GlobalIsSet(legacyMemprofilerateFlag.Name) { runtime.MemProfileRate = ctx.GlobalInt(legacyMemprofilerateFlag.Name) log.Warn("The flag --memprofilerate is deprecated and will be removed in the future, please use --pprof.memprofilerate") } - runtime.MemProfileRate = ctx.GlobalInt(memprofilerateFlag.Name) + if ctx.GlobalIsSet(memprofilerateFlag.Name) { + runtime.MemProfileRate = ctx.GlobalInt(memprofilerateFlag.Name) + } + blockProfileRate := blockprofilerateFlag.Value if ctx.GlobalIsSet(legacyBlockprofilerateFlag.Name) { - Handler.SetBlockProfileRate(ctx.GlobalInt(legacyBlockprofilerateFlag.Name)) + blockProfileRate = ctx.GlobalInt(legacyBlockprofilerateFlag.Name) log.Warn("The flag --blockprofilerate is deprecated and will be removed in the future, please use --pprof.blockprofilerate") } - Handler.SetBlockProfileRate(ctx.GlobalInt(blockprofilerateFlag.Name)) + if ctx.GlobalIsSet(blockprofilerateFlag.Name) { + blockProfileRate = ctx.GlobalInt(blockprofilerateFlag.Name) + } + Handler.SetBlockProfileRate(blockProfileRate) if traceFile := ctx.GlobalString(traceFlag.Name); traceFile != "" { if err := Handler.StartGoTrace(traceFile); err != nil { @@ -172,10 +235,9 @@ func Setup(ctx *cli.Context) error { if err := Handler.StartCPUProfile(cpuFile); err != nil { return err } - } - if cpuFile := ctx.GlobalString(legacyCpuprofileFlag.Name); cpuFile != "" { + } else if legacyCPUFile := ctx.GlobalString(legacyCpuprofileFlag.Name); legacyCPUFile != "" { log.Warn("The flag --cpuprofile is deprecated and will be removed in the future, please use --pprof.cpuprofile") - if err := Handler.StartCPUProfile(cpuFile); err != nil { + if err := Handler.StartCPUProfile(legacyCPUFile); err != nil { return err } } @@ -183,13 +245,13 @@ func Setup(ctx *cli.Context) error { // pprof server if ctx.GlobalBool(pprofFlag.Name) { listenHost := ctx.GlobalString(pprofAddrFlag.Name) - if ctx.GlobalIsSet(legacyPprofAddrFlag.Name) && !ctx.GlobalIsSet(pprofAddrFlag.Name) { + if ctx.GlobalIsSet(legacyPprofAddrFlag.Name) { listenHost = ctx.GlobalString(legacyPprofAddrFlag.Name) log.Warn("The flag --pprofaddr is deprecated and will be removed in the future, please use --pprof.addr") } port := ctx.GlobalInt(pprofPortFlag.Name) - if ctx.GlobalIsSet(legacyPprofPortFlag.Name) && !ctx.GlobalIsSet(pprofPortFlag.Name) { + if ctx.GlobalIsSet(legacyPprofPortFlag.Name) { port = ctx.GlobalInt(legacyPprofPortFlag.Name) log.Warn("The flag --pprofport is deprecated and will be removed in the future, please use --pprof.port") } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 1277dc4485..1461316c2d 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -65,11 +65,6 @@ func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) return (*hexutil.Big)(price), err } -// ProtocolVersion returns the current Ethereum protocol version this node supports -func (s *PublicEthereumAPI) ProtocolVersion() hexutil.Uint { - return hexutil.Uint(s.b.ProtocolVersion()) -} - // Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not // yet received the latest block headers from its pears. In case it is synchronizing: // - startingBlock: block number this node started to synchronise from @@ -374,7 +369,7 @@ func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args *SendTxArg } // SendTransaction will create a transaction from the given arguments and -// tries to sign it with the key associated with args.To. If the given passwd isn't +// tries to sign it with the key associated with args.From. If the given passwd isn't // able to decrypt the key it fails. func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) { if args.Nonce == nil { @@ -392,7 +387,7 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs } // SignTransaction will create a transaction from the given arguments and -// tries to sign it with the key associated with args.To. If the given passwd isn't +// tries to sign it with the key associated with args.From. If the given passwd isn't // able to decrypt the key it fails. The transaction is returned in RLP-form, not broadcast // to other nodes func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args SendTxArgs, passwd string) (*SignTransactionResult, error) { @@ -417,7 +412,7 @@ func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args SendTxArgs log.Warn("Failed transaction sign attempt", "from", args.From, "to", args.To, "value", args.Value.ToInt(), "err", err) return nil, err } - data, err := rlp.EncodeToBytes(signed) + data, err := signed.MarshalBinary() if err != nil { return nil, err } @@ -537,8 +532,12 @@ func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI { } // ChainId returns the chainID value for transaction replay protection. -func (s *PublicBlockChainAPI) ChainId() *hexutil.Big { - return (*hexutil.Big)(s.b.ChainConfig().GetChainID()) +func (api *PublicBlockChainAPI) ChainId() (*hexutil.Big, error) { + // if current block is at or past the EIP-155 replay-protection fork block, return chainID from config + if config := api.b.ChainConfig(); config.IsEnabled(config.GetEIP155Transition, api.b.CurrentBlock().Number()) { + return (*hexutil.Big)(api.b.ChainConfig().GetChainID()), nil + } + return nil, fmt.Errorf("chain not synced beyond EIP-155 replay-protection fork block") } // BlockNumber returns the block number of the chain head. @@ -751,12 +750,13 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A // CallArgs represents the arguments for a call. type CallArgs struct { - From *common.Address `json:"from"` - To *common.Address `json:"to"` - Gas *hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - Value *hexutil.Big `json:"value"` - Data *hexutil.Bytes `json:"data"` + From *common.Address `json:"from"` + To *common.Address `json:"to"` + Gas *hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + Value *hexutil.Big `json:"value"` + Data *hexutil.Bytes `json:"data"` + AccessList *types.AccessList `json:"accessList"` } // ToMessage converts CallArgs to the Message type used by the core evm @@ -783,18 +783,20 @@ func (args *CallArgs) ToMessage(globalGasCap uint64) types.Message { if args.GasPrice != nil { gasPrice = args.GasPrice.ToInt() } - value := new(big.Int) if args.Value != nil { value = args.Value.ToInt() } - var data []byte if args.Data != nil { - data = []byte(*args.Data) + data = *args.Data + } + var accessList types.AccessList + if args.AccessList != nil { + accessList = *args.AccessList } - msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false) + msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, accessList, false) return msg } @@ -861,7 +863,7 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo // Get a new instance of the EVM. msg := args.ToMessage(globalGasCap) - evm, vmError, err := b.GetEVM(ctx, msg, state, header) + evm, vmError, err := b.GetEVM(ctx, msg, state, header, nil) if err != nil { return nil, err } @@ -872,13 +874,13 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo evm.Cancel() }() - // Setup the gas pool (also for unmetered requests) - // and apply the message. + // Execute the message. gp := new(core.GasPool).AddGas(math.MaxUint64) result, err := core.ApplyMessage(evm, msg, gp) if err := vmError(); err != nil { return nil, err } + // If the timer caused an abort, return an appropriate error message if evm.Cancelled() { return nil, fmt.Errorf("execution aborted (timeout = %v)", timeout) @@ -1335,33 +1337,43 @@ func (s *PublicBlockChainAPI) rpcMarshalBlock(ctx context.Context, b *types.Bloc // RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction type RPCTransaction struct { - BlockHash *common.Hash `json:"blockHash"` - BlockNumber *hexutil.Big `json:"blockNumber"` - From common.Address `json:"from"` - Gas hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - Hash common.Hash `json:"hash"` - Input hexutil.Bytes `json:"input"` - Nonce hexutil.Uint64 `json:"nonce"` - To *common.Address `json:"to"` - TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` - Value *hexutil.Big `json:"value"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` + BlockHash *common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + From common.Address `json:"from"` + Gas hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + Hash common.Hash `json:"hash"` + Input hexutil.Bytes `json:"input"` + Nonce hexutil.Uint64 `json:"nonce"` + To *common.Address `json:"to"` + TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` + Value *hexutil.Big `json:"value"` + Type hexutil.Uint64 `json:"type"` + Accesses *types.AccessList `json:"accessList,omitempty"` + ChainID *hexutil.Big `json:"chainId,omitempty"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` } // newRPCTransaction returns a transaction that will serialize to the RPC // representation, with the given location metadata set (if available). func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransaction { - var signer types.Signer = types.FrontierSigner{} + // Determine the signer. For replay-protected transactions, use the most permissive + // signer, because we assume that signers are backwards-compatible with old + // transactions. For non-protected transactions, the homestead signer signer is used + // because the return value of ChainId is zero for those transactions. + var signer types.Signer if tx.Protected() { - signer = types.NewEIP155Signer(tx.ChainId()) + signer = types.LatestSignerForChainID(tx.ChainId()) + } else { + signer = types.HomesteadSigner{} } + from, _ := types.Sender(signer, tx) v, r, s := tx.RawSignatureValues() - result := &RPCTransaction{ + Type: hexutil.Uint64(tx.Type()), From: from, Gas: hexutil.Uint64(tx.Gas()), GasPrice: (*hexutil.Big)(tx.GasPrice()), @@ -1379,6 +1391,11 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) result.TransactionIndex = (*hexutil.Uint64)(&index) } + if tx.Type() == types.AccessListTxType { + al := tx.AccessList() + result.Accesses = &al + result.ChainID = (*hexutil.Big)(tx.ChainId()) + } return result } @@ -1402,7 +1419,7 @@ func newRPCRawTransactionFromBlockIndex(b *types.Block, index uint64) hexutil.By if index >= uint64(len(txs)) { return nil } - blob, _ := rlp.EncodeToBytes(txs[index]) + blob, _ := txs[index].MarshalBinary() return blob } @@ -1416,15 +1433,123 @@ func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransa return nil } +// accessListResult returns an optional accesslist +// Its the result of the `debug_createAccessList` RPC call. +// It contains an error if the transaction itself failed. +type accessListResult struct { + Accesslist *types.AccessList `json:"accessList"` + Error string `json:"error,omitempty"` + GasUsed hexutil.Uint64 `json:"gasUsed"` +} + +// CreateAccessList creates a EIP-2930 type AccessList for the given transaction. +// Reexec and BlockNrOrHash can be specified to create the accessList on top of a certain state. +func (s *PublicBlockChainAPI) CreateAccessList(ctx context.Context, args SendTxArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*accessListResult, error) { + bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) + if blockNrOrHash != nil { + bNrOrHash = *blockNrOrHash + } + acl, gasUsed, vmerr, err := AccessList(ctx, s.b, bNrOrHash, args) + if err != nil { + return nil, err + } + result := &accessListResult{Accesslist: &acl, GasUsed: hexutil.Uint64(gasUsed)} + if vmerr != nil { + result.Error = vmerr.Error() + } + return result, nil +} + +// AccessList creates an access list for the given transaction. +// If the accesslist creation fails an error is returned. +// If the transaction itself fails, an vmErr is returned. +func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrHash, args SendTxArgs) (acl types.AccessList, gasUsed uint64, vmErr error, err error) { + // Retrieve the execution context + db, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + if db == nil || err != nil { + return nil, 0, nil, err + } + // If the gas amount is not set, extract this as it will depend on access + // lists and we'll need to reestimate every time + nogas := args.Gas == nil + + // Ensure any missing fields are filled, extract the recipient and input data + if err := args.setDefaults(ctx, b); err != nil { + return nil, 0, nil, err + } + var to common.Address + if args.To != nil { + to = *args.To + } else { + to = crypto.CreateAddress(args.From, uint64(*args.Nonce)) + } + var input []byte + if args.Input != nil { + input = *args.Input + } else if args.Data != nil { + input = *args.Data + } + // Retrieve the precompiles since they don't need to be added to the access list + precompileMap := vm.PrecompiledContractsForConfig(b.ChainConfig(), header.Number) + precompiles := make([]common.Address, len(precompileMap)) + for k := range precompileMap { + precompiles = append(precompiles, k) + } + + // Create an initial tracer + prevTracer := vm.NewAccessListTracer(nil, args.From, to, precompiles) + if args.AccessList != nil { + prevTracer = vm.NewAccessListTracer(*args.AccessList, args.From, to, precompiles) + } + for { + // Retrieve the current access list to expand + accessList := prevTracer.AccessList() + log.Trace("Creating access list", "input", accessList) + + // If no gas amount was specified, each unique access list needs it's own + // gas calculation. This is quite expensive, but we need to be accurate + // and it's convered by the sender only anyway. + if nogas { + args.Gas = nil + if err := args.setDefaults(ctx, b); err != nil { + return nil, 0, nil, err // shouldn't happen, just in case + } + } + // Copy the original db so we don't modify it + statedb := db.Copy() + msg := types.NewMessage(args.From, args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), input, accessList, false) + + // Apply the transaction with the access list tracer + tracer := vm.NewAccessListTracer(accessList, args.From, to, precompiles) + config := vm.Config{Tracer: tracer, Debug: true} + vmenv, _, err := b.GetEVM(ctx, msg, statedb, header, &config) + if err != nil { + return nil, 0, nil, err + } + res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) + if err != nil { + return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err) + } + if tracer.Equal(prevTracer) { + return accessList, res.UsedGas, res.Err, nil + } + prevTracer = tracer + } +} + // PublicTransactionPoolAPI exposes methods for the RPC interface type PublicTransactionPoolAPI struct { b Backend nonceLock *AddrLocker + signer types.Signer } // NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool. func NewPublicTransactionPoolAPI(b Backend, nonceLock *AddrLocker) *PublicTransactionPoolAPI { - return &PublicTransactionPoolAPI{b, nonceLock} + // The signer used by the API should always be the 'latest' known one because we expect + // signers to be backwards-compatible with old transactions. + signer := types.LatestSigner(b.ChainConfig()) + return &PublicTransactionPoolAPI{b, nonceLock, signer} } // GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. @@ -1529,7 +1654,7 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, } } // Serialize to RLP and return - return rlp.EncodeToBytes(tx) + return tx.MarshalBinary() } // GetTransactionReceipt returns the transaction receipt for the given transaction hash. @@ -1547,10 +1672,9 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha } receipt := receipts[index] - var signer types.Signer = types.FrontierSigner{} - if tx.Protected() { - signer = types.NewEIP155Signer(tx.ChainId()) - } + // Derive the sender. + bigblock := new(big.Int).SetUint64(blockNumber) + signer := types.MakeSigner(s.b.ChainConfig(), bigblock) from, _ := types.Sender(signer, tx) fields := map[string]interface{}{ @@ -1565,6 +1689,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha "contractAddress": nil, "logs": receipt.Logs, "logsBloom": receipt.Bloom, + "type": hexutil.Uint(tx.Type()), } // Assign receipt status or post state. @@ -1608,9 +1733,13 @@ type SendTxArgs struct { // newer name and should be preferred by clients. Data *hexutil.Bytes `json:"data"` Input *hexutil.Bytes `json:"input"` + + // For non-legacy transactions + AccessList *types.AccessList `json:"accessList,omitempty"` + ChainID *hexutil.Big `json:"chainId,omitempty"` } -// setDefaults is a helper function that fills in default values for unspecified tx fields. +// setDefaults fills in default values for unspecified tx fields. func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { if args.GasPrice == nil { price, err := b.SuggestPrice(ctx) @@ -1653,11 +1782,12 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { input = args.Data } callArgs := CallArgs{ - From: &args.From, // From shouldn't be nil - To: args.To, - GasPrice: args.GasPrice, - Value: args.Value, - Data: input, + From: &args.From, // From shouldn't be nil + To: args.To, + GasPrice: args.GasPrice, + Value: args.Value, + Data: input, + AccessList: args.AccessList, } pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, b.RPCGasCap()) @@ -1667,9 +1797,15 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { args.Gas = &estimated log.Trace("Estimate gas usage automatically", "gas", args.Gas) } + if args.ChainID == nil { + id := (*hexutil.Big)(b.ChainConfig().GetChainID()) + args.ChainID = id + } return nil } +// toTransaction converts the arguments to a transaction. +// This assumes that setDefaults has been called. func (args *SendTxArgs) toTransaction() *types.Transaction { var input []byte if args.Input != nil { @@ -1677,10 +1813,29 @@ func (args *SendTxArgs) toTransaction() *types.Transaction { } else if args.Data != nil { input = *args.Data } - if args.To == nil { - return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input) + var data types.TxData + if args.AccessList == nil { + data = &types.LegacyTx{ + To: args.To, + Nonce: uint64(*args.Nonce), + Gas: uint64(*args.Gas), + GasPrice: (*big.Int)(args.GasPrice), + Value: (*big.Int)(args.Value), + Data: input, + } + } else { + data = &types.AccessListTx{ + To: args.To, + ChainID: (*big.Int)(args.ChainID), + Nonce: uint64(*args.Nonce), + Gas: uint64(*args.Gas), + GasPrice: (*big.Int)(args.GasPrice), + Value: (*big.Int)(args.Value), + Data: input, + AccessList: *args.AccessList, + } } - return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input) + return types.NewTx(data) } // SubmitTransaction is a helper function that submits tx to txPool and logs a message. @@ -1690,19 +1845,25 @@ func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c if err := checkTxFee(tx.GasPrice(), tx.Gas(), b.RPCTxFeeCap()); err != nil { return common.Hash{}, err } + if !b.UnprotectedAllowed() && !tx.Protected() { + // Ensure only eip155 signed transactions are submitted if EIP155Required is set. + return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC") + } if err := b.SendTx(ctx, tx); err != nil { return common.Hash{}, err } + // Print a log with full tx details for manual investigations and interventions + signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number()) + from, err := types.Sender(signer, tx) + if err != nil { + return common.Hash{}, err + } + if tx.To() == nil { - signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number()) - from, err := types.Sender(signer, tx) - if err != nil { - return common.Hash{}, err - } addr := crypto.CreateAddress(from, tx.Nonce()) - log.Info("Submitted contract creation", "fullhash", tx.Hash().Hex(), "contract", addr.Hex()) + log.Info("Submitted contract creation", "hash", tx.Hash().Hex(), "from", from, "nonce", tx.Nonce(), "contract", addr.Hex(), "value", tx.Value()) } else { - log.Info("Submitted transaction", "fullhash", tx.Hash().Hex(), "recipient", tx.To()) + log.Info("Submitted transaction", "hash", tx.Hash().Hex(), "from", from, "nonce", tx.Nonce(), "recipient", tx.To(), "value", tx.Value()) } return tx.Hash(), nil } @@ -1748,7 +1909,7 @@ func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args Sen } // Assemble the transaction and obtain rlp tx := args.toTransaction() - data, err := rlp.EncodeToBytes(tx) + data, err := tx.MarshalBinary() if err != nil { return nil, err } @@ -1757,9 +1918,9 @@ func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args Sen // SendRawTransaction will add the signed transaction to the transaction pool. // The sender is responsible for signing the transaction and using the correct nonce. -func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) { +func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, input hexutil.Bytes) (common.Hash, error) { tx := new(types.Transaction) - if err := rlp.DecodeBytes(encodedTx, tx); err != nil { + if err := tx.UnmarshalBinary(input); err != nil { return common.Hash{}, err } return SubmitTransaction(ctx, s.b, tx) @@ -1820,7 +1981,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sen if err != nil { return nil, err } - data, err := rlp.EncodeToBytes(tx) + data, err := tx.MarshalBinary() if err != nil { return nil, err } @@ -1842,11 +2003,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, err } transactions := make([]*RPCTransaction, 0, len(pending)) for _, tx := range pending { - var signer types.Signer = types.HomesteadSigner{} - if tx.Protected() { - signer = types.NewEIP155Signer(tx.ChainId()) - } - from, _ := types.Sender(signer, tx) + from, _ := types.Sender(s.signer, tx) if _, exists := accounts[from]; exists { transactions = append(transactions, newRPCPendingTransaction(tx)) } @@ -1883,13 +2040,9 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr return common.Hash{}, err } for _, p := range pending { - var signer types.Signer = types.HomesteadSigner{} - if p.Protected() { - signer = types.NewEIP155Signer(p.ChainId()) - } - wantSigHash := signer.Hash(matchTx) - - if pFrom, err := types.Sender(signer, p); err == nil && pFrom == sendArgs.From && signer.Hash(p) == wantSigHash { + wantSigHash := s.signer.Hash(matchTx) + pFrom, err := types.Sender(s.signer, p) + if err == nil && pFrom == sendArgs.From && s.signer.Hash(p) == wantSigHash { // Match. Re-sign and send the transaction. if gasPrice != nil && (*big.Int)(gasPrice).Sign() != 0 { sendArgs.GasPrice = gasPrice @@ -1907,7 +2060,6 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr return signedTx.Hash(), nil } } - return common.Hash{}, fmt.Errorf("transaction %#x not found", matchTx.Hash()) } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index b32fc3a013..e7228d2d7b 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -41,13 +41,13 @@ import ( type Backend interface { // General Ethereum API Downloader() *downloader.Downloader - ProtocolVersion() int SuggestPrice(ctx context.Context) (*big.Int, error) ChainDb() ethdb.Database AccountManager() *accounts.Manager ExtRPCEnabled() bool - RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection - RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs + RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection + RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs + UnprotectedAllowed() bool // allows only for EIP155 transactions. // Blockchain API SetHead(number uint64) @@ -63,7 +63,7 @@ type Backend interface { StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) GetTd(ctx context.Context, hash common.Hash) *big.Int - GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) + GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go index 900fec454c..43bbcf0201 100644 --- a/internal/flags/helpers.go +++ b/internal/flags/helpers.go @@ -21,11 +21,11 @@ import ( "path/filepath" "github.com/ethereum/go-ethereum/params" - cli "gopkg.in/urfave/cli.v1" + "gopkg.in/urfave/cli.v1" ) var ( - CommandHelpTemplate = `{{.cmd.Name}}{{if .cmd.Subcommands}} command{{end}}{{if .cmd.Flags}} [command options]{{end}} [arguments...] + CommandHelpTemplate = `{{.cmd.Name}}{{if .cmd.Subcommands}} command{{end}}{{if .cmd.Flags}} [command options]{{end}} {{.cmd.ArgsUsage}} {{if .cmd.Description}}{{.cmd.Description}} {{end}}{{if .cmd.Subcommands}} SUBCOMMANDS: @@ -36,7 +36,7 @@ SUBCOMMANDS: {{end}} {{end}}{{end}}` - OriginCommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...] + OriginCommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} {{.ArgsUsage}} {{if .Description}}{{.Description}} {{end}}{{if .Subcommands}} SUBCOMMANDS: @@ -51,10 +51,10 @@ OPTIONS: AppHelpTemplate = `NAME: {{.App.Name}} - {{.App.Usage}} - Copyright 2013-2019 The go-ethereum Authors + Copyright 2013-2021 The go-ethereum Authors USAGE: - {{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}} + {{.App.HelpName}} [options]{{if .App.Commands}} [command] [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}} {{if .App.Version}} VERSION: {{.App.Version}} @@ -77,7 +77,7 @@ COPYRIGHT: ClefAppHelpTemplate = `NAME: {{.App.Name}} - {{.App.Usage}} - Copyright 2013-2019 The go-ethereum Authors + Copyright 2013-2021 The go-ethereum Authors USAGE: {{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}} diff --git a/internal/guide/guide_test.go b/internal/guide/guide_test.go index 9c7ad16d18..abc48e0e4b 100644 --- a/internal/guide/guide_test.go +++ b/internal/guide/guide_test.go @@ -31,6 +31,7 @@ import ( "time" "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" ) @@ -75,7 +76,8 @@ func TestAccountManagement(t *testing.T) { if err != nil { t.Fatalf("Failed to create signer account: %v", err) } - tx, chain := new(types.Transaction), big.NewInt(1) + tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 0, big.NewInt(0), nil) + chain := big.NewInt(1) // Sign a transaction with a single authorization if _, err := ks.SignTxWithPassphrase(signer, "Signer password", tx, chain); err != nil { diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 355ccb5ca7..8623091676 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -34,7 +34,7 @@ var Modules = map[string]string{ "trace": TraceJs, "txpool": TxpoolJs, "les": LESJs, - "lespay": LESPayJs, + "vflux": VfluxJs, } const ChequebookJs = ` @@ -139,8 +139,8 @@ web3._extend({ params: 3, }), new web3._extend.Method({ - name: 'submitHashRate', - call: 'ethash_submitHashRate', + name: 'submitHashrate', + call: 'ethash_submitHashrate', params: 2, }), ] @@ -197,12 +197,24 @@ web3._extend({ call: 'admin_sleepBlocks', params: 2 }), + new web3._extend.Method({ + name: 'startHTTP', + call: 'admin_startHTTP', + params: 5, + inputFormatter: [null, null, null, null, null] + }), + new web3._extend.Method({ + name: 'stopHTTP', + call: 'admin_stopHTTP' + }), + // This method is deprecated. new web3._extend.Method({ name: 'startRPC', call: 'admin_startRPC', - params: 4, - inputFormatter: [null, null, null, null] + params: 5, + inputFormatter: [null, null, null, null, null] }), + // This method is deprecated. new web3._extend.Method({ name: 'stopRPC', call: 'admin_stopRPC' @@ -579,6 +591,12 @@ web3._extend({ params: 3, inputFormatter: [web3._extend.formatters.inputAddressFormatter, null, web3._extend.formatters.inputBlockNumberFormatter] }), + new web3._extend.Method({ + name: 'createAccessList', + call: 'eth_createAccessList', + params: 2, + inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter], + }), ], properties: [ new web3._extend.Property({ @@ -942,24 +960,24 @@ web3._extend({ }); ` -const LESPayJs = ` +const VfluxJs = ` web3._extend({ - property: 'lespay', + property: 'vflux', methods: [ new web3._extend.Method({ name: 'distribution', - call: 'lespay_distribution', + call: 'vflux_distribution', params: 2 }), new web3._extend.Method({ name: 'timeout', - call: 'lespay_timeout', + call: 'vflux_timeout', params: 2 }), new web3._extend.Method({ name: 'value', - call: 'lespay_value', + call: 'vflux_value', params: 2 }), ], @@ -967,7 +985,7 @@ web3._extend({ [ new web3._extend.Property({ name: 'requestStats', - getter: 'lespay_requestStats' + getter: 'vflux_requestStats' }), ] }); diff --git a/les/api.go b/les/api.go index 66d133b854..782bb31ef2 100644 --- a/les/api.go +++ b/les/api.go @@ -23,7 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/mclock" - lps "github.com/ethereum/go-ethereum/les/lespay/server" + vfs "github.com/ethereum/go-ethereum/les/vflux/server" "github.com/ethereum/go-ethereum/p2p/enode" ) @@ -31,21 +31,32 @@ var ( errNoCheckpoint = errors.New("no local checkpoint provided") errNotActivated = errors.New("checkpoint registrar is not activated") errUnknownBenchmarkType = errors.New("unknown benchmark type") - errNoPriority = errors.New("priority too low to raise capacity") ) // PrivateLightServerAPI provides an API to access the LES light server. type PrivateLightServerAPI struct { server *LesServer - defaultPosFactors, defaultNegFactors lps.PriceFactors + defaultPosFactors, defaultNegFactors vfs.PriceFactors } // NewPrivateLightServerAPI creates a new LES light server API. func NewPrivateLightServerAPI(server *LesServer) *PrivateLightServerAPI { return &PrivateLightServerAPI{ server: server, - defaultPosFactors: server.clientPool.defaultPosFactors, - defaultNegFactors: server.clientPool.defaultNegFactors, + defaultPosFactors: defaultPosFactors, + defaultNegFactors: defaultNegFactors, + } +} + +// parseNode parses either an enode address a raw hex node id +func parseNode(node string) (enode.ID, error) { + if id, err := enode.ParseID(node); err == nil { + return id, nil + } + if node, err := enode.Parse(enode.ValidSchemes, node); err == nil { + return node.ID(), nil + } else { + return enode.ID{}, err } } @@ -54,16 +65,34 @@ func (api *PrivateLightServerAPI) ServerInfo() map[string]interface{} { res := make(map[string]interface{}) res["minimumCapacity"] = api.server.minCapacity res["maximumCapacity"] = api.server.maxCapacity - res["totalCapacity"], res["totalConnectedCapacity"], res["priorityConnectedCapacity"] = api.server.clientPool.capacityInfo() + _, res["totalCapacity"] = api.server.clientPool.Limits() + _, res["totalConnectedCapacity"] = api.server.clientPool.Active() + res["priorityConnectedCapacity"] = 0 //TODO connect when token sale module is added return res } // ClientInfo returns information about clients listed in the ids list or matching the given tags -func (api *PrivateLightServerAPI) ClientInfo(ids []enode.ID) map[enode.ID]map[string]interface{} { +func (api *PrivateLightServerAPI) ClientInfo(nodes []string) map[enode.ID]map[string]interface{} { + var ids []enode.ID + for _, node := range nodes { + if id, err := parseNode(node); err == nil { + ids = append(ids, id) + } + } + res := make(map[enode.ID]map[string]interface{}) - api.server.clientPool.forClients(ids, func(client *clientInfo) { - res[client.node.ID()] = api.clientInfo(client) - }) + if len(ids) == 0 { + ids = api.server.peers.ids() + } + for _, id := range ids { + if peer := api.server.peers.peer(id); peer != nil { + res[id] = api.clientInfo(peer, peer.balance) + } else { + api.server.clientPool.BalanceOperation(id, "", func(balance vfs.AtomicBalanceOperator) { + res[id] = api.clientInfo(nil, balance) + }) + } + } return res } @@ -75,31 +104,35 @@ func (api *PrivateLightServerAPI) ClientInfo(ids []enode.ID) map[enode.ID]map[st // assigned to it. func (api *PrivateLightServerAPI) PriorityClientInfo(start, stop enode.ID, maxCount int) map[enode.ID]map[string]interface{} { res := make(map[enode.ID]map[string]interface{}) - ids := api.server.clientPool.bt.GetPosBalanceIDs(start, stop, maxCount+1) + ids := api.server.clientPool.GetPosBalanceIDs(start, stop, maxCount+1) if len(ids) > maxCount { res[ids[maxCount]] = make(map[string]interface{}) ids = ids[:maxCount] } - if len(ids) != 0 { - api.server.clientPool.forClients(ids, func(client *clientInfo) { - res[client.node.ID()] = api.clientInfo(client) - }) + for _, id := range ids { + if peer := api.server.peers.peer(id); peer != nil { + res[id] = api.clientInfo(peer, peer.balance) + } else { + api.server.clientPool.BalanceOperation(id, "", func(balance vfs.AtomicBalanceOperator) { + res[id] = api.clientInfo(nil, balance) + }) + } } return res } // clientInfo creates a client info data structure -func (api *PrivateLightServerAPI) clientInfo(c *clientInfo) map[string]interface{} { +func (api *PrivateLightServerAPI) clientInfo(peer *clientPeer, balance vfs.ReadOnlyBalance) map[string]interface{} { info := make(map[string]interface{}) - pb, nb := c.balance.GetBalance() - info["isConnected"] = c.connected + pb, nb := balance.GetBalance() + info["isConnected"] = peer != nil info["pricing/balance"] = pb info["priority"] = pb != 0 // cb := api.server.clientPool.ndb.getCurrencyBalance(id) // info["pricing/currency"] = cb.amount - if c.connected { - info["connectionTime"] = float64(mclock.Now()-c.connectedAt) / float64(time.Second) - info["capacity"], _ = api.server.clientPool.ns.GetField(c.node, priorityPoolSetup.CapacityField).(uint64) + if peer != nil { + info["connectionTime"] = float64(mclock.Now()-peer.connectedAt) / float64(time.Second) + info["capacity"] = peer.getCapacity() info["pricing/negBalance"] = nb } return info @@ -107,7 +140,7 @@ func (api *PrivateLightServerAPI) clientInfo(c *clientInfo) map[string]interface // setParams either sets the given parameters for a single connected client (if specified) // or the default parameters applicable to clients connected in the future -func (api *PrivateLightServerAPI) setParams(params map[string]interface{}, client *clientInfo, posFactors, negFactors *lps.PriceFactors) (updateFactors bool, err error) { +func (api *PrivateLightServerAPI) setParams(params map[string]interface{}, client *clientPeer, posFactors, negFactors *vfs.PriceFactors) (updateFactors bool, err error) { defParams := client == nil for name, value := range params { errValue := func() error { @@ -137,9 +170,8 @@ func (api *PrivateLightServerAPI) setParams(params map[string]interface{}, clien setFactor(&negFactors.RequestFactor) case !defParams && name == "capacity": if capacity, ok := value.(float64); ok && uint64(capacity) >= api.server.minCapacity { - _, err = api.server.clientPool.setCapacity(client.node, client.address, uint64(capacity), 0, true) - // Don't have to call factor update explicitly. It's already done - // in setCapacity function. + _, err = api.server.clientPool.SetCapacity(client.Node(), uint64(capacity), 0, false) + // time factor recalculation is performed automatically by the balance tracker } else { err = errValue() } @@ -159,22 +191,26 @@ func (api *PrivateLightServerAPI) setParams(params map[string]interface{}, clien // SetClientParams sets client parameters for all clients listed in the ids list // or all connected clients if the list is empty -func (api *PrivateLightServerAPI) SetClientParams(ids []enode.ID, params map[string]interface{}) error { +func (api *PrivateLightServerAPI) SetClientParams(nodes []string, params map[string]interface{}) error { var err error - api.server.clientPool.forClients(ids, func(client *clientInfo) { - if client.connected { - posFactors, negFactors := client.balance.GetPriceFactors() - update, e := api.setParams(params, client, &posFactors, &negFactors) + for _, node := range nodes { + var id enode.ID + if id, err = parseNode(node); err != nil { + return err + } + if peer := api.server.peers.peer(id); peer != nil { + posFactors, negFactors := peer.balance.GetPriceFactors() + update, e := api.setParams(params, peer, &posFactors, &negFactors) if update { - client.balance.SetPriceFactors(posFactors, negFactors) + peer.balance.SetPriceFactors(posFactors, negFactors) } if e != nil { err = e } } else { - err = fmt.Errorf("client %064x is not connected", client.node.ID()) + err = fmt.Errorf("client %064x is not connected", id) } - }) + } return err } @@ -182,7 +218,7 @@ func (api *PrivateLightServerAPI) SetClientParams(ids []enode.ID, params map[str func (api *PrivateLightServerAPI) SetDefaultParams(params map[string]interface{}) error { update, err := api.setParams(params, nil, &api.defaultPosFactors, &api.defaultNegFactors) if update { - api.server.clientPool.setDefaultFactors(api.defaultPosFactors, api.defaultNegFactors) + api.server.clientPool.SetDefaultFactors(api.defaultPosFactors, api.defaultNegFactors) } return err } @@ -195,15 +231,19 @@ func (api *PrivateLightServerAPI) SetConnectedBias(bias time.Duration) error { if bias < time.Duration(0) { return fmt.Errorf("bias illegal: %v less than 0", bias) } - api.server.clientPool.setConnectedBias(bias) + api.server.clientPool.SetConnectedBias(bias) return nil } // AddBalance adds the given amount to the balance of a client if possible and returns // the balance before and after the operation -func (api *PrivateLightServerAPI) AddBalance(id enode.ID, amount int64) (balance [2]uint64, err error) { - api.server.clientPool.forClients([]enode.ID{id}, func(c *clientInfo) { - balance[0], balance[1], err = c.balance.AddBalance(amount) +func (api *PrivateLightServerAPI) AddBalance(node string, amount int64) (balance [2]uint64, err error) { + var id enode.ID + if id, err = parseNode(node); err != nil { + return + } + api.server.clientPool.BalanceOperation(id, "", func(nb vfs.AtomicBalanceOperator) { + balance[0], balance[1], err = nb.AddBalance(amount) }) return } @@ -297,16 +337,20 @@ func NewPrivateDebugAPI(server *LesServer) *PrivateDebugAPI { } // FreezeClient forces a temporary client freeze which normally happens when the server is overloaded -func (api *PrivateDebugAPI) FreezeClient(id enode.ID) error { - var err error - api.server.clientPool.forClients([]enode.ID{id}, func(c *clientInfo) { - if c.connected { - c.peer.freeze() - } else { - err = fmt.Errorf("client %064x is not connected", id[:]) - } - }) - return err +func (api *PrivateDebugAPI) FreezeClient(node string) error { + var ( + id enode.ID + err error + ) + if id, err = parseNode(node); err != nil { + return err + } + if peer := api.server.peers.peer(id); peer != nil { + peer.freeze() + return nil + } else { + return fmt.Errorf("client %064x is not connected", id[:]) + } } // PrivateLightAPI provides an API to access the LES light server or light client. diff --git a/les/api_backend.go b/les/api_backend.go index 39190ce268..e1bef55936 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -41,9 +41,10 @@ import ( ) type LesApiBackend struct { - extRPCEnabled bool - eth *LightEthereum - gpo *gasprice.Oracle + extRPCEnabled bool + allowUnprotectedTxs bool + eth *LightEthereum + gpo *gasprice.Oracle } func (b *LesApiBackend) ChainConfig() ctypes.ChainConfigurator { @@ -171,9 +172,13 @@ func (b *LesApiBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int { return nil } -func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) { - context := core.NewEVMContext(msg, header, b.eth.blockchain, nil) - return vm.NewEVM(context, state, b.eth.chainConfig, vm.Config{}), state.Error, nil +func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) { + if vmConfig == nil { + vmConfig = new(vm.Config) + } + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(header, b.eth.blockchain, nil) + return vm.NewEVM(context, txContext, state, b.eth.chainConfig, *vmConfig), state.Error, nil } func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { @@ -263,6 +268,10 @@ func (b *LesApiBackend) ExtRPCEnabled() bool { return b.extRPCEnabled } +func (b *LesApiBackend) UnprotectedAllowed() bool { + return b.allowUnprotectedTxs +} + func (b *LesApiBackend) RPCGasCap() uint64 { return b.eth.config.RPCGasCap } @@ -292,3 +301,11 @@ func (b *LesApiBackend) Engine() consensus.Engine { func (b *LesApiBackend) CurrentHeader() *types.Header { return b.eth.blockchain.CurrentHeader() } + +func (b *LesApiBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) { + return b.eth.stateAtBlock(ctx, block, reexec) +} + +func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { + return b.eth.stateAtTransaction(ctx, block, txIndex, reexec) +} diff --git a/les/api_test.go b/les/api_test.go index 2895264f67..f7017c5d98 100644 --- a/les/api_test.go +++ b/les/api_test.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" @@ -492,14 +493,14 @@ func testSim(t *testing.T, serverCount, clientCount int, serverDir, clientDir [] } func newLesClientService(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) { - config := eth.DefaultConfig + config := ethconfig.Defaults config.SyncMode = downloader.LightSync config.Ethash.PowMode = ethash.ModeFake return New(stack, &config) } func newLesServerService(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) { - config := eth.DefaultConfig + config := ethconfig.Defaults config.SyncMode = downloader.FullSync config.LightServ = testServerCapacity config.LightPeers = testMaxClients diff --git a/les/benchmark.go b/les/benchmark.go index 3e05e0df3f..c0beae476f 100644 --- a/les/benchmark.go +++ b/les/benchmark.go @@ -75,9 +75,8 @@ func (b *benchmarkBlockHeaders) init(h *serverHandler, count int) error { func (b *benchmarkBlockHeaders) request(peer *serverPeer, index int) error { if b.byHash { return peer.requestHeadersByHash(0, b.hashes[index], b.amount, b.skip, b.reverse) - } else { - return peer.requestHeadersByNumber(0, uint64(b.offset+rand.Int63n(b.randMax)), b.amount, b.skip, b.reverse) } + return peer.requestHeadersByNumber(0, uint64(b.offset+rand.Int63n(b.randMax)), b.amount, b.skip, b.reverse) } // benchmarkBodiesOrReceipts implements requestBenchmark @@ -98,9 +97,8 @@ func (b *benchmarkBodiesOrReceipts) init(h *serverHandler, count int) error { func (b *benchmarkBodiesOrReceipts) request(peer *serverPeer, index int) error { if b.receipts { return peer.requestReceipts(0, []common.Hash{b.hashes[index]}) - } else { - return peer.requestBodies(0, []common.Hash{b.hashes[index]}) } + return peer.requestBodies(0, []common.Hash{b.hashes[index]}) } // benchmarkProofsOrCode implements requestBenchmark @@ -119,9 +117,8 @@ func (b *benchmarkProofsOrCode) request(peer *serverPeer, index int) error { rand.Read(key) if b.code { return peer.requestCode(0, []CodeReq{{BHash: b.headHash, AccKey: key}}) - } else { - return peer.requestProofs(0, []ProofReq{{BHash: b.headHash, Key: key}}) } + return peer.requestProofs(0, []ProofReq{{BHash: b.headHash, Key: key}}) } // benchmarkHelperTrie implements requestBenchmark @@ -159,7 +156,7 @@ func (b *benchmarkHelperTrie) request(peer *serverPeer, index int) error { for i := range reqs { key := make([]byte, 8) binary.BigEndian.PutUint64(key[:], uint64(rand.Int63n(int64(b.headNum)))) - reqs[i] = HelperTrieReq{Type: htCanonical, TrieIdx: b.sectionCount - 1, Key: key, AuxReq: auxHeader} + reqs[i] = HelperTrieReq{Type: htCanonical, TrieIdx: b.sectionCount - 1, Key: key, AuxReq: htAuxHeader} } } @@ -174,7 +171,7 @@ type benchmarkTxSend struct { func (b *benchmarkTxSend) init(h *serverHandler, count int) error { key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey) - signer := types.NewEIP155Signer(big.NewInt(18)) + signer := types.LatestSigner(h.server.chainConfig) b.txs = make(types.Transactions, count) for i := range b.txs { diff --git a/les/client.go b/les/client.go index 99d495db0b..ceaeaacc69 100644 --- a/les/client.go +++ b/les/client.go @@ -30,40 +30,42 @@ import ( "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" - lpc "github.com/ethereum/go-ethereum/les/lespay/client" + "github.com/ethereum/go-ethereum/les/vflux" + vfc "github.com/ethereum/go-ethereum/les/vflux/client" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/params/confp" "github.com/ethereum/go-ethereum/params/types/coregeth" "github.com/ethereum/go-ethereum/params/types/goethereum" "github.com/ethereum/go-ethereum/params/vars" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" ) type LightEthereum struct { lesCommons - peers *serverPeerSet - reqDist *requestDistributor - retriever *retrieveManager - odr *LesOdr - relay *lesTxRelay - handler *clientHandler - txPool *light.TxPool - blockchain *light.LightChain - serverPool *serverPool - valueTracker *lpc.ValueTracker - dialCandidates enode.Iterator - pruner *pruner + peers *serverPeerSet + reqDist *requestDistributor + retriever *retrieveManager + odr *LesOdr + relay *lesTxRelay + handler *clientHandler + txPool *light.TxPool + blockchain *light.LightChain + serverPool *vfc.ServerPool + serverPoolIterator enode.Iterator + pruner *pruner bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports @@ -74,16 +76,18 @@ type LightEthereum struct { accountManager *accounts.Manager netRPCService *ethapi.PublicNetAPI - p2pServer *p2p.Server + p2pServer *p2p.Server + p2pConfig *p2p.Config + udpEnabled bool } // New creates an instance of the light client. -func New(stack *node.Node, config *eth.Config) (*LightEthereum, error) { - chainDb, err := stack.OpenDatabase("lightchaindata", config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/") +func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { + chainDb, err := stack.OpenDatabase("lightchaindata", config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/", false) if err != nil { return nil, err } - lespayDb, err := stack.OpenDatabase("lespay", 0, 0, "eth/db/lespay") + lesDb, err := stack.OpenDatabase("les.client", 0, 0, "eth/db/lesclient/", false) if err != nil { return nil, err } @@ -101,32 +105,32 @@ func New(stack *node.Node, config *eth.Config) (*LightEthereum, error) { chainConfig: chainConfig, iConfig: light.DefaultClientIndexerConfig, chainDb: chainDb, + lesDb: lesDb, closeCh: make(chan struct{}), }, peers: peers, eventMux: stack.EventMux(), reqDist: newRequestDistributor(peers, &mclock.System{}), accountManager: stack.AccountManager(), - engine: eth.CreateConsensusEngine(stack, chainConfig, &config.Ethash, nil, false, chainDb), + engine: ethconfig.CreateConsensusEngine(stack, chainConfig, &config.Ethash, nil, false, chainDb), bloomRequests: make(chan chan *bloombits.Retrieval), - bloomIndexer: eth.NewBloomIndexer(chainDb, vars.BloomBitsBlocksClient, vars.HelperTrieConfirmations), - valueTracker: lpc.NewValueTracker(lespayDb, &mclock.System{}, requestList, time.Minute, 1/float64(time.Hour), 1/float64(time.Hour*100), 1/float64(time.Hour*1000)), + bloomIndexer: core.NewBloomIndexer(chainDb, vars.BloomBitsBlocksClient, vars.HelperTrieConfirmations), p2pServer: stack.Server(), + p2pConfig: &stack.Config().P2P, + udpEnabled: stack.Config().P2P.DiscoveryV5, } - peers.subscribe((*vtSubscription)(leth.valueTracker)) - dnsdisc, err := leth.setupDiscovery(&stack.Config().P2P) - if err != nil { - return nil, err + var prenegQuery vfc.QueryFunc + if leth.udpEnabled { + prenegQuery = leth.prenegQuery } - leth.serverPool = newServerPool(lespayDb, []byte("serverpool:"), leth.valueTracker, dnsdisc, time.Second, nil, &mclock.System{}, config.UltraLightServers) - peers.subscribe(leth.serverPool) - leth.dialCandidates = leth.serverPool.dialIterator + leth.serverPool, leth.serverPoolIterator = vfc.NewServerPool(lesDb, []byte("serverpool:"), time.Second, prenegQuery, &mclock.System{}, config.UltraLightServers, requestList) + leth.serverPool.AddMetrics(suggestedTimeoutGauge, totalValueGauge, serverSelectableGauge, serverConnectedGauge, sessionValueMeter, serverDialedMeter) - leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool.getTimeout) + leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool.GetTimeout) leth.relay = newLesTxRelay(peers, leth.retriever) - leth.odr = NewLesOdr(chainDb, light.DefaultClientIndexerConfig, leth.retriever) + leth.odr = NewLesOdr(chainDb, light.DefaultClientIndexerConfig, leth.peers, leth.retriever) leth.chtIndexer = light.NewChtIndexer(chainDb, leth.odr, vars.CHTFrequency, vars.HelperTrieConfirmations, config.LightNoPrune) leth.bloomTrieIndexer = light.NewBloomTrieIndexer(chainDb, leth.odr, vars.BloomBitsBlocksClient, vars.BloomTrieFrequency, config.LightNoPrune) leth.odr.SetIndexers(leth.chtIndexer, leth.bloomTrieIndexer, leth.bloomIndexer) @@ -165,7 +169,7 @@ func New(stack *node.Node, config *eth.Config) (*LightEthereum, error) { rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig) } - leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), leth, nil} + leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, leth, nil} gpoParams := config.GPO if gpoParams.Default == nil { gpoParams.Default = config.Miner.GasPrice @@ -185,24 +189,82 @@ func New(stack *node.Node, config *eth.Config) (*LightEthereum, error) { stack.RegisterProtocols(leth.Protocols()) stack.RegisterLifecycle(leth) + // Check for unclean shutdown + if uncleanShutdowns, discards, err := rawdb.PushUncleanShutdownMarker(chainDb); err != nil { + log.Error("Could not update unclean-shutdown-marker list", "error", err) + } else { + if discards > 0 { + log.Warn("Old unclean shutdowns found", "count", discards) + } + for _, tstamp := range uncleanShutdowns { + t := time.Unix(int64(tstamp), 0) + log.Warn("Unclean shutdown detected", "booted", t, + "age", common.PrettyAge(t)) + } + } return leth, nil } -// vtSubscription implements serverPeerSubscriber -type vtSubscription lpc.ValueTracker +// VfluxRequest sends a batch of requests to the given node through discv5 UDP TalkRequest and returns the responses +func (s *LightEthereum) VfluxRequest(n *enode.Node, reqs vflux.Requests) vflux.Replies { + if !s.udpEnabled { + return nil + } + reqsEnc, _ := rlp.EncodeToBytes(&reqs) + repliesEnc, _ := s.p2pServer.DiscV5.TalkRequest(s.serverPool.DialNode(n), "vfx", reqsEnc) + var replies vflux.Replies + if len(repliesEnc) == 0 || rlp.DecodeBytes(repliesEnc, &replies) != nil { + return nil + } + return replies +} -// registerPeer implements serverPeerSubscriber -func (v *vtSubscription) registerPeer(p *serverPeer) { - vt := (*lpc.ValueTracker)(v) - p.setValueTracker(vt, vt.Register(p.ID())) - p.updateVtParams() +// vfxVersion returns the version number of the "les" service subdomain of the vflux UDP +// service, as advertised in the ENR record +func (s *LightEthereum) vfxVersion(n *enode.Node) uint { + if n.Seq() == 0 { + var err error + if !s.udpEnabled { + return 0 + } + if n, err = s.p2pServer.DiscV5.RequestENR(n); n != nil && err == nil && n.Seq() != 0 { + s.serverPool.Persist(n) + } else { + return 0 + } + } + + var les []rlp.RawValue + if err := n.Load(enr.WithEntry("les", &les)); err != nil || len(les) < 1 { + return 0 + } + var version uint + rlp.DecodeBytes(les[0], &version) // Ignore additional fields (for forward compatibility). + return version } -// unregisterPeer implements serverPeerSubscriber -func (v *vtSubscription) unregisterPeer(p *serverPeer) { - vt := (*lpc.ValueTracker)(v) - vt.Unregister(p.ID()) - p.setValueTracker(nil, nil) +// prenegQuery sends a capacity query to the given server node to determine whether +// a connection slot is immediately available +func (s *LightEthereum) prenegQuery(n *enode.Node) int { + if s.vfxVersion(n) < 1 { + // UDP query not supported, always try TCP connection + return 1 + } + + var requests vflux.Requests + requests.Add("les", vflux.CapacityQueryName, vflux.CapacityQueryReq{ + Bias: 180, + AddTokens: []vflux.IntOrInf{{}}, + }) + replies := s.VfluxRequest(n, requests) + var cqr vflux.CapacityQueryReply + if replies.Get(0, &cqr) != nil || len(cqr) != 1 { // Note: Get returns an error if replies is nil + return -1 + } + if cqr[0] > 0 { + return 1 + } + return 0 } type LightDummyAPI struct{} @@ -246,7 +308,7 @@ func (s *LightEthereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.ApiBackend, true), + Service: filters.NewPublicFilterAPI(s.ApiBackend, true, 5*time.Minute), Public: true, }, { Namespace: "net", @@ -259,9 +321,9 @@ func (s *LightEthereum) APIs() []rpc.API { Service: NewPrivateLightAPI(&s.lesCommons), Public: false, }, { - Namespace: "lespay", + Namespace: "vflux", Version: "1.0", - Service: lpc.NewPrivateClientAPI(s.valueTracker), + Service: s.serverPool.API(), Public: false, }, }...) @@ -285,7 +347,7 @@ func (s *LightEthereum) Protocols() []p2p.Protocol { return p.Info() } return nil - }, s.dialCandidates) + }, s.serverPoolIterator) } // Start implements node.Lifecycle, starting all internal goroutines needed by the @@ -293,7 +355,16 @@ func (s *LightEthereum) Protocols() []p2p.Protocol { func (s *LightEthereum) Start() error { log.Warn("Light client mode is an experimental feature") - s.serverPool.start() + if s.udpEnabled && s.p2pServer.DiscV5 == nil { + s.udpEnabled = false + log.Error("Discovery v5 is not initialized") + } + discovery, err := s.setupDiscovery() + if err != nil { + return err + } + s.serverPool.AddSource(discovery) + s.serverPool.Start() // Start bloom request workers. s.wg.Add(bloomServiceThreads) s.startBloomHandlers(vars.BloomBitsBlocksClient) @@ -306,8 +377,7 @@ func (s *LightEthereum) Start() error { // Ethereum protocol. func (s *LightEthereum) Stop() error { close(s.closeCh) - s.serverPool.stop() - s.valueTracker.Stop() + s.serverPool.Stop() s.peers.close() s.reqDist.close() s.odr.Stop() @@ -320,7 +390,9 @@ func (s *LightEthereum) Stop() error { s.engine.Close() s.pruner.close() s.eventMux.Stop() + rawdb.PopUncleanShutdownMarker(s.chainDb) s.chainDb.Close() + s.lesDb.Close() s.wg.Wait() log.Info("Light ethereum stopped") return nil diff --git a/les/client_handler.go b/les/client_handler.go index 5c7f7750ce..972c2e8fb5 100644 --- a/les/client_handler.go +++ b/les/client_handler.go @@ -17,6 +17,7 @@ package les import ( + "context" "math/big" "sync" "sync/atomic" @@ -24,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/light" @@ -37,18 +39,23 @@ import ( // responses. type clientHandler struct { ulc *ulc + forkFilter forkid.Filter checkpoint *ctypes.TrustedCheckpoint fetcher *lightFetcher downloader *downloader.Downloader backend *LightEthereum - closeCh chan struct{} - wg sync.WaitGroup // WaitGroup used to track all connected peers. - syncDone func() // Test hooks when syncing is done. + closeCh chan struct{} + wg sync.WaitGroup // WaitGroup used to track all connected peers. + + // Hooks used in the testing + syncStart func(header *types.Header) // Hook called when the syncing is started + syncEnd func(header *types.Header) // Hook called when the syncing is done } func newClientHandler(ulcServers []string, ulcFraction int, checkpoint *ctypes.TrustedCheckpoint, backend *LightEthereum) *clientHandler { handler := &clientHandler{ + forkFilter: forkid.NewFilter(backend.blockchain), checkpoint: checkpoint, backend: backend, closeCh: make(chan struct{}), @@ -103,15 +110,30 @@ func (h *clientHandler) handle(p *serverPeer) error { p.Log().Debug("Light Ethereum peer connected", "name", p.Name()) // Execute the LES handshake - if err := p.Handshake(h.backend.blockchain.Genesis().Hash()); err != nil { + forkid := forkid.NewID(h.backend.blockchain.Config(), h.backend.genesis, h.backend.blockchain.CurrentHeader().Number.Uint64()) + if err := p.Handshake(h.backend.blockchain.Genesis().Hash(), forkid, h.forkFilter); err != nil { p.Log().Debug("Light Ethereum handshake failed", "err", err) return err } + // Register peer with the server pool + if h.backend.serverPool != nil { + if nvt, err := h.backend.serverPool.RegisterNode(p.Node()); err == nil { + p.setValueTracker(nvt) + p.updateVtParams() + defer func() { + p.setValueTracker(nil) + h.backend.serverPool.UnregisterNode(p.Node()) + }() + } else { + return err + } + } // Register the peer locally if err := h.backend.peers.register(p); err != nil { p.Log().Error("Light Ethereum peer registration failed", "err", err) return err } + serverConnectionGauge.Update(int64(h.backend.peers.len())) connectedAt := mclock.Now() @@ -154,8 +176,8 @@ func (h *clientHandler) handleMsg(p *serverPeer) error { var deliverMsg *Msg // Handle the message depending on its contents - switch msg.Code { - case AnnounceMsg: + switch { + case msg.Code == AnnounceMsg: p.Log().Trace("Received announce message") var req announceData if err := msg.Decode(&req); err != nil { @@ -188,7 +210,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error { p.updateHead(req.Hash, req.Number, req.Td) h.fetcher.announce(p, &req) } - case BlockHeadersMsg: + case msg.Code == BlockHeadersMsg: p.Log().Trace("Received block header response message") var resp struct { ReqID, BV uint64 @@ -201,17 +223,26 @@ func (h *clientHandler) handleMsg(p *serverPeer) error { p.fcServer.ReceivedReply(resp.ReqID, resp.BV) p.answeredRequest(resp.ReqID) - // Filter out any explicitly requested headers, deliver the rest to the downloader - filter := len(headers) == 1 - if filter { - headers = h.fetcher.deliverHeaders(p, resp.ReqID, resp.Headers) - } - if len(headers) != 0 || !filter { - if err := h.downloader.DeliverHeaders(p.id, headers); err != nil { - log.Debug("Failed to deliver headers", "err", err) + // Filter out the explicitly requested header by the retriever + if h.backend.retriever.requested(resp.ReqID) { + deliverMsg = &Msg{ + MsgType: MsgBlockHeaders, + ReqID: resp.ReqID, + Obj: resp.Headers, + } + } else { + // Filter out any explicitly requested headers, deliver the rest to the downloader + filter := len(headers) == 1 + if filter { + headers = h.fetcher.deliverHeaders(p, resp.ReqID, resp.Headers) + } + if len(headers) != 0 || !filter { + if err := h.downloader.DeliverHeaders(p.id, headers); err != nil { + log.Debug("Failed to deliver headers", "err", err) + } } } - case BlockBodiesMsg: + case msg.Code == BlockBodiesMsg: p.Log().Trace("Received block bodies response") var resp struct { ReqID, BV uint64 @@ -227,7 +258,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error { ReqID: resp.ReqID, Obj: resp.Data, } - case CodeMsg: + case msg.Code == CodeMsg: p.Log().Trace("Received code response") var resp struct { ReqID, BV uint64 @@ -243,7 +274,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error { ReqID: resp.ReqID, Obj: resp.Data, } - case ReceiptsMsg: + case msg.Code == ReceiptsMsg: p.Log().Trace("Received receipts response") var resp struct { ReqID, BV uint64 @@ -259,7 +290,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error { ReqID: resp.ReqID, Obj: resp.Receipts, } - case ProofsV2Msg: + case msg.Code == ProofsV2Msg: p.Log().Trace("Received les/2 proofs response") var resp struct { ReqID, BV uint64 @@ -275,7 +306,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error { ReqID: resp.ReqID, Obj: resp.Data, } - case HelperTrieProofsMsg: + case msg.Code == HelperTrieProofsMsg: p.Log().Trace("Received helper trie proof response") var resp struct { ReqID, BV uint64 @@ -291,7 +322,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error { ReqID: resp.ReqID, Obj: resp.Data, } - case TxStatusMsg: + case msg.Code == TxStatusMsg: p.Log().Trace("Received tx status response") var resp struct { ReqID, BV uint64 @@ -307,11 +338,11 @@ func (h *clientHandler) handleMsg(p *serverPeer) error { ReqID: resp.ReqID, Obj: resp.Status, } - case StopMsg: + case msg.Code == StopMsg && p.version >= lpv3: p.freeze() h.backend.retriever.frozen(p) p.Log().Debug("Service stopped") - case ResumeMsg: + case msg.Code == ResumeMsg && p.version >= lpv3: var bv uint64 if err := msg.Decode(&bv); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) @@ -395,6 +426,42 @@ func (pc *peerConnection) RequestHeadersByNumber(origin uint64, amount int, skip return nil } +// RetrieveSingleHeaderByNumber requests a single header by the specified block +// number. This function will wait the response until it's timeout or delivered. +func (pc *peerConnection) RetrieveSingleHeaderByNumber(context context.Context, number uint64) (*types.Header, error) { + reqID := genReqID() + rq := &distReq{ + getCost: func(dp distPeer) uint64 { + peer := dp.(*serverPeer) + return peer.getRequestCost(GetBlockHeadersMsg, 1) + }, + canSend: func(dp distPeer) bool { + return dp.(*serverPeer) == pc.peer + }, + request: func(dp distPeer) func() { + peer := dp.(*serverPeer) + cost := peer.getRequestCost(GetBlockHeadersMsg, 1) + peer.fcServer.QueuedRequest(reqID, cost) + return func() { peer.requestHeadersByNumber(reqID, number, 1, 0, false) } + }, + } + var header *types.Header + if err := pc.handler.backend.retriever.retrieve(context, reqID, rq, func(peer distPeer, msg *Msg) error { + if msg.MsgType != MsgBlockHeaders { + return errInvalidMessageType + } + headers := msg.Obj.([]*types.Header) + if len(headers) != 1 { + return errInvalidEntryCount + } + header = headers[0] + return nil + }, nil); err != nil { + return nil, err + } + return header, nil +} + // downloaderPeerNotify implements peerSetNotify type downloaderPeerNotify clientHandler diff --git a/les/clientpool.go b/les/clientpool.go deleted file mode 100644 index da0db6e622..0000000000 --- a/les/clientpool.go +++ /dev/null @@ -1,384 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package les - -import ( - "fmt" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/ethdb" - lps "github.com/ethereum/go-ethereum/les/lespay/server" - "github.com/ethereum/go-ethereum/les/utils" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/p2p/enr" - "github.com/ethereum/go-ethereum/p2p/nodestate" -) - -const ( - defaultNegExpTC = 3600 // default time constant (in seconds) for exponentially reducing negative balance - - // defaultConnectedBias is applied to already connected clients So that - // already connected client won't be kicked out very soon and we - // can ensure all connected clients can have enough time to request - // or sync some data. - // - // todo(rjl493456442) make it configurable. It can be the option of - // free trial time! - defaultConnectedBias = time.Minute * 3 - inactiveTimeout = time.Second * 10 -) - -// clientPool implements a client database that assigns a priority to each client -// based on a positive and negative balance. Positive balance is externally assigned -// to prioritized clients and is decreased with connection time and processed -// requests (unless the price factors are zero). If the positive balance is zero -// then negative balance is accumulated. -// -// Balance tracking and priority calculation for connected clients is done by -// balanceTracker. activeQueue ensures that clients with the lowest positive or -// highest negative balance get evicted when the total capacity allowance is full -// and new clients with a better balance want to connect. -// -// Already connected nodes receive a small bias in their favor in order to avoid -// accepting and instantly kicking out clients. In theory, we try to ensure that -// each client can have several minutes of connection time. -// -// Balances of disconnected clients are stored in nodeDB including positive balance -// and negative banalce. Boeth positive balance and negative balance will decrease -// exponentially. If the balance is low enough, then the record will be dropped. -type clientPool struct { - lps.BalanceTrackerSetup - lps.PriorityPoolSetup - lock sync.Mutex - clock mclock.Clock - closed bool - removePeer func(enode.ID) - ns *nodestate.NodeStateMachine - pp *lps.PriorityPool - bt *lps.BalanceTracker - - defaultPosFactors, defaultNegFactors lps.PriceFactors - posExpTC, negExpTC uint64 - minCap uint64 // The minimal capacity value allowed for any client - connectedBias time.Duration - capLimit uint64 -} - -// clientPoolPeer represents a client peer in the pool. -// Positive balances are assigned to node key while negative balances are assigned -// to freeClientId. Currently network IP address without port is used because -// clients have a limited access to IP addresses while new node keys can be easily -// generated so it would be useless to assign a negative value to them. -type clientPoolPeer interface { - Node() *enode.Node - freeClientId() string - updateCapacity(uint64) - freeze() - allowInactive() bool -} - -// clientInfo defines all information required by clientpool. -type clientInfo struct { - node *enode.Node - address string - peer clientPoolPeer - connected, priority bool - connectedAt mclock.AbsTime - balance *lps.NodeBalance -} - -// newClientPool creates a new client pool -func newClientPool(ns *nodestate.NodeStateMachine, lespayDb ethdb.Database, minCap uint64, connectedBias time.Duration, clock mclock.Clock, removePeer func(enode.ID)) *clientPool { - pool := &clientPool{ - ns: ns, - BalanceTrackerSetup: balanceTrackerSetup, - PriorityPoolSetup: priorityPoolSetup, - clock: clock, - minCap: minCap, - connectedBias: connectedBias, - removePeer: removePeer, - } - pool.bt = lps.NewBalanceTracker(ns, balanceTrackerSetup, lespayDb, clock, &utils.Expirer{}, &utils.Expirer{}) - pool.pp = lps.NewPriorityPool(ns, priorityPoolSetup, clock, minCap, connectedBias, 4) - - // set default expiration constants used by tests - // Note: server overwrites this if token sale is active - pool.bt.SetExpirationTCs(0, defaultNegExpTC) - - ns.SubscribeState(pool.InactiveFlag.Or(pool.PriorityFlag), func(node *enode.Node, oldState, newState nodestate.Flags) { - if newState.Equals(pool.InactiveFlag) { - ns.AddTimeout(node, pool.InactiveFlag, inactiveTimeout) - } - if oldState.Equals(pool.InactiveFlag) && newState.Equals(pool.InactiveFlag.Or(pool.PriorityFlag)) { - ns.SetStateSub(node, pool.InactiveFlag, nodestate.Flags{}, 0) // remove timeout - } - }) - - ns.SubscribeState(pool.ActiveFlag.Or(pool.PriorityFlag), func(node *enode.Node, oldState, newState nodestate.Flags) { - c, _ := ns.GetField(node, clientInfoField).(*clientInfo) - if c == nil { - return - } - c.priority = newState.HasAll(pool.PriorityFlag) - if newState.Equals(pool.ActiveFlag) { - cap, _ := ns.GetField(node, pool.CapacityField).(uint64) - if cap > minCap { - pool.pp.RequestCapacity(node, minCap, 0, true) - } - } - }) - - ns.SubscribeState(pool.InactiveFlag.Or(pool.ActiveFlag), func(node *enode.Node, oldState, newState nodestate.Flags) { - if oldState.IsEmpty() { - clientConnectedMeter.Mark(1) - log.Debug("Client connected", "id", node.ID()) - } - if oldState.Equals(pool.InactiveFlag) && newState.Equals(pool.ActiveFlag) { - clientActivatedMeter.Mark(1) - log.Debug("Client activated", "id", node.ID()) - } - if oldState.Equals(pool.ActiveFlag) && newState.Equals(pool.InactiveFlag) { - clientDeactivatedMeter.Mark(1) - log.Debug("Client deactivated", "id", node.ID()) - c, _ := ns.GetField(node, clientInfoField).(*clientInfo) - if c == nil || !c.peer.allowInactive() { - pool.removePeer(node.ID()) - } - } - if newState.IsEmpty() { - clientDisconnectedMeter.Mark(1) - log.Debug("Client disconnected", "id", node.ID()) - pool.removePeer(node.ID()) - } - }) - - var totalConnected uint64 - ns.SubscribeField(pool.CapacityField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) { - oldCap, _ := oldValue.(uint64) - newCap, _ := newValue.(uint64) - totalConnected += newCap - oldCap - totalConnectedGauge.Update(int64(totalConnected)) - c, _ := ns.GetField(node, clientInfoField).(*clientInfo) - if c != nil { - c.peer.updateCapacity(newCap) - } - }) - return pool -} - -// stop shuts the client pool down -func (f *clientPool) stop() { - f.lock.Lock() - f.closed = true - f.lock.Unlock() - f.ns.ForEach(nodestate.Flags{}, nodestate.Flags{}, func(node *enode.Node, state nodestate.Flags) { - // enforces saving all balances in BalanceTracker - f.disconnectNode(node) - }) - f.bt.Stop() -} - -// connect should be called after a successful handshake. If the connection was -// rejected, there is no need to call disconnect. -func (f *clientPool) connect(peer clientPoolPeer) (uint64, error) { - f.lock.Lock() - defer f.lock.Unlock() - - // Short circuit if clientPool is already closed. - if f.closed { - return 0, fmt.Errorf("Client pool is already closed") - } - // Dedup connected peers. - node, freeID := peer.Node(), peer.freeClientId() - if f.ns.GetField(node, clientInfoField) != nil { - log.Debug("Client already connected", "address", freeID, "id", node.ID().String()) - return 0, fmt.Errorf("Client already connected address=%s id=%s", freeID, node.ID().String()) - } - now := f.clock.Now() - c := &clientInfo{ - node: node, - address: freeID, - peer: peer, - connected: true, - connectedAt: now, - } - f.ns.SetField(node, clientInfoField, c) - f.ns.SetField(node, connAddressField, freeID) - if c.balance, _ = f.ns.GetField(node, f.BalanceField).(*lps.NodeBalance); c.balance == nil { - f.disconnect(peer) - return 0, nil - } - c.balance.SetPriceFactors(f.defaultPosFactors, f.defaultNegFactors) - - f.ns.SetState(node, f.InactiveFlag, nodestate.Flags{}, 0) - var allowed bool - f.ns.Operation(func() { - _, allowed = f.pp.RequestCapacity(node, f.minCap, f.connectedBias, true) - }) - if allowed { - return f.minCap, nil - } - if !peer.allowInactive() { - f.disconnect(peer) - } - return 0, nil -} - -// setConnectedBias sets the connection bias, which is applied to already connected clients -// So that already connected client won't be kicked out very soon and we can ensure all -// connected clients can have enough time to request or sync some data. -func (f *clientPool) setConnectedBias(bias time.Duration) { - f.lock.Lock() - defer f.lock.Unlock() - - f.connectedBias = bias - f.pp.SetActiveBias(bias) -} - -// disconnect should be called when a connection is terminated. If the disconnection -// was initiated by the pool itself using disconnectFn then calling disconnect is -// not necessary but permitted. -func (f *clientPool) disconnect(p clientPoolPeer) { - f.disconnectNode(p.Node()) -} - -// disconnectNode removes node fields and flags related to connected status -func (f *clientPool) disconnectNode(node *enode.Node) { - f.ns.SetField(node, connAddressField, nil) - f.ns.SetField(node, clientInfoField, nil) -} - -// setDefaultFactors sets the default price factors applied to subsequently connected clients -func (f *clientPool) setDefaultFactors(posFactors, negFactors lps.PriceFactors) { - f.lock.Lock() - defer f.lock.Unlock() - - f.defaultPosFactors = posFactors - f.defaultNegFactors = negFactors -} - -// capacityInfo returns the total capacity allowance, the total capacity of connected -// clients and the total capacity of connected and prioritized clients -func (f *clientPool) capacityInfo() (uint64, uint64, uint64) { - f.lock.Lock() - defer f.lock.Unlock() - - // total priority active cap will be supported when the token issuer module is added - _, activeCap := f.pp.Active() - return f.capLimit, activeCap, 0 -} - -// setLimits sets the maximum number and total capacity of connected clients, -// dropping some of them if necessary. -func (f *clientPool) setLimits(totalConn int, totalCap uint64) { - f.lock.Lock() - defer f.lock.Unlock() - - f.capLimit = totalCap - f.pp.SetLimits(uint64(totalConn), totalCap) -} - -// setCapacity sets the assigned capacity of a connected client -func (f *clientPool) setCapacity(node *enode.Node, freeID string, capacity uint64, bias time.Duration, setCap bool) (uint64, error) { - c, _ := f.ns.GetField(node, clientInfoField).(*clientInfo) - if c == nil { - if setCap { - return 0, fmt.Errorf("client %064x is not connected", node.ID()) - } - c = &clientInfo{node: node} - f.ns.SetField(node, clientInfoField, c) - f.ns.SetField(node, connAddressField, freeID) - if c.balance, _ = f.ns.GetField(node, f.BalanceField).(*lps.NodeBalance); c.balance == nil { - log.Error("BalanceField is missing", "node", node.ID()) - return 0, fmt.Errorf("BalanceField of %064x is missing", node.ID()) - } - defer func() { - f.ns.SetField(node, connAddressField, nil) - f.ns.SetField(node, clientInfoField, nil) - }() - } - var ( - minPriority int64 - allowed bool - ) - f.ns.Operation(func() { - if !setCap || c.priority { - // check clientInfo.priority inside Operation to ensure thread safety - minPriority, allowed = f.pp.RequestCapacity(node, capacity, bias, setCap) - } - }) - if allowed { - return 0, nil - } - missing := c.balance.PosBalanceMissing(minPriority, capacity, bias) - if missing < 1 { - // ensure that we never return 0 missing and insufficient priority error - missing = 1 - } - return missing, errNoPriority -} - -// setCapacityLocked is the equivalent of setCapacity used when f.lock is already locked -func (f *clientPool) setCapacityLocked(node *enode.Node, freeID string, capacity uint64, minConnTime time.Duration, setCap bool) (uint64, error) { - f.lock.Lock() - defer f.lock.Unlock() - - return f.setCapacity(node, freeID, capacity, minConnTime, setCap) -} - -// forClients calls the supplied callback for either the listed node IDs or all connected -// nodes. It passes a valid clientInfo to the callback and ensures that the necessary -// fields and flags are set in order for BalanceTracker and PriorityPool to work even if -// the node is not connected. -func (f *clientPool) forClients(ids []enode.ID, cb func(client *clientInfo)) { - f.lock.Lock() - defer f.lock.Unlock() - - if len(ids) == 0 { - f.ns.ForEach(nodestate.Flags{}, nodestate.Flags{}, func(node *enode.Node, state nodestate.Flags) { - c, _ := f.ns.GetField(node, clientInfoField).(*clientInfo) - if c != nil { - cb(c) - } - }) - } else { - for _, id := range ids { - node := f.ns.GetNode(id) - if node == nil { - node = enode.SignNull(&enr.Record{}, id) - } - c, _ := f.ns.GetField(node, clientInfoField).(*clientInfo) - if c != nil { - cb(c) - } else { - c = &clientInfo{node: node} - f.ns.SetField(node, clientInfoField, c) - f.ns.SetField(node, connAddressField, "") - if c.balance, _ = f.ns.GetField(node, f.BalanceField).(*lps.NodeBalance); c.balance != nil { - cb(c) - } else { - log.Error("BalanceField is missing") - } - f.ns.SetField(node, connAddressField, nil) - f.ns.SetField(node, clientInfoField, nil) - } - } - } -} diff --git a/les/commons.go b/les/commons.go index 977a23e623..2b396403ab 100644 --- a/les/commons.go +++ b/les/commons.go @@ -25,7 +25,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/les/checkpointoracle" @@ -33,7 +33,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/types/ctypes" @@ -43,17 +42,6 @@ func errResp(code errCode, format string, v ...interface{}) error { return fmt.Errorf("%v - %v", code, fmt.Sprintf(format, v...)) } -func lesTopic(genesisHash common.Hash, protocolVersion uint) discv5.Topic { - var name string - switch protocolVersion { - case lpv2: - name = "LES2" - default: - panic(nil) - } - return discv5.Topic(name + "@" + common.Bytes2Hex(genesisHash.Bytes()[0:8])) -} - type chainReader interface { CurrentHeader() *types.Header } @@ -61,10 +49,10 @@ type chainReader interface { // lesCommons contains fields needed by both server and client. type lesCommons struct { genesis common.Hash - config *eth.Config + config *ethconfig.Config chainConfig ctypes.ChainConfigurator iConfig *light.IndexerConfig - chainDb ethdb.Database + chainDb, lesDb ethdb.Database chainReader chainReader chtIndexer, bloomTrieIndexer *core.ChainIndexer oracle *checkpointoracle.CheckpointOracle @@ -151,24 +139,24 @@ func (c *lesCommons) localCheckpoint(index uint64) ctypes.TrustedCheckpoint { } // setupOracle sets up the checkpoint oracle contract client. -func (c *lesCommons) setupOracle(node *node.Node, genesis common.Hash, ethconfig *eth.Config) *checkpointoracle.CheckpointOracle { +func (c *lesCommons) setupOracle(node *node.Node, genesis common.Hash, ethconfig *ethconfig.Config) *checkpointoracle.CheckpointOracle { config := ethconfig.CheckpointOracle if config == nil { // Try loading default config. config = params.CheckpointOracles[genesis] } if config == nil { - log.Info("Checkpoint registrar is not enabled") + log.Info("Checkpoint oracle is not enabled") return nil } if config.Address == (common.Address{}) || uint64(len(config.Signers)) < config.Threshold { - log.Warn("Invalid checkpoint registrar config") + log.Warn("Invalid checkpoint oracle config") return nil } oracle := checkpointoracle.New(config, c.localCheckpoint) rpcClient, _ := node.Attach() client := ethclient.NewClient(rpcClient) oracle.Start(client) - log.Info("Configured checkpoint registrar", "address", config.Address, "signers", len(config.Signers), "threshold", config.Threshold) + log.Info("Configured checkpoint oracle", "address", config.Address, "signers", len(config.Signers), "threshold", config.Threshold) return oracle } diff --git a/les/costtracker.go b/les/costtracker.go index 0558779bc5..43e32a5b2d 100644 --- a/les/costtracker.go +++ b/les/costtracker.go @@ -24,7 +24,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/log" @@ -137,7 +137,7 @@ type costTracker struct { // newCostTracker creates a cost tracker and loads the cost factor statistics from the database. // It also returns the minimum capacity that can be assigned to any peer. -func newCostTracker(db ethdb.Database, config *eth.Config) (*costTracker, uint64) { +func newCostTracker(db ethdb.Database, config *ethconfig.Config) (*costTracker, uint64) { utilTarget := float64(config.LightServ) * flowcontrol.FixedPointMultiplier / 100 ct := &costTracker{ db: db, diff --git a/les/enr_entry.go b/les/enr_entry.go index 11e6273be5..307313fb10 100644 --- a/les/enr_entry.go +++ b/les/enr_entry.go @@ -17,7 +17,7 @@ package les import ( - "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/p2p/dnsdisc" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rlp" @@ -26,19 +26,47 @@ import ( // lesEntry is the "les" ENR entry. This is set for LES servers only. type lesEntry struct { // Ignore additional fields (for forward compatibility). - Rest []rlp.RawValue `rlp:"tail"` + VfxVersion uint + Rest []rlp.RawValue `rlp:"tail"` } -// ENRKey implements enr.Entry. -func (e lesEntry) ENRKey() string { - return "les" +func (lesEntry) ENRKey() string { return "les" } + +// ethEntry is the "eth" ENR entry. This is redeclared here to avoid depending on package eth. +type ethEntry struct { + ForkID forkid.ID + Tail []rlp.RawValue `rlp:"tail"` } +func (ethEntry) ENRKey() string { return "eth" } + // setupDiscovery creates the node discovery source for the eth protocol. -func (eth *LightEthereum) setupDiscovery(cfg *p2p.Config) (enode.Iterator, error) { - if cfg.NoDiscovery || len(eth.config.DiscoveryURLs) == 0 { - return nil, nil +func (eth *LightEthereum) setupDiscovery() (enode.Iterator, error) { + it := enode.NewFairMix(0) + + // Enable DNS discovery. + if len(eth.config.EthDiscoveryURLs) != 0 { + client := dnsdisc.NewClient(dnsdisc.Config{}) + dns, err := client.NewIterator(eth.config.EthDiscoveryURLs...) + if err != nil { + return nil, err + } + it.AddSource(dns) + } + + // Enable DHT. + if eth.udpEnabled { + it.AddSource(eth.p2pServer.DiscV5.RandomNodes()) } - client := dnsdisc.NewClient(dnsdisc.Config{}) - return client.NewIterator(eth.config.DiscoveryURLs...) + + forkFilter := forkid.NewFilter(eth.blockchain) + iterator := enode.Filter(it, func(n *enode.Node) bool { return nodeIsServer(forkFilter, n) }) + return iterator, nil +} + +// nodeIsServer checks whether n is an LES server node. +func nodeIsServer(forkFilter forkid.Filter, n *enode.Node) bool { + var les lesEntry + var eth ethEntry + return n.Load(&les) == nil && n.Load(ð) == nil && forkFilter(eth.ForkID) == nil } diff --git a/les/fetcher_test.go b/les/fetcher_test.go index a9e6e6835e..d3a74d25c2 100644 --- a/les/fetcher_test.go +++ b/les/fetcher_test.go @@ -66,7 +66,12 @@ func TestSequentialAnnouncementsLes2(t *testing.T) { testSequentialAnnouncements func TestSequentialAnnouncementsLes3(t *testing.T) { testSequentialAnnouncements(t, 3) } func testSequentialAnnouncements(t *testing.T, protocol int) { - s, c, teardown := newClientServerEnv(t, 4, protocol, nil, nil, 0, false, false, true) + netconfig := testnetConfig{ + blocks: 4, + protocol: protocol, + nopruning: true, + } + s, c, teardown := newClientServerEnv(t, netconfig) defer teardown() // Create connected peer pair. @@ -101,7 +106,12 @@ func TestGappedAnnouncementsLes2(t *testing.T) { testGappedAnnouncements(t, 2) } func TestGappedAnnouncementsLes3(t *testing.T) { testGappedAnnouncements(t, 3) } func testGappedAnnouncements(t *testing.T, protocol int) { - s, c, teardown := newClientServerEnv(t, 4, protocol, nil, nil, 0, false, false, true) + netconfig := testnetConfig{ + blocks: 4, + protocol: protocol, + nopruning: true, + } + s, c, teardown := newClientServerEnv(t, netconfig) defer teardown() // Create connected peer pair. @@ -183,7 +193,13 @@ func testTrustedAnnouncement(t *testing.T, protocol int) { ids = append(ids, n.String()) } } - _, c, teardown := newClientServerEnv(t, 0, protocol, nil, ids, 60, false, false, true) + netconfig := testnetConfig{ + protocol: protocol, + nopruning: true, + ulcServers: ids, + ulcFraction: 60, + } + _, c, teardown := newClientServerEnv(t, netconfig) defer teardown() defer func() { for i := 0; i < len(teardowns); i++ { @@ -233,8 +249,17 @@ func testTrustedAnnouncement(t *testing.T, protocol int) { check([]uint64{10}, 10, func() { <-newHead }) // Sync the whole chain. } -func TestInvalidAnnounces(t *testing.T) { - s, c, teardown := newClientServerEnv(t, 4, lpv3, nil, nil, 0, false, false, true) +func TestInvalidAnnouncesLES2(t *testing.T) { testInvalidAnnounces(t, lpv2) } +func TestInvalidAnnouncesLES3(t *testing.T) { testInvalidAnnounces(t, lpv3) } +func TestInvalidAnnouncesLES4(t *testing.T) { testInvalidAnnounces(t, lpv4) } + +func testInvalidAnnounces(t *testing.T, protocol int) { + netconfig := testnetConfig{ + blocks: 4, + protocol: protocol, + nopruning: true, + } + s, c, teardown := newClientServerEnv(t, netconfig) defer teardown() // Create connected peer pair. diff --git a/les/flowcontrol/control.go b/les/flowcontrol/control.go index 490013677c..4f0de82318 100644 --- a/les/flowcontrol/control.go +++ b/les/flowcontrol/control.go @@ -19,6 +19,7 @@ package flowcontrol import ( "fmt" + "math" "sync" "time" @@ -316,6 +317,9 @@ func (node *ServerNode) CanSend(maxCost uint64) (time.Duration, float64) { node.lock.RLock() defer node.lock.RUnlock() + if node.params.BufLimit == 0 { + return time.Duration(math.MaxInt64), 0 + } now := node.clock.Now() node.recalcBLE(now) maxCost += uint64(safetyMargin) * node.params.MinRecharge / uint64(fcTimeConst) diff --git a/les/flowcontrol/manager.go b/les/flowcontrol/manager.go index d6d0b1adde..c9e681c144 100644 --- a/les/flowcontrol/manager.go +++ b/les/flowcontrol/manager.go @@ -108,7 +108,7 @@ type ClientManager struct { func NewClientManager(curve PieceWiseLinear, clock mclock.Clock) *ClientManager { cm := &ClientManager{ clock: clock, - rcQueue: prque.New(func(a interface{}, i int) { a.(*ClientNode).queueIndex = i }), + rcQueue: prque.NewWrapAround(func(a interface{}, i int) { a.(*ClientNode).queueIndex = i }), capLastUpdate: clock.Now(), stop: make(chan chan struct{}), } diff --git a/les/handler_test.go b/les/handler_test.go index e42971752f..0e50a7eadc 100644 --- a/les/handler_test.go +++ b/les/handler_test.go @@ -50,11 +50,19 @@ func expectResponse(r p2p.MsgReader, msgcode, reqID, bv uint64, data interface{} // Tests that block headers can be retrieved from a remote chain based on user queries. func TestGetBlockHeadersLes2(t *testing.T) { testGetBlockHeaders(t, 2) } func TestGetBlockHeadersLes3(t *testing.T) { testGetBlockHeaders(t, 3) } +func TestGetBlockHeadersLes4(t *testing.T) { testGetBlockHeaders(t, 4) } func testGetBlockHeaders(t *testing.T, protocol int) { - server, tearDown := newServerEnv(t, downloader.MaxHashFetch+15, protocol, nil, false, true, 0) + netconfig := testnetConfig{ + blocks: downloader.MaxHeaderFetch + 15, + protocol: protocol, + nopruning: true, + } + server, _, tearDown := newClientServerEnv(t, netconfig) defer tearDown() + rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol) + defer closePeer() bc := server.handler.blockchain // Create a "random" unknown hash for testing @@ -65,27 +73,27 @@ func testGetBlockHeaders(t *testing.T, protocol int) { // Create a batch of tests for various scenarios limit := uint64(MaxHeaderFetch) tests := []struct { - query *getBlockHeadersData // The query to execute for header retrieval + query *GetBlockHeadersData // The query to execute for header retrieval expect []common.Hash // The hashes of the block whose headers are expected }{ // A single random block should be retrievable by hash and number too { - &getBlockHeadersData{Origin: hashOrNumber{Hash: bc.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, + &GetBlockHeadersData{Origin: hashOrNumber{Hash: bc.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, []common.Hash{bc.GetBlockByNumber(limit / 2).Hash()}, }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, + &GetBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, []common.Hash{bc.GetBlockByNumber(limit / 2).Hash()}, }, // Multiple headers should be retrievable in both directions { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, + &GetBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, []common.Hash{ bc.GetBlockByNumber(limit / 2).Hash(), bc.GetBlockByNumber(limit/2 + 1).Hash(), bc.GetBlockByNumber(limit/2 + 2).Hash(), }, }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, + &GetBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, []common.Hash{ bc.GetBlockByNumber(limit / 2).Hash(), bc.GetBlockByNumber(limit/2 - 1).Hash(), @@ -94,14 +102,14 @@ func testGetBlockHeaders(t *testing.T, protocol int) { }, // Multiple headers with skip lists should be retrievable { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, + &GetBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, []common.Hash{ bc.GetBlockByNumber(limit / 2).Hash(), bc.GetBlockByNumber(limit/2 + 4).Hash(), bc.GetBlockByNumber(limit/2 + 8).Hash(), }, }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, + &GetBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, []common.Hash{ bc.GetBlockByNumber(limit / 2).Hash(), bc.GetBlockByNumber(limit/2 - 4).Hash(), @@ -110,26 +118,26 @@ func testGetBlockHeaders(t *testing.T, protocol int) { }, // The chain endpoints should be retrievable { - &getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1}, + &GetBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1}, []common.Hash{bc.GetBlockByNumber(0).Hash()}, }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64()}, Amount: 1}, + &GetBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64()}, Amount: 1}, []common.Hash{bc.CurrentBlock().Hash()}, }, // Ensure protocol limits are honored //{ - // &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, + // &GetBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, // []common.Hash{}, //}, // Check that requesting more than available is handled gracefully { - &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, + &GetBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, []common.Hash{ bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 4).Hash(), bc.GetBlockByNumber(bc.CurrentBlock().NumberU64()).Hash(), }, }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, + &GetBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, []common.Hash{ bc.GetBlockByNumber(4).Hash(), bc.GetBlockByNumber(0).Hash(), @@ -137,13 +145,13 @@ func testGetBlockHeaders(t *testing.T, protocol int) { }, // Check that requesting more than available is handled gracefully, even if mid skip { - &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, + &GetBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, []common.Hash{ bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 4).Hash(), bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1).Hash(), }, }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, + &GetBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, []common.Hash{ bc.GetBlockByNumber(4).Hash(), bc.GetBlockByNumber(1).Hash(), @@ -151,10 +159,10 @@ func testGetBlockHeaders(t *testing.T, protocol int) { }, // Check that non existing headers aren't returned { - &getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1}, + &GetBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1}, []common.Hash{}, }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() + 1}, Amount: 1}, + &GetBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() + 1}, Amount: 1}, []common.Hash{}, }, } @@ -169,8 +177,8 @@ func testGetBlockHeaders(t *testing.T, protocol int) { // Send the hash request and verify the response reqID++ - sendRequest(server.peer.app, GetBlockHeadersMsg, reqID, tt.query) - if err := expectResponse(server.peer.app, BlockHeadersMsg, reqID, testBufLimit, headers); err != nil { + sendRequest(rawPeer.app, GetBlockHeadersMsg, reqID, tt.query) + if err := expectResponse(rawPeer.app, BlockHeadersMsg, reqID, testBufLimit, headers); err != nil { t.Errorf("test %d: headers mismatch: %v", i, err) } } @@ -179,11 +187,20 @@ func testGetBlockHeaders(t *testing.T, protocol int) { // Tests that block contents can be retrieved from a remote chain based on their hashes. func TestGetBlockBodiesLes2(t *testing.T) { testGetBlockBodies(t, 2) } func TestGetBlockBodiesLes3(t *testing.T) { testGetBlockBodies(t, 3) } +func TestGetBlockBodiesLes4(t *testing.T) { testGetBlockBodies(t, 4) } func testGetBlockBodies(t *testing.T, protocol int) { - server, tearDown := newServerEnv(t, downloader.MaxBlockFetch+15, protocol, nil, false, true, 0) + netconfig := testnetConfig{ + blocks: downloader.MaxHeaderFetch + 15, + protocol: protocol, + nopruning: true, + } + server, _, tearDown := newClientServerEnv(t, netconfig) defer tearDown() + rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol) + defer closePeer() + bc := server.handler.blockchain // Create a batch of tests for various scenarios @@ -246,8 +263,8 @@ func testGetBlockBodies(t *testing.T, protocol int) { reqID++ // Send the hash request and verify the response - sendRequest(server.peer.app, GetBlockBodiesMsg, reqID, hashes) - if err := expectResponse(server.peer.app, BlockBodiesMsg, reqID, testBufLimit, bodies); err != nil { + sendRequest(rawPeer.app, GetBlockBodiesMsg, reqID, hashes) + if err := expectResponse(rawPeer.app, BlockBodiesMsg, reqID, testBufLimit, bodies); err != nil { t.Errorf("test %d: bodies mismatch: %v", i, err) } } @@ -256,11 +273,21 @@ func testGetBlockBodies(t *testing.T, protocol int) { // Tests that the contract codes can be retrieved based on account addresses. func TestGetCodeLes2(t *testing.T) { testGetCode(t, 2) } func TestGetCodeLes3(t *testing.T) { testGetCode(t, 3) } +func TestGetCodeLes4(t *testing.T) { testGetCode(t, 4) } func testGetCode(t *testing.T, protocol int) { // Assemble the test environment - server, tearDown := newServerEnv(t, 4, protocol, nil, false, true, 0) + netconfig := testnetConfig{ + blocks: 4, + protocol: protocol, + nopruning: true, + } + server, _, tearDown := newClientServerEnv(t, netconfig) defer tearDown() + + rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol) + defer closePeer() + bc := server.handler.blockchain var codereqs []*CodeReq @@ -277,8 +304,8 @@ func testGetCode(t *testing.T, protocol int) { } } - sendRequest(server.peer.app, GetCodeMsg, 42, codereqs) - if err := expectResponse(server.peer.app, CodeMsg, 42, testBufLimit, codes); err != nil { + sendRequest(rawPeer.app, GetCodeMsg, 42, codereqs) + if err := expectResponse(rawPeer.app, CodeMsg, 42, testBufLimit, codes); err != nil { t.Errorf("codes mismatch: %v", err) } } @@ -286,10 +313,20 @@ func testGetCode(t *testing.T, protocol int) { // Tests that the stale contract codes can't be retrieved based on account addresses. func TestGetStaleCodeLes2(t *testing.T) { testGetStaleCode(t, 2) } func TestGetStaleCodeLes3(t *testing.T) { testGetStaleCode(t, 3) } +func TestGetStaleCodeLes4(t *testing.T) { testGetStaleCode(t, 4) } func testGetStaleCode(t *testing.T, protocol int) { - server, tearDown := newServerEnv(t, core.TriesInMemory+4, protocol, nil, false, true, 0) + netconfig := testnetConfig{ + blocks: core.TriesInMemory + 4, + protocol: protocol, + nopruning: true, + } + server, _, tearDown := newClientServerEnv(t, netconfig) defer tearDown() + + rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol) + defer closePeer() + bc := server.handler.blockchain check := func(number uint64, expected [][]byte) { @@ -297,8 +334,8 @@ func testGetStaleCode(t *testing.T, protocol int) { BHash: bc.GetHeaderByNumber(number).Hash(), AccKey: crypto.Keccak256(testContractAddr[:]), } - sendRequest(server.peer.app, GetCodeMsg, 42, []*CodeReq{req}) - if err := expectResponse(server.peer.app, CodeMsg, 42, testBufLimit, expected); err != nil { + sendRequest(rawPeer.app, GetCodeMsg, 42, []*CodeReq{req}) + if err := expectResponse(rawPeer.app, CodeMsg, 42, testBufLimit, expected); err != nil { t.Errorf("codes mismatch: %v", err) } } @@ -310,12 +347,21 @@ func testGetStaleCode(t *testing.T, protocol int) { // Tests that the transaction receipts can be retrieved based on hashes. func TestGetReceiptLes2(t *testing.T) { testGetReceipt(t, 2) } func TestGetReceiptLes3(t *testing.T) { testGetReceipt(t, 3) } +func TestGetReceiptLes4(t *testing.T) { testGetReceipt(t, 4) } func testGetReceipt(t *testing.T, protocol int) { // Assemble the test environment - server, tearDown := newServerEnv(t, 4, protocol, nil, false, true, 0) + netconfig := testnetConfig{ + blocks: 4, + protocol: protocol, + nopruning: true, + } + server, _, tearDown := newClientServerEnv(t, netconfig) defer tearDown() + rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol) + defer closePeer() + bc := server.handler.blockchain // Collect the hashes to request, and the response to expect @@ -328,8 +374,8 @@ func testGetReceipt(t *testing.T, protocol int) { receipts = append(receipts, rawdb.ReadRawReceipts(server.db, block.Hash(), block.NumberU64())) } // Send the hash request and verify the response - sendRequest(server.peer.app, GetReceiptsMsg, 42, hashes) - if err := expectResponse(server.peer.app, ReceiptsMsg, 42, testBufLimit, receipts); err != nil { + sendRequest(rawPeer.app, GetReceiptsMsg, 42, hashes) + if err := expectResponse(rawPeer.app, ReceiptsMsg, 42, testBufLimit, receipts); err != nil { t.Errorf("receipts mismatch: %v", err) } } @@ -337,12 +383,21 @@ func testGetReceipt(t *testing.T, protocol int) { // Tests that trie merkle proofs can be retrieved func TestGetProofsLes2(t *testing.T) { testGetProofs(t, 2) } func TestGetProofsLes3(t *testing.T) { testGetProofs(t, 3) } +func TestGetProofsLes4(t *testing.T) { testGetProofs(t, 4) } func testGetProofs(t *testing.T, protocol int) { // Assemble the test environment - server, tearDown := newServerEnv(t, 4, protocol, nil, false, true, 0) + netconfig := testnetConfig{ + blocks: 4, + protocol: protocol, + nopruning: true, + } + server, _, tearDown := newClientServerEnv(t, netconfig) defer tearDown() + rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol) + defer closePeer() + bc := server.handler.blockchain var proofreqs []ProofReq @@ -363,8 +418,8 @@ func testGetProofs(t *testing.T, protocol int) { } } // Send the proof request and verify the response - sendRequest(server.peer.app, GetProofsV2Msg, 42, proofreqs) - if err := expectResponse(server.peer.app, ProofsV2Msg, 42, testBufLimit, proofsV2.NodeList()); err != nil { + sendRequest(rawPeer.app, GetProofsV2Msg, 42, proofreqs) + if err := expectResponse(rawPeer.app, ProofsV2Msg, 42, testBufLimit, proofsV2.NodeList()); err != nil { t.Errorf("proofs mismatch: %v", err) } } @@ -372,10 +427,20 @@ func testGetProofs(t *testing.T, protocol int) { // Tests that the stale contract codes can't be retrieved based on account addresses. func TestGetStaleProofLes2(t *testing.T) { testGetStaleProof(t, 2) } func TestGetStaleProofLes3(t *testing.T) { testGetStaleProof(t, 3) } +func TestGetStaleProofLes4(t *testing.T) { testGetStaleProof(t, 4) } func testGetStaleProof(t *testing.T, protocol int) { - server, tearDown := newServerEnv(t, core.TriesInMemory+4, protocol, nil, false, true, 0) + netconfig := testnetConfig{ + blocks: core.TriesInMemory + 4, + protocol: protocol, + nopruning: true, + } + server, _, tearDown := newClientServerEnv(t, netconfig) defer tearDown() + + rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol) + defer closePeer() + bc := server.handler.blockchain check := func(number uint64, wantOK bool) { @@ -387,7 +452,7 @@ func testGetStaleProof(t *testing.T, protocol int) { BHash: header.Hash(), Key: account, } - sendRequest(server.peer.app, GetProofsV2Msg, 42, []*ProofReq{req}) + sendRequest(rawPeer.app, GetProofsV2Msg, 42, []*ProofReq{req}) var expected []rlp.RawValue if wantOK { @@ -396,7 +461,7 @@ func testGetStaleProof(t *testing.T, protocol int) { t.Prove(account, 0, proofsV2) expected = proofsV2.NodeList() } - if err := expectResponse(server.peer.app, ProofsV2Msg, 42, testBufLimit, expected); err != nil { + if err := expectResponse(rawPeer.app, ProofsV2Msg, 42, testBufLimit, expected); err != nil { t.Errorf("codes mismatch: %v", err) } } @@ -408,22 +473,33 @@ func testGetStaleProof(t *testing.T, protocol int) { // Tests that CHT proofs can be correctly retrieved. func TestGetCHTProofsLes2(t *testing.T) { testGetCHTProofs(t, 2) } func TestGetCHTProofsLes3(t *testing.T) { testGetCHTProofs(t, 3) } +func TestGetCHTProofsLes4(t *testing.T) { testGetCHTProofs(t, 4) } func testGetCHTProofs(t *testing.T, protocol int) { - config := light.TestServerIndexerConfig - - waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) { - for { - cs, _, _ := cIndexer.Sections() - if cs >= 1 { - break + var ( + config = light.TestServerIndexerConfig + waitIndexers = func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) { + for { + cs, _, _ := cIndexer.Sections() + if cs >= 1 { + break + } + time.Sleep(10 * time.Millisecond) } - time.Sleep(10 * time.Millisecond) } - } - server, tearDown := newServerEnv(t, int(config.ChtSize+config.ChtConfirms), protocol, waitIndexers, false, true, 0) + netconfig = testnetConfig{ + blocks: int(config.ChtSize + config.ChtConfirms), + protocol: protocol, + indexFn: waitIndexers, + nopruning: true, + } + ) + server, _, tearDown := newClientServerEnv(t, netconfig) defer tearDown() + rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol) + defer closePeer() + bc := server.handler.blockchain // Assemble the proofs from the different protocols @@ -444,34 +520,45 @@ func testGetCHTProofs(t *testing.T, protocol int) { Type: htCanonical, TrieIdx: 0, Key: key, - AuxReq: auxHeader, + AuxReq: htAuxHeader, }} // Send the proof request and verify the response - sendRequest(server.peer.app, GetHelperTrieProofsMsg, 42, requestsV2) - if err := expectResponse(server.peer.app, HelperTrieProofsMsg, 42, testBufLimit, proofsV2); err != nil { + sendRequest(rawPeer.app, GetHelperTrieProofsMsg, 42, requestsV2) + if err := expectResponse(rawPeer.app, HelperTrieProofsMsg, 42, testBufLimit, proofsV2); err != nil { t.Errorf("proofs mismatch: %v", err) } } func TestGetBloombitsProofsLes2(t *testing.T) { testGetBloombitsProofs(t, 2) } func TestGetBloombitsProofsLes3(t *testing.T) { testGetBloombitsProofs(t, 3) } +func TestGetBloombitsProofsLes4(t *testing.T) { testGetBloombitsProofs(t, 4) } // Tests that bloombits proofs can be correctly retrieved. func testGetBloombitsProofs(t *testing.T, protocol int) { - config := light.TestServerIndexerConfig - - waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) { - for { - bts, _, _ := btIndexer.Sections() - if bts >= 1 { - break + var ( + config = light.TestServerIndexerConfig + waitIndexers = func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) { + for { + bts, _, _ := btIndexer.Sections() + if bts >= 1 { + break + } + time.Sleep(10 * time.Millisecond) } - time.Sleep(10 * time.Millisecond) } - } - server, tearDown := newServerEnv(t, int(config.BloomTrieSize+config.BloomTrieConfirms), protocol, waitIndexers, false, true, 0) + netconfig = testnetConfig{ + blocks: int(config.BloomTrieSize + config.BloomTrieConfirms), + protocol: protocol, + indexFn: waitIndexers, + nopruning: true, + } + ) + server, _, tearDown := newClientServerEnv(t, netconfig) defer tearDown() + rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol) + defer closePeer() + bc := server.handler.blockchain // Request and verify each bit of the bloom bits proofs @@ -495,19 +582,28 @@ func testGetBloombitsProofs(t *testing.T, protocol int) { trie.Prove(key, 0, &proofs.Proofs) // Send the proof request and verify the response - sendRequest(server.peer.app, GetHelperTrieProofsMsg, 42, requests) - if err := expectResponse(server.peer.app, HelperTrieProofsMsg, 42, testBufLimit, proofs); err != nil { + sendRequest(rawPeer.app, GetHelperTrieProofsMsg, 42, requests) + if err := expectResponse(rawPeer.app, HelperTrieProofsMsg, 42, testBufLimit, proofs); err != nil { t.Errorf("bit %d: proofs mismatch: %v", bit, err) } } } -func TestTransactionStatusLes2(t *testing.T) { testTransactionStatus(t, 2) } -func TestTransactionStatusLes3(t *testing.T) { testTransactionStatus(t, 3) } +func TestTransactionStatusLes2(t *testing.T) { testTransactionStatus(t, lpv2) } +func TestTransactionStatusLes3(t *testing.T) { testTransactionStatus(t, lpv3) } +func TestTransactionStatusLes4(t *testing.T) { testTransactionStatus(t, lpv4) } func testTransactionStatus(t *testing.T, protocol int) { - server, tearDown := newServerEnv(t, 0, protocol, nil, false, true, 0) + netconfig := testnetConfig{ + protocol: protocol, + nopruning: true, + } + server, _, tearDown := newClientServerEnv(t, netconfig) defer tearDown() + + rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol) + defer closePeer() + server.handler.addTxsSync = true chain := server.handler.blockchain @@ -517,11 +613,11 @@ func testTransactionStatus(t *testing.T, protocol int) { test := func(tx *types.Transaction, send bool, expStatus light.TxStatus) { reqID++ if send { - sendRequest(server.peer.app, SendTxV2Msg, reqID, types.Transactions{tx}) + sendRequest(rawPeer.app, SendTxV2Msg, reqID, types.Transactions{tx}) } else { - sendRequest(server.peer.app, GetTxStatusMsg, reqID, []common.Hash{tx.Hash()}) + sendRequest(rawPeer.app, GetTxStatusMsg, reqID, []common.Hash{tx.Hash()}) } - if err := expectResponse(server.peer.app, TxStatusMsg, reqID, testBufLimit, []light.TxStatus{expStatus}); err != nil { + if err := expectResponse(rawPeer.app, TxStatusMsg, reqID, testBufLimit, []light.TxStatus{expStatus}); err != nil { t.Errorf("transaction status mismatch") } } @@ -563,7 +659,7 @@ func testTransactionStatus(t *testing.T, protocol int) { t.Fatalf("pending count mismatch: have %d, want 1", pending) } // Discard new block announcement - msg, _ := server.peer.app.ReadMsg() + msg, _ := rawPeer.app.ReadMsg() msg.Discard() // check if their status is included now @@ -588,7 +684,7 @@ func testTransactionStatus(t *testing.T, protocol int) { t.Fatalf("pending count mismatch: have %d, want 3", pending) } // Discard new block announcement - msg, _ = server.peer.app.ReadMsg() + msg, _ = rawPeer.app.ReadMsg() msg.Discard() // check if their status is pending again @@ -596,11 +692,23 @@ func testTransactionStatus(t *testing.T, protocol int) { test(tx2, false, light.TxStatus{Status: core.TxStatusPending}) } -func TestStopResumeLes3(t *testing.T) { - server, tearDown := newServerEnv(t, 0, 3, nil, true, true, testBufLimit/10) +func TestStopResumeLES3(t *testing.T) { testStopResume(t, lpv3) } +func TestStopResumeLES4(t *testing.T) { testStopResume(t, lpv4) } + +func testStopResume(t *testing.T, protocol int) { + netconfig := testnetConfig{ + protocol: protocol, + simClock: true, + nopruning: true, + } + server, _, tearDown := newClientServerEnv(t, netconfig) defer tearDown() server.handler.server.costTracker.testing = true + server.handler.server.costTracker.testCostList = testCostList(testBufLimit / 10) + + rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol) + defer closePeer() var ( reqID uint64 @@ -610,14 +718,14 @@ func TestStopResumeLes3(t *testing.T) { header := server.handler.blockchain.CurrentHeader() req := func() { reqID++ - sendRequest(server.peer.app, GetBlockHeadersMsg, reqID, &getBlockHeadersData{Origin: hashOrNumber{Hash: header.Hash()}, Amount: 1}) + sendRequest(rawPeer.app, GetBlockHeadersMsg, reqID, &GetBlockHeadersData{Origin: hashOrNumber{Hash: header.Hash()}, Amount: 1}) } for i := 1; i <= 5; i++ { // send requests while we still have enough buffer and expect a response for expBuf >= testCost { req() expBuf -= testCost - if err := expectResponse(server.peer.app, BlockHeadersMsg, reqID, expBuf, []*types.Header{header}); err != nil { + if err := expectResponse(rawPeer.app, BlockHeadersMsg, reqID, expBuf, []*types.Header{header}); err != nil { t.Errorf("expected response and failed: %v", err) } } @@ -627,7 +735,7 @@ func TestStopResumeLes3(t *testing.T) { req() c-- } - if err := p2p.ExpectMsg(server.peer.app, StopMsg, nil); err != nil { + if err := p2p.ExpectMsg(rawPeer.app, StopMsg, nil); err != nil { t.Errorf("expected StopMsg and failed: %v", err) } // wait until the buffer is recharged by half of the limit @@ -636,7 +744,7 @@ func TestStopResumeLes3(t *testing.T) { // expect a ResumeMsg with the partially recharged buffer value expBuf += testBufRecharge * wait - if err := p2p.ExpectMsg(server.peer.app, ResumeMsg, expBuf); err != nil { + if err := p2p.ExpectMsg(rawPeer.app, ResumeMsg, expBuf); err != nil { t.Errorf("expected ResumeMsg and failed: %v", err) } } diff --git a/les/lespay/server/prioritypool.go b/les/lespay/server/prioritypool.go deleted file mode 100644 index c0c33840ca..0000000000 --- a/les/lespay/server/prioritypool.go +++ /dev/null @@ -1,503 +0,0 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package server - -import ( - "math" - "reflect" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/common/prque" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/p2p/nodestate" -) - -const ( - lazyQueueRefresh = time.Second * 10 // refresh period of the active queue -) - -// PriorityPoolSetup contains node state flags and fields used by PriorityPool -// Note: ActiveFlag and InactiveFlag can be controlled both externally and by the pool, -// see PriorityPool description for details. -type PriorityPoolSetup struct { - // controlled by PriorityPool - ActiveFlag, InactiveFlag nodestate.Flags - CapacityField, ppNodeInfoField nodestate.Field - // external connections - updateFlag nodestate.Flags - priorityField nodestate.Field -} - -// NewPriorityPoolSetup creates a new PriorityPoolSetup and initializes the fields -// and flags controlled by PriorityPool -func NewPriorityPoolSetup(setup *nodestate.Setup) PriorityPoolSetup { - return PriorityPoolSetup{ - ActiveFlag: setup.NewFlag("active"), - InactiveFlag: setup.NewFlag("inactive"), - CapacityField: setup.NewField("capacity", reflect.TypeOf(uint64(0))), - ppNodeInfoField: setup.NewField("ppNodeInfo", reflect.TypeOf(&ppNodeInfo{})), - } -} - -// Connect sets the fields and flags used by PriorityPool as an input -func (pps *PriorityPoolSetup) Connect(priorityField nodestate.Field, updateFlag nodestate.Flags) { - pps.priorityField = priorityField // should implement nodePriority - pps.updateFlag = updateFlag // triggers an immediate priority update -} - -// PriorityPool handles a set of nodes where each node has a capacity (a scalar value) -// and a priority (which can change over time and can also depend on the capacity). -// A node is active if it has at least the necessary minimal amount of capacity while -// inactive nodes have 0 capacity (values between 0 and the minimum are not allowed). -// The pool ensures that the number and total capacity of all active nodes are limited -// and the highest priority nodes are active at all times (limits can be changed -// during operation with immediate effect). -// -// When activating clients a priority bias is applied in favor of the already active -// nodes in order to avoid nodes quickly alternating between active and inactive states -// when their priorities are close to each other. The bias is specified in terms of -// duration (time) because priorities are expected to usually get lower over time and -// therefore a future minimum prediction (see EstMinPriority) should monotonously -// decrease with the specified time parameter. -// This time bias can be interpreted as minimum expected active time at the given -// capacity (if the threshold priority stays the same). -// -// Nodes in the pool always have either InactiveFlag or ActiveFlag set. A new node is -// added to the pool by externally setting InactiveFlag. PriorityPool can switch a node -// between InactiveFlag and ActiveFlag at any time. Nodes can be removed from the pool -// by externally resetting both flags. ActiveFlag should not be set externally. -// -// The highest priority nodes in "inactive" state are moved to "active" state as soon as -// the minimum capacity can be granted for them. The capacity of lower priority active -// nodes is reduced or they are demoted to "inactive" state if their priority is -// insufficient even at minimal capacity. -type PriorityPool struct { - PriorityPoolSetup - ns *nodestate.NodeStateMachine - clock mclock.Clock - lock sync.Mutex - activeQueue *prque.LazyQueue - inactiveQueue *prque.Prque - changed []*ppNodeInfo - activeCount, activeCap uint64 - maxCount, maxCap uint64 - minCap uint64 - activeBias time.Duration - capacityStepDiv uint64 -} - -// nodePriority interface provides current and estimated future priorities on demand -type nodePriority interface { - // Priority should return the current priority of the node (higher is better) - Priority(now mclock.AbsTime, cap uint64) int64 - // EstMinPriority should return a lower estimate for the minimum of the node priority - // value starting from the current moment until the given time. If the priority goes - // under the returned estimate before the specified moment then it is the caller's - // responsibility to signal with updateFlag. - EstMinPriority(until mclock.AbsTime, cap uint64, update bool) int64 -} - -// ppNodeInfo is the internal node descriptor of PriorityPool -type ppNodeInfo struct { - nodePriority nodePriority - node *enode.Node - connected bool - capacity, origCap uint64 - bias time.Duration - forced, changed bool - activeIndex, inactiveIndex int -} - -// NewPriorityPool creates a new PriorityPool -func NewPriorityPool(ns *nodestate.NodeStateMachine, setup PriorityPoolSetup, clock mclock.Clock, minCap uint64, activeBias time.Duration, capacityStepDiv uint64) *PriorityPool { - pp := &PriorityPool{ - ns: ns, - PriorityPoolSetup: setup, - clock: clock, - activeQueue: prque.NewLazyQueue(activeSetIndex, activePriority, activeMaxPriority, clock, lazyQueueRefresh), - inactiveQueue: prque.New(inactiveSetIndex), - minCap: minCap, - activeBias: activeBias, - capacityStepDiv: capacityStepDiv, - } - - ns.SubscribeField(pp.priorityField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) { - if newValue != nil { - c := &ppNodeInfo{ - node: node, - nodePriority: newValue.(nodePriority), - activeIndex: -1, - inactiveIndex: -1, - } - ns.SetFieldSub(node, pp.ppNodeInfoField, c) - } else { - ns.SetStateSub(node, nodestate.Flags{}, pp.ActiveFlag.Or(pp.InactiveFlag), 0) - if n, _ := pp.ns.GetField(node, pp.ppNodeInfoField).(*ppNodeInfo); n != nil { - pp.disconnectedNode(n) - } - ns.SetFieldSub(node, pp.CapacityField, nil) - ns.SetFieldSub(node, pp.ppNodeInfoField, nil) - } - }) - ns.SubscribeState(pp.ActiveFlag.Or(pp.InactiveFlag), func(node *enode.Node, oldState, newState nodestate.Flags) { - if c, _ := pp.ns.GetField(node, pp.ppNodeInfoField).(*ppNodeInfo); c != nil { - if oldState.IsEmpty() { - pp.connectedNode(c) - } - if newState.IsEmpty() { - pp.disconnectedNode(c) - } - } - }) - ns.SubscribeState(pp.updateFlag, func(node *enode.Node, oldState, newState nodestate.Flags) { - if !newState.IsEmpty() { - pp.updatePriority(node) - } - }) - return pp -} - -// RequestCapacity checks whether changing the capacity of a node to the given target -// is possible (bias is applied in favor of other active nodes if the target is higher -// than the current capacity). -// If setCap is true then it also performs the change if possible. The function returns -// the minimum priority needed to do the change and whether it is currently allowed. -// If setCap and allowed are both true then the caller can assume that the change was -// successful. -// Note: priorityField should always be set before calling RequestCapacity. If setCap -// is false then both InactiveFlag and ActiveFlag can be unset and they are not changed -// by this function call either. -// Note 2: this function should run inside a NodeStateMachine operation -func (pp *PriorityPool) RequestCapacity(node *enode.Node, targetCap uint64, bias time.Duration, setCap bool) (minPriority int64, allowed bool) { - pp.lock.Lock() - pp.activeQueue.Refresh() - var updates []capUpdate - defer func() { - pp.lock.Unlock() - pp.updateFlags(updates) - }() - - if targetCap < pp.minCap { - targetCap = pp.minCap - } - c, _ := pp.ns.GetField(node, pp.ppNodeInfoField).(*ppNodeInfo) - if c == nil { - log.Error("RequestCapacity called for unknown node", "id", node.ID()) - return math.MaxInt64, false - } - var priority int64 - if targetCap > c.capacity { - priority = c.nodePriority.EstMinPriority(pp.clock.Now()+mclock.AbsTime(bias), targetCap, false) - } else { - priority = c.nodePriority.Priority(pp.clock.Now(), targetCap) - } - pp.markForChange(c) - pp.setCapacity(c, targetCap) - c.forced = true - pp.activeQueue.Remove(c.activeIndex) - pp.inactiveQueue.Remove(c.inactiveIndex) - pp.activeQueue.Push(c) - minPriority = pp.enforceLimits() - // if capacity update is possible now then minPriority == math.MinInt64 - // if it is not possible at all then minPriority == math.MaxInt64 - allowed = priority > minPriority - updates = pp.finalizeChanges(setCap && allowed) - return -} - -// SetLimits sets the maximum number and total capacity of simultaneously active nodes -func (pp *PriorityPool) SetLimits(maxCount, maxCap uint64) { - pp.lock.Lock() - pp.activeQueue.Refresh() - var updates []capUpdate - defer func() { - pp.lock.Unlock() - pp.ns.Operation(func() { pp.updateFlags(updates) }) - }() - - inc := (maxCount > pp.maxCount) || (maxCap > pp.maxCap) - dec := (maxCount < pp.maxCount) || (maxCap < pp.maxCap) - pp.maxCount, pp.maxCap = maxCount, maxCap - if dec { - pp.enforceLimits() - updates = pp.finalizeChanges(true) - } - if inc { - updates = pp.tryActivate() - } -} - -// SetActiveBias sets the bias applied when trying to activate inactive nodes -func (pp *PriorityPool) SetActiveBias(bias time.Duration) { - pp.lock.Lock() - defer pp.lock.Unlock() - - pp.activeBias = bias - pp.tryActivate() -} - -// Active returns the number and total capacity of currently active nodes -func (pp *PriorityPool) Active() (uint64, uint64) { - pp.lock.Lock() - defer pp.lock.Unlock() - - return pp.activeCount, pp.activeCap -} - -// inactiveSetIndex callback updates ppNodeInfo item index in inactiveQueue -func inactiveSetIndex(a interface{}, index int) { - a.(*ppNodeInfo).inactiveIndex = index -} - -// activeSetIndex callback updates ppNodeInfo item index in activeQueue -func activeSetIndex(a interface{}, index int) { - a.(*ppNodeInfo).activeIndex = index -} - -// invertPriority inverts a priority value. The active queue uses inverted priorities -// because the node on the top is the first to be deactivated. -func invertPriority(p int64) int64 { - if p == math.MinInt64 { - return math.MaxInt64 - } - return -p -} - -// activePriority callback returns actual priority of ppNodeInfo item in activeQueue -func activePriority(a interface{}, now mclock.AbsTime) int64 { - c := a.(*ppNodeInfo) - if c.forced { - return math.MinInt64 - } - if c.bias == 0 { - return invertPriority(c.nodePriority.Priority(now, c.capacity)) - } else { - return invertPriority(c.nodePriority.EstMinPriority(now+mclock.AbsTime(c.bias), c.capacity, true)) - } -} - -// activeMaxPriority callback returns estimated maximum priority of ppNodeInfo item in activeQueue -func activeMaxPriority(a interface{}, until mclock.AbsTime) int64 { - c := a.(*ppNodeInfo) - if c.forced { - return math.MinInt64 - } - return invertPriority(c.nodePriority.EstMinPriority(until+mclock.AbsTime(c.bias), c.capacity, false)) -} - -// inactivePriority callback returns actual priority of ppNodeInfo item in inactiveQueue -func (pp *PriorityPool) inactivePriority(p *ppNodeInfo) int64 { - return p.nodePriority.Priority(pp.clock.Now(), pp.minCap) -} - -// connectedNode is called when a new node has been added to the pool (InactiveFlag set) -// Note: this function should run inside a NodeStateMachine operation -func (pp *PriorityPool) connectedNode(c *ppNodeInfo) { - pp.lock.Lock() - pp.activeQueue.Refresh() - var updates []capUpdate - defer func() { - pp.lock.Unlock() - pp.updateFlags(updates) - }() - - if c.connected { - return - } - c.connected = true - pp.inactiveQueue.Push(c, pp.inactivePriority(c)) - updates = pp.tryActivate() -} - -// disconnectedNode is called when a node has been removed from the pool (both InactiveFlag -// and ActiveFlag reset) -// Note: this function should run inside a NodeStateMachine operation -func (pp *PriorityPool) disconnectedNode(c *ppNodeInfo) { - pp.lock.Lock() - pp.activeQueue.Refresh() - var updates []capUpdate - defer func() { - pp.lock.Unlock() - pp.updateFlags(updates) - }() - - if !c.connected { - return - } - c.connected = false - pp.activeQueue.Remove(c.activeIndex) - pp.inactiveQueue.Remove(c.inactiveIndex) - if c.capacity != 0 { - pp.setCapacity(c, 0) - updates = pp.tryActivate() - } -} - -// markForChange internally puts a node in a temporary state that can either be reverted -// or confirmed later. This temporary state allows changing the capacity of a node and -// moving it between the active and inactive queue. ActiveFlag/InactiveFlag and -// CapacityField are not changed while the changes are still temporary. -func (pp *PriorityPool) markForChange(c *ppNodeInfo) { - if c.changed { - return - } - c.changed = true - c.origCap = c.capacity - pp.changed = append(pp.changed, c) -} - -// setCapacity changes the capacity of a node and adjusts activeCap and activeCount -// accordingly. Note that this change is performed in the temporary state so it should -// be called after markForChange and before finalizeChanges. -func (pp *PriorityPool) setCapacity(n *ppNodeInfo, cap uint64) { - pp.activeCap += cap - n.capacity - if n.capacity == 0 { - pp.activeCount++ - } - if cap == 0 { - pp.activeCount-- - } - n.capacity = cap -} - -// enforceLimits enforces active node count and total capacity limits. It returns the -// lowest active node priority. Note that this function is performed on the temporary -// internal state. -func (pp *PriorityPool) enforceLimits() int64 { - if pp.activeCap <= pp.maxCap && pp.activeCount <= pp.maxCount { - return math.MinInt64 - } - var maxActivePriority int64 - pp.activeQueue.MultiPop(func(data interface{}, priority int64) bool { - c := data.(*ppNodeInfo) - pp.markForChange(c) - maxActivePriority = priority - if c.capacity == pp.minCap { - pp.setCapacity(c, 0) - } else { - sub := c.capacity / pp.capacityStepDiv - if c.capacity-sub < pp.minCap { - sub = c.capacity - pp.minCap - } - pp.setCapacity(c, c.capacity-sub) - pp.activeQueue.Push(c) - } - return pp.activeCap > pp.maxCap || pp.activeCount > pp.maxCount - }) - return invertPriority(maxActivePriority) -} - -// finalizeChanges either commits or reverts temporary changes. The necessary capacity -// field and according flag updates are not performed here but returned in a list because -// they should be performed while the mutex is not held. -func (pp *PriorityPool) finalizeChanges(commit bool) (updates []capUpdate) { - for _, c := range pp.changed { - // always remove and push back in order to update biased/forced priority - pp.activeQueue.Remove(c.activeIndex) - pp.inactiveQueue.Remove(c.inactiveIndex) - c.bias = 0 - c.forced = false - c.changed = false - if !commit { - pp.setCapacity(c, c.origCap) - } - if c.connected { - if c.capacity != 0 { - pp.activeQueue.Push(c) - } else { - pp.inactiveQueue.Push(c, pp.inactivePriority(c)) - } - if c.capacity != c.origCap && commit { - updates = append(updates, capUpdate{c.node, c.origCap, c.capacity}) - } - } - c.origCap = 0 - } - pp.changed = nil - return -} - -// capUpdate describes a CapacityField and ActiveFlag/InactiveFlag update -type capUpdate struct { - node *enode.Node - oldCap, newCap uint64 -} - -// updateFlags performs CapacityField and ActiveFlag/InactiveFlag updates while the -// pool mutex is not held -// Note: this function should run inside a NodeStateMachine operation -func (pp *PriorityPool) updateFlags(updates []capUpdate) { - for _, f := range updates { - if f.oldCap == 0 { - pp.ns.SetStateSub(f.node, pp.ActiveFlag, pp.InactiveFlag, 0) - } - if f.newCap == 0 { - pp.ns.SetStateSub(f.node, pp.InactiveFlag, pp.ActiveFlag, 0) - pp.ns.SetFieldSub(f.node, pp.CapacityField, nil) - } else { - pp.ns.SetFieldSub(f.node, pp.CapacityField, f.newCap) - } - } -} - -// tryActivate tries to activate inactive nodes if possible -func (pp *PriorityPool) tryActivate() []capUpdate { - var commit bool - for pp.inactiveQueue.Size() > 0 { - c := pp.inactiveQueue.PopItem().(*ppNodeInfo) - pp.markForChange(c) - pp.setCapacity(c, pp.minCap) - c.bias = pp.activeBias - pp.activeQueue.Push(c) - pp.enforceLimits() - if c.capacity > 0 { - commit = true - } else { - break - } - } - return pp.finalizeChanges(commit) -} - -// updatePriority gets the current priority value of the given node from the nodePriority -// interface and performs the necessary changes. It is triggered by updateFlag. -// Note: this function should run inside a NodeStateMachine operation -func (pp *PriorityPool) updatePriority(node *enode.Node) { - pp.lock.Lock() - pp.activeQueue.Refresh() - var updates []capUpdate - defer func() { - pp.lock.Unlock() - pp.updateFlags(updates) - }() - - c, _ := pp.ns.GetField(node, pp.ppNodeInfoField).(*ppNodeInfo) - if c == nil || !c.connected { - return - } - pp.activeQueue.Remove(c.activeIndex) - pp.inactiveQueue.Remove(c.inactiveIndex) - if c.capacity != 0 { - pp.activeQueue.Push(c) - } else { - pp.inactiveQueue.Push(c, pp.inactivePriority(c)) - } - updates = pp.tryActivate() -} diff --git a/les/lespay/server/prioritypool_test.go b/les/lespay/server/prioritypool_test.go deleted file mode 100644 index cbb3f5b372..0000000000 --- a/les/lespay/server/prioritypool_test.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package server - -import ( - "math/rand" - "reflect" - "testing" - - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/p2p/enr" - "github.com/ethereum/go-ethereum/p2p/nodestate" -) - -var ( - testSetup = &nodestate.Setup{} - ppTestClientFlag = testSetup.NewFlag("ppTestClientFlag") - ppTestClientField = testSetup.NewField("ppTestClient", reflect.TypeOf(&ppTestClient{})) - ppUpdateFlag = testSetup.NewFlag("ppUpdateFlag") - ppTestSetup = NewPriorityPoolSetup(testSetup) -) - -func init() { - ppTestSetup.Connect(ppTestClientField, ppUpdateFlag) -} - -const ( - testCapacityStepDiv = 100 - testCapacityToleranceDiv = 10 -) - -type ppTestClient struct { - node *enode.Node - balance, cap uint64 -} - -func (c *ppTestClient) Priority(now mclock.AbsTime, cap uint64) int64 { - return int64(c.balance / cap) -} - -func (c *ppTestClient) EstMinPriority(until mclock.AbsTime, cap uint64, update bool) int64 { - return int64(c.balance / cap) -} - -func TestPriorityPool(t *testing.T) { - clock := &mclock.Simulated{} - ns := nodestate.NewNodeStateMachine(nil, nil, clock, testSetup) - - ns.SubscribeField(ppTestSetup.CapacityField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) { - if n := ns.GetField(node, ppTestSetup.priorityField); n != nil { - c := n.(*ppTestClient) - c.cap = newValue.(uint64) - } - }) - pp := NewPriorityPool(ns, ppTestSetup, clock, 100, 0, testCapacityStepDiv) - ns.Start() - pp.SetLimits(100, 1000000) - clients := make([]*ppTestClient, 100) - raise := func(c *ppTestClient) { - for { - var ok bool - ns.Operation(func() { - _, ok = pp.RequestCapacity(c.node, c.cap+c.cap/testCapacityStepDiv, 0, true) - }) - if !ok { - return - } - } - } - var sumBalance uint64 - check := func(c *ppTestClient) { - expCap := 1000000 * c.balance / sumBalance - capTol := expCap / testCapacityToleranceDiv - if c.cap < expCap-capTol || c.cap > expCap+capTol { - t.Errorf("Wrong node capacity (expected %d, got %d)", expCap, c.cap) - } - } - - for i := range clients { - c := &ppTestClient{ - node: enode.SignNull(&enr.Record{}, enode.ID{byte(i)}), - balance: 1000000000, - cap: 1000, - } - sumBalance += c.balance - clients[i] = c - ns.SetState(c.node, ppTestClientFlag, nodestate.Flags{}, 0) - ns.SetField(c.node, ppTestSetup.priorityField, c) - ns.SetState(c.node, ppTestSetup.InactiveFlag, nodestate.Flags{}, 0) - raise(c) - check(c) - } - - for count := 0; count < 100; count++ { - c := clients[rand.Intn(len(clients))] - oldBalance := c.balance - c.balance = uint64(rand.Int63n(1000000000) + 1000000000) - sumBalance += c.balance - oldBalance - pp.ns.SetState(c.node, ppUpdateFlag, nodestate.Flags{}, 0) - pp.ns.SetState(c.node, nodestate.Flags{}, ppUpdateFlag, 0) - if c.balance > oldBalance { - raise(c) - } else { - for _, c := range clients { - raise(c) - } - } - for _, c := range clients { - check(c) - } - } - - ns.Stop() -} diff --git a/les/metrics.go b/les/metrics.go index 9a79fd1bbd..d356326b76 100644 --- a/les/metrics.go +++ b/les/metrics.go @@ -75,7 +75,6 @@ var ( totalCapacityGauge = metrics.NewRegisteredGauge("les/server/totalCapacity", nil) totalRechargeGauge = metrics.NewRegisteredGauge("les/server/totalRecharge", nil) - totalConnectedGauge = metrics.NewRegisteredGauge("les/server/totalConnected", nil) blockProcessingTimer = metrics.NewRegisteredTimer("les/server/blockProcessingTime", nil) requestServedMeter = metrics.NewRegisteredMeter("les/server/req/avgServedTime", nil) @@ -98,12 +97,8 @@ var ( sqServedGauge = metrics.NewRegisteredGauge("les/server/servingQueue/served", nil) sqQueuedGauge = metrics.NewRegisteredGauge("les/server/servingQueue/queued", nil) - clientConnectedMeter = metrics.NewRegisteredMeter("les/server/clientEvent/connected", nil) - clientActivatedMeter = metrics.NewRegisteredMeter("les/server/clientEvent/activated", nil) - clientDeactivatedMeter = metrics.NewRegisteredMeter("les/server/clientEvent/deactivated", nil) - clientDisconnectedMeter = metrics.NewRegisteredMeter("les/server/clientEvent/disconnected", nil) - clientFreezeMeter = metrics.NewRegisteredMeter("les/server/clientEvent/freeze", nil) - clientErrorMeter = metrics.NewRegisteredMeter("les/server/clientEvent/error", nil) + clientFreezeMeter = metrics.NewRegisteredMeter("les/server/clientEvent/freeze", nil) + clientErrorMeter = metrics.NewRegisteredMeter("les/server/clientEvent/error", nil) requestRTT = metrics.NewRegisteredTimer("les/client/req/rtt", nil) requestSendDelay = metrics.NewRegisteredTimer("les/client/req/sendDelay", nil) diff --git a/les/odr.go b/les/odr.go index f8469cc103..d45c6a1a5d 100644 --- a/les/odr.go +++ b/les/odr.go @@ -18,13 +18,13 @@ package les import ( "context" + "sort" "time" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/light" - "github.com/ethereum/go-ethereum/log" ) // LesOdr implements light.OdrBackend @@ -32,14 +32,16 @@ type LesOdr struct { db ethdb.Database indexerConfig *light.IndexerConfig chtIndexer, bloomTrieIndexer, bloomIndexer *core.ChainIndexer + peers *serverPeerSet retriever *retrieveManager stop chan struct{} } -func NewLesOdr(db ethdb.Database, config *light.IndexerConfig, retriever *retrieveManager) *LesOdr { +func NewLesOdr(db ethdb.Database, config *light.IndexerConfig, peers *serverPeerSet, retriever *retrieveManager) *LesOdr { return &LesOdr{ db: db, indexerConfig: config, + peers: peers, retriever: retriever, stop: make(chan struct{}), } @@ -83,7 +85,8 @@ func (odr *LesOdr) IndexerConfig() *light.IndexerConfig { } const ( - MsgBlockBodies = iota + MsgBlockHeaders = iota + MsgBlockBodies MsgCode MsgReceipts MsgProofsV2 @@ -98,7 +101,101 @@ type Msg struct { Obj interface{} } -// Retrieve tries to fetch an object from the LES network. +// peerByTxHistory is a heap.Interface implementation which can sort +// the peerset by transaction history. +type peerByTxHistory []*serverPeer + +func (h peerByTxHistory) Len() int { return len(h) } +func (h peerByTxHistory) Less(i, j int) bool { + if h[i].txHistory == txIndexUnlimited { + return false + } + if h[j].txHistory == txIndexUnlimited { + return true + } + return h[i].txHistory < h[j].txHistory +} +func (h peerByTxHistory) Swap(i, j int) { h[i], h[j] = h[j], h[i] } + +const ( + maxTxStatusRetry = 3 // The maximum retrys will be made for tx status request. + maxTxStatusCandidates = 5 // The maximum les servers the tx status requests will be sent to. +) + +// RetrieveTxStatus retrieves the transaction status from the LES network. +// There is no guarantee in the LES protocol that the mined transaction will +// be retrieved back for sure because of different reasons(the transaction +// is unindexed, the malicous server doesn't reply it deliberately, etc). +// Therefore, unretrieved transactions(UNKNOWN) will receive a certain number +// of retries, thus giving a weak guarantee. +func (odr *LesOdr) RetrieveTxStatus(ctx context.Context, req *light.TxStatusRequest) error { + // Sort according to the transaction history supported by the peer and + // select the peers with longest history. + var ( + retries int + peers []*serverPeer + missing = len(req.Hashes) + result = make([]light.TxStatus, len(req.Hashes)) + canSend = make(map[string]bool) + ) + for _, peer := range odr.peers.allPeers() { + if peer.txHistory == txIndexDisabled { + continue + } + peers = append(peers, peer) + } + sort.Sort(sort.Reverse(peerByTxHistory(peers))) + for i := 0; i < maxTxStatusCandidates && i < len(peers); i++ { + canSend[peers[i].id] = true + } + // Send out the request and assemble the result. + for { + if retries >= maxTxStatusRetry || len(canSend) == 0 { + break + } + var ( + // Deep copy the request, so that the partial result won't be mixed. + req = &TxStatusRequest{Hashes: req.Hashes} + id = genReqID() + distreq = &distReq{ + getCost: func(dp distPeer) uint64 { return req.GetCost(dp.(*serverPeer)) }, + canSend: func(dp distPeer) bool { return canSend[dp.(*serverPeer).id] }, + request: func(dp distPeer) func() { + p := dp.(*serverPeer) + p.fcServer.QueuedRequest(id, req.GetCost(p)) + delete(canSend, p.id) + return func() { req.Request(id, p) } + }, + } + ) + if err := odr.retriever.retrieve(ctx, id, distreq, func(p distPeer, msg *Msg) error { return req.Validate(odr.db, msg) }, odr.stop); err != nil { + return err + } + // Collect the response and assemble them to the final result. + // All the response is not verifiable, so always pick the first + // one we get. + for index, status := range req.Status { + if result[index].Status != core.TxStatusUnknown { + continue + } + if status.Status == core.TxStatusUnknown { + continue + } + result[index], missing = status, missing-1 + } + // Abort the procedure if all the status are retrieved + if missing == 0 { + break + } + retries += 1 + } + req.Status = result + return nil +} + +// Retrieve tries to fetch an object from the LES network. It's a common API +// for most of the LES requests except for the TxStatusRequest which needs +// the additional retry mechanism. // If the network retrieval was successful, it stores the object in local db. func (odr *LesOdr) Retrieve(ctx context.Context, req light.OdrRequest) (err error) { lreq := LesRequest(req) @@ -122,13 +219,17 @@ func (odr *LesOdr) Retrieve(ctx context.Context, req light.OdrRequest) (err erro return func() { lreq.Request(reqID, p) } }, } - sent := mclock.Now() - if err = odr.retriever.retrieve(ctx, reqID, rq, func(p distPeer, msg *Msg) error { return lreq.Validate(odr.db, msg) }, odr.stop); err == nil { - // retrieved from network, store in db - req.StoreResult(odr.db) + + defer func(sent mclock.AbsTime) { + if err != nil { + return + } requestRTT.Update(time.Duration(mclock.Now() - sent)) - } else { - log.Debug("Failed to retrieve data from network", "err", err) + }(mclock.Now()) + + if err := odr.retriever.retrieve(ctx, reqID, rq, func(p distPeer, msg *Msg) error { return lreq.Validate(odr.db, msg) }, odr.stop); err != nil { + return err } - return + req.StoreResult(odr.db) + return nil } diff --git a/les/odr_requests.go b/les/odr_requests.go index 3cc55c98d8..d548fb1ee0 100644 --- a/les/odr_requests.go +++ b/les/odr_requests.go @@ -116,7 +116,7 @@ func (r *BlockRequest) Validate(db ethdb.Database, msg *Msg) error { if r.Header == nil { return errHeaderUnavailable } - if r.Header.TxHash != types.DeriveSha(types.Transactions(body.Transactions), new(trie.Trie)) { + if r.Header.TxHash != types.DeriveSha(types.Transactions(body.Transactions), trie.NewStackTrie(nil)) { return errTxHashMismatch } if r.Header.UncleHash != types.CalcUncleHash(body.Uncles) { @@ -174,7 +174,7 @@ func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error { if r.Header == nil { return errHeaderUnavailable } - if r.Header.ReceiptHash != types.DeriveSha(receipt, new(trie.Trie)) { + if r.Header.ReceiptHash != types.DeriveSha(receipt, trie.NewStackTrie(nil)) { return errReceiptHashMismatch } // Validations passed, store and return @@ -295,10 +295,9 @@ const ( htCanonical = iota // Canonical hash trie htBloomBits // BloomBits trie - // applicable for all helper trie requests - auxRoot = 1 - // applicable for htCanonical - auxHeader = 2 + // helper trie auxiliary types + // htAuxNone = 1 ; deprecated number, used in les2/3 previously. + htAuxHeader = 2 // applicable for htCanonical, requests for relevant headers ) type HelperTrieReq struct { @@ -327,11 +326,7 @@ func (r *ChtRequest) CanSend(peer *serverPeer) bool { peer.lock.RLock() defer peer.lock.RUnlock() - if r.Untrusted { - return peer.headInfo.Number >= r.BlockNum && peer.id == r.PeerId - } else { - return peer.headInfo.Number >= r.Config.ChtConfirms && r.ChtNum <= (peer.headInfo.Number-r.Config.ChtConfirms)/r.Config.ChtSize - } + return peer.headInfo.Number >= r.Config.ChtConfirms && r.ChtNum <= (peer.headInfo.Number-r.Config.ChtConfirms)/r.Config.ChtSize } // Request sends an ODR request to the LES network (implementation of LesOdrRequest) @@ -343,7 +338,7 @@ func (r *ChtRequest) Request(reqID uint64, peer *serverPeer) error { Type: htCanonical, TrieIdx: r.ChtNum, Key: encNum[:], - AuxReq: auxHeader, + AuxReq: htAuxHeader, } return peer.requestHelperTrieProofs(reqID, []HelperTrieReq{req}) } @@ -370,39 +365,34 @@ func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error { if err := rlp.DecodeBytes(headerEnc, header); err != nil { return errHeaderUnavailable } - // Verify the CHT - // Note: For untrusted CHT request, there is no proof response but - // header data. - var node light.ChtNode - if !r.Untrusted { - var encNumber [8]byte - binary.BigEndian.PutUint64(encNumber[:], r.BlockNum) - - reads := &readTraceDB{db: nodeSet} - value, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads) - if err != nil { - return fmt.Errorf("merkle proof verification failed: %v", err) - } - if len(reads.reads) != nodeSet.KeyCount() { - return errUselessNodes - } + var ( + node light.ChtNode + encNumber [8]byte + ) + binary.BigEndian.PutUint64(encNumber[:], r.BlockNum) - if err := rlp.DecodeBytes(value, &node); err != nil { - return err - } - if node.Hash != header.Hash() { - return errCHTHashMismatch - } - if r.BlockNum != header.Number.Uint64() { - return errCHTNumberMismatch - } + reads := &readTraceDB{db: nodeSet} + value, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads) + if err != nil { + return fmt.Errorf("merkle proof verification failed: %v", err) + } + if len(reads.reads) != nodeSet.KeyCount() { + return errUselessNodes + } + if err := rlp.DecodeBytes(value, &node); err != nil { + return err + } + if node.Hash != header.Hash() { + return errCHTHashMismatch + } + if r.BlockNum != header.Number.Uint64() { + return errCHTNumberMismatch } // Verifications passed, store and return r.Header = header r.Proof = nodeSet - r.Td = node.Td // For untrusted request, td here is nil, todo improve the les/2 protocol - + r.Td = node.Td return nil } @@ -497,7 +487,7 @@ func (r *TxStatusRequest) GetCost(peer *serverPeer) uint64 { // CanSend tells if a certain peer is suitable for serving the given request func (r *TxStatusRequest) CanSend(peer *serverPeer) bool { - return peer.version >= lpv2 + return peer.txHistory != txIndexDisabled } // Request sends an ODR request to the LES network (implementation of LesOdrRequest) @@ -506,13 +496,12 @@ func (r *TxStatusRequest) Request(reqID uint64, peer *serverPeer) error { return peer.requestTxStatus(reqID, r.Hashes) } -// Valid processes an ODR request reply message from the LES network +// Validate 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 *TxStatusRequest) Validate(db ethdb.Database, msg *Msg) error { log.Debug("Validating transaction status", "count", len(r.Hashes)) - // Ensure we have a correct message with a single block body if msg.MsgType != MsgTxStatus { return errInvalidMessageType } diff --git a/les/odr_test.go b/les/odr_test.go index 98e61f41c9..15ef4a0f47 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -19,7 +19,10 @@ package les import ( "bytes" "context" + "crypto/rand" + "fmt" "math/big" + "reflect" "testing" "time" @@ -40,6 +43,7 @@ type odrTestFn func(ctx context.Context, db ethdb.Database, config ctypes.ChainC func TestOdrGetBlockLes2(t *testing.T) { testOdr(t, 2, 1, true, odrGetBlock) } func TestOdrGetBlockLes3(t *testing.T) { testOdr(t, 3, 1, true, odrGetBlock) } +func TestOdrGetBlockLes4(t *testing.T) { testOdr(t, 4, 1, true, odrGetBlock) } func odrGetBlock(ctx context.Context, db ethdb.Database, config ctypes.ChainConfigurator, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { var block *types.Block @@ -57,6 +61,7 @@ func odrGetBlock(ctx context.Context, db ethdb.Database, config ctypes.ChainConf func TestOdrGetReceiptsLes2(t *testing.T) { testOdr(t, 2, 1, true, odrGetReceipts) } func TestOdrGetReceiptsLes3(t *testing.T) { testOdr(t, 3, 1, true, odrGetReceipts) } +func TestOdrGetReceiptsLes4(t *testing.T) { testOdr(t, 4, 1, true, odrGetReceipts) } func odrGetReceipts(ctx context.Context, db ethdb.Database, config ctypes.ChainConfigurator, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { var receipts types.Receipts @@ -78,6 +83,7 @@ func odrGetReceipts(ctx context.Context, db ethdb.Database, config ctypes.ChainC func TestOdrAccountsLes2(t *testing.T) { testOdr(t, 2, 1, true, odrAccounts) } func TestOdrAccountsLes3(t *testing.T) { testOdr(t, 3, 1, true, odrAccounts) } +func TestOdrAccountsLes4(t *testing.T) { testOdr(t, 4, 1, true, odrAccounts) } func odrAccounts(ctx context.Context, db ethdb.Database, config ctypes.ChainConfigurator, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678") @@ -107,6 +113,7 @@ func odrAccounts(ctx context.Context, db ethdb.Database, config ctypes.ChainConf func TestOdrContractCallLes2(t *testing.T) { testOdr(t, 2, 2, true, odrContractCall) } func TestOdrContractCallLes3(t *testing.T) { testOdr(t, 3, 2, true, odrContractCall) } +func TestOdrContractCallLes4(t *testing.T) { testOdr(t, 4, 2, true, odrContractCall) } type callmsg struct { types.Message @@ -128,10 +135,11 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config ctypes.Chain from := statedb.GetOrNewStateObject(bankAddr) from.SetBalance(math.MaxBig256) - msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false)} + msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false)} - context := core.NewEVMContext(msg, header, bc, nil) - vmenv := vm.NewEVM(context, statedb, config, vm.Config{}) + context := core.NewEVMBlockContext(header, bc, nil) + txContext := core.NewEVMTxContext(msg) + vmenv := vm.NewEVM(context, txContext, statedb, config, vm.Config{}) //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) @@ -142,9 +150,10 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config ctypes.Chain header := lc.GetHeaderByHash(bhash) state := light.NewState(ctx, header, lc.Odr()) state.SetBalance(bankAddr, math.MaxBig256) - msg := callmsg{types.NewMessage(bankAddr, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false)} - context := core.NewEVMContext(msg, header, lc, nil) - vmenv := vm.NewEVM(context, state, config, vm.Config{}) + msg := callmsg{types.NewMessage(bankAddr, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false)} + context := core.NewEVMBlockContext(header, lc, nil) + txContext := core.NewEVMTxContext(msg) + vmenv := vm.NewEVM(context, txContext, state, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) result, _ := core.ApplyMessage(vmenv, msg, gp) if state.Error() == nil { @@ -157,6 +166,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config ctypes.Chain func TestOdrTxStatusLes2(t *testing.T) { testOdr(t, 2, 1, false, odrTxStatus) } func TestOdrTxStatusLes3(t *testing.T) { testOdr(t, 3, 1, false, odrTxStatus) } +func TestOdrTxStatusLes4(t *testing.T) { testOdr(t, 4, 1, false, odrTxStatus) } func odrTxStatus(ctx context.Context, db ethdb.Database, config ctypes.ChainConfigurator, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { var txs types.Transactions @@ -183,7 +193,13 @@ func odrTxStatus(ctx context.Context, db ethdb.Database, config ctypes.ChainConf // testOdr tests odr requests whose validation guaranteed by block headers. func testOdr(t *testing.T, protocol int, expFail uint64, checkCached bool, fn odrTestFn) { // Assemble the test environment - server, client, tearDown := newClientServerEnv(t, 4, protocol, nil, nil, 0, false, true, true) + netconfig := testnetConfig{ + blocks: 4, + protocol: protocol, + connect: true, + nopruning: true, + } + server, client, tearDown := newClientServerEnv(t, netconfig) defer tearDown() // Ensure the client has synced all necessary data. @@ -239,3 +255,184 @@ func testOdr(t *testing.T, protocol int, expFail uint64, checkCached bool, fn od test(5) } } + +func TestGetTxStatusFromUnindexedPeersLES4(t *testing.T) { testGetTxStatusFromUnindexedPeers(t, lpv4) } + +func testGetTxStatusFromUnindexedPeers(t *testing.T, protocol int) { + var ( + blocks = 8 + netconfig = testnetConfig{ + blocks: blocks, + protocol: protocol, + nopruning: true, + } + ) + server, client, tearDown := newClientServerEnv(t, netconfig) + defer tearDown() + + // Iterate the chain, create the tx indexes locally + var ( + testHash common.Hash + testStatus light.TxStatus + + txs = make(map[common.Hash]*types.Transaction) // Transaction objects set + blockNumbers = make(map[common.Hash]uint64) // Transaction hash to block number mappings + blockHashes = make(map[common.Hash]common.Hash) // Transaction hash to block hash mappings + intraIndex = make(map[common.Hash]uint64) // Transaction intra-index in block + ) + for number := uint64(1); number < server.backend.Blockchain().CurrentBlock().NumberU64(); number++ { + block := server.backend.Blockchain().GetBlockByNumber(number) + if block == nil { + t.Fatalf("Failed to retrieve block %d", number) + } + for index, tx := range block.Transactions() { + txs[tx.Hash()] = tx + blockNumbers[tx.Hash()] = number + blockHashes[tx.Hash()] = block.Hash() + intraIndex[tx.Hash()] = uint64(index) + + if testHash == (common.Hash{}) { + testHash = tx.Hash() + testStatus = light.TxStatus{ + Status: core.TxStatusIncluded, + Lookup: &rawdb.LegacyTxLookupEntry{ + BlockHash: block.Hash(), + BlockIndex: block.NumberU64(), + Index: uint64(index), + }, + } + } + } + } + // serveMsg processes incoming GetTxStatusMsg and sends the response back. + serveMsg := func(peer *testPeer, txLookup uint64) error { + msg, err := peer.app.ReadMsg() + if err != nil { + return err + } + if msg.Code != GetTxStatusMsg { + return fmt.Errorf("message code mismatch: got %d, expected %d", msg.Code, GetTxStatusMsg) + } + var r GetTxStatusPacket + if err := msg.Decode(&r); err != nil { + return err + } + stats := make([]light.TxStatus, len(r.Hashes)) + for i, hash := range r.Hashes { + number, exist := blockNumbers[hash] + if !exist { + continue // Filter out unknown transactions + } + min := uint64(blocks) - txLookup + if txLookup != txIndexUnlimited && (txLookup == txIndexDisabled || number < min) { + continue // Filter out unindexed transactions + } + stats[i].Status = core.TxStatusIncluded + stats[i].Lookup = &rawdb.LegacyTxLookupEntry{ + BlockHash: blockHashes[hash], + BlockIndex: number, + Index: intraIndex[hash], + } + } + data, _ := rlp.EncodeToBytes(stats) + reply := &reply{peer.app, TxStatusMsg, r.ReqID, data} + reply.send(testBufLimit) + return nil + } + + var testspecs = []struct { + peers int + txLookups []uint64 + txs []common.Hash + results []light.TxStatus + }{ + // Retrieve mined transaction from the empty peerset + { + peers: 0, + txLookups: []uint64{}, + txs: []common.Hash{testHash}, + results: []light.TxStatus{{}}, + }, + // Retrieve unknown transaction from the full peers + { + peers: 3, + txLookups: []uint64{txIndexUnlimited, txIndexUnlimited, txIndexUnlimited}, + txs: []common.Hash{randomHash()}, + results: []light.TxStatus{{}}, + }, + // Retrieve mined transaction from the full peers + { + peers: 3, + txLookups: []uint64{txIndexUnlimited, txIndexUnlimited, txIndexUnlimited}, + txs: []common.Hash{testHash}, + results: []light.TxStatus{testStatus}, + }, + // Retrieve mixed transactions from the full peers + { + peers: 3, + txLookups: []uint64{txIndexUnlimited, txIndexUnlimited, txIndexUnlimited}, + txs: []common.Hash{randomHash(), testHash}, + results: []light.TxStatus{{}, testStatus}, + }, + // Retrieve mixed transactions from unindexed peer(but the target is still available) + { + peers: 3, + txLookups: []uint64{uint64(blocks) - testStatus.Lookup.BlockIndex, uint64(blocks) - testStatus.Lookup.BlockIndex - 1, uint64(blocks) - testStatus.Lookup.BlockIndex - 2}, + txs: []common.Hash{randomHash(), testHash}, + results: []light.TxStatus{{}, testStatus}, + }, + // Retrieve mixed transactions from unindexed peer(but the target is not available) + { + peers: 3, + txLookups: []uint64{uint64(blocks) - testStatus.Lookup.BlockIndex - 1, uint64(blocks) - testStatus.Lookup.BlockIndex - 1, uint64(blocks) - testStatus.Lookup.BlockIndex - 2}, + txs: []common.Hash{randomHash(), testHash}, + results: []light.TxStatus{{}, {}}, + }, + } + for _, testspec := range testspecs { + // Create a bunch of server peers with different tx history + var ( + serverPeers []*testPeer + closeFns []func() + ) + for i := 0; i < testspec.peers; i++ { + peer, closePeer, _ := client.newRawPeer(t, fmt.Sprintf("server-%d", i), protocol, testspec.txLookups[i]) + serverPeers = append(serverPeers, peer) + closeFns = append(closeFns, closePeer) + + // Create a one-time routine for serving message + go func(i int, peer *testPeer) { + serveMsg(peer, testspec.txLookups[i]) + }(i, peer) + } + + // Send out the GetTxStatus requests, compare the result with + // expected value. + r := &light.TxStatusRequest{Hashes: testspec.txs} + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + err := client.handler.backend.odr.RetrieveTxStatus(ctx, r) + if err != nil { + t.Errorf("Failed to retrieve tx status %v", err) + } else { + if !reflect.DeepEqual(testspec.results, r.Status) { + t.Errorf("Result mismatch, diff") + } + } + + // Close all connected peers and start the next round + for _, closeFn := range closeFns { + closeFn() + } + } +} + +// randomHash generates a random blob of data and returns it as a hash. +func randomHash() common.Hash { + var hash common.Hash + if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil { + panic(err) + } + return hash +} diff --git a/les/peer.go b/les/peer.go index abc43dd0e9..9f59517acc 100644 --- a/les/peer.go +++ b/les/peer.go @@ -17,6 +17,7 @@ package les import ( + "crypto/ecdsa" "errors" "fmt" "math/big" @@ -29,14 +30,15 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/les/flowcontrol" - lpc "github.com/ethereum/go-ethereum/les/lespay/client" - lps "github.com/ethereum/go-ethereum/les/lespay/server" "github.com/ethereum/go-ethereum/les/utils" + vfc "github.com/ethereum/go-ethereum/les/vflux/client" + vfs "github.com/ethereum/go-ethereum/les/vflux/server" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params/types/ctypes" "github.com/ethereum/go-ethereum/rlp" ) @@ -161,9 +163,17 @@ func (p *peerCommons) String() string { return fmt.Sprintf("Peer %s [%s]", p.id, fmt.Sprintf("les/%d", p.version)) } +// PeerInfo represents a short summary of the `eth` sub-protocol metadata known +// about a connected peer. +type PeerInfo struct { + Version int `json:"version"` // Ethereum protocol version negotiated + Difficulty *big.Int `json:"difficulty"` // Total difficulty of the peer's blockchain + Head string `json:"head"` // SHA3 hash of the peer's best owned block +} + // Info gathers and returns a collection of metadata known about a peer. -func (p *peerCommons) Info() *eth.PeerInfo { - return ð.PeerInfo{ +func (p *peerCommons) Info() *PeerInfo { + return &PeerInfo{ Version: p.version, Difficulty: p.Td(), Head: fmt.Sprintf("%x", p.Head()), @@ -246,7 +256,7 @@ func (p *peerCommons) sendReceiveHandshake(sendList keyValueList) (keyValueList, // network IDs, difficulties, head and genesis blocks. Besides the basic handshake // fields, server and client can exchange and resolve some specified fields through // two callback functions. -func (p *peerCommons) handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, sendCallback func(*keyValueList), recvCallback func(keyValueMap) error) error { +func (p *peerCommons) handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter, sendCallback func(*keyValueList), recvCallback func(keyValueMap) error) error { p.lock.Lock() defer p.lock.Unlock() @@ -262,6 +272,12 @@ func (p *peerCommons) handshake(td *big.Int, head common.Hash, headNum uint64, g send = send.add("headNum", headNum) send = send.add("genesisHash", genesis) + // If the protocol version is beyond les4, then pass the forkID + // as well. Check http://eips.ethereum.org/EIPS/eip-2124 for more + // spec detail. + if p.version >= lpv4 { + send = send.add("forkID", forkID) + } // Add client-specified or server-specified fields if sendCallback != nil { sendCallback(&send) @@ -295,6 +311,16 @@ func (p *peerCommons) handshake(td *big.Int, head common.Hash, headNum uint64, g if int(rVersion) != p.version { return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", rVersion, p.version) } + // Check forkID if the protocol version is beyond the les4 + if p.version >= lpv4 { + var forkID forkid.ID + if err := recv.get("forkID", &forkID); err != nil { + return err + } + if err := forkFilter(forkID); err != nil { + return errResp(ErrForkIDRejected, "%v", err) + } + } if recvCallback != nil { return recvCallback(recv) } @@ -317,6 +343,7 @@ type serverPeer struct { onlyAnnounce bool // The flag whether the server sends announcement only. chainSince, chainRecent uint64 // The range of chain server peer can serve. stateSince, stateRecent uint64 // The range of state server peer can serve. + txHistory uint64 // The length of available tx history, 0 means all, 1 means disabled // Advertised checkpoint fields checkpointNumber uint64 // The block height which the checkpoint is registered. @@ -324,8 +351,7 @@ type serverPeer struct { fcServer *flowcontrol.ServerNode // Client side mirror token bucket. vtLock sync.Mutex - valueTracker *lpc.ValueTracker - nodeValueTracker *lpc.NodeValueTracker + nodeValueTracker *vfc.NodeValueTracker sentReqs map[uint64]sentReqEntry // Statistics @@ -407,14 +433,14 @@ func (p *serverPeer) sendRequest(msgcode, reqID uint64, data interface{}, amount // specified header query, based on the hash of an origin block. func (p *serverPeer) requestHeadersByHash(reqID uint64, origin common.Hash, amount int, skip int, reverse bool) error { p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse) - return p.sendRequest(GetBlockHeadersMsg, reqID, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}, amount) + return p.sendRequest(GetBlockHeadersMsg, reqID, &GetBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}, amount) } // requestHeadersByNumber fetches a batch of blocks' headers corresponding to the // specified header query, based on the number of an origin block. func (p *serverPeer) requestHeadersByNumber(reqID, origin uint64, amount int, skip int, reverse bool) error { p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse) - return p.sendRequest(GetBlockHeadersMsg, reqID, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}, amount) + return p.sendRequest(GetBlockHeadersMsg, reqID, &GetBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}, amount) } // requestBodies fetches a batch of blocks' bodies corresponding to the hashes @@ -561,10 +587,10 @@ func (p *serverPeer) updateHead(hash common.Hash, number uint64, td *big.Int) { // Handshake executes the les protocol handshake, negotiating version number, // network IDs and genesis blocks. -func (p *serverPeer) Handshake(genesis common.Hash) error { +func (p *serverPeer) Handshake(genesis common.Hash, forkid forkid.ID, forkFilter forkid.Filter) error { // Note: there is no need to share local head with a server but older servers still // require these fields so we announce zero values. - return p.handshake(common.Big0, common.Hash{}, 0, genesis, func(lists *keyValueList) { + return p.handshake(common.Big0, common.Hash{}, 0, genesis, forkid, forkFilter, func(lists *keyValueList) { // Add some client-specific handshake fields // // Enable signed announcement randomly even the server is not trusted. @@ -604,6 +630,18 @@ func (p *serverPeer) Handshake(genesis common.Hash) error { if recv.get("txRelay", nil) != nil { p.onlyAnnounce = true } + if p.version >= lpv4 { + var recentTx uint + if err := recv.get("recentTxLookup", &recentTx); err != nil { + return err + } + p.txHistory = uint64(recentTx) + } else { + // The weak assumption is held here that legacy les server(les2,3) + // has unlimited transaction history. The les serving in these legacy + // versions is disabled if the transaction is unindexed. + p.txHistory = txIndexUnlimited + } if p.onlyAnnounce && !p.trusted { return errResp(ErrUselessPeer, "peer cannot serve requests") } @@ -639,9 +677,8 @@ func (p *serverPeer) Handshake(genesis common.Hash) error { // setValueTracker sets the value tracker references for connected servers. Note that the // references should be removed upon disconnection by setValueTracker(nil, nil). -func (p *serverPeer) setValueTracker(vt *lpc.ValueTracker, nvt *lpc.NodeValueTracker) { +func (p *serverPeer) setValueTracker(nvt *vfc.NodeValueTracker) { p.vtLock.Lock() - p.valueTracker = vt p.nodeValueTracker = nvt if nvt != nil { p.sentReqs = make(map[uint64]sentReqEntry) @@ -668,7 +705,7 @@ func (p *serverPeer) updateVtParams() { } } } - p.valueTracker.UpdateCosts(p.nodeValueTracker, reqCosts) + p.nodeValueTracker.UpdateCosts(reqCosts) } // sentReqEntry remembers sent requests and their sending times @@ -695,27 +732,26 @@ func (p *serverPeer) answeredRequest(id uint64) { } e, ok := p.sentReqs[id] delete(p.sentReqs, id) - vt := p.valueTracker nvt := p.nodeValueTracker p.vtLock.Unlock() if !ok { return } var ( - vtReqs [2]lpc.ServedRequest + vtReqs [2]vfc.ServedRequest reqCount int ) m := requestMapping[e.reqType] if m.rest == -1 || e.amount <= 1 { reqCount = 1 - vtReqs[0] = lpc.ServedRequest{ReqType: uint32(m.first), Amount: e.amount} + vtReqs[0] = vfc.ServedRequest{ReqType: uint32(m.first), Amount: e.amount} } else { reqCount = 2 - vtReqs[0] = lpc.ServedRequest{ReqType: uint32(m.first), Amount: 1} - vtReqs[1] = lpc.ServedRequest{ReqType: uint32(m.rest), Amount: e.amount - 1} + vtReqs[0] = vfc.ServedRequest{ReqType: uint32(m.first), Amount: 1} + vtReqs[1] = vfc.ServedRequest{ReqType: uint32(m.rest), Amount: e.amount - 1} } dt := time.Duration(mclock.Now() - e.at) - vt.Served(nvt, vtReqs[:reqCount], dt) + nvt.Served(vtReqs[:reqCount], dt) } // clientPeer represents each node to which the les server is connected. @@ -728,15 +764,22 @@ type clientPeer struct { responseLock sync.Mutex responseCount uint64 // Counter to generate an unique id for request processing. - balance *lps.NodeBalance + balance vfs.ConnectedBalance // invalidLock is used for protecting invalidCount. invalidLock sync.RWMutex invalidCount utils.LinearExpiredValue // Counter the invalid request the client peer has made. - server bool - errCh chan error - fcClient *flowcontrol.ClientNode // Server side mirror token bucket. + capacity uint64 + // lastAnnounce is the last broadcast created by the server; may be newer than the last head + // sent to the specific client (stored in headInfo) if capacity is zero. In this case the + // latest head is sent when the client gains non-zero capacity. + lastAnnounce announceData + + connectedAt mclock.AbsTime + server bool + errCh chan error + fcClient *flowcontrol.ClientNode // Server side mirror token bucket. } func newClientPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *clientPeer { @@ -755,9 +798,9 @@ func newClientPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWrite } } -// freeClientId returns a string identifier for the peer. Multiple peers with +// FreeClientId returns a string identifier for the peer. Multiple peers with // the same identifier can not be connected in free mode simultaneously. -func (p *clientPeer) freeClientId() string { +func (p *clientPeer) FreeClientId() string { if addr, ok := p.RemoteAddr().(*net.TCPAddr); ok { if addr.IP.IsLoopback() { // using peer id instead of loopback ip address allows multiple free @@ -887,25 +930,69 @@ func (p *clientPeer) sendAnnounce(request announceData) error { return p2p.Send(p.rw, AnnounceMsg, request) } -// allowInactive implements clientPoolPeer -func (p *clientPeer) allowInactive() bool { - return false +// InactiveAllowance implements vfs.clientPeer +func (p *clientPeer) InactiveAllowance() time.Duration { + return 0 // will return more than zero for les/5 clients } -// updateCapacity updates the request serving capacity assigned to a given client -// and also sends an announcement about the updated flow control parameters -func (p *clientPeer) updateCapacity(cap uint64) { +// getCapacity returns the current capacity of the peer +func (p *clientPeer) getCapacity() uint64 { + p.lock.RLock() + defer p.lock.RUnlock() + + return p.capacity +} + +// UpdateCapacity updates the request serving capacity assigned to a given client +// and also sends an announcement about the updated flow control parameters. +// Note: UpdateCapacity implements vfs.clientPeer and should not block. The requested +// parameter is true if the callback was initiated by ClientPool.SetCapacity on the given peer. +func (p *clientPeer) UpdateCapacity(newCap uint64, requested bool) { p.lock.Lock() defer p.lock.Unlock() - if cap != p.fcParams.MinRecharge { - p.fcParams = flowcontrol.ServerParams{MinRecharge: cap, BufLimit: cap * bufLimitRatio} + if newCap != p.fcParams.MinRecharge { + p.fcParams = flowcontrol.ServerParams{MinRecharge: newCap, BufLimit: newCap * bufLimitRatio} p.fcClient.UpdateParams(p.fcParams) var kvList keyValueList - kvList = kvList.add("flowControl/MRR", cap) - kvList = kvList.add("flowControl/BL", cap*bufLimitRatio) + kvList = kvList.add("flowControl/MRR", newCap) + kvList = kvList.add("flowControl/BL", newCap*bufLimitRatio) p.queueSend(func() { p.sendAnnounce(announceData{Update: kvList}) }) } + + if p.capacity == 0 && newCap != 0 { + p.sendLastAnnounce() + } + p.capacity = newCap +} + +// announceOrStore sends the given head announcement to the client if the client is +// active (capacity != 0) and the same announcement hasn't been sent before. If the +// client is inactive the announcement is stored and sent later if the client is +// activated again. +func (p *clientPeer) announceOrStore(announce announceData) { + p.lock.Lock() + defer p.lock.Unlock() + + p.lastAnnounce = announce + if p.capacity != 0 { + p.sendLastAnnounce() + } +} + +// announce sends the given head announcement to the client if it hasn't been sent before +func (p *clientPeer) sendLastAnnounce() { + if p.lastAnnounce.Td == nil { + return + } + if p.headInfo.Td == nil || p.lastAnnounce.Td.Cmp(p.headInfo.Td) > 0 { + if !p.queueSend(func() { p.sendAnnounce(p.lastAnnounce) }) { + p.Log().Debug("Dropped announcement because queue is full", "number", p.lastAnnounce.Number, "hash", p.lastAnnounce.Hash) + } else { + p.Log().Debug("Sent announcement", "number", p.lastAnnounce.Number, "hash", p.lastAnnounce.Hash) + } + p.headInfo = blockInfo{Hash: p.lastAnnounce.Hash, Number: p.lastAnnounce.Number, Td: p.lastAnnounce.Td} + } } // freezeClient temporarily puts the client in a frozen state which means all @@ -944,11 +1031,25 @@ func (p *clientPeer) freezeClient() { // Handshake executes the les protocol handshake, negotiating version number, // network IDs, difficulties, head and genesis blocks. -func (p *clientPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, server *LesServer) error { +func (p *clientPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter, server *LesServer) error { + recentTx := server.handler.blockchain.TxLookupLimit() + if recentTx != txIndexUnlimited { + if recentTx < blockSafetyMargin { + recentTx = txIndexDisabled + } else { + recentTx -= blockSafetyMargin - txIndexRecentOffset + } + } + if server.config.UltraLightOnlyAnnounce { + recentTx = txIndexDisabled + } + if recentTx != txIndexUnlimited && p.version < lpv4 { + return errors.New("Cannot serve old clients without a complete tx index") + } // Note: clientPeer.headInfo should contain the last head announced to the client by us. // The values announced in the handshake are dummy values for compatibility reasons and should be ignored. p.headInfo = blockInfo{Hash: head, Number: headNum, Td: td} - return p.handshake(td, head, headNum, genesis, func(lists *keyValueList) { + return p.handshake(td, head, headNum, genesis, forkID, forkFilter, func(lists *keyValueList) { // Add some information which services server can offer. if !server.config.UltraLightOnlyAnnounce { *lists = (*lists).add("serveHeaders", nil) @@ -957,13 +1058,16 @@ func (p *clientPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, ge // If local ethereum node is running in archive mode, advertise ourselves we have // all version state data. Otherwise only recent state is available. - stateRecent := uint64(core.TriesInMemory - 4) + stateRecent := uint64(core.TriesInMemory - blockSafetyMargin) if server.archiveMode { stateRecent = 0 } *lists = (*lists).add("serveRecentState", stateRecent) *lists = (*lists).add("txRelay", nil) } + if p.version >= lpv4 { + *lists = (*lists).add("recentTxLookup", recentTx) + } *lists = (*lists).add("flowControl/BL", server.defParams.BufLimit) *lists = (*lists).add("flowControl/MRR", server.defParams.MinRecharge) @@ -1013,6 +1117,11 @@ func (p *clientPeer) getInvalid() uint64 { return p.invalidCount.Value(mclock.Now()) } +// Disconnect implements vfs.clientPeer +func (p *clientPeer) Disconnect() { + p.Peer.Disconnect(p2p.DiscRequested) +} + // serverPeerSubscriber is an interface to notify services about added or // removed server peers type serverPeerSubscriber interface { @@ -1170,3 +1279,181 @@ func (ps *serverPeerSet) close() { } ps.closed = true } + +// clientPeerSet represents the set of active client peers currently +// participating in the Light Ethereum sub-protocol. +type clientPeerSet struct { + peers map[enode.ID]*clientPeer + lock sync.RWMutex + closed bool + + privateKey *ecdsa.PrivateKey + lastAnnounce, signedAnnounce announceData +} + +// newClientPeerSet creates a new peer set to track the client peers. +func newClientPeerSet() *clientPeerSet { + return &clientPeerSet{peers: make(map[enode.ID]*clientPeer)} +} + +// register adds a new peer into the peer set, or returns an error if the +// peer is already known. +func (ps *clientPeerSet) register(peer *clientPeer) error { + ps.lock.Lock() + defer ps.lock.Unlock() + + if ps.closed { + return errClosed + } + if _, exist := ps.peers[peer.ID()]; exist { + return errAlreadyRegistered + } + ps.peers[peer.ID()] = peer + ps.announceOrStore(peer) + return nil +} + +// unregister removes a remote peer from the peer set, disabling any further +// actions to/from that particular entity. It also initiates disconnection +// at the networking layer. +func (ps *clientPeerSet) unregister(id enode.ID) error { + ps.lock.Lock() + defer ps.lock.Unlock() + + p, ok := ps.peers[id] + if !ok { + return errNotRegistered + } + delete(ps.peers, id) + p.Peer.Disconnect(p2p.DiscRequested) + return nil +} + +// ids returns a list of all registered peer IDs +func (ps *clientPeerSet) ids() []enode.ID { + ps.lock.RLock() + defer ps.lock.RUnlock() + + var ids []enode.ID + for id := range ps.peers { + ids = append(ids, id) + } + return ids +} + +// peer retrieves the registered peer with the given id. +func (ps *clientPeerSet) peer(id enode.ID) *clientPeer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + return ps.peers[id] +} + +// len returns if the current number of peers in the set. +func (ps *clientPeerSet) len() int { + ps.lock.RLock() + defer ps.lock.RUnlock() + + return len(ps.peers) +} + +// setSignerKey sets the signer key for signed announcements. Should be called before +// starting the protocol handler. +func (ps *clientPeerSet) setSignerKey(privateKey *ecdsa.PrivateKey) { + ps.privateKey = privateKey +} + +// broadcast sends the given announcements to all active peers +func (ps *clientPeerSet) broadcast(announce announceData) { + ps.lock.Lock() + defer ps.lock.Unlock() + + ps.lastAnnounce = announce + for _, peer := range ps.peers { + ps.announceOrStore(peer) + } +} + +// announceOrStore sends the requested type of announcement to the given peer or stores +// it for later if the peer is inactive (capacity == 0). +func (ps *clientPeerSet) announceOrStore(p *clientPeer) { + if ps.lastAnnounce.Td == nil { + return + } + switch p.announceType { + case announceTypeSimple: + p.announceOrStore(ps.lastAnnounce) + case announceTypeSigned: + if ps.signedAnnounce.Hash != ps.lastAnnounce.Hash { + ps.signedAnnounce = ps.lastAnnounce + ps.signedAnnounce.sign(ps.privateKey) + } + p.announceOrStore(ps.signedAnnounce) + } +} + +// close disconnects all peers. No new peers can be registered +// after close has returned. +func (ps *clientPeerSet) close() { + ps.lock.Lock() + defer ps.lock.Unlock() + + for _, p := range ps.peers { + p.Peer.Disconnect(p2p.DiscQuitting) + } + ps.closed = true +} + +// serverSet is a special set which contains all connected les servers. +// Les servers will also be discovered by discovery protocol because they +// also run the LES protocol. We can't drop them although they are useless +// for us(server) but for other protocols(e.g. ETH) upon the devp2p they +// may be useful. +type serverSet struct { + lock sync.Mutex + set map[string]*clientPeer + closed bool +} + +func newServerSet() *serverSet { + return &serverSet{set: make(map[string]*clientPeer)} +} + +func (s *serverSet) register(peer *clientPeer) error { + s.lock.Lock() + defer s.lock.Unlock() + + if s.closed { + return errClosed + } + if _, exist := s.set[peer.id]; exist { + return errAlreadyRegistered + } + s.set[peer.id] = peer + return nil +} + +func (s *serverSet) unregister(peer *clientPeer) error { + s.lock.Lock() + defer s.lock.Unlock() + + if s.closed { + return errClosed + } + if _, exist := s.set[peer.id]; !exist { + return errNotRegistered + } + delete(s.set, peer.id) + peer.Peer.Disconnect(p2p.DiscQuitting) + return nil +} + +func (s *serverSet) close() { + s.lock.Lock() + defer s.lock.Unlock() + + for _, p := range s.set { + p.Peer.Disconnect(p2p.DiscQuitting) + } + s.closed = true +} diff --git a/les/peer_test.go b/les/peer_test.go index 6d3c7f9755..aa4570cfc9 100644 --- a/les/peer_test.go +++ b/les/peer_test.go @@ -26,8 +26,14 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/forkid" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/params/types/ctypes" ) type testServerPeerSub struct { @@ -91,6 +97,14 @@ func TestPeerSubscription(t *testing.T) { checkPeers(sub.unregCh) } +type fakeChain struct{} + +func (f *fakeChain) Config() ctypes.ChainConfigurator { return params.MainnetChainConfig } +func (f *fakeChain) Genesis() *types.Block { + return core.MustCommitGenesis(rawdb.NewMemoryDatabase(), params.DefaultGenesisBlock()) +} +func (f *fakeChain) CurrentHeader() *types.Header { return &types.Header{Number: big.NewInt(10000000)} } + func TestHandshake(t *testing.T) { // Create a message pipe to communicate through app, net := p2p.MsgPipe() @@ -110,15 +124,21 @@ func TestHandshake(t *testing.T) { head = common.HexToHash("deadbeef") headNum = uint64(10) genesis = common.HexToHash("cafebabe") + + chain1, chain2 = &fakeChain{}, &fakeChain{} + forkID1 = forkid.NewID(chain1.Config(), chain1.Genesis().Hash(), chain1.CurrentHeader().Number.Uint64()) + forkID2 = forkid.NewID(chain2.Config(), chain2.Genesis().Hash(), chain2.CurrentHeader().Number.Uint64()) + filter1, filter2 = forkid.NewFilter(chain1), forkid.NewFilter(chain2) ) + go func() { - errCh1 <- peer1.handshake(td, head, headNum, genesis, func(list *keyValueList) { + errCh1 <- peer1.handshake(td, head, headNum, genesis, forkID1, filter1, func(list *keyValueList) { var announceType uint64 = announceTypeSigned *list = (*list).add("announceType", announceType) }, nil) }() go func() { - errCh2 <- peer2.handshake(td, head, headNum, genesis, nil, func(recv keyValueMap) error { + errCh2 <- peer2.handshake(td, head, headNum, genesis, forkID2, filter2, nil, func(recv keyValueMap) error { var reqType uint64 err := recv.get("announceType", &reqType) if err != nil { diff --git a/les/protocol.go b/les/protocol.go index 19a9561ce9..07a4452f40 100644 --- a/les/protocol.go +++ b/les/protocol.go @@ -24,8 +24,9 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - lpc "github.com/ethereum/go-ethereum/les/lespay/client" + vfc "github.com/ethereum/go-ethereum/les/vflux/client" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rlp" ) @@ -34,21 +35,27 @@ import ( const ( lpv2 = 2 lpv3 = 3 + lpv4 = 4 ) // Supported versions of the les protocol (first is primary) var ( - ClientProtocolVersions = []uint{lpv2, lpv3} - ServerProtocolVersions = []uint{lpv2, lpv3} + ClientProtocolVersions = []uint{lpv2, lpv3, lpv4} + ServerProtocolVersions = []uint{lpv2, lpv3, lpv4} AdvertiseProtocolVersions = []uint{lpv2} // clients are searching for the first advertised protocol in the list ) // Number of implemented message corresponding to different protocol versions. -var ProtocolLengths = map[uint]uint64{lpv2: 22, lpv3: 24} +var ProtocolLengths = map[uint]uint64{lpv2: 22, lpv3: 24, lpv4: 24} const ( NetworkId = 1 ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message + blockSafetyMargin = 4 // safety margin applied to block ranges specified relative to head block + + txIndexUnlimited = 0 // this value in the "recentTxLookup" handshake field means the entire tx index history is served + txIndexDisabled = 1 // this value means tx index is not served at all + txIndexRecentOffset = 1 // txIndexRecentOffset + N in the handshake field means then tx index of the last N blocks is supported ) // les protocol message codes @@ -77,13 +84,69 @@ const ( ResumeMsg = 0x17 ) +// GetBlockHeadersData represents a block header query (the request ID is not included) +type GetBlockHeadersData struct { + Origin hashOrNumber // Block from which to retrieve headers + Amount uint64 // Maximum number of headers to retrieve + Skip uint64 // Blocks to skip between consecutive headers + Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis) +} + +// GetBlockHeadersPacket represents a block header request +type GetBlockHeadersPacket struct { + ReqID uint64 + Query GetBlockHeadersData +} + +// GetBlockBodiesPacket represents a block body request +type GetBlockBodiesPacket struct { + ReqID uint64 + Hashes []common.Hash +} + +// GetCodePacket represents a contract code request +type GetCodePacket struct { + ReqID uint64 + Reqs []CodeReq +} + +// GetReceiptsPacket represents a block receipts request +type GetReceiptsPacket struct { + ReqID uint64 + Hashes []common.Hash +} + +// GetProofsPacket represents a proof request +type GetProofsPacket struct { + ReqID uint64 + Reqs []ProofReq +} + +// GetHelperTrieProofsPacket represents a helper trie proof request +type GetHelperTrieProofsPacket struct { + ReqID uint64 + Reqs []HelperTrieReq +} + +// SendTxPacket represents a transaction propagation request +type SendTxPacket struct { + ReqID uint64 + Txs []*types.Transaction +} + +// GetTxStatusPacket represents a transaction status query +type GetTxStatusPacket struct { + ReqID uint64 + Hashes []common.Hash +} + type requestInfo struct { name string maxCount uint64 refBasketFirst, refBasketRest float64 } -// reqMapping maps an LES request to one or two lespay service vector entries. +// reqMapping maps an LES request to one or two vflux service vector entries. // If rest != -1 and the request type is used with amounts larger than one then the // first one of the multi-request is mapped to first while the rest is mapped to rest. type reqMapping struct { @@ -92,7 +155,7 @@ type reqMapping struct { var ( // requests describes the available LES request types and their initializing amounts - // in the lespay/client.ValueTracker reference basket. Initial values are estimates + // in the vfc.ValueTracker reference basket. Initial values are estimates // based on the same values as the server's default cost estimates (reqAvgTimeCost). requests = map[uint64]requestInfo{ GetBlockHeadersMsg: {"GetBlockHeaders", MaxHeaderFetch, 10, 1000}, @@ -104,25 +167,25 @@ var ( SendTxV2Msg: {"SendTxV2", MaxTxSend, 1, 0}, GetTxStatusMsg: {"GetTxStatus", MaxTxStatus, 10, 0}, } - requestList []lpc.RequestInfo + requestList []vfc.RequestInfo requestMapping map[uint32]reqMapping ) -// init creates a request list and mapping between protocol message codes and lespay +// init creates a request list and mapping between protocol message codes and vflux // service vector indices. func init() { requestMapping = make(map[uint32]reqMapping) for code, req := range requests { cost := reqAvgTimeCost[code] rm := reqMapping{len(requestList), -1} - requestList = append(requestList, lpc.RequestInfo{ + requestList = append(requestList, vfc.RequestInfo{ Name: req.name + ".first", InitAmount: req.refBasketFirst, InitValue: float64(cost.baseCost + cost.reqCost), }) if req.refBasketRest != 0 { rm.rest = len(requestList) - requestList = append(requestList, lpc.RequestInfo{ + requestList = append(requestList, vfc.RequestInfo{ Name: req.name + ".rest", InitAmount: req.refBasketRest, InitValue: float64(cost.reqCost), @@ -150,6 +213,7 @@ const ( ErrInvalidResponse ErrTooManyTimeouts ErrMissingKey + ErrForkIDRejected ) func (e errCode) String() string { @@ -172,6 +236,7 @@ var errorToString = map[int]string{ ErrInvalidResponse: "Invalid response", ErrTooManyTimeouts: "Too many request timeouts", ErrMissingKey: "Key missing from list", + ErrForkIDRejected: "ForkID rejected", } // announceData is the network packet for the block announcements. @@ -221,14 +286,6 @@ type blockInfo struct { Td *big.Int // Total difficulty of one particular block being announced } -// getBlockHeadersData represents a block header query. -type getBlockHeadersData struct { - Origin hashOrNumber // Block from which to retrieve headers - Amount uint64 // Maximum number of headers to retrieve - Skip uint64 // Blocks to skip between consecutive headers - Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis) -} - // hashOrNumber is a combined field for specifying an origin block. type hashOrNumber struct { Hash common.Hash // Block hash from which to retrieve headers (excludes Number) diff --git a/les/pruner_test.go b/les/pruner_test.go index 62b4e9a950..c6f198c088 100644 --- a/les/pruner_test.go +++ b/les/pruner_test.go @@ -28,19 +28,26 @@ import ( ) func TestLightPruner(t *testing.T) { - config := light.TestClientIndexerConfig - - waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) { - for { - cs, _, _ := cIndexer.Sections() - bts, _, _ := btIndexer.Sections() - if cs >= 3 && bts >= 3 { - break + var ( + waitIndexers = func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) { + for { + cs, _, _ := cIndexer.Sections() + bts, _, _ := btIndexer.Sections() + if cs >= 3 && bts >= 3 { + break + } + time.Sleep(10 * time.Millisecond) } - time.Sleep(10 * time.Millisecond) } - } - server, client, tearDown := newClientServerEnv(t, int(3*config.ChtSize+config.ChtConfirms), 2, waitIndexers, nil, 0, false, true, false) + config = light.TestClientIndexerConfig + netconfig = testnetConfig{ + blocks: int(3*config.ChtSize + config.ChtConfirms), + protocol: 3, + indexFn: waitIndexers, + connect: true, + } + ) + server, client, tearDown := newClientServerEnv(t, netconfig) defer tearDown() // checkDB iterates the chain with given prefix, resolves the block number diff --git a/les/request_test.go b/les/request_test.go index 4851274382..c65405e375 100644 --- a/les/request_test.go +++ b/les/request_test.go @@ -38,6 +38,7 @@ type accessTestFn func(db ethdb.Database, bhash common.Hash, number uint64) ligh func TestBlockAccessLes2(t *testing.T) { testAccess(t, 2, tfBlockAccess) } func TestBlockAccessLes3(t *testing.T) { testAccess(t, 3, tfBlockAccess) } +func TestBlockAccessLes4(t *testing.T) { testAccess(t, 4, tfBlockAccess) } func tfBlockAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { return &light.BlockRequest{Hash: bhash, Number: number} @@ -45,6 +46,7 @@ func tfBlockAccess(db ethdb.Database, bhash common.Hash, number uint64) light.Od func TestReceiptsAccessLes2(t *testing.T) { testAccess(t, 2, tfReceiptsAccess) } func TestReceiptsAccessLes3(t *testing.T) { testAccess(t, 3, tfReceiptsAccess) } +func TestReceiptsAccessLes4(t *testing.T) { testAccess(t, 4, tfReceiptsAccess) } func tfReceiptsAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { return &light.ReceiptsRequest{Hash: bhash, Number: number} @@ -52,6 +54,7 @@ func tfReceiptsAccess(db ethdb.Database, bhash common.Hash, number uint64) light func TestTrieEntryAccessLes2(t *testing.T) { testAccess(t, 2, tfTrieEntryAccess) } func TestTrieEntryAccessLes3(t *testing.T) { testAccess(t, 3, tfTrieEntryAccess) } +func TestTrieEntryAccessLes4(t *testing.T) { testAccess(t, 4, tfTrieEntryAccess) } func tfTrieEntryAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { if number := rawdb.ReadHeaderNumber(db, bhash); number != nil { @@ -62,6 +65,7 @@ func tfTrieEntryAccess(db ethdb.Database, bhash common.Hash, number uint64) ligh func TestCodeAccessLes2(t *testing.T) { testAccess(t, 2, tfCodeAccess) } func TestCodeAccessLes3(t *testing.T) { testAccess(t, 3, tfCodeAccess) } +func TestCodeAccessLes4(t *testing.T) { testAccess(t, 4, tfCodeAccess) } func tfCodeAccess(db ethdb.Database, bhash common.Hash, num uint64) light.OdrRequest { number := rawdb.ReadHeaderNumber(db, bhash) @@ -79,7 +83,14 @@ func tfCodeAccess(db ethdb.Database, bhash common.Hash, num uint64) light.OdrReq func testAccess(t *testing.T, protocol int, fn accessTestFn) { // Assemble the test environment - server, client, tearDown := newClientServerEnv(t, 4, protocol, nil, nil, 0, false, true, true) + netconfig := testnetConfig{ + blocks: 4, + protocol: protocol, + indexFn: nil, + connect: true, + nopruning: true, + } + server, client, tearDown := newClientServerEnv(t, netconfig) defer tearDown() // Ensure the client has synced all necessary data. diff --git a/les/retrieve.go b/les/retrieve.go index 4f77004f20..3174d49878 100644 --- a/les/retrieve.go +++ b/les/retrieve.go @@ -155,6 +155,15 @@ func (rm *retrieveManager) sendReq(reqID uint64, req *distReq, val validatorFunc return r } +// requested reports whether the request with given reqid is sent by the retriever. +func (rm *retrieveManager) requested(reqId uint64) bool { + rm.lock.RLock() + defer rm.lock.RUnlock() + + _, ok := rm.sentReqs[reqId] + return ok +} + // deliver is called by the LES protocol manager to deliver reply messages to waiting requests func (rm *retrieveManager) deliver(peer distPeer, msg *Msg) error { rm.lock.RLock() @@ -328,7 +337,6 @@ func (r *sentReq) tryRequest() { } defer func() { - // send feedback to server pool and remove peer if hard timeout happened pp, ok := p.(*serverPeer) if hrto && ok { pp.Log().Debug("Request timed out hard") @@ -336,10 +344,6 @@ func (r *sentReq) tryRequest() { r.rm.peers.unregister(pp.id) } } - - r.lock.Lock() - delete(r.sentTo, p) - r.lock.Unlock() }() select { diff --git a/les/server.go b/les/server.go index 035050e9c9..e364daae22 100644 --- a/les/server.go +++ b/les/server.go @@ -18,47 +18,48 @@ package les import ( "crypto/ecdsa" - "reflect" "time" "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/les/flowcontrol" - lps "github.com/ethereum/go-ethereum/les/lespay/server" + vfs "github.com/ethereum/go-ethereum/les/vflux/server" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" - "github.com/ethereum/go-ethereum/p2p/nodestate" "github.com/ethereum/go-ethereum/params/vars" "github.com/ethereum/go-ethereum/rpc" ) var ( - serverSetup = &nodestate.Setup{} - clientPeerField = serverSetup.NewField("clientPeer", reflect.TypeOf(&clientPeer{})) - clientInfoField = serverSetup.NewField("clientInfo", reflect.TypeOf(&clientInfo{})) - connAddressField = serverSetup.NewField("connAddr", reflect.TypeOf("")) - balanceTrackerSetup = lps.NewBalanceTrackerSetup(serverSetup) - priorityPoolSetup = lps.NewPriorityPoolSetup(serverSetup) + defaultPosFactors = vfs.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1} + defaultNegFactors = vfs.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1} ) -func init() { - balanceTrackerSetup.Connect(connAddressField, priorityPoolSetup.CapacityField) - priorityPoolSetup.Connect(balanceTrackerSetup.BalanceField, balanceTrackerSetup.UpdateFlag) // NodeBalance implements nodePriority +const defaultConnectedBias = time.Minute * 3 + +type ethBackend interface { + ArchiveMode() bool + BlockChain() *core.BlockChain + BloomIndexer() *core.ChainIndexer + ChainDb() ethdb.Database + Synced() bool + TxPool() *core.TxPool } type LesServer struct { lesCommons - ns *nodestate.NodeStateMachine archiveMode bool // Flag whether the ethereum node runs in archive mode. handler *serverHandler - broadcaster *broadcaster - lesTopics []discv5.Topic + peers *clientPeerSet + serverset *serverSet + vfluxServer *vfs.Server privateKey *ecdsa.PrivateKey // Flow control and capacity management @@ -66,7 +67,7 @@ type LesServer struct { costTracker *costTracker defParams flowcontrol.ServerParams servingQueue *servingQueue - clientPool *clientPool + clientPool *vfs.ClientPool minCapacity, maxCapacity uint64 threadsIdle int // Request serving threads count when system is idle. @@ -75,12 +76,10 @@ type LesServer struct { p2pSrv *p2p.Server } -func NewLesServer(node *node.Node, e *eth.Ethereum, config *eth.Config) (*LesServer, error) { - ns := nodestate.NewNodeStateMachine(nil, nil, mclock.System{}, serverSetup) - // Collect les protocol version information supported by local node. - lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions)) - for i, pv := range AdvertiseProtocolVersions { - lesTopics[i] = lesTopic(e.BlockChain().Genesis().Hash(), pv) +func NewLesServer(node *node.Node, e ethBackend, config *ethconfig.Config) (*LesServer, error) { + lesDb, err := node.OpenDatabase("les.server", 0, 0, "eth/db/lesserver/", false) + if err != nil { + return nil, err } // Calculate the number of threads used to service the light client // requests based on the user-specified value. @@ -95,22 +94,27 @@ func NewLesServer(node *node.Node, e *eth.Ethereum, config *eth.Config) (*LesSer chainConfig: e.BlockChain().Config(), iConfig: light.DefaultServerIndexerConfig, chainDb: e.ChainDb(), + lesDb: lesDb, chainReader: e.BlockChain(), chtIndexer: light.NewChtIndexer(e.ChainDb(), nil, vars.CHTFrequency, vars.HelperTrieProcessConfirmations, true), bloomTrieIndexer: light.NewBloomTrieIndexer(e.ChainDb(), nil, vars.BloomBitsBlocks, vars.BloomTrieFrequency, true), closeCh: make(chan struct{}), }, - ns: ns, archiveMode: e.ArchiveMode(), - broadcaster: newBroadcaster(ns), - lesTopics: lesTopics, + peers: newClientPeerSet(), + serverset: newServerSet(), + vfluxServer: vfs.NewServer(time.Millisecond * 10), fcManager: flowcontrol.NewClientManager(nil, &mclock.System{}), servingQueue: newServingQueue(int64(time.Millisecond*10), float64(config.LightServ)/100), threadsBusy: config.LightServ/100 + 1, threadsIdle: threads, p2pSrv: node.Server(), } - srv.handler = newServerHandler(srv, e.BlockChain(), e.ChainDb(), e.TxPool(), e.Synced) + issync := e.Synced + if config.LightNoSyncServe { + issync = func() bool { return true } + } + srv.handler = newServerHandler(srv, e.BlockChain(), e.ChainDb(), e.TxPool(), issync) srv.costTracker, srv.minCapacity = newCostTracker(e.ChainDb(), config) srv.oracle = srv.setupOracle(node, e.BlockChain().Genesis().Hash(), config) @@ -133,8 +137,10 @@ func NewLesServer(node *node.Node, e *eth.Ethereum, config *eth.Config) (*LesSer srv.maxCapacity = totalRecharge } srv.fcManager.SetCapacityLimits(srv.minCapacity, srv.maxCapacity, srv.minCapacity*2) - srv.clientPool = newClientPool(ns, srv.chainDb, srv.minCapacity, defaultConnectedBias, mclock.System{}, srv.dropClient) - srv.clientPool.setDefaultFactors(lps.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1}) + srv.clientPool = vfs.NewClientPool(lesDb, srv.minCapacity, defaultConnectedBias, mclock.System{}, issync) + srv.clientPool.Start() + srv.clientPool.SetDefaultFactors(defaultPosFactors, defaultNegFactors) + srv.vfluxServer.Register(srv.clientPool, "les", "Ethereum light client service") checkpoint := srv.latestLocalCheckpoint() if !checkpoint.Empty() { @@ -146,14 +152,6 @@ func NewLesServer(node *node.Node, e *eth.Ethereum, config *eth.Config) (*LesSer node.RegisterProtocols(srv.Protocols()) node.RegisterAPIs(srv.APIs()) node.RegisterLifecycle(srv) - - // disconnect all peers at nsm shutdown - ns.SubscribeField(clientPeerField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) { - if state.Equals(serverSetup.OfflineFlag()) && oldValue != nil { - oldValue.(*clientPeer).Peer.Disconnect(p2p.DiscRequested) - } - }) - ns.Start() return srv, nil } @@ -182,14 +180,16 @@ func (s *LesServer) APIs() []rpc.API { func (s *LesServer) Protocols() []p2p.Protocol { ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} { - if p := s.getClient(id); p != nil { + if p := s.peers.peer(id); p != nil { return p.Info() } return nil }, nil) // Add "les" ENR entries. for i := range ps { - ps[i].Attributes = []enr.Entry{&lesEntry{}} + ps[i].Attributes = []enr.Entry{&lesEntry{ + VfxVersion: 1, + }} } return ps } @@ -197,25 +197,13 @@ func (s *LesServer) Protocols() []p2p.Protocol { // Start starts the LES server func (s *LesServer) Start() error { s.privateKey = s.p2pSrv.PrivateKey - s.broadcaster.setSignerKey(s.privateKey) + s.peers.setSignerKey(s.privateKey) s.handler.start() - s.wg.Add(1) go s.capacityManagement() - if s.p2pSrv.DiscV5 != nil { - for _, topic := range s.lesTopics { - topic := topic - go func() { - logger := log.New("topic", topic) - logger.Info("Starting topic registration") - defer logger.Info("Terminated topic registration") - - s.p2pSrv.DiscV5.RegisterTopic(topic, s.closeCh) - }() - } + s.p2pSrv.DiscV5.RegisterTalkHandler("vfx", s.vfluxServer.ServeEncoded) } - return nil } @@ -223,15 +211,18 @@ func (s *LesServer) Start() error { func (s *LesServer) Stop() error { close(s.closeCh) - s.clientPool.stop() - s.ns.Stop() + s.clientPool.Stop() + s.serverset.close() + s.peers.close() s.fcManager.Stop() s.costTracker.stop() s.handler.stop() s.servingQueue.stop() + s.vfluxServer.Stop() // Note, bloom trie indexer is closed by parent bloombits indexer. s.chtIndexer.Close() + s.lesDb.Close() s.wg.Wait() log.Info("Les server stopped") @@ -253,7 +244,7 @@ func (s *LesServer) capacityManagement() { totalCapacityCh := make(chan uint64, 100) totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh) - s.clientPool.setLimits(s.config.LightPeers, totalCapacity) + s.clientPool.SetLimits(uint64(s.config.LightPeers), totalCapacity) var ( busy bool @@ -290,24 +281,9 @@ func (s *LesServer) capacityManagement() { log.Warn("Reduced free peer connections", "from", freePeers, "to", newFreePeers) } freePeers = newFreePeers - s.clientPool.setLimits(s.config.LightPeers, totalCapacity) + s.clientPool.SetLimits(uint64(s.config.LightPeers), totalCapacity) case <-s.closeCh: return } } } - -func (s *LesServer) getClient(id enode.ID) *clientPeer { - if node := s.ns.GetNode(id); node != nil { - if p, ok := s.ns.GetField(node, clientPeerField).(*clientPeer); ok { - return p - } - } - return nil -} - -func (s *LesServer) dropClient(id enode.ID) { - if p := s.getClient(id); p != nil { - p.Peer.Disconnect(p2p.DiscRequested) - } -} diff --git a/les/server_handler.go b/les/server_handler.go index d3e2c956b3..5e12136d90 100644 --- a/les/server_handler.go +++ b/les/server_handler.go @@ -17,9 +17,6 @@ package les import ( - "crypto/ecdsa" - "encoding/binary" - "encoding/json" "errors" "sync" "sync/atomic" @@ -28,17 +25,15 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" - lps "github.com/ethereum/go-ethereum/les/lespay/server" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/p2p/nodestate" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" ) @@ -46,7 +41,7 @@ import ( const ( softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data. estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header - ethVersion = 63 // equivalent eth version for the downloader + ethVersion = 64 // equivalent eth version for the downloader MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request MaxBodyFetch = 32 // Amount of block bodies to be fetched per retrieval request @@ -60,12 +55,12 @@ const ( var ( errTooManyInvalidRequest = errors.New("too many invalid requests made") - errFullClientPool = errors.New("client pool is full") ) // serverHandler is responsible for serving light client and process // all incoming light requests. type serverHandler struct { + forkFilter forkid.Filter blockchain *core.BlockChain chainDb ethdb.Database txpool *core.TxPool @@ -81,6 +76,7 @@ type serverHandler struct { func newServerHandler(server *LesServer, blockchain *core.BlockChain, chainDb ethdb.Database, txpool *core.TxPool, synced func() bool) *serverHandler { handler := &serverHandler{ + forkFilter: forkid.NewFilter(blockchain), server: server, blockchain: blockchain, chainDb: chainDb, @@ -121,37 +117,24 @@ func (h *serverHandler) handle(p *clientPeer) error { hash = head.Hash() number = head.Number.Uint64() td = h.blockchain.GetTd(hash, number) + forkID = forkid.NewID(h.blockchain.Config(), h.blockchain.Genesis().Hash(), h.blockchain.CurrentBlock().NumberU64()) ) - if err := p.Handshake(td, hash, number, h.blockchain.Genesis().Hash(), h.server); err != nil { + if err := p.Handshake(td, hash, number, h.blockchain.Genesis().Hash(), forkID, h.forkFilter, h.server); err != nil { p.Log().Debug("Light Ethereum handshake failed", "err", err) return err } - // Reject the duplicated peer, otherwise register it to peerset. - var registered bool - if err := h.server.ns.Operation(func() { - if h.server.ns.GetField(p.Node(), clientPeerField) != nil { - registered = true - } else { - h.server.ns.SetFieldSub(p.Node(), clientPeerField, p) - } - }); err != nil { - return err - } - if registered { - return errAlreadyRegistered - } - defer func() { - h.server.ns.SetField(p.Node(), clientPeerField, nil) - if p.fcClient != nil { // is nil when connecting another server - p.fcClient.Disconnect() - } - }() if p.server { + if err := h.server.serverset.register(p); err != nil { + return err + } // connected to another server, no messages expected, just wait for disconnection _, err := p.rw.ReadMsg() + h.server.serverset.unregister(p) return err } + defer p.fcClient.Disconnect() // set by handshake if it's not another server + // Reject light clients if server is not synced. // // Put this checking here, so that "non-synced" les-server peers are still allowed @@ -160,30 +143,31 @@ func (h *serverHandler) handle(p *clientPeer) error { p.Log().Debug("Light server not synced, rejecting peer") return p2p.DiscRequested } - // Disconnect the inbound peer if it's rejected by clientPool - if cap, err := h.server.clientPool.connect(p); cap != p.fcParams.MinRecharge || err != nil { - p.Log().Debug("Light Ethereum peer rejected", "err", errFullClientPool) - return errFullClientPool + if err := h.server.peers.register(p); err != nil { + return err } - p.balance, _ = h.server.ns.GetField(p.Node(), h.server.clientPool.BalanceField).(*lps.NodeBalance) - if p.balance == nil { + if p.balance = h.server.clientPool.Register(p); p.balance == nil { + h.server.peers.unregister(p.ID()) + p.Log().Debug("Client pool already closed") return p2p.DiscRequested } - activeCount, _ := h.server.clientPool.pp.Active() + activeCount, _ := h.server.clientPool.Active() clientConnectionGauge.Update(int64(activeCount)) + p.connectedAt = mclock.Now() var wg sync.WaitGroup // Wait group used to track all in-flight task routines. - connectedAt := mclock.Now() defer func() { wg.Wait() // Ensure all background task routines have exited. - h.server.clientPool.disconnect(p) + h.server.clientPool.Unregister(p) + h.server.peers.unregister(p.ID()) p.balance = nil - activeCount, _ := h.server.clientPool.pp.Active() + activeCount, _ := h.server.clientPool.Active() clientConnectionGauge.Update(int64(activeCount)) - connectionTimer.Update(time.Duration(mclock.Now() - connectedAt)) + connectionTimer.Update(time.Duration(mclock.Now() - p.connectedAt)) }() - // Mark the peer starts to be served. + + // Mark the peer as being served. atomic.StoreUint32(&p.serving, 1) defer atomic.StoreUint32(&p.serving, 0) @@ -202,6 +186,90 @@ func (h *serverHandler) handle(p *clientPeer) error { } } +// beforeHandle will do a series of prechecks before handling message. +func (h *serverHandler) beforeHandle(p *clientPeer, reqID, responseCount uint64, msg p2p.Msg, reqCnt uint64, maxCount uint64) (*servingTask, uint64) { + // Ensure that the request sent by client peer is valid + inSizeCost := h.server.costTracker.realCost(0, msg.Size, 0) + if reqCnt == 0 || reqCnt > maxCount { + p.fcClient.OneTimeCost(inSizeCost) + return nil, 0 + } + // Ensure that the client peer complies with the flow control + // rules agreed by both sides. + if p.isFrozen() { + p.fcClient.OneTimeCost(inSizeCost) + return nil, 0 + } + maxCost := p.fcCosts.getMaxCost(msg.Code, reqCnt) + accepted, bufShort, priority := p.fcClient.AcceptRequest(reqID, responseCount, maxCost) + if !accepted { + p.freeze() + p.Log().Error("Request came too early", "remaining", common.PrettyDuration(time.Duration(bufShort*1000000/p.fcParams.MinRecharge))) + p.fcClient.OneTimeCost(inSizeCost) + return nil, 0 + } + // Create a multi-stage task, estimate the time it takes for the task to + // execute, and cache it in the request service queue. + factor := h.server.costTracker.globalFactor() + if factor < 0.001 { + factor = 1 + p.Log().Error("Invalid global cost factor", "factor", factor) + } + maxTime := uint64(float64(maxCost) / factor) + task := h.server.servingQueue.newTask(p, maxTime, priority) + if !task.start() { + p.fcClient.RequestProcessed(reqID, responseCount, maxCost, inSizeCost) + return nil, 0 + } + return task, maxCost +} + +// Afterhandle will perform a series of operations after message handling, +// such as updating flow control data, sending reply, etc. +func (h *serverHandler) afterHandle(p *clientPeer, reqID, responseCount uint64, msg p2p.Msg, maxCost uint64, reqCnt uint64, task *servingTask, reply *reply) { + if reply != nil { + task.done() + } + p.responseLock.Lock() + defer p.responseLock.Unlock() + + // Short circuit if the client is already frozen. + if p.isFrozen() { + realCost := h.server.costTracker.realCost(task.servingTime, msg.Size, 0) + p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost) + return + } + // Positive correction buffer value with real cost. + var replySize uint32 + if reply != nil { + replySize = reply.size() + } + var realCost uint64 + if h.server.costTracker.testing { + realCost = maxCost // Assign a fake cost for testing purpose + } else { + realCost = h.server.costTracker.realCost(task.servingTime, msg.Size, replySize) + if realCost > maxCost { + realCost = maxCost + } + } + bv := p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost) + if reply != nil { + // Feed cost tracker request serving statistic. + h.server.costTracker.updateStats(msg.Code, reqCnt, task.servingTime, realCost) + // Reduce priority "balance" for the specific peer. + p.balance.RequestServed(realCost) + p.queueSend(func() { + if err := reply.send(bv); err != nil { + select { + case p.errCh <- err: + default: + } + } + }) + } +} + // handleMsg is invoked whenever an inbound message is received from a remote // peer. The remote connection is torn down upon returning any error. func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { @@ -219,648 +287,52 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { } defer msg.Discard() - var ( - maxCost uint64 - task *servingTask - ) - p.responseCount++ - responseCount := p.responseCount - // accept returns an indicator whether the request can be served. - // If so, deduct the max cost from the flow control buffer. - accept := func(reqID, reqCnt, maxCnt uint64) bool { - // Short circuit if the peer is already frozen or the request is invalid. - inSizeCost := h.server.costTracker.realCost(0, msg.Size, 0) - if p.isFrozen() || reqCnt == 0 || reqCnt > maxCnt { - p.fcClient.OneTimeCost(inSizeCost) - return false - } - // Prepaid max cost units before request been serving. - maxCost = p.fcCosts.getMaxCost(msg.Code, reqCnt) - accepted, bufShort, priority := p.fcClient.AcceptRequest(reqID, responseCount, maxCost) - if !accepted { - p.freeze() - p.Log().Error("Request came too early", "remaining", common.PrettyDuration(time.Duration(bufShort*1000000/p.fcParams.MinRecharge))) - p.fcClient.OneTimeCost(inSizeCost) - return false - } - // Create a multi-stage task, estimate the time it takes for the task to - // execute, and cache it in the request service queue. - factor := h.server.costTracker.globalFactor() - if factor < 0.001 { - factor = 1 - p.Log().Error("Invalid global cost factor", "factor", factor) - } - maxTime := uint64(float64(maxCost) / factor) - task = h.server.servingQueue.newTask(p, maxTime, priority) - if task.start() { - return true - } - p.fcClient.RequestProcessed(reqID, responseCount, maxCost, inSizeCost) - return false + // Lookup the request handler table, ensure it's supported + // message type by the protocol. + req, ok := Les3[msg.Code] + if !ok { + p.Log().Trace("Received invalid message", "code", msg.Code) + clientErrorMeter.Mark(1) + return errResp(ErrInvalidMsgCode, "%v", msg.Code) } - // sendResponse sends back the response and updates the flow control statistic. - sendResponse := func(reqID, amount uint64, reply *reply, servingTime uint64) { - p.responseLock.Lock() - defer p.responseLock.Unlock() + p.Log().Trace("Received " + req.Name) - // Short circuit if the client is already frozen. - if p.isFrozen() { - realCost := h.server.costTracker.realCost(servingTime, msg.Size, 0) - p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost) - return - } - // Positive correction buffer value with real cost. - var replySize uint32 - if reply != nil { - replySize = reply.size() - } - var realCost uint64 - if h.server.costTracker.testing { - realCost = maxCost // Assign a fake cost for testing purpose - } else { - realCost = h.server.costTracker.realCost(servingTime, msg.Size, replySize) - if realCost > maxCost { - realCost = maxCost - } - } - bv := p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost) - if amount != 0 { - // Feed cost tracker request serving statistic. - h.server.costTracker.updateStats(msg.Code, amount, servingTime, realCost) - // Reduce priority "balance" for the specific peer. - p.balance.RequestServed(realCost) - } - if reply != nil { - p.queueSend(func() { - if err := reply.send(bv); err != nil { - select { - case p.errCh <- err: - default: - } - } - }) - } + // Decode the p2p message, resolve the concrete handler for it. + serve, reqID, reqCnt, err := req.Handle(msg) + if err != nil { + clientErrorMeter.Mark(1) + return errResp(ErrDecode, "%v: %v", msg, err) } - switch msg.Code { - case GetBlockHeadersMsg: - p.Log().Trace("Received block header request") - if metrics.EnabledExpensive { - miscInHeaderPacketsMeter.Mark(1) - miscInHeaderTrafficMeter.Mark(int64(msg.Size)) - } - var req struct { - ReqID uint64 - Query getBlockHeadersData - } - if err := msg.Decode(&req); err != nil { - clientErrorMeter.Mark(1) - return errResp(ErrDecode, "%v: %v", msg, err) - } - query := req.Query - if accept(req.ReqID, query.Amount, MaxHeaderFetch) { - wg.Add(1) - go func() { - defer wg.Done() - hashMode := query.Origin.Hash != (common.Hash{}) - first := true - maxNonCanonical := uint64(100) - - // Gather headers until the fetch or network limits is reached - var ( - bytes common.StorageSize - headers []*types.Header - unknown bool - ) - for !unknown && len(headers) < int(query.Amount) && bytes < softResponseLimit { - if !first && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - // Retrieve the next header satisfying the query - var origin *types.Header - if hashMode { - if first { - origin = h.blockchain.GetHeaderByHash(query.Origin.Hash) - if origin != nil { - query.Origin.Number = origin.Number.Uint64() - } - } else { - origin = h.blockchain.GetHeader(query.Origin.Hash, query.Origin.Number) - } - } else { - origin = h.blockchain.GetHeaderByNumber(query.Origin.Number) - } - if origin == nil { - break - } - headers = append(headers, origin) - bytes += estHeaderRlpSize - - // Advance to the next header of the query - switch { - case hashMode && query.Reverse: - // Hash based traversal towards the genesis block - ancestor := query.Skip + 1 - if ancestor == 0 { - unknown = true - } else { - query.Origin.Hash, query.Origin.Number = h.blockchain.GetAncestor(query.Origin.Hash, query.Origin.Number, ancestor, &maxNonCanonical) - unknown = query.Origin.Hash == common.Hash{} - } - case hashMode && !query.Reverse: - // Hash based traversal towards the leaf block - var ( - current = origin.Number.Uint64() - next = current + query.Skip + 1 - ) - if next <= current { - infos, _ := json.MarshalIndent(p.Peer.Info(), "", " ") - p.Log().Warn("GetBlockHeaders skip overflow attack", "current", current, "skip", query.Skip, "next", next, "attacker", infos) - unknown = true - } else { - if header := h.blockchain.GetHeaderByNumber(next); header != nil { - nextHash := header.Hash() - expOldHash, _ := h.blockchain.GetAncestor(nextHash, next, query.Skip+1, &maxNonCanonical) - if expOldHash == query.Origin.Hash { - query.Origin.Hash, query.Origin.Number = nextHash, next - } else { - unknown = true - } - } else { - unknown = true - } - } - case query.Reverse: - // Number based traversal towards the genesis block - if query.Origin.Number >= query.Skip+1 { - query.Origin.Number -= query.Skip + 1 - } else { - unknown = true - } - - case !query.Reverse: - // Number based traversal towards the leaf block - query.Origin.Number += query.Skip + 1 - } - first = false - } - reply := p.replyBlockHeaders(req.ReqID, headers) - sendResponse(req.ReqID, query.Amount, reply, task.done()) - if metrics.EnabledExpensive { - miscOutHeaderPacketsMeter.Mark(1) - miscOutHeaderTrafficMeter.Mark(int64(reply.size())) - miscServingTimeHeaderTimer.Update(time.Duration(task.servingTime)) - } - }() - } - - case GetBlockBodiesMsg: - p.Log().Trace("Received block bodies request") - if metrics.EnabledExpensive { - miscInBodyPacketsMeter.Mark(1) - miscInBodyTrafficMeter.Mark(int64(msg.Size)) - } - var req struct { - ReqID uint64 - Hashes []common.Hash - } - if err := msg.Decode(&req); err != nil { - clientErrorMeter.Mark(1) - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - var ( - bytes int - bodies []rlp.RawValue - ) - reqCnt := len(req.Hashes) - if accept(req.ReqID, uint64(reqCnt), MaxBodyFetch) { - wg.Add(1) - go func() { - defer wg.Done() - for i, hash := range req.Hashes { - if i != 0 && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - if bytes >= softResponseLimit { - break - } - body := h.blockchain.GetBodyRLP(hash) - if body == nil { - p.bumpInvalid() - continue - } - bodies = append(bodies, body) - bytes += len(body) - } - reply := p.replyBlockBodiesRLP(req.ReqID, bodies) - sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) - if metrics.EnabledExpensive { - miscOutBodyPacketsMeter.Mark(1) - miscOutBodyTrafficMeter.Mark(int64(reply.size())) - miscServingTimeBodyTimer.Update(time.Duration(task.servingTime)) - } - }() - } - - case GetCodeMsg: - p.Log().Trace("Received code request") - if metrics.EnabledExpensive { - miscInCodePacketsMeter.Mark(1) - miscInCodeTrafficMeter.Mark(int64(msg.Size)) - } - var req struct { - ReqID uint64 - Reqs []CodeReq - } - if err := msg.Decode(&req); err != nil { - clientErrorMeter.Mark(1) - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - var ( - bytes int - data [][]byte - ) - reqCnt := len(req.Reqs) - if accept(req.ReqID, uint64(reqCnt), MaxCodeFetch) { - wg.Add(1) - go func() { - defer wg.Done() - for i, request := range req.Reqs { - if i != 0 && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - // Look up the root hash belonging to the request - header := h.blockchain.GetHeaderByHash(request.BHash) - if header == nil { - p.Log().Warn("Failed to retrieve associate header for code", "hash", request.BHash) - p.bumpInvalid() - continue - } - // Refuse to search stale state data in the database since looking for - // a non-exist key is kind of expensive. - local := h.blockchain.CurrentHeader().Number.Uint64() - if !h.server.archiveMode && header.Number.Uint64()+core.TriesInMemory <= local { - p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local) - p.bumpInvalid() - continue - } - triedb := h.blockchain.StateCache().TrieDB() - - account, err := h.getAccount(triedb, header.Root, common.BytesToHash(request.AccKey)) - if err != nil { - p.Log().Warn("Failed to retrieve account for code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) - p.bumpInvalid() - continue - } - code, err := h.blockchain.StateCache().ContractCode(common.BytesToHash(request.AccKey), common.BytesToHash(account.CodeHash)) - if err != nil { - p.Log().Warn("Failed to retrieve account code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "codehash", common.BytesToHash(account.CodeHash), "err", err) - continue - } - // Accumulate the code and abort if enough data was retrieved - data = append(data, code) - if bytes += len(code); bytes >= softResponseLimit { - break - } - } - reply := p.replyCode(req.ReqID, data) - sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) - if metrics.EnabledExpensive { - miscOutCodePacketsMeter.Mark(1) - miscOutCodeTrafficMeter.Mark(int64(reply.size())) - miscServingTimeCodeTimer.Update(time.Duration(task.servingTime)) - } - }() - } - - case GetReceiptsMsg: - p.Log().Trace("Received receipts request") - if metrics.EnabledExpensive { - miscInReceiptPacketsMeter.Mark(1) - miscInReceiptTrafficMeter.Mark(int64(msg.Size)) - } - var req struct { - ReqID uint64 - Hashes []common.Hash - } - if err := msg.Decode(&req); err != nil { - clientErrorMeter.Mark(1) - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - var ( - bytes int - receipts []rlp.RawValue - ) - reqCnt := len(req.Hashes) - if accept(req.ReqID, uint64(reqCnt), MaxReceiptFetch) { - wg.Add(1) - go func() { - defer wg.Done() - for i, hash := range req.Hashes { - if i != 0 && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - if bytes >= softResponseLimit { - break - } - // Retrieve the requested block's receipts, skipping if unknown to us - results := h.blockchain.GetReceiptsByHash(hash) - if results == nil { - if header := h.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { - p.bumpInvalid() - continue - } - } - // If known, encode and queue for response packet - if encoded, err := rlp.EncodeToBytes(results); err != nil { - log.Error("Failed to encode receipt", "err", err) - } else { - receipts = append(receipts, encoded) - bytes += len(encoded) - } - } - reply := p.replyReceiptsRLP(req.ReqID, receipts) - sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) - if metrics.EnabledExpensive { - miscOutReceiptPacketsMeter.Mark(1) - miscOutReceiptTrafficMeter.Mark(int64(reply.size())) - miscServingTimeReceiptTimer.Update(time.Duration(task.servingTime)) - } - }() - } - - case GetProofsV2Msg: - p.Log().Trace("Received les/2 proofs request") - if metrics.EnabledExpensive { - miscInTrieProofPacketsMeter.Mark(1) - miscInTrieProofTrafficMeter.Mark(int64(msg.Size)) - } - var req struct { - ReqID uint64 - Reqs []ProofReq - } - if err := msg.Decode(&req); err != nil { - clientErrorMeter.Mark(1) - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather state data until the fetch or network limits is reached - var ( - lastBHash common.Hash - root common.Hash - ) - reqCnt := len(req.Reqs) - if accept(req.ReqID, uint64(reqCnt), MaxProofsFetch) { - wg.Add(1) - go func() { - defer wg.Done() - nodes := light.NewNodeSet() - - for i, request := range req.Reqs { - if i != 0 && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - // Look up the root hash belonging to the request - var ( - header *types.Header - trie state.Trie - ) - if request.BHash != lastBHash { - root, lastBHash = common.Hash{}, request.BHash - - if header = h.blockchain.GetHeaderByHash(request.BHash); header == nil { - p.Log().Warn("Failed to retrieve header for proof", "hash", request.BHash) - p.bumpInvalid() - continue - } - // Refuse to search stale state data in the database since looking for - // a non-exist key is kind of expensive. - local := h.blockchain.CurrentHeader().Number.Uint64() - if !h.server.archiveMode && header.Number.Uint64()+core.TriesInMemory <= local { - p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local) - p.bumpInvalid() - continue - } - root = header.Root - } - // If a header lookup failed (non existent), ignore subsequent requests for the same header - if root == (common.Hash{}) { - p.bumpInvalid() - continue - } - // Open the account or storage trie for the request - statedb := h.blockchain.StateCache() - - switch len(request.AccKey) { - case 0: - // No account key specified, open an account trie - trie, err = statedb.OpenTrie(root) - if trie == nil || err != nil { - p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "root", root, "err", err) - continue - } - default: - // Account key specified, open a storage trie - account, err := h.getAccount(statedb.TrieDB(), root, common.BytesToHash(request.AccKey)) - if err != nil { - p.Log().Warn("Failed to retrieve account for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) - p.bumpInvalid() - continue - } - trie, err = statedb.OpenStorageTrie(common.BytesToHash(request.AccKey), account.Root) - if trie == nil || err != nil { - p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "root", account.Root, "err", err) - continue - } - } - // Prove the user's request from the account or stroage trie - if err := trie.Prove(request.Key, request.FromLevel, nodes); err != nil { - p.Log().Warn("Failed to prove state request", "block", header.Number, "hash", header.Hash(), "err", err) - continue - } - if nodes.DataSize() >= softResponseLimit { - break - } - } - reply := p.replyProofsV2(req.ReqID, nodes.NodeList()) - sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) - if metrics.EnabledExpensive { - miscOutTrieProofPacketsMeter.Mark(1) - miscOutTrieProofTrafficMeter.Mark(int64(reply.size())) - miscServingTimeTrieProofTimer.Update(time.Duration(task.servingTime)) - } - }() - } - - case GetHelperTrieProofsMsg: - p.Log().Trace("Received helper trie proof request") - if metrics.EnabledExpensive { - miscInHelperTriePacketsMeter.Mark(1) - miscInHelperTrieTrafficMeter.Mark(int64(msg.Size)) - } - var req struct { - ReqID uint64 - Reqs []HelperTrieReq - } - if err := msg.Decode(&req); err != nil { - clientErrorMeter.Mark(1) - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather state data until the fetch or network limits is reached - var ( - auxBytes int - auxData [][]byte - ) - reqCnt := len(req.Reqs) - if accept(req.ReqID, uint64(reqCnt), MaxHelperTrieProofsFetch) { - wg.Add(1) - go func() { - defer wg.Done() - var ( - lastIdx uint64 - lastType uint - root common.Hash - auxTrie *trie.Trie - ) - nodes := light.NewNodeSet() - for i, request := range req.Reqs { - if i != 0 && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - if auxTrie == nil || request.Type != lastType || request.TrieIdx != lastIdx { - auxTrie, lastType, lastIdx = nil, request.Type, request.TrieIdx + if metrics.EnabledExpensive { + req.InPacketsMeter.Mark(1) + req.InTrafficMeter.Mark(int64(msg.Size)) + } + p.responseCount++ + responseCount := p.responseCount - var prefix string - if root, prefix = h.getHelperTrie(request.Type, request.TrieIdx); root != (common.Hash{}) { - auxTrie, _ = trie.New(root, trie.NewDatabase(rawdb.NewTable(h.chainDb, prefix))) - } - } - if request.AuxReq == auxRoot { - var data []byte - if root != (common.Hash{}) { - data = root[:] - } - auxData = append(auxData, data) - auxBytes += len(data) - } else { - if auxTrie != nil { - auxTrie.Prove(request.Key, request.FromLevel, nodes) - } - if request.AuxReq != 0 { - data := h.getAuxiliaryHeaders(request) - auxData = append(auxData, data) - auxBytes += len(data) - } - } - if nodes.DataSize()+auxBytes >= softResponseLimit { - break - } - } - reply := p.replyHelperTrieProofs(req.ReqID, HelperTrieResps{Proofs: nodes.NodeList(), AuxData: auxData}) - sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) - if metrics.EnabledExpensive { - miscOutHelperTriePacketsMeter.Mark(1) - miscOutHelperTrieTrafficMeter.Mark(int64(reply.size())) - miscServingTimeHelperTrieTimer.Update(time.Duration(task.servingTime)) - } - }() - } + // First check this client message complies all rules before + // handling it and return a processor if all checks are passed. + task, maxCost := h.beforeHandle(p, reqID, responseCount, msg, reqCnt, req.MaxCount) + if task == nil { + return nil + } + wg.Add(1) + go func() { + defer wg.Done() - case SendTxV2Msg: - p.Log().Trace("Received new transactions") - if metrics.EnabledExpensive { - miscInTxsPacketsMeter.Mark(1) - miscInTxsTrafficMeter.Mark(int64(msg.Size)) - } - var req struct { - ReqID uint64 - Txs []*types.Transaction - } - if err := msg.Decode(&req); err != nil { - clientErrorMeter.Mark(1) - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - reqCnt := len(req.Txs) - if accept(req.ReqID, uint64(reqCnt), MaxTxSend) { - wg.Add(1) - go func() { - defer wg.Done() - stats := make([]light.TxStatus, len(req.Txs)) - for i, tx := range req.Txs { - if i != 0 && !task.waitOrStop() { - return - } - hash := tx.Hash() - stats[i] = h.txStatus(hash) - if stats[i].Status == core.TxStatusUnknown { - addFn := h.txpool.AddRemotes - // Add txs synchronously for testing purpose - if h.addTxsSync { - addFn = h.txpool.AddRemotesSync - } - if errs := addFn([]*types.Transaction{tx}); errs[0] != nil { - stats[i].Error = errs[0].Error() - continue - } - stats[i] = h.txStatus(hash) - } - } - reply := p.replyTxStatus(req.ReqID, stats) - sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) - if metrics.EnabledExpensive { - miscOutTxsPacketsMeter.Mark(1) - miscOutTxsTrafficMeter.Mark(int64(reply.size())) - miscServingTimeTxTimer.Update(time.Duration(task.servingTime)) - } - }() - } + reply := serve(h, p, task.waitOrStop) + h.afterHandle(p, reqID, responseCount, msg, maxCost, reqCnt, task, reply) - case GetTxStatusMsg: - p.Log().Trace("Received transaction status query request") if metrics.EnabledExpensive { - miscInTxStatusPacketsMeter.Mark(1) - miscInTxStatusTrafficMeter.Mark(int64(msg.Size)) - } - var req struct { - ReqID uint64 - Hashes []common.Hash - } - if err := msg.Decode(&req); err != nil { - clientErrorMeter.Mark(1) - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - reqCnt := len(req.Hashes) - if accept(req.ReqID, uint64(reqCnt), MaxTxStatus) { - wg.Add(1) - go func() { - defer wg.Done() - stats := make([]light.TxStatus, len(req.Hashes)) - for i, hash := range req.Hashes { - if i != 0 && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - stats[i] = h.txStatus(hash) - } - reply := p.replyTxStatus(req.ReqID, stats) - sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) - if metrics.EnabledExpensive { - miscOutTxStatusPacketsMeter.Mark(1) - miscOutTxStatusTrafficMeter.Mark(int64(reply.size())) - miscServingTimeTxStatusTimer.Update(time.Duration(task.servingTime)) - } - }() + size := uint32(0) + if reply != nil { + size = reply.size() + } + req.OutPacketsMeter.Mark(1) + req.OutTrafficMeter.Mark(int64(size)) + req.ServingTimeMeter.Update(time.Duration(task.servingTime)) } - - default: - p.Log().Trace("Received invalid message", "code", msg.Code) - clientErrorMeter.Mark(1) - return errResp(ErrInvalidMsgCode, "%v", msg.Code) - } + }() // If the client has made too much invalid request(e.g. request a non-existent data), // reject them to prevent SPAM attack. if p.getInvalid() > maxRequestErrors { @@ -870,8 +342,28 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { return nil } +// BlockChain implements serverBackend +func (h *serverHandler) BlockChain() *core.BlockChain { + return h.blockchain +} + +// TxPool implements serverBackend +func (h *serverHandler) TxPool() *core.TxPool { + return h.txpool +} + +// ArchiveMode implements serverBackend +func (h *serverHandler) ArchiveMode() bool { + return h.server.archiveMode +} + +// AddTxsSync implements serverBackend +func (h *serverHandler) AddTxsSync() bool { + return h.addTxsSync +} + // getAccount retrieves an account from the state based on root. -func (h *serverHandler) getAccount(triedb *trie.Database, root, hash common.Hash) (state.Account, error) { +func getAccount(triedb *trie.Database, root, hash common.Hash) (state.Account, error) { trie, err := trie.New(root, triedb) if err != nil { return state.Account{}, err @@ -888,43 +380,24 @@ func (h *serverHandler) getAccount(triedb *trie.Database, root, hash common.Hash } // getHelperTrie returns the post-processed trie root for the given trie ID and section index -func (h *serverHandler) getHelperTrie(typ uint, index uint64) (common.Hash, string) { +func (h *serverHandler) GetHelperTrie(typ uint, index uint64) *trie.Trie { + var ( + root common.Hash + prefix string + ) switch typ { case htCanonical: sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.ChtSize-1) - return light.GetChtRoot(h.chainDb, index, sectionHead), light.ChtTablePrefix + root, prefix = light.GetChtRoot(h.chainDb, index, sectionHead), light.ChtTablePrefix case htBloomBits: sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.BloomTrieSize-1) - return light.GetBloomTrieRoot(h.chainDb, index, sectionHead), light.BloomTrieTablePrefix - } - return common.Hash{}, "" -} - -// getAuxiliaryHeaders returns requested auxiliary headers for the CHT request. -func (h *serverHandler) getAuxiliaryHeaders(req HelperTrieReq) []byte { - if req.Type == htCanonical && req.AuxReq == auxHeader && len(req.Key) == 8 { - blockNum := binary.BigEndian.Uint64(req.Key) - hash := rawdb.ReadCanonicalHash(h.chainDb, blockNum) - return rawdb.ReadHeaderRLP(h.chainDb, hash, blockNum) + root, prefix = light.GetBloomTrieRoot(h.chainDb, index, sectionHead), light.BloomTrieTablePrefix } - return nil -} - -// txStatus returns the status of a specified transaction. -func (h *serverHandler) txStatus(hash common.Hash) light.TxStatus { - var stat light.TxStatus - // Looking the transaction in txpool first. - stat.Status = h.txpool.Status([]common.Hash{hash})[0] - - // If the transaction is unknown to the pool, try looking it up locally. - if stat.Status == core.TxStatusUnknown { - lookup := h.blockchain.GetTransactionLookup(hash) - if lookup != nil { - stat.Status = core.TxStatusIncluded - stat.Lookup = lookup - } + if root == (common.Hash{}) { + return nil } - return stat + trie, _ := trie.New(root, trie.NewDatabase(rawdb.NewTable(h.chainDb, prefix))) + return trie } // broadcastLoop broadcasts new block information to all connected light @@ -957,78 +430,9 @@ func (h *serverHandler) broadcastLoop() { } lastHead, lastTd = header, td log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg) - h.server.broadcaster.broadcast(announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg}) + h.server.peers.broadcast(announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg}) case <-h.closeCh: return } } } - -// broadcaster sends new header announcements to active client peers -type broadcaster struct { - ns *nodestate.NodeStateMachine - privateKey *ecdsa.PrivateKey - lastAnnounce, signedAnnounce announceData -} - -// newBroadcaster creates a new broadcaster -func newBroadcaster(ns *nodestate.NodeStateMachine) *broadcaster { - b := &broadcaster{ns: ns} - ns.SubscribeState(priorityPoolSetup.ActiveFlag, func(node *enode.Node, oldState, newState nodestate.Flags) { - if newState.Equals(priorityPoolSetup.ActiveFlag) { - // send last announcement to activated peers - b.sendTo(node) - } - }) - return b -} - -// setSignerKey sets the signer key for signed announcements. Should be called before -// starting the protocol handler. -func (b *broadcaster) setSignerKey(privateKey *ecdsa.PrivateKey) { - b.privateKey = privateKey -} - -// broadcast sends the given announcements to all active peers -func (b *broadcaster) broadcast(announce announceData) { - b.ns.Operation(func() { - // iterate in an Operation to ensure that the active set does not change while iterating - b.lastAnnounce = announce - b.ns.ForEach(priorityPoolSetup.ActiveFlag, nodestate.Flags{}, func(node *enode.Node, state nodestate.Flags) { - b.sendTo(node) - }) - }) -} - -// sendTo sends the most recent announcement to the given node unless the same or higher Td -// announcement has already been sent. -func (b *broadcaster) sendTo(node *enode.Node) { - if b.lastAnnounce.Td == nil { - return - } - if p, _ := b.ns.GetField(node, clientPeerField).(*clientPeer); p != nil { - if p.headInfo.Td == nil || b.lastAnnounce.Td.Cmp(p.headInfo.Td) > 0 { - announce := b.lastAnnounce - switch p.announceType { - case announceTypeSimple: - if !p.queueSend(func() { p.sendAnnounce(announce) }) { - log.Debug("Drop announcement because queue is full", "number", announce.Number, "hash", announce.Hash) - } else { - log.Debug("Sent announcement", "number", announce.Number, "hash", announce.Hash) - } - case announceTypeSigned: - if b.signedAnnounce.Hash != b.lastAnnounce.Hash { - b.signedAnnounce = b.lastAnnounce - b.signedAnnounce.sign(b.privateKey) - } - announce := b.signedAnnounce - if !p.queueSend(func() { p.sendAnnounce(announce) }) { - log.Debug("Drop announcement because queue is full", "number", announce.Number, "hash", announce.Hash) - } else { - log.Debug("Sent announcement", "number", announce.Number, "hash", announce.Hash) - } - } - p.headInfo = blockInfo{b.lastAnnounce.Hash, b.lastAnnounce.Number, b.lastAnnounce.Td} - } - } -} diff --git a/les/server_requests.go b/les/server_requests.go new file mode 100644 index 0000000000..bab5f733d5 --- /dev/null +++ b/les/server_requests.go @@ -0,0 +1,569 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "encoding/binary" + "encoding/json" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +// serverBackend defines the backend functions needed for serving LES requests +type serverBackend interface { + ArchiveMode() bool + AddTxsSync() bool + BlockChain() *core.BlockChain + TxPool() *core.TxPool + GetHelperTrie(typ uint, index uint64) *trie.Trie +} + +// Decoder is implemented by the messages passed to the handler functions +type Decoder interface { + Decode(val interface{}) error +} + +// RequestType is a static struct that describes an LES request type and references +// its handler function. +type RequestType struct { + Name string + MaxCount uint64 + InPacketsMeter, InTrafficMeter, OutPacketsMeter, OutTrafficMeter metrics.Meter + ServingTimeMeter metrics.Timer + Handle func(msg Decoder) (serve serveRequestFn, reqID, amount uint64, err error) +} + +// serveRequestFn is returned by the request handler functions after decoding the request. +// This function does the actual request serving using the supplied backend. waitOrStop is +// called between serving individual request items and may block if the serving process +// needs to be throttled. If it returns false then the process is terminated. +// The reply is not sent by this function yet. The flow control feedback value is supplied +// by the protocol handler when calling the send function of the returned reply struct. +type serveRequestFn func(backend serverBackend, peer *clientPeer, waitOrStop func() bool) *reply + +// Les3 contains the request types supported by les/2 and les/3 +var Les3 = map[uint64]RequestType{ + GetBlockHeadersMsg: { + Name: "block header request", + MaxCount: MaxHeaderFetch, + InPacketsMeter: miscInHeaderPacketsMeter, + InTrafficMeter: miscInHeaderTrafficMeter, + OutPacketsMeter: miscOutHeaderPacketsMeter, + OutTrafficMeter: miscOutHeaderTrafficMeter, + ServingTimeMeter: miscServingTimeHeaderTimer, + Handle: handleGetBlockHeaders, + }, + GetBlockBodiesMsg: { + Name: "block bodies request", + MaxCount: MaxBodyFetch, + InPacketsMeter: miscInBodyPacketsMeter, + InTrafficMeter: miscInBodyTrafficMeter, + OutPacketsMeter: miscOutBodyPacketsMeter, + OutTrafficMeter: miscOutBodyTrafficMeter, + ServingTimeMeter: miscServingTimeBodyTimer, + Handle: handleGetBlockBodies, + }, + GetCodeMsg: { + Name: "code request", + MaxCount: MaxCodeFetch, + InPacketsMeter: miscInCodePacketsMeter, + InTrafficMeter: miscInCodeTrafficMeter, + OutPacketsMeter: miscOutCodePacketsMeter, + OutTrafficMeter: miscOutCodeTrafficMeter, + ServingTimeMeter: miscServingTimeCodeTimer, + Handle: handleGetCode, + }, + GetReceiptsMsg: { + Name: "receipts request", + MaxCount: MaxReceiptFetch, + InPacketsMeter: miscInReceiptPacketsMeter, + InTrafficMeter: miscInReceiptTrafficMeter, + OutPacketsMeter: miscOutReceiptPacketsMeter, + OutTrafficMeter: miscOutReceiptTrafficMeter, + ServingTimeMeter: miscServingTimeReceiptTimer, + Handle: handleGetReceipts, + }, + GetProofsV2Msg: { + Name: "les/2 proofs request", + MaxCount: MaxProofsFetch, + InPacketsMeter: miscInTrieProofPacketsMeter, + InTrafficMeter: miscInTrieProofTrafficMeter, + OutPacketsMeter: miscOutTrieProofPacketsMeter, + OutTrafficMeter: miscOutTrieProofTrafficMeter, + ServingTimeMeter: miscServingTimeTrieProofTimer, + Handle: handleGetProofs, + }, + GetHelperTrieProofsMsg: { + Name: "helper trie proof request", + MaxCount: MaxHelperTrieProofsFetch, + InPacketsMeter: miscInHelperTriePacketsMeter, + InTrafficMeter: miscInHelperTrieTrafficMeter, + OutPacketsMeter: miscOutHelperTriePacketsMeter, + OutTrafficMeter: miscOutHelperTrieTrafficMeter, + ServingTimeMeter: miscServingTimeHelperTrieTimer, + Handle: handleGetHelperTrieProofs, + }, + SendTxV2Msg: { + Name: "new transactions", + MaxCount: MaxTxSend, + InPacketsMeter: miscInTxsPacketsMeter, + InTrafficMeter: miscInTxsTrafficMeter, + OutPacketsMeter: miscOutTxsPacketsMeter, + OutTrafficMeter: miscOutTxsTrafficMeter, + ServingTimeMeter: miscServingTimeTxTimer, + Handle: handleSendTx, + }, + GetTxStatusMsg: { + Name: "transaction status query request", + MaxCount: MaxTxStatus, + InPacketsMeter: miscInTxStatusPacketsMeter, + InTrafficMeter: miscInTxStatusTrafficMeter, + OutPacketsMeter: miscOutTxStatusPacketsMeter, + OutTrafficMeter: miscOutTxStatusTrafficMeter, + ServingTimeMeter: miscServingTimeTxStatusTimer, + Handle: handleGetTxStatus, + }, +} + +// handleGetBlockHeaders handles a block header request +func handleGetBlockHeaders(msg Decoder) (serveRequestFn, uint64, uint64, error) { + var r GetBlockHeadersPacket + if err := msg.Decode(&r); err != nil { + return nil, 0, 0, err + } + return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { + // Gather headers until the fetch or network limits is reached + var ( + bc = backend.BlockChain() + hashMode = r.Query.Origin.Hash != (common.Hash{}) + first = true + maxNonCanonical = uint64(100) + bytes common.StorageSize + headers []*types.Header + unknown bool + ) + for !unknown && len(headers) < int(r.Query.Amount) && bytes < softResponseLimit { + if !first && !waitOrStop() { + return nil + } + // Retrieve the next header satisfying the r + var origin *types.Header + if hashMode { + if first { + origin = bc.GetHeaderByHash(r.Query.Origin.Hash) + if origin != nil { + r.Query.Origin.Number = origin.Number.Uint64() + } + } else { + origin = bc.GetHeader(r.Query.Origin.Hash, r.Query.Origin.Number) + } + } else { + origin = bc.GetHeaderByNumber(r.Query.Origin.Number) + } + if origin == nil { + break + } + headers = append(headers, origin) + bytes += estHeaderRlpSize + + // Advance to the next header of the r + switch { + case hashMode && r.Query.Reverse: + // Hash based traversal towards the genesis block + ancestor := r.Query.Skip + 1 + if ancestor == 0 { + unknown = true + } else { + r.Query.Origin.Hash, r.Query.Origin.Number = bc.GetAncestor(r.Query.Origin.Hash, r.Query.Origin.Number, ancestor, &maxNonCanonical) + unknown = r.Query.Origin.Hash == common.Hash{} + } + case hashMode && !r.Query.Reverse: + // Hash based traversal towards the leaf block + var ( + current = origin.Number.Uint64() + next = current + r.Query.Skip + 1 + ) + if next <= current { + infos, _ := json.Marshal(p.Peer.Info()) + p.Log().Warn("GetBlockHeaders skip overflow attack", "current", current, "skip", r.Query.Skip, "next", next, "attacker", string(infos)) + unknown = true + } else { + if header := bc.GetHeaderByNumber(next); header != nil { + nextHash := header.Hash() + expOldHash, _ := bc.GetAncestor(nextHash, next, r.Query.Skip+1, &maxNonCanonical) + if expOldHash == r.Query.Origin.Hash { + r.Query.Origin.Hash, r.Query.Origin.Number = nextHash, next + } else { + unknown = true + } + } else { + unknown = true + } + } + case r.Query.Reverse: + // Number based traversal towards the genesis block + if r.Query.Origin.Number >= r.Query.Skip+1 { + r.Query.Origin.Number -= r.Query.Skip + 1 + } else { + unknown = true + } + + case !r.Query.Reverse: + // Number based traversal towards the leaf block + r.Query.Origin.Number += r.Query.Skip + 1 + } + first = false + } + return p.replyBlockHeaders(r.ReqID, headers) + }, r.ReqID, r.Query.Amount, nil +} + +// handleGetBlockBodies handles a block body request +func handleGetBlockBodies(msg Decoder) (serveRequestFn, uint64, uint64, error) { + var r GetBlockBodiesPacket + if err := msg.Decode(&r); err != nil { + return nil, 0, 0, err + } + return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { + var ( + bytes int + bodies []rlp.RawValue + ) + bc := backend.BlockChain() + for i, hash := range r.Hashes { + if i != 0 && !waitOrStop() { + return nil + } + if bytes >= softResponseLimit { + break + } + body := bc.GetBodyRLP(hash) + if body == nil { + p.bumpInvalid() + continue + } + bodies = append(bodies, body) + bytes += len(body) + } + return p.replyBlockBodiesRLP(r.ReqID, bodies) + }, r.ReqID, uint64(len(r.Hashes)), nil +} + +// handleGetCode handles a contract code request +func handleGetCode(msg Decoder) (serveRequestFn, uint64, uint64, error) { + var r GetCodePacket + if err := msg.Decode(&r); err != nil { + return nil, 0, 0, err + } + return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { + var ( + bytes int + data [][]byte + ) + bc := backend.BlockChain() + for i, request := range r.Reqs { + if i != 0 && !waitOrStop() { + return nil + } + // Look up the root hash belonging to the request + header := bc.GetHeaderByHash(request.BHash) + if header == nil { + p.Log().Warn("Failed to retrieve associate header for code", "hash", request.BHash) + p.bumpInvalid() + continue + } + // Refuse to search stale state data in the database since looking for + // a non-exist key is kind of expensive. + local := bc.CurrentHeader().Number.Uint64() + if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local { + p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local) + p.bumpInvalid() + continue + } + triedb := bc.StateCache().TrieDB() + + account, err := getAccount(triedb, header.Root, common.BytesToHash(request.AccKey)) + if err != nil { + p.Log().Warn("Failed to retrieve account for code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) + p.bumpInvalid() + continue + } + code, err := bc.StateCache().ContractCode(common.BytesToHash(request.AccKey), common.BytesToHash(account.CodeHash)) + if err != nil { + p.Log().Warn("Failed to retrieve account code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "codehash", common.BytesToHash(account.CodeHash), "err", err) + continue + } + // Accumulate the code and abort if enough data was retrieved + data = append(data, code) + if bytes += len(code); bytes >= softResponseLimit { + break + } + } + return p.replyCode(r.ReqID, data) + }, r.ReqID, uint64(len(r.Reqs)), nil +} + +// handleGetReceipts handles a block receipts request +func handleGetReceipts(msg Decoder) (serveRequestFn, uint64, uint64, error) { + var r GetReceiptsPacket + if err := msg.Decode(&r); err != nil { + return nil, 0, 0, err + } + return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { + var ( + bytes int + receipts []rlp.RawValue + ) + bc := backend.BlockChain() + for i, hash := range r.Hashes { + if i != 0 && !waitOrStop() { + return nil + } + if bytes >= softResponseLimit { + break + } + // Retrieve the requested block's receipts, skipping if unknown to us + results := bc.GetReceiptsByHash(hash) + if results == nil { + if header := bc.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { + p.bumpInvalid() + continue + } + } + // If known, encode and queue for response packet + if encoded, err := rlp.EncodeToBytes(results); err != nil { + log.Error("Failed to encode receipt", "err", err) + } else { + receipts = append(receipts, encoded) + bytes += len(encoded) + } + } + return p.replyReceiptsRLP(r.ReqID, receipts) + }, r.ReqID, uint64(len(r.Hashes)), nil +} + +// handleGetProofs handles a proof request +func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) { + var r GetProofsPacket + if err := msg.Decode(&r); err != nil { + return nil, 0, 0, err + } + return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { + var ( + lastBHash common.Hash + root common.Hash + header *types.Header + err error + ) + bc := backend.BlockChain() + nodes := light.NewNodeSet() + + for i, request := range r.Reqs { + if i != 0 && !waitOrStop() { + return nil + } + // Look up the root hash belonging to the request + if request.BHash != lastBHash { + root, lastBHash = common.Hash{}, request.BHash + + if header = bc.GetHeaderByHash(request.BHash); header == nil { + p.Log().Warn("Failed to retrieve header for proof", "hash", request.BHash) + p.bumpInvalid() + continue + } + // Refuse to search stale state data in the database since looking for + // a non-exist key is kind of expensive. + local := bc.CurrentHeader().Number.Uint64() + if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local { + p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local) + p.bumpInvalid() + continue + } + root = header.Root + } + // If a header lookup failed (non existent), ignore subsequent requests for the same header + if root == (common.Hash{}) { + p.bumpInvalid() + continue + } + // Open the account or storage trie for the request + statedb := bc.StateCache() + + var trie state.Trie + switch len(request.AccKey) { + case 0: + // No account key specified, open an account trie + trie, err = statedb.OpenTrie(root) + if trie == nil || err != nil { + p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "root", root, "err", err) + continue + } + default: + // Account key specified, open a storage trie + account, err := getAccount(statedb.TrieDB(), root, common.BytesToHash(request.AccKey)) + if err != nil { + p.Log().Warn("Failed to retrieve account for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) + p.bumpInvalid() + continue + } + trie, err = statedb.OpenStorageTrie(common.BytesToHash(request.AccKey), account.Root) + if trie == nil || err != nil { + p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "root", account.Root, "err", err) + continue + } + } + // Prove the user's request from the account or stroage trie + if err := trie.Prove(request.Key, request.FromLevel, nodes); err != nil { + p.Log().Warn("Failed to prove state request", "block", header.Number, "hash", header.Hash(), "err", err) + continue + } + if nodes.DataSize() >= softResponseLimit { + break + } + } + return p.replyProofsV2(r.ReqID, nodes.NodeList()) + }, r.ReqID, uint64(len(r.Reqs)), nil +} + +// handleGetHelperTrieProofs handles a helper trie proof request +func handleGetHelperTrieProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) { + var r GetHelperTrieProofsPacket + if err := msg.Decode(&r); err != nil { + return nil, 0, 0, err + } + return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { + var ( + lastIdx uint64 + lastType uint + auxTrie *trie.Trie + auxBytes int + auxData [][]byte + ) + bc := backend.BlockChain() + nodes := light.NewNodeSet() + for i, request := range r.Reqs { + if i != 0 && !waitOrStop() { + return nil + } + if auxTrie == nil || request.Type != lastType || request.TrieIdx != lastIdx { + lastType, lastIdx = request.Type, request.TrieIdx + auxTrie = backend.GetHelperTrie(request.Type, request.TrieIdx) + } + if auxTrie == nil { + return nil + } + // TODO(rjl493456442) short circuit if the proving is failed. + // The original client side code has a dirty hack to retrieve + // the headers with no valid proof. Keep the compatibility for + // legacy les protocol and drop this hack when the les2/3 are + // not supported. + err := auxTrie.Prove(request.Key, request.FromLevel, nodes) + if p.version >= lpv4 && err != nil { + return nil + } + if request.Type == htCanonical && request.AuxReq == htAuxHeader && len(request.Key) == 8 { + header := bc.GetHeaderByNumber(binary.BigEndian.Uint64(request.Key)) + data, err := rlp.EncodeToBytes(header) + if err != nil { + log.Error("Failed to encode header", "err", err) + return nil + } + auxData = append(auxData, data) + auxBytes += len(data) + } + if nodes.DataSize()+auxBytes >= softResponseLimit { + break + } + } + return p.replyHelperTrieProofs(r.ReqID, HelperTrieResps{Proofs: nodes.NodeList(), AuxData: auxData}) + }, r.ReqID, uint64(len(r.Reqs)), nil +} + +// handleSendTx handles a transaction propagation request +func handleSendTx(msg Decoder) (serveRequestFn, uint64, uint64, error) { + var r SendTxPacket + if err := msg.Decode(&r); err != nil { + return nil, 0, 0, err + } + amount := uint64(len(r.Txs)) + return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { + stats := make([]light.TxStatus, len(r.Txs)) + for i, tx := range r.Txs { + if i != 0 && !waitOrStop() { + return nil + } + hash := tx.Hash() + stats[i] = txStatus(backend, hash) + if stats[i].Status == core.TxStatusUnknown { + addFn := backend.TxPool().AddRemotes + // Add txs synchronously for testing purpose + if backend.AddTxsSync() { + addFn = backend.TxPool().AddRemotesSync + } + if errs := addFn([]*types.Transaction{tx}); errs[0] != nil { + stats[i].Error = errs[0].Error() + continue + } + stats[i] = txStatus(backend, hash) + } + } + return p.replyTxStatus(r.ReqID, stats) + }, r.ReqID, amount, nil +} + +// handleGetTxStatus handles a transaction status query +func handleGetTxStatus(msg Decoder) (serveRequestFn, uint64, uint64, error) { + var r GetTxStatusPacket + if err := msg.Decode(&r); err != nil { + return nil, 0, 0, err + } + return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { + stats := make([]light.TxStatus, len(r.Hashes)) + for i, hash := range r.Hashes { + if i != 0 && !waitOrStop() { + return nil + } + stats[i] = txStatus(backend, hash) + } + return p.replyTxStatus(r.ReqID, stats) + }, r.ReqID, uint64(len(r.Hashes)), nil +} + +// txStatus returns the status of a specified transaction. +func txStatus(b serverBackend, hash common.Hash) light.TxStatus { + var stat light.TxStatus + // Looking the transaction in txpool first. + stat.Status = b.TxPool().Status([]common.Hash{hash})[0] + + // If the transaction is unknown to the pool, try looking it up locally. + if stat.Status == core.TxStatusUnknown { + lookup := b.BlockChain().GetTransactionLookup(hash) + if lookup != nil { + stat.Status = core.TxStatusIncluded + stat.Lookup = lookup + } + } + return stat +} diff --git a/les/servingqueue.go b/les/servingqueue.go index 9db84e6159..16e064cb3f 100644 --- a/les/servingqueue.go +++ b/les/servingqueue.go @@ -123,7 +123,7 @@ func (t *servingTask) waitOrStop() bool { // newServingQueue returns a new servingQueue func newServingQueue(suspendBias int64, utilTarget float64) *servingQueue { sq := &servingQueue{ - queue: prque.New(nil), + queue: prque.NewWrapAround(nil), suspendBias: suspendBias, queueAddCh: make(chan *servingTask, 100), queueBestCh: make(chan *servingTask), @@ -279,7 +279,7 @@ func (sq *servingQueue) updateRecentTime() { func (sq *servingQueue) addTask(task *servingTask) { if sq.best == nil { sq.best = task - } else if task.priority > sq.best.priority { + } else if task.priority-sq.best.priority > 0 { sq.queue.Push(sq.best, sq.best.priority) sq.best = task } else { diff --git a/les/state_accessor.go b/les/state_accessor.go new file mode 100644 index 0000000000..c291b43a6d --- /dev/null +++ b/les/state_accessor.go @@ -0,0 +1,75 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "context" + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/light" +) + +// stateAtBlock retrieves the state database associated with a certain block. +func (leth *LightEthereum) stateAtBlock(ctx context.Context, block *types.Block, reexec uint64) (*state.StateDB, error) { + return light.NewState(ctx, block.Header(), leth.odr), nil +} + +// stateAtTransaction returns the execution environment of a certain transaction. +func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { + // Short circuit if it's genesis block. + if block.NumberU64() == 0 { + return nil, vm.BlockContext{}, nil, errors.New("no transaction in genesis") + } + // Create the parent state database + parent, err := leth.blockchain.GetBlock(ctx, block.ParentHash(), block.NumberU64()-1) + if err != nil { + return nil, vm.BlockContext{}, nil, err + } + statedb, err := leth.stateAtBlock(ctx, parent, reexec) + if err != nil { + return nil, vm.BlockContext{}, nil, err + } + if txIndex == 0 && len(block.Transactions()) == 0 { + return nil, vm.BlockContext{}, statedb, nil + } + // Recompute transactions up to the target index. + signer := types.MakeSigner(leth.blockchain.Config(), block.Number()) + for idx, tx := range block.Transactions() { + // Assemble the transaction call message and return if the requested offset + msg, _ := tx.AsMessage(signer) + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil) + statedb.Prepare(tx.Hash(), block.Hash(), idx) + if idx == txIndex { + return msg, context, statedb, nil + } + // Not yet the searched for transaction, execute on top of the current state + vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{}) + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { + return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) + } + // Ensure any modifications are committed to the state + // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect + statedb.Finalise(vmenv.ChainConfig().IsEnabled(vmenv.ChainConfig().GetEIP161dTransition, block.Number())) + } + return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) +} diff --git a/les/sync.go b/les/sync.go index d2568d45bc..b7849ff47e 100644 --- a/les/sync.go +++ b/les/sync.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params/types/ctypes" ) var errInvalidCheckpoint = errors.New("invalid advertised checkpoint") @@ -56,8 +57,8 @@ func (h *clientHandler) validateCheckpoint(peer *serverPeer) error { defer cancel() // Fetch the block header corresponding to the checkpoint registration. - cp := peer.checkpoint - header, err := light.GetUntrustedHeaderByNumber(ctx, h.backend.odr, peer.checkpointNumber, peer.id) + wrapPeer := &peerConnection{handler: h, peer: peer} + header, err := wrapPeer.RetrieveSingleHeaderByNumber(ctx, peer.checkpointNumber) if err != nil { return err } @@ -66,7 +67,7 @@ func (h *clientHandler) validateCheckpoint(peer *serverPeer) error { if err != nil { return err } - events := h.backend.oracle.Contract().LookupCheckpointEvents(logs, cp.SectionIndex, cp.Hash()) + events := h.backend.oracle.Contract().LookupCheckpointEvents(logs, peer.checkpoint.SectionIndex, peer.checkpoint.Hash()) if len(events) == 0 { return errInvalidCheckpoint } @@ -98,22 +99,33 @@ func (h *clientHandler) synchronise(peer *serverPeer) { if currentTd != nil && peer.Td().Cmp(currentTd) < 0 { return } - // Recap the checkpoint. - // - // The light client may be connected to several different versions of the server. - // (1) Old version server which can not provide stable checkpoint in the handshake packet. - // => Use hardcoded checkpoint or empty checkpoint - // (2) New version server but simple checkpoint syncing is not enabled(e.g. mainnet, new testnet or private network) - // => Use hardcoded checkpoint or empty checkpoint - // (3) New version server but the provided stable checkpoint is even lower than the hardcoded one. - // => Use hardcoded checkpoint + // Recap the checkpoint. The light client may be connected to several different + // versions of the server. + // (1) Old version server which can not provide stable checkpoint in the + // handshake packet. + // => Use local checkpoint or empty checkpoint + // (2) New version server but simple checkpoint syncing is not enabled + // (e.g. mainnet, new testnet or private network) + // => Use local checkpoint or empty checkpoint + // (3) New version server but the provided stable checkpoint is even lower + // than the local one. + // => Use local checkpoint // (4) New version server with valid and higher stable checkpoint // => Use provided checkpoint - var checkpoint = &peer.checkpoint - var hardcoded bool + var ( + local bool + checkpoint = &peer.checkpoint + ) if h.checkpoint != nil && h.checkpoint.SectionIndex >= peer.checkpoint.SectionIndex { - checkpoint = h.checkpoint // Use the hardcoded one. - hardcoded = true + local, checkpoint = true, h.checkpoint + } + // Replace the checkpoint with locally configured one If it's required by + // users. Nil checkpoint means synchronization from the scratch. + if h.backend.config.SyncFromCheckpoint { + local, checkpoint = true, h.backend.config.Checkpoint + if h.backend.config.Checkpoint == nil { + checkpoint = &ctypes.TrustedCheckpoint{} + } } // Determine whether we should run checkpoint syncing or normal light syncing. // @@ -121,7 +133,7 @@ func (h *clientHandler) synchronise(peer *serverPeer) { // // 1. The checkpoint is empty // 2. The latest head block of the local chain is above the checkpoint. - // 3. The checkpoint is hardcoded(recap with local hardcoded checkpoint) + // 3. The checkpoint is local(replaced with local checkpoint) // 4. For some networks the checkpoint syncing is not activated. mode := checkpointSync switch { @@ -131,7 +143,7 @@ func (h *clientHandler) synchronise(peer *serverPeer) { case latest.Number.Uint64() >= (checkpoint.SectionIndex+1)*h.backend.iConfig.ChtSize-1: mode = lightSync log.Debug("Disable checkpoint syncing", "reason", "local chain beyond the checkpoint") - case hardcoded: + case local: mode = legacyCheckpointSync log.Debug("Disable checkpoint syncing", "reason", "checkpoint is hardcoded") case h.backend.oracle == nil || !h.backend.oracle.IsRunning(): @@ -143,12 +155,14 @@ func (h *clientHandler) synchronise(peer *serverPeer) { } log.Debug("Disable checkpoint syncing", "reason", "checkpoint syncing is not activated") } + // Notify testing framework if syncing has completed(for testing purpose). defer func() { - if h.syncDone != nil { - h.syncDone() + if h.syncEnd != nil { + h.syncEnd(h.backend.blockchain.CurrentHeader()) } }() + start := time.Now() if mode == checkpointSync || mode == legacyCheckpointSync { // Validate the advertised checkpoint @@ -177,6 +191,10 @@ func (h *clientHandler) synchronise(peer *serverPeer) { return } } + + if h.syncStart != nil { + h.syncStart(h.backend.blockchain.CurrentHeader()) + } // Fetch the remaining block headers based on the current chain header. if err := h.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync); err != nil { log.Debug("Synchronise failed", "reason", err) diff --git a/les/sync_test.go b/les/sync_test.go index 21c92e9741..7ae662e133 100644 --- a/les/sync_test.go +++ b/les/sync_test.go @@ -24,21 +24,22 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/params/types/ctypes" ) // Test light syncing which will download all headers from genesis. -func TestLightSyncingLes3(t *testing.T) { testCheckpointSyncing(t, 3, 0) } +func TestLightSyncingLes3(t *testing.T) { testCheckpointSyncing(t, lpv3, 0) } // Test legacy checkpoint syncing which will download tail headers // based on a hardcoded checkpoint. -func TestLegacyCheckpointSyncingLes3(t *testing.T) { testCheckpointSyncing(t, 3, 1) } +func TestLegacyCheckpointSyncingLes3(t *testing.T) { testCheckpointSyncing(t, lpv3, 1) } // Test checkpoint syncing which will download tail headers based // on a verified checkpoint. -func TestCheckpointSyncingLes3(t *testing.T) { testCheckpointSyncing(t, 3, 2) } +func TestCheckpointSyncingLes3(t *testing.T) { testCheckpointSyncing(t, lpv3, 2) } func testCheckpointSyncing(t *testing.T, protocol int, syncMode int) { config := light.TestServerIndexerConfig @@ -53,8 +54,14 @@ func testCheckpointSyncing(t *testing.T, protocol int, syncMode int) { time.Sleep(10 * time.Millisecond) } } - // Generate 512+4 blocks (totally 1 CHT sections) - server, client, tearDown := newClientServerEnv(t, int(config.ChtSize+config.ChtConfirms), protocol, waitIndexers, nil, 0, false, false, true) + // Generate 128+1 blocks (totally 1 CHT section) + netconfig := testnetConfig{ + blocks: int(config.ChtSize + config.ChtConfirms), + protocol: protocol, + indexFn: waitIndexers, + nopruning: true, + } + server, client, tearDown := newClientServerEnv(t, netconfig) defer tearDown() expected := config.ChtSize + config.ChtConfirms @@ -77,10 +84,11 @@ func testCheckpointSyncing(t *testing.T, protocol int, syncMode int) { // Register the assembled checkpoint into oracle. header := server.backend.Blockchain().CurrentHeader() - data := append([]byte{0x19, 0x00}, append(registrarAddr.Bytes(), append([]byte{0, 0, 0, 0, 0, 0, 0, 0}, cp.Hash().Bytes()...)...)...) + data := append([]byte{0x19, 0x00}, append(oracleAddr.Bytes(), append([]byte{0, 0, 0, 0, 0, 0, 0, 0}, cp.Hash().Bytes()...)...)...) sig, _ := crypto.Sign(crypto.Keccak256(data), signerKey) sig[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper - if _, err := server.handler.server.oracle.Contract().RegisterCheckpoint(bind.NewKeyedTransactor(signerKey), cp.SectionIndex, cp.Hash().Bytes(), new(big.Int).Sub(header.Number, big.NewInt(1)), header.ParentHash, [][]byte{sig}); err != nil { + auth, _ := bind.NewKeyedTransactorWithChainID(signerKey, big.NewInt(1337)) + if _, err := server.handler.server.oracle.Contract().RegisterCheckpoint(auth, cp.SectionIndex, cp.Hash().Bytes(), new(big.Int).Sub(header.Number, big.NewInt(1)), header.ParentHash, [][]byte{sig}); err != nil { t.Error("register checkpoint failed", err) } server.backend.Commit() @@ -99,8 +107,7 @@ func testCheckpointSyncing(t *testing.T, protocol int, syncMode int) { } done := make(chan error) - client.handler.syncDone = func() { - header := client.handler.backend.blockchain.CurrentHeader() + client.handler.syncEnd = func(header *types.Header) { if header.Number.Uint64() == expected { done <- nil } else { @@ -127,10 +134,10 @@ func testCheckpointSyncing(t *testing.T, protocol int, syncMode int) { } } -func TestMissOracleBackend(t *testing.T) { testMissOracleBackend(t, true) } -func TestMissOracleBackendNoCheckpoint(t *testing.T) { testMissOracleBackend(t, false) } +func TestMissOracleBackendLES3(t *testing.T) { testMissOracleBackend(t, true, lpv3) } +func TestMissOracleBackendNoCheckpointLES3(t *testing.T) { testMissOracleBackend(t, false, lpv3) } -func testMissOracleBackend(t *testing.T, hasCheckpoint bool) { +func testMissOracleBackend(t *testing.T, hasCheckpoint bool, protocol int) { config := light.TestServerIndexerConfig waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) { @@ -143,8 +150,14 @@ func testMissOracleBackend(t *testing.T, hasCheckpoint bool) { time.Sleep(10 * time.Millisecond) } } - // Generate 512+4 blocks (totally 1 CHT sections) - server, client, tearDown := newClientServerEnv(t, int(config.ChtSize+config.ChtConfirms), 3, waitIndexers, nil, 0, false, false, true) + // Generate 128+1 blocks (totally 1 CHT section) + netconfig := testnetConfig{ + blocks: int(config.ChtSize + config.ChtConfirms), + protocol: protocol, + indexFn: waitIndexers, + nopruning: true, + } + server, client, tearDown := newClientServerEnv(t, netconfig) defer tearDown() expected := config.ChtSize + config.ChtConfirms @@ -159,10 +172,11 @@ func testMissOracleBackend(t *testing.T, hasCheckpoint bool) { // Register the assembled checkpoint into oracle. header := server.backend.Blockchain().CurrentHeader() - data := append([]byte{0x19, 0x00}, append(registrarAddr.Bytes(), append([]byte{0, 0, 0, 0, 0, 0, 0, 0}, cp.Hash().Bytes()...)...)...) + data := append([]byte{0x19, 0x00}, append(oracleAddr.Bytes(), append([]byte{0, 0, 0, 0, 0, 0, 0, 0}, cp.Hash().Bytes()...)...)...) sig, _ := crypto.Sign(crypto.Keccak256(data), signerKey) sig[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper - if _, err := server.handler.server.oracle.Contract().RegisterCheckpoint(bind.NewKeyedTransactor(signerKey), cp.SectionIndex, cp.Hash().Bytes(), new(big.Int).Sub(header.Number, big.NewInt(1)), header.ParentHash, [][]byte{sig}); err != nil { + auth, _ := bind.NewKeyedTransactorWithChainID(signerKey, big.NewInt(1337)) + if _, err := server.handler.server.oracle.Contract().RegisterCheckpoint(auth, cp.SectionIndex, cp.Hash().Bytes(), new(big.Int).Sub(header.Number, big.NewInt(1)), header.ParentHash, [][]byte{sig}); err != nil { t.Error("register checkpoint failed", err) } server.backend.Commit() @@ -196,8 +210,7 @@ func testMissOracleBackend(t *testing.T, hasCheckpoint bool) { } done := make(chan error) - client.handler.syncDone = func() { - header := client.handler.backend.blockchain.CurrentHeader() + client.handler.syncEnd = func(header *types.Header) { if header.Number.Uint64() == expected { done <- nil } else { @@ -218,3 +231,160 @@ func testMissOracleBackend(t *testing.T, hasCheckpoint bool) { t.Error("checkpoint syncing timeout") } } + +func TestSyncFromConfiguredCheckpointLES3(t *testing.T) { testSyncFromConfiguredCheckpoint(t, lpv3) } + +func testSyncFromConfiguredCheckpoint(t *testing.T, protocol int) { + config := light.TestServerIndexerConfig + + waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) { + for { + cs, _, _ := cIndexer.Sections() + bts, _, _ := btIndexer.Sections() + if cs >= 2 && bts >= 2 { + break + } + time.Sleep(10 * time.Millisecond) + } + } + // Generate 256+1 blocks (totally 2 CHT sections) + netconfig := testnetConfig{ + blocks: int(2*config.ChtSize + config.ChtConfirms), + protocol: protocol, + indexFn: waitIndexers, + nopruning: true, + } + server, client, tearDown := newClientServerEnv(t, netconfig) + defer tearDown() + + // Configure the local checkpoint(the first section) + head := server.handler.blockchain.GetHeaderByNumber(config.ChtSize - 1).Hash() + cp := &ctypes.TrustedCheckpoint{ + SectionIndex: 0, + SectionHead: head, + CHTRoot: light.GetChtRoot(server.db, 0, head), + BloomRoot: light.GetBloomTrieRoot(server.db, 0, head), + } + client.handler.backend.config.SyncFromCheckpoint = true + client.handler.backend.config.Checkpoint = cp + client.handler.checkpoint = cp + client.handler.backend.blockchain.AddTrustedCheckpoint(cp) + + var ( + start = make(chan error, 1) + end = make(chan error, 1) + expectStart = config.ChtSize - 1 + expectEnd = 2*config.ChtSize + config.ChtConfirms + ) + client.handler.syncStart = func(header *types.Header) { + if header.Number.Uint64() == expectStart { + start <- nil + } else { + start <- fmt.Errorf("blockchain length mismatch, want %d, got %d", expectStart, header.Number) + } + } + client.handler.syncEnd = func(header *types.Header) { + if header.Number.Uint64() == expectEnd { + end <- nil + } else { + end <- fmt.Errorf("blockchain length mismatch, want %d, got %d", expectEnd, header.Number) + } + } + // Create connected peer pair. + if _, _, err := newTestPeerPair("peer", 2, server.handler, client.handler); err != nil { + t.Fatalf("Failed to connect testing peers %v", err) + } + + select { + case err := <-start: + if err != nil { + t.Error("sync failed", err) + } + return + case <-time.NewTimer(10 * time.Second).C: + t.Error("checkpoint syncing timeout") + } + + select { + case err := <-end: + if err != nil { + t.Error("sync failed", err) + } + return + case <-time.NewTimer(10 * time.Second).C: + t.Error("checkpoint syncing timeout") + } +} + +func TestSyncAll(t *testing.T) { testSyncAll(t, lpv3) } + +func testSyncAll(t *testing.T, protocol int) { + config := light.TestServerIndexerConfig + + waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) { + for { + cs, _, _ := cIndexer.Sections() + bts, _, _ := btIndexer.Sections() + if cs >= 2 && bts >= 2 { + break + } + time.Sleep(10 * time.Millisecond) + } + } + // Generate 256+1 blocks (totally 2 CHT sections) + netconfig := testnetConfig{ + blocks: int(2*config.ChtSize + config.ChtConfirms), + protocol: protocol, + indexFn: waitIndexers, + nopruning: true, + } + server, client, tearDown := newClientServerEnv(t, netconfig) + defer tearDown() + + client.handler.backend.config.SyncFromCheckpoint = true + + var ( + start = make(chan error, 1) + end = make(chan error, 1) + expectStart = uint64(0) + expectEnd = 2*config.ChtSize + config.ChtConfirms + ) + client.handler.syncStart = func(header *types.Header) { + if header.Number.Uint64() == expectStart { + start <- nil + } else { + start <- fmt.Errorf("blockchain length mismatch, want %d, got %d", expectStart, header.Number) + } + } + client.handler.syncEnd = func(header *types.Header) { + if header.Number.Uint64() == expectEnd { + end <- nil + } else { + end <- fmt.Errorf("blockchain length mismatch, want %d, got %d", expectEnd, header.Number) + } + } + // Create connected peer pair. + if _, _, err := newTestPeerPair("peer", 2, server.handler, client.handler); err != nil { + t.Fatalf("Failed to connect testing peers %v", err) + } + + select { + case err := <-start: + if err != nil { + t.Error("sync failed", err) + } + return + case <-time.NewTimer(10 * time.Second).C: + t.Error("checkpoint syncing timeout") + } + + select { + case err := <-end: + if err != nil { + t.Error("sync failed", err) + } + return + case <-time.NewTimer(10 * time.Second).C: + t.Error("checkpoint syncing timeout") + } +} diff --git a/les/test_helper.go b/les/test_helper.go index 955e2c1b01..77cbfedf49 100644 --- a/les/test_helper.go +++ b/les/test_helper.go @@ -14,8 +14,9 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// This file contains some shares testing functionality, common to multiple -// different files and modules being tested. +// This file contains some shares testing functionality, common to multiple +// different files and modules being tested. Client based network and Server +// based network can be created easily with available APIs. package les @@ -35,18 +36,19 @@ import ( "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/contracts/checkpointoracle/contract" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/les/checkpointoracle" "github.com/ethereum/go-ethereum/les/flowcontrol" + vfs "github.com/ethereum/go-ethereum/les/vflux/server" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/p2p/nodestate" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/types/ctypes" "github.com/ethereum/go-ethereum/params/types/genesisT" @@ -70,10 +72,10 @@ var ( testEventEmitterCode = common.Hex2Bytes("60606040523415600e57600080fd5b7f57050ab73f6b9ebdd9f76b8d4997793f48cf956e965ee070551b9ca0bb71584e60405160405180910390a160358060476000396000f3006060604052600080fd00a165627a7a723058203f727efcad8b5811f8cb1fc2620ce5e8c63570d697aef968172de296ea3994140029") - // Checkpoint registrar relative - registrarAddr common.Address - signerKey, _ = crypto.GenerateKey() - signerAddr = crypto.PubkeyToAddress(signerKey.PublicKey) + // Checkpoint oracle relative fields + oracleAddr common.Address + signerKey, _ = crypto.GenerateKey() + signerAddr = crypto.PubkeyToAddress(signerKey.PublicKey) ) var ( @@ -114,13 +116,23 @@ func prepare(n int, backend *backends.SimulatedBackend) { for i := 0; i < n; i++ { switch i { case 0: + // Builtin-block + // number: 1 + // txs: 2 + // deploy checkpoint contract - registrarAddr, _, _, _ = contract.DeployCheckpointOracle(bind.NewKeyedTransactor(bankKey), backend, []common.Address{signerAddr}, sectionSize, processConfirms, big.NewInt(1)) + auth, _ := bind.NewKeyedTransactorWithChainID(bankKey, big.NewInt(1337)) + oracleAddr, _, _, _ = contract.DeployCheckpointOracle(auth, backend, []common.Address{signerAddr}, sectionSize, processConfirms, big.NewInt(1)) + // bankUser transfers some ether to user1 nonce, _ := backend.PendingNonceAt(ctx, bankAddr) tx, _ := types.SignTx(types.NewTransaction(nonce, userAddr1, big.NewInt(10000), vars.TxGas, nil, nil), signer, bankKey) backend.SendTransaction(ctx, tx) case 1: + // Builtin-block + // number: 2 + // txs: 4 + bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) userNonce1, _ := backend.PendingNonceAt(ctx, userAddr1) @@ -141,6 +153,10 @@ func prepare(n int, backend *backends.SimulatedBackend) { tx4, _ := types.SignTx(types.NewContractCreation(userNonce1+2, big.NewInt(0), 200000, big.NewInt(0), testEventEmitterCode), signer, userKey1) backend.SendTransaction(ctx, tx4) case 2: + // Builtin-block + // number: 3 + // txs: 2 + // bankUser transfer some ether to signer bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) tx1, _ := types.SignTx(types.NewTransaction(bankNonce, signerAddr, big.NewInt(1000000000), vars.TxGas, nil, nil), signer, bankKey) @@ -151,6 +167,10 @@ func prepare(n int, backend *backends.SimulatedBackend) { tx2, _ := types.SignTx(types.NewTransaction(bankNonce+1, testContractAddr, big.NewInt(0), 100000, nil, data), signer, bankKey) backend.SendTransaction(ctx, tx2) case 3: + // Builtin-block + // number: 4 + // txs: 1 + // invoke test contract bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002") @@ -165,7 +185,7 @@ func prepare(n int, backend *backends.SimulatedBackend) { func testIndexers(db ethdb.Database, odr light.OdrBackend, config *light.IndexerConfig, disablePruning bool) []*core.ChainIndexer { var indexers [3]*core.ChainIndexer indexers[0] = light.NewChtIndexer(db, odr, config.ChtSize, config.ChtConfirms, disablePruning) - indexers[1] = eth.NewBloomIndexer(db, config.BloomSize, config.BloomConfirms) + indexers[1] = core.NewBloomIndexer(db, config.BloomSize, config.BloomConfirms) indexers[2] = light.NewBloomTrieIndexer(db, odr, config.BloomSize, config.BloomTrieSize, disablePruning) // make bloomTrieIndexer as a child indexer of bloom indexer. indexers[1].AddChildIndexer(indexers[2]) @@ -206,7 +226,7 @@ func newTestClientHandler(backend *backends.SimulatedBackend, odr *LesOdr, index client := &LightEthereum{ lesCommons: lesCommons{ genesis: genesis.Hash(), - config: ð.Config{LightPeers: 100, NetworkId: NetworkId}, + config: ðconfig.Config{LightPeers: 100, NetworkId: NetworkId}, chainConfig: params.AllEthashProtocolChanges, iConfig: light.TestClientIndexerConfig, chainDb: db, @@ -267,11 +287,10 @@ func newTestServerHandler(blocks int, indexers []*core.ChainIndexer, db ethdb.Da } oracle = checkpointoracle.New(checkpointConfig, getLocal) } - ns := nodestate.NewNodeStateMachine(nil, nil, mclock.System{}, serverSetup) server := &LesServer{ lesCommons: lesCommons{ genesis: genesis.Hash(), - config: ð.Config{LightPeers: 100, NetworkId: NetworkId}, + config: ðconfig.Config{LightPeers: 100, NetworkId: NetworkId}, chainConfig: params.AllEthashProtocolChanges, iConfig: light.TestServerIndexerConfig, chainDb: db, @@ -279,8 +298,7 @@ func newTestServerHandler(blocks int, indexers []*core.ChainIndexer, db ethdb.Da oracle: oracle, closeCh: make(chan struct{}), }, - ns: ns, - broadcaster: newBroadcaster(ns), + peers: newClientPeerSet(), servingQueue: newServingQueue(int64(time.Millisecond*10), 1), defParams: flowcontrol.ServerParams{ BufLimit: testBufLimit, @@ -290,18 +308,22 @@ func newTestServerHandler(blocks int, indexers []*core.ChainIndexer, db ethdb.Da } server.costTracker, server.minCapacity = newCostTracker(db, server.config) server.costTracker.testCostList = testCostList(0) // Disable flow control mechanism. - server.clientPool = newClientPool(ns, db, testBufRecharge, defaultConnectedBias, clock, func(id enode.ID) {}) - server.clientPool.setLimits(10000, 10000) // Assign enough capacity for clientpool + server.clientPool = vfs.NewClientPool(db, testBufRecharge, defaultConnectedBias, clock, alwaysTrueFn) + server.clientPool.Start() + server.clientPool.SetLimits(10000, 10000) // Assign enough capacity for clientpool server.handler = newServerHandler(server, simulation.Blockchain(), db, txpool, func() bool { return true }) if server.oracle != nil { server.oracle.Start(simulation) } server.servingQueue.setThreads(4) - ns.Start() server.handler.start() return server.handler, simulation } +func alwaysTrueFn() bool { + return true +} + // testPeer is a simulated peer to allow testing direct network calls. type testPeer struct { cpeer *clientPeer @@ -311,44 +333,61 @@ type testPeer struct { app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side } -// newTestPeer creates a new peer registered at the given protocol manager. -func newTestPeer(t *testing.T, name string, version int, handler *serverHandler, shake bool, testCost uint64) (*testPeer, <-chan error) { - // Create a message pipe to communicate through - app, net := p2p.MsgPipe() - - // Generate a random id and create the peer - var id enode.ID - rand.Read(id[:]) - peer := newClientPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net) +// handshakeWithServer executes the handshake with the remote server peer. +func (p *testPeer) handshakeWithServer(t *testing.T, td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID) { + // It only works for the simulated client peer + if p.cpeer == nil { + t.Fatal("handshake for client peer only") + } + var sendList keyValueList + sendList = sendList.add("protocolVersion", uint64(p.cpeer.version)) + sendList = sendList.add("networkId", uint64(NetworkId)) + sendList = sendList.add("headTd", td) + sendList = sendList.add("headHash", head) + sendList = sendList.add("headNum", headNum) + sendList = sendList.add("genesisHash", genesis) + if p.cpeer.version >= lpv4 { + sendList = sendList.add("forkID", &forkID) + } + if err := p2p.ExpectMsg(p.app, StatusMsg, nil); err != nil { + t.Fatalf("status recv: %v", err) + } + if err := p2p.Send(p.app, StatusMsg, sendList); err != nil { + t.Fatalf("status send: %v", err) + } +} - // Start the peer on a new thread - errCh := make(chan error, 1) - go func() { - select { - case <-handler.closeCh: - errCh <- p2p.DiscQuitting - case errCh <- handler.handle(peer): - } - }() - tp := &testPeer{ - app: app, - net: net, - cpeer: peer, +// handshakeWithClient executes the handshake with the remote client peer. +func (p *testPeer) handshakeWithClient(t *testing.T, td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID, costList RequestCostList, recentTxLookup uint64) { + // It only works for the simulated client peer + if p.speer == nil { + t.Fatal("handshake for server peer only") } - // Execute any implicitly requested handshakes and return - if shake { - // Customize the cost table if required. - if testCost != 0 { - handler.server.costTracker.testCostList = testCostList(testCost) - } - var ( - genesis = handler.blockchain.Genesis() - head = handler.blockchain.CurrentHeader() - td = handler.blockchain.GetTd(head.Hash(), head.Number.Uint64()) - ) - tp.handshake(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), testCostList(testCost)) + var sendList keyValueList + sendList = sendList.add("protocolVersion", uint64(p.speer.version)) + sendList = sendList.add("networkId", uint64(NetworkId)) + sendList = sendList.add("headTd", td) + sendList = sendList.add("headHash", head) + sendList = sendList.add("headNum", headNum) + sendList = sendList.add("genesisHash", genesis) + sendList = sendList.add("serveHeaders", nil) + sendList = sendList.add("serveChainSince", uint64(0)) + sendList = sendList.add("serveStateSince", uint64(0)) + sendList = sendList.add("serveRecentState", uint64(core.TriesInMemory-4)) + sendList = sendList.add("txRelay", nil) + sendList = sendList.add("flowControl/BL", testBufLimit) + sendList = sendList.add("flowControl/MRR", testBufRecharge) + sendList = sendList.add("flowControl/MRC", costList) + if p.speer.version >= lpv4 { + sendList = sendList.add("forkID", &forkID) + sendList = sendList.add("recentTxLookup", recentTxLookup) + } + if err := p2p.ExpectMsg(p.app, StatusMsg, nil); err != nil { + t.Fatalf("status recv: %v", err) + } + if err := p2p.Send(p.app, StatusMsg, sendList); err != nil { + t.Fatalf("status send: %v", err) } - return tp, errCh } // close terminates the local side of the peer, notifying the remote protocol @@ -402,42 +441,9 @@ func newTestPeerPair(name string, version int, server *serverHandler, client *cl return &testPeer{cpeer: peer1, net: net, app: app}, &testPeer{speer: peer2, net: app, app: net}, nil } -// handshake simulates a trivial handshake that expects the same state from the -// remote side as we are simulating locally. -func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, costList RequestCostList) { - var expList keyValueList - expList = expList.add("protocolVersion", uint64(p.cpeer.version)) - expList = expList.add("networkId", uint64(NetworkId)) - expList = expList.add("headTd", td) - expList = expList.add("headHash", head) - expList = expList.add("headNum", headNum) - expList = expList.add("genesisHash", genesis) - sendList := make(keyValueList, len(expList)) - copy(sendList, expList) - expList = expList.add("serveHeaders", nil) - expList = expList.add("serveChainSince", uint64(0)) - expList = expList.add("serveStateSince", uint64(0)) - expList = expList.add("serveRecentState", uint64(core.TriesInMemory-4)) - expList = expList.add("txRelay", nil) - expList = expList.add("flowControl/BL", testBufLimit) - expList = expList.add("flowControl/MRR", testBufRecharge) - expList = expList.add("flowControl/MRC", costList) - - if err := p2p.ExpectMsg(p.app, StatusMsg, expList); err != nil { - t.Fatalf("status recv: %v", err) - } - if err := p2p.Send(p.app, StatusMsg, sendList); err != nil { - t.Fatalf("status send: %v", err) - } - p.cpeer.fcParams = flowcontrol.ServerParams{ - BufLimit: testBufLimit, - MinRecharge: testBufRecharge, - } -} - type indexerCallback func(*core.ChainIndexer, *core.ChainIndexer, *core.ChainIndexer) -// testClient represents a client for testing with necessary auxiliary fields. +// testClient represents a client object for testing with necessary auxiliary fields. type testClient struct { clock mclock.Clock db ethdb.Database @@ -449,7 +455,58 @@ type testClient struct { bloomTrieIndexer *core.ChainIndexer } -// testServer represents a server for testing with necessary auxiliary fields. +// newRawPeer creates a new server peer connects to the server and do the handshake. +func (client *testClient) newRawPeer(t *testing.T, name string, version int, recentTxLookup uint64) (*testPeer, func(), <-chan error) { + // Create a message pipe to communicate through + app, net := p2p.MsgPipe() + + // Generate a random id and create the peer + var id enode.ID + rand.Read(id[:]) + peer := newServerPeer(version, NetworkId, false, p2p.NewPeer(id, name, nil), net) + + // Start the peer on a new thread + errCh := make(chan error, 1) + go func() { + select { + case <-client.handler.closeCh: + errCh <- p2p.DiscQuitting + case errCh <- client.handler.handle(peer): + } + }() + tp := &testPeer{ + app: app, + net: net, + speer: peer, + } + var ( + genesis = client.handler.backend.blockchain.Genesis() + head = client.handler.backend.blockchain.CurrentHeader() + td = client.handler.backend.blockchain.GetTd(head.Hash(), head.Number.Uint64()) + ) + forkID := forkid.NewID(client.handler.backend.blockchain.Config(), genesis.Hash(), head.Number.Uint64()) + tp.handshakeWithClient(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID, testCostList(0), recentTxLookup) // disable flow control by default + + // Ensure the connection is established or exits when any error occurs + for { + select { + case <-errCh: + return nil, nil, nil + default: + } + if atomic.LoadUint32(&peer.serving) == 1 { + break + } + time.Sleep(50 * time.Millisecond) + } + closePeer := func() { + tp.speer.close() + tp.close() + } + return tp, closePeer, errCh +} + +// testServer represents a server object for testing with necessary auxiliary fields. type testServer struct { clock mclock.Clock backend *backends.SimulatedBackend @@ -462,89 +519,109 @@ type testServer struct { bloomTrieIndexer *core.ChainIndexer } -func newServerEnv(t *testing.T, blocks int, protocol int, callback indexerCallback, simClock bool, newPeer bool, testCost uint64) (*testServer, func()) { - db := rawdb.NewMemoryDatabase() - indexers := testIndexers(db, nil, light.TestServerIndexerConfig, true) +// newRawPeer creates a new client peer connects to the server and do the handshake. +func (server *testServer) newRawPeer(t *testing.T, name string, version int) (*testPeer, func(), <-chan error) { + // Create a message pipe to communicate through + app, net := p2p.MsgPipe() - var clock mclock.Clock = &mclock.System{} - if simClock { - clock = &mclock.Simulated{} - } - handler, b := newTestServerHandler(blocks, indexers, db, clock) + // Generate a random id and create the peer + var id enode.ID + rand.Read(id[:]) + peer := newClientPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net) - var peer *testPeer - if newPeer { - peer, _ = newTestPeer(t, "peer", protocol, handler, true, testCost) + // Start the peer on a new thread + errCh := make(chan error, 1) + go func() { + select { + case <-server.handler.closeCh: + errCh <- p2p.DiscQuitting + case errCh <- server.handler.handle(peer): + } + }() + tp := &testPeer{ + app: app, + net: net, + cpeer: peer, } + var ( + genesis = server.handler.blockchain.Genesis() + head = server.handler.blockchain.CurrentHeader() + td = server.handler.blockchain.GetTd(head.Hash(), head.Number.Uint64()) + ) + forkID := forkid.NewID(server.handler.blockchain.Config(), genesis.Hash(), head.Number.Uint64()) + tp.handshakeWithServer(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID) - cIndexer, bIndexer, btIndexer := indexers[0], indexers[1], indexers[2] - cIndexer.Start(handler.blockchain) - bIndexer.Start(handler.blockchain) - - // Wait until indexers generate enough index data. - if callback != nil { - callback(cIndexer, bIndexer, btIndexer) - } - server := &testServer{ - clock: clock, - backend: b, - db: db, - peer: peer, - handler: handler, - chtIndexer: cIndexer, - bloomIndexer: bIndexer, - bloomTrieIndexer: btIndexer, - } - teardown := func() { - if newPeer { - peer.close() - peer.cpeer.close() - b.Close() + // Ensure the connection is established or exits when any error occurs + for { + select { + case <-errCh: + return nil, nil, nil + default: + } + if atomic.LoadUint32(&peer.serving) == 1 { + break } - cIndexer.Close() - bIndexer.Close() + time.Sleep(50 * time.Millisecond) + } + closePeer := func() { + tp.cpeer.close() + tp.close() } - return server, teardown + return tp, closePeer, errCh } -func newClientServerEnv(t *testing.T, blocks int, protocol int, callback indexerCallback, ulcServers []string, ulcFraction int, simClock bool, connect bool, disablePruning bool) (*testServer, *testClient, func()) { - sdb, cdb := rawdb.NewMemoryDatabase(), rawdb.NewMemoryDatabase() - speers := newServerPeerSet() +// testnetConfig wraps all the configurations for testing network. +type testnetConfig struct { + blocks int + protocol int + indexFn indexerCallback + ulcServers []string + ulcFraction int + simClock bool + connect bool + nopruning bool +} +func newClientServerEnv(t *testing.T, config testnetConfig) (*testServer, *testClient, func()) { + var ( + sdb = rawdb.NewMemoryDatabase() + cdb = rawdb.NewMemoryDatabase() + speers = newServerPeerSet() + ) var clock mclock.Clock = &mclock.System{} - if simClock { + if config.simClock { clock = &mclock.Simulated{} } dist := newRequestDistributor(speers, clock) rm := newRetrieveManager(speers, dist, func() time.Duration { return time.Millisecond * 500 }) - odr := NewLesOdr(cdb, light.TestClientIndexerConfig, rm) + odr := NewLesOdr(cdb, light.TestClientIndexerConfig, speers, rm) sindexers := testIndexers(sdb, nil, light.TestServerIndexerConfig, true) - cIndexers := testIndexers(cdb, odr, light.TestClientIndexerConfig, disablePruning) + cIndexers := testIndexers(cdb, odr, light.TestClientIndexerConfig, config.nopruning) scIndexer, sbIndexer, sbtIndexer := sindexers[0], sindexers[1], sindexers[2] ccIndexer, cbIndexer, cbtIndexer := cIndexers[0], cIndexers[1], cIndexers[2] odr.SetIndexers(ccIndexer, cbIndexer, cbtIndexer) - server, b := newTestServerHandler(blocks, sindexers, sdb, clock) - client := newTestClientHandler(b, odr, cIndexers, cdb, speers, ulcServers, ulcFraction) + server, b := newTestServerHandler(config.blocks, sindexers, sdb, clock) + client := newTestClientHandler(b, odr, cIndexers, cdb, speers, config.ulcServers, config.ulcFraction) scIndexer.Start(server.blockchain) sbIndexer.Start(server.blockchain) ccIndexer.Start(client.backend.blockchain) cbIndexer.Start(client.backend.blockchain) - if callback != nil { - callback(scIndexer, sbIndexer, sbtIndexer) + if config.indexFn != nil { + config.indexFn(scIndexer, sbIndexer, sbtIndexer) } var ( err error speer, cpeer *testPeer ) - if connect { + if config.connect { done := make(chan struct{}) - client.syncDone = func() { close(done) } - cpeer, speer, err = newTestPeerPair("peer", protocol, server, client) + client.syncEnd = func(_ *types.Header) { close(done) } + cpeer, speer, err = newTestPeerPair("peer", config.protocol, server, client) if err != nil { t.Fatalf("Failed to connect testing peers %v", err) } @@ -574,7 +651,7 @@ func newClientServerEnv(t *testing.T, blocks int, protocol int, callback indexer bloomTrieIndexer: cbtIndexer, } teardown := func() { - if connect { + if config.connect { speer.close() cpeer.close() cpeer.cpeer.close() @@ -588,3 +665,11 @@ func newClientServerEnv(t *testing.T, blocks int, protocol int, callback indexer } return s, c, teardown } + +// NewFuzzerPeer creates a client peer for test purposes, and also returns +// a function to close the peer: this is needed to avoid goroutine leaks in the +// exec queue. +func NewFuzzerPeer(version int) (p *clientPeer, closer func()) { + p = newClientPeer(version, 0, p2p.NewPeer(enode.ID{}, "", nil), nil) + return p, func() { p.peerCommons.close() } +} diff --git a/les/txrelay.go b/les/txrelay.go index 57f2412eba..9d29b2f234 100644 --- a/les/txrelay.go +++ b/les/txrelay.go @@ -25,13 +25,8 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -type ltrInfo struct { - tx *types.Transaction - sentTo map[*serverPeer]struct{} -} - type lesTxRelay struct { - txSent map[common.Hash]*ltrInfo + txSent map[common.Hash]*types.Transaction txPending map[common.Hash]struct{} peerList []*serverPeer peerStartPos int @@ -43,7 +38,7 @@ type lesTxRelay struct { func newLesTxRelay(ps *serverPeerSet, retriever *retrieveManager) *lesTxRelay { r := &lesTxRelay{ - txSent: make(map[common.Hash]*ltrInfo), + txSent: make(map[common.Hash]*types.Transaction), txPending: make(map[common.Hash]struct{}), retriever: retriever, stop: make(chan struct{}), @@ -80,8 +75,7 @@ func (ltrx *lesTxRelay) unregisterPeer(p *serverPeer) { } } -// send sends a list of transactions to at most a given number of peers at -// once, never resending any particular transaction to the same peer twice +// send sends a list of transactions to at most a given number of peers. func (ltrx *lesTxRelay) send(txs types.Transactions, count int) { sendTo := make(map[*serverPeer]types.Transactions) @@ -92,26 +86,18 @@ func (ltrx *lesTxRelay) send(txs types.Transactions, count int) { for _, tx := range txs { hash := tx.Hash() - ltr, ok := ltrx.txSent[hash] + _, ok := ltrx.txSent[hash] if !ok { - ltr = <rInfo{ - tx: tx, - sentTo: make(map[*serverPeer]struct{}), - } - ltrx.txSent[hash] = ltr + ltrx.txSent[hash] = tx ltrx.txPending[hash] = struct{}{} } - if len(ltrx.peerList) > 0 { cnt := count pos := ltrx.peerStartPos for { peer := ltrx.peerList[pos] - if _, ok := ltr.sentTo[peer]; !ok { - sendTo[peer] = append(sendTo[peer], tx) - ltr.sentTo[peer] = struct{}{} - cnt-- - } + sendTo[peer] = append(sendTo[peer], tx) + cnt-- if cnt == 0 { break // sent it to the desired number of peers } @@ -174,7 +160,7 @@ func (ltrx *lesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback txs := make(types.Transactions, len(ltrx.txPending)) i := 0 for hash := range ltrx.txPending { - txs[i] = ltrx.txSent[hash].tx + txs[i] = ltrx.txSent[hash] i++ } ltrx.send(txs, 1) diff --git a/les/ulc_test.go b/les/ulc_test.go index 657b13db2c..d7308fa593 100644 --- a/les/ulc_test.go +++ b/les/ulc_test.go @@ -126,7 +126,12 @@ func connect(server *serverHandler, serverId enode.ID, client *clientHandler, pr // newTestServerPeer creates server peer. func newTestServerPeer(t *testing.T, blocks int, protocol int) (*testServer, *enode.Node, func()) { - s, teardown := newServerEnv(t, blocks, protocol, nil, false, false, 0) + netconfig := testnetConfig{ + blocks: blocks, + protocol: protocol, + nopruning: true, + } + s, _, teardown := newClientServerEnv(t, netconfig) key, err := crypto.GenerateKey() if err != nil { t.Fatal("generate key err:", err) @@ -138,6 +143,12 @@ func newTestServerPeer(t *testing.T, blocks int, protocol int) (*testServer, *en // newTestLightPeer creates node with light sync mode func newTestLightPeer(t *testing.T, protocol int, ulcServers []string, ulcFraction int) (*testClient, func()) { - _, c, teardown := newClientServerEnv(t, 0, protocol, nil, ulcServers, ulcFraction, false, false, true) + netconfig := testnetConfig{ + protocol: protocol, + ulcServers: ulcServers, + ulcFraction: ulcFraction, + nopruning: true, + } + _, c, teardown := newClientServerEnv(t, netconfig) return c, teardown } diff --git a/les/utils/expiredvalue.go b/les/utils/expiredvalue.go index 55e82cee48..3fd52616fa 100644 --- a/les/utils/expiredvalue.go +++ b/les/utils/expiredvalue.go @@ -86,10 +86,15 @@ func (e *ExpiredValue) Add(amount int64, logOffset Fixed64) int64 { e.Exp = integer } if base >= 0 || uint64(-base) <= e.Base { - // This is a temporary fix to circumvent a golang - // uint conversion issue on arm64, which needs to - // be investigated further. FIXME - e.Base = uint64(int64(e.Base) + int64(base)) + // The conversion from negative float64 to + // uint64 is undefined in golang, and doesn't + // work with ARMv8. More details at: + // https://github.com/golang/go/issues/43047 + if base >= 0 { + e.Base += uint64(base) + } else { + e.Base -= uint64(-base) + } return amount } net := int64(-float64(e.Base) / factor) diff --git a/les/utils/limiter.go b/les/utils/limiter.go new file mode 100644 index 0000000000..0cc2d7b262 --- /dev/null +++ b/les/utils/limiter.go @@ -0,0 +1,405 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package utils + +import ( + "sort" + "sync" + + "github.com/ethereum/go-ethereum/p2p/enode" +) + +const maxSelectionWeight = 1000000000 // maximum selection weight of each individual node/address group + +// Limiter protects a network request serving mechanism from denial-of-service attacks. +// It limits the total amount of resources used for serving requests while ensuring that +// the most valuable connections always have a reasonable chance of being served. +type Limiter struct { + lock sync.Mutex + cond *sync.Cond + quit bool + + nodes map[enode.ID]*nodeQueue + addresses map[string]*addressGroup + addressSelect, valueSelect *WeightedRandomSelect + maxValue float64 + maxCost, sumCost, sumCostLimit uint + selectAddressNext bool +} + +// nodeQueue represents queued requests coming from a single node ID +type nodeQueue struct { + queue []request // always nil if penaltyCost != 0 + id enode.ID + address string + value float64 + flatWeight, valueWeight uint64 // current selection weights in the address/value selectors + sumCost uint // summed cost of requests queued by the node + penaltyCost uint // cumulative cost of dropped requests since last processed request + groupIndex int +} + +// addressGroup is a group of node IDs that have sent their last requests from the same +// network address +type addressGroup struct { + nodes []*nodeQueue + nodeSelect *WeightedRandomSelect + sumFlatWeight, groupWeight uint64 +} + +// request represents an incoming request scheduled for processing +type request struct { + process chan chan struct{} + cost uint +} + +// flatWeight distributes weights equally between each active network address +func flatWeight(item interface{}) uint64 { return item.(*nodeQueue).flatWeight } + +// add adds the node queue to the address group. It is the caller's responsibility to +// add the address group to the address map and the address selector if it wasn't +// there before. +func (ag *addressGroup) add(nq *nodeQueue) { + if nq.groupIndex != -1 { + panic("added node queue is already in an address group") + } + l := len(ag.nodes) + nq.groupIndex = l + ag.nodes = append(ag.nodes, nq) + ag.sumFlatWeight += nq.flatWeight + ag.groupWeight = ag.sumFlatWeight / uint64(l+1) + ag.nodeSelect.Update(ag.nodes[l]) +} + +// update updates the selection weight of the node queue inside the address group. +// It is the caller's responsibility to update the group's selection weight in the +// address selector. +func (ag *addressGroup) update(nq *nodeQueue, weight uint64) { + if nq.groupIndex == -1 || nq.groupIndex >= len(ag.nodes) || ag.nodes[nq.groupIndex] != nq { + panic("updated node queue is not in this address group") + } + ag.sumFlatWeight += weight - nq.flatWeight + nq.flatWeight = weight + ag.groupWeight = ag.sumFlatWeight / uint64(len(ag.nodes)) + ag.nodeSelect.Update(nq) +} + +// remove removes the node queue from the address group. It is the caller's responsibility +// to remove the address group from the address map if it is empty. +func (ag *addressGroup) remove(nq *nodeQueue) { + if nq.groupIndex == -1 || nq.groupIndex >= len(ag.nodes) || ag.nodes[nq.groupIndex] != nq { + panic("removed node queue is not in this address group") + } + + l := len(ag.nodes) - 1 + if nq.groupIndex != l { + ag.nodes[nq.groupIndex] = ag.nodes[l] + ag.nodes[nq.groupIndex].groupIndex = nq.groupIndex + } + nq.groupIndex = -1 + ag.nodes = ag.nodes[:l] + ag.sumFlatWeight -= nq.flatWeight + if l >= 1 { + ag.groupWeight = ag.sumFlatWeight / uint64(l) + } else { + ag.groupWeight = 0 + } + ag.nodeSelect.Remove(nq) +} + +// choose selects one of the node queues belonging to the address group +func (ag *addressGroup) choose() *nodeQueue { + return ag.nodeSelect.Choose().(*nodeQueue) +} + +// NewLimiter creates a new Limiter +func NewLimiter(sumCostLimit uint) *Limiter { + l := &Limiter{ + addressSelect: NewWeightedRandomSelect(func(item interface{}) uint64 { return item.(*addressGroup).groupWeight }), + valueSelect: NewWeightedRandomSelect(func(item interface{}) uint64 { return item.(*nodeQueue).valueWeight }), + nodes: make(map[enode.ID]*nodeQueue), + addresses: make(map[string]*addressGroup), + sumCostLimit: sumCostLimit, + } + l.cond = sync.NewCond(&l.lock) + go l.processLoop() + return l +} + +// selectionWeights calculates the selection weights of a node for both the address and +// the value selector. The selection weight depends on the next request cost or the +// summed cost of recently dropped requests. +func (l *Limiter) selectionWeights(reqCost uint, value float64) (flatWeight, valueWeight uint64) { + if value > l.maxValue { + l.maxValue = value + } + if value > 0 { + // normalize value to <= 1 + value /= l.maxValue + } + if reqCost > l.maxCost { + l.maxCost = reqCost + } + relCost := float64(reqCost) / float64(l.maxCost) + var f float64 + if relCost <= 0.001 { + f = 1 + } else { + f = 0.001 / relCost + } + f *= maxSelectionWeight + flatWeight, valueWeight = uint64(f), uint64(f*value) + if flatWeight == 0 { + flatWeight = 1 + } + return +} + +// Add adds a new request to the node queue belonging to the given id. Value belongs +// to the requesting node. A higher value gives the request a higher chance of being +// served quickly in case of heavy load or a DDoS attack. Cost is a rough estimate +// of the serving cost of the request. A lower cost also gives the request a +// better chance. +func (l *Limiter) Add(id enode.ID, address string, value float64, reqCost uint) chan chan struct{} { + l.lock.Lock() + defer l.lock.Unlock() + + process := make(chan chan struct{}, 1) + if l.quit { + close(process) + return process + } + if reqCost == 0 { + reqCost = 1 + } + if nq, ok := l.nodes[id]; ok { + if nq.queue != nil { + nq.queue = append(nq.queue, request{process, reqCost}) + nq.sumCost += reqCost + nq.value = value + if address != nq.address { + // known id sending request from a new address, move to different address group + l.removeFromGroup(nq) + l.addToGroup(nq, address) + } + } else { + // already waiting on a penalty, just add to the penalty cost and drop the request + nq.penaltyCost += reqCost + l.update(nq) + close(process) + return process + } + } else { + nq := &nodeQueue{ + queue: []request{{process, reqCost}}, + id: id, + value: value, + sumCost: reqCost, + groupIndex: -1, + } + nq.flatWeight, nq.valueWeight = l.selectionWeights(reqCost, value) + if len(l.nodes) == 0 { + l.cond.Signal() + } + l.nodes[id] = nq + if nq.valueWeight != 0 { + l.valueSelect.Update(nq) + } + l.addToGroup(nq, address) + } + l.sumCost += reqCost + if l.sumCost > l.sumCostLimit { + l.dropRequests() + } + return process +} + +// update updates the selection weights of the node queue +func (l *Limiter) update(nq *nodeQueue) { + var cost uint + if nq.queue != nil { + cost = nq.queue[0].cost + } else { + cost = nq.penaltyCost + } + flatWeight, valueWeight := l.selectionWeights(cost, nq.value) + ag := l.addresses[nq.address] + ag.update(nq, flatWeight) + l.addressSelect.Update(ag) + nq.valueWeight = valueWeight + l.valueSelect.Update(nq) +} + +// addToGroup adds the node queue to the given address group. The group is created if +// it does not exist yet. +func (l *Limiter) addToGroup(nq *nodeQueue, address string) { + nq.address = address + ag := l.addresses[address] + if ag == nil { + ag = &addressGroup{nodeSelect: NewWeightedRandomSelect(flatWeight)} + l.addresses[address] = ag + } + ag.add(nq) + l.addressSelect.Update(ag) +} + +// removeFromGroup removes the node queue from its address group +func (l *Limiter) removeFromGroup(nq *nodeQueue) { + ag := l.addresses[nq.address] + ag.remove(nq) + if len(ag.nodes) == 0 { + delete(l.addresses, nq.address) + } + l.addressSelect.Update(ag) +} + +// remove removes the node queue from its address group, the nodes map and the value +// selector +func (l *Limiter) remove(nq *nodeQueue) { + l.removeFromGroup(nq) + if nq.valueWeight != 0 { + l.valueSelect.Remove(nq) + } + delete(l.nodes, nq.id) +} + +// choose selects the next node queue to process. +func (l *Limiter) choose() *nodeQueue { + if l.valueSelect.IsEmpty() || l.selectAddressNext { + if ag, ok := l.addressSelect.Choose().(*addressGroup); ok { + l.selectAddressNext = false + return ag.choose() + } + } + nq, _ := l.valueSelect.Choose().(*nodeQueue) + l.selectAddressNext = true + return nq +} + +// processLoop processes requests sequentially +func (l *Limiter) processLoop() { + l.lock.Lock() + defer l.lock.Unlock() + + for { + if l.quit { + for _, nq := range l.nodes { + for _, request := range nq.queue { + close(request.process) + } + } + return + } + nq := l.choose() + if nq == nil { + l.cond.Wait() + continue + } + if nq.queue != nil { + request := nq.queue[0] + nq.queue = nq.queue[1:] + nq.sumCost -= request.cost + l.sumCost -= request.cost + l.lock.Unlock() + ch := make(chan struct{}) + request.process <- ch + <-ch + l.lock.Lock() + if len(nq.queue) > 0 { + l.update(nq) + } else { + l.remove(nq) + } + } else { + // penalized queue removed, next request will be added to a clean queue + l.remove(nq) + } + } +} + +// Stop stops the processing loop. All queued and future requests are rejected. +func (l *Limiter) Stop() { + l.lock.Lock() + defer l.lock.Unlock() + + l.quit = true + l.cond.Signal() +} + +type ( + dropList []dropListItem + dropListItem struct { + nq *nodeQueue + priority float64 + } +) + +func (l dropList) Len() int { + return len(l) +} + +func (l dropList) Less(i, j int) bool { + return l[i].priority < l[j].priority +} + +func (l dropList) Swap(i, j int) { + l[i], l[j] = l[j], l[i] +} + +// dropRequests selects the nodes with the highest queued request cost to selection +// weight ratio and drops their queued request. The empty node queues stay in the +// selectors with a low selection weight in order to penalize these nodes. +func (l *Limiter) dropRequests() { + var ( + sumValue float64 + list dropList + ) + for _, nq := range l.nodes { + sumValue += nq.value + } + for _, nq := range l.nodes { + if nq.sumCost == 0 { + continue + } + w := 1 / float64(len(l.addresses)*len(l.addresses[nq.address].nodes)) + if sumValue > 0 { + w += nq.value / sumValue + } + list = append(list, dropListItem{ + nq: nq, + priority: w / float64(nq.sumCost), + }) + } + sort.Sort(list) + for _, item := range list { + for _, request := range item.nq.queue { + close(request.process) + } + // make the queue penalized; no more requests are accepted until the node is + // selected based on the penalty cost which is the cumulative cost of all dropped + // requests. This ensures that sending excess requests is always penalized + // and incentivizes the sender to stop for a while if no replies are received. + item.nq.queue = nil + item.nq.penaltyCost = item.nq.sumCost + l.sumCost -= item.nq.sumCost // penalty costs are not counted in sumCost + item.nq.sumCost = 0 + l.update(item.nq) + if l.sumCost <= l.sumCostLimit/2 { + return + } + } +} diff --git a/les/utils/limiter_test.go b/les/utils/limiter_test.go new file mode 100644 index 0000000000..43af3309ab --- /dev/null +++ b/les/utils/limiter_test.go @@ -0,0 +1,206 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package utils + +import ( + "math/rand" + "testing" + + "github.com/ethereum/go-ethereum/p2p/enode" +) + +const ( + ltTolerance = 0.03 + ltRounds = 7 +) + +type ( + ltNode struct { + addr, id int + value, exp float64 + cost uint + reqRate float64 + reqMax, runCount int + lastTotalCost uint + + served, dropped int + } + + ltResult struct { + node *ltNode + ch chan struct{} + } + + limTest struct { + limiter *Limiter + results chan ltResult + runCount int + expCost, totalCost uint + } +) + +func (lt *limTest) request(n *ltNode) { + var ( + address string + id enode.ID + ) + if n.addr >= 0 { + address = string([]byte{byte(n.addr)}) + } else { + var b [32]byte + rand.Read(b[:]) + address = string(b[:]) + } + if n.id >= 0 { + id = enode.ID{byte(n.id)} + } else { + rand.Read(id[:]) + } + lt.runCount++ + n.runCount++ + cch := lt.limiter.Add(id, address, n.value, n.cost) + go func() { + lt.results <- ltResult{n, <-cch} + }() +} + +func (lt *limTest) moreRequests(n *ltNode) { + maxStart := int(float64(lt.totalCost-n.lastTotalCost) * n.reqRate) + if maxStart != 0 { + n.lastTotalCost = lt.totalCost + } + for n.reqMax > n.runCount && maxStart > 0 { + lt.request(n) + maxStart-- + } +} + +func (lt *limTest) process() { + res := <-lt.results + lt.runCount-- + res.node.runCount-- + if res.ch != nil { + res.node.served++ + if res.node.exp != 0 { + lt.expCost += res.node.cost + } + lt.totalCost += res.node.cost + close(res.ch) + } else { + res.node.dropped++ + } +} + +func TestLimiter(t *testing.T) { + limTests := [][]*ltNode{ + { // one id from an individual address and two ids from a shared address + {addr: 0, id: 0, value: 0, cost: 1, reqRate: 1, reqMax: 1, exp: 0.5}, + {addr: 1, id: 1, value: 0, cost: 1, reqRate: 1, reqMax: 1, exp: 0.25}, + {addr: 1, id: 2, value: 0, cost: 1, reqRate: 1, reqMax: 1, exp: 0.25}, + }, + { // varying request costs + {addr: 0, id: 0, value: 0, cost: 10, reqRate: 0.2, reqMax: 1, exp: 0.5}, + {addr: 1, id: 1, value: 0, cost: 3, reqRate: 0.5, reqMax: 1, exp: 0.25}, + {addr: 1, id: 2, value: 0, cost: 1, reqRate: 1, reqMax: 1, exp: 0.25}, + }, + { // different request rate + {addr: 0, id: 0, value: 0, cost: 1, reqRate: 2, reqMax: 2, exp: 0.5}, + {addr: 1, id: 1, value: 0, cost: 1, reqRate: 10, reqMax: 10, exp: 0.25}, + {addr: 1, id: 2, value: 0, cost: 1, reqRate: 1, reqMax: 1, exp: 0.25}, + }, + { // adding value + {addr: 0, id: 0, value: 3, cost: 1, reqRate: 1, reqMax: 1, exp: (0.5 + 0.3) / 2}, + {addr: 1, id: 1, value: 0, cost: 1, reqRate: 1, reqMax: 1, exp: 0.25 / 2}, + {addr: 1, id: 2, value: 7, cost: 1, reqRate: 1, reqMax: 1, exp: (0.25 + 0.7) / 2}, + }, + { // DoS attack from a single address with a single id + {addr: 0, id: 0, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333}, + {addr: 1, id: 1, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333}, + {addr: 2, id: 2, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333}, + {addr: 3, id: 3, value: 0, cost: 1, reqRate: 10, reqMax: 1000000000, exp: 0}, + }, + { // DoS attack from a single address with different ids + {addr: 0, id: 0, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333}, + {addr: 1, id: 1, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333}, + {addr: 2, id: 2, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333}, + {addr: 3, id: -1, value: 0, cost: 1, reqRate: 1, reqMax: 1000000000, exp: 0}, + }, + { // DDoS attack from different addresses with a single id + {addr: 0, id: 0, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333}, + {addr: 1, id: 1, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333}, + {addr: 2, id: 2, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333}, + {addr: -1, id: 3, value: 0, cost: 1, reqRate: 1, reqMax: 1000000000, exp: 0}, + }, + { // DDoS attack from different addresses with different ids + {addr: 0, id: 0, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333}, + {addr: 1, id: 1, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333}, + {addr: 2, id: 2, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333}, + {addr: -1, id: -1, value: 0, cost: 1, reqRate: 1, reqMax: 1000000000, exp: 0}, + }, + } + + lt := &limTest{ + limiter: NewLimiter(100), + results: make(chan ltResult), + } + for _, test := range limTests { + lt.expCost, lt.totalCost = 0, 0 + iterCount := 10000 + for j := 0; j < ltRounds; j++ { + // try to reach expected target range in multiple rounds with increasing iteration counts + last := j == ltRounds-1 + for _, n := range test { + lt.request(n) + } + for i := 0; i < iterCount; i++ { + lt.process() + for _, n := range test { + lt.moreRequests(n) + } + } + for lt.runCount > 0 { + lt.process() + } + if spamRatio := 1 - float64(lt.expCost)/float64(lt.totalCost); spamRatio > 0.5*(1+ltTolerance) { + t.Errorf("Spam ratio too high (%f)", spamRatio) + } + fail, success := false, true + for _, n := range test { + if n.exp != 0 { + if n.dropped > 0 { + t.Errorf("Dropped %d requests of non-spam node", n.dropped) + fail = true + } + r := float64(n.served) * float64(n.cost) / float64(lt.expCost) + if r < n.exp*(1-ltTolerance) || r > n.exp*(1+ltTolerance) { + if last { + // print error only if the target is still not reached in the last round + t.Errorf("Request ratio (%f) does not match expected value (%f)", r, n.exp) + } + success = false + } + } + } + if fail || success { + break + } + // neither failed nor succeeded; try more iterations to reach probability targets + iterCount *= 2 + } + } + lt.limiter.Stop() +} diff --git a/les/utils/weighted_select.go b/les/utils/weighted_select.go index d6db3c0e65..486b00820a 100644 --- a/les/utils/weighted_select.go +++ b/les/utils/weighted_select.go @@ -17,7 +17,10 @@ package utils import ( + "math" "math/rand" + + "github.com/ethereum/go-ethereum/log" ) type ( @@ -49,11 +52,19 @@ func (w *WeightedRandomSelect) Remove(item WrsItem) { // IsEmpty returns true if the set is empty func (w *WeightedRandomSelect) IsEmpty() bool { - return w.root.sumWeight == 0 + return w.root.sumCost == 0 } // setWeight sets an item's weight to a specific value (removes it if zero) func (w *WeightedRandomSelect) setWeight(item WrsItem, weight uint64) { + if weight > math.MaxInt64-w.root.sumCost { + // old weight is still included in sumCost, remove and check again + w.setWeight(item, 0) + if weight > math.MaxInt64-w.root.sumCost { + log.Error("WeightedRandomSelect overflow", "sumCost", w.root.sumCost, "new weight", weight) + weight = math.MaxInt64 - w.root.sumCost + } + } idx, ok := w.idx[item] if ok { w.root.setWeight(idx, weight) @@ -64,9 +75,9 @@ func (w *WeightedRandomSelect) setWeight(item WrsItem, weight uint64) { if weight != 0 { if w.root.itemCnt == w.root.maxItems { // add a new level - newRoot := &wrsNode{sumWeight: w.root.sumWeight, itemCnt: w.root.itemCnt, level: w.root.level + 1, maxItems: w.root.maxItems * wrsBranches} + newRoot := &wrsNode{sumCost: w.root.sumCost, itemCnt: w.root.itemCnt, level: w.root.level + 1, maxItems: w.root.maxItems * wrsBranches} newRoot.items[0] = w.root - newRoot.weights[0] = w.root.sumWeight + newRoot.weights[0] = w.root.sumCost w.root = newRoot } w.idx[item] = w.root.insert(item, weight) @@ -80,10 +91,10 @@ func (w *WeightedRandomSelect) setWeight(item WrsItem, weight uint64) { // updates its weight and selects another one func (w *WeightedRandomSelect) Choose() WrsItem { for { - if w.root.sumWeight == 0 { + if w.root.sumCost == 0 { return nil } - val := uint64(rand.Int63n(int64(w.root.sumWeight))) + val := uint64(rand.Int63n(int64(w.root.sumCost))) choice, lastWeight := w.root.choose(val) weight := w.wfn(choice) if weight != lastWeight { @@ -101,7 +112,7 @@ const wrsBranches = 8 // max number of branches in the wrsNode tree type wrsNode struct { items [wrsBranches]interface{} weights [wrsBranches]uint64 - sumWeight uint64 + sumCost uint64 level, itemCnt, maxItems int } @@ -115,7 +126,7 @@ func (n *wrsNode) insert(item WrsItem, weight uint64) int { } } n.itemCnt++ - n.sumWeight += weight + n.sumCost += weight n.weights[branch] += weight if n.level == 0 { n.items[branch] = item @@ -139,7 +150,7 @@ func (n *wrsNode) setWeight(idx int, weight uint64) uint64 { oldWeight := n.weights[idx] n.weights[idx] = weight diff := weight - oldWeight - n.sumWeight += diff + n.sumCost += diff if weight == 0 { n.items[idx] = nil n.itemCnt-- @@ -150,7 +161,7 @@ func (n *wrsNode) setWeight(idx int, weight uint64) uint64 { branch := idx / branchItems diff := n.items[branch].(*wrsNode).setWeight(idx-branch*branchItems, weight) n.weights[branch] += diff - n.sumWeight += diff + n.sumCost += diff if weight == 0 { n.itemCnt-- } diff --git a/les/lespay/client/api.go b/les/vflux/client/api.go similarity index 98% rename from les/lespay/client/api.go rename to les/vflux/client/api.go index 5ad6ffd77e..135273ef96 100644 --- a/les/lespay/client/api.go +++ b/les/vflux/client/api.go @@ -24,7 +24,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" ) -// PrivateClientAPI implements the lespay client side API +// PrivateClientAPI implements the vflux client side API type PrivateClientAPI struct { vt *ValueTracker } diff --git a/les/lespay/client/fillset.go b/les/vflux/client/fillset.go similarity index 100% rename from les/lespay/client/fillset.go rename to les/vflux/client/fillset.go diff --git a/les/lespay/client/fillset_test.go b/les/vflux/client/fillset_test.go similarity index 100% rename from les/lespay/client/fillset_test.go rename to les/vflux/client/fillset_test.go diff --git a/les/lespay/client/queueiterator.go b/les/vflux/client/queueiterator.go similarity index 100% rename from les/lespay/client/queueiterator.go rename to les/vflux/client/queueiterator.go diff --git a/les/lespay/client/queueiterator_test.go b/les/vflux/client/queueiterator_test.go similarity index 93% rename from les/lespay/client/queueiterator_test.go rename to les/vflux/client/queueiterator_test.go index a74301c7d3..400d978e19 100644 --- a/les/lespay/client/queueiterator_test.go +++ b/les/vflux/client/queueiterator_test.go @@ -26,17 +26,6 @@ import ( "github.com/ethereum/go-ethereum/p2p/nodestate" ) -func testNodeID(i int) enode.ID { - return enode.ID{42, byte(i % 256), byte(i / 256)} -} - -func testNodeIndex(id enode.ID) int { - if id[0] != 42 { - return -1 - } - return int(id[1]) + int(id[2])*256 -} - func testNode(i int) *enode.Node { return enode.SignNull(new(enr.Record), testNodeID(i)) } diff --git a/les/lespay/client/requestbasket.go b/les/vflux/client/requestbasket.go similarity index 100% rename from les/lespay/client/requestbasket.go rename to les/vflux/client/requestbasket.go diff --git a/les/lespay/client/requestbasket_test.go b/les/vflux/client/requestbasket_test.go similarity index 100% rename from les/lespay/client/requestbasket_test.go rename to les/vflux/client/requestbasket_test.go diff --git a/les/serverpool.go b/les/vflux/client/serverpool.go similarity index 59% rename from les/serverpool.go rename to les/vflux/client/serverpool.go index 9bfa0bd725..cc0254c127 100644 --- a/les/serverpool.go +++ b/les/vflux/client/serverpool.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package les +package client import ( "errors" @@ -26,9 +26,9 @@ import ( "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/ethdb" - lpc "github.com/ethereum/go-ethereum/les/lespay/client" "github.com/ethereum/go-ethereum/les/utils" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/p2p/nodestate" @@ -47,34 +47,38 @@ const ( nodeWeightThreshold = 100 // minimum weight for keeping a node in the the known (valuable) set minRedialWait = 10 // minimum redial wait time in seconds preNegLimit = 5 // maximum number of simultaneous pre-negotiation queries - maxQueryFails = 100 // number of consecutive UDP query failures before we print a warning + warnQueryFails = 20 // number of consecutive UDP query failures before we print a warning + maxQueryFails = 100 // number of consecutive UDP query failures when then chance of skipping a query reaches 50% ) -// serverPool provides a node iterator for dial candidates. The output is a mix of newly discovered +// ServerPool provides a node iterator for dial candidates. The output is a mix of newly discovered // nodes, a weighted random selection of known (previously valuable) nodes and trusted/paid nodes. -type serverPool struct { +type ServerPool struct { clock mclock.Clock unixTime func() int64 db ethdb.KeyValueStore - ns *nodestate.NodeStateMachine - vt *lpc.ValueTracker - mixer *enode.FairMix - mixSources []enode.Iterator - dialIterator enode.Iterator - validSchemes enr.IdentityScheme - trustedURLs []string - fillSet *lpc.FillSet - queryFails uint32 + ns *nodestate.NodeStateMachine + vt *ValueTracker + mixer *enode.FairMix + mixSources []enode.Iterator + dialIterator enode.Iterator + validSchemes enr.IdentityScheme + trustedURLs []string + fillSet *FillSet + started, queryFails uint32 timeoutLock sync.RWMutex timeout time.Duration - timeWeights lpc.ResponseTimeWeights + timeWeights ResponseTimeWeights timeoutRefreshed mclock.AbsTime + + suggestedTimeoutGauge, totalValueGauge metrics.Gauge + sessionValueMeter metrics.Meter } // nodeHistory keeps track of dial costs which determine node weight together with the -// service value calculated by lpc.ValueTracker. +// service value calculated by ValueTracker. type nodeHistory struct { dialCost utils.ExpiredValue redialWaitStart, redialWaitEnd int64 // unix time (seconds) @@ -88,21 +92,21 @@ type nodeHistoryEnc struct { // queryFunc sends a pre-negotiation query and blocks until a response arrives or timeout occurs. // It returns 1 if the remote node has confirmed that connection is possible, 0 if not // possible and -1 if no response arrived (timeout). -type queryFunc func(*enode.Node) int +type QueryFunc func(*enode.Node) int var ( - serverPoolSetup = &nodestate.Setup{Version: 1} - sfHasValue = serverPoolSetup.NewPersistentFlag("hasValue") - sfQueried = serverPoolSetup.NewFlag("queried") - sfCanDial = serverPoolSetup.NewFlag("canDial") - sfDialing = serverPoolSetup.NewFlag("dialed") - sfWaitDialTimeout = serverPoolSetup.NewFlag("dialTimeout") - sfConnected = serverPoolSetup.NewFlag("connected") - sfRedialWait = serverPoolSetup.NewFlag("redialWait") - sfAlwaysConnect = serverPoolSetup.NewFlag("alwaysConnect") - sfDisableSelection = nodestate.MergeFlags(sfQueried, sfCanDial, sfDialing, sfConnected, sfRedialWait) - - sfiNodeHistory = serverPoolSetup.NewPersistentField("nodeHistory", reflect.TypeOf(nodeHistory{}), + clientSetup = &nodestate.Setup{Version: 2} + sfHasValue = clientSetup.NewPersistentFlag("hasValue") + sfQuery = clientSetup.NewFlag("query") + sfCanDial = clientSetup.NewFlag("canDial") + sfDialing = clientSetup.NewFlag("dialed") + sfWaitDialTimeout = clientSetup.NewFlag("dialTimeout") + sfConnected = clientSetup.NewFlag("connected") + sfRedialWait = clientSetup.NewFlag("redialWait") + sfAlwaysConnect = clientSetup.NewFlag("alwaysConnect") + sfDialProcess = nodestate.MergeFlags(sfQuery, sfCanDial, sfDialing, sfConnected, sfRedialWait) + + sfiNodeHistory = clientSetup.NewPersistentField("nodeHistory", reflect.TypeOf(nodeHistory{}), func(field interface{}) ([]byte, error) { if n, ok := field.(nodeHistory); ok { ne := nodeHistoryEnc{ @@ -112,9 +116,8 @@ var ( } enc, err := rlp.EncodeToBytes(&ne) return enc, err - } else { - return nil, errors.New("invalid field type") } + return nil, errors.New("invalid field type") }, func(enc []byte) (interface{}, error) { var ne nodeHistoryEnc @@ -127,40 +130,48 @@ var ( return n, err }, ) - sfiNodeWeight = serverPoolSetup.NewField("nodeWeight", reflect.TypeOf(uint64(0))) - sfiConnectedStats = serverPoolSetup.NewField("connectedStats", reflect.TypeOf(lpc.ResponseTimeStats{})) + sfiNodeWeight = clientSetup.NewField("nodeWeight", reflect.TypeOf(uint64(0))) + sfiConnectedStats = clientSetup.NewField("connectedStats", reflect.TypeOf(ResponseTimeStats{})) + sfiLocalAddress = clientSetup.NewPersistentField("localAddress", reflect.TypeOf(&enr.Record{}), + func(field interface{}) ([]byte, error) { + if enr, ok := field.(*enr.Record); ok { + enc, err := rlp.EncodeToBytes(enr) + return enc, err + } + return nil, errors.New("invalid field type") + }, + func(enc []byte) (interface{}, error) { + var enr enr.Record + if err := rlp.DecodeBytes(enc, &enr); err != nil { + return nil, err + } + return &enr, nil + }, + ) ) -// newServerPool creates a new server pool -func newServerPool(db ethdb.KeyValueStore, dbKey []byte, vt *lpc.ValueTracker, discovery enode.Iterator, mixTimeout time.Duration, query queryFunc, clock mclock.Clock, trustedURLs []string) *serverPool { - s := &serverPool{ +// NewServerPool creates a new server pool +func NewServerPool(db ethdb.KeyValueStore, dbKey []byte, mixTimeout time.Duration, query QueryFunc, clock mclock.Clock, trustedURLs []string, requestList []RequestInfo) (*ServerPool, enode.Iterator) { + s := &ServerPool{ db: db, clock: clock, unixTime: func() int64 { return time.Now().Unix() }, validSchemes: enode.ValidSchemes, trustedURLs: trustedURLs, - vt: vt, - ns: nodestate.NewNodeStateMachine(db, []byte(string(dbKey)+"ns:"), clock, serverPoolSetup), + vt: NewValueTracker(db, &mclock.System{}, requestList, time.Minute, 1/float64(time.Hour), 1/float64(time.Hour*100), 1/float64(time.Hour*1000)), + ns: nodestate.NewNodeStateMachine(db, []byte(string(dbKey)+"ns:"), clock, clientSetup), } s.recalTimeout() s.mixer = enode.NewFairMix(mixTimeout) - knownSelector := lpc.NewWrsIterator(s.ns, sfHasValue, sfDisableSelection, sfiNodeWeight) - alwaysConnect := lpc.NewQueueIterator(s.ns, sfAlwaysConnect, sfDisableSelection, true, nil) + knownSelector := NewWrsIterator(s.ns, sfHasValue, sfDialProcess, sfiNodeWeight) + alwaysConnect := NewQueueIterator(s.ns, sfAlwaysConnect, sfDialProcess, true, nil) s.mixSources = append(s.mixSources, knownSelector) s.mixSources = append(s.mixSources, alwaysConnect) - if discovery != nil { - s.mixSources = append(s.mixSources, discovery) - } - iter := enode.Iterator(s.mixer) + s.dialIterator = s.mixer if query != nil { - iter = s.addPreNegFilter(iter, query) + s.dialIterator = s.addPreNegFilter(s.dialIterator, query) } - s.dialIterator = enode.Filter(iter, func(node *enode.Node) bool { - s.ns.SetState(node, sfDialing, sfCanDial, 0) - s.ns.SetState(node, sfWaitDialTimeout, nodestate.Flags{}, time.Second*10) - return true - }) s.ns.SubscribeState(nodestate.MergeFlags(sfWaitDialTimeout, sfConnected), func(n *enode.Node, oldState, newState nodestate.Flags) { if oldState.Equals(sfWaitDialTimeout) && newState.IsEmpty() { @@ -170,55 +181,120 @@ func newServerPool(db ethdb.KeyValueStore, dbKey []byte, vt *lpc.ValueTracker, d } }) - s.ns.AddLogMetrics(sfHasValue, sfDisableSelection, "selectable", nil, nil, serverSelectableGauge) - s.ns.AddLogMetrics(sfDialing, nodestate.Flags{}, "dialed", serverDialedMeter, nil, nil) - s.ns.AddLogMetrics(sfConnected, nodestate.Flags{}, "connected", nil, nil, serverConnectedGauge) - return s + return s, &serverPoolIterator{ + dialIterator: s.dialIterator, + nextFn: func(node *enode.Node) { + s.ns.Operation(func() { + s.ns.SetStateSub(node, sfDialing, sfCanDial, 0) + s.ns.SetStateSub(node, sfWaitDialTimeout, nodestate.Flags{}, time.Second*10) + }) + }, + nodeFn: s.DialNode, + } +} + +type serverPoolIterator struct { + dialIterator enode.Iterator + nextFn func(*enode.Node) + nodeFn func(*enode.Node) *enode.Node +} + +// Next implements enode.Iterator +func (s *serverPoolIterator) Next() bool { + if s.dialIterator.Next() { + s.nextFn(s.dialIterator.Node()) + return true + } + return false +} + +// Node implements enode.Iterator +func (s *serverPoolIterator) Node() *enode.Node { + return s.nodeFn(s.dialIterator.Node()) +} + +// Close implements enode.Iterator +func (s *serverPoolIterator) Close() { + s.dialIterator.Close() +} + +// AddMetrics adds metrics to the server pool. Should be called before Start(). +func (s *ServerPool) AddMetrics( + suggestedTimeoutGauge, totalValueGauge, serverSelectableGauge, serverConnectedGauge metrics.Gauge, + sessionValueMeter, serverDialedMeter metrics.Meter) { + + s.suggestedTimeoutGauge = suggestedTimeoutGauge + s.totalValueGauge = totalValueGauge + s.sessionValueMeter = sessionValueMeter + if serverSelectableGauge != nil { + s.ns.AddLogMetrics(sfHasValue, sfDialProcess, "selectable", nil, nil, serverSelectableGauge) + } + if serverDialedMeter != nil { + s.ns.AddLogMetrics(sfDialing, nodestate.Flags{}, "dialed", serverDialedMeter, nil, nil) + } + if serverConnectedGauge != nil { + s.ns.AddLogMetrics(sfConnected, nodestate.Flags{}, "connected", nil, nil, serverConnectedGauge) + } +} + +// AddSource adds a node discovery source to the server pool (should be called before start) +func (s *ServerPool) AddSource(source enode.Iterator) { + if source != nil { + s.mixSources = append(s.mixSources, source) + } } // addPreNegFilter installs a node filter mechanism that performs a pre-negotiation query. // Nodes that are filtered out and does not appear on the output iterator are put back // into redialWait state. -func (s *serverPool) addPreNegFilter(input enode.Iterator, query queryFunc) enode.Iterator { - s.fillSet = lpc.NewFillSet(s.ns, input, sfQueried) - s.ns.SubscribeState(sfQueried, func(n *enode.Node, oldState, newState nodestate.Flags) { - if newState.Equals(sfQueried) { - fails := atomic.LoadUint32(&s.queryFails) - if fails == maxQueryFails { - log.Warn("UDP pre-negotiation query does not seem to work") - } - if fails > maxQueryFails { - fails = maxQueryFails +func (s *ServerPool) addPreNegFilter(input enode.Iterator, query QueryFunc) enode.Iterator { + s.fillSet = NewFillSet(s.ns, input, sfQuery) + s.ns.SubscribeState(sfDialProcess, func(n *enode.Node, oldState, newState nodestate.Flags) { + if !newState.Equals(sfQuery) { + if newState.HasAll(sfQuery) { + // remove query flag if the node is already somewhere in the dial process + s.ns.SetStateSub(n, nodestate.Flags{}, sfQuery, 0) } - if rand.Intn(maxQueryFails*2) < int(fails) { - // skip pre-negotiation with increasing chance, max 50% - // this ensures that the client can operate even if UDP is not working at all - s.ns.SetStateSub(n, sfCanDial, nodestate.Flags{}, time.Second*10) - // set canDial before resetting queried so that FillSet will not read more - // candidates unnecessarily - s.ns.SetStateSub(n, nodestate.Flags{}, sfQueried, 0) - return + return + } + fails := atomic.LoadUint32(&s.queryFails) + failMax := fails + if failMax > maxQueryFails { + failMax = maxQueryFails + } + if rand.Intn(maxQueryFails*2) < int(failMax) { + // skip pre-negotiation with increasing chance, max 50% + // this ensures that the client can operate even if UDP is not working at all + s.ns.SetStateSub(n, sfCanDial, nodestate.Flags{}, time.Second*10) + // set canDial before resetting queried so that FillSet will not read more + // candidates unnecessarily + s.ns.SetStateSub(n, nodestate.Flags{}, sfQuery, 0) + return + } + go func() { + q := query(n) + if q == -1 { + atomic.AddUint32(&s.queryFails, 1) + fails++ + if fails%warnQueryFails == 0 { + // warn if a large number of consecutive queries have failed + log.Warn("UDP connection queries failed", "count", fails) + } + } else { + atomic.StoreUint32(&s.queryFails, 0) } - go func() { - q := query(n) - if q == -1 { - atomic.AddUint32(&s.queryFails, 1) + s.ns.Operation(func() { + // we are no longer running in the operation that the callback belongs to, start a new one because of setRedialWait + if q == 1 { + s.ns.SetStateSub(n, sfCanDial, nodestate.Flags{}, time.Second*10) } else { - atomic.StoreUint32(&s.queryFails, 0) + s.setRedialWait(n, queryCost, queryWaitStep) } - s.ns.Operation(func() { - // we are no longer running in the operation that the callback belongs to, start a new one because of setRedialWait - if q == 1 { - s.ns.SetStateSub(n, sfCanDial, nodestate.Flags{}, time.Second*10) - } else { - s.setRedialWait(n, queryCost, queryWaitStep) - } - s.ns.SetStateSub(n, nodestate.Flags{}, sfQueried, 0) - }) - }() - } + s.ns.SetStateSub(n, nodestate.Flags{}, sfQuery, 0) + }) + }() }) - return lpc.NewQueueIterator(s.ns, sfCanDial, nodestate.Flags{}, false, func(waiting bool) { + return NewQueueIterator(s.ns, sfCanDial, nodestate.Flags{}, false, func(waiting bool) { if waiting { s.fillSet.SetTarget(preNegLimit) } else { @@ -228,7 +304,7 @@ func (s *serverPool) addPreNegFilter(input enode.Iterator, query queryFunc) enod } // start starts the server pool. Note that NodeStateMachine should be started first. -func (s *serverPool) start() { +func (s *ServerPool) Start() { s.ns.Start() for _, iter := range s.mixSources { // add sources to mixer at startup because the mixer instantly tries to read them @@ -258,11 +334,11 @@ func (s *serverPool) start() { } }) }) + atomic.StoreUint32(&s.started, 1) } // stop stops the server pool -func (s *serverPool) stop() { - s.dialIterator.Close() +func (s *ServerPool) Stop() { if s.fillSet != nil { s.fillSet.Close() } @@ -273,32 +349,39 @@ func (s *serverPool) stop() { }) }) s.ns.Stop() + s.vt.Stop() } -// registerPeer implements serverPeerSubscriber -func (s *serverPool) registerPeer(p *serverPeer) { - s.ns.SetState(p.Node(), sfConnected, sfDialing.Or(sfWaitDialTimeout), 0) - nvt := s.vt.Register(p.ID()) - s.ns.SetField(p.Node(), sfiConnectedStats, nvt.RtStats()) - p.setValueTracker(s.vt, nvt) - p.updateVtParams() +// RegisterNode implements serverPeerSubscriber +func (s *ServerPool) RegisterNode(node *enode.Node) (*NodeValueTracker, error) { + if atomic.LoadUint32(&s.started) == 0 { + return nil, errors.New("server pool not started yet") + } + nvt := s.vt.Register(node.ID()) + s.ns.Operation(func() { + s.ns.SetStateSub(node, sfConnected, sfDialing.Or(sfWaitDialTimeout), 0) + s.ns.SetFieldSub(node, sfiConnectedStats, nvt.RtStats()) + if node.IP().IsLoopback() { + s.ns.SetFieldSub(node, sfiLocalAddress, node.Record()) + } + }) + return nvt, nil } -// unregisterPeer implements serverPeerSubscriber -func (s *serverPool) unregisterPeer(p *serverPeer) { +// UnregisterNode implements serverPeerSubscriber +func (s *ServerPool) UnregisterNode(node *enode.Node) { s.ns.Operation(func() { - s.setRedialWait(p.Node(), dialCost, dialWaitStep) - s.ns.SetStateSub(p.Node(), nodestate.Flags{}, sfConnected, 0) - s.ns.SetFieldSub(p.Node(), sfiConnectedStats, nil) + s.setRedialWait(node, dialCost, dialWaitStep) + s.ns.SetStateSub(node, nodestate.Flags{}, sfConnected, 0) + s.ns.SetFieldSub(node, sfiConnectedStats, nil) }) - s.vt.Unregister(p.ID()) - p.setValueTracker(nil, nil) + s.vt.Unregister(node.ID()) } // recalTimeout calculates the current recommended timeout. This value is used by // the client as a "soft timeout" value. It also affects the service value calculation // of individual nodes. -func (s *serverPool) recalTimeout() { +func (s *ServerPool) recalTimeout() { // Use cached result if possible, avoid recalculating too frequently. s.timeoutLock.RLock() refreshed := s.timeoutRefreshed @@ -327,17 +410,21 @@ func (s *serverPool) recalTimeout() { s.timeoutLock.Lock() if s.timeout != timeout { s.timeout = timeout - s.timeWeights = lpc.TimeoutWeights(s.timeout) + s.timeWeights = TimeoutWeights(s.timeout) - suggestedTimeoutGauge.Update(int64(s.timeout / time.Millisecond)) - totalValueGauge.Update(int64(rts.Value(s.timeWeights, s.vt.StatsExpFactor()))) + if s.suggestedTimeoutGauge != nil { + s.suggestedTimeoutGauge.Update(int64(s.timeout / time.Millisecond)) + } + if s.totalValueGauge != nil { + s.totalValueGauge.Update(int64(rts.Value(s.timeWeights, s.vt.StatsExpFactor()))) + } } s.timeoutRefreshed = now s.timeoutLock.Unlock() } -// getTimeout returns the recommended request timeout. -func (s *serverPool) getTimeout() time.Duration { +// GetTimeout returns the recommended request timeout. +func (s *ServerPool) GetTimeout() time.Duration { s.recalTimeout() s.timeoutLock.RLock() defer s.timeoutLock.RUnlock() @@ -346,7 +433,7 @@ func (s *serverPool) getTimeout() time.Duration { // getTimeoutAndWeight returns the recommended request timeout as well as the // response time weight which is necessary to calculate service value. -func (s *serverPool) getTimeoutAndWeight() (time.Duration, lpc.ResponseTimeWeights) { +func (s *ServerPool) getTimeoutAndWeight() (time.Duration, ResponseTimeWeights) { s.recalTimeout() s.timeoutLock.RLock() defer s.timeoutLock.RUnlock() @@ -355,7 +442,7 @@ func (s *serverPool) getTimeoutAndWeight() (time.Duration, lpc.ResponseTimeWeigh // addDialCost adds the given amount of dial cost to the node history and returns the current // amount of total dial cost -func (s *serverPool) addDialCost(n *nodeHistory, amount int64) uint64 { +func (s *ServerPool) addDialCost(n *nodeHistory, amount int64) uint64 { logOffset := s.vt.StatsExpirer().LogOffset(s.clock.Now()) if amount > 0 { n.dialCost.Add(amount, logOffset) @@ -368,7 +455,7 @@ func (s *serverPool) addDialCost(n *nodeHistory, amount int64) uint64 { } // serviceValue returns the service value accumulated in this session and in total -func (s *serverPool) serviceValue(node *enode.Node) (sessionValue, totalValue float64) { +func (s *ServerPool) serviceValue(node *enode.Node) (sessionValue, totalValue float64) { nvt := s.vt.GetNode(node.ID()) if nvt == nil { return 0, 0 @@ -378,11 +465,13 @@ func (s *serverPool) serviceValue(node *enode.Node) (sessionValue, totalValue fl expFactor := s.vt.StatsExpFactor() totalValue = currentStats.Value(timeWeights, expFactor) - if connStats, ok := s.ns.GetField(node, sfiConnectedStats).(lpc.ResponseTimeStats); ok { + if connStats, ok := s.ns.GetField(node, sfiConnectedStats).(ResponseTimeStats); ok { diff := currentStats diff.SubStats(&connStats) sessionValue = diff.Value(timeWeights, expFactor) - sessionValueMeter.Mark(int64(sessionValue)) + if s.sessionValueMeter != nil { + s.sessionValueMeter.Mark(int64(sessionValue)) + } } return } @@ -390,7 +479,7 @@ func (s *serverPool) serviceValue(node *enode.Node) (sessionValue, totalValue fl // updateWeight calculates the node weight and updates the nodeWeight field and the // hasValue flag. It also saves the node state if necessary. // Note: this function should run inside a NodeStateMachine operation -func (s *serverPool) updateWeight(node *enode.Node, totalValue float64, totalDialCost uint64) { +func (s *ServerPool) updateWeight(node *enode.Node, totalValue float64, totalDialCost uint64) { weight := uint64(totalValue * nodeWeightMul / float64(totalDialCost)) if weight >= nodeWeightThreshold { s.ns.SetStateSub(node, sfHasValue, nodestate.Flags{}, 0) @@ -399,6 +488,7 @@ func (s *serverPool) updateWeight(node *enode.Node, totalValue float64, totalDia s.ns.SetStateSub(node, nodestate.Flags{}, sfHasValue, 0) s.ns.SetFieldSub(node, sfiNodeWeight, nil) s.ns.SetFieldSub(node, sfiNodeHistory, nil) + s.ns.SetFieldSub(node, sfiLocalAddress, nil) } s.ns.Persist(node) // saved if node history or hasValue changed } @@ -412,7 +502,7 @@ func (s *serverPool) updateWeight(node *enode.Node, totalValue float64, totalDia // to the minimum. // Note: node weight is also recalculated and updated by this function. // Note 2: this function should run inside a NodeStateMachine operation -func (s *serverPool) setRedialWait(node *enode.Node, addDialCost int64, waitStep float64) { +func (s *ServerPool) setRedialWait(node *enode.Node, addDialCost int64, waitStep float64) { n, _ := s.ns.GetField(node, sfiNodeHistory).(nodeHistory) sessionValue, totalValue := s.serviceValue(node) totalDialCost := s.addDialCost(&n, addDialCost) @@ -478,9 +568,39 @@ func (s *serverPool) setRedialWait(node *enode.Node, addDialCost int64, waitStep // This function should be called during startup and shutdown only, otherwise setRedialWait // will keep the weights updated as the underlying statistics are adjusted. // Note: this function should run inside a NodeStateMachine operation -func (s *serverPool) calculateWeight(node *enode.Node) { +func (s *ServerPool) calculateWeight(node *enode.Node) { n, _ := s.ns.GetField(node, sfiNodeHistory).(nodeHistory) _, totalValue := s.serviceValue(node) totalDialCost := s.addDialCost(&n, 0) s.updateWeight(node, totalValue, totalDialCost) } + +// API returns the vflux client API +func (s *ServerPool) API() *PrivateClientAPI { + return NewPrivateClientAPI(s.vt) +} + +type dummyIdentity enode.ID + +func (id dummyIdentity) Verify(r *enr.Record, sig []byte) error { return nil } +func (id dummyIdentity) NodeAddr(r *enr.Record) []byte { return id[:] } + +// DialNode replaces the given enode with a locally generated one containing the ENR +// stored in the sfiLocalAddress field if present. This workaround ensures that nodes +// on the local network can be dialed at the local address if a connection has been +// successfully established previously. +// Note that NodeStateMachine always remembers the enode with the latest version of +// the remote signed ENR. ENR filtering should be performed on that version while +// dialNode should be used for dialing the node over TCP or UDP. +func (s *ServerPool) DialNode(n *enode.Node) *enode.Node { + if enr, ok := s.ns.GetField(n, sfiLocalAddress).(*enr.Record); ok { + n, _ := enode.New(dummyIdentity(n.ID()), enr) + return n + } + return n +} + +// Persist immediately stores the state of a node in the node database +func (s *ServerPool) Persist(n *enode.Node) { + s.ns.Persist(n) +} diff --git a/les/serverpool_test.go b/les/vflux/client/serverpool_test.go similarity index 80% rename from les/serverpool_test.go rename to les/vflux/client/serverpool_test.go index 3d0487d102..c777d6c16d 100644 --- a/les/serverpool_test.go +++ b/les/vflux/client/serverpool_test.go @@ -14,10 +14,11 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package les +package client import ( "math/rand" + "strconv" "sync/atomic" "testing" "time" @@ -25,8 +26,6 @@ import ( "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb/memorydb" - lpc "github.com/ethereum/go-ethereum/les/lespay/client" - "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" ) @@ -50,13 +49,14 @@ func testNodeIndex(id enode.ID) int { return int(id[1]) + int(id[2])*256 } -type serverPoolTest struct { +type ServerPoolTest struct { db ethdb.KeyValueStore clock *mclock.Simulated quit chan struct{} preNeg, preNegFail bool - vt *lpc.ValueTracker - sp *serverPool + vt *ValueTracker + sp *ServerPool + spi enode.Iterator input enode.Iterator testNodes []spTestNode trusted []string @@ -71,15 +71,15 @@ type spTestNode struct { connectCycles, waitCycles int nextConnCycle, totalConn int connected, service bool - peer *serverPeer + node *enode.Node } -func newServerPoolTest(preNeg, preNegFail bool) *serverPoolTest { +func newServerPoolTest(preNeg, preNegFail bool) *ServerPoolTest { nodes := make([]*enode.Node, spTestNodes) for i := range nodes { nodes[i] = enode.SignNull(&enr.Record{}, testNodeID(i)) } - return &serverPoolTest{ + return &ServerPoolTest{ clock: &mclock.Simulated{}, db: memorydb.New(), input: enode.CycleNodes(nodes), @@ -89,7 +89,7 @@ func newServerPoolTest(preNeg, preNegFail bool) *serverPoolTest { } } -func (s *serverPoolTest) beginWait() { +func (s *ServerPoolTest) beginWait() { // ensure that dialIterator and the maximal number of pre-neg queries are not all stuck in a waiting state for atomic.AddInt32(&s.waitCount, 1) > preNegLimit { atomic.AddInt32(&s.waitCount, -1) @@ -97,17 +97,17 @@ func (s *serverPoolTest) beginWait() { } } -func (s *serverPoolTest) endWait() { +func (s *ServerPoolTest) endWait() { atomic.AddInt32(&s.waitCount, -1) atomic.AddInt32(&s.waitEnded, 1) } -func (s *serverPoolTest) addTrusted(i int) { +func (s *ServerPoolTest) addTrusted(i int) { s.trusted = append(s.trusted, enode.SignNull(&enr.Record{}, testNodeID(i)).String()) } -func (s *serverPoolTest) start() { - var testQuery queryFunc +func (s *ServerPoolTest) start() { + var testQuery QueryFunc if s.preNeg { testQuery = func(node *enode.Node) int { idx := testNodeIndex(node.ID()) @@ -119,40 +119,42 @@ func (s *serverPoolTest) start() { s.clock.Sleep(time.Second * 5) s.endWait() return -1 - } else { - switch idx % 3 { - case 0: - // pre-neg returns true only if connection is possible - if canConnect { - return 1 - } else { - return 0 - } - case 1: - // pre-neg returns true but connection might still fail + } + switch idx % 3 { + case 0: + // pre-neg returns true only if connection is possible + if canConnect { + return 1 + } + return 0 + case 1: + // pre-neg returns true but connection might still fail + return 1 + case 2: + // pre-neg returns true if connection is possible, otherwise timeout (node unresponsive) + if canConnect { return 1 - case 2: - // pre-neg returns true if connection is possible, otherwise timeout (node unresponsive) - if canConnect { - return 1 - } else { - s.beginWait() - s.clock.Sleep(time.Second * 5) - s.endWait() - return -1 - } } + s.beginWait() + s.clock.Sleep(time.Second * 5) + s.endWait() return -1 } + return -1 } } - s.vt = lpc.NewValueTracker(s.db, s.clock, requestList, time.Minute, 1/float64(time.Hour), 1/float64(time.Hour*100), 1/float64(time.Hour*1000)) - s.sp = newServerPool(s.db, []byte("serverpool:"), s.vt, s.input, 0, testQuery, s.clock, s.trusted) + requestList := make([]RequestInfo, testReqTypes) + for i := range requestList { + requestList[i] = RequestInfo{Name: "testreq" + strconv.Itoa(i), InitAmount: 1, InitValue: 1} + } + + s.sp, s.spi = NewServerPool(s.db, []byte("sp:"), 0, testQuery, s.clock, s.trusted, requestList) + s.sp.AddSource(s.input) s.sp.validSchemes = enode.ValidSchemesForTesting s.sp.unixTime = func() int64 { return int64(s.clock.Now()) / int64(time.Second) } s.disconnect = make(map[int][]int) - s.sp.start() + s.sp.Start() s.quit = make(chan struct{}) go func() { last := int32(-1) @@ -172,31 +174,31 @@ func (s *serverPoolTest) start() { }() } -func (s *serverPoolTest) stop() { +func (s *ServerPoolTest) stop() { close(s.quit) - s.sp.stop() - s.vt.Stop() + s.sp.Stop() + s.spi.Close() for i := range s.testNodes { n := &s.testNodes[i] if n.connected { n.totalConn += s.cycle } n.connected = false - n.peer = nil + n.node = nil n.nextConnCycle = 0 } s.conn, s.servedConn = 0, 0 } -func (s *serverPoolTest) run() { +func (s *ServerPoolTest) run() { for count := spTestLength; count > 0; count-- { if dcList := s.disconnect[s.cycle]; dcList != nil { for _, idx := range dcList { n := &s.testNodes[idx] - s.sp.unregisterPeer(n.peer) + s.sp.UnregisterNode(n.node) n.totalConn += s.cycle n.connected = false - n.peer = nil + n.node = nil s.conn-- if n.service { s.servedConn-- @@ -208,9 +210,9 @@ func (s *serverPoolTest) run() { if s.conn < spTestTarget { s.dialCount++ s.beginWait() - s.sp.dialIterator.Next() + s.spi.Next() s.endWait() - dial := s.sp.dialIterator.Node() + dial := s.spi.Node() id := dial.ID() idx := testNodeIndex(id) n := &s.testNodes[idx] @@ -223,10 +225,10 @@ func (s *serverPoolTest) run() { n.connected = true dc := s.cycle + n.connectCycles s.disconnect[dc] = append(s.disconnect[dc], idx) - n.peer = &serverPeer{peerCommons: peerCommons{Peer: p2p.NewPeer(id, "", nil)}} - s.sp.registerPeer(n.peer) + n.node = dial + nv, _ := s.sp.RegisterNode(n.node) if n.service { - s.vt.Served(s.vt.GetNode(id), []lpc.ServedRequest{{ReqType: 0, Amount: 100}}, 0) + nv.Served([]ServedRequest{{ReqType: 0, Amount: 100}}, 0) } } } @@ -236,7 +238,7 @@ func (s *serverPoolTest) run() { } } -func (s *serverPoolTest) setNodes(count, conn, wait int, service, trusted bool) (res []int) { +func (s *ServerPoolTest) setNodes(count, conn, wait int, service, trusted bool) (res []int) { for ; count > 0; count-- { idx := rand.Intn(spTestNodes) for s.testNodes[idx].connectCycles != 0 || s.testNodes[idx].connected { @@ -255,11 +257,11 @@ func (s *serverPoolTest) setNodes(count, conn, wait int, service, trusted bool) return } -func (s *serverPoolTest) resetNodes() { +func (s *ServerPoolTest) resetNodes() { for i, n := range s.testNodes { if n.connected { n.totalConn += s.cycle - s.sp.unregisterPeer(n.peer) + s.sp.UnregisterNode(n.node) } s.testNodes[i] = spTestNode{totalConn: n.totalConn} } @@ -268,7 +270,7 @@ func (s *serverPoolTest) resetNodes() { s.trusted = nil } -func (s *serverPoolTest) checkNodes(t *testing.T, nodes []int) { +func (s *ServerPoolTest) checkNodes(t *testing.T, nodes []int) { var sum int for _, idx := range nodes { n := &s.testNodes[idx] diff --git a/les/lespay/client/timestats.go b/les/vflux/client/timestats.go similarity index 100% rename from les/lespay/client/timestats.go rename to les/vflux/client/timestats.go diff --git a/les/lespay/client/timestats_test.go b/les/vflux/client/timestats_test.go similarity index 100% rename from les/lespay/client/timestats_test.go rename to les/vflux/client/timestats_test.go diff --git a/les/lespay/client/valuetracker.go b/les/vflux/client/valuetracker.go similarity index 94% rename from les/lespay/client/valuetracker.go rename to les/vflux/client/valuetracker.go index 4e67b31d96..f5390d0920 100644 --- a/les/lespay/client/valuetracker.go +++ b/les/vflux/client/valuetracker.go @@ -45,6 +45,7 @@ var ( type NodeValueTracker struct { lock sync.Mutex + vt *ValueTracker rtStats, lastRtStats ResponseTimeStats lastTransfer mclock.AbsTime basket serverBasket @@ -52,15 +53,12 @@ type NodeValueTracker struct { reqValues *[]float64 } -// init initializes a NodeValueTracker. -// Note that the contents of the referenced reqValues slice will not change; a new -// reference is passed if the values are updated by ValueTracker. -func (nv *NodeValueTracker) init(now mclock.AbsTime, reqValues *[]float64) { - reqTypeCount := len(*reqValues) - nv.reqCosts = make([]uint64, reqTypeCount) - nv.lastTransfer = now - nv.reqValues = reqValues - nv.basket.init(reqTypeCount) +// UpdateCosts updates the node value tracker's request cost table +func (nv *NodeValueTracker) UpdateCosts(reqCosts []uint64) { + nv.vt.lock.Lock() + defer nv.vt.lock.Unlock() + + nv.updateCosts(reqCosts, &nv.vt.refBasket.reqValues, nv.vt.refBasket.reqValueFactor(reqCosts)) } // updateCosts updates the request cost table of the server. The request value factor @@ -97,6 +95,28 @@ func (nv *NodeValueTracker) transferStats(now mclock.AbsTime, transferRate float return nv.basket.transfer(-math.Expm1(-transferRate * float64(dt))), recentRtStats } +type ServedRequest struct { + ReqType, Amount uint32 +} + +// Served adds a served request to the node's statistics. An actual request may be composed +// of one or more request types (service vector indices). +func (nv *NodeValueTracker) Served(reqs []ServedRequest, respTime time.Duration) { + nv.vt.statsExpLock.RLock() + expFactor := nv.vt.statsExpFactor + nv.vt.statsExpLock.RUnlock() + + nv.lock.Lock() + defer nv.lock.Unlock() + + var value float64 + for _, r := range reqs { + nv.basket.add(r.ReqType, r.Amount, nv.reqCosts[r.ReqType]*uint64(r.Amount), expFactor) + value += (*nv.reqValues)[r.ReqType] * float64(r.Amount) + } + nv.rtStats.Add(respTime, value, expFactor) +} + // RtStats returns the node's own response time distribution statistics func (nv *NodeValueTracker) RtStats() ResponseTimeStats { nv.lock.Lock() @@ -333,7 +353,12 @@ func (vt *ValueTracker) Register(id enode.ID) *NodeValueTracker { return nil } nv := vt.loadOrNewNode(id) - nv.init(vt.clock.Now(), &vt.refBasket.reqValues) + reqTypeCount := len(vt.refBasket.reqValues) + nv.reqCosts = make([]uint64, reqTypeCount) + nv.lastTransfer = vt.clock.Now() + nv.reqValues = &vt.refBasket.reqValues + nv.basket.init(reqTypeCount) + vt.connected[id] = nv return nv } @@ -364,7 +389,7 @@ func (vt *ValueTracker) loadOrNewNode(id enode.ID) *NodeValueTracker { if nv, ok := vt.connected[id]; ok { return nv } - nv := &NodeValueTracker{lastTransfer: vt.clock.Now()} + nv := &NodeValueTracker{vt: vt, lastTransfer: vt.clock.Now()} enc, err := vt.db.Get(append(vtNodeKey, id[:]...)) if err != nil { return nv @@ -425,14 +450,6 @@ func (vt *ValueTracker) saveNode(id enode.ID, nv *NodeValueTracker) { } } -// UpdateCosts updates the node value tracker's request cost table -func (vt *ValueTracker) UpdateCosts(nv *NodeValueTracker, reqCosts []uint64) { - vt.lock.Lock() - defer vt.lock.Unlock() - - nv.updateCosts(reqCosts, &vt.refBasket.reqValues, vt.refBasket.reqValueFactor(reqCosts)) -} - // RtStats returns the global response time distribution statistics func (vt *ValueTracker) RtStats() ResponseTimeStats { vt.lock.Lock() @@ -464,28 +481,6 @@ func (vt *ValueTracker) periodicUpdate() { vt.saveToDb() } -type ServedRequest struct { - ReqType, Amount uint32 -} - -// Served adds a served request to the node's statistics. An actual request may be composed -// of one or more request types (service vector indices). -func (vt *ValueTracker) Served(nv *NodeValueTracker, reqs []ServedRequest, respTime time.Duration) { - vt.statsExpLock.RLock() - expFactor := vt.statsExpFactor - vt.statsExpLock.RUnlock() - - nv.lock.Lock() - defer nv.lock.Unlock() - - var value float64 - for _, r := range reqs { - nv.basket.add(r.ReqType, r.Amount, nv.reqCosts[r.ReqType]*uint64(r.Amount), expFactor) - value += (*nv.reqValues)[r.ReqType] * float64(r.Amount) - } - nv.rtStats.Add(respTime, value, vt.statsExpFactor) -} - type RequestStatsItem struct { Name string ReqAmount, ReqValue float64 diff --git a/les/lespay/client/valuetracker_test.go b/les/vflux/client/valuetracker_test.go similarity index 97% rename from les/lespay/client/valuetracker_test.go rename to les/vflux/client/valuetracker_test.go index ad398749e9..87a337be8d 100644 --- a/les/lespay/client/valuetracker_test.go +++ b/les/vflux/client/valuetracker_test.go @@ -64,7 +64,7 @@ func TestValueTracker(t *testing.T) { for j := range costList { costList[j] = uint64(baseCost * relPrices[j]) } - vt.UpdateCosts(nodes[i], costList) + nodes[i].UpdateCosts(costList) } for i := range nodes { nodes[i] = vt.Register(enode.ID{byte(i)}) @@ -77,7 +77,7 @@ func TestValueTracker(t *testing.T) { node := rand.Intn(testNodeCount) respTime := time.Duration((rand.Float64() + 1) * float64(time.Second) * float64(node+1) / testNodeCount) totalAmount[reqType] += uint64(reqAmount) - vt.Served(nodes[node], []ServedRequest{{uint32(reqType), uint32(reqAmount)}}, respTime) + nodes[node].Served([]ServedRequest{{uint32(reqType), uint32(reqAmount)}}, respTime) clock.Run(time.Second) } } else { diff --git a/les/lespay/client/wrsiterator.go b/les/vflux/client/wrsiterator.go similarity index 100% rename from les/lespay/client/wrsiterator.go rename to les/vflux/client/wrsiterator.go diff --git a/les/lespay/client/wrsiterator_test.go b/les/vflux/client/wrsiterator_test.go similarity index 100% rename from les/lespay/client/wrsiterator_test.go rename to les/vflux/client/wrsiterator_test.go diff --git a/les/vflux/requests.go b/les/vflux/requests.go new file mode 100644 index 0000000000..11255607e8 --- /dev/null +++ b/les/vflux/requests.go @@ -0,0 +1,180 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vflux + +import ( + "errors" + "math" + "math/big" + + "github.com/ethereum/go-ethereum/rlp" +) + +var ErrNoReply = errors.New("no reply for given request") + +const ( + MaxRequestLength = 16 // max number of individual requests in a batch + CapacityQueryName = "cq" + CapacityQueryMaxLen = 16 +) + +type ( + // Request describes a single vflux request inside a batch. Service and request + // type are identified by strings, parameters are RLP encoded. + Request struct { + Service, Name string + Params []byte + } + // Requests are a batch of vflux requests + Requests []Request + + // Replies are the replies to a batch of requests + Replies [][]byte + + // CapacityQueryReq is the encoding format of the capacity query + CapacityQueryReq struct { + Bias uint64 // seconds + AddTokens []IntOrInf + } + // CapacityQueryReq is the encoding format of the response to the capacity query + CapacityQueryReply []uint64 +) + +// Add encodes and adds a new request to the batch +func (r *Requests) Add(service, name string, val interface{}) (int, error) { + enc, err := rlp.EncodeToBytes(val) + if err != nil { + return -1, err + } + *r = append(*r, Request{ + Service: service, + Name: name, + Params: enc, + }) + return len(*r) - 1, nil +} + +// Get decodes the reply to the i-th request in the batch +func (r Replies) Get(i int, val interface{}) error { + if i < 0 || i >= len(r) { + return ErrNoReply + } + return rlp.DecodeBytes(r[i], val) +} + +const ( + IntNonNegative = iota + IntNegative + IntPlusInf + IntMinusInf +) + +// IntOrInf is the encoding format for arbitrary length signed integers that can also +// hold the values of +Inf or -Inf +type IntOrInf struct { + Type uint8 + Value big.Int +} + +// BigInt returns the value as a big.Int or panics if the value is infinity +func (i *IntOrInf) BigInt() *big.Int { + switch i.Type { + case IntNonNegative: + return new(big.Int).Set(&i.Value) + case IntNegative: + return new(big.Int).Neg(&i.Value) + case IntPlusInf: + panic(nil) // caller should check Inf() before trying to convert to big.Int + case IntMinusInf: + panic(nil) + } + return &big.Int{} // invalid type decodes to 0 value +} + +// Inf returns 1 if the value is +Inf, -1 if it is -Inf, 0 otherwise +func (i *IntOrInf) Inf() int { + switch i.Type { + case IntPlusInf: + return 1 + case IntMinusInf: + return -1 + } + return 0 // invalid type decodes to 0 value +} + +// Int64 limits the value between MinInt64 and MaxInt64 (even if it is +-Inf) and returns an int64 type +func (i *IntOrInf) Int64() int64 { + switch i.Type { + case IntNonNegative: + if i.Value.IsInt64() { + return i.Value.Int64() + } else { + return math.MaxInt64 + } + case IntNegative: + if i.Value.IsInt64() { + return -i.Value.Int64() + } else { + return math.MinInt64 + } + case IntPlusInf: + return math.MaxInt64 + case IntMinusInf: + return math.MinInt64 + } + return 0 // invalid type decodes to 0 value +} + +// SetBigInt sets the value to the given big.Int +func (i *IntOrInf) SetBigInt(v *big.Int) { + if v.Sign() >= 0 { + i.Type = IntNonNegative + i.Value.Set(v) + } else { + i.Type = IntNegative + i.Value.Neg(v) + } +} + +// SetInt64 sets the value to the given int64. Note that MaxInt64 translates to +Inf +// while MinInt64 translates to -Inf. +func (i *IntOrInf) SetInt64(v int64) { + if v >= 0 { + if v == math.MaxInt64 { + i.Type = IntPlusInf + } else { + i.Type = IntNonNegative + i.Value.SetInt64(v) + } + } else { + if v == math.MinInt64 { + i.Type = IntMinusInf + } else { + i.Type = IntNegative + i.Value.SetInt64(-v) + } + } +} + +// SetInf sets the value to +Inf or -Inf +func (i *IntOrInf) SetInf(sign int) { + if sign == 1 { + i.Type = IntPlusInf + } else { + i.Type = IntMinusInf + } +} diff --git a/les/lespay/server/balance.go b/les/vflux/server/balance.go similarity index 52% rename from les/lespay/server/balance.go rename to les/vflux/server/balance.go index f820a4ad05..01e645a16a 100644 --- a/les/lespay/server/balance.go +++ b/les/vflux/server/balance.go @@ -47,21 +47,57 @@ type PriceFactors struct { TimeFactor, CapacityFactor, RequestFactor float64 } -// timePrice returns the price of connection per nanosecond at the given capacity -func (p PriceFactors) timePrice(cap uint64) float64 { - return p.TimeFactor + float64(cap)*p.CapacityFactor/1000000 +// connectionPrice returns the price of connection per nanosecond at the given capacity +// and the estimated average request cost. +func (p PriceFactors) connectionPrice(cap uint64, avgReqCost float64) float64 { + return p.TimeFactor + float64(cap)*p.CapacityFactor/1000000 + p.RequestFactor*avgReqCost } -// NodeBalance keeps track of the positive and negative balances of a connected +type ( + // nodePriority interface provides current and estimated future priorities on demand + nodePriority interface { + // priority should return the current priority of the node (higher is better) + priority(cap uint64) int64 + // estimatePriority should return a lower estimate for the minimum of the node priority + // value starting from the current moment until the given time. If the priority goes + // under the returned estimate before the specified moment then it is the caller's + // responsibility to signal with updateFlag. + estimatePriority(cap uint64, addBalance int64, future, bias time.Duration, update bool) int64 + } + + // ReadOnlyBalance provides read-only operations on the node balance + ReadOnlyBalance interface { + nodePriority + GetBalance() (uint64, uint64) + GetRawBalance() (utils.ExpiredValue, utils.ExpiredValue) + GetPriceFactors() (posFactor, negFactor PriceFactors) + } + + // ConnectedBalance provides operations permitted on connected nodes (non-read-only + // operations are not permitted inside a BalanceOperation) + ConnectedBalance interface { + ReadOnlyBalance + SetPriceFactors(posFactor, negFactor PriceFactors) + RequestServed(cost uint64) uint64 + } + + // AtomicBalanceOperator provides operations permitted in an atomic BalanceOperation + AtomicBalanceOperator interface { + ReadOnlyBalance + AddBalance(amount int64) (uint64, uint64, error) + SetBalance(pos, neg uint64) error + } +) + +// nodeBalance keeps track of the positive and negative balances of a connected // client and calculates actual and projected future priority values. // Implements nodePriority interface. -type NodeBalance struct { - bt *BalanceTracker +type nodeBalance struct { + bt *balanceTracker lock sync.RWMutex node *enode.Node connAddress string - active bool - priority bool + active, hasPriority, setFlags bool capacity uint64 balance balance posFactor, negFactor PriceFactors @@ -78,7 +114,62 @@ type NodeBalance struct { // balance represents a pair of positive and negative balances type balance struct { - pos, neg utils.ExpiredValue + pos, neg utils.ExpiredValue + posExp, negExp utils.ValueExpirer +} + +// posValue returns the value of positive balance at a given timestamp. +func (b balance) posValue(now mclock.AbsTime) uint64 { + return b.pos.Value(b.posExp.LogOffset(now)) +} + +// negValue returns the value of negative balance at a given timestamp. +func (b balance) negValue(now mclock.AbsTime) uint64 { + return b.neg.Value(b.negExp.LogOffset(now)) +} + +// addValue adds the value of a given amount to the balance. The original value and +// updated value will also be returned if the addition is successful. +// Returns the error if the given value is too large and the value overflows. +func (b *balance) addValue(now mclock.AbsTime, amount int64, pos bool, force bool) (uint64, uint64, int64, error) { + var ( + val utils.ExpiredValue + offset utils.Fixed64 + ) + if pos { + offset, val = b.posExp.LogOffset(now), b.pos + } else { + offset, val = b.negExp.LogOffset(now), b.neg + } + old := val.Value(offset) + if amount > 0 && (amount > maxBalance || old > maxBalance-uint64(amount)) { + if !force { + return old, 0, 0, errBalanceOverflow + } + val = utils.ExpiredValue{} + amount = maxBalance + } + net := val.Add(amount, offset) + if pos { + b.pos = val + } else { + b.neg = val + } + return old, val.Value(offset), net, nil +} + +// setValue sets the internal balance amount to the given values. Returns the +// error if the given value is too large. +func (b *balance) setValue(now mclock.AbsTime, pos uint64, neg uint64) error { + if pos > maxBalance || neg > maxBalance { + return errBalanceOverflow + } + var pb, nb utils.ExpiredValue + pb.Add(int64(pos), b.posExp.LogOffset(now)) + nb.Add(int64(neg), b.negExp.LogOffset(now)) + b.pos = pb + b.neg = nb + return nil } // balanceCallback represents a single callback that is activated when client priority @@ -90,18 +181,18 @@ type balanceCallback struct { } // GetBalance returns the current positive and negative balance. -func (n *NodeBalance) GetBalance() (uint64, uint64) { +func (n *nodeBalance) GetBalance() (uint64, uint64) { n.lock.Lock() defer n.lock.Unlock() now := n.bt.clock.Now() n.updateBalance(now) - return n.balance.pos.Value(n.bt.posExp.LogOffset(now)), n.balance.neg.Value(n.bt.negExp.LogOffset(now)) + return n.balance.posValue(now), n.balance.negValue(now) } // GetRawBalance returns the current positive and negative balance // but in the raw(expired value) format. -func (n *NodeBalance) GetRawBalance() (utils.ExpiredValue, utils.ExpiredValue) { +func (n *nodeBalance) GetRawBalance() (utils.ExpiredValue, utils.ExpiredValue) { n.lock.Lock() defer n.lock.Unlock() @@ -114,203 +205,173 @@ func (n *NodeBalance) GetRawBalance() (utils.ExpiredValue, utils.ExpiredValue) { // before and after the operation. Exceeding maxBalance results in an error (balance is // unchanged) while adding a negative amount higher than the current balance results in // zero balance. -func (n *NodeBalance) AddBalance(amount int64) (uint64, uint64, error) { +// Note: this function should run inside a NodeStateMachine operation +func (n *nodeBalance) AddBalance(amount int64) (uint64, uint64, error) { var ( - err error - old, new uint64 + err error + old, new uint64 + now = n.bt.clock.Now() + callbacks []func() + setPriority bool ) - n.bt.ns.Operation(func() { - var ( - callbacks []func() - setPriority bool - ) - n.bt.updateTotalBalance(n, func() bool { - now := n.bt.clock.Now() - n.updateBalance(now) - - // Ensure the given amount is valid to apply. - offset := n.bt.posExp.LogOffset(now) - old = n.balance.pos.Value(offset) - if amount > 0 && (amount > maxBalance || old > maxBalance-uint64(amount)) { - err = errBalanceOverflow - return false - } - - // Update the total positive balance counter. - n.balance.pos.Add(amount, offset) - callbacks = n.checkCallbacks(now) - setPriority = n.checkPriorityStatus() - new = n.balance.pos.Value(offset) - n.storeBalance(true, false) - return true - }) - for _, cb := range callbacks { - cb() - } - if setPriority { - n.bt.ns.SetStateSub(n.node, n.bt.PriorityFlag, nodestate.Flags{}, 0) + // Operation with holding the lock + n.bt.updateTotalBalance(n, func() bool { + n.updateBalance(now) + if old, new, _, err = n.balance.addValue(now, amount, true, false); err != nil { + return false } - n.signalPriorityUpdate() + callbacks, setPriority = n.checkCallbacks(now), n.checkPriorityStatus() + n.storeBalance(true, false) + return true }) if err != nil { return old, old, err } - + // Operation without holding the lock + for _, cb := range callbacks { + cb() + } + if n.setFlags { + if setPriority { + n.bt.ns.SetStateSub(n.node, n.bt.setup.priorityFlag, nodestate.Flags{}, 0) + } + // Note: priority flag is automatically removed by the zero priority callback if necessary + n.signalPriorityUpdate() + } return old, new, nil } // SetBalance sets the positive and negative balance to the given values -func (n *NodeBalance) SetBalance(pos, neg uint64) error { - if pos > maxBalance || neg > maxBalance { - return errBalanceOverflow - } - n.bt.ns.Operation(func() { - var ( - callbacks []func() - setPriority bool - ) - n.bt.updateTotalBalance(n, func() bool { - now := n.bt.clock.Now() - n.updateBalance(now) - - var pb, nb utils.ExpiredValue - pb.Add(int64(pos), n.bt.posExp.LogOffset(now)) - nb.Add(int64(neg), n.bt.negExp.LogOffset(now)) - n.balance.pos = pb - n.balance.neg = nb - callbacks = n.checkCallbacks(now) - setPriority = n.checkPriorityStatus() - n.storeBalance(true, true) - return true - }) - for _, cb := range callbacks { - cb() +// Note: this function should run inside a NodeStateMachine operation +func (n *nodeBalance) SetBalance(pos, neg uint64) error { + var ( + now = n.bt.clock.Now() + callbacks []func() + setPriority bool + ) + // Operation with holding the lock + n.bt.updateTotalBalance(n, func() bool { + n.updateBalance(now) + if err := n.balance.setValue(now, pos, neg); err != nil { + return false } + callbacks, setPriority = n.checkCallbacks(now), n.checkPriorityStatus() + n.storeBalance(true, true) + return true + }) + // Operation without holding the lock + for _, cb := range callbacks { + cb() + } + if n.setFlags { if setPriority { - n.bt.ns.SetStateSub(n.node, n.bt.PriorityFlag, nodestate.Flags{}, 0) + n.bt.ns.SetStateSub(n.node, n.bt.setup.priorityFlag, nodestate.Flags{}, 0) } + // Note: priority flag is automatically removed by the zero priority callback if necessary n.signalPriorityUpdate() - }) + } return nil } // RequestServed should be called after serving a request for the given peer -func (n *NodeBalance) RequestServed(cost uint64) uint64 { +func (n *nodeBalance) RequestServed(cost uint64) (newBalance uint64) { n.lock.Lock() - var callbacks []func() - defer func() { - n.lock.Unlock() - if callbacks != nil { - n.bt.ns.Operation(func() { - for _, cb := range callbacks { - cb() - } - }) - } - }() - now := n.bt.clock.Now() + var ( + check bool + fcost = float64(cost) + now = n.bt.clock.Now() + ) n.updateBalance(now) - fcost := float64(cost) - - posExp := n.bt.posExp.LogOffset(now) - var check bool if !n.balance.pos.IsZero() { - if n.posFactor.RequestFactor != 0 { - c := -int64(fcost * n.posFactor.RequestFactor) - cc := n.balance.pos.Add(c, posExp) - if c == cc { + posCost := -int64(fcost * n.posFactor.RequestFactor) + if posCost == 0 { + fcost = 0 + newBalance = n.balance.posValue(now) + } else { + var net int64 + _, newBalance, net, _ = n.balance.addValue(now, posCost, true, false) + if posCost == net { fcost = 0 } else { - fcost *= 1 - float64(cc)/float64(c) + fcost *= 1 - float64(net)/float64(posCost) } check = true - } else { - fcost = 0 } } - if fcost > 0 { - if n.negFactor.RequestFactor != 0 { - n.balance.neg.Add(int64(fcost*n.negFactor.RequestFactor), n.bt.negExp.LogOffset(now)) - check = true - } + if fcost > 0 && n.negFactor.RequestFactor != 0 { + n.balance.addValue(now, int64(fcost*n.negFactor.RequestFactor), false, false) + check = true } + n.sumReqCost += cost + + var callbacks []func() if check { callbacks = n.checkCallbacks(now) } - n.sumReqCost += cost - return n.balance.pos.Value(posExp) + n.lock.Unlock() + + if callbacks != nil { + n.bt.ns.Operation(func() { + for _, cb := range callbacks { + cb() + } + }) + } + return } -// Priority returns the actual priority based on the current balance -func (n *NodeBalance) Priority(now mclock.AbsTime, capacity uint64) int64 { +// priority returns the actual priority based on the current balance +func (n *nodeBalance) priority(capacity uint64) int64 { n.lock.Lock() defer n.lock.Unlock() + now := n.bt.clock.Now() n.updateBalance(now) - return n.balanceToPriority(n.balance, capacity) + return n.balanceToPriority(now, n.balance, capacity) } // EstMinPriority gives a lower estimate for the priority at a given time in the future. // An average request cost per time is assumed that is twice the average cost per time // in the current session. -// If update is true then a priority callback is added that turns UpdateFlag on and off +// If update is true then a priority callback is added that turns updateFlag on and off // in case the priority goes below the estimated minimum. -func (n *NodeBalance) EstMinPriority(at mclock.AbsTime, capacity uint64, update bool) int64 { - n.lock.Lock() - defer n.lock.Unlock() - - var avgReqCost float64 - dt := time.Duration(n.lastUpdate - n.initTime) - if dt > time.Second { - avgReqCost = float64(n.sumReqCost) * 2 / float64(dt) - } - pri := n.balanceToPriority(n.reducedBalance(at, capacity, avgReqCost), capacity) - if update { - n.addCallback(balanceCallbackUpdate, pri, n.signalPriorityUpdate) - } - return pri -} - -// PosBalanceMissing calculates the missing amount of positive balance in order to -// connect at targetCapacity, stay connected for the given amount of time and then -// still have a priority of targetPriority -func (n *NodeBalance) PosBalanceMissing(targetPriority int64, targetCapacity uint64, after time.Duration) uint64 { +func (n *nodeBalance) estimatePriority(capacity uint64, addBalance int64, future, bias time.Duration, update bool) int64 { n.lock.Lock() defer n.lock.Unlock() now := n.bt.clock.Now() - if targetPriority < 0 { - timePrice := n.negFactor.timePrice(targetCapacity) - timeCost := uint64(float64(after) * timePrice) - negBalance := n.balance.neg.Value(n.bt.negExp.LogOffset(now)) - if timeCost+negBalance < uint64(-targetPriority) { - return 0 - } - if uint64(-targetPriority) > negBalance && timePrice > 1e-100 { - if negTime := time.Duration(float64(uint64(-targetPriority)-negBalance) / timePrice); negTime < after { - after -= negTime - } else { - after = 0 - } + n.updateBalance(now) + + b := n.balance // copy the balance + if addBalance != 0 { + b.addValue(now, addBalance, true, true) + } + if future > 0 { + var avgReqCost float64 + dt := time.Duration(n.lastUpdate - n.initTime) + if dt > time.Second { + avgReqCost = float64(n.sumReqCost) * 2 / float64(dt) } - targetPriority = 0 + b = n.reducedBalance(b, now, future, capacity, avgReqCost) } - timePrice := n.posFactor.timePrice(targetCapacity) - posRequired := uint64(float64(targetPriority)*float64(targetCapacity)+float64(after)*timePrice) + 1 - if posRequired >= maxBalance { - return math.MaxUint64 // target not reachable + if bias > 0 { + b = n.reducedBalance(b, now+mclock.AbsTime(future), bias, capacity, 0) } - posBalance := n.balance.pos.Value(n.bt.posExp.LogOffset(now)) - if posRequired > posBalance { - return posRequired - posBalance + // Note: we subtract one from the estimated priority in order to ensure that biased + // estimates are always lower than actual priorities, even if the bias is very small. + // This ensures that two nodes will not ping-pong update signals forever if both of + // them have zero estimated priority drop in the projected future. + pri := n.balanceToPriority(now, b, capacity) - 1 + if update { + n.addCallback(balanceCallbackUpdate, pri, n.signalPriorityUpdate) } - return 0 + return pri } // SetPriceFactors sets the price factors. TimeFactor is the price of a nanosecond of // connection while RequestFactor is the price of a request cost unit. -func (n *NodeBalance) SetPriceFactors(posFactor, negFactor PriceFactors) { +func (n *nodeBalance) SetPriceFactors(posFactor, negFactor PriceFactors) { n.lock.Lock() now := n.bt.clock.Now() n.updateBalance(now) @@ -327,7 +388,7 @@ func (n *NodeBalance) SetPriceFactors(posFactor, negFactor PriceFactors) { } // GetPriceFactors returns the price factors -func (n *NodeBalance) GetPriceFactors() (posFactor, negFactor PriceFactors) { +func (n *nodeBalance) GetPriceFactors() (posFactor, negFactor PriceFactors) { n.lock.Lock() defer n.lock.Unlock() @@ -335,7 +396,7 @@ func (n *NodeBalance) GetPriceFactors() (posFactor, negFactor PriceFactors) { } // activate starts time/capacity cost deduction. -func (n *NodeBalance) activate() { +func (n *nodeBalance) activate() { n.bt.updateTotalBalance(n, func() bool { if n.active { return false @@ -347,7 +408,7 @@ func (n *NodeBalance) activate() { } // deactivate stops time/capacity cost deduction and saves the balances in the database -func (n *NodeBalance) deactivate() { +func (n *nodeBalance) deactivate() { n.bt.updateTotalBalance(n, func() bool { if !n.active { return false @@ -364,15 +425,15 @@ func (n *NodeBalance) deactivate() { } // updateBalance updates balance based on the time factor -func (n *NodeBalance) updateBalance(now mclock.AbsTime) { +func (n *nodeBalance) updateBalance(now mclock.AbsTime) { if n.active && now > n.lastUpdate { - n.balance = n.reducedBalance(now, n.capacity, 0) + n.balance = n.reducedBalance(n.balance, n.lastUpdate, time.Duration(now-n.lastUpdate), n.capacity, 0) n.lastUpdate = now } } // storeBalance stores the positive and/or negative balance of the node in the database -func (n *NodeBalance) storeBalance(pos, neg bool) { +func (n *nodeBalance) storeBalance(pos, neg bool) { if pos { n.bt.storeBalance(n.node.ID().Bytes(), false, n.balance.pos) } @@ -386,7 +447,7 @@ func (n *NodeBalance) storeBalance(pos, neg bool) { // immediately. // Note: should be called while n.lock is held // Note 2: the callback function runs inside a NodeStateMachine operation -func (n *NodeBalance) addCallback(id int, threshold int64, callback func()) { +func (n *nodeBalance) addCallback(id int, threshold int64, callback func()) { n.removeCallback(id) idx := 0 for idx < n.callbackCount && threshold > n.callbacks[idx].threshold { @@ -406,7 +467,7 @@ func (n *NodeBalance) addCallback(id int, threshold int64, callback func()) { // removeCallback removes the given callback and returns true if it was active // Note: should be called while n.lock is held -func (n *NodeBalance) removeCallback(id int) bool { +func (n *nodeBalance) removeCallback(id int) bool { idx := n.callbackIndex[id] if idx == -1 { return false @@ -423,11 +484,11 @@ func (n *NodeBalance) removeCallback(id int) bool { // checkCallbacks checks whether the threshold of any of the active callbacks // have been reached and returns triggered callbacks. // Note: checkCallbacks assumes that the balance has been recently updated. -func (n *NodeBalance) checkCallbacks(now mclock.AbsTime) (callbacks []func()) { +func (n *nodeBalance) checkCallbacks(now mclock.AbsTime) (callbacks []func()) { if n.callbackCount == 0 || n.capacity == 0 { return } - pri := n.balanceToPriority(n.balance, n.capacity) + pri := n.balanceToPriority(now, n.balance, n.capacity) for n.callbackCount != 0 && n.callbacks[n.callbackCount-1].threshold >= pri { n.callbackCount-- n.callbackIndex[n.callbacks[n.callbackCount].id] = -1 @@ -439,7 +500,7 @@ func (n *NodeBalance) checkCallbacks(now mclock.AbsTime) (callbacks []func()) { // scheduleCheck sets up or updates a scheduled event to ensure that it will be called // again just after the next threshold has been reached. -func (n *NodeBalance) scheduleCheck(now mclock.AbsTime) { +func (n *nodeBalance) scheduleCheck(now mclock.AbsTime) { if n.callbackCount != 0 { d, ok := n.timeUntil(n.callbacks[n.callbackCount-1].threshold) if !ok { @@ -465,7 +526,7 @@ func (n *NodeBalance) scheduleCheck(now mclock.AbsTime) { } // updateAfter schedules a balance update and callback check in the future -func (n *NodeBalance) updateAfter(dt time.Duration) { +func (n *nodeBalance) updateAfter(dt time.Duration) { if n.updateEvent == nil || n.updateEvent.Stop() { if dt == 0 { n.updateEvent = nil @@ -493,20 +554,22 @@ func (n *NodeBalance) updateAfter(dt time.Duration) { // balanceExhausted should be called when the positive balance is exhausted (priority goes to zero/negative) // Note: this function should run inside a NodeStateMachine operation -func (n *NodeBalance) balanceExhausted() { +func (n *nodeBalance) balanceExhausted() { n.lock.Lock() n.storeBalance(true, false) - n.priority = false + n.hasPriority = false n.lock.Unlock() - n.bt.ns.SetStateSub(n.node, nodestate.Flags{}, n.bt.PriorityFlag, 0) + if n.setFlags { + n.bt.ns.SetStateSub(n.node, nodestate.Flags{}, n.bt.setup.priorityFlag, 0) + } } // checkPriorityStatus checks whether the node has gained priority status and sets the priority // callback and flag if necessary. It assumes that the balance has been recently updated. // Note that the priority flag has to be set by the caller after the mutex has been released. -func (n *NodeBalance) checkPriorityStatus() bool { - if !n.priority && !n.balance.pos.IsZero() { - n.priority = true +func (n *nodeBalance) checkPriorityStatus() bool { + if !n.hasPriority && !n.balance.pos.IsZero() { + n.hasPriority = true n.addCallback(balanceCallbackZero, 0, func() { n.balanceExhausted() }) return true } @@ -515,15 +578,15 @@ func (n *NodeBalance) checkPriorityStatus() bool { // signalPriorityUpdate signals that the priority fell below the previous minimum estimate // Note: this function should run inside a NodeStateMachine operation -func (n *NodeBalance) signalPriorityUpdate() { - n.bt.ns.SetStateSub(n.node, n.bt.UpdateFlag, nodestate.Flags{}, 0) - n.bt.ns.SetStateSub(n.node, nodestate.Flags{}, n.bt.UpdateFlag, 0) +func (n *nodeBalance) signalPriorityUpdate() { + n.bt.ns.SetStateSub(n.node, n.bt.setup.updateFlag, nodestate.Flags{}, 0) + n.bt.ns.SetStateSub(n.node, nodestate.Flags{}, n.bt.setup.updateFlag, 0) } // setCapacity updates the capacity value used for priority calculation // Note: capacity should never be zero // Note 2: this function should run inside a NodeStateMachine operation -func (n *NodeBalance) setCapacity(capacity uint64) { +func (n *nodeBalance) setCapacity(capacity uint64) { n.lock.Lock() now := n.bt.clock.Now() n.updateBalance(now) @@ -538,72 +601,89 @@ func (n *NodeBalance) setCapacity(capacity uint64) { // balanceToPriority converts a balance to a priority value. Lower priority means // first to disconnect. Positive balance translates to positive priority. If positive // balance is zero then negative balance translates to a negative priority. -func (n *NodeBalance) balanceToPriority(b balance, capacity uint64) int64 { - if !b.pos.IsZero() { - return int64(b.pos.Value(n.bt.posExp.LogOffset(n.bt.clock.Now())) / capacity) +func (n *nodeBalance) balanceToPriority(now mclock.AbsTime, b balance, capacity uint64) int64 { + pos := b.posValue(now) + if pos > 0 { + return int64(pos / capacity) + } + return -int64(b.negValue(now)) +} + +// priorityToBalance converts a target priority to a requested balance value. +// If the priority is negative, then minimal negative balance is returned; +// otherwise the minimal positive balance is returned. +func (n *nodeBalance) priorityToBalance(priority int64, capacity uint64) (uint64, uint64) { + if priority > 0 { + return uint64(priority) * n.capacity, 0 } - return -int64(b.neg.Value(n.bt.negExp.LogOffset(n.bt.clock.Now()))) + return 0, uint64(-priority) } // reducedBalance estimates the reduced balance at a given time in the fututre based -// on the current balance, the time factor and an estimated average request cost per time ratio -func (n *NodeBalance) reducedBalance(at mclock.AbsTime, capacity uint64, avgReqCost float64) balance { - dt := float64(at - n.lastUpdate) - b := n.balance +// on the given balance, the time factor and an estimated average request cost per time ratio +func (n *nodeBalance) reducedBalance(b balance, start mclock.AbsTime, dt time.Duration, capacity uint64, avgReqCost float64) balance { + // since the costs are applied continuously during the dt time period we calculate + // the expiration offset at the middle of the period + var ( + at = start + mclock.AbsTime(dt/2) + dtf = float64(dt) + ) if !b.pos.IsZero() { - factor := n.posFactor.timePrice(capacity) + n.posFactor.RequestFactor*avgReqCost - diff := -int64(dt * factor) - dd := b.pos.Add(diff, n.bt.posExp.LogOffset(at)) - if dd == diff { - dt = 0 + factor := n.posFactor.connectionPrice(capacity, avgReqCost) + diff := -int64(dtf * factor) + _, _, net, _ := b.addValue(at, diff, true, false) + if net == diff { + dtf = 0 } else { - dt += float64(dd) / factor + dtf += float64(net) / factor } } - if dt > 0 { - factor := n.negFactor.timePrice(capacity) + n.negFactor.RequestFactor*avgReqCost - b.neg.Add(int64(dt*factor), n.bt.negExp.LogOffset(at)) + if dtf > 0 { + factor := n.negFactor.connectionPrice(capacity, avgReqCost) + b.addValue(at, int64(dtf*factor), false, false) } return b } // timeUntil calculates the remaining time needed to reach a given priority level // assuming that no requests are processed until then. If the given level is never -// reached then (0, false) is returned. +// reached then (0, false) is returned. If it has already been reached then (0, true) +// is returned. // Note: the function assumes that the balance has been recently updated and // calculates the time starting from the last update. -func (n *NodeBalance) timeUntil(priority int64) (time.Duration, bool) { - now := n.bt.clock.Now() - var dt float64 - if !n.balance.pos.IsZero() { - posBalance := n.balance.pos.Value(n.bt.posExp.LogOffset(now)) - timePrice := n.posFactor.timePrice(n.capacity) +func (n *nodeBalance) timeUntil(priority int64) (time.Duration, bool) { + var ( + now = n.bt.clock.Now() + pos = n.balance.posValue(now) + targetPos, targetNeg = n.priorityToBalance(priority, n.capacity) + diffTime float64 + ) + if pos > 0 { + timePrice := n.posFactor.connectionPrice(n.capacity, 0) if timePrice < 1e-100 { return 0, false } - if priority > 0 { - newBalance := uint64(priority) * n.capacity - if newBalance > posBalance { - return 0, false + if targetPos > 0 { + if targetPos > pos { + return 0, true } - dt = float64(posBalance-newBalance) / timePrice - return time.Duration(dt), true + diffTime = float64(pos-targetPos) / timePrice + return time.Duration(diffTime), true } else { - dt = float64(posBalance) / timePrice + diffTime = float64(pos) / timePrice } } else { - if priority > 0 { - return 0, false + if targetPos > 0 { + return 0, true } } - // if we have a positive balance then dt equals the time needed to get it to zero - negBalance := n.balance.neg.Value(n.bt.negExp.LogOffset(now)) - timePrice := n.negFactor.timePrice(n.capacity) - if uint64(-priority) > negBalance { + neg := n.balance.negValue(now) + if targetNeg > neg { + timePrice := n.negFactor.connectionPrice(n.capacity, 0) if timePrice < 1e-100 { return 0, false } - dt += float64(uint64(-priority)-negBalance) / timePrice + diffTime += float64(targetNeg-neg) / timePrice } - return time.Duration(dt), true + return time.Duration(diffTime), true } diff --git a/les/lespay/server/balance_test.go b/les/vflux/server/balance_test.go similarity index 66% rename from les/lespay/server/balance_test.go rename to les/vflux/server/balance_test.go index 67e1944373..66f0d1f301 100644 --- a/les/lespay/server/balance_test.go +++ b/les/vflux/server/balance_test.go @@ -17,12 +17,14 @@ package server import ( + "math" "math/rand" "reflect" "testing" "time" "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/les/utils" "github.com/ethereum/go-ethereum/p2p/enode" @@ -30,57 +32,82 @@ import ( "github.com/ethereum/go-ethereum/p2p/nodestate" ) -var ( - testFlag = testSetup.NewFlag("testFlag") - connAddrFlag = testSetup.NewField("connAddr", reflect.TypeOf("")) - btTestSetup = NewBalanceTrackerSetup(testSetup) -) - -func init() { - btTestSetup.Connect(connAddrFlag, ppTestSetup.CapacityField) -} - type zeroExpirer struct{} func (z zeroExpirer) SetRate(now mclock.AbsTime, rate float64) {} func (z zeroExpirer) SetLogOffset(now mclock.AbsTime, logOffset utils.Fixed64) {} func (z zeroExpirer) LogOffset(now mclock.AbsTime) utils.Fixed64 { return 0 } +type balanceTestClient struct{} + +func (client balanceTestClient) FreeClientId() string { return "" } + type balanceTestSetup struct { clock *mclock.Simulated + db ethdb.KeyValueStore ns *nodestate.NodeStateMachine - bt *BalanceTracker + setup *serverSetup + bt *balanceTracker } -func newBalanceTestSetup() *balanceTestSetup { +func newBalanceTestSetup(db ethdb.KeyValueStore, posExp, negExp utils.ValueExpirer) *balanceTestSetup { + // Initialize and customize the setup for the balance testing clock := &mclock.Simulated{} - ns := nodestate.NewNodeStateMachine(nil, nil, clock, testSetup) - db := memorydb.New() - bt := NewBalanceTracker(ns, btTestSetup, db, clock, zeroExpirer{}, zeroExpirer{}) + setup := newServerSetup() + setup.clientField = setup.setup.NewField("balancTestClient", reflect.TypeOf(balanceTestClient{})) + + ns := nodestate.NewNodeStateMachine(nil, nil, clock, setup.setup) + if posExp == nil { + posExp = zeroExpirer{} + } + if negExp == nil { + negExp = zeroExpirer{} + } + if db == nil { + db = memorydb.New() + } + bt := newBalanceTracker(ns, setup, db, clock, posExp, negExp) ns.Start() return &balanceTestSetup{ clock: clock, + db: db, ns: ns, + setup: setup, bt: bt, } } -func (b *balanceTestSetup) newNode(capacity uint64) *NodeBalance { +func (b *balanceTestSetup) newNode(capacity uint64) *nodeBalance { node := enode.SignNull(&enr.Record{}, enode.ID{}) - b.ns.SetState(node, testFlag, nodestate.Flags{}, 0) - b.ns.SetField(node, btTestSetup.connAddressField, "") - b.ns.SetField(node, ppTestSetup.CapacityField, capacity) - n, _ := b.ns.GetField(node, btTestSetup.BalanceField).(*NodeBalance) + b.ns.SetField(node, b.setup.clientField, balanceTestClient{}) + if capacity != 0 { + b.ns.SetField(node, b.setup.capacityField, capacity) + } + n, _ := b.ns.GetField(node, b.setup.balanceField).(*nodeBalance) return n } +func (b *balanceTestSetup) setBalance(node *nodeBalance, pos, neg uint64) (err error) { + b.bt.BalanceOperation(node.node.ID(), node.connAddress, func(balance AtomicBalanceOperator) { + err = balance.SetBalance(pos, neg) + }) + return +} + +func (b *balanceTestSetup) addBalance(node *nodeBalance, add int64) (old, new uint64, err error) { + b.bt.BalanceOperation(node.node.ID(), node.connAddress, func(balance AtomicBalanceOperator) { + old, new, err = balance.AddBalance(add) + }) + return +} + func (b *balanceTestSetup) stop() { - b.bt.Stop() + b.bt.stop() b.ns.Stop() } func TestAddBalance(t *testing.T) { - b := newBalanceTestSetup() + b := newBalanceTestSetup(nil, nil, nil) defer b.stop() node := b.newNode(1000) @@ -97,7 +124,7 @@ func TestAddBalance(t *testing.T) { {maxBalance, [2]uint64{0, 0}, 0, true}, } for _, i := range inputs { - old, new, err := node.AddBalance(i.delta) + old, new, err := b.addBalance(node, i.delta) if i.expectErr { if err == nil { t.Fatalf("Expect get error but nil") @@ -116,7 +143,7 @@ func TestAddBalance(t *testing.T) { } func TestSetBalance(t *testing.T) { - b := newBalanceTestSetup() + b := newBalanceTestSetup(nil, nil, nil) defer b.stop() node := b.newNode(1000) @@ -127,9 +154,8 @@ func TestSetBalance(t *testing.T) { {0, 1000}, {1000, 1000}, } - for _, i := range inputs { - node.SetBalance(i.pos, i.neg) + b.setBalance(node, i.pos, i.neg) pos, neg := node.GetBalance() if pos != i.pos { t.Fatalf("Positive balance mismatch, want %v, got %v", i.pos, pos) @@ -141,13 +167,12 @@ func TestSetBalance(t *testing.T) { } func TestBalanceTimeCost(t *testing.T) { - b := newBalanceTestSetup() + b := newBalanceTestSetup(nil, nil, nil) defer b.stop() node := b.newNode(1000) - b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1)) node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1}) - node.SetBalance(uint64(time.Minute), 0) // 1 minute time allowance + b.setBalance(node, uint64(time.Minute), 0) // 1 minute time allowance var inputs = []struct { runTime time.Duration @@ -169,7 +194,7 @@ func TestBalanceTimeCost(t *testing.T) { } } - node.SetBalance(uint64(time.Minute), 0) // Refill 1 minute time allowance + b.setBalance(node, uint64(time.Minute), 0) // Refill 1 minute time allowance for _, i := range inputs { b.clock.Run(i.runTime) if pos, _ := node.GetBalance(); pos != i.expPos { @@ -182,13 +207,12 @@ func TestBalanceTimeCost(t *testing.T) { } func TestBalanceReqCost(t *testing.T) { - b := newBalanceTestSetup() + b := newBalanceTestSetup(nil, nil, nil) defer b.stop() node := b.newNode(1000) node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1}) - b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1)) - node.SetBalance(uint64(time.Minute), 0) // 1 minute time serving time allowance + b.setBalance(node, uint64(time.Minute), 0) // 1 minute time serving time allowance var inputs = []struct { reqCost uint64 expPos uint64 @@ -211,7 +235,7 @@ func TestBalanceReqCost(t *testing.T) { } func TestBalanceToPriority(t *testing.T) { - b := newBalanceTestSetup() + b := newBalanceTestSetup(nil, nil, nil) defer b.stop() node := b.newNode(1000) node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1}) @@ -227,22 +251,20 @@ func TestBalanceToPriority(t *testing.T) { {0, 1000, -1000}, } for _, i := range inputs { - node.SetBalance(i.pos, i.neg) - priority := node.Priority(b.clock.Now(), 1000) + b.setBalance(node, i.pos, i.neg) + priority := node.priority(1000) if priority != i.priority { - t.Fatalf("Priority mismatch, want %v, got %v", i.priority, priority) + t.Fatalf("priority mismatch, want %v, got %v", i.priority, priority) } } } func TestEstimatedPriority(t *testing.T) { - b := newBalanceTestSetup() + b := newBalanceTestSetup(nil, nil, nil) defer b.stop() node := b.newNode(1000000000) node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1}) - - b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1)) - node.SetBalance(uint64(time.Minute), 0) + b.setBalance(node, uint64(time.Minute), 0) var inputs = []struct { runTime time.Duration // time cost futureTime time.Duration // diff of future time @@ -269,47 +291,18 @@ func TestEstimatedPriority(t *testing.T) { for _, i := range inputs { b.clock.Run(i.runTime) node.RequestServed(i.reqCost) - priority := node.EstMinPriority(b.clock.Now()+mclock.AbsTime(i.futureTime), 1000000000, false) - if priority != i.priority { - t.Fatalf("Estimated priority mismatch, want %v, got %v", i.priority, priority) - } - } -} - -func TestPosBalanceMissing(t *testing.T) { - b := newBalanceTestSetup() - defer b.stop() - node := b.newNode(1000) - node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1}) - - b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1)) - var inputs = []struct { - pos, neg uint64 - priority int64 - cap uint64 - after time.Duration - expect uint64 - }{ - {uint64(time.Second * 2), 0, 0, 1, time.Second, 0}, - {uint64(time.Second * 2), 0, 0, 1, 2 * time.Second, 1}, - {uint64(time.Second * 2), 0, int64(time.Second), 1, 2 * time.Second, uint64(time.Second) + 1}, - {0, 0, int64(time.Second), 1, time.Second, uint64(2*time.Second) + 1}, - {0, 0, -int64(time.Second), 1, time.Second, 1}, - } - for _, i := range inputs { - node.SetBalance(i.pos, i.neg) - got := node.PosBalanceMissing(i.priority, i.cap, i.after) - if got != i.expect { - t.Fatalf("Missing budget mismatch, want %v, got %v", i.expect, got) + priority := node.estimatePriority(1000000000, 0, i.futureTime, 0, false) + if priority != i.priority-1 { + t.Fatalf("Estimated priority mismatch, want %v, got %v", i.priority-1, priority) } } } func TestPostiveBalanceCounting(t *testing.T) { - b := newBalanceTestSetup() + b := newBalanceTestSetup(nil, nil, nil) defer b.stop() - var nodes []*NodeBalance + var nodes []*nodeBalance for i := 0; i < 100; i += 1 { node := b.newNode(1000000) node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1}) @@ -320,7 +313,7 @@ func TestPostiveBalanceCounting(t *testing.T) { var sum uint64 for i := 0; i < 100; i += 1 { amount := int64(rand.Intn(100) + 100) - nodes[i].AddBalance(amount) + b.addBalance(nodes[i], amount) sum += uint64(amount) } if b.bt.TotalTokenAmount() != sum { @@ -330,7 +323,7 @@ func TestPostiveBalanceCounting(t *testing.T) { // Change client status for i := 0; i < 100; i += 1 { if rand.Intn(2) == 0 { - b.ns.SetField(nodes[i].node, ppTestSetup.CapacityField, uint64(1)) + b.ns.SetField(nodes[i].node, b.setup.capacityField, uint64(1)) } } if b.bt.TotalTokenAmount() != sum { @@ -338,7 +331,7 @@ func TestPostiveBalanceCounting(t *testing.T) { } for i := 0; i < 100; i += 1 { if rand.Intn(2) == 0 { - b.ns.SetField(nodes[i].node, ppTestSetup.CapacityField, uint64(1)) + b.ns.SetField(nodes[i].node, b.setup.capacityField, uint64(1)) } } if b.bt.TotalTokenAmount() != sum { @@ -347,7 +340,7 @@ func TestPostiveBalanceCounting(t *testing.T) { } func TestCallbackChecking(t *testing.T) { - b := newBalanceTestSetup() + b := newBalanceTestSetup(nil, nil, nil) defer b.stop() node := b.newNode(1000000) node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1}) @@ -360,7 +353,7 @@ func TestCallbackChecking(t *testing.T) { {0, time.Second}, {-int64(time.Second), 2 * time.Second}, } - node.SetBalance(uint64(time.Second), 0) + b.setBalance(node, uint64(time.Second), 0) for _, i := range inputs { diff, _ := node.timeUntil(i.priority) if diff != i.expDiff { @@ -370,14 +363,13 @@ func TestCallbackChecking(t *testing.T) { } func TestCallback(t *testing.T) { - b := newBalanceTestSetup() + b := newBalanceTestSetup(nil, nil, nil) defer b.stop() node := b.newNode(1000) node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1}) - b.ns.SetField(node.node, ppTestSetup.CapacityField, uint64(1)) callCh := make(chan struct{}, 1) - node.SetBalance(uint64(time.Minute), 0) + b.setBalance(node, uint64(time.Minute), 0) node.addCallback(balanceCallbackZero, 0, func() { callCh <- struct{}{} }) b.clock.Run(time.Minute) @@ -387,7 +379,7 @@ func TestCallback(t *testing.T) { t.Fatalf("Callback hasn't been called yet") } - node.SetBalance(uint64(time.Minute), 0) + b.setBalance(node, uint64(time.Minute), 0) node.addCallback(balanceCallbackZero, 0, func() { callCh <- struct{}{} }) node.removeCallback(balanceCallbackZero) @@ -398,3 +390,50 @@ func TestCallback(t *testing.T) { case <-time.NewTimer(time.Millisecond * 100).C: } } + +func TestBalancePersistence(t *testing.T) { + posExp := &utils.Expirer{} + negExp := &utils.Expirer{} + posExp.SetRate(0, math.Log(2)/float64(time.Hour*2)) // halves every two hours + negExp.SetRate(0, math.Log(2)/float64(time.Hour)) // halves every hour + setup := newBalanceTestSetup(nil, posExp, negExp) + + exp := func(balance *nodeBalance, expPos, expNeg uint64) { + pos, neg := balance.GetBalance() + if pos != expPos { + t.Fatalf("Positive balance incorrect, want %v, got %v", expPos, pos) + } + if neg != expNeg { + t.Fatalf("Positive balance incorrect, want %v, got %v", expPos, pos) + } + } + expTotal := func(expTotal uint64) { + total := setup.bt.TotalTokenAmount() + if total != expTotal { + t.Fatalf("Total token amount incorrect, want %v, got %v", expTotal, total) + } + } + + expTotal(0) + balance := setup.newNode(0) + expTotal(0) + setup.setBalance(balance, 16000000000, 16000000000) + exp(balance, 16000000000, 16000000000) + expTotal(16000000000) + + setup.clock.Run(time.Hour * 2) + exp(balance, 8000000000, 4000000000) + expTotal(8000000000) + setup.stop() + + // Test the functionalities after restart + setup = newBalanceTestSetup(setup.db, posExp, negExp) + expTotal(8000000000) + balance = setup.newNode(0) + exp(balance, 8000000000, 4000000000) + expTotal(8000000000) + setup.clock.Run(time.Hour * 2) + exp(balance, 4000000000, 1000000000) + expTotal(4000000000) + setup.stop() +} diff --git a/les/lespay/server/balance_tracker.go b/les/vflux/server/balance_tracker.go similarity index 59% rename from les/lespay/server/balance_tracker.go rename to les/vflux/server/balance_tracker.go index c1ea3c6496..746697a8c7 100644 --- a/les/lespay/server/balance_tracker.go +++ b/les/vflux/server/balance_tracker.go @@ -17,7 +17,6 @@ package server import ( - "reflect" "sync" "time" @@ -25,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/les/utils" "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/p2p/nodestate" ) @@ -34,78 +34,56 @@ const ( persistExpirationRefresh = time.Minute * 5 // refresh period of the token expiration persistence ) -// BalanceTrackerSetup contains node state flags and fields used by BalanceTracker -type BalanceTrackerSetup struct { - // controlled by PriorityPool - PriorityFlag, UpdateFlag nodestate.Flags - BalanceField nodestate.Field - // external connections - connAddressField, capacityField nodestate.Field -} - -// NewBalanceTrackerSetup creates a new BalanceTrackerSetup and initializes the fields -// and flags controlled by BalanceTracker -func NewBalanceTrackerSetup(setup *nodestate.Setup) BalanceTrackerSetup { - return BalanceTrackerSetup{ - // PriorityFlag is set if the node has a positive balance - PriorityFlag: setup.NewFlag("priorityNode"), - // UpdateFlag set and then immediately reset if the balance has been updated and - // therefore priority is suddenly changed - UpdateFlag: setup.NewFlag("balanceUpdate"), - // BalanceField contains the NodeBalance struct which implements nodePriority, - // allowing on-demand priority calculation and future priority estimation - BalanceField: setup.NewField("balance", reflect.TypeOf(&NodeBalance{})), - } -} - -// Connect sets the fields used by BalanceTracker as an input -func (bts *BalanceTrackerSetup) Connect(connAddressField, capacityField nodestate.Field) { - bts.connAddressField = connAddressField - bts.capacityField = capacityField -} - -// BalanceTracker tracks positive and negative balances for connected nodes. -// After connAddressField is set externally, a NodeBalance is created and previous +// balanceTracker tracks positive and negative balances for connected nodes. +// After clientField is set externally, a nodeBalance is created and previous // balance values are loaded from the database. Both balances are exponentially expired // values. Costs are deducted from the positive balance if present, otherwise added to // the negative balance. If the capacity is non-zero then a time cost is applied // continuously while individual request costs are applied immediately. // The two balances are translated into a single priority value that also depends // on the actual capacity. -type BalanceTracker struct { - BalanceTrackerSetup - clock mclock.Clock - lock sync.Mutex - ns *nodestate.NodeStateMachine - ndb *nodeDB - posExp, negExp utils.ValueExpirer - posExpTC, negExpTC uint64 +type balanceTracker struct { + setup *serverSetup + clock mclock.Clock + lock sync.Mutex + ns *nodestate.NodeStateMachine + ndb *nodeDB + posExp, negExp utils.ValueExpirer + + posExpTC, negExpTC uint64 + defaultPosFactors, defaultNegFactors PriceFactors active, inactive utils.ExpiredValue balanceTimer *utils.UpdateTimer quit chan struct{} } -// NewBalanceTracker creates a new BalanceTracker -func NewBalanceTracker(ns *nodestate.NodeStateMachine, setup BalanceTrackerSetup, db ethdb.KeyValueStore, clock mclock.Clock, posExp, negExp utils.ValueExpirer) *BalanceTracker { +// newBalanceTracker creates a new balanceTracker +func newBalanceTracker(ns *nodestate.NodeStateMachine, setup *serverSetup, db ethdb.KeyValueStore, clock mclock.Clock, posExp, negExp utils.ValueExpirer) *balanceTracker { ndb := newNodeDB(db, clock) - bt := &BalanceTracker{ - ns: ns, - BalanceTrackerSetup: setup, - ndb: ndb, - clock: clock, - posExp: posExp, - negExp: negExp, - balanceTimer: utils.NewUpdateTimer(clock, time.Second*10), - quit: make(chan struct{}), + bt := &balanceTracker{ + ns: ns, + setup: setup, + ndb: ndb, + clock: clock, + posExp: posExp, + negExp: negExp, + balanceTimer: utils.NewUpdateTimer(clock, time.Second*10), + quit: make(chan struct{}), } + posOffset, negOffset := bt.ndb.getExpiration() + posExp.SetLogOffset(clock.Now(), posOffset) + negExp.SetLogOffset(clock.Now(), negOffset) + + // Load all persisted balance entries of priority nodes, + // calculate the total number of issued service tokens. bt.ndb.forEachBalance(false, func(id enode.ID, balance utils.ExpiredValue) bool { bt.inactive.AddExp(balance) return true }) - ns.SubscribeField(bt.capacityField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) { - n, _ := ns.GetField(node, bt.BalanceField).(*NodeBalance) + ns.SubscribeField(bt.setup.capacityField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) { + n, _ := ns.GetField(node, bt.setup.balanceField).(*nodeBalance) if n == nil { return } @@ -122,15 +100,22 @@ func NewBalanceTracker(ns *nodestate.NodeStateMachine, setup BalanceTrackerSetup n.deactivate() } }) - ns.SubscribeField(bt.connAddressField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) { + ns.SubscribeField(bt.setup.clientField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) { + type peer interface { + FreeClientId() string + } if newValue != nil { - ns.SetFieldSub(node, bt.BalanceField, bt.newNodeBalance(node, newValue.(string))) + n := bt.newNodeBalance(node, newValue.(peer).FreeClientId(), true) + bt.lock.Lock() + n.SetPriceFactors(bt.defaultPosFactors, bt.defaultNegFactors) + bt.lock.Unlock() + ns.SetFieldSub(node, bt.setup.balanceField, n) } else { - ns.SetStateSub(node, nodestate.Flags{}, bt.PriorityFlag, 0) - if b, _ := ns.GetField(node, bt.BalanceField).(*NodeBalance); b != nil { + ns.SetStateSub(node, nodestate.Flags{}, bt.setup.priorityFlag, 0) + if b, _ := ns.GetField(node, bt.setup.balanceField).(*nodeBalance); b != nil { b.deactivate() } - ns.SetFieldSub(node, bt.BalanceField, nil) + ns.SetFieldSub(node, bt.setup.balanceField, nil) } }) @@ -153,31 +138,31 @@ func NewBalanceTracker(ns *nodestate.NodeStateMachine, setup BalanceTrackerSetup return bt } -// Stop saves expiration offset and unsaved node balances and shuts BalanceTracker down -func (bt *BalanceTracker) Stop() { +// Stop saves expiration offset and unsaved node balances and shuts balanceTracker down +func (bt *balanceTracker) stop() { now := bt.clock.Now() bt.ndb.setExpiration(bt.posExp.LogOffset(now), bt.negExp.LogOffset(now)) close(bt.quit) bt.ns.ForEach(nodestate.Flags{}, nodestate.Flags{}, func(node *enode.Node, state nodestate.Flags) { - if n, ok := bt.ns.GetField(node, bt.BalanceField).(*NodeBalance); ok { + if n, ok := bt.ns.GetField(node, bt.setup.balanceField).(*nodeBalance); ok { n.lock.Lock() n.storeBalance(true, true) n.lock.Unlock() - bt.ns.SetField(node, bt.BalanceField, nil) + bt.ns.SetField(node, bt.setup.balanceField, nil) } }) bt.ndb.close() } // TotalTokenAmount returns the current total amount of service tokens in existence -func (bt *BalanceTracker) TotalTokenAmount() uint64 { +func (bt *balanceTracker) TotalTokenAmount() uint64 { bt.lock.Lock() defer bt.lock.Unlock() bt.balanceTimer.Update(func(_ time.Duration) bool { bt.active = utils.ExpiredValue{} bt.ns.ForEach(nodestate.Flags{}, nodestate.Flags{}, func(node *enode.Node, state nodestate.Flags) { - if n, ok := bt.ns.GetField(node, bt.BalanceField).(*NodeBalance); ok { + if n, ok := bt.ns.GetField(node, bt.setup.balanceField).(*nodeBalance); ok && n.active { pos, _ := n.GetRawBalance() bt.active.AddExp(pos) } @@ -190,13 +175,21 @@ func (bt *BalanceTracker) TotalTokenAmount() uint64 { } // GetPosBalanceIDs lists node IDs with an associated positive balance -func (bt *BalanceTracker) GetPosBalanceIDs(start, stop enode.ID, maxCount int) (result []enode.ID) { +func (bt *balanceTracker) GetPosBalanceIDs(start, stop enode.ID, maxCount int) (result []enode.ID) { return bt.ndb.getPosBalanceIDs(start, stop, maxCount) } +// SetDefaultFactors sets the default price factors applied to subsequently connected clients +func (bt *balanceTracker) SetDefaultFactors(posFactors, negFactors PriceFactors) { + bt.lock.Lock() + bt.defaultPosFactors = posFactors + bt.defaultNegFactors = negFactors + bt.lock.Unlock() +} + // SetExpirationTCs sets positive and negative token expiration time constants. // Specified in seconds, 0 means infinite (no expiration). -func (bt *BalanceTracker) SetExpirationTCs(pos, neg uint64) { +func (bt *balanceTracker) SetExpirationTCs(pos, neg uint64) { bt.lock.Lock() defer bt.lock.Unlock() @@ -216,39 +209,55 @@ func (bt *BalanceTracker) SetExpirationTCs(pos, neg uint64) { // GetExpirationTCs returns the current positive and negative token expiration // time constants -func (bt *BalanceTracker) GetExpirationTCs() (pos, neg uint64) { +func (bt *balanceTracker) GetExpirationTCs() (pos, neg uint64) { bt.lock.Lock() defer bt.lock.Unlock() return bt.posExpTC, bt.negExpTC } -// newNodeBalance loads balances from the database and creates a NodeBalance instance -// for the given node. It also sets the PriorityFlag and adds balanceCallbackZero if +// BalanceOperation allows atomic operations on the balance of a node regardless of whether +// it is currently connected or not +func (bt *balanceTracker) BalanceOperation(id enode.ID, connAddress string, cb func(AtomicBalanceOperator)) { + bt.ns.Operation(func() { + var nb *nodeBalance + if node := bt.ns.GetNode(id); node != nil { + nb, _ = bt.ns.GetField(node, bt.setup.balanceField).(*nodeBalance) + } else { + node = enode.SignNull(&enr.Record{}, id) + nb = bt.newNodeBalance(node, connAddress, false) + } + cb(nb) + }) +} + +// newNodeBalance loads balances from the database and creates a nodeBalance instance +// for the given node. It also sets the priorityFlag and adds balanceCallbackZero if // the node has a positive balance. // Note: this function should run inside a NodeStateMachine operation -func (bt *BalanceTracker) newNodeBalance(node *enode.Node, negBalanceKey string) *NodeBalance { +func (bt *balanceTracker) newNodeBalance(node *enode.Node, connAddress string, setFlags bool) *nodeBalance { pb := bt.ndb.getOrNewBalance(node.ID().Bytes(), false) - nb := bt.ndb.getOrNewBalance([]byte(negBalanceKey), true) - n := &NodeBalance{ + nb := bt.ndb.getOrNewBalance([]byte(connAddress), true) + n := &nodeBalance{ bt: bt, node: node, - connAddress: negBalanceKey, - balance: balance{pos: pb, neg: nb}, + setFlags: setFlags, + connAddress: connAddress, + balance: balance{pos: pb, neg: nb, posExp: bt.posExp, negExp: bt.negExp}, initTime: bt.clock.Now(), lastUpdate: bt.clock.Now(), } for i := range n.callbackIndex { n.callbackIndex[i] = -1 } - if n.checkPriorityStatus() { - n.bt.ns.SetStateSub(n.node, n.bt.PriorityFlag, nodestate.Flags{}, 0) + if setFlags && n.checkPriorityStatus() { + n.bt.ns.SetStateSub(n.node, n.bt.setup.priorityFlag, nodestate.Flags{}, 0) } return n } // storeBalance stores either a positive or a negative balance in the database -func (bt *BalanceTracker) storeBalance(id []byte, neg bool, value utils.ExpiredValue) { +func (bt *balanceTracker) storeBalance(id []byte, neg bool, value utils.ExpiredValue) { if bt.canDropBalance(bt.clock.Now(), neg, value) { bt.ndb.delBalance(id, neg) // balance is small enough, drop it directly. } else { @@ -258,16 +267,15 @@ func (bt *BalanceTracker) storeBalance(id []byte, neg bool, value utils.ExpiredV // canDropBalance tells whether a positive or negative balance is below the threshold // and therefore can be dropped from the database -func (bt *BalanceTracker) canDropBalance(now mclock.AbsTime, neg bool, b utils.ExpiredValue) bool { +func (bt *balanceTracker) canDropBalance(now mclock.AbsTime, neg bool, b utils.ExpiredValue) bool { if neg { return b.Value(bt.negExp.LogOffset(now)) <= negThreshold - } else { - return b.Value(bt.posExp.LogOffset(now)) <= posThreshold } + return b.Value(bt.posExp.LogOffset(now)) <= posThreshold } // updateTotalBalance adjusts the total balance after executing given callback. -func (bt *BalanceTracker) updateTotalBalance(n *NodeBalance, callback func() bool) { +func (bt *balanceTracker) updateTotalBalance(n *nodeBalance, callback func() bool) { bt.lock.Lock() defer bt.lock.Unlock() diff --git a/les/lespay/server/clientdb.go b/les/vflux/server/clientdb.go similarity index 100% rename from les/lespay/server/clientdb.go rename to les/vflux/server/clientdb.go diff --git a/les/lespay/server/clientdb_test.go b/les/vflux/server/clientdb_test.go similarity index 100% rename from les/lespay/server/clientdb_test.go rename to les/vflux/server/clientdb_test.go diff --git a/les/vflux/server/clientpool.go b/les/vflux/server/clientpool.go new file mode 100644 index 0000000000..2e5fdd0ee7 --- /dev/null +++ b/les/vflux/server/clientpool.go @@ -0,0 +1,335 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package server + +import ( + "errors" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/les/utils" + "github.com/ethereum/go-ethereum/les/vflux" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/nodestate" + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + ErrNotConnected = errors.New("client not connected") + ErrNoPriority = errors.New("priority too low to raise capacity") + ErrCantFindMaximum = errors.New("Unable to find maximum allowed capacity") +) + +// ClientPool implements a client database that assigns a priority to each client +// based on a positive and negative balance. Positive balance is externally assigned +// to prioritized clients and is decreased with connection time and processed +// requests (unless the price factors are zero). If the positive balance is zero +// then negative balance is accumulated. +// +// Balance tracking and priority calculation for connected clients is done by +// balanceTracker. PriorityQueue ensures that clients with the lowest positive or +// highest negative balance get evicted when the total capacity allowance is full +// and new clients with a better balance want to connect. +// +// Already connected nodes receive a small bias in their favor in order to avoid +// accepting and instantly kicking out clients. In theory, we try to ensure that +// each client can have several minutes of connection time. +// +// Balances of disconnected clients are stored in nodeDB including positive balance +// and negative banalce. Boeth positive balance and negative balance will decrease +// exponentially. If the balance is low enough, then the record will be dropped. +type ClientPool struct { + *priorityPool + *balanceTracker + + setup *serverSetup + clock mclock.Clock + closed bool + ns *nodestate.NodeStateMachine + synced func() bool + + lock sync.RWMutex + connectedBias time.Duration + + minCap uint64 // the minimal capacity value allowed for any client + capReqNode *enode.Node // node that is requesting capacity change; only used inside NSM operation +} + +// clientPeer represents a peer in the client pool. None of the callbacks should block. +type clientPeer interface { + Node() *enode.Node + FreeClientId() string // unique id for non-priority clients (typically a prefix of the network address) + InactiveAllowance() time.Duration // disconnection timeout for inactive non-priority peers + UpdateCapacity(newCap uint64, requested bool) // signals a capacity update (requested is true if it is a result of a SetCapacity call on the given peer + Disconnect() // initiates disconnection (Unregister should always be called) +} + +// NewClientPool creates a new client pool +func NewClientPool(balanceDb ethdb.KeyValueStore, minCap uint64, connectedBias time.Duration, clock mclock.Clock, synced func() bool) *ClientPool { + setup := newServerSetup() + ns := nodestate.NewNodeStateMachine(nil, nil, clock, setup.setup) + cp := &ClientPool{ + priorityPool: newPriorityPool(ns, setup, clock, minCap, connectedBias, 4, 100), + balanceTracker: newBalanceTracker(ns, setup, balanceDb, clock, &utils.Expirer{}, &utils.Expirer{}), + setup: setup, + ns: ns, + clock: clock, + minCap: minCap, + connectedBias: connectedBias, + synced: synced, + } + + ns.SubscribeState(nodestate.MergeFlags(setup.activeFlag, setup.inactiveFlag, setup.priorityFlag), func(node *enode.Node, oldState, newState nodestate.Flags) { + if newState.Equals(setup.inactiveFlag) { + // set timeout for non-priority inactive client + var timeout time.Duration + if c, ok := ns.GetField(node, setup.clientField).(clientPeer); ok { + timeout = c.InactiveAllowance() + } + if timeout > 0 { + ns.AddTimeout(node, setup.inactiveFlag, timeout) + } else { + // Note: if capacity is immediately available then priorityPool will set the active + // flag simultaneously with removing the inactive flag and therefore this will not + // initiate disconnection + ns.SetStateSub(node, nodestate.Flags{}, setup.inactiveFlag, 0) + } + } + if oldState.Equals(setup.inactiveFlag) && newState.Equals(setup.inactiveFlag.Or(setup.priorityFlag)) { + ns.SetStateSub(node, setup.inactiveFlag, nodestate.Flags{}, 0) // priority gained; remove timeout + } + if newState.Equals(setup.activeFlag) { + // active with no priority; limit capacity to minCap + cap, _ := ns.GetField(node, setup.capacityField).(uint64) + if cap > minCap { + cp.requestCapacity(node, minCap, minCap, 0) + } + } + if newState.Equals(nodestate.Flags{}) { + if c, ok := ns.GetField(node, setup.clientField).(clientPeer); ok { + c.Disconnect() + } + } + }) + + ns.SubscribeField(setup.capacityField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) { + if c, ok := ns.GetField(node, setup.clientField).(clientPeer); ok { + newCap, _ := newValue.(uint64) + c.UpdateCapacity(newCap, node == cp.capReqNode) + } + }) + + // add metrics + cp.ns.SubscribeState(nodestate.MergeFlags(cp.setup.activeFlag, cp.setup.inactiveFlag), func(node *enode.Node, oldState, newState nodestate.Flags) { + if oldState.IsEmpty() && !newState.IsEmpty() { + clientConnectedMeter.Mark(1) + } + if !oldState.IsEmpty() && newState.IsEmpty() { + clientDisconnectedMeter.Mark(1) + } + if oldState.HasNone(cp.setup.activeFlag) && oldState.HasAll(cp.setup.activeFlag) { + clientActivatedMeter.Mark(1) + } + if oldState.HasAll(cp.setup.activeFlag) && oldState.HasNone(cp.setup.activeFlag) { + clientDeactivatedMeter.Mark(1) + } + _, connected := cp.Active() + totalConnectedGauge.Update(int64(connected)) + }) + return cp +} + +// Start starts the client pool. Should be called before Register/Unregister. +func (cp *ClientPool) Start() { + cp.ns.Start() +} + +// Stop shuts the client pool down. The clientPeer interface callbacks will not be called +// after Stop. Register calls will return nil. +func (cp *ClientPool) Stop() { + cp.balanceTracker.stop() + cp.ns.Stop() +} + +// Register registers the peer into the client pool. If the peer has insufficient +// priority and remains inactive for longer than the allowed timeout then it will be +// disconnected by calling the Disconnect function of the clientPeer interface. +func (cp *ClientPool) Register(peer clientPeer) ConnectedBalance { + cp.ns.SetField(peer.Node(), cp.setup.clientField, peerWrapper{peer}) + balance, _ := cp.ns.GetField(peer.Node(), cp.setup.balanceField).(*nodeBalance) + return balance +} + +// Unregister removes the peer from the client pool +func (cp *ClientPool) Unregister(peer clientPeer) { + cp.ns.SetField(peer.Node(), cp.setup.clientField, nil) +} + +// setConnectedBias sets the connection bias, which is applied to already connected clients +// So that already connected client won't be kicked out very soon and we can ensure all +// connected clients can have enough time to request or sync some data. +func (cp *ClientPool) SetConnectedBias(bias time.Duration) { + cp.lock.Lock() + cp.connectedBias = bias + cp.setActiveBias(bias) + cp.lock.Unlock() +} + +// SetCapacity sets the assigned capacity of a connected client +func (cp *ClientPool) SetCapacity(node *enode.Node, reqCap uint64, bias time.Duration, requested bool) (capacity uint64, err error) { + cp.lock.RLock() + if cp.connectedBias > bias { + bias = cp.connectedBias + } + cp.lock.RUnlock() + + cp.ns.Operation(func() { + balance, _ := cp.ns.GetField(node, cp.setup.balanceField).(*nodeBalance) + if balance == nil { + err = ErrNotConnected + return + } + capacity, _ = cp.ns.GetField(node, cp.setup.capacityField).(uint64) + if capacity == 0 { + // if the client is inactive then it has insufficient priority for the minimal capacity + // (will be activated automatically with minCap when possible) + return + } + if reqCap < cp.minCap { + // can't request less than minCap; switching between 0 (inactive state) and minCap is + // performed by the server automatically as soon as necessary/possible + reqCap = cp.minCap + } + if reqCap > cp.minCap && cp.ns.GetState(node).HasNone(cp.setup.priorityFlag) { + err = ErrNoPriority + return + } + if reqCap == capacity { + return + } + if requested { + // mark the requested node so that the UpdateCapacity callback can signal + // whether the update is the direct result of a SetCapacity call on the given node + cp.capReqNode = node + defer func() { + cp.capReqNode = nil + }() + } + + var minTarget, maxTarget uint64 + if reqCap > capacity { + // Estimate maximum available capacity at the current priority level and request + // the estimated amount. + // Note: requestCapacity could find the highest available capacity between the + // current and the requested capacity but it could cost a lot of iterations with + // fine step adjustment if the requested capacity is very high. By doing a quick + // estimation of the maximum available capacity based on the capacity curve we + // can limit the number of required iterations. + curve := cp.getCapacityCurve().exclude(node.ID()) + maxTarget = curve.maxCapacity(func(capacity uint64) int64 { + return balance.estimatePriority(capacity, 0, 0, bias, false) + }) + if maxTarget <= capacity { + return + } + if maxTarget > reqCap { + maxTarget = reqCap + } + // Specify a narrow target range that allows a limited number of fine step + // iterations + minTarget = maxTarget - maxTarget/20 + if minTarget < capacity { + minTarget = capacity + } + } else { + minTarget, maxTarget = reqCap, reqCap + } + if newCap := cp.requestCapacity(node, minTarget, maxTarget, bias); newCap >= minTarget && newCap <= maxTarget { + capacity = newCap + return + } + // we should be able to find the maximum allowed capacity in a few iterations + log.Error("Unable to find maximum allowed capacity") + err = ErrCantFindMaximum + }) + return +} + +// serveCapQuery serves a vflux capacity query. It receives multiple token amount values +// and a bias time value. For each given token amount it calculates the maximum achievable +// capacity in case the amount is added to the balance. +func (cp *ClientPool) serveCapQuery(id enode.ID, freeID string, data []byte) []byte { + var req vflux.CapacityQueryReq + if rlp.DecodeBytes(data, &req) != nil { + return nil + } + if l := len(req.AddTokens); l == 0 || l > vflux.CapacityQueryMaxLen { + return nil + } + result := make(vflux.CapacityQueryReply, len(req.AddTokens)) + if !cp.synced() { + capacityQueryZeroMeter.Mark(1) + reply, _ := rlp.EncodeToBytes(&result) + return reply + } + + bias := time.Second * time.Duration(req.Bias) + cp.lock.RLock() + if cp.connectedBias > bias { + bias = cp.connectedBias + } + cp.lock.RUnlock() + + // use capacityCurve to answer request for multiple newly bought token amounts + curve := cp.getCapacityCurve().exclude(id) + cp.BalanceOperation(id, freeID, func(balance AtomicBalanceOperator) { + pb, _ := balance.GetBalance() + for i, addTokens := range req.AddTokens { + add := addTokens.Int64() + result[i] = curve.maxCapacity(func(capacity uint64) int64 { + return balance.estimatePriority(capacity, add, 0, bias, false) / int64(capacity) + }) + if add <= 0 && uint64(-add) >= pb && result[i] > cp.minCap { + result[i] = cp.minCap + } + if result[i] < cp.minCap { + result[i] = 0 + } + } + }) + // add first result to metrics (don't care about priority client multi-queries yet) + if result[0] == 0 { + capacityQueryZeroMeter.Mark(1) + } else { + capacityQueryNonZeroMeter.Mark(1) + } + reply, _ := rlp.EncodeToBytes(&result) + return reply +} + +// Handle implements Service +func (cp *ClientPool) Handle(id enode.ID, address string, name string, data []byte) []byte { + switch name { + case vflux.CapacityQueryName: + return cp.serveCapQuery(id, address, data) + default: + return nil + } +} diff --git a/les/clientpool_test.go b/les/vflux/server/clientpool_test.go similarity index 61% rename from les/clientpool_test.go rename to les/vflux/server/clientpool_test.go index b1c38d374c..9503121697 100644 --- a/les/clientpool_test.go +++ b/les/vflux/server/clientpool_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package les +package server import ( "fmt" @@ -24,12 +24,13 @@ import ( "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/core/rawdb" - lps "github.com/ethereum/go-ethereum/les/lespay/server" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/p2p/nodestate" ) +const defaultConnectedBias = time.Minute * 3 + func TestClientPoolL10C100Free(t *testing.T) { testClientPool(t, 10, 100, 0, true) } @@ -64,11 +65,6 @@ type poolTestPeer struct { inactiveAllowed bool } -func testStateMachine() *nodestate.NodeStateMachine { - return nodestate.NewNodeStateMachine(nil, nil, mclock.System{}, serverSetup) - -} - func newPoolTestPeer(i int, disconnCh chan int) *poolTestPeer { return &poolTestPeer{ index: i, @@ -81,36 +77,39 @@ func (i *poolTestPeer) Node() *enode.Node { return i.node } -func (i *poolTestPeer) freeClientId() string { +func (i *poolTestPeer) FreeClientId() string { return fmt.Sprintf("addr #%d", i.index) } -func (i *poolTestPeer) updateCapacity(cap uint64) { - i.cap = cap +func (i *poolTestPeer) InactiveAllowance() time.Duration { + if i.inactiveAllowed { + return time.Second * 10 + } + return 0 } -func (i *poolTestPeer) freeze() {} - -func (i *poolTestPeer) allowInactive() bool { - return i.inactiveAllowed +func (i *poolTestPeer) UpdateCapacity(capacity uint64, requested bool) { + i.cap = capacity } -func getBalance(pool *clientPool, p *poolTestPeer) (pos, neg uint64) { - temp := pool.ns.GetField(p.node, clientInfoField) == nil - if temp { - pool.ns.SetField(p.node, connAddressField, p.freeClientId()) - } - n, _ := pool.ns.GetField(p.node, pool.BalanceField).(*lps.NodeBalance) - pos, neg = n.GetBalance() - if temp { - pool.ns.SetField(p.node, connAddressField, nil) +func (i *poolTestPeer) Disconnect() { + if i.disconnCh == nil { + return } + id := i.node.ID() + i.disconnCh <- int(id[0]) + int(id[1])<<8 +} + +func getBalance(pool *ClientPool, p *poolTestPeer) (pos, neg uint64) { + pool.BalanceOperation(p.node.ID(), p.FreeClientId(), func(nb AtomicBalanceOperator) { + pos, neg = nb.GetBalance() + }) return } -func addBalance(pool *clientPool, id enode.ID, amount int64) { - pool.forClients([]enode.ID{id}, func(c *clientInfo) { - c.balance.AddBalance(amount) +func addBalance(pool *ClientPool, id enode.ID, amount int64) { + pool.BalanceOperation(id, "", func(nb AtomicBalanceOperator) { + nb.AddBalance(amount) }) } @@ -122,6 +121,19 @@ func checkDiff(a, b uint64) bool { return a > b+maxDiff || b > a+maxDiff } +func connect(pool *ClientPool, peer *poolTestPeer) uint64 { + pool.Register(peer) + return peer.cap +} + +func disconnect(pool *ClientPool, peer *poolTestPeer) { + pool.Unregister(peer) +} + +func alwaysTrueFn() bool { + return true +} + func testClientPool(t *testing.T, activeLimit, clientCount, paidCount int, randomDisconnect bool) { rand.Seed(time.Now().UnixNano()) var ( @@ -130,19 +142,17 @@ func testClientPool(t *testing.T, activeLimit, clientCount, paidCount int, rando connected = make([]bool, clientCount) connTicks = make([]int, clientCount) disconnCh = make(chan int, clientCount) - disconnFn = func(id enode.ID) { - disconnCh <- int(id[0]) + int(id[1])<<8 - } - pool = newClientPool(testStateMachine(), db, 1, 0, &clock, disconnFn) + pool = NewClientPool(db, 1, 0, &clock, alwaysTrueFn) ) - pool.ns.Start() + pool.Start() + pool.SetExpirationTCs(0, 1000) - pool.setLimits(activeLimit, uint64(activeLimit)) - pool.setDefaultFactors(lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) + pool.SetLimits(uint64(activeLimit), uint64(activeLimit)) + pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) // pool should accept new peers up to its connected limit for i := 0; i < activeLimit; i++ { - if cap, _ := pool.connect(newPoolTestPeer(i, disconnCh)); cap != 0 { + if cap := connect(pool, newPoolTestPeer(i, disconnCh)); cap != 0 { connected[i] = true } else { t.Fatalf("Test peer #%d rejected", i) @@ -163,23 +173,23 @@ func testClientPool(t *testing.T, activeLimit, clientCount, paidCount int, rando i := rand.Intn(clientCount) if connected[i] { if randomDisconnect { - pool.disconnect(newPoolTestPeer(i, disconnCh)) + disconnect(pool, newPoolTestPeer(i, disconnCh)) connected[i] = false connTicks[i] += tickCounter } } else { - if cap, _ := pool.connect(newPoolTestPeer(i, disconnCh)); cap != 0 { + if cap := connect(pool, newPoolTestPeer(i, disconnCh)); cap != 0 { connected[i] = true connTicks[i] -= tickCounter } else { - pool.disconnect(newPoolTestPeer(i, disconnCh)) + disconnect(pool, newPoolTestPeer(i, disconnCh)) } } pollDisconnects: for { select { case i := <-disconnCh: - pool.disconnect(newPoolTestPeer(i, disconnCh)) + disconnect(pool, newPoolTestPeer(i, disconnCh)) if connected[i] { connTicks[i] += tickCounter connected[i] = false @@ -211,18 +221,18 @@ func testClientPool(t *testing.T, activeLimit, clientCount, paidCount int, rando t.Errorf("Total connected time of test node #%d (%d) outside expected range (%d to %d)", i, connTicks[i], min, max) } } - pool.stop() + pool.Stop() } -func testPriorityConnect(t *testing.T, pool *clientPool, p *poolTestPeer, cap uint64, expSuccess bool) { - if cap, _ := pool.connect(p); cap == 0 { +func testPriorityConnect(t *testing.T, pool *ClientPool, p *poolTestPeer, cap uint64, expSuccess bool) { + if cap := connect(pool, p); cap == 0 { if expSuccess { t.Fatalf("Failed to connect paid client") } else { return } } - if _, err := pool.setCapacity(p.node, "", cap, defaultConnectedBias, true); err != nil { + if newCap, _ := pool.SetCapacity(p.node, cap, defaultConnectedBias, true); newCap != cap { if expSuccess { t.Fatalf("Failed to raise capacity of paid client") } else { @@ -239,11 +249,11 @@ func TestConnectPaidClient(t *testing.T) { clock mclock.Simulated db = rawdb.NewMemoryDatabase() ) - pool := newClientPool(testStateMachine(), db, 1, defaultConnectedBias, &clock, func(id enode.ID) {}) - pool.ns.Start() - defer pool.stop() - pool.setLimits(10, uint64(10)) - pool.setDefaultFactors(lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) + pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn) + pool.Start() + defer pool.Stop() + pool.SetLimits(10, uint64(10)) + pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) // Add balance for an external client and mark it as paid client addBalance(pool, newPoolTestPeer(0, nil).node.ID(), int64(time.Minute)) @@ -255,16 +265,16 @@ func TestConnectPaidClientToSmallPool(t *testing.T) { clock mclock.Simulated db = rawdb.NewMemoryDatabase() ) - pool := newClientPool(testStateMachine(), db, 1, defaultConnectedBias, &clock, func(id enode.ID) {}) - pool.ns.Start() - defer pool.stop() - pool.setLimits(10, uint64(10)) // Total capacity limit is 10 - pool.setDefaultFactors(lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) + pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn) + pool.Start() + defer pool.Stop() + pool.SetLimits(10, uint64(10)) // Total capacity limit is 10 + pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) // Add balance for an external client and mark it as paid client addBalance(pool, newPoolTestPeer(0, nil).node.ID(), int64(time.Minute)) - // Connect a fat paid client to pool, should reject it. + // connect a fat paid client to pool, should reject it. testPriorityConnect(t, pool, newPoolTestPeer(0, nil), 100, false) } @@ -273,24 +283,23 @@ func TestConnectPaidClientToFullPool(t *testing.T) { clock mclock.Simulated db = rawdb.NewMemoryDatabase() ) - removeFn := func(enode.ID) {} // Noop - pool := newClientPool(testStateMachine(), db, 1, defaultConnectedBias, &clock, removeFn) - pool.ns.Start() - defer pool.stop() - pool.setLimits(10, uint64(10)) // Total capacity limit is 10 - pool.setDefaultFactors(lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) + pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn) + pool.Start() + defer pool.Stop() + pool.SetLimits(10, uint64(10)) // Total capacity limit is 10 + pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) for i := 0; i < 10; i++ { addBalance(pool, newPoolTestPeer(i, nil).node.ID(), int64(time.Second*20)) - pool.connect(newPoolTestPeer(i, nil)) + connect(pool, newPoolTestPeer(i, nil)) } addBalance(pool, newPoolTestPeer(11, nil).node.ID(), int64(time.Second*2)) // Add low balance to new paid client - if cap, _ := pool.connect(newPoolTestPeer(11, nil)); cap != 0 { + if cap := connect(pool, newPoolTestPeer(11, nil)); cap != 0 { t.Fatalf("Low balance paid client should be rejected") } clock.Run(time.Second) addBalance(pool, newPoolTestPeer(12, nil).node.ID(), int64(time.Minute*5)) // Add high balance to new paid client - if cap, _ := pool.connect(newPoolTestPeer(12, nil)); cap == 0 { + if cap := connect(pool, newPoolTestPeer(12, nil)); cap == 0 { t.Fatalf("High balance paid client should be accepted") } } @@ -301,23 +310,20 @@ func TestPaidClientKickedOut(t *testing.T) { db = rawdb.NewMemoryDatabase() kickedCh = make(chan int, 100) ) - removeFn := func(id enode.ID) { - kickedCh <- int(id[0]) - } - pool := newClientPool(testStateMachine(), db, 1, defaultConnectedBias, &clock, removeFn) - pool.ns.Start() - pool.bt.SetExpirationTCs(0, 0) - defer pool.stop() - pool.setLimits(10, uint64(10)) // Total capacity limit is 10 - pool.setDefaultFactors(lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) + pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn) + pool.Start() + pool.SetExpirationTCs(0, 0) + defer pool.Stop() + pool.SetLimits(10, uint64(10)) // Total capacity limit is 10 + pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) for i := 0; i < 10; i++ { addBalance(pool, newPoolTestPeer(i, kickedCh).node.ID(), 10000000000) // 10 second allowance - pool.connect(newPoolTestPeer(i, kickedCh)) + connect(pool, newPoolTestPeer(i, kickedCh)) clock.Run(time.Millisecond) } clock.Run(defaultConnectedBias + time.Second*11) - if cap, _ := pool.connect(newPoolTestPeer(11, kickedCh)); cap == 0 { + if cap := connect(pool, newPoolTestPeer(11, kickedCh)); cap == 0 { t.Fatalf("Free client should be accepted") } select { @@ -335,12 +341,12 @@ func TestConnectFreeClient(t *testing.T) { clock mclock.Simulated db = rawdb.NewMemoryDatabase() ) - pool := newClientPool(testStateMachine(), db, 1, defaultConnectedBias, &clock, func(id enode.ID) {}) - pool.ns.Start() - defer pool.stop() - pool.setLimits(10, uint64(10)) - pool.setDefaultFactors(lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) - if cap, _ := pool.connect(newPoolTestPeer(0, nil)); cap == 0 { + pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn) + pool.Start() + defer pool.Stop() + pool.SetLimits(10, uint64(10)) + pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) + if cap := connect(pool, newPoolTestPeer(0, nil)); cap == 0 { t.Fatalf("Failed to connect free client") } testPriorityConnect(t, pool, newPoolTestPeer(0, nil), 2, false) @@ -351,26 +357,25 @@ func TestConnectFreeClientToFullPool(t *testing.T) { clock mclock.Simulated db = rawdb.NewMemoryDatabase() ) - removeFn := func(enode.ID) {} // Noop - pool := newClientPool(testStateMachine(), db, 1, defaultConnectedBias, &clock, removeFn) - pool.ns.Start() - defer pool.stop() - pool.setLimits(10, uint64(10)) // Total capacity limit is 10 - pool.setDefaultFactors(lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) + pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn) + pool.Start() + defer pool.Stop() + pool.SetLimits(10, uint64(10)) // Total capacity limit is 10 + pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) for i := 0; i < 10; i++ { - pool.connect(newPoolTestPeer(i, nil)) + connect(pool, newPoolTestPeer(i, nil)) } - if cap, _ := pool.connect(newPoolTestPeer(11, nil)); cap != 0 { + if cap := connect(pool, newPoolTestPeer(11, nil)); cap != 0 { t.Fatalf("New free client should be rejected") } clock.Run(time.Minute) - if cap, _ := pool.connect(newPoolTestPeer(12, nil)); cap != 0 { + if cap := connect(pool, newPoolTestPeer(12, nil)); cap != 0 { t.Fatalf("New free client should be rejected") } clock.Run(time.Millisecond) clock.Run(4 * time.Minute) - if cap, _ := pool.connect(newPoolTestPeer(13, nil)); cap == 0 { + if cap := connect(pool, newPoolTestPeer(13, nil)); cap == 0 { t.Fatalf("Old client connects more than 5min should be kicked") } } @@ -381,18 +386,17 @@ func TestFreeClientKickedOut(t *testing.T) { db = rawdb.NewMemoryDatabase() kicked = make(chan int, 100) ) - removeFn := func(id enode.ID) { kicked <- int(id[0]) } - pool := newClientPool(testStateMachine(), db, 1, defaultConnectedBias, &clock, removeFn) - pool.ns.Start() - defer pool.stop() - pool.setLimits(10, uint64(10)) // Total capacity limit is 10 - pool.setDefaultFactors(lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) + pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn) + pool.Start() + defer pool.Stop() + pool.SetLimits(10, uint64(10)) // Total capacity limit is 10 + pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) for i := 0; i < 10; i++ { - pool.connect(newPoolTestPeer(i, kicked)) + connect(pool, newPoolTestPeer(i, kicked)) clock.Run(time.Millisecond) } - if cap, _ := pool.connect(newPoolTestPeer(10, kicked)); cap != 0 { + if cap := connect(pool, newPoolTestPeer(10, kicked)); cap != 0 { t.Fatalf("New free client should be rejected") } select { @@ -400,10 +404,10 @@ func TestFreeClientKickedOut(t *testing.T) { case <-time.NewTimer(time.Second).C: t.Fatalf("timeout") } - pool.disconnect(newPoolTestPeer(10, kicked)) + disconnect(pool, newPoolTestPeer(10, kicked)) clock.Run(5 * time.Minute) for i := 0; i < 10; i++ { - pool.connect(newPoolTestPeer(i+10, kicked)) + connect(pool, newPoolTestPeer(i+10, kicked)) } for i := 0; i < 10; i++ { select { @@ -423,18 +427,17 @@ func TestPositiveBalanceCalculation(t *testing.T) { db = rawdb.NewMemoryDatabase() kicked = make(chan int, 10) ) - removeFn := func(id enode.ID) { kicked <- int(id[0]) } // Noop - pool := newClientPool(testStateMachine(), db, 1, defaultConnectedBias, &clock, removeFn) - pool.ns.Start() - defer pool.stop() - pool.setLimits(10, uint64(10)) // Total capacity limit is 10 - pool.setDefaultFactors(lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) + pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn) + pool.Start() + defer pool.Stop() + pool.SetLimits(10, uint64(10)) // Total capacity limit is 10 + pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) addBalance(pool, newPoolTestPeer(0, kicked).node.ID(), int64(time.Minute*3)) testPriorityConnect(t, pool, newPoolTestPeer(0, kicked), 10, true) clock.Run(time.Minute) - pool.disconnect(newPoolTestPeer(0, kicked)) + disconnect(pool, newPoolTestPeer(0, kicked)) pb, _ := getBalance(pool, newPoolTestPeer(0, kicked)) if checkDiff(pb, uint64(time.Minute*2)) { t.Fatalf("Positive balance mismatch, want %v, got %v", uint64(time.Minute*2), pb) @@ -447,12 +450,11 @@ func TestDowngradePriorityClient(t *testing.T) { db = rawdb.NewMemoryDatabase() kicked = make(chan int, 10) ) - removeFn := func(id enode.ID) { kicked <- int(id[0]) } // Noop - pool := newClientPool(testStateMachine(), db, 1, defaultConnectedBias, &clock, removeFn) - pool.ns.Start() - defer pool.stop() - pool.setLimits(10, uint64(10)) // Total capacity limit is 10 - pool.setDefaultFactors(lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) + pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn) + pool.Start() + defer pool.Stop() + pool.SetLimits(10, uint64(10)) // Total capacity limit is 10 + pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}) p := newPoolTestPeer(0, kicked) addBalance(pool, p.node.ID(), int64(time.Minute)) @@ -483,33 +485,36 @@ func TestNegativeBalanceCalculation(t *testing.T) { clock mclock.Simulated db = rawdb.NewMemoryDatabase() ) - pool := newClientPool(testStateMachine(), db, 1, defaultConnectedBias, &clock, func(id enode.ID) {}) - pool.ns.Start() - defer pool.stop() - pool.setLimits(10, uint64(10)) // Total capacity limit is 10 - pool.setDefaultFactors(lps.PriceFactors{TimeFactor: 1e-3, CapacityFactor: 0, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 1e-3, CapacityFactor: 0, RequestFactor: 1}) + pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn) + pool.Start() + defer pool.Stop() + pool.SetExpirationTCs(0, 3600) + pool.SetLimits(10, uint64(10)) // Total capacity limit is 10 + pool.SetDefaultFactors(PriceFactors{TimeFactor: 1e-3, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1e-3, CapacityFactor: 0, RequestFactor: 1}) for i := 0; i < 10; i++ { - pool.connect(newPoolTestPeer(i, nil)) + connect(pool, newPoolTestPeer(i, nil)) } clock.Run(time.Second) for i := 0; i < 10; i++ { - pool.disconnect(newPoolTestPeer(i, nil)) + disconnect(pool, newPoolTestPeer(i, nil)) _, nb := getBalance(pool, newPoolTestPeer(i, nil)) if nb != 0 { t.Fatalf("Short connection shouldn't be recorded") } } for i := 0; i < 10; i++ { - pool.connect(newPoolTestPeer(i, nil)) + connect(pool, newPoolTestPeer(i, nil)) } clock.Run(time.Minute) for i := 0; i < 10; i++ { - pool.disconnect(newPoolTestPeer(i, nil)) + disconnect(pool, newPoolTestPeer(i, nil)) _, nb := getBalance(pool, newPoolTestPeer(i, nil)) - if checkDiff(nb, uint64(time.Minute)/1000) { - t.Fatalf("Negative balance mismatch, want %v, got %v", uint64(time.Minute)/1000, nb) + exp := uint64(time.Minute) / 1000 + exp -= exp / 120 // correct for negative balance expiration + if checkDiff(nb, exp) { + t.Fatalf("Negative balance mismatch, want %v, got %v", exp, nb) } } } @@ -519,10 +524,10 @@ func TestInactiveClient(t *testing.T) { clock mclock.Simulated db = rawdb.NewMemoryDatabase() ) - pool := newClientPool(testStateMachine(), db, 1, defaultConnectedBias, &clock, func(id enode.ID) {}) - pool.ns.Start() - defer pool.stop() - pool.setLimits(2, uint64(2)) + pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn) + pool.Start() + defer pool.Stop() + pool.SetLimits(2, uint64(2)) p1 := newPoolTestPeer(1, nil) p1.inactiveAllowed = true @@ -533,15 +538,15 @@ func TestInactiveClient(t *testing.T) { addBalance(pool, p1.node.ID(), 1000*int64(time.Second)) addBalance(pool, p3.node.ID(), 2000*int64(time.Second)) // p1: 1000 p2: 0 p3: 2000 - p1.cap, _ = pool.connect(p1) + p1.cap = connect(pool, p1) if p1.cap != 1 { t.Fatalf("Failed to connect peer #1") } - p2.cap, _ = pool.connect(p2) + p2.cap = connect(pool, p2) if p2.cap != 1 { t.Fatalf("Failed to connect peer #2") } - p3.cap, _ = pool.connect(p3) + p3.cap = connect(pool, p3) if p3.cap != 1 { t.Fatalf("Failed to connect peer #3") } @@ -564,11 +569,11 @@ func TestInactiveClient(t *testing.T) { if p2.cap != 0 { t.Fatalf("Failed to deactivate peer #2") } - pool.setDefaultFactors(lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 0}, lps.PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 0}) + pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 0}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 0}) p4 := newPoolTestPeer(4, nil) addBalance(pool, p4.node.ID(), 1500*int64(time.Second)) // p1: 1000 p2: 500 p3: 2000 p4: 1500 - p4.cap, _ = pool.connect(p4) + p4.cap = connect(pool, p4) if p4.cap != 1 { t.Fatalf("Failed to activate peer #4") } @@ -577,8 +582,8 @@ func TestInactiveClient(t *testing.T) { } clock.Run(time.Second * 600) // manually trigger a check to avoid a long real-time wait - pool.ns.SetState(p1.node, pool.UpdateFlag, nodestate.Flags{}, 0) - pool.ns.SetState(p1.node, nodestate.Flags{}, pool.UpdateFlag, 0) + pool.ns.SetState(p1.node, pool.setup.updateFlag, nodestate.Flags{}, 0) + pool.ns.SetState(p1.node, nodestate.Flags{}, pool.setup.updateFlag, 0) // p1: 1000 p2: 500 p3: 2000 p4: 900 if p1.cap != 1 { t.Fatalf("Failed to activate peer #1") @@ -586,8 +591,8 @@ func TestInactiveClient(t *testing.T) { if p4.cap != 0 { t.Fatalf("Failed to deactivate peer #4") } - pool.disconnect(p2) - pool.disconnect(p4) + disconnect(pool, p2) + disconnect(pool, p4) addBalance(pool, p1.node.ID(), -1000*int64(time.Second)) if p1.cap != 1 { t.Fatalf("Should not deactivate peer #1") diff --git a/les/vflux/server/metrics.go b/les/vflux/server/metrics.go new file mode 100644 index 0000000000..307b8347af --- /dev/null +++ b/les/vflux/server/metrics.go @@ -0,0 +1,33 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package server + +import ( + "github.com/ethereum/go-ethereum/metrics" +) + +var ( + totalConnectedGauge = metrics.NewRegisteredGauge("vflux/server/totalConnected", nil) + + clientConnectedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/connected", nil) + clientActivatedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/activated", nil) + clientDeactivatedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/deactivated", nil) + clientDisconnectedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/disconnected", nil) + + capacityQueryZeroMeter = metrics.NewRegisteredMeter("vflux/server/capQueryZero", nil) + capacityQueryNonZeroMeter = metrics.NewRegisteredMeter("vflux/server/capQueryNonZero", nil) +) diff --git a/les/vflux/server/prioritypool.go b/les/vflux/server/prioritypool.go new file mode 100644 index 0000000000..573a3570a4 --- /dev/null +++ b/les/vflux/server/prioritypool.go @@ -0,0 +1,658 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package server + +import ( + "math" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/common/prque" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/nodestate" +) + +const ( + lazyQueueRefresh = time.Second * 10 // refresh period of the active queue +) + +// priorityPool handles a set of nodes where each node has a capacity (a scalar value) +// and a priority (which can change over time and can also depend on the capacity). +// A node is active if it has at least the necessary minimal amount of capacity while +// inactive nodes have 0 capacity (values between 0 and the minimum are not allowed). +// The pool ensures that the number and total capacity of all active nodes are limited +// and the highest priority nodes are active at all times (limits can be changed +// during operation with immediate effect). +// +// When activating clients a priority bias is applied in favor of the already active +// nodes in order to avoid nodes quickly alternating between active and inactive states +// when their priorities are close to each other. The bias is specified in terms of +// duration (time) because priorities are expected to usually get lower over time and +// therefore a future minimum prediction (see EstMinPriority) should monotonously +// decrease with the specified time parameter. +// This time bias can be interpreted as minimum expected active time at the given +// capacity (if the threshold priority stays the same). +// +// Nodes in the pool always have either inactiveFlag or activeFlag set. A new node is +// added to the pool by externally setting inactiveFlag. priorityPool can switch a node +// between inactiveFlag and activeFlag at any time. Nodes can be removed from the pool +// by externally resetting both flags. activeFlag should not be set externally. +// +// The highest priority nodes in "inactive" state are moved to "active" state as soon as +// the minimum capacity can be granted for them. The capacity of lower priority active +// nodes is reduced or they are demoted to "inactive" state if their priority is +// insufficient even at minimal capacity. +type priorityPool struct { + setup *serverSetup + ns *nodestate.NodeStateMachine + clock mclock.Clock + lock sync.Mutex + inactiveQueue *prque.Prque + maxCount, maxCap uint64 + minCap uint64 + activeBias time.Duration + capacityStepDiv, fineStepDiv uint64 + + cachedCurve *capacityCurve + ccUpdatedAt mclock.AbsTime + ccUpdateForced bool + + tempState []*ppNodeInfo // nodes currently in temporary state + // the following fields represent the temporary state if tempState is not empty + activeCount, activeCap uint64 + activeQueue *prque.LazyQueue +} + +// ppNodeInfo is the internal node descriptor of priorityPool +type ppNodeInfo struct { + nodePriority nodePriority + node *enode.Node + connected bool + capacity uint64 // only changed when temporary state is committed + activeIndex, inactiveIndex int + + tempState bool // should only be true while the priorityPool lock is held + tempCapacity uint64 // equals capacity when tempState is false + // the following fields only affect the temporary state and they are set to their + // default value when entering the temp state + minTarget, stepDiv uint64 + bias time.Duration +} + +// newPriorityPool creates a new priorityPool +func newPriorityPool(ns *nodestate.NodeStateMachine, setup *serverSetup, clock mclock.Clock, minCap uint64, activeBias time.Duration, capacityStepDiv, fineStepDiv uint64) *priorityPool { + pp := &priorityPool{ + setup: setup, + ns: ns, + clock: clock, + inactiveQueue: prque.New(inactiveSetIndex), + minCap: minCap, + activeBias: activeBias, + capacityStepDiv: capacityStepDiv, + fineStepDiv: fineStepDiv, + } + if pp.activeBias < time.Duration(1) { + pp.activeBias = time.Duration(1) + } + pp.activeQueue = prque.NewLazyQueue(activeSetIndex, activePriority, pp.activeMaxPriority, clock, lazyQueueRefresh) + + ns.SubscribeField(pp.setup.balanceField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) { + if newValue != nil { + c := &ppNodeInfo{ + node: node, + nodePriority: newValue.(nodePriority), + activeIndex: -1, + inactiveIndex: -1, + } + ns.SetFieldSub(node, pp.setup.queueField, c) + ns.SetStateSub(node, setup.inactiveFlag, nodestate.Flags{}, 0) + } else { + ns.SetStateSub(node, nodestate.Flags{}, pp.setup.activeFlag.Or(pp.setup.inactiveFlag), 0) + if n, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo); n != nil { + pp.disconnectedNode(n) + } + ns.SetFieldSub(node, pp.setup.capacityField, nil) + ns.SetFieldSub(node, pp.setup.queueField, nil) + } + }) + ns.SubscribeState(pp.setup.activeFlag.Or(pp.setup.inactiveFlag), func(node *enode.Node, oldState, newState nodestate.Flags) { + if c, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo); c != nil { + if oldState.IsEmpty() { + pp.connectedNode(c) + } + if newState.IsEmpty() { + pp.disconnectedNode(c) + } + } + }) + ns.SubscribeState(pp.setup.updateFlag, func(node *enode.Node, oldState, newState nodestate.Flags) { + if !newState.IsEmpty() { + pp.updatePriority(node) + } + }) + return pp +} + +// requestCapacity tries to set the capacity of a connected node to the highest possible +// value inside the given target range. If maxTarget is not reachable then the capacity is +// iteratively reduced in fine steps based on the fineStepDiv parameter until minTarget is reached. +// The function returns the new capacity if successful and the original capacity otherwise. +// Note: this function should run inside a NodeStateMachine operation +func (pp *priorityPool) requestCapacity(node *enode.Node, minTarget, maxTarget uint64, bias time.Duration) uint64 { + pp.lock.Lock() + pp.activeQueue.Refresh() + var updates []capUpdate + defer func() { + pp.lock.Unlock() + pp.updateFlags(updates) + }() + + if minTarget < pp.minCap { + minTarget = pp.minCap + } + if maxTarget < minTarget { + maxTarget = minTarget + } + if bias < pp.activeBias { + bias = pp.activeBias + } + c, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo) + if c == nil { + log.Error("requestCapacity called for unknown node", "id", node.ID()) + return 0 + } + pp.setTempState(c) + if maxTarget > c.capacity { + c.bias = bias + c.stepDiv = pp.fineStepDiv + } + pp.setTempCapacity(c, maxTarget) + c.minTarget = minTarget + pp.activeQueue.Remove(c.activeIndex) + pp.inactiveQueue.Remove(c.inactiveIndex) + pp.activeQueue.Push(c) + pp.enforceLimits() + updates = pp.finalizeChanges(c.tempCapacity >= minTarget && c.tempCapacity <= maxTarget && c.tempCapacity != c.capacity) + return c.capacity +} + +// SetLimits sets the maximum number and total capacity of simultaneously active nodes +func (pp *priorityPool) SetLimits(maxCount, maxCap uint64) { + pp.lock.Lock() + pp.activeQueue.Refresh() + var updates []capUpdate + defer func() { + pp.lock.Unlock() + pp.ns.Operation(func() { pp.updateFlags(updates) }) + }() + + inc := (maxCount > pp.maxCount) || (maxCap > pp.maxCap) + dec := (maxCount < pp.maxCount) || (maxCap < pp.maxCap) + pp.maxCount, pp.maxCap = maxCount, maxCap + if dec { + pp.enforceLimits() + updates = pp.finalizeChanges(true) + } + if inc { + updates = append(updates, pp.tryActivate(false)...) + } +} + +// setActiveBias sets the bias applied when trying to activate inactive nodes +func (pp *priorityPool) setActiveBias(bias time.Duration) { + pp.lock.Lock() + pp.activeBias = bias + if pp.activeBias < time.Duration(1) { + pp.activeBias = time.Duration(1) + } + updates := pp.tryActivate(false) + pp.lock.Unlock() + pp.ns.Operation(func() { pp.updateFlags(updates) }) +} + +// Active returns the number and total capacity of currently active nodes +func (pp *priorityPool) Active() (uint64, uint64) { + pp.lock.Lock() + defer pp.lock.Unlock() + + return pp.activeCount, pp.activeCap +} + +// Limits returns the maximum allowed number and total capacity of active nodes +func (pp *priorityPool) Limits() (uint64, uint64) { + pp.lock.Lock() + defer pp.lock.Unlock() + + return pp.maxCount, pp.maxCap +} + +// inactiveSetIndex callback updates ppNodeInfo item index in inactiveQueue +func inactiveSetIndex(a interface{}, index int) { + a.(*ppNodeInfo).inactiveIndex = index +} + +// activeSetIndex callback updates ppNodeInfo item index in activeQueue +func activeSetIndex(a interface{}, index int) { + a.(*ppNodeInfo).activeIndex = index +} + +// invertPriority inverts a priority value. The active queue uses inverted priorities +// because the node on the top is the first to be deactivated. +func invertPriority(p int64) int64 { + if p == math.MinInt64 { + return math.MaxInt64 + } + return -p +} + +// activePriority callback returns actual priority of ppNodeInfo item in activeQueue +func activePriority(a interface{}) int64 { + c := a.(*ppNodeInfo) + if c.bias == 0 { + return invertPriority(c.nodePriority.priority(c.tempCapacity)) + } else { + return invertPriority(c.nodePriority.estimatePriority(c.tempCapacity, 0, 0, c.bias, true)) + } +} + +// activeMaxPriority callback returns estimated maximum priority of ppNodeInfo item in activeQueue +func (pp *priorityPool) activeMaxPriority(a interface{}, until mclock.AbsTime) int64 { + c := a.(*ppNodeInfo) + future := time.Duration(until - pp.clock.Now()) + if future < 0 { + future = 0 + } + return invertPriority(c.nodePriority.estimatePriority(c.tempCapacity, 0, future, c.bias, false)) +} + +// inactivePriority callback returns actual priority of ppNodeInfo item in inactiveQueue +func (pp *priorityPool) inactivePriority(p *ppNodeInfo) int64 { + return p.nodePriority.priority(pp.minCap) +} + +// connectedNode is called when a new node has been added to the pool (inactiveFlag set) +// Note: this function should run inside a NodeStateMachine operation +func (pp *priorityPool) connectedNode(c *ppNodeInfo) { + pp.lock.Lock() + pp.activeQueue.Refresh() + var updates []capUpdate + defer func() { + pp.lock.Unlock() + pp.updateFlags(updates) + }() + + if c.connected { + return + } + c.connected = true + pp.inactiveQueue.Push(c, pp.inactivePriority(c)) + updates = pp.tryActivate(false) +} + +// disconnectedNode is called when a node has been removed from the pool (both inactiveFlag +// and activeFlag reset) +// Note: this function should run inside a NodeStateMachine operation +func (pp *priorityPool) disconnectedNode(c *ppNodeInfo) { + pp.lock.Lock() + pp.activeQueue.Refresh() + var updates []capUpdate + defer func() { + pp.lock.Unlock() + pp.updateFlags(updates) + }() + + if !c.connected { + return + } + c.connected = false + pp.activeQueue.Remove(c.activeIndex) + pp.inactiveQueue.Remove(c.inactiveIndex) + if c.capacity != 0 { + pp.setTempState(c) + pp.setTempCapacity(c, 0) + updates = pp.tryActivate(true) + } +} + +// setTempState internally puts a node in a temporary state that can either be reverted +// or confirmed later. This temporary state allows changing the capacity of a node and +// moving it between the active and inactive queue. activeFlag/inactiveFlag and +// capacityField are not changed while the changes are still temporary. +func (pp *priorityPool) setTempState(c *ppNodeInfo) { + if c.tempState { + return + } + c.tempState = true + if c.tempCapacity != c.capacity { // should never happen + log.Error("tempCapacity != capacity when entering tempState") + } + c.minTarget = pp.minCap + c.stepDiv = pp.capacityStepDiv + pp.tempState = append(pp.tempState, c) +} + +// setTempCapacity changes the capacity of a node in the temporary state and adjusts +// activeCap and activeCount accordingly. Since this change is performed in the temporary +// state it should be called after setTempState and before finalizeChanges. +func (pp *priorityPool) setTempCapacity(n *ppNodeInfo, cap uint64) { + if !n.tempState { // should never happen + log.Error("Node is not in temporary state") + return + } + pp.activeCap += cap - n.tempCapacity + if n.tempCapacity == 0 { + pp.activeCount++ + } + if cap == 0 { + pp.activeCount-- + } + n.tempCapacity = cap +} + +// enforceLimits enforces active node count and total capacity limits. It returns the +// lowest active node priority. Note that this function is performed on the temporary +// internal state. +func (pp *priorityPool) enforceLimits() (*ppNodeInfo, int64) { + if pp.activeCap <= pp.maxCap && pp.activeCount <= pp.maxCount { + return nil, math.MinInt64 + } + var ( + c *ppNodeInfo + maxActivePriority int64 + ) + pp.activeQueue.MultiPop(func(data interface{}, priority int64) bool { + c = data.(*ppNodeInfo) + pp.setTempState(c) + maxActivePriority = priority + if c.tempCapacity == c.minTarget || pp.activeCount > pp.maxCount { + pp.setTempCapacity(c, 0) + } else { + sub := c.tempCapacity / c.stepDiv + if sub == 0 { + sub = 1 + } + if c.tempCapacity-sub < c.minTarget { + sub = c.tempCapacity - c.minTarget + } + pp.setTempCapacity(c, c.tempCapacity-sub) + pp.activeQueue.Push(c) + } + return pp.activeCap > pp.maxCap || pp.activeCount > pp.maxCount + }) + return c, invertPriority(maxActivePriority) +} + +// finalizeChanges either commits or reverts temporary changes. The necessary capacity +// field and according flag updates are not performed here but returned in a list because +// they should be performed while the mutex is not held. +func (pp *priorityPool) finalizeChanges(commit bool) (updates []capUpdate) { + for _, c := range pp.tempState { + // always remove and push back in order to update biased priority + pp.activeQueue.Remove(c.activeIndex) + pp.inactiveQueue.Remove(c.inactiveIndex) + oldCapacity := c.capacity + if commit { + c.capacity = c.tempCapacity + } else { + pp.setTempCapacity(c, c.capacity) // revert activeCount/activeCap + } + c.tempState = false + c.bias = 0 + c.stepDiv = pp.capacityStepDiv + c.minTarget = pp.minCap + if c.connected { + if c.capacity != 0 { + pp.activeQueue.Push(c) + } else { + pp.inactiveQueue.Push(c, pp.inactivePriority(c)) + } + if c.capacity != oldCapacity { + updates = append(updates, capUpdate{c.node, oldCapacity, c.capacity}) + } + } + } + pp.tempState = nil + if commit { + pp.ccUpdateForced = true + } + return +} + +// capUpdate describes a capacityField and activeFlag/inactiveFlag update +type capUpdate struct { + node *enode.Node + oldCap, newCap uint64 +} + +// updateFlags performs capacityField and activeFlag/inactiveFlag updates while the +// pool mutex is not held +// Note: this function should run inside a NodeStateMachine operation +func (pp *priorityPool) updateFlags(updates []capUpdate) { + for _, f := range updates { + if f.oldCap == 0 { + pp.ns.SetStateSub(f.node, pp.setup.activeFlag, pp.setup.inactiveFlag, 0) + } + if f.newCap == 0 { + pp.ns.SetStateSub(f.node, pp.setup.inactiveFlag, pp.setup.activeFlag, 0) + pp.ns.SetFieldSub(f.node, pp.setup.capacityField, nil) + } else { + pp.ns.SetFieldSub(f.node, pp.setup.capacityField, f.newCap) + } + } +} + +// tryActivate tries to activate inactive nodes if possible +func (pp *priorityPool) tryActivate(commit bool) []capUpdate { + for pp.inactiveQueue.Size() > 0 { + c := pp.inactiveQueue.PopItem().(*ppNodeInfo) + pp.setTempState(c) + pp.setTempCapacity(c, pp.minCap) + c.bias = pp.activeBias + pp.activeQueue.Push(c) + pp.enforceLimits() + if c.tempCapacity > 0 { + commit = true + c.bias = 0 + } else { + break + } + } + pp.ccUpdateForced = true + return pp.finalizeChanges(commit) +} + +// updatePriority gets the current priority value of the given node from the nodePriority +// interface and performs the necessary changes. It is triggered by updateFlag. +// Note: this function should run inside a NodeStateMachine operation +func (pp *priorityPool) updatePriority(node *enode.Node) { + pp.lock.Lock() + pp.activeQueue.Refresh() + var updates []capUpdate + defer func() { + pp.lock.Unlock() + pp.updateFlags(updates) + }() + + c, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo) + if c == nil || !c.connected { + return + } + pp.activeQueue.Remove(c.activeIndex) + pp.inactiveQueue.Remove(c.inactiveIndex) + if c.capacity != 0 { + pp.activeQueue.Push(c) + } else { + pp.inactiveQueue.Push(c, pp.inactivePriority(c)) + } + updates = pp.tryActivate(false) +} + +// capacityCurve is a snapshot of the priority pool contents in a format that can efficiently +// estimate how much capacity could be granted to a given node at a given priority level. +type capacityCurve struct { + points []curvePoint // curve points sorted in descending order of priority + index map[enode.ID][]int // curve point indexes belonging to each node + excludeList []int // curve point indexes of excluded node + excludeFirst bool // true if activeCount == maxCount +} + +type curvePoint struct { + freeCap uint64 // available capacity and node count at the current priority level + nextPri int64 // next priority level where more capacity will be available +} + +// getCapacityCurve returns a new or recently cached capacityCurve based on the contents of the pool +func (pp *priorityPool) getCapacityCurve() *capacityCurve { + pp.lock.Lock() + defer pp.lock.Unlock() + + now := pp.clock.Now() + dt := time.Duration(now - pp.ccUpdatedAt) + if !pp.ccUpdateForced && pp.cachedCurve != nil && dt < time.Second*10 { + return pp.cachedCurve + } + + pp.ccUpdateForced = false + pp.ccUpdatedAt = now + curve := &capacityCurve{ + index: make(map[enode.ID][]int), + } + pp.cachedCurve = curve + + var excludeID enode.ID + excludeFirst := pp.maxCount == pp.activeCount + // reduce node capacities or remove nodes until nothing is left in the queue; + // record the available capacity and the necessary priority after each step + lastPri := int64(math.MinInt64) + for pp.activeCap > 0 { + cp := curvePoint{} + if pp.activeCap > pp.maxCap { + log.Error("Active capacity is greater than allowed maximum", "active", pp.activeCap, "maximum", pp.maxCap) + } else { + cp.freeCap = pp.maxCap - pp.activeCap + } + // temporarily increase activeCap to enforce reducing or removing a node capacity + tempCap := cp.freeCap + 1 + pp.activeCap += tempCap + var next *ppNodeInfo + // enforceLimits removes the lowest priority node if it has minimal capacity, + // otherwise reduces its capacity + next, cp.nextPri = pp.enforceLimits() + if cp.nextPri < lastPri { + // enforce monotonicity which may be broken by continuously changing priorities + cp.nextPri = lastPri + } else { + lastPri = cp.nextPri + } + pp.activeCap -= tempCap + if next == nil { + log.Error("getCapacityCurve: cannot remove next element from the priority queue") + break + } + id := next.node.ID() + if excludeFirst { + // if the node count limit is already reached then mark the node with the + // lowest priority for exclusion + curve.excludeFirst = true + excludeID = id + excludeFirst = false + } + // multiple curve points and therefore multiple indexes may belong to a node + // if it was removed in multiple steps (if its capacity was more than the minimum) + curve.index[id] = append(curve.index[id], len(curve.points)) + curve.points = append(curve.points, cp) + } + // restore original state of the queue + pp.finalizeChanges(false) + curve.points = append(curve.points, curvePoint{ + freeCap: pp.maxCap, + nextPri: math.MaxInt64, + }) + if curve.excludeFirst { + curve.excludeList = curve.index[excludeID] + } + return curve +} + +// exclude returns a capacityCurve with the given node excluded from the original curve +func (cc *capacityCurve) exclude(id enode.ID) *capacityCurve { + if excludeList, ok := cc.index[id]; ok { + // return a new version of the curve (only one excluded node can be selected) + // Note: if the first node was excluded by default (excludeFirst == true) then + // we can forget about that and exclude the node with the given id instead. + return &capacityCurve{ + points: cc.points, + index: cc.index, + excludeList: excludeList, + } + } + return cc +} + +func (cc *capacityCurve) getPoint(i int) curvePoint { + cp := cc.points[i] + if i == 0 && cc.excludeFirst { + cp.freeCap = 0 + return cp + } + for ii := len(cc.excludeList) - 1; ii >= 0; ii-- { + ei := cc.excludeList[ii] + if ei < i { + break + } + e1, e2 := cc.points[ei], cc.points[ei+1] + cp.freeCap += e2.freeCap - e1.freeCap + } + return cp +} + +// maxCapacity calculates the maximum capacity available for a node with a given +// (monotonically decreasing) priority vs. capacity function. Note that if the requesting +// node is already in the pool then it should be excluded from the curve in order to get +// the correct result. +func (cc *capacityCurve) maxCapacity(priority func(cap uint64) int64) uint64 { + min, max := 0, len(cc.points)-1 // the curve always has at least one point + for min < max { + mid := (min + max) / 2 + cp := cc.getPoint(mid) + if cp.freeCap == 0 || priority(cp.freeCap) > cp.nextPri { + min = mid + 1 + } else { + max = mid + } + } + cp2 := cc.getPoint(min) + if cp2.freeCap == 0 || min == 0 { + return cp2.freeCap + } + cp1 := cc.getPoint(min - 1) + if priority(cp2.freeCap) > cp1.nextPri { + return cp2.freeCap + } + minc, maxc := cp1.freeCap, cp2.freeCap-1 + for minc < maxc { + midc := (minc + maxc + 1) / 2 + if midc == 0 || priority(midc) > cp1.nextPri { + minc = midc + } else { + maxc = midc - 1 + } + } + return maxc +} diff --git a/les/vflux/server/prioritypool_test.go b/les/vflux/server/prioritypool_test.go new file mode 100644 index 0000000000..5152312116 --- /dev/null +++ b/les/vflux/server/prioritypool_test.go @@ -0,0 +1,233 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package server + +import ( + "math/rand" + "reflect" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" + "github.com/ethereum/go-ethereum/p2p/nodestate" +) + +const ( + testCapacityStepDiv = 100 + testCapacityToleranceDiv = 10 + testMinCap = 100 +) + +type ppTestClient struct { + node *enode.Node + balance, cap uint64 +} + +func (c *ppTestClient) priority(cap uint64) int64 { + return int64(c.balance / cap) +} + +func (c *ppTestClient) estimatePriority(cap uint64, addBalance int64, future, bias time.Duration, update bool) int64 { + return int64(c.balance / cap) +} + +func TestPriorityPool(t *testing.T) { + clock := &mclock.Simulated{} + setup := newServerSetup() + setup.balanceField = setup.setup.NewField("ppTestClient", reflect.TypeOf(&ppTestClient{})) + ns := nodestate.NewNodeStateMachine(nil, nil, clock, setup.setup) + + ns.SubscribeField(setup.capacityField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) { + if n := ns.GetField(node, setup.balanceField); n != nil { + c := n.(*ppTestClient) + c.cap = newValue.(uint64) + } + }) + pp := newPriorityPool(ns, setup, clock, testMinCap, 0, testCapacityStepDiv, testCapacityStepDiv) + ns.Start() + pp.SetLimits(100, 1000000) + clients := make([]*ppTestClient, 100) + raise := func(c *ppTestClient) { + for { + var ok bool + ns.Operation(func() { + newCap := c.cap + c.cap/testCapacityStepDiv + ok = pp.requestCapacity(c.node, newCap, newCap, 0) == newCap + }) + if !ok { + return + } + } + } + var sumBalance uint64 + check := func(c *ppTestClient) { + expCap := 1000000 * c.balance / sumBalance + capTol := expCap / testCapacityToleranceDiv + if c.cap < expCap-capTol || c.cap > expCap+capTol { + t.Errorf("Wrong node capacity (expected %d, got %d)", expCap, c.cap) + } + } + + for i := range clients { + c := &ppTestClient{ + node: enode.SignNull(&enr.Record{}, enode.ID{byte(i)}), + balance: 100000000000, + cap: 1000, + } + sumBalance += c.balance + clients[i] = c + ns.SetField(c.node, setup.balanceField, c) + ns.SetState(c.node, setup.inactiveFlag, nodestate.Flags{}, 0) + raise(c) + check(c) + } + + for count := 0; count < 100; count++ { + c := clients[rand.Intn(len(clients))] + oldBalance := c.balance + c.balance = uint64(rand.Int63n(100000000000) + 100000000000) + sumBalance += c.balance - oldBalance + pp.ns.SetState(c.node, setup.updateFlag, nodestate.Flags{}, 0) + pp.ns.SetState(c.node, nodestate.Flags{}, setup.updateFlag, 0) + if c.balance > oldBalance { + raise(c) + } else { + for _, c := range clients { + raise(c) + } + } + // check whether capacities are proportional to balances + for _, c := range clients { + check(c) + } + if count%10 == 0 { + // test available capacity calculation with capacity curve + c = clients[rand.Intn(len(clients))] + curve := pp.getCapacityCurve().exclude(c.node.ID()) + + add := uint64(rand.Int63n(10000000000000)) + c.balance += add + sumBalance += add + expCap := curve.maxCapacity(func(cap uint64) int64 { + return int64(c.balance / cap) + }) + var ok bool + expFail := expCap + 10 + if expFail < testMinCap { + expFail = testMinCap + } + ns.Operation(func() { + ok = pp.requestCapacity(c.node, expFail, expFail, 0) == expFail + }) + if ok { + t.Errorf("Request for more than expected available capacity succeeded") + } + if expCap >= testMinCap { + ns.Operation(func() { + ok = pp.requestCapacity(c.node, expCap, expCap, 0) == expCap + }) + if !ok { + t.Errorf("Request for expected available capacity failed") + } + } + c.balance -= add + sumBalance -= add + pp.ns.SetState(c.node, setup.updateFlag, nodestate.Flags{}, 0) + pp.ns.SetState(c.node, nodestate.Flags{}, setup.updateFlag, 0) + for _, c := range clients { + raise(c) + } + } + } + + ns.Stop() +} + +func TestCapacityCurve(t *testing.T) { + clock := &mclock.Simulated{} + setup := newServerSetup() + setup.balanceField = setup.setup.NewField("ppTestClient", reflect.TypeOf(&ppTestClient{})) + ns := nodestate.NewNodeStateMachine(nil, nil, clock, setup.setup) + + pp := newPriorityPool(ns, setup, clock, 400000, 0, 2, 2) + ns.Start() + pp.SetLimits(10, 10000000) + clients := make([]*ppTestClient, 10) + + for i := range clients { + c := &ppTestClient{ + node: enode.SignNull(&enr.Record{}, enode.ID{byte(i)}), + balance: 100000000000 * uint64(i+1), + cap: 1000000, + } + clients[i] = c + ns.SetField(c.node, setup.balanceField, c) + ns.SetState(c.node, setup.inactiveFlag, nodestate.Flags{}, 0) + ns.Operation(func() { + pp.requestCapacity(c.node, c.cap, c.cap, 0) + }) + } + + curve := pp.getCapacityCurve() + check := func(balance, expCap uint64) { + cap := curve.maxCapacity(func(cap uint64) int64 { + return int64(balance / cap) + }) + var fail bool + if cap == 0 || expCap == 0 { + fail = cap != expCap + } else { + pri := balance / cap + expPri := balance / expCap + fail = pri != expPri && pri != expPri+1 + } + if fail { + t.Errorf("Incorrect capacity for %d balance (got %d, expected %d)", balance, cap, expCap) + } + } + + check(0, 0) + check(10000000000, 100000) + check(50000000000, 500000) + check(100000000000, 1000000) + check(200000000000, 1000000) + check(300000000000, 1500000) + check(450000000000, 1500000) + check(600000000000, 2000000) + check(800000000000, 2000000) + check(1000000000000, 2500000) + + pp.SetLimits(11, 10000000) + curve = pp.getCapacityCurve() + + check(0, 0) + check(10000000000, 100000) + check(50000000000, 500000) + check(150000000000, 750000) + check(200000000000, 1000000) + check(220000000000, 1100000) + check(275000000000, 1100000) + check(375000000000, 1500000) + check(450000000000, 1500000) + check(600000000000, 2000000) + check(800000000000, 2000000) + check(1000000000000, 2500000) + + ns.Stop() +} diff --git a/les/vflux/server/service.go b/les/vflux/server/service.go new file mode 100644 index 0000000000..80a0f47543 --- /dev/null +++ b/les/vflux/server/service.go @@ -0,0 +1,120 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package server + +import ( + "net" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/les/utils" + "github.com/ethereum/go-ethereum/les/vflux" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/rlp" +) + +type ( + // Server serves vflux requests + Server struct { + limiter *utils.Limiter + lock sync.Mutex + services map[string]*serviceEntry + delayPerRequest time.Duration + } + + // Service is a service registered at the Server and identified by a string id + Service interface { + Handle(id enode.ID, address string, name string, data []byte) []byte // never called concurrently + } + + serviceEntry struct { + id, desc string + backend Service + } +) + +// NewServer creates a new Server +func NewServer(delayPerRequest time.Duration) *Server { + return &Server{ + limiter: utils.NewLimiter(1000), + delayPerRequest: delayPerRequest, + services: make(map[string]*serviceEntry), + } +} + +// Register registers a Service +func (s *Server) Register(b Service, id, desc string) { + srv := &serviceEntry{backend: b, id: id, desc: desc} + if strings.Contains(srv.id, ":") { + // srv.id + ":" will be used as a service database prefix + log.Error("Service ID contains ':'", "id", srv.id) + return + } + s.lock.Lock() + s.services[srv.id] = srv + s.lock.Unlock() +} + +// Serve serves a vflux request batch +// Note: requests are served by the Handle functions of the registered services. Serve +// may be called concurrently but the Handle functions are called sequentially and +// therefore thread safety is guaranteed. +func (s *Server) Serve(id enode.ID, address string, requests vflux.Requests) vflux.Replies { + reqLen := uint(len(requests)) + if reqLen == 0 || reqLen > vflux.MaxRequestLength { + return nil + } + // Note: the value parameter will be supplied by the token sale module (total amount paid) + ch := <-s.limiter.Add(id, address, 0, reqLen) + if ch == nil { + return nil + } + // Note: the limiter ensures that the following section is not running concurrently, + // the lock only protects against contention caused by new service registration + s.lock.Lock() + results := make(vflux.Replies, len(requests)) + for i, req := range requests { + if service := s.services[req.Service]; service != nil { + results[i] = service.backend.Handle(id, address, req.Name, req.Params) + } + } + s.lock.Unlock() + time.Sleep(s.delayPerRequest * time.Duration(reqLen)) + close(ch) + return results +} + +// ServeEncoded serves an encoded vflux request batch and returns the encoded replies +func (s *Server) ServeEncoded(id enode.ID, addr *net.UDPAddr, req []byte) []byte { + var requests vflux.Requests + if err := rlp.DecodeBytes(req, &requests); err != nil { + return nil + } + results := s.Serve(id, addr.String(), requests) + if results == nil { + return nil + } + res, _ := rlp.EncodeToBytes(&results) + return res +} + +// Stop shuts down the server +func (s *Server) Stop() { + s.limiter.Stop() +} diff --git a/les/vflux/server/status.go b/les/vflux/server/status.go new file mode 100644 index 0000000000..469190777b --- /dev/null +++ b/les/vflux/server/status.go @@ -0,0 +1,59 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package server + +import ( + "reflect" + + "github.com/ethereum/go-ethereum/p2p/nodestate" +) + +type peerWrapper struct{ clientPeer } // the NodeStateMachine type system needs this wrapper + +// serverSetup is a wrapper of the node state machine setup, which contains +// all the created flags and fields used in the vflux server side. +type serverSetup struct { + setup *nodestate.Setup + clientField nodestate.Field // Field contains the client peer handler + + // Flags and fields controlled by balance tracker. BalanceTracker + // is responsible for setting/deleting these flags or fields. + priorityFlag nodestate.Flags // Flag is set if the node has a positive balance + updateFlag nodestate.Flags // Flag is set whenever the node balance is changed(priority changed) + balanceField nodestate.Field // Field contains the client balance for priority calculation + + // Flags and fields controlled by priority queue. Priority queue + // is responsible for setting/deleting these flags or fields. + activeFlag nodestate.Flags // Flag is set if the node is active + inactiveFlag nodestate.Flags // Flag is set if the node is inactive + capacityField nodestate.Field // Field contains the capacity of the node + queueField nodestate.Field // Field contains the infomration in the priority queue +} + +// newServerSetup initializes the setup for state machine and returns the flags/fields group. +func newServerSetup() *serverSetup { + setup := &serverSetup{setup: &nodestate.Setup{}} + setup.clientField = setup.setup.NewField("client", reflect.TypeOf(peerWrapper{})) + setup.priorityFlag = setup.setup.NewFlag("priority") + setup.updateFlag = setup.setup.NewFlag("update") + setup.balanceField = setup.setup.NewField("balance", reflect.TypeOf(&nodeBalance{})) + setup.activeFlag = setup.setup.NewFlag("active") + setup.inactiveFlag = setup.setup.NewFlag("inactive") + setup.capacityField = setup.setup.NewField("capacity", reflect.TypeOf(uint64(0))) + setup.queueField = setup.setup.NewField("queue", reflect.TypeOf(&ppNodeInfo{})) + return setup +} diff --git a/light/lightchain.go b/light/lightchain.go index 7e9b78fd79..c8d36e1eed 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -396,24 +396,26 @@ func (lc *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i lc.wg.Add(1) defer lc.wg.Done() - var events []interface{} - whFunc := func(header *types.Header) error { - status, err := lc.hc.WriteHeader(header) - - switch status { - case core.CanonStatTy: - log.Debug("Inserted new header", "number", header.Number, "hash", header.Hash()) - events = append(events, core.ChainEvent{Block: types.NewBlockWithHeader(header), Hash: header.Hash()}) - - case core.SideStatTy: - log.Debug("Inserted forked header", "number", header.Number, "hash", header.Hash()) - events = append(events, core.ChainSideEvent{Block: types.NewBlockWithHeader(header)}) - } - return err + status, err := lc.hc.InsertHeaderChain(chain, start) + if err != nil || len(chain) == 0 { + return 0, err + } + + // Create chain event for the new head block of this insertion. + var ( + events = make([]interface{}, 0, 1) + lastHeader = chain[len(chain)-1] + block = types.NewBlockWithHeader(lastHeader) + ) + switch status { + case core.CanonStatTy: + events = append(events, core.ChainEvent{Block: block, Hash: block.Hash()}) + case core.SideStatTy: + events = append(events, core.ChainSideEvent{Block: block}) } - i, err := lc.hc.InsertHeaderChain(chain, whFunc, start) lc.postChainEvents(events) - return i, err + + return 0, err } // CurrentHeader retrieves the current head header of the canonical chain. The diff --git a/light/lightchain_test.go b/light/lightchain_test.go index 300c716c54..ae5e6ee5a1 100644 --- a/light/lightchain_test.go +++ b/light/lightchain_test.go @@ -18,6 +18,7 @@ package light import ( "context" + "errors" "math/big" "testing" @@ -322,7 +323,7 @@ func TestBadHeaderHashes(t *testing.T) { var err error headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10) core.BadHashes[headers[2].Hash()] = true - if _, err = bc.InsertHeaderChain(headers, 1); err != core.ErrBlacklistedHash { + if _, err = bc.InsertHeaderChain(headers, 1); !errors.Is(err, core.ErrBlacklistedHash) { t.Errorf("error mismatch: have: %v, want %v", err, core.ErrBlacklistedHash) } } diff --git a/light/odr.go b/light/odr.go index 7016ef8ef2..9521dd53e8 100644 --- a/light/odr.go +++ b/light/odr.go @@ -42,6 +42,7 @@ type OdrBackend interface { BloomTrieIndexer() *core.ChainIndexer BloomIndexer() *core.ChainIndexer Retrieve(ctx context.Context, req OdrRequest) error + RetrieveTxStatus(ctx context.Context, req *TxStatusRequest) error IndexerConfig() *IndexerConfig } @@ -135,8 +136,6 @@ func (req *ReceiptsRequest) StoreResult(db ethdb.Database) { // ChtRequest is the ODR request type for retrieving header by Canonical Hash Trie type ChtRequest struct { - Untrusted bool // Indicator whether the result retrieved is trusted or not - PeerId string // The specified peer id from which to retrieve data. Config *IndexerConfig ChtNum, BlockNum uint64 ChtRoot common.Hash @@ -148,12 +147,9 @@ type ChtRequest struct { // StoreResult stores the retrieved data in local database func (req *ChtRequest) StoreResult(db ethdb.Database) { hash, num := req.Header.Hash(), req.Header.Number.Uint64() - - if !req.Untrusted { - rawdb.WriteHeader(db, req.Header) - rawdb.WriteTd(db, hash, num, req.Td) - rawdb.WriteCanonicalHash(db, hash, num) - } + rawdb.WriteHeader(db, req.Header) + rawdb.WriteTd(db, hash, num, req.Td) + rawdb.WriteCanonicalHash(db, hash, num) } // BloomRequest is the ODR request type for retrieving bloom filters from a CHT structure diff --git a/light/odr_test.go b/light/odr_test.go index 14493107f3..f36f561d84 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -196,9 +196,10 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain // Perform read-only call. st.SetBalance(testBankAddress, math.MaxBig256) - msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), data, false)} - context := core.NewEVMContext(msg, header, chain, nil) - vmenv := vm.NewEVM(context, st, config, vm.Config{}) + msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), data, nil, false)} + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(header, chain, nil) + vmenv := vm.NewEVM(context, txContext, st, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) result, _ := core.ApplyMessage(vmenv, msg, gp) res = append(res, result.Return()...) diff --git a/light/odr_util.go b/light/odr_util.go index aec0c7b69f..bbbcdbce21 100644 --- a/light/odr_util.go +++ b/light/odr_util.go @@ -19,20 +19,23 @@ package light import ( "bytes" "context" + "errors" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" ) -var sha3Nil = crypto.Keccak256Hash(nil) +// errNonCanonicalHash is returned if the requested chain data doesn't belong +// to the canonical chain. ODR can only retrieve the canonical chain data covered +// by the CHT or Bloom trie for verification. +var errNonCanonicalHash = errors.New("hash is not currently canonical") // GetHeaderByNumber retrieves the canonical block header corresponding to the -// given number. +// given number. The returned header is proven by local CHT. func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*types.Header, error) { // Try to find it in the local database first. db := odr.Database() @@ -63,25 +66,6 @@ func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*typ return r.Header, nil } -// GetUntrustedHeaderByNumber retrieves specified block header without -// correctness checking. Note this function should only be used in light -// client checkpoint syncing. -func GetUntrustedHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64, peerId string) (*types.Header, error) { - // todo(rjl493456442) it's a hack to retrieve headers which is not covered - // by CHT. Fix it in LES4 - r := &ChtRequest{ - BlockNum: number, - ChtNum: number / odr.IndexerConfig().ChtSize, - Untrusted: true, - PeerId: peerId, - Config: odr.IndexerConfig(), - } - if err := odr.Retrieve(ctx, r); err != nil { - return nil, err - } - return r.Header, nil -} - // GetCanonicalHash retrieves the canonical block hash corresponding to the number. func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) { hash := rawdb.ReadCanonicalHash(odr.Database(), number) @@ -102,10 +86,13 @@ func GetTd(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) if td != nil { return td, nil } - _, err := GetHeaderByNumber(ctx, odr, number) + header, err := GetHeaderByNumber(ctx, odr, number) if err != nil { return nil, err } + if header.Hash() != hash { + return nil, errNonCanonicalHash + } // -> td mapping already be stored in db, get it. return rawdb.ReadTd(odr.Database(), hash, number), nil } @@ -120,6 +107,9 @@ func GetBodyRLP(ctx context.Context, odr OdrBackend, hash common.Hash, number ui if err != nil { return nil, errNoHeader } + if header.Hash() != hash { + return nil, errNonCanonicalHash + } r := &BlockRequest{Hash: hash, Number: number, Header: header} if err := odr.Retrieve(ctx, r); err != nil { return nil, err @@ -167,6 +157,9 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num if err != nil { return nil, errNoHeader } + if header.Hash() != hash { + return nil, errNonCanonicalHash + } r := &ReceiptsRequest{Hash: hash, Number: number, Header: header} if err := odr.Retrieve(ctx, r); err != nil { return nil, err @@ -276,10 +269,15 @@ func GetBloomBits(ctx context.Context, odr OdrBackend, bit uint, sections []uint return result, nil } -// GetTransaction retrieves a canonical transaction by hash and also returns its position in the chain +// GetTransaction retrieves a canonical transaction by hash and also returns +// its position in the chain. There is no guarantee in the LES protocol that +// the mined transaction will be retrieved back for sure because of different +// reasons(the transaction is unindexed, the malicous server doesn't reply it +// deliberately, etc). Therefore, unretrieved transactions will receive a certain +// number of retrys, thus giving a weak guarantee. func GetTransaction(ctx context.Context, odr OdrBackend, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { r := &TxStatusRequest{Hashes: []common.Hash{txHash}} - if err := odr.Retrieve(ctx, r); err != nil || r.Status[0].Status != core.TxStatusIncluded { + if err := odr.RetrieveTxStatus(ctx, r); err != nil || r.Status[0].Status != core.TxStatusIncluded { return nil, common.Hash{}, 0, 0, err } pos := r.Status[0].Lookup diff --git a/light/postprocess.go b/light/postprocess.go index 08a3e60ac2..1f2af23bf5 100644 --- a/light/postprocess.go +++ b/light/postprocess.go @@ -147,7 +147,7 @@ func NewChtIndexer(db ethdb.Database, odr OdrBackend, size, confirms uint64, dis diskdb: db, odr: odr, trieTable: trieTable, - triedb: trie.NewDatabaseWithCache(trieTable, 1, ""), // Use a tiny cache only to keep memory down + triedb: trie.NewDatabaseWithConfig(trieTable, &trie.Config{Cache: 1}), // Use a tiny cache only to keep memory down trieset: mapset.NewSet(), sectionSize: size, disablePruning: disablePruning, @@ -340,7 +340,7 @@ func NewBloomTrieIndexer(db ethdb.Database, odr OdrBackend, parentSize, size uin diskdb: db, odr: odr, trieTable: trieTable, - triedb: trie.NewDatabaseWithCache(trieTable, 1, ""), // Use a tiny cache only to keep memory down + triedb: trie.NewDatabaseWithConfig(trieTable, &trie.Config{Cache: 1}), // Use a tiny cache only to keep memory down trieset: mapset.NewSet(), parentSize: parentSize, size: size, diff --git a/light/trie.go b/light/trie.go index 3eb05f4a3f..0516b94486 100644 --- a/light/trie.go +++ b/light/trie.go @@ -30,6 +30,10 @@ import ( "github.com/ethereum/go-ethereum/trie" ) +var ( + sha3Nil = crypto.Keccak256Hash(nil) +) + func NewState(ctx context.Context, head *types.Header, odr OdrBackend) *state.StateDB { state, _ := state.New(head.Root, NewStateDatabase(ctx, head, odr), nil) return state diff --git a/light/txpool.go b/light/txpool.go index d0c92834ee..d0891b9c16 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -32,7 +32,6 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params/types/ctypes" - "github.com/ethereum/go-ethereum/rlp" ) const ( @@ -70,6 +69,7 @@ type TxPool struct { eip2f bool eip2028f bool + eip2718 bool // Fork indicator whether we are in the eip2718 stage. } // TxRelayBackend provides an interface to the mechanism that forwards transacions @@ -91,7 +91,7 @@ type TxRelayBackend interface { func NewTxPool(config ctypes.ChainConfigurator, chain *LightChain, relay TxRelayBackend) *TxPool { pool := &TxPool{ config: config, - signer: types.NewEIP155Signer(config.GetChainID()), + signer: types.LatestSigner(config), nonce: make(map[common.Address]uint64), pending: make(map[common.Hash]*types.Transaction), mined: make(map[common.Hash][]*types.Transaction), @@ -332,7 +332,9 @@ func (pool *TxPool) setNewHead(head *types.Header) { // Update fork indicator by next pending block number next := new(big.Int).Add(head.Number, big.NewInt(1)) + pool.eip2028f = pool.config.IsEnabled(pool.config.GetEIP2028Transition, next) + pool.eip2718 = pool.config.IsEnabled(pool.config.GetEIP2718Transition, next) } // Stop stops the light transaction pool @@ -400,7 +402,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error } // Should supply enough intrinsic gas - gas, err := core.IntrinsicGas(tx.Data(), tx.To() == nil, pool.eip2f, pool.eip2028f) + gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, pool.eip2f, pool.eip2028f) if err != nil { return err } @@ -449,8 +451,7 @@ func (pool *TxPool) add(ctx context.Context, tx *types.Transaction) error { func (pool *TxPool) Add(ctx context.Context, tx *types.Transaction) error { pool.mu.Lock() defer pool.mu.Unlock() - - data, err := rlp.EncodeToBytes(tx) + data, err := tx.MarshalBinary() if err != nil { return err } diff --git a/light/txpool_test.go b/light/txpool_test.go index 45da1a0599..922f21a2eb 100644 --- a/light/txpool_test.go +++ b/light/txpool_test.go @@ -120,6 +120,8 @@ func TestTxPool(t *testing.T) { t.Errorf("relay.Send expected len = %d, got %d", exp, got) } } + + // core-geth if ii == len(gchain)/4 { // Fuck up pool head // This is an edge case that I'm not sure could really happen (hopefully not), @@ -128,18 +130,23 @@ func TestTxPool(t *testing.T) { pool.head = common.Hash{} } + // core-geth if ii == len(gchain)/2 { // Attempt to insert a nil header into the headerchain + // NOTE(ia) t.Log("Inserting nil header into header chain") if _, err := lightchain.InsertHeaderChain([]*types.Header{nil}, 1); err == nil { t.Fatal("insert nil header error should not be errorless") } } + + // core-geth if ii == len(gchain)/4*3 { var h *types.Header t.Log("Setting pool head to a nil header", h.Hash().Hex()) pool.setNewHead(h) } + if _, err := lightchain.InsertHeaderChain([]*types.Header{block.Header()}, 1); err != nil { t.Fatal(err) } diff --git a/metrics/config.go b/metrics/config.go new file mode 100644 index 0000000000..d05d664265 --- /dev/null +++ b/metrics/config.go @@ -0,0 +1,45 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of go-ethereum. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package metrics + +// Config contains the configuration for the metric collection. +type Config struct { + Enabled bool `toml:",omitempty"` + EnabledExpensive bool `toml:",omitempty"` + HTTP string `toml:",omitempty"` + Port int `toml:",omitempty"` + EnableInfluxDB bool `toml:",omitempty"` + InfluxDBEndpoint string `toml:",omitempty"` + InfluxDBDatabase string `toml:",omitempty"` + InfluxDBUsername string `toml:",omitempty"` + InfluxDBPassword string `toml:",omitempty"` + InfluxDBTags string `toml:",omitempty"` +} + +// DefaultConfig is the default config for metrics used in go-ethereum. +var DefaultConfig = Config{ + Enabled: false, + EnabledExpensive: false, + HTTP: "127.0.0.1", + Port: 6060, + EnableInfluxDB: false, + InfluxDBEndpoint: "http://localhost:8086", + InfluxDBDatabase: "geth", + InfluxDBUsername: "test", + InfluxDBPassword: "test", + InfluxDBTags: "host=localhost", +} diff --git a/metrics/cpu_enabled.go b/metrics/cpu_enabled.go index 52a3c2e966..02192928b7 100644 --- a/metrics/cpu_enabled.go +++ b/metrics/cpu_enabled.go @@ -31,6 +31,10 @@ func ReadCPUStats(stats *CPUStats) { log.Error("Could not read cpu stats", "err", err) return } + if len(timeStats) == 0 { + log.Error("Empty cpu stats") + return + } // requesting all cpu times will always return an array with only one time stats entry timeStat := timeStats[0] stats.GlobalTime = int64((timeStat.User + timeStat.Nice + timeStat.System) * cpu.ClocksPerSec) diff --git a/metrics/exp/exp.go b/metrics/exp/exp.go index f510b8381e..3ebe8cc68a 100644 --- a/metrics/exp/exp.go +++ b/metrics/exp/exp.go @@ -128,7 +128,7 @@ func (exp *exp) publishMeter(name string, metric metrics.Meter) { exp.getInt(name + ".count").Set(m.Count()) exp.getFloat(name + ".one-minute").Set(m.Rate1()) exp.getFloat(name + ".five-minute").Set(m.Rate5()) - exp.getFloat(name + ".fifteen-minute").Set((m.Rate15())) + exp.getFloat(name + ".fifteen-minute").Set(m.Rate15()) exp.getFloat(name + ".mean").Set(m.RateMean()) } diff --git a/metrics/gauge_float64_test.go b/metrics/gauge_float64_test.go index 3ee568e7ba..02b75580c4 100644 --- a/metrics/gauge_float64_test.go +++ b/metrics/gauge_float64_test.go @@ -12,27 +12,27 @@ func BenchmarkGuageFloat64(b *testing.B) { func TestGaugeFloat64(t *testing.T) { g := NewGaugeFloat64() - g.Update(float64(47.0)) - if v := g.Value(); float64(47.0) != v { + g.Update(47.0) + if v := g.Value(); 47.0 != v { t.Errorf("g.Value(): 47.0 != %v\n", v) } } func TestGaugeFloat64Snapshot(t *testing.T) { g := NewGaugeFloat64() - g.Update(float64(47.0)) + g.Update(47.0) snapshot := g.Snapshot() g.Update(float64(0)) - if v := snapshot.Value(); float64(47.0) != v { + if v := snapshot.Value(); 47.0 != v { t.Errorf("g.Value(): 47.0 != %v\n", v) } } func TestGetOrRegisterGaugeFloat64(t *testing.T) { r := NewRegistry() - NewRegisteredGaugeFloat64("foo", r).Update(float64(47.0)) + NewRegisteredGaugeFloat64("foo", r).Update(47.0) t.Logf("registry: %v", r) - if g := GetOrRegisterGaugeFloat64("foo", r); float64(47.0) != g.Value() { + if g := GetOrRegisterGaugeFloat64("foo", r); 47.0 != g.Value() { t.Fatal(g) } } diff --git a/metrics/histogram.go b/metrics/histogram.go index 46f3bbd2f1..2c54ce8b40 100644 --- a/metrics/histogram.go +++ b/metrics/histogram.go @@ -26,6 +26,15 @@ func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram { return r.GetOrRegister(name, func() Histogram { return NewHistogram(s) }).(Histogram) } +// GetOrRegisterHistogramLazy returns an existing Histogram or constructs and +// registers a new StandardHistogram. +func GetOrRegisterHistogramLazy(name string, r Registry, s func() Sample) Histogram { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, func() Histogram { return NewHistogram(s()) }).(Histogram) +} + // NewHistogram constructs a new StandardHistogram from a Sample. func NewHistogram(s Sample) Histogram { if !Enabled { diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index d4fc478e7b..52d0091034 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -162,26 +162,29 @@ func (r *reporter) send() error { }) case metrics.Histogram: ms := metric.Snapshot() - ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) - pts = append(pts, client.Point{ - Measurement: fmt.Sprintf("%s%s.histogram", namespace, name), - Tags: r.tags, - Fields: map[string]interface{}{ - "count": ms.Count(), - "max": ms.Max(), - "mean": ms.Mean(), - "min": ms.Min(), - "stddev": ms.StdDev(), - "variance": ms.Variance(), - "p50": ps[0], - "p75": ps[1], - "p95": ps[2], - "p99": ps[3], - "p999": ps[4], - "p9999": ps[5], - }, - Time: now, - }) + + if ms.Count() > 0 { + ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) + pts = append(pts, client.Point{ + Measurement: fmt.Sprintf("%s%s.histogram", namespace, name), + Tags: r.tags, + Fields: map[string]interface{}{ + "count": ms.Count(), + "max": ms.Max(), + "mean": ms.Mean(), + "min": ms.Min(), + "stddev": ms.StdDev(), + "variance": ms.Variance(), + "p50": ps[0], + "p75": ps[1], + "p95": ps[2], + "p99": ps[3], + "p999": ps[4], + "p9999": ps[5], + }, + Time: now, + }) + } case metrics.Meter: ms := metric.Snapshot() pts = append(pts, client.Point{ diff --git a/metrics/resetting_sample.go b/metrics/resetting_sample.go new file mode 100644 index 0000000000..43c1129cd0 --- /dev/null +++ b/metrics/resetting_sample.go @@ -0,0 +1,24 @@ +package metrics + +// ResettingSample converts an ordinary sample into one that resets whenever its +// snapshot is retrieved. This will break for multi-monitor systems, but when only +// a single metric is being pushed out, this ensure that low-frequency events don't +// skew th charts indefinitely. +func ResettingSample(sample Sample) Sample { + return &resettingSample{ + Sample: sample, + } +} + +// resettingSample is a simple wrapper around a sample that resets it upon the +// snapshot retrieval. +type resettingSample struct { + Sample +} + +// Snapshot returns a read-only copy of the sample with the original reset. +func (rs *resettingSample) Snapshot() Sample { + s := rs.Sample.Snapshot() + rs.Sample.Clear() + return s +} diff --git a/miner/miner.go b/miner/miner.go index ef431bfa0c..c1f58dcdc4 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -43,14 +43,15 @@ type Backend interface { // Config is the configuration parameters of mining. type Config struct { - Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards (default = first account) - Notify []string `toml:",omitempty"` // HTTP URL list to be notified of new work packages(only useful in ethash). - ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner - GasFloor uint64 // Target gas floor for mined blocks. - GasCeil uint64 // Target gas ceiling for mined blocks. - GasPrice *big.Int // Minimum gas price for mining a transaction - Recommit time.Duration // The time interval for miner to re-create mining work. - Noverify bool // Disable remote mining solution verification(only useful in ethash). + Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards (default = first account) + Notify []string `toml:",omitempty"` // HTTP URL list to be notified of new work packages (only useful in ethash). + NotifyFull bool `toml:",omitempty"` // Notify with pending block headers instead of work packages + ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner + GasFloor uint64 // Target gas floor for mined blocks. + GasCeil uint64 // Target gas ceiling for mined blocks. + GasPrice *big.Int // Minimum gas price for mining a transaction + Recommit time.Duration // The time interval for miner to re-create mining work. + Noverify bool // Disable remote mining solution verification(only useful in ethash). } // Miner creates blocks and searches for proof-of-work values. @@ -160,7 +161,7 @@ func (miner *Miner) Mining() bool { return miner.worker.isRunning() } -func (miner *Miner) HashRate() uint64 { +func (miner *Miner) Hashrate() uint64 { if pow, ok := miner.engine.(consensus.PoW); ok { return uint64(pow.Hashrate()) } diff --git a/miner/miner_test.go b/miner/miner_test.go index 0ddd58cd0c..87d7e4be7b 100644 --- a/miner/miner_test.go +++ b/miner/miner_test.go @@ -65,7 +65,7 @@ type testBlockChain struct { func (bc *testBlockChain) CurrentBlock() *types.Block { return types.NewBlock(&types.Header{ GasLimit: bc.gasLimit, - }, nil, nil, nil, new(trie.Trie)) + }, nil, nil, nil, trie.NewStackTrie(nil)) } func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { diff --git a/miner/stress_clique.go b/miner/stress_clique.go index c036af79a6..e4d28d7994 100644 --- a/miner/stress_clique.go +++ b/miner/stress_clique.go @@ -179,7 +179,6 @@ func makeSealer(genesis *genesisT.Genesis) (*node.Node, *eth.Ethereum, error) { NoDiscovery: true, MaxPeers: 25, }, - NoUSB: true, } // Start the node and configure a full Ethereum node on it stack, err := node.New(config) @@ -187,7 +186,7 @@ func makeSealer(genesis *genesisT.Genesis) (*node.Node, *eth.Ethereum, error) { return nil, nil, err } // Create and register the backend - ethBackend, err := eth.New(stack, ð.Config{ + ethBackend, err := eth.New(stack, ðconfig.Config{ Genesis: genesis, NetworkId: genesis.Config.ChainID.Uint64(), SyncMode: downloader.FullSync, diff --git a/miner/stress_ethash.go b/miner/stress_ethash.go index a6337e9bc0..9c6f4e92c5 100644 --- a/miner/stress_ethash.go +++ b/miner/stress_ethash.go @@ -157,7 +157,6 @@ func makeMiner(genesis *genesisT.Genesis) (*node.Node, *eth.Ethereum, error) { NoDiscovery: true, MaxPeers: 25, }, - NoUSB: true, UseLightweightKDF: true, } // Create the node and configure a full Ethereum node on it @@ -165,7 +164,7 @@ func makeMiner(genesis *genesisT.Genesis) (*node.Node, *eth.Ethereum, error) { if err != nil { return nil, nil, err } - ethBackend, err := eth.New(stack, ð.Config{ + ethBackend, err := eth.New(stack, ðconfig.Config{ Genesis: genesis, NetworkId: genesis.Config.ChainID.Uint64(), SyncMode: downloader.FullSync, diff --git a/miner/unconfirmed_test.go b/miner/unconfirmed_test.go index 42e77f3e64..dc83cb9265 100644 --- a/miner/unconfirmed_test.go +++ b/miner/unconfirmed_test.go @@ -19,7 +19,6 @@ package miner import ( "testing" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" ) @@ -43,7 +42,7 @@ func TestUnconfirmedInsertBounds(t *testing.T) { for depth := uint64(0); depth < 2*uint64(limit); depth++ { // Insert multiple blocks for the same level just to stress it for i := 0; i < int(depth); i++ { - pool.Insert(depth, common.Hash([32]byte{byte(depth), byte(i)})) + pool.Insert(depth, [32]byte{byte(depth), byte(i)}) } // Validate that no blocks below the depth allowance are left in pool.blocks.Do(func(block interface{}) { @@ -63,7 +62,7 @@ func TestUnconfirmedShifts(t *testing.T) { pool := newUnconfirmedBlocks(new(noopChainRetriever), limit) for depth := start; depth < start+uint64(limit); depth++ { - pool.Insert(depth, common.Hash([32]byte{byte(depth)})) + pool.Insert(depth, [32]byte{byte(depth)}) } // Try to shift below the limit and ensure no blocks are dropped pool.Shift(start + uint64(limit) - 1) diff --git a/miner/worker.go b/miner/worker.go index d260d9cec5..dd5b4b28e7 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -304,6 +304,9 @@ func (w *worker) isRunning() bool { // close terminates all background threads maintained by the worker. // Note the worker does not support being closed multiple times. func (w *worker) close() { + if w.current != nil && w.current.state != nil { + w.current.state.StopPrefetcher() + } atomic.StoreInt32(&w.running, 0) close(w.exitCh) } @@ -348,7 +351,11 @@ func (w *worker) newWorkLoop(recommit time.Duration) { atomic.StoreInt32(interrupt, s) } interrupt = new(int32) - w.newWorkCh <- &newWorkReq{interrupt: interrupt, noempty: noempty, timestamp: timestamp} + select { + case w.newWorkCh <- &newWorkReq{interrupt: interrupt, noempty: noempty, timestamp: timestamp}: + case <-w.exitCh: + return + } timer.Reset(recommit) atomic.StoreInt32(&w.newTxs, 0) } @@ -639,19 +646,22 @@ func (w *worker) resultLoop() { // makeCurrent creates a new environment for the current cycle. func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error { + // Retrieve the parent state to execute on top and start a prefetcher for + // the miner to speed block sealing up a bit state, err := w.chain.StateAt(parent.Root()) if err != nil { return err } + state.StartPrefetcher("miner") + env := &environment{ - signer: types.NewEIP155Signer(w.chainConfig.GetChainID()), + signer: types.MakeSigner(w.chainConfig, header.Number), state: state, ancestors: mapset.NewSet(), family: mapset.NewSet(), uncles: mapset.NewSet(), header: header, } - // when 08 is processed ancestors contain 07 (quick block) for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) { for _, uncle := range ancestor.Uncles() { @@ -660,9 +670,14 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error { env.family.Add(ancestor.Hash()) env.ancestors.Add(ancestor.Hash()) } - // Keep track of transactions which return errors so they can be removed env.tcount = 0 + + // Swap out the old work with the new one, terminating any leftover prefetcher + // processes in the mean time and starting a new one. + if w.current != nil && w.current.state != nil { + w.current.state.StopPrefetcher() + } w.current = env return nil } @@ -714,9 +729,8 @@ func (w *worker) updateSnapshot() { w.current.txs, uncles, w.current.receipts, - new(trie.Trie), + trie.NewStackTrie(nil), ) - w.snapshotState = w.current.state.Copy() } @@ -794,28 +808,33 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin w.current.state.Prepare(tx.Hash(), common.Hash{}, w.current.tcount) logs, err := w.commitTransaction(tx, coinbase) - switch err { - case core.ErrGasLimitReached: + switch { + case errors.Is(err, core.ErrGasLimitReached): // Pop the current out-of-gas transaction without shifting in the next from the account log.Trace("Gas limit exceeded for current block", "sender", from) txs.Pop() - case core.ErrNonceTooLow: + case errors.Is(err, core.ErrNonceTooLow): // New head notification data race between the transaction pool and miner, shift log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) txs.Shift() - case core.ErrNonceTooHigh: + case errors.Is(err, core.ErrNonceTooHigh): // Reorg notification data race between the transaction pool and miner, skip account = log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) txs.Pop() - case nil: + case errors.Is(err, nil): // Everything ok, collect the logs and shift in the next transaction from the same account coalescedLogs = append(coalescedLogs, logs...) w.current.tcount++ txs.Shift() + case errors.Is(err, core.ErrTxTypeNotSupported): + // Pop the unsupported transaction without shifting in the next from the account + log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) + txs.Pop() + default: // Strange error, discard the transaction and get the next in line (note, the // nonce-too-high clause will prevent us from executing in vain). @@ -858,17 +877,6 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64) if parent.Time() >= uint64(timestamp) { timestamp = int64(parent.Time() + 1) } - // this will ensure we're not going off too far in the future - if now := time.Now().Unix(); timestamp > now+1 { - wait := time.Duration(timestamp-now) * time.Second - logFn := log.Trace - if w.isRunning() { - logFn = log.Info - } - logFn("Mining too far in the future", "wait", common.PrettyDuration(wait)) - time.Sleep(wait) - } - num := parent.Number() header := &types.Header{ ParentHash: parent.Hash(), diff --git a/miner/worker_cg_test.go b/miner/worker_cg_test.go index adb5ab9315..96ae23eab0 100644 --- a/miner/worker_cg_test.go +++ b/miner/worker_cg_test.go @@ -113,7 +113,7 @@ func TestGenerateBlockAndImport_CG1(t *testing.T) { PetersburgBlock: big.NewInt(6), IstanbulBlock: big.NewInt(7), MuirGlacierBlock: big.NewInt(8), - YoloV2Block: nil, + YoloV3Block: nil, EWASMBlock: nil, Ethash: new(ctypes.EthashConfig), Clique: nil, @@ -144,7 +144,7 @@ func TestGenerateBlockAndImport_CG1(t *testing.T) { PetersburgBlock: big.NewInt(6), IstanbulBlock: big.NewInt(7), MuirGlacierBlock: big.NewInt(8), - YoloV2Block: nil, + YoloV3Block: nil, EWASMBlock: nil, Ethash: new(ctypes.EthashConfig), Clique: nil, diff --git a/miner/worker_test.go b/miner/worker_test.go index ce9e9d80f4..56ca494f58 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -83,10 +83,25 @@ func init() { cliqueChainConfig.MustSetConsensusEngineType(ctypes.ConsensusEngineT_Clique) cliqueChainConfig.SetCliquePeriod(10) cliqueChainConfig.SetCliqueEpoch(30000) - tx1, _ := types.SignTx(types.NewTransaction(0, testUserAddress, big.NewInt(1000), vars.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) + + signer := types.LatestSigner(params.TestChainConfig) + tx1 := types.MustSignNewTx(testBankKey, signer, &types.AccessListTx{ + ChainID: params.TestChainConfig.ChainID, + Nonce: 0, + To: &testUserAddress, + Value: big.NewInt(1000), + Gas: vars.TxGas, + }) pendingTxs = append(pendingTxs, tx1) - tx2, _ := types.SignTx(types.NewTransaction(1, testUserAddress, big.NewInt(1000), vars.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) + + tx2 := types.MustSignNewTx(testBankKey, signer, &types.LegacyTx{ + Nonce: 1, + To: &testUserAddress, + Value: big.NewInt(1000), + Gas: vars.TxGas, + }) newTxs = append(newTxs, tx2) + rand.Seed(time.Now().UnixNano()) } diff --git a/mobile/bind.go b/mobile/bind.go index afa97b5382..e32d864aa5 100644 --- a/mobile/bind.go +++ b/mobile/bind.go @@ -40,7 +40,7 @@ type MobileSigner struct { } func (s *MobileSigner) Sign(addr *Address, unsignedTx *Transaction) (signedTx *Transaction, _ error) { - sig, err := s.sign(types.EIP155Signer{}, addr.address, unsignedTx.tx) + sig, err := s.sign(addr.address, unsignedTx.tx) if err != nil { return nil, err } @@ -82,12 +82,16 @@ func NewTransactOpts() *TransactOpts { // NewKeyedTransactOpts is a utility method to easily create a transaction signer // from a single private key. -func NewKeyedTransactOpts(keyJson []byte, passphrase string) (*TransactOpts, error) { +func NewKeyedTransactOpts(keyJson []byte, passphrase string, chainID *big.Int) (*TransactOpts, error) { key, err := keystore.DecryptKey(keyJson, passphrase) if err != nil { return nil, err } - return &TransactOpts{*bind.NewKeyedTransactor(key.PrivateKey)}, nil + auth, err := bind.NewKeyedTransactorWithChainID(key.PrivateKey, chainID) + if err != nil { + return nil, err + } + return &TransactOpts{*auth}, nil } func (opts *TransactOpts) GetFrom() *Address { return &Address{opts.opts.From} } @@ -106,7 +110,7 @@ func (opts *TransactOpts) GetGasLimit() int64 { return int64(opts.opts.GasLimi func (opts *TransactOpts) SetFrom(from *Address) { opts.opts.From = from.address } func (opts *TransactOpts) SetNonce(nonce int64) { opts.opts.Nonce = big.NewInt(nonce) } func (opts *TransactOpts) SetSigner(s Signer) { - opts.opts.Signer = func(signer types.Signer, addr common.Address, tx *types.Transaction) (*types.Transaction, error) { + opts.opts.Signer = func(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { sig, err := s.Sign(&Address{addr}, &Transaction{tx}) if err != nil { return nil, err diff --git a/mobile/discover.go b/mobile/discover.go index 9b3c93ccd9..2c699f08be 100644 --- a/mobile/discover.go +++ b/mobile/discover.go @@ -22,12 +22,12 @@ package geth import ( "errors" - "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/p2p/enode" ) // Enode represents a host on the network. type Enode struct { - node *discv5.Node + node *enode.Node } // NewEnode parses a node designator. @@ -53,8 +53,8 @@ type Enode struct { // and UDP discovery port 30301. // // enode://@10.3.58.6:30303?discport=30301 -func NewEnode(rawurl string) (enode *Enode, _ error) { - node, err := discv5.ParseNode(rawurl) +func NewEnode(rawurl string) (*Enode, error) { + node, err := enode.Parse(enode.ValidSchemes, rawurl) if err != nil { return nil, err } @@ -62,12 +62,12 @@ func NewEnode(rawurl string) (enode *Enode, _ error) { } // Enodes represents a slice of accounts. -type Enodes struct{ nodes []*discv5.Node } +type Enodes struct{ nodes []*enode.Node } // NewEnodes creates a slice of uninitialized enodes. func NewEnodes(size int) *Enodes { return &Enodes{ - nodes: make([]*discv5.Node, size), + nodes: make([]*enode.Node, size), } } diff --git a/mobile/ethereum.go b/mobile/ethereum.go index 59da852397..97c46ddca7 100644 --- a/mobile/ethereum.go +++ b/mobile/ethereum.go @@ -21,7 +21,7 @@ package geth import ( "errors" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) diff --git a/mobile/geth.go b/mobile/geth.go index 1f3e39191d..769071263a 100644 --- a/mobile/geth.go +++ b/mobile/geth.go @@ -24,8 +24,8 @@ import ( "fmt" "path/filepath" - "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethstats" "github.com/ethereum/go-ethereum/internal/debug" @@ -195,7 +195,7 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) { } // Register the Ethereum protocol if requested if config.EthereumEnabled { - ethConf := eth.DefaultConfig + ethConf := ethconfig.Defaults ethConf.Genesis = genesis ethConf.SyncMode = downloader.LightSync ethConf.NetworkId = *genesis.Config.GetNetworkID() diff --git a/mobile/params.go b/mobile/params.go index ee461d065a..fc9ae181e0 100644 --- a/mobile/params.go +++ b/mobile/params.go @@ -21,7 +21,7 @@ package geth import ( "encoding/json" - "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params" ) @@ -70,9 +70,13 @@ func MordorGenesis() string { // FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated // by the foundation running the V5 discovery protocol. func FoundationBootnodes() *Enodes { - nodes := &Enodes{nodes: make([]*discv5.Node, len(params.MainnetBootnodes))} + nodes := &Enodes{nodes: make([]*enode.Node, len(params.MainnetBootnodes))} for i, url := range params.MainnetBootnodes { - nodes.nodes[i] = discv5.MustParseNode(url) + var err error + nodes.nodes[i], err = enode.Parse(enode.ValidSchemes, url) + if err != nil { + panic("invalid node URL: " + err.Error()) + } } return nodes } diff --git a/node/api.go b/node/api.go index 083784f4e4..be20b89d95 100644 --- a/node/api.go +++ b/node/api.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/debug" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rpc" @@ -162,8 +163,8 @@ func (api *privateAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, return rpcSub, nil } -// StartRPC starts the HTTP RPC API server. -func (api *privateAdminAPI) StartRPC(host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) { +// StartHTTP starts the HTTP RPC API server. +func (api *privateAdminAPI) StartHTTP(host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) { api.node.lock.Lock() defer api.node.lock.Unlock() @@ -216,12 +217,26 @@ func (api *privateAdminAPI) StartRPC(host *string, port *int, cors *string, apis return true, nil } -// StopRPC shuts down the HTTP server. -func (api *privateAdminAPI) StopRPC() (bool, error) { +// StartRPC starts the HTTP RPC API server. +// This method is deprecated. Use StartHTTP instead. +func (api *privateAdminAPI) StartRPC(host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) { + log.Warn("Deprecation warning", "method", "admin.StartRPC", "use-instead", "admin.StartHTTP") + return api.StartHTTP(host, port, cors, apis, vhosts) +} + +// StopHTTP shuts down the HTTP server. +func (api *privateAdminAPI) StopHTTP() (bool, error) { api.node.http.stop() return true, nil } +// StopRPC shuts down the HTTP server. +// This method is deprecated. Use StopHTTP instead. +func (api *privateAdminAPI) StopRPC() (bool, error) { + log.Warn("Deprecation warning", "method", "admin.StopRPC", "use-instead", "admin.StopHTTP") + return api.StopHTTP() +} + // StartWS starts the websocket RPC API server. func (api *privateAdminAPI) StartWS(host *string, port *int, allowedOrigins *string, apis *string) (bool, error) { api.node.lock.Lock() diff --git a/node/api_test.go b/node/api_test.go index e4c08962c3..9549adf9c2 100644 --- a/node/api_test.go +++ b/node/api_test.go @@ -69,7 +69,7 @@ func TestStartRPC(t *testing.T) { name: "rpc enabled through API", cfg: Config{}, fn: func(t *testing.T, n *Node, api *privateAdminAPI) { - _, err := api.StartRPC(sp("127.0.0.1"), ip(0), nil, nil, nil) + _, err := api.StartHTTP(sp("127.0.0.1"), ip(0), nil, nil, nil) assert.NoError(t, err) }, wantReachable: true, @@ -90,14 +90,14 @@ func TestStartRPC(t *testing.T) { port := listener.Addr().(*net.TCPAddr).Port // Now try to start RPC on that port. This should fail. - _, err = api.StartRPC(sp("127.0.0.1"), ip(port), nil, nil, nil) + _, err = api.StartHTTP(sp("127.0.0.1"), ip(port), nil, nil, nil) if err == nil { - t.Fatal("StartRPC should have failed on port", port) + t.Fatal("StartHTTP should have failed on port", port) } // Try again after unblocking the port. It should work this time. listener.Close() - _, err = api.StartRPC(sp("127.0.0.1"), ip(port), nil, nil, nil) + _, err = api.StartHTTP(sp("127.0.0.1"), ip(port), nil, nil, nil) assert.NoError(t, err) }, wantReachable: true, @@ -109,7 +109,7 @@ func TestStartRPC(t *testing.T) { name: "rpc stopped through API", cfg: Config{HTTPHost: "127.0.0.1"}, fn: func(t *testing.T, n *Node, api *privateAdminAPI) { - _, err := api.StopRPC() + _, err := api.StopHTTP() assert.NoError(t, err) }, wantReachable: false, @@ -121,10 +121,10 @@ func TestStartRPC(t *testing.T) { name: "rpc stopped twice", cfg: Config{HTTPHost: "127.0.0.1"}, fn: func(t *testing.T, n *Node, api *privateAdminAPI) { - _, err := api.StopRPC() + _, err := api.StopHTTP() assert.NoError(t, err) - _, err = api.StopRPC() + _, err = api.StopHTTP() assert.NoError(t, err) }, wantReachable: false, @@ -211,14 +211,14 @@ func TestStartRPC(t *testing.T) { { name: "rpc stopped with ws enabled", fn: func(t *testing.T, n *Node, api *privateAdminAPI) { - _, err := api.StartRPC(sp("127.0.0.1"), ip(0), nil, nil, nil) + _, err := api.StartHTTP(sp("127.0.0.1"), ip(0), nil, nil, nil) assert.NoError(t, err) wsport := n.http.port _, err = api.StartWS(sp("127.0.0.1"), ip(wsport), nil, nil) assert.NoError(t, err) - _, err = api.StopRPC() + _, err = api.StopHTTP() assert.NoError(t, err) }, wantReachable: false, @@ -233,7 +233,7 @@ func TestStartRPC(t *testing.T) { assert.NoError(t, err) wsport := n.http.port - _, err = api.StartRPC(sp("127.0.0.1"), ip(wsport), nil, nil, nil) + _, err = api.StartHTTP(sp("127.0.0.1"), ip(wsport), nil, nil, nil) assert.NoError(t, err) }, wantReachable: true, @@ -244,11 +244,13 @@ func TestStartRPC(t *testing.T) { } for _, test := range tests { + test := test t.Run(test.name, func(t *testing.T) { + t.Parallel() + // Apply some sane defaults. config := test.cfg // config.Logger = testlog.Logger(t, log.LvlDebug) - config.NoUSB = true config.P2P.NoDiscovery = true // Create Node. diff --git a/node/config.go b/node/config.go index 15769f258e..430d4ac797 100644 --- a/node/config.go +++ b/node/config.go @@ -97,6 +97,9 @@ type Config struct { // NoUSB disables hardware wallet monitoring and connectivity. NoUSB bool `toml:",omitempty"` + // USB enables hardware wallet monitoring and connectivity. + USB bool `toml:",omitempty"` + // SmartCardDaemonPath is the path to the smartcard daemon's socket SmartCardDaemonPath string `toml:",omitempty"` @@ -138,6 +141,9 @@ type Config struct { // interface. HTTPTimeouts rpc.HTTPTimeouts + // HTTPPathPrefix specifies a path prefix on which http-rpc is to be served. + HTTPPathPrefix 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 @@ -147,6 +153,9 @@ type Config struct { // ephemeral nodes). WSPort int `toml:",omitempty"` + // WSPathPrefix specifies a path prefix on which ws-rpc is to be served. + WSPathPrefix string `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. @@ -184,6 +193,9 @@ type Config struct { staticNodesWarning bool trustedNodesWarning bool oldGethResourceWarning bool + + // AllowUnprotectedTxs allows non EIP-155 protected transactions to be send over RPC. + AllowUnprotectedTxs bool `toml:",omitempty"` } // IPCEndpoint resolves an IPC endpoint based on a configured value, taking into @@ -481,7 +493,7 @@ func makeAccountManager(conf *Config) (*accounts.Manager, string, error) { // we can have both, but it's very confusing for the user to see the same // accounts in both externally and locally, plus very racey. backends = append(backends, keystore.NewKeyStore(keydir, scryptN, scryptP)) - if !conf.NoUSB { + if conf.USB { // Start a USB hub for Ledger hardware wallets if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil { log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err)) diff --git a/node/node.go b/node/node.go index 26267fabf6..e66ef10504 100644 --- a/node/node.go +++ b/node/node.go @@ -142,6 +142,14 @@ func New(conf *Config) (*Node, error) { node.server.Config.NodeDatabase = node.config.NodeDB() } + // Check HTTP/WS prefixes are valid. + if err := validatePrefix("HTTP", conf.HTTPPathPrefix); err != nil { + return nil, err + } + if err := validatePrefix("WebSocket", conf.WSPathPrefix); err != nil { + return nil, err + } + // Configure RPC servers. node.http = newHTTPServer(node.log, conf.HTTPTimeouts) node.ws = newHTTPServer(node.log, rpc.DefaultHTTPTimeouts) @@ -166,12 +174,13 @@ func (n *Node) Start() error { return ErrNodeStopped } n.state = runningState - err := n.startNetworking() + // open networking and RPC endpoints + err := n.openEndpoints() lifecycles := make([]Lifecycle, len(n.lifecycles)) copy(lifecycles, n.lifecycles) n.lock.Unlock() - // Check if networking startup failed. + // Check if endpoint startup failed. if err != nil { n.doClose(nil) return err @@ -254,12 +263,14 @@ func (n *Node) doClose(errs []error) error { } } -// startNetworking starts all network endpoints. -func (n *Node) startNetworking() error { +// openEndpoints starts all network and RPC endpoints. +func (n *Node) openEndpoints() error { + // start networking endpoints n.log.Info("Starting peer-to-peer node", "instance", n.server.Name) if err := n.server.Start(); err != nil { return convertFileLockError(err) } + // start RPC endpoints err := n.startRPC() if err != nil { n.stopRPC() @@ -399,6 +410,7 @@ func (n *Node) startRPC() error { CorsAllowedOrigins: n.config.HTTPCors, Vhosts: n.config.HTTPVirtualHosts, Modules: n.config.HTTPModules, + prefix: n.config.HTTPPathPrefix, } if err := n.http.setListenAddr(n.config.HTTPHost, n.config.HTTPPort); err != nil { return err @@ -414,6 +426,7 @@ func (n *Node) startRPC() error { config := wsConfig{ Modules: n.config.WSModules, Origins: n.config.WSOrigins, + prefix: n.config.WSPathPrefix, } if err := server.setListenAddr(n.config.WSHost, n.config.WSPort); err != nil { return err @@ -510,6 +523,7 @@ func (n *Node) RegisterHandler(name, path string, handler http.Handler) { if n.state != initializingState { panic("can't register HTTP handler on running/stopped node") } + n.http.mux.Handle(path, handler) n.http.handlerNames[path] = name } @@ -566,17 +580,18 @@ func (n *Node) IPCEndpoint() string { return n.ipc.endpoint } -// HTTPEndpoint returns the URL of the HTTP server. +// HTTPEndpoint returns the URL of the HTTP server. Note that this URL does not +// contain the JSON-RPC path prefix set by HTTPPathPrefix. func (n *Node) HTTPEndpoint() string { return "http://" + n.http.listenAddr() } -// WSEndpoint retrieves the current WS endpoint used by the protocol stack. +// WSEndpoint returns the current JSON-RPC over WebSocket endpoint. func (n *Node) WSEndpoint() string { if n.http.wsAllowed() { - return "ws://" + n.http.listenAddr() + return "ws://" + n.http.listenAddr() + n.http.wsConfig.prefix } - return "ws://" + n.ws.listenAddr() + return "ws://" + n.ws.listenAddr() + n.ws.wsConfig.prefix } // EventMux retrieves the event multiplexer used by all the network services in @@ -588,7 +603,7 @@ func (n *Node) EventMux() *event.TypeMux { // OpenDatabase opens an existing database with the given name (or creates one if no // previous can be found) from within the node's instance directory. If the node is // ephemeral, a memory database is returned. -func (n *Node) OpenDatabase(name string, cache, handles int, namespace string) (ethdb.Database, error) { +func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, readonly bool) (ethdb.Database, error) { n.lock.Lock() defer n.lock.Unlock() if n.state == closedState { @@ -600,7 +615,7 @@ func (n *Node) OpenDatabase(name string, cache, handles int, namespace string) ( if n.config.DataDir == "" { db = rawdb.NewMemoryDatabase() } else { - db, err = rawdb.NewLevelDBDatabase(n.ResolvePath(name), cache, handles, namespace) + db, err = rawdb.NewLevelDBDatabase(n.ResolvePath(name), cache, handles, namespace, readonly) } if err == nil { @@ -614,12 +629,12 @@ func (n *Node) OpenDatabase(name string, cache, handles int, namespace string) ( // also attaching a chain freezer to it that moves ancient chain data from the // database to immutable append-only files. If the node is an ephemeral one, a // memory database is returned. -func (n *Node) OpenDatabaseWithFreezerRemote(name string, cache, handles int, freezerURL string) (ethdb.Database, error) { +func (n *Node) OpenDatabaseWithFreezerRemote(name string, cache, handles int, freezerURL string, readonly bool) (ethdb.Database, error) { if n.config.DataDir == "" { return rawdb.NewMemoryDatabase(), nil } root := n.config.ResolvePath(name) - return rawdb.NewLevelDBDatabaseWithFreezerRemote(root, cache, handles, freezerURL) + return rawdb.NewLevelDBDatabaseWithFreezerRemote(root, cache, handles, freezerURL, readonly) } // OpenDatabaseWithFreezer opens an existing database with the given name (or @@ -627,7 +642,7 @@ func (n *Node) OpenDatabaseWithFreezerRemote(name string, cache, handles int, fr // also attaching a chain freezer to it that moves ancient chain data from the // database to immutable append-only files. If the node is an ephemeral one, a // memory database is returned. -func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, freezer, namespace string) (ethdb.Database, error) { +func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, freezer, namespace string, readonly bool) (ethdb.Database, error) { n.lock.Lock() defer n.lock.Unlock() if n.state == closedState { @@ -646,7 +661,7 @@ func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, freezer, case !filepath.IsAbs(freezer): freezer = n.ResolvePath(freezer) } - db, err = rawdb.NewLevelDBDatabaseWithFreezer(root, cache, handles, freezer, namespace) + db, err = rawdb.NewLevelDBDatabaseWithFreezer(root, cache, handles, freezer, namespace, readonly) } if err == nil { diff --git a/node/node_test.go b/node/node_test.go index 21718956cd..d758ed058f 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -352,7 +352,7 @@ func TestNodeCloseClosesDB(t *testing.T) { stack, _ := New(testNodeConfig()) defer stack.Close() - db, err := stack.OpenDatabase("mydb", 0, 0, "") + db, err := stack.OpenDatabase("mydb", 0, 0, "", false) if err != nil { t.Fatal("can't open DB:", err) } @@ -375,7 +375,7 @@ func TestNodeOpenDatabaseFromLifecycleStart(t *testing.T) { var err error stack.RegisterLifecycle(&InstrumentedService{ startHook: func() { - db, err = stack.OpenDatabase("mydb", 0, 0, "") + db, err = stack.OpenDatabase("mydb", 0, 0, "", false) if err != nil { t.Fatal("can't open DB:", err) } @@ -396,7 +396,7 @@ func TestNodeOpenDatabaseFromLifecycleStop(t *testing.T) { stack.RegisterLifecycle(&InstrumentedService{ stopHook: func() { - db, err := stack.OpenDatabase("mydb", 0, 0, "") + db, err := stack.OpenDatabase("mydb", 0, 0, "", false) if err != nil { t.Fatal("can't open DB:", err) } @@ -584,7 +584,7 @@ func TestLifecycleTerminationGuarantee(t *testing.T) { } // Tests whether a handler can be successfully mounted on the canonical HTTP server -// on the given path +// on the given prefix func TestRegisterHandler_Successful(t *testing.T) { node := createNode(t, 7878, 7979) @@ -677,7 +677,112 @@ func TestWebsocketHTTPOnSeparatePort_WSRequest(t *testing.T) { if !checkRPC(node.HTTPEndpoint()) { t.Fatalf("http request failed") } +} + +type rpcPrefixTest struct { + httpPrefix, wsPrefix string + // These lists paths on which JSON-RPC should be served / not served. + wantHTTP []string + wantNoHTTP []string + wantWS []string + wantNoWS []string +} + +func TestNodeRPCPrefix(t *testing.T) { + t.Parallel() + + tests := []rpcPrefixTest{ + // both off + { + httpPrefix: "", wsPrefix: "", + wantHTTP: []string{"/", "/?p=1"}, + wantNoHTTP: []string{"/test", "/test?p=1"}, + wantWS: []string{"/", "/?p=1"}, + wantNoWS: []string{"/test", "/test?p=1"}, + }, + // only http prefix + { + httpPrefix: "/testprefix", wsPrefix: "", + wantHTTP: []string{"/testprefix", "/testprefix?p=1", "/testprefix/x", "/testprefix/x?p=1"}, + wantNoHTTP: []string{"/", "/?p=1", "/test", "/test?p=1"}, + wantWS: []string{"/", "/?p=1"}, + wantNoWS: []string{"/testprefix", "/testprefix?p=1", "/test", "/test?p=1"}, + }, + // only ws prefix + { + httpPrefix: "", wsPrefix: "/testprefix", + wantHTTP: []string{"/", "/?p=1"}, + wantNoHTTP: []string{"/testprefix", "/testprefix?p=1", "/test", "/test?p=1"}, + wantWS: []string{"/testprefix", "/testprefix?p=1", "/testprefix/x", "/testprefix/x?p=1"}, + wantNoWS: []string{"/", "/?p=1", "/test", "/test?p=1"}, + }, + // both set + { + httpPrefix: "/testprefix", wsPrefix: "/testprefix", + wantHTTP: []string{"/testprefix", "/testprefix?p=1", "/testprefix/x", "/testprefix/x?p=1"}, + wantNoHTTP: []string{"/", "/?p=1", "/test", "/test?p=1"}, + wantWS: []string{"/testprefix", "/testprefix?p=1", "/testprefix/x", "/testprefix/x?p=1"}, + wantNoWS: []string{"/", "/?p=1", "/test", "/test?p=1"}, + }, + } + + for _, test := range tests { + test := test + name := fmt.Sprintf("http=%s ws=%s", test.httpPrefix, test.wsPrefix) + t.Run(name, func(t *testing.T) { + cfg := &Config{ + HTTPHost: "127.0.0.1", + HTTPPathPrefix: test.httpPrefix, + WSHost: "127.0.0.1", + WSPathPrefix: test.wsPrefix, + } + node, err := New(cfg) + if err != nil { + t.Fatal("can't create node:", err) + } + defer node.Close() + if err := node.Start(); err != nil { + t.Fatal("can't start node:", err) + } + test.check(t, node) + }) + } +} + +func (test rpcPrefixTest) check(t *testing.T, node *Node) { + t.Helper() + httpBase := "http://" + node.http.listenAddr() + wsBase := "ws://" + node.http.listenAddr() + + if node.WSEndpoint() != wsBase+test.wsPrefix { + t.Errorf("Error: node has wrong WSEndpoint %q", node.WSEndpoint()) + } + + for _, path := range test.wantHTTP { + resp := rpcRequest(t, httpBase+path) + if resp.StatusCode != 200 { + t.Errorf("Error: %s: bad status code %d, want 200", path, resp.StatusCode) + } + } + for _, path := range test.wantNoHTTP { + resp := rpcRequest(t, httpBase+path) + if resp.StatusCode != 404 { + t.Errorf("Error: %s: bad status code %d, want 404", path, resp.StatusCode) + } + } + for _, path := range test.wantWS { + err := wsRequest(t, wsBase+path, "") + if err != nil { + t.Errorf("Error: %s: WebSocket connection failed: %v", path, err) + } + } + for _, path := range test.wantNoWS { + err := wsRequest(t, wsBase+path, "") + if err == nil { + t.Errorf("Error: %s: WebSocket connection succeeded for path in wantNoWS", path) + } + } } func createNode(t *testing.T, httpPort, wsPort int) *Node { diff --git a/node/openrpc.go b/node/openrpc.go index c79fc34f5e..3b5d31bf72 100644 --- a/node/openrpc.go +++ b/node/openrpc.go @@ -21,6 +21,8 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/filters" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" meta_schema "github.com/open-rpc/meta-schema" @@ -140,7 +142,8 @@ func newOpenRPCDocument() *go_openrpc_reflect.Document { // the mock subscription receiver type RPCSubscription. // This pattern matches all strings that start with Subscribe and are suffixed with a non-zero // number of A-z characters. - if isPubSub(method.Type) { + pkgStr := method.Type.In(0).String() + if isPubSub(method.Type) && pkgStr == "*eth.PublicEthereumAPI" { // This catches MOST of them (except SubscribeSyncStatus) return false } @@ -208,25 +211,59 @@ func newOpenRPCDocument() *go_openrpc_reflect.Document { } /* -The following struct RPCSubscription and RPCSubscription.Unsubscribe method +The following structs *RPCSubscription and *RPCSubscription.Unsubscribe method are documentation-only mocks to represent the otherwise invisible (un-reflected) method. It is appended to the OpenRPC document when the eth/api/filters.PublicFilterAPI receiver -is registered. +is registered, similar logic applies to other modules. */ -type RPCSubscription struct{} +type RPCEthSubscription struct{} // Unsubscribe terminates an existing subscription by ID. -func (sub *RPCSubscription) Unsubscribe(id rpc.ID) error { +func (sub *RPCEthSubscription) Unsubscribe(id rpc.ID) error { // This is a mock function, not the real one. return nil } -type RPCSubscriptionParamsName string +type RPCEthSubscriptionParamsName string // Subscribe creates a subscription to an event channel. // Subscriptions are not available over HTTP; they are only available over WS, IPC, and Process connections. -func (sub *RPCSubscription) Subscribe(subscriptionName RPCSubscriptionParamsName, subscriptionOptions interface{}) (subscriptionID rpc.ID, err error) { +func (sub *RPCEthSubscription) Subscribe(subscriptionName RPCEthSubscriptionParamsName, subscriptionOptions interface{}) (subscriptionID rpc.ID, err error) { + // This is a mock function, not the real one. + return +} + +type RPCDebugSubscription struct{} + +// Unsubscribe terminates an existing subscription by ID. +func (sub *RPCDebugSubscription) Unsubscribe(id rpc.ID) error { + // This is a mock function, not the real one. + return nil +} + +type RPCDebugSubscriptionParamsName string + +// Subscribe creates a subscription to an event channel. +// Subscriptions are not available over HTTP; they are only available over WS, IPC, and Process connections. +func (sub *RPCDebugSubscription) Subscribe(subscriptionName RPCDebugSubscriptionParamsName, subscriptionOptions interface{}) (subscriptionID rpc.ID, err error) { + // This is a mock function, not the real one. + return +} + +type RPCTraceSubscription struct{} + +// Unsubscribe terminates an existing subscription by ID. +func (sub *RPCTraceSubscription) Unsubscribe(id rpc.ID) error { + // This is a mock function, not the real one. + return nil +} + +type RPCTraceSubscriptionParamsName string + +// Subscribe creates a subscription to an event channel. +// Subscriptions are not available over HTTP; they are only available over WS, IPC, and Process connections. +func (sub *RPCTraceSubscription) Subscribe(subscriptionName RPCTraceSubscriptionParamsName, subscriptionOptions interface{}) (subscriptionID rpc.ID, err error) { // This is a mock function, not the real one. return } @@ -241,7 +278,11 @@ func registerOpenRPCAPIs(doc *go_openrpc_reflect.Document, apis []rpc.API) { // would otherwise not occur in the document. switch api.Service.(type) { case *filters.PublicFilterAPI: - doc.RegisterReceiverName("eth", &RPCSubscription{}) + doc.RegisterReceiverName("eth", &RPCEthSubscription{}) + case *debug.HandlerT: + doc.RegisterReceiverName("debug", &RPCDebugSubscription{}) + case *tracers.TraceAPI: + doc.RegisterReceiverName("trace", &RPCTraceSubscription{}) } } } @@ -329,7 +370,8 @@ var blockNumberOrHashD = fmt.Sprintf(`{ ] }`, blockNumberD, commonHashD, requireCanonicalD) -var rpcSubscriptionParamsNameD = `{ +var rpcEthSubscriptionParamsNameD = `{ + "title": "subscriptionName", "oneOf": [ {"type": "string", "enum": ["newHeads"], "description": "Fires a notification each time a new header is appended to the chain, including chain reorganizations."}, {"type": "string", "enum": ["newSideHeads"], "description": "Fires a notification each time a new header is appended to the non-canonical (side) chain, including chain reorganizations."}, @@ -339,6 +381,20 @@ var rpcSubscriptionParamsNameD = `{ ] }` +var rpcDebugSubscriptionParamsNameD = `{ + "title": "subscriptionName", + "oneOf": [ + {"type": "string", "enum": ["traceChain"], "description": "Returns transaction traces within a range of blocks."} + ] + }` + +var rpcTraceSubscriptionParamsNameD = `{ + "title": "subscriptionName", + "oneOf": [ + {"type": "string", "enum": ["filter"], "description": "Returns transaction traces for the filtered addresses within a range of blocks."} + ] + }` + // schemaDictEntry represents a type association passed to the jsonschema reflector. type schemaDictEntry struct { example interface{} @@ -381,7 +437,10 @@ func OpenRPCJSONSchemaTypeMapper(ty reflect.Type) *jsonschema.Type { {rpc.BlockNumber(0), blockNumberD}, {rpc.BlockNumberOrHash{}, blockNumberOrHashD}, {rpc.Subscription{}, rpcSubscriptionIDD}, - {RPCSubscriptionParamsName(""), rpcSubscriptionParamsNameD}, + {rpc.ID(""), rpcSubscriptionIDD}, + {RPCEthSubscriptionParamsName(""), rpcEthSubscriptionParamsNameD}, + {RPCDebugSubscriptionParamsName(""), rpcDebugSubscriptionParamsNameD}, + {RPCTraceSubscriptionParamsName(""), rpcTraceSubscriptionParamsNameD}, } for _, d := range dict { diff --git a/node/rpcstack.go b/node/rpcstack.go index 731e807aca..56e23cc5c7 100644 --- a/node/rpcstack.go +++ b/node/rpcstack.go @@ -39,12 +39,14 @@ type httpConfig struct { Modules []string CorsAllowedOrigins []string Vhosts []string + prefix string // path prefix on which to mount http handler } // wsConfig is the JSON-RPC/Websocket configuration type wsConfig struct { Origins []string Modules []string + prefix string // path prefix on which to mount ws handler } type rpcHandler struct { @@ -62,6 +64,7 @@ type httpServer struct { listener net.Listener // non-nil when server is running // HTTP RPC handler things. + httpConfig httpConfig httpHandler atomic.Value // *rpcHandler @@ -79,6 +82,7 @@ type httpServer struct { func newHTTPServer(log log.Logger, timeouts rpc.HTTPTimeouts) *httpServer { h := &httpServer{log: log, timeouts: timeouts, handlerNames: make(map[string]string)} + h.httpHandler.Store((*rpcHandler)(nil)) h.wsHandler.Store((*rpcHandler)(nil)) return h @@ -140,14 +144,21 @@ func (h *httpServer) start() error { h.listener = listener go h.server.Serve(listener) + if h.wsAllowed() { + url := fmt.Sprintf("ws://%v", listener.Addr()) + if h.wsConfig.prefix != "" { + url += h.wsConfig.prefix + } + h.log.Info("WebSocket enabled", "url", url) + } // if server is websocket only, return after logging - if h.wsAllowed() && !h.rpcAllowed() { - h.log.Info("WebSocket enabled", "url", fmt.Sprintf("ws://%v", listener.Addr())) + if !h.rpcAllowed() { return nil } // Log http endpoint. h.log.Info("HTTP server started", "endpoint", listener.Addr(), + "prefix", h.httpConfig.prefix, "cors", strings.Join(h.httpConfig.CorsAllowedOrigins, ","), "vhosts", strings.Join(h.httpConfig.Vhosts, ","), ) @@ -170,26 +181,60 @@ func (h *httpServer) start() error { } func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - rpc := h.httpHandler.Load().(*rpcHandler) - if r.RequestURI == "/" { - // Serve JSON-RPC on the root path. - ws := h.wsHandler.Load().(*rpcHandler) - if ws != nil && isWebsocket(r) { + // check if ws request and serve if ws enabled + ws := h.wsHandler.Load().(*rpcHandler) + if ws != nil && isWebsocket(r) { + if checkPath(r, h.wsConfig.prefix) { ws.ServeHTTP(w, r) + } + return + } + // if http-rpc is enabled, try to serve request + rpc := h.httpHandler.Load().(*rpcHandler) + if rpc != nil { + // First try to route in the mux. + // Requests to a path below root are handled by the mux, + // which has all the handlers registered via Node.RegisterHandler. + // These are made available when RPC is enabled. + muxHandler, pattern := h.mux.Handler(r) + if pattern != "" { + muxHandler.ServeHTTP(w, r) return } - if rpc != nil { + + if checkPath(r, h.httpConfig.prefix) { rpc.ServeHTTP(w, r) return } - } else if rpc != nil { - // Requests to a path below root are handled by the mux, - // which has all the handlers registered via Node.RegisterHandler. - // These are made available when RPC is enabled. - h.mux.ServeHTTP(w, r) - return } - w.WriteHeader(404) + w.WriteHeader(http.StatusNotFound) +} + +// checkPath checks whether a given request URL matches a given path prefix. +func checkPath(r *http.Request, path string) bool { + // if no prefix has been specified, request URL must be on root + if path == "" { + return r.URL.Path == "/" + } + // otherwise, check to make sure prefix matches + return len(r.URL.Path) >= len(path) && r.URL.Path[:len(path)] == path +} + +// validatePrefix checks if 'path' is a valid configuration value for the RPC prefix option. +func validatePrefix(what, path string) error { + if path == "" { + return nil + } + if path[0] != '/' { + return fmt.Errorf(`%s RPC path prefix %q does not contain leading "/"`, what, path) + } + if strings.ContainsAny(path, "?#") { + // This is just to avoid confusion. While these would match correctly (i.e. they'd + // match if URL-escaped into path), it's not easy to understand for users when + // setting that on the command line. + return fmt.Errorf("%s RPC path prefix %q contains URL meta-characters", what, path) + } + return nil } // stop shuts down the HTTP server. @@ -448,6 +493,7 @@ func (is *ipcServer) start(apis []rpc.API) error { } listener, srv, err := rpc.StartIPCEndpoint(is.endpoint, apis) if err != nil { + is.log.Warn("IPC opening failed", "url", is.endpoint, "error", err) return err } is.log.Info("IPC endpoint opened", "url", is.endpoint) diff --git a/node/rpcstack_test.go b/node/rpcstack_test.go index 0ee120efd7..f92f0ba396 100644 --- a/node/rpcstack_test.go +++ b/node/rpcstack_test.go @@ -18,7 +18,11 @@ package node import ( "bytes" + "fmt" "net/http" + "net/url" + "strconv" + "strings" "testing" "github.com/ethereum/go-ethereum/internal/testlog" @@ -30,47 +34,129 @@ import ( // TestCorsHandler makes sure CORS are properly handled on the http server. func TestCorsHandler(t *testing.T) { - srv := createAndStartServer(t, httpConfig{CorsAllowedOrigins: []string{"test", "test.com"}}, false, wsConfig{}) + srv := createAndStartServer(t, &httpConfig{CorsAllowedOrigins: []string{"test", "test.com"}}, false, &wsConfig{}) defer srv.stop() + url := "http://" + srv.listenAddr() - resp := testRequest(t, "origin", "test.com", "", srv) + resp := rpcRequest(t, url, "origin", "test.com") assert.Equal(t, "test.com", resp.Header.Get("Access-Control-Allow-Origin")) - resp2 := testRequest(t, "origin", "bad", "", srv) + resp2 := rpcRequest(t, url, "origin", "bad") assert.Equal(t, "", resp2.Header.Get("Access-Control-Allow-Origin")) } // TestVhosts makes sure vhosts are properly handled on the http server. func TestVhosts(t *testing.T) { - srv := createAndStartServer(t, httpConfig{Vhosts: []string{"test"}}, false, wsConfig{}) + srv := createAndStartServer(t, &httpConfig{Vhosts: []string{"test"}}, false, &wsConfig{}) defer srv.stop() + url := "http://" + srv.listenAddr() - resp := testRequest(t, "", "", "test", srv) + resp := rpcRequest(t, url, "host", "test") assert.Equal(t, resp.StatusCode, http.StatusOK) - resp2 := testRequest(t, "", "", "bad", srv) + resp2 := rpcRequest(t, url, "host", "bad") assert.Equal(t, resp2.StatusCode, http.StatusForbidden) } +type originTest struct { + spec string + expOk []string + expFail []string +} + +// splitAndTrim splits input separated by a comma +// and trims excessive white space from the substrings. +// Copied over from flags.go +func splitAndTrim(input string) (ret []string) { + l := strings.Split(input, ",") + for _, r := range l { + r = strings.TrimSpace(r) + if len(r) > 0 { + ret = append(ret, r) + } + } + return ret +} + // TestWebsocketOrigins makes sure the websocket origins are properly handled on the websocket server. func TestWebsocketOrigins(t *testing.T) { - srv := createAndStartServer(t, httpConfig{}, true, wsConfig{Origins: []string{"test"}}) - defer srv.stop() - - dialer := websocket.DefaultDialer - _, _, err := dialer.Dial("ws://"+srv.listenAddr(), http.Header{ - "Content-type": []string{"application/json"}, - "Sec-WebSocket-Version": []string{"13"}, - "Origin": []string{"test"}, - }) - assert.NoError(t, err) - - _, _, err = dialer.Dial("ws://"+srv.listenAddr(), http.Header{ - "Content-type": []string{"application/json"}, - "Sec-WebSocket-Version": []string{"13"}, - "Origin": []string{"bad"}, - }) - assert.Error(t, err) + tests := []originTest{ + { + spec: "*", // allow all + expOk: []string{"", "http://test", "https://test", "http://test:8540", "https://test:8540", + "http://test.com", "https://foo.test", "http://testa", "http://atestb:8540", "https://atestb:8540"}, + }, + { + spec: "test", + expOk: []string{"http://test", "https://test", "http://test:8540", "https://test:8540"}, + expFail: []string{"http://test.com", "https://foo.test", "http://testa", "http://atestb:8540", "https://atestb:8540"}, + }, + // scheme tests + { + spec: "https://test", + expOk: []string{"https://test", "https://test:9999"}, + expFail: []string{ + "test", // no scheme, required by spec + "http://test", // wrong scheme + "http://test.foo", "https://a.test.x", // subdomain variatoins + "http://testx:8540", "https://xtest:8540"}, + }, + // ip tests + { + spec: "https://12.34.56.78", + expOk: []string{"https://12.34.56.78", "https://12.34.56.78:8540"}, + expFail: []string{ + "http://12.34.56.78", // wrong scheme + "http://12.34.56.78:443", // wrong scheme + "http://1.12.34.56.78", // wrong 'domain name' + "http://12.34.56.78.a", // wrong 'domain name' + "https://87.65.43.21", "http://87.65.43.21:8540", "https://87.65.43.21:8540"}, + }, + // port tests + { + spec: "test:8540", + expOk: []string{"http://test:8540", "https://test:8540"}, + expFail: []string{ + "http://test", "https://test", // spec says port required + "http://test:8541", "https://test:8541", // wrong port + "http://bad", "https://bad", "http://bad:8540", "https://bad:8540"}, + }, + // scheme and port + { + spec: "https://test:8540", + expOk: []string{"https://test:8540"}, + expFail: []string{ + "https://test", // missing port + "http://test", // missing port, + wrong scheme + "http://test:8540", // wrong scheme + "http://test:8541", "https://test:8541", // wrong port + "http://bad", "https://bad", "http://bad:8540", "https://bad:8540"}, + }, + // several allowed origins + { + spec: "localhost,http://127.0.0.1", + expOk: []string{"localhost", "http://localhost", "https://localhost:8443", + "http://127.0.0.1", "http://127.0.0.1:8080"}, + expFail: []string{ + "https://127.0.0.1", // wrong scheme + "http://bad", "https://bad", "http://bad:8540", "https://bad:8540"}, + }, + } + for _, tc := range tests { + srv := createAndStartServer(t, &httpConfig{}, true, &wsConfig{Origins: splitAndTrim(tc.spec)}) + url := fmt.Sprintf("ws://%v", srv.listenAddr()) + for _, origin := range tc.expOk { + if err := wsRequest(t, url, origin); err != nil { + t.Errorf("spec '%v', origin '%v': expected ok, got %v", tc.spec, origin, err) + } + } + for _, origin := range tc.expFail { + if err := wsRequest(t, url, origin); err == nil { + t.Errorf("spec '%v', origin '%v': expected not to allow, got ok", tc.spec, origin) + } + } + srv.stop() + } } // TestIsWebsocket tests if an incoming websocket upgrade request is handled properly. @@ -88,36 +174,118 @@ func TestIsWebsocket(t *testing.T) { assert.True(t, isWebsocket(r)) } -func createAndStartServer(t *testing.T, conf httpConfig, ws bool, wsConf wsConfig) *httpServer { +func Test_checkPath(t *testing.T) { + tests := []struct { + req *http.Request + prefix string + expected bool + }{ + { + req: &http.Request{URL: &url.URL{Path: "/test"}}, + prefix: "/test", + expected: true, + }, + { + req: &http.Request{URL: &url.URL{Path: "/testing"}}, + prefix: "/test", + expected: true, + }, + { + req: &http.Request{URL: &url.URL{Path: "/"}}, + prefix: "/test", + expected: false, + }, + { + req: &http.Request{URL: &url.URL{Path: "/fail"}}, + prefix: "/test", + expected: false, + }, + { + req: &http.Request{URL: &url.URL{Path: "/"}}, + prefix: "", + expected: true, + }, + { + req: &http.Request{URL: &url.URL{Path: "/fail"}}, + prefix: "", + expected: false, + }, + { + req: &http.Request{URL: &url.URL{Path: "/"}}, + prefix: "/", + expected: true, + }, + { + req: &http.Request{URL: &url.URL{Path: "/testing"}}, + prefix: "/", + expected: true, + }, + } + + for i, tt := range tests { + t.Run(strconv.Itoa(i), func(t *testing.T) { + assert.Equal(t, tt.expected, checkPath(tt.req, tt.prefix)) + }) + } +} + +func createAndStartServer(t *testing.T, conf *httpConfig, ws bool, wsConf *wsConfig) *httpServer { t.Helper() srv := newHTTPServer(testlog.Logger(t, log.LvlDebug), rpc.DefaultHTTPTimeouts) - - assert.NoError(t, srv.enableRPC(nil, conf)) + assert.NoError(t, srv.enableRPC(nil, *conf)) if ws { - assert.NoError(t, srv.enableWS(nil, wsConf)) + assert.NoError(t, srv.enableWS(nil, *wsConf)) } assert.NoError(t, srv.setListenAddr("localhost", 0)) assert.NoError(t, srv.start()) - return srv } -func testRequest(t *testing.T, key, value, host string, srv *httpServer) *http.Response { +// wsRequest attempts to open a WebSocket connection to the given URL. +func wsRequest(t *testing.T, url, browserOrigin string) error { + t.Helper() + t.Logf("checking WebSocket on %s (origin %q)", url, browserOrigin) + + headers := make(http.Header) + if browserOrigin != "" { + headers.Set("Origin", browserOrigin) + } + conn, _, err := websocket.DefaultDialer.Dial(url, headers) + if conn != nil { + conn.Close() + } + return err +} + +// rpcRequest performs a JSON-RPC request to the given URL. +func rpcRequest(t *testing.T, url string, extraHeaders ...string) *http.Response { t.Helper() - body := bytes.NewReader([]byte(`{"jsonrpc":"2.0","id":1,method":"rpc_modules"}`)) - req, _ := http.NewRequest("POST", "http://"+srv.listenAddr(), body) + // Create the request. + body := bytes.NewReader([]byte(`{"jsonrpc":"2.0","id":1,"method":"rpc_modules","params":[]}`)) + req, err := http.NewRequest("POST", url, body) + if err != nil { + t.Fatal("could not create http request:", err) + } req.Header.Set("content-type", "application/json") - if key != "" && value != "" { - req.Header.Set(key, value) + + // Apply extra headers. + if len(extraHeaders)%2 != 0 { + panic("odd extraHeaders length") } - if host != "" { - req.Host = host + for i := 0; i < len(extraHeaders); i += 2 { + key, value := extraHeaders[i], extraHeaders[i+1] + if strings.ToLower(key) == "host" { + req.Host = value + } else { + req.Header.Set(key, value) + } } - client := http.DefaultClient - resp, err := client.Do(req) + // Perform the request. + t.Logf("checking RPC/HTTP on %s %v", url, extraHeaders) + resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } diff --git a/node/utils_test.go b/node/utils_test.go index 44c83e22da..b7474bb706 100644 --- a/node/utils_test.go +++ b/node/utils_test.go @@ -82,11 +82,11 @@ func (f *FullService) Stop() error { return nil } func (f *FullService) Protocols() []p2p.Protocol { return []p2p.Protocol{ - p2p.Protocol{ + { Name: "test1", Version: uint(1), }, - p2p.Protocol{ + { Name: "test2", Version: uint(2), }, diff --git a/oss-fuzz.sh b/oss-fuzz.sh index 23fb4dd412..f8152f0fad 100644 --- a/oss-fuzz.sh +++ b/oss-fuzz.sh @@ -26,31 +26,93 @@ # $CFLAGS, $CXXFLAGS C and C++ compiler flags. # $LIB_FUZZING_ENGINE C++ compiler argument to link fuzz target against the prebuilt engine library (e.g. libFuzzer). +# This sets the -coverpgk for the coverage report when the corpus is executed through go test +coverpkg="github.com/ethereum/go-ethereum/..." + +function coverbuild { + path=$1 + function=$2 + fuzzer=$3 + tags="" + + if [[ $# -eq 4 ]]; then + tags="-tags $4" + fi + cd $path + fuzzed_package=`pwd | rev | cut -d'/' -f 1 | rev` + cp $GOPATH/ossfuzz_coverage_runner.go ./"${function,,}"_test.go + sed -i -e 's/FuzzFunction/'$function'/' ./"${function,,}"_test.go + sed -i -e 's/mypackagebeingfuzzed/'$fuzzed_package'/' ./"${function,,}"_test.go + sed -i -e 's/TestFuzzCorpus/Test'$function'Corpus/' ./"${function,,}"_test.go + +cat << DOG > $OUT/$fuzzer +#/bin/sh + + cd $OUT/$path + go test -run Test${function}Corpus -v $tags -coverprofile \$1 -coverpkg $coverpkg + +DOG + + chmod +x $OUT/$fuzzer + #echo "Built script $OUT/$fuzzer" + #cat $OUT/$fuzzer + cd - +} + function compile_fuzzer { - path=$SRC/go-ethereum/$1 + # Inputs: + # $1: The package to fuzz, within go-ethereum + # $2: The name of the fuzzing function + # $3: The name to give to the final fuzzing-binary + + path=$GOPATH/src/github.com/ethereum/go-ethereum/$1 func=$2 fuzzer=$3 + echo "Building $fuzzer" - (cd $path && \ + + # Do a coverage-build or a regular build + if [[ $SANITIZER = *coverage* ]]; then + coverbuild $path $func $fuzzer $coverpkg + else + (cd $path && \ go-fuzz -func $func -o $WORK/$fuzzer.a . && \ - echo "First stage built OK" && \ - $CXX $CXXFLAGS $LIB_FUZZING_ENGINE $WORK/$fuzzer.a -o $OUT/$fuzzer && \ - echo "Second stage built ok" ) + $CXX $CXXFLAGS $LIB_FUZZING_ENGINE $WORK/$fuzzer.a -o $OUT/$fuzzer) + fi + ## Check if there exists a seed corpus file + corpusfile="${path}/testdata/${fuzzer}_seed_corpus.zip" + if [ -f $corpusfile ] + then + cp $corpusfile $OUT/ + echo "Found seed corpus: $corpusfile" + fi } -compile_fuzzer common/bitutil Fuzz fuzzBitutilCompress -compile_fuzzer crypto/bn256 FuzzAdd fuzzBn256Add -compile_fuzzer crypto/bn256 FuzzMul fuzzBn256Mul -compile_fuzzer crypto/bn256 FuzzPair fuzzBn256Pair -compile_fuzzer core/vm/runtime Fuzz fuzzVmRuntime -compile_fuzzer crypto/blake2b Fuzz fuzzBlake2b +compile_fuzzer tests/fuzzers/bitutil Fuzz fuzzBitutilCompress +compile_fuzzer tests/fuzzers/bn256 FuzzAdd fuzzBn256Add +compile_fuzzer tests/fuzzers/bn256 FuzzMul fuzzBn256Mul +compile_fuzzer tests/fuzzers/bn256 FuzzPair fuzzBn256Pair +compile_fuzzer tests/fuzzers/runtime Fuzz fuzzVmRuntime compile_fuzzer tests/fuzzers/keystore Fuzz fuzzKeystore compile_fuzzer tests/fuzzers/txfetcher Fuzz fuzzTxfetcher compile_fuzzer tests/fuzzers/rlp Fuzz fuzzRlp compile_fuzzer tests/fuzzers/trie Fuzz fuzzTrie compile_fuzzer tests/fuzzers/stacktrie Fuzz fuzzStackTrie +compile_fuzzer tests/fuzzers/difficulty Fuzz fuzzDifficulty +compile_fuzzer tests/fuzzers/abi Fuzz fuzzAbi +compile_fuzzer tests/fuzzers/les Fuzz fuzzLes +compile_fuzzer tests/fuzzers/vflux FuzzClientPool fuzzClientPool -# This doesn't work very well @TODO -#compile_fuzzertests/fuzzers/abi Fuzz fuzzAbi +compile_fuzzer tests/fuzzers/bls12381 FuzzG1Add fuzz_g1_add +compile_fuzzer tests/fuzzers/bls12381 FuzzG1Mul fuzz_g1_mul +compile_fuzzer tests/fuzzers/bls12381 FuzzG1MultiExp fuzz_g1_multiexp +compile_fuzzer tests/fuzzers/bls12381 FuzzG2Add fuzz_g2_add +compile_fuzzer tests/fuzzers/bls12381 FuzzG2Mul fuzz_g2_mul +compile_fuzzer tests/fuzzers/bls12381 FuzzG2MultiExp fuzz_g2_multiexp +compile_fuzzer tests/fuzzers/bls12381 FuzzPairing fuzz_pairing +compile_fuzzer tests/fuzzers/bls12381 FuzzMapG1 fuzz_map_g1 +compile_fuzzer tests/fuzzers/bls12381 FuzzMapG2 fuzz_map_g2 +#TODO: move this to tests/fuzzers, if possible +compile_fuzzer crypto/blake2b Fuzz fuzzBlake2b diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go index c95317a005..eb01d95e93 100644 --- a/p2p/discover/v5_udp.go +++ b/p2p/discover/v5_udp.go @@ -74,7 +74,7 @@ type UDPv5 struct { // talkreq handler registry trlock sync.Mutex - trhandlers map[string]func([]byte) []byte + trhandlers map[string]TalkRequestHandler // channels into dispatch packetInCh chan ReadPacket @@ -96,6 +96,9 @@ type UDPv5 struct { wg sync.WaitGroup } +// TalkRequestHandler callback processes a talk request and optionally returns a reply +type TalkRequestHandler func(enode.ID, *net.UDPAddr, []byte) []byte + // callV5 represents a remote procedure call against another node. type callV5 struct { node *enode.Node @@ -145,7 +148,7 @@ func newUDPv5(conn UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv5, error) { log: cfg.Log, validSchemes: cfg.ValidSchemes, clock: cfg.Clock, - trhandlers: make(map[string]func([]byte) []byte), + trhandlers: make(map[string]TalkRequestHandler), // channels into dispatch packetInCh: make(chan ReadPacket, 1), readNextCh: make(chan struct{}, 1), @@ -233,7 +236,7 @@ func (t *UDPv5) LocalNode() *enode.LocalNode { // RegisterTalkHandler adds a handler for 'talk requests'. The handler function is called // whenever a request for the given protocol is received and should return the response // data or nil. -func (t *UDPv5) RegisterTalkHandler(protocol string, handler func([]byte) []byte) { +func (t *UDPv5) RegisterTalkHandler(protocol string, handler TalkRequestHandler) { t.trlock.Lock() defer t.trlock.Unlock() t.trhandlers[protocol] = handler @@ -454,9 +457,20 @@ func (t *UDPv5) call(node *enode.Node, responseType byte, packet v5wire.Packet) // callDone tells dispatch that the active call is done. func (t *UDPv5) callDone(c *callV5) { - select { - case t.callDoneCh <- c: - case <-t.closeCtx.Done(): + // This needs a loop because further responses may be incoming until the + // send to callDoneCh has completed. Such responses need to be discarded + // in order to avoid blocking the dispatch loop. + for { + select { + case <-c.ch: + // late response, discard. + case <-c.err: + // late error, discard. + case t.callDoneCh <- c: + return + case <-t.closeCtx.Done(): + return + } } } @@ -830,7 +844,7 @@ func (t *UDPv5) handleTalkRequest(p *v5wire.TalkRequest, fromID enode.ID, fromAd var response []byte if handler != nil { - response = handler(p.Message) + response = handler(fromID, fromAddr, p.Message) } resp := &v5wire.TalkResponse{ReqID: p.ReqID, Message: response} t.sendResponse(fromID, fromAddr, resp) diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go index d91a2097db..292785bd51 100644 --- a/p2p/discover/v5_udp_test.go +++ b/p2p/discover/v5_udp_test.go @@ -435,7 +435,7 @@ func TestUDPv5_talkHandling(t *testing.T) { defer test.close() var recvMessage []byte - test.udp.RegisterTalkHandler("test", func(message []byte) []byte { + test.udp.RegisterTalkHandler("test", func(id enode.ID, addr *net.UDPAddr, message []byte) []byte { recvMessage = message return []byte("test response") }) diff --git a/p2p/discv5/README b/p2p/discv5/README deleted file mode 100644 index 617a473d7f..0000000000 --- a/p2p/discv5/README +++ /dev/null @@ -1,4 +0,0 @@ -This package is an early prototype of Discovery v5. Do not use this code. - -See https://github.com/ethereum/devp2p/blob/master/discv5/discv5.md for the -current Discovery v5 specification. \ No newline at end of file diff --git a/p2p/discv5/database.go b/p2p/discv5/database.go deleted file mode 100644 index ca118e7f80..0000000000 --- a/p2p/discv5/database.go +++ /dev/null @@ -1,396 +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 . - -// Contains the node database, storing previously seen nodes and any collected -// metadata about them for QoS purposes. - -package discv5 - -import ( - "bytes" - "crypto/rand" - "encoding/binary" - "fmt" - "os" - "sync" - "time" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rlp" - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/errors" - "github.com/syndtr/goleveldb/leveldb/iterator" - "github.com/syndtr/goleveldb/leveldb/opt" - "github.com/syndtr/goleveldb/leveldb/storage" - "github.com/syndtr/goleveldb/leveldb/util" -) - -var ( - nodeDBNilNodeID = NodeID{} // Special node ID to use as a nil element. - nodeDBNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped. - nodeDBCleanupCycle = time.Hour // Time period for running the expiration task. -) - -// nodeDB stores all nodes we know about. -type nodeDB struct { - lvl *leveldb.DB // Interface to the database itself - self NodeID // Own node id to prevent adding it into the database - runner sync.Once // Ensures we can start at most one expirer - quit chan struct{} // Channel to signal the expiring thread to stop -} - -// Schema layout for the node database -var ( - nodeDBVersionKey = []byte("version") // Version of the database to flush if changes - nodeDBItemPrefix = []byte("n:") // Identifier to prefix node entries with - - nodeDBDiscoverRoot = ":discover" - nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping" - nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong" - nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail" - nodeDBTopicRegTickets = ":tickets" -) - -// newNodeDB creates a new node database for storing and retrieving infos about -// known peers in the network. If no path is given, an in-memory, temporary -// database is constructed. -func newNodeDB(path string, version int, self NodeID) (*nodeDB, error) { - if path == "" { - return newMemoryNodeDB(self) - } - return newPersistentNodeDB(path, version, self) -} - -// newMemoryNodeDB creates a new in-memory node database without a persistent -// backend. -func newMemoryNodeDB(self NodeID) (*nodeDB, error) { - db, err := leveldb.Open(storage.NewMemStorage(), nil) - if err != nil { - return nil, err - } - return &nodeDB{ - lvl: db, - self: self, - quit: make(chan struct{}), - }, nil -} - -// newPersistentNodeDB creates/opens a leveldb backed persistent node database, -// also flushing its contents in case of a version mismatch. -func newPersistentNodeDB(path string, version int, self NodeID) (*nodeDB, error) { - opts := &opt.Options{OpenFilesCacheCapacity: 5} - db, err := leveldb.OpenFile(path, opts) - if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted { - db, err = leveldb.RecoverFile(path, nil) - } - if err != nil { - return nil, err - } - // The nodes contained in the cache correspond to a certain protocol version. - // Flush all nodes if the version doesn't match. - currentVer := make([]byte, binary.MaxVarintLen64) - currentVer = currentVer[:binary.PutVarint(currentVer, int64(version))] - - blob, err := db.Get(nodeDBVersionKey, nil) - switch err { - case leveldb.ErrNotFound: - // Version not found (i.e. empty cache), insert it - if err := db.Put(nodeDBVersionKey, currentVer, nil); err != nil { - db.Close() - return nil, err - } - - case nil: - // Version present, flush if different - if !bytes.Equal(blob, currentVer) { - db.Close() - if err = os.RemoveAll(path); err != nil { - return nil, err - } - return newPersistentNodeDB(path, version, self) - } - } - return &nodeDB{ - lvl: db, - self: self, - quit: make(chan struct{}), - }, nil -} - -// makeKey generates the leveldb key-blob from a node id and its particular -// field of interest. -func makeKey(id NodeID, field string) []byte { - if bytes.Equal(id[:], nodeDBNilNodeID[:]) { - return []byte(field) - } - return append(nodeDBItemPrefix, append(id[:], field...)...) -} - -// splitKey tries to split a database key into a node id and a field part. -func splitKey(key []byte) (id NodeID, field string) { - // If the key is not of a node, return it plainly - if !bytes.HasPrefix(key, nodeDBItemPrefix) { - return NodeID{}, string(key) - } - // Otherwise split the id and field - item := key[len(nodeDBItemPrefix):] - copy(id[:], item[:len(id)]) - field = string(item[len(id):]) - - return id, field -} - -// fetchInt64 retrieves an integer instance associated with a particular -// database key. -func (db *nodeDB) fetchInt64(key []byte) int64 { - blob, err := db.lvl.Get(key, nil) - if err != nil { - return 0 - } - val, read := binary.Varint(blob) - if read <= 0 { - return 0 - } - return val -} - -// storeInt64 update a specific database entry to the current time instance as a -// unix timestamp. -func (db *nodeDB) storeInt64(key []byte, n int64) error { - blob := make([]byte, binary.MaxVarintLen64) - blob = blob[:binary.PutVarint(blob, n)] - return db.lvl.Put(key, blob, nil) -} - -func (db *nodeDB) storeRLP(key []byte, val interface{}) error { - blob, err := rlp.EncodeToBytes(val) - if err != nil { - return err - } - return db.lvl.Put(key, blob, nil) -} - -func (db *nodeDB) fetchRLP(key []byte, val interface{}) error { - blob, err := db.lvl.Get(key, nil) - if err != nil { - return err - } - err = rlp.DecodeBytes(blob, val) - if err != nil { - log.Warn(fmt.Sprintf("key %x (%T) %v", key, val, err)) - } - return err -} - -// node retrieves a node with a given id from the database. -func (db *nodeDB) node(id NodeID) *Node { - var node Node - if err := db.fetchRLP(makeKey(id, nodeDBDiscoverRoot), &node); err != nil { - return nil - } - node.sha = crypto.Keccak256Hash(node.ID[:]) - return &node -} - -// updateNode inserts - potentially overwriting - a node into the peer database. -func (db *nodeDB) updateNode(node *Node) error { - return db.storeRLP(makeKey(node.ID, nodeDBDiscoverRoot), node) -} - -// deleteNode deletes all information/keys associated with a node. -func (db *nodeDB) deleteNode(id NodeID) error { - deleter := db.lvl.NewIterator(util.BytesPrefix(makeKey(id, "")), nil) - for deleter.Next() { - if err := db.lvl.Delete(deleter.Key(), nil); err != nil { - return err - } - } - return nil -} - -// ensureExpirer is a small helper method ensuring that the data expiration -// mechanism is running. If the expiration goroutine is already running, this -// method simply returns. -// -// The goal is to start the data evacuation only after the network successfully -// bootstrapped itself (to prevent dumping potentially useful seed nodes). Since -// it would require significant overhead to exactly trace the first successful -// convergence, it's simpler to "ensure" the correct state when an appropriate -// condition occurs (i.e. a successful bonding), and discard further events. -func (db *nodeDB) ensureExpirer() { - db.runner.Do(func() { go db.expirer() }) -} - -// expirer should be started in a go routine, and is responsible for looping ad -// infinitum and dropping stale data from the database. -func (db *nodeDB) expirer() { - tick := time.NewTicker(nodeDBCleanupCycle) - defer tick.Stop() - for { - select { - case <-tick.C: - if err := db.expireNodes(); err != nil { - log.Error(fmt.Sprintf("Failed to expire nodedb items: %v", err)) - } - case <-db.quit: - return - } - } -} - -// expireNodes iterates over the database and deletes all nodes that have not -// been seen (i.e. received a pong from) for some allotted time. -func (db *nodeDB) expireNodes() error { - threshold := time.Now().Add(-nodeDBNodeExpiration) - - // Find discovered nodes that are older than the allowance - it := db.lvl.NewIterator(nil, nil) - defer it.Release() - - for it.Next() { - // Skip the item if not a discovery node - id, field := splitKey(it.Key()) - if field != nodeDBDiscoverRoot { - continue - } - // Skip the node if not expired yet (and not self) - if !bytes.Equal(id[:], db.self[:]) { - if seen := db.lastPong(id); seen.After(threshold) { - continue - } - } - // Otherwise delete all associated information - db.deleteNode(id) - } - return nil -} - -// lastPing retrieves the time of the last ping packet send to a remote node, -// requesting binding. -func (db *nodeDB) lastPing(id NodeID) time.Time { - return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPing)), 0) -} - -// updateLastPing updates the last time we tried contacting a remote node. -func (db *nodeDB) updateLastPing(id NodeID, instance time.Time) error { - return db.storeInt64(makeKey(id, nodeDBDiscoverPing), instance.Unix()) -} - -// lastPong retrieves the time of the last successful contact from remote node. -func (db *nodeDB) lastPong(id NodeID) time.Time { - return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPong)), 0) -} - -// updateLastPong updates the last time a remote node successfully contacted. -func (db *nodeDB) updateLastPong(id NodeID, instance time.Time) error { - return db.storeInt64(makeKey(id, nodeDBDiscoverPong), instance.Unix()) -} - -// findFails retrieves the number of findnode failures since bonding. -func (db *nodeDB) findFails(id NodeID) int { - return int(db.fetchInt64(makeKey(id, nodeDBDiscoverFindFails))) -} - -// updateFindFails updates the number of findnode failures since bonding. -func (db *nodeDB) updateFindFails(id NodeID, fails int) error { - return db.storeInt64(makeKey(id, nodeDBDiscoverFindFails), int64(fails)) -} - -// querySeeds retrieves random nodes to be used as potential seed nodes -// for bootstrapping. -func (db *nodeDB) querySeeds(n int, maxAge time.Duration) []*Node { - var ( - now = time.Now() - nodes = make([]*Node, 0, n) - it = db.lvl.NewIterator(nil, nil) - id NodeID - ) - defer it.Release() - -seek: - for seeks := 0; len(nodes) < n && seeks < n*5; seeks++ { - // Seek to a random entry. The first byte is incremented by a - // random amount each time in order to increase the likelihood - // of hitting all existing nodes in very small databases. - ctr := id[0] - rand.Read(id[:]) - id[0] = ctr + id[0]%16 - it.Seek(makeKey(id, nodeDBDiscoverRoot)) - - n := nextNode(it) - if n == nil { - id[0] = 0 - continue seek // iterator exhausted - } - if n.ID == db.self { - continue seek - } - if now.Sub(db.lastPong(n.ID)) > maxAge { - continue seek - } - for i := range nodes { - if nodes[i].ID == n.ID { - continue seek // duplicate - } - } - nodes = append(nodes, n) - } - return nodes -} - -func (db *nodeDB) fetchTopicRegTickets(id NodeID) (issued, used uint32) { - key := makeKey(id, nodeDBTopicRegTickets) - blob, _ := db.lvl.Get(key, nil) - if len(blob) != 8 { - return 0, 0 - } - issued = binary.BigEndian.Uint32(blob[0:4]) - used = binary.BigEndian.Uint32(blob[4:8]) - return -} - -func (db *nodeDB) updateTopicRegTickets(id NodeID, issued, used uint32) error { - key := makeKey(id, nodeDBTopicRegTickets) - blob := make([]byte, 8) - binary.BigEndian.PutUint32(blob[0:4], issued) - binary.BigEndian.PutUint32(blob[4:8], used) - return db.lvl.Put(key, blob, nil) -} - -// reads the next node record from the iterator, skipping over other -// database entries. -func nextNode(it iterator.Iterator) *Node { - for end := false; !end; end = !it.Next() { - id, field := splitKey(it.Key()) - if field != nodeDBDiscoverRoot { - continue - } - var n Node - if err := rlp.DecodeBytes(it.Value(), &n); err != nil { - log.Warn(fmt.Sprintf("invalid node %x: %v", id, err)) - continue - } - return &n - } - return nil -} - -// close flushes and closes the database files. -func (db *nodeDB) close() { - close(db.quit) - db.lvl.Close() -} diff --git a/p2p/discv5/database_test.go b/p2p/discv5/database_test.go deleted file mode 100644 index 2b86dc9cec..0000000000 --- a/p2p/discv5/database_test.go +++ /dev/null @@ -1,380 +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 . - -package discv5 - -import ( - "bytes" - "io/ioutil" - "net" - "os" - "path/filepath" - "reflect" - "testing" - "time" -) - -var nodeDBKeyTests = []struct { - id NodeID - field string - key []byte -}{ - { - id: NodeID{}, - field: "version", - key: []byte{0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e}, // field - }, - { - id: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - field: ":discover", - key: []byte{0x6e, 0x3a, // prefix - 0x1d, 0xd9, 0xd6, 0x5c, 0x45, 0x52, 0xb5, 0xeb, // node id - 0x43, 0xd5, 0xad, 0x55, 0xa2, 0xee, 0x3f, 0x56, // - 0xc6, 0xcb, 0xc1, 0xc6, 0x4a, 0x5c, 0x8d, 0x65, // - 0x9f, 0x51, 0xfc, 0xd5, 0x1b, 0xac, 0xe2, 0x43, // - 0x51, 0x23, 0x2b, 0x8d, 0x78, 0x21, 0x61, 0x7d, // - 0x2b, 0x29, 0xb5, 0x4b, 0x81, 0xcd, 0xef, 0xb9, // - 0xb3, 0xe9, 0xc3, 0x7d, 0x7f, 0xd5, 0xf6, 0x32, // - 0x70, 0xbc, 0xc9, 0xe1, 0xa6, 0xf6, 0xa4, 0x39, // - 0x3a, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, // field - }, - }, -} - -func TestNodeDBKeys(t *testing.T) { - for i, tt := range nodeDBKeyTests { - if key := makeKey(tt.id, tt.field); !bytes.Equal(key, tt.key) { - t.Errorf("make test %d: key mismatch: have 0x%x, want 0x%x", i, key, tt.key) - } - id, field := splitKey(tt.key) - if !bytes.Equal(id[:], tt.id[:]) { - t.Errorf("split test %d: id mismatch: have 0x%x, want 0x%x", i, id, tt.id) - } - if field != tt.field { - t.Errorf("split test %d: field mismatch: have 0x%x, want 0x%x", i, field, tt.field) - } - } -} - -var nodeDBInt64Tests = []struct { - key []byte - value int64 -}{ - {key: []byte{0x01}, value: 1}, - {key: []byte{0x02}, value: 2}, - {key: []byte{0x03}, value: 3}, -} - -func TestNodeDBInt64(t *testing.T) { - db, _ := newNodeDB("", Version, NodeID{}) - defer db.close() - - tests := nodeDBInt64Tests - for i := 0; i < len(tests); i++ { - // Insert the next value - if err := db.storeInt64(tests[i].key, tests[i].value); err != nil { - t.Errorf("test %d: failed to store value: %v", i, err) - } - // Check all existing and non existing values - for j := 0; j < len(tests); j++ { - num := db.fetchInt64(tests[j].key) - switch { - case j <= i && num != tests[j].value: - t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, tests[j].value) - case j > i && num != 0: - t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, 0) - } - } - } -} - -func TestNodeDBFetchStore(t *testing.T) { - node := NewNode( - MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - net.IP{192, 168, 0, 1}, - 30303, - 30303, - ) - inst := time.Now() - num := 314 - - db, _ := newNodeDB("", Version, NodeID{}) - defer db.close() - - // Check fetch/store operations on a node ping object - if stored := db.lastPing(node.ID); stored.Unix() != 0 { - t.Errorf("ping: non-existing object: %v", stored) - } - if err := db.updateLastPing(node.ID, inst); err != nil { - t.Errorf("ping: failed to update: %v", err) - } - if stored := db.lastPing(node.ID); stored.Unix() != inst.Unix() { - t.Errorf("ping: value mismatch: have %v, want %v", stored, inst) - } - // Check fetch/store operations on a node pong object - if stored := db.lastPong(node.ID); stored.Unix() != 0 { - t.Errorf("pong: non-existing object: %v", stored) - } - if err := db.updateLastPong(node.ID, inst); err != nil { - t.Errorf("pong: failed to update: %v", err) - } - if stored := db.lastPong(node.ID); stored.Unix() != inst.Unix() { - t.Errorf("pong: value mismatch: have %v, want %v", stored, inst) - } - // Check fetch/store operations on a node findnode-failure object - if stored := db.findFails(node.ID); stored != 0 { - t.Errorf("find-node fails: non-existing object: %v", stored) - } - if err := db.updateFindFails(node.ID, num); err != nil { - t.Errorf("find-node fails: failed to update: %v", err) - } - if stored := db.findFails(node.ID); stored != num { - t.Errorf("find-node fails: value mismatch: have %v, want %v", stored, num) - } - // Check fetch/store operations on an actual node object - if stored := db.node(node.ID); stored != nil { - t.Errorf("node: non-existing object: %v", stored) - } - if err := db.updateNode(node); err != nil { - t.Errorf("node: failed to update: %v", err) - } - if stored := db.node(node.ID); stored == nil { - t.Errorf("node: not found") - } else if !reflect.DeepEqual(stored, node) { - t.Errorf("node: data mismatch: have %v, want %v", stored, node) - } -} - -var nodeDBSeedQueryNodes = []struct { - node *Node - pong time.Time -}{ - // This one should not be in the result set because its last - // pong time is too far in the past. - { - node: NewNode( - MustHexID("0x84d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - net.IP{127, 0, 0, 3}, - 30303, - 30303, - ), - pong: time.Now().Add(-3 * time.Hour), - }, - // This one shouldn't be in the result set because its - // nodeID is the local node's ID. - { - node: NewNode( - MustHexID("0x57d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - net.IP{127, 0, 0, 3}, - 30303, - 30303, - ), - pong: time.Now().Add(-4 * time.Second), - }, - - // These should be in the result set. - { - node: NewNode( - MustHexID("0x22d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - net.IP{127, 0, 0, 1}, - 30303, - 30303, - ), - pong: time.Now().Add(-2 * time.Second), - }, - { - node: NewNode( - MustHexID("0x44d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - net.IP{127, 0, 0, 2}, - 30303, - 30303, - ), - pong: time.Now().Add(-3 * time.Second), - }, - { - node: NewNode( - MustHexID("0xe2d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - net.IP{127, 0, 0, 3}, - 30303, - 30303, - ), - pong: time.Now().Add(-1 * time.Second), - }, -} - -func TestNodeDBSeedQuery(t *testing.T) { - db, _ := newNodeDB("", Version, nodeDBSeedQueryNodes[1].node.ID) - defer db.close() - - // Insert a batch of nodes for querying - for i, seed := range nodeDBSeedQueryNodes { - if err := db.updateNode(seed.node); err != nil { - t.Fatalf("node %d: failed to insert: %v", i, err) - } - if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil { - t.Fatalf("node %d: failed to insert lastPong: %v", i, err) - } - } - - // Retrieve the entire batch and check for duplicates - seeds := db.querySeeds(len(nodeDBSeedQueryNodes)*2, time.Hour) - have := make(map[NodeID]struct{}) - for _, seed := range seeds { - have[seed.ID] = struct{}{} - } - want := make(map[NodeID]struct{}) - for _, seed := range nodeDBSeedQueryNodes[2:] { - want[seed.node.ID] = struct{}{} - } - if len(seeds) != len(want) { - t.Errorf("seed count mismatch: have %v, want %v", len(seeds), len(want)) - } - for id := range have { - if _, ok := want[id]; !ok { - t.Errorf("extra seed: %v", id) - } - } - for id := range want { - if _, ok := have[id]; !ok { - t.Errorf("missing seed: %v", id) - } - } -} - -func TestNodeDBPersistency(t *testing.T) { - root, err := ioutil.TempDir("", "nodedb-") - if err != nil { - t.Fatalf("failed to create temporary data folder: %v", err) - } - defer os.RemoveAll(root) - - var ( - testKey = []byte("somekey") - testInt = int64(314) - ) - - // Create a persistent database and store some values - db, err := newNodeDB(filepath.Join(root, "database"), Version, NodeID{}) - if err != nil { - t.Fatalf("failed to create persistent database: %v", err) - } - if err := db.storeInt64(testKey, testInt); err != nil { - t.Fatalf("failed to store value: %v.", err) - } - db.close() - - // Reopen the database and check the value - db, err = newNodeDB(filepath.Join(root, "database"), Version, NodeID{}) - if err != nil { - t.Fatalf("failed to open persistent database: %v", err) - } - if val := db.fetchInt64(testKey); val != testInt { - t.Fatalf("value mismatch: have %v, want %v", val, testInt) - } - db.close() - - // Change the database version and check flush - db, err = newNodeDB(filepath.Join(root, "database"), Version+1, NodeID{}) - if err != nil { - t.Fatalf("failed to open persistent database: %v", err) - } - if val := db.fetchInt64(testKey); val != 0 { - t.Fatalf("value mismatch: have %v, want %v", val, 0) - } - db.close() -} - -var nodeDBExpirationNodes = []struct { - node *Node - pong time.Time - exp bool -}{ - { - node: NewNode( - MustHexID("0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - net.IP{127, 0, 0, 1}, - 30303, - 30303, - ), - pong: time.Now().Add(-nodeDBNodeExpiration + time.Minute), - exp: false, - }, { - node: NewNode( - MustHexID("0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - net.IP{127, 0, 0, 2}, - 30303, - 30303, - ), - pong: time.Now().Add(-nodeDBNodeExpiration - time.Minute), - exp: true, - }, -} - -func TestNodeDBExpiration(t *testing.T) { - db, _ := newNodeDB("", Version, NodeID{}) - defer db.close() - - // Add all the test nodes and set their last pong time - for i, seed := range nodeDBExpirationNodes { - if err := db.updateNode(seed.node); err != nil { - t.Fatalf("node %d: failed to insert: %v", i, err) - } - if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil { - t.Fatalf("node %d: failed to update pong: %v", i, err) - } - } - // Expire some of them, and check the rest - if err := db.expireNodes(); err != nil { - t.Fatalf("failed to expire nodes: %v", err) - } - for i, seed := range nodeDBExpirationNodes { - node := db.node(seed.node.ID) - if (node == nil && !seed.exp) || (node != nil && seed.exp) { - t.Errorf("node %d: expiration mismatch: have %v, want %v", i, node, seed.exp) - } - } -} - -func TestNodeDBSelfExpiration(t *testing.T) { - // Find a node in the tests that shouldn't expire, and assign it as self - var self NodeID - for _, node := range nodeDBExpirationNodes { - if !node.exp { - self = node.node.ID - break - } - } - db, _ := newNodeDB("", Version, self) - defer db.close() - - // Add all the test nodes and set their last pong time - for i, seed := range nodeDBExpirationNodes { - if err := db.updateNode(seed.node); err != nil { - t.Fatalf("node %d: failed to insert: %v", i, err) - } - if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil { - t.Fatalf("node %d: failed to update pong: %v", i, err) - } - } - // Expire the nodes and make sure self has been evacuated too - if err := db.expireNodes(); err != nil { - t.Fatalf("failed to expire nodes: %v", err) - } - node := db.node(self) - if node != nil { - t.Errorf("self not evacuated") - } -} diff --git a/p2p/discv5/net.go b/p2p/discv5/net.go deleted file mode 100644 index 53e00a3881..0000000000 --- a/p2p/discv5/net.go +++ /dev/null @@ -1,1269 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package discv5 - -import ( - "bytes" - "crypto/ecdsa" - "errors" - "fmt" - "net" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p/netutil" - "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/crypto/sha3" -) - -var ( - errInvalidEvent = errors.New("invalid in current state") - errNoQuery = errors.New("no pending query") -) - -const ( - autoRefreshInterval = 1 * time.Hour - bucketRefreshInterval = 1 * time.Minute - seedCount = 30 - seedMaxAge = 5 * 24 * time.Hour - lowPort = 1024 -) - -const testTopic = "foo" - -const ( - printTestImgLogs = false -) - -// Network manages the table and all protocol interaction. -type Network struct { - db *nodeDB // database of known nodes - conn transport - netrestrict *netutil.Netlist - - closed chan struct{} // closed when loop is done - closeReq chan struct{} // 'request to close' - refreshReq chan []*Node // lookups ask for refresh on this channel - refreshResp chan (<-chan struct{}) // ...and get the channel to block on from this one - read chan ingressPacket // ingress packets arrive here - timeout chan timeoutEvent - queryReq chan *findnodeQuery // lookups submit findnode queries on this channel - tableOpReq chan func() - tableOpResp chan struct{} - topicRegisterReq chan topicRegisterReq - topicSearchReq chan topicSearchReq - - // State of the main loop. - tab *Table - topictab *topicTable - ticketStore *ticketStore - nursery []*Node - nodes map[NodeID]*Node // tracks active nodes with state != known - timeoutTimers map[timeoutEvent]*time.Timer -} - -// transport is implemented by the UDP transport. -// it is an interface so we can test without opening lots of UDP -// sockets and without generating a private key. -type transport interface { - sendPing(remote *Node, remoteAddr *net.UDPAddr, topics []Topic) (hash []byte) - sendNeighbours(remote *Node, nodes []*Node) - sendFindnodeHash(remote *Node, target common.Hash) - sendTopicRegister(remote *Node, topics []Topic, topicIdx int, pong []byte) - sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node) - - send(remote *Node, ptype nodeEvent, p interface{}) (hash []byte) - - localAddr() *net.UDPAddr - Close() -} - -type findnodeQuery struct { - remote *Node - target common.Hash - reply chan<- []*Node -} - -type topicRegisterReq struct { - add bool - topic Topic -} - -type topicSearchReq struct { - topic Topic - found chan<- *Node - lookup chan<- bool - delay time.Duration -} - -type topicSearchResult struct { - target lookupInfo - nodes []*Node -} - -type timeoutEvent struct { - ev nodeEvent - node *Node -} - -func newNetwork(conn transport, ourPubkey ecdsa.PublicKey, dbPath string, netrestrict *netutil.Netlist) (*Network, error) { - ourID := PubkeyID(&ourPubkey) - - var db *nodeDB - if dbPath != "" { - var err error - if db, err = newNodeDB(dbPath, Version, ourID); err != nil { - return nil, err - } - } - - tab := newTable(ourID, conn.localAddr()) - net := &Network{ - db: db, - conn: conn, - netrestrict: netrestrict, - tab: tab, - topictab: newTopicTable(db, tab.self), - ticketStore: newTicketStore(), - refreshReq: make(chan []*Node), - refreshResp: make(chan (<-chan struct{})), - closed: make(chan struct{}), - closeReq: make(chan struct{}), - read: make(chan ingressPacket, 100), - timeout: make(chan timeoutEvent), - timeoutTimers: make(map[timeoutEvent]*time.Timer), - tableOpReq: make(chan func()), - tableOpResp: make(chan struct{}), - queryReq: make(chan *findnodeQuery), - topicRegisterReq: make(chan topicRegisterReq), - topicSearchReq: make(chan topicSearchReq), - nodes: make(map[NodeID]*Node), - } - go net.loop() - return net, nil -} - -// Close terminates the network listener and flushes the node database. -func (net *Network) Close() { - net.conn.Close() - select { - case <-net.closed: - case net.closeReq <- struct{}{}: - <-net.closed - } -} - -// Self returns the local node. -// The returned node should not be modified by the caller. -func (net *Network) Self() *Node { - return net.tab.self -} - -// ReadRandomNodes fills the given slice with random nodes from the -// table. It will not write the same node more than once. The nodes in -// the slice are copies and can be modified by the caller. -func (net *Network) ReadRandomNodes(buf []*Node) (n int) { - net.reqTableOp(func() { n = net.tab.readRandomNodes(buf) }) - return n -} - -// SetFallbackNodes sets the initial points of contact. These nodes -// are used to connect to the network if the table is empty and there -// are no known nodes in the database. -func (net *Network) SetFallbackNodes(nodes []*Node) error { - nursery := make([]*Node, 0, len(nodes)) - for _, n := range nodes { - if err := n.validateComplete(); err != nil { - return fmt.Errorf("bad bootstrap/fallback node %q (%v)", n, err) - } - // Recompute cpy.sha because the node might not have been - // created by NewNode or ParseNode. - cpy := *n - cpy.sha = crypto.Keccak256Hash(n.ID[:]) - nursery = append(nursery, &cpy) - } - net.reqRefresh(nursery) - return nil -} - -// Resolve searches for a specific node with the given ID. -// It returns nil if the node could not be found. -func (net *Network) Resolve(targetID NodeID) *Node { - result := net.lookup(crypto.Keccak256Hash(targetID[:]), true) - for _, n := range result { - if n.ID == targetID { - return n - } - } - return nil -} - -// Lookup performs a network search for nodes close -// to the given target. It approaches the target by querying -// nodes that are closer to it on each iteration. -// The given target does not need to be an actual node -// identifier. -// -// The local node may be included in the result. -func (net *Network) Lookup(targetID NodeID) []*Node { - return net.lookup(crypto.Keccak256Hash(targetID[:]), false) -} - -func (net *Network) lookup(target common.Hash, stopOnMatch bool) []*Node { - var ( - asked = make(map[NodeID]bool) - seen = make(map[NodeID]bool) - reply = make(chan []*Node, alpha) - result = nodesByDistance{target: target} - pendingQueries = 0 - ) - // Get initial answers from the local node. - result.push(net.tab.self, bucketSize) - for { - // Ask the α closest nodes that we haven't asked yet. - for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ { - n := result.entries[i] - if !asked[n.ID] { - asked[n.ID] = true - pendingQueries++ - net.reqQueryFindnode(n, target, reply) - } - } - if pendingQueries == 0 { - // We have asked all closest nodes, stop the search. - break - } - // Wait for the next reply. - select { - case nodes := <-reply: - for _, n := range nodes { - if n != nil && !seen[n.ID] { - seen[n.ID] = true - result.push(n, bucketSize) - if stopOnMatch && n.sha == target { - return result.entries - } - } - } - pendingQueries-- - case <-time.After(respTimeout): - // forget all pending requests, start new ones - pendingQueries = 0 - reply = make(chan []*Node, alpha) - } - } - return result.entries -} - -func (net *Network) RegisterTopic(topic Topic, stop <-chan struct{}) { - select { - case net.topicRegisterReq <- topicRegisterReq{true, topic}: - case <-net.closed: - return - } - select { - case <-net.closed: - case <-stop: - select { - case net.topicRegisterReq <- topicRegisterReq{false, topic}: - case <-net.closed: - } - } -} - -func (net *Network) SearchTopic(topic Topic, setPeriod <-chan time.Duration, found chan<- *Node, lookup chan<- bool) { - for { - select { - case <-net.closed: - return - case delay, ok := <-setPeriod: - select { - case net.topicSearchReq <- topicSearchReq{topic: topic, found: found, lookup: lookup, delay: delay}: - case <-net.closed: - return - } - if !ok { - return - } - } - } -} - -func (net *Network) reqRefresh(nursery []*Node) <-chan struct{} { - select { - case net.refreshReq <- nursery: - return <-net.refreshResp - case <-net.closed: - return net.closed - } -} - -func (net *Network) reqQueryFindnode(n *Node, target common.Hash, reply chan []*Node) bool { - q := &findnodeQuery{remote: n, target: target, reply: reply} - select { - case net.queryReq <- q: - return true - case <-net.closed: - return false - } -} - -func (net *Network) reqReadPacket(pkt ingressPacket) { - select { - case net.read <- pkt: - case <-net.closed: - } -} - -func (net *Network) reqTableOp(f func()) (called bool) { - select { - case net.tableOpReq <- f: - <-net.tableOpResp - return true - case <-net.closed: - return false - } -} - -// TODO: external address handling. - -type topicSearchInfo struct { - lookupChn chan<- bool - period time.Duration -} - -const maxSearchCount = 5 - -func (net *Network) loop() { - var ( - refreshTimer = time.NewTicker(autoRefreshInterval) - bucketRefreshTimer = time.NewTimer(bucketRefreshInterval) - refreshDone chan struct{} // closed when the 'refresh' lookup has ended - ) - defer refreshTimer.Stop() - defer bucketRefreshTimer.Stop() - - // Tracking the next ticket to register. - var ( - nextTicket *ticketRef - nextRegisterTimer *time.Timer - nextRegisterTime <-chan time.Time - ) - defer func() { - if nextRegisterTimer != nil { - nextRegisterTimer.Stop() - } - }() - resetNextTicket := func() { - ticket, timeout := net.ticketStore.nextFilteredTicket() - if nextTicket != ticket { - nextTicket = ticket - if nextRegisterTimer != nil { - nextRegisterTimer.Stop() - nextRegisterTime = nil - } - if ticket != nil { - nextRegisterTimer = time.NewTimer(timeout) - nextRegisterTime = nextRegisterTimer.C - } - } - } - - // Tracking registration and search lookups. - var ( - topicRegisterLookupTarget lookupInfo - topicRegisterLookupDone chan []*Node - topicRegisterLookupTick = time.NewTimer(0) - searchReqWhenRefreshDone []topicSearchReq - searchInfo = make(map[Topic]topicSearchInfo) - activeSearchCount int - ) - defer topicRegisterLookupTick.Stop() - topicSearchLookupDone := make(chan topicSearchResult, 100) - topicSearch := make(chan Topic, 100) - <-topicRegisterLookupTick.C - - statsDump := time.NewTicker(10 * time.Second) - defer statsDump.Stop() - -loop: - for { - resetNextTicket() - - select { - case <-net.closeReq: - log.Trace("<-net.closeReq") - break loop - - // Ingress packet handling. - case pkt := <-net.read: - //fmt.Println("read", pkt.ev) - log.Trace("<-net.read") - n := net.internNode(&pkt) - prestate := n.state - status := "ok" - if err := net.handle(n, pkt.ev, &pkt); err != nil { - status = err.Error() - } - log.Trace("", "msg", log.Lazy{Fn: func() string { - return fmt.Sprintf("<<< (%d) %v from %x@%v: %v -> %v (%v)", - net.tab.count, pkt.ev, pkt.remoteID[:8], pkt.remoteAddr, prestate, n.state, status) - }}) - // TODO: persist state if n.state goes >= known, delete if it goes <= known - - // State transition timeouts. - case timeout := <-net.timeout: - log.Trace("<-net.timeout") - if net.timeoutTimers[timeout] == nil { - // Stale timer (was aborted). - continue - } - delete(net.timeoutTimers, timeout) - prestate := timeout.node.state - status := "ok" - if err := net.handle(timeout.node, timeout.ev, nil); err != nil { - status = err.Error() - } - log.Trace("", "msg", log.Lazy{Fn: func() string { - return fmt.Sprintf("--- (%d) %v for %x@%v: %v -> %v (%v)", - net.tab.count, timeout.ev, timeout.node.ID[:8], timeout.node.addr(), prestate, timeout.node.state, status) - }}) - - // Querying. - case q := <-net.queryReq: - log.Trace("<-net.queryReq") - if !q.start(net) { - q.remote.deferQuery(q) - } - - // Interacting with the table. - case f := <-net.tableOpReq: - log.Trace("<-net.tableOpReq") - f() - net.tableOpResp <- struct{}{} - - // Topic registration stuff. - case req := <-net.topicRegisterReq: - log.Trace("<-net.topicRegisterReq") - if !req.add { - net.ticketStore.removeRegisterTopic(req.topic) - continue - } - net.ticketStore.addTopic(req.topic, true) - // If we're currently waiting idle (nothing to look up), give the ticket store a - // chance to start it sooner. This should speed up convergence of the radius - // determination for new topics. - // if topicRegisterLookupDone == nil { - if topicRegisterLookupTarget.target == (common.Hash{}) { - log.Trace("topicRegisterLookupTarget == null") - if topicRegisterLookupTick.Stop() { - <-topicRegisterLookupTick.C - } - target, delay := net.ticketStore.nextRegisterLookup() - topicRegisterLookupTarget = target - topicRegisterLookupTick.Reset(delay) - } - - case nodes := <-topicRegisterLookupDone: - log.Trace("<-topicRegisterLookupDone") - net.ticketStore.registerLookupDone(topicRegisterLookupTarget, nodes, func(n *Node) []byte { - net.ping(n, n.addr()) - return n.pingEcho - }) - target, delay := net.ticketStore.nextRegisterLookup() - topicRegisterLookupTarget = target - topicRegisterLookupTick.Reset(delay) - topicRegisterLookupDone = nil - - case <-topicRegisterLookupTick.C: - log.Trace("<-topicRegisterLookupTick") - if (topicRegisterLookupTarget.target == common.Hash{}) { - target, delay := net.ticketStore.nextRegisterLookup() - topicRegisterLookupTarget = target - topicRegisterLookupTick.Reset(delay) - topicRegisterLookupDone = nil - } else { - topicRegisterLookupDone = make(chan []*Node) - target := topicRegisterLookupTarget.target - go func() { topicRegisterLookupDone <- net.lookup(target, false) }() - } - - case <-nextRegisterTime: - log.Trace("<-nextRegisterTime") - net.ticketStore.ticketRegistered(*nextTicket) - //fmt.Println("sendTopicRegister", nextTicket.t.node.addr().String(), nextTicket.t.topics, nextTicket.idx, nextTicket.t.pong) - net.conn.sendTopicRegister(nextTicket.t.node, nextTicket.t.topics, nextTicket.idx, nextTicket.t.pong) - - case req := <-net.topicSearchReq: - if refreshDone == nil { - log.Trace("<-net.topicSearchReq") - info, ok := searchInfo[req.topic] - if ok { - if req.delay == time.Duration(0) { - delete(searchInfo, req.topic) - net.ticketStore.removeSearchTopic(req.topic) - } else { - info.period = req.delay - searchInfo[req.topic] = info - } - continue - } - if req.delay != time.Duration(0) { - var info topicSearchInfo - info.period = req.delay - info.lookupChn = req.lookup - searchInfo[req.topic] = info - net.ticketStore.addSearchTopic(req.topic, req.found) - topicSearch <- req.topic - } - } else { - searchReqWhenRefreshDone = append(searchReqWhenRefreshDone, req) - } - - case topic := <-topicSearch: - if activeSearchCount < maxSearchCount { - activeSearchCount++ - target := net.ticketStore.nextSearchLookup(topic) - go func() { - nodes := net.lookup(target.target, false) - topicSearchLookupDone <- topicSearchResult{target: target, nodes: nodes} - }() - } - period := searchInfo[topic].period - if period != time.Duration(0) { - go func() { - time.Sleep(period) - topicSearch <- topic - }() - } - - case res := <-topicSearchLookupDone: - activeSearchCount-- - if lookupChn := searchInfo[res.target.topic].lookupChn; lookupChn != nil { - lookupChn <- net.ticketStore.radius[res.target.topic].converged - } - net.ticketStore.searchLookupDone(res.target, res.nodes, func(n *Node, topic Topic) []byte { - if n.state != nil && n.state.canQuery { - return net.conn.send(n, topicQueryPacket, topicQuery{Topic: topic}) // TODO: set expiration - } - if n.state == unknown { - net.ping(n, n.addr()) - } - return nil - }) - - case <-statsDump.C: - log.Trace("<-statsDump.C") - /*r, ok := net.ticketStore.radius[testTopic] - if !ok { - fmt.Printf("(%x) no radius @ %v\n", net.tab.self.ID[:8], time.Now()) - } else { - topics := len(net.ticketStore.tickets) - tickets := len(net.ticketStore.nodes) - rad := r.radius / (maxRadius/10000+1) - fmt.Printf("(%x) topics:%d radius:%d tickets:%d @ %v\n", net.tab.self.ID[:8], topics, rad, tickets, time.Now()) - }*/ - - tm := mclock.Now() - for topic, r := range net.ticketStore.radius { - if printTestImgLogs { - rad := r.radius / (maxRadius/1000000 + 1) - minrad := r.minRadius / (maxRadius/1000000 + 1) - fmt.Printf("*R %d %v %016x %v\n", tm/1000000, topic, net.tab.self.sha[:8], rad) - fmt.Printf("*MR %d %v %016x %v\n", tm/1000000, topic, net.tab.self.sha[:8], minrad) - } - } - for topic, t := range net.topictab.topics { - wp := t.wcl.nextWaitPeriod(tm) - if printTestImgLogs { - fmt.Printf("*W %d %v %016x %d\n", tm/1000000, topic, net.tab.self.sha[:8], wp/1000000) - } - } - - // Periodic / lookup-initiated bucket refresh. - case <-refreshTimer.C: - log.Trace("<-refreshTimer.C") - // TODO: ideally we would start the refresh timer after - // fallback nodes have been set for the first time. - if refreshDone == nil { - refreshDone = make(chan struct{}) - net.refresh(refreshDone) - } - case <-bucketRefreshTimer.C: - target := net.tab.chooseBucketRefreshTarget() - go func() { - net.lookup(target, false) - bucketRefreshTimer.Reset(bucketRefreshInterval) - }() - case newNursery := <-net.refreshReq: - log.Trace("<-net.refreshReq") - if newNursery != nil { - net.nursery = newNursery - } - if refreshDone == nil { - refreshDone = make(chan struct{}) - net.refresh(refreshDone) - } - net.refreshResp <- refreshDone - case <-refreshDone: - log.Trace("<-net.refreshDone", "table size", net.tab.count) - if net.tab.count != 0 { - refreshDone = nil - list := searchReqWhenRefreshDone - searchReqWhenRefreshDone = nil - go func() { - for _, req := range list { - net.topicSearchReq <- req - } - }() - } else { - refreshDone = make(chan struct{}) - net.refresh(refreshDone) - } - } - } - log.Trace("loop stopped") - - log.Debug("shutting down") - if net.conn != nil { - net.conn.Close() - } - // TODO: wait for pending refresh. - // if refreshDone != nil { - // <-refreshResults - // } - // Cancel all pending timeouts. - for _, timer := range net.timeoutTimers { - timer.Stop() - } - if net.db != nil { - net.db.close() - } - close(net.closed) -} - -// Everything below runs on the Network.loop goroutine -// and can modify Node, Table and Network at any time without locking. - -func (net *Network) refresh(done chan<- struct{}) { - var seeds []*Node - if net.db != nil { - seeds = net.db.querySeeds(seedCount, seedMaxAge) - } - if len(seeds) == 0 { - seeds = net.nursery - } - if len(seeds) == 0 { - log.Trace("no seed nodes found") - time.AfterFunc(time.Second*10, func() { close(done) }) - return - } - for _, n := range seeds { - log.Debug("", "msg", log.Lazy{Fn: func() string { - var age string - if net.db != nil { - age = time.Since(net.db.lastPong(n.ID)).String() - } else { - age = "unknown" - } - return fmt.Sprintf("seed node (age %s): %v", age, n) - }}) - n = net.internNodeFromDB(n) - if n.state == unknown { - net.transition(n, verifyinit) - } - // Force-add the seed node so Lookup does something. - // It will be deleted again if verification fails. - net.tab.add(n) - } - // Start self lookup to fill up the buckets. - go func() { - net.Lookup(net.tab.self.ID) - close(done) - }() -} - -// Node Interning. - -func (net *Network) internNode(pkt *ingressPacket) *Node { - if n := net.nodes[pkt.remoteID]; n != nil { - n.IP = pkt.remoteAddr.IP - n.UDP = uint16(pkt.remoteAddr.Port) - n.TCP = uint16(pkt.remoteAddr.Port) - return n - } - n := NewNode(pkt.remoteID, pkt.remoteAddr.IP, uint16(pkt.remoteAddr.Port), uint16(pkt.remoteAddr.Port)) - n.state = unknown - net.nodes[pkt.remoteID] = n - return n -} - -func (net *Network) internNodeFromDB(dbn *Node) *Node { - if n := net.nodes[dbn.ID]; n != nil { - return n - } - n := NewNode(dbn.ID, dbn.IP, dbn.UDP, dbn.TCP) - n.state = unknown - net.nodes[n.ID] = n - return n -} - -func (net *Network) internNodeFromNeighbours(sender *net.UDPAddr, rn rpcNode) (n *Node, err error) { - if rn.ID == net.tab.self.ID { - return nil, errors.New("is self") - } - if rn.UDP <= lowPort { - return nil, errors.New("low port") - } - n = net.nodes[rn.ID] - if n == nil { - // We haven't seen this node before. - n, err = nodeFromRPC(sender, rn) - if net.netrestrict != nil && !net.netrestrict.Contains(n.IP) { - return n, errors.New("not contained in netrestrict whitelist") - } - if err == nil { - n.state = unknown - net.nodes[n.ID] = n - } - return n, err - } - if !n.IP.Equal(rn.IP) || n.UDP != rn.UDP || n.TCP != rn.TCP { - if n.state == known { - // reject address change if node is known by us - err = fmt.Errorf("metadata mismatch: got %v, want %v", rn, n) - } else { - // accept otherwise; this will be handled nicer with signed ENRs - n.IP = rn.IP - n.UDP = rn.UDP - n.TCP = rn.TCP - } - } - return n, err -} - -// nodeNetGuts is embedded in Node and contains fields. -type nodeNetGuts struct { - // This is a cached copy of sha3(ID) which is used for node - // distance calculations. This is part of Node in order to make it - // possible to write tests that need a node at a certain distance. - // In those tests, the content of sha will not actually correspond - // with ID. - sha common.Hash - - // State machine fields. Access to these fields - // is restricted to the Network.loop goroutine. - state *nodeState - pingEcho []byte // hash of last ping sent by us - pingTopics []Topic // topic set sent by us in last ping - deferredQueries []*findnodeQuery // queries that can't be sent yet - pendingNeighbours *findnodeQuery // current query, waiting for reply - queryTimeouts int -} - -func (n *nodeNetGuts) deferQuery(q *findnodeQuery) { - n.deferredQueries = append(n.deferredQueries, q) -} - -func (n *nodeNetGuts) startNextQuery(net *Network) { - if len(n.deferredQueries) == 0 { - return - } - nextq := n.deferredQueries[0] - if nextq.start(net) { - n.deferredQueries = append(n.deferredQueries[:0], n.deferredQueries[1:]...) - } -} - -func (q *findnodeQuery) start(net *Network) bool { - // Satisfy queries against the local node directly. - if q.remote == net.tab.self { - closest := net.tab.closest(q.target, bucketSize) - q.reply <- closest.entries - return true - } - if q.remote.state.canQuery && q.remote.pendingNeighbours == nil { - net.conn.sendFindnodeHash(q.remote, q.target) - net.timedEvent(respTimeout, q.remote, neighboursTimeout) - q.remote.pendingNeighbours = q - return true - } - // If the node is not known yet, it won't accept queries. - // Initiate the transition to known. - // The request will be sent later when the node reaches known state. - if q.remote.state == unknown { - net.transition(q.remote, verifyinit) - } - return false -} - -// Node Events (the input to the state machine). - -type nodeEvent uint - -//go:generate stringer -type=nodeEvent - -const ( - - // Packet type events. - // These correspond to packet types in the UDP protocol. - pingPacket = iota + 1 - pongPacket - findnodePacket - neighborsPacket - findnodeHashPacket - topicRegisterPacket - topicQueryPacket - topicNodesPacket - - // Non-packet events. - // Event values in this category are allocated outside - // the packet type range (packet types are encoded as a single byte). - pongTimeout nodeEvent = iota + 256 - pingTimeout - neighboursTimeout -) - -// Node State Machine. - -type nodeState struct { - name string - handle func(*Network, *Node, nodeEvent, *ingressPacket) (next *nodeState, err error) - enter func(*Network, *Node) - canQuery bool -} - -func (s *nodeState) String() string { - return s.name -} - -var ( - unknown *nodeState - verifyinit *nodeState - verifywait *nodeState - remoteverifywait *nodeState - known *nodeState - contested *nodeState - unresponsive *nodeState -) - -func init() { - unknown = &nodeState{ - name: "unknown", - enter: func(net *Network, n *Node) { - net.tab.delete(n) - n.pingEcho = nil - // Abort active queries. - for _, q := range n.deferredQueries { - q.reply <- nil - } - n.deferredQueries = nil - if n.pendingNeighbours != nil { - n.pendingNeighbours.reply <- nil - n.pendingNeighbours = nil - } - n.queryTimeouts = 0 - }, - handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { - switch ev { - case pingPacket: - net.handlePing(n, pkt) - net.ping(n, pkt.remoteAddr) - return verifywait, nil - default: - return unknown, errInvalidEvent - } - }, - } - - verifyinit = &nodeState{ - name: "verifyinit", - enter: func(net *Network, n *Node) { - net.ping(n, n.addr()) - }, - handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { - switch ev { - case pingPacket: - net.handlePing(n, pkt) - return verifywait, nil - case pongPacket: - err := net.handleKnownPong(n, pkt) - return remoteverifywait, err - case pongTimeout: - return unknown, nil - default: - return verifyinit, errInvalidEvent - } - }, - } - - verifywait = &nodeState{ - name: "verifywait", - handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { - switch ev { - case pingPacket: - net.handlePing(n, pkt) - return verifywait, nil - case pongPacket: - err := net.handleKnownPong(n, pkt) - return known, err - case pongTimeout: - return unknown, nil - default: - return verifywait, errInvalidEvent - } - }, - } - - remoteverifywait = &nodeState{ - name: "remoteverifywait", - enter: func(net *Network, n *Node) { - net.timedEvent(respTimeout, n, pingTimeout) - }, - handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { - switch ev { - case pingPacket: - net.handlePing(n, pkt) - return remoteverifywait, nil - case pingTimeout: - return known, nil - default: - return remoteverifywait, errInvalidEvent - } - }, - } - - known = &nodeState{ - name: "known", - canQuery: true, - enter: func(net *Network, n *Node) { - n.queryTimeouts = 0 - n.startNextQuery(net) - // Insert into the table and start revalidation of the last node - // in the bucket if it is full. - last := net.tab.add(n) - if last != nil && last.state == known { - // TODO: do this asynchronously - net.transition(last, contested) - } - }, - handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { - switch ev { - case pingPacket: - net.handlePing(n, pkt) - return known, nil - case pongPacket: - err := net.handleKnownPong(n, pkt) - return known, err - default: - return net.handleQueryEvent(n, ev, pkt) - } - }, - } - - contested = &nodeState{ - name: "contested", - canQuery: true, - enter: func(net *Network, n *Node) { - net.ping(n, n.addr()) - }, - handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { - switch ev { - case pongPacket: - // Node is still alive. - err := net.handleKnownPong(n, pkt) - return known, err - case pongTimeout: - net.tab.deleteReplace(n) - return unresponsive, nil - case pingPacket: - net.handlePing(n, pkt) - return contested, nil - default: - return net.handleQueryEvent(n, ev, pkt) - } - }, - } - - unresponsive = &nodeState{ - name: "unresponsive", - canQuery: true, - handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { - switch ev { - case pingPacket: - net.handlePing(n, pkt) - return known, nil - case pongPacket: - err := net.handleKnownPong(n, pkt) - return known, err - default: - return net.handleQueryEvent(n, ev, pkt) - } - }, - } -} - -// handle processes packets sent by n and events related to n. -func (net *Network) handle(n *Node, ev nodeEvent, pkt *ingressPacket) error { - //fmt.Println("handle", n.addr().String(), n.state, ev) - if pkt != nil { - if err := net.checkPacket(n, ev, pkt); err != nil { - //fmt.Println("check err:", err) - return err - } - // Start the background expiration goroutine after the first - // successful communication. Subsequent calls have no effect if it - // is already running. We do this here instead of somewhere else - // so that the search for seed nodes also considers older nodes - // that would otherwise be removed by the expirer. - if net.db != nil { - net.db.ensureExpirer() - } - } - if ev == pongTimeout { - n.pingEcho = nil // clean up if pongtimeout - } - if n.state == nil { - n.state = unknown //??? - } - next, err := n.state.handle(net, n, ev, pkt) - net.transition(n, next) - //fmt.Println("new state:", n.state) - return err -} - -func (net *Network) checkPacket(n *Node, ev nodeEvent, pkt *ingressPacket) error { - // Replay prevention checks. - switch ev { - case pingPacket, findnodeHashPacket, neighborsPacket: - // TODO: check date is > last date seen - // TODO: check ping version - case pongPacket: - if !bytes.Equal(pkt.data.(*pong).ReplyTok, n.pingEcho) { - // fmt.Println("pong reply token mismatch") - return fmt.Errorf("pong reply token mismatch") - } - n.pingEcho = nil - } - // Address validation. - // TODO: Ideally we would do the following: - // - reject all packets with wrong address except ping. - // - for ping with new address, transition to verifywait but keep the - // previous node (with old address) around. if the new one reaches known, - // swap it out. - return nil -} - -func (net *Network) transition(n *Node, next *nodeState) { - if n.state != next { - n.state = next - if next.enter != nil { - next.enter(net, n) - } - } - - // TODO: persist/unpersist node -} - -func (net *Network) timedEvent(d time.Duration, n *Node, ev nodeEvent) { - timeout := timeoutEvent{ev, n} - net.timeoutTimers[timeout] = time.AfterFunc(d, func() { - select { - case net.timeout <- timeout: - case <-net.closed: - } - }) -} - -func (net *Network) abortTimedEvent(n *Node, ev nodeEvent) { - timer := net.timeoutTimers[timeoutEvent{ev, n}] - if timer != nil { - timer.Stop() - delete(net.timeoutTimers, timeoutEvent{ev, n}) - } -} - -func (net *Network) ping(n *Node, addr *net.UDPAddr) { - //fmt.Println("ping", n.addr().String(), n.ID.String(), n.sha.Hex()) - if n.pingEcho != nil || n.ID == net.tab.self.ID { - //fmt.Println(" not sent") - return - } - log.Trace("Pinging remote node", "node", n.ID) - n.pingTopics = net.ticketStore.regTopicSet() - n.pingEcho = net.conn.sendPing(n, addr, n.pingTopics) - net.timedEvent(respTimeout, n, pongTimeout) -} - -func (net *Network) handlePing(n *Node, pkt *ingressPacket) { - log.Trace("Handling remote ping", "node", n.ID) - ping := pkt.data.(*ping) - n.TCP = ping.From.TCP - t := net.topictab.getTicket(n, ping.Topics) - - pong := &pong{ - To: makeEndpoint(n.addr(), n.TCP), // TODO: maybe use known TCP port from DB - ReplyTok: pkt.hash, - Expiration: uint64(time.Now().Add(expiration).Unix()), - } - ticketToPong(t, pong) - net.conn.send(n, pongPacket, pong) -} - -func (net *Network) handleKnownPong(n *Node, pkt *ingressPacket) error { - log.Trace("Handling known pong", "node", n.ID) - net.abortTimedEvent(n, pongTimeout) - now := mclock.Now() - ticket, err := pongToTicket(now, n.pingTopics, n, pkt) - if err == nil { - // fmt.Printf("(%x) ticket: %+v\n", net.tab.self.ID[:8], pkt.data) - net.ticketStore.addTicket(now, pkt.data.(*pong).ReplyTok, ticket) - } else { - log.Trace("Failed to convert pong to ticket", "err", err) - } - n.pingEcho = nil - n.pingTopics = nil - return err -} - -func (net *Network) handleQueryEvent(n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { - switch ev { - case findnodePacket: - target := crypto.Keccak256Hash(pkt.data.(*findnode).Target[:]) - results := net.tab.closest(target, bucketSize).entries - net.conn.sendNeighbours(n, results) - return n.state, nil - case neighborsPacket: - err := net.handleNeighboursPacket(n, pkt) - return n.state, err - case neighboursTimeout: - if n.pendingNeighbours != nil { - n.pendingNeighbours.reply <- nil - n.pendingNeighbours = nil - } - n.queryTimeouts++ - if n.queryTimeouts > maxFindnodeFailures && n.state == known { - return contested, errors.New("too many timeouts") - } - return n.state, nil - - // v5 - - case findnodeHashPacket: - results := net.tab.closest(pkt.data.(*findnodeHash).Target, bucketSize).entries - net.conn.sendNeighbours(n, results) - return n.state, nil - case topicRegisterPacket: - //fmt.Println("got topicRegisterPacket") - regdata := pkt.data.(*topicRegister) - pong, err := net.checkTopicRegister(regdata) - if err != nil { - //fmt.Println(err) - return n.state, fmt.Errorf("bad waiting ticket: %v", err) - } - net.topictab.useTicket(n, pong.TicketSerial, regdata.Topics, int(regdata.Idx), pong.Expiration, pong.WaitPeriods) - return n.state, nil - case topicQueryPacket: - // TODO: handle expiration - topic := pkt.data.(*topicQuery).Topic - results := net.topictab.getEntries(topic) - if _, ok := net.ticketStore.tickets[topic]; ok { - results = append(results, net.tab.self) // we're not registering in our own table but if we're advertising, return ourselves too - } - if len(results) > 10 { - results = results[:10] - } - var hash common.Hash - copy(hash[:], pkt.hash) - net.conn.sendTopicNodes(n, hash, results) - return n.state, nil - case topicNodesPacket: - p := pkt.data.(*topicNodes) - if net.ticketStore.gotTopicNodes(n, p.Echo, p.Nodes) { - n.queryTimeouts++ - if n.queryTimeouts > maxFindnodeFailures && n.state == known { - return contested, errors.New("too many timeouts") - } - } - return n.state, nil - - default: - return n.state, errInvalidEvent - } -} - -func (net *Network) checkTopicRegister(data *topicRegister) (*pong, error) { - var pongpkt ingressPacket - if err := decodePacket(data.Pong, &pongpkt); err != nil { - return nil, err - } - if pongpkt.ev != pongPacket { - return nil, errors.New("is not pong packet") - } - if pongpkt.remoteID != net.tab.self.ID { - return nil, errors.New("not signed by us") - } - // check that we previously authorised all topics - // that the other side is trying to register. - if rlpHash(data.Topics) != pongpkt.data.(*pong).TopicHash { - return nil, errors.New("topic hash mismatch") - } - if data.Idx >= uint(len(data.Topics)) { - return nil, errors.New("topic index out of range") - } - return pongpkt.data.(*pong), nil -} - -func rlpHash(x interface{}) (h common.Hash) { - hw := sha3.NewLegacyKeccak256() - rlp.Encode(hw, x) - hw.Sum(h[:0]) - return h -} - -func (net *Network) handleNeighboursPacket(n *Node, pkt *ingressPacket) error { - if n.pendingNeighbours == nil { - return errNoQuery - } - net.abortTimedEvent(n, neighboursTimeout) - - req := pkt.data.(*neighbors) - nodes := make([]*Node, len(req.Nodes)) - for i, rn := range req.Nodes { - nn, err := net.internNodeFromNeighbours(pkt.remoteAddr, rn) - if err != nil { - log.Debug(fmt.Sprintf("invalid neighbour (%v) from %x@%v: %v", rn.IP, n.ID[:8], pkt.remoteAddr, err)) - continue - } - nodes[i] = nn - // Start validation of query results immediately. - // This fills the table quickly. - // TODO: generates way too many packets, maybe do it via queue. - if nn.state == unknown { - net.transition(nn, verifyinit) - } - } - // TODO: don't ignore second packet - n.pendingNeighbours.reply <- nodes - n.pendingNeighbours = nil - // Now that this query is done, start the next one. - n.startNextQuery(net) - return nil -} diff --git a/p2p/discv5/net_test.go b/p2p/discv5/net_test.go deleted file mode 100644 index 29321bc86f..0000000000 --- a/p2p/discv5/net_test.go +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package discv5 - -import ( - "net" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -func TestNetwork_Lookup(t *testing.T) { - key, _ := crypto.GenerateKey() - network, err := newNetwork(lookupTestnet, key.PublicKey, "", nil) - if err != nil { - t.Fatal(err) - } - lookupTestnet.net = network - defer network.Close() - - // lookup on empty table returns no nodes - // if results := network.Lookup(lookupTestnet.target, false); len(results) > 0 { - // t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results) - // } - // seed table with initial node (otherwise lookup will terminate immediately) - seeds := []*Node{NewNode(lookupTestnet.dists[256][0], net.IP{10, 0, 2, 99}, lowPort+256, 999)} - if err := network.SetFallbackNodes(seeds); err != nil { - t.Fatal(err) - } - time.Sleep(3 * time.Second) - - results := network.Lookup(lookupTestnet.target) - t.Logf("results:") - for _, e := range results { - t.Logf(" ld=%d, %x", logdist(lookupTestnet.targetSha, e.sha), e.sha[:]) - } - if len(results) != bucketSize { - t.Errorf("wrong number of results: got %d, want %d", len(results), bucketSize) - } - if hasDuplicates(results) { - t.Errorf("result set contains duplicate entries") - } - if !sortedByDistanceTo(lookupTestnet.targetSha, results) { - t.Errorf("result set not sorted by distance to target") - } - // TODO: check result nodes are actually closest -} - -// This is the test network for the Lookup test. -// The nodes were obtained by running testnet.mine with a random NodeID as target. -var lookupTestnet = &preminedTestnet{ - target: MustHexID("166aea4f556532c6d34e8b740e5d314af7e9ac0ca79833bd751d6b665f12dfd38ec563c363b32f02aef4a80b44fd3def94612d497b99cb5f17fd24de454927ec"), - targetSha: common.Hash{0x5c, 0x94, 0x4e, 0xe5, 0x1c, 0x5a, 0xe9, 0xf7, 0x2a, 0x95, 0xec, 0xcb, 0x8a, 0xed, 0x3, 0x74, 0xee, 0xcb, 0x51, 0x19, 0xd7, 0x20, 0xcb, 0xea, 0x68, 0x13, 0xe8, 0xe0, 0xd6, 0xad, 0x92, 0x61}, - dists: [257][]NodeID{ - 240: { - MustHexID("2001ad5e3e80c71b952161bc0186731cf5ffe942d24a79230a0555802296238e57ea7a32f5b6f18564eadc1c65389448481f8c9338df0a3dbd18f708cbc2cbcb"), - MustHexID("6ba3f4f57d084b6bf94cc4555b8c657e4a8ac7b7baf23c6874efc21dd1e4f56b7eb2721e07f5242d2f1d8381fc8cae535e860197c69236798ba1ad231b105794"), - }, - 244: { - MustHexID("696ba1f0a9d55c59246f776600542a9e6432490f0cd78f8bb55a196918df2081a9b521c3c3ba48e465a75c10768807717f8f689b0b4adce00e1c75737552a178"), - }, - 246: { - MustHexID("d6d32178bdc38416f46ffb8b3ec9e4cb2cfff8d04dd7e4311a70e403cb62b10be1b447311b60b4f9ee221a8131fc2cbd45b96dd80deba68a949d467241facfa8"), - MustHexID("3ea3d04a43a3dfb5ac11cffc2319248cf41b6279659393c2f55b8a0a5fc9d12581a9d97ef5d8ff9b5abf3321a290e8f63a4f785f450dc8a672aba3ba2ff4fdab"), - MustHexID("2fc897f05ae585553e5c014effd3078f84f37f9333afacffb109f00ca8e7a3373de810a3946be971cbccdfd40249f9fe7f322118ea459ac71acca85a1ef8b7f4"), - }, - 247: { - MustHexID("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"), - MustHexID("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"), - MustHexID("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"), - MustHexID("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"), - MustHexID("8b58c6073dd98bbad4e310b97186c8f822d3a5c7d57af40e2136e88e315afd115edb27d2d0685a908cfe5aa49d0debdda6e6e63972691d6bd8c5af2d771dd2a9"), - MustHexID("2cbb718b7dc682da19652e7d9eb4fefaf7b7147d82c1c2b6805edf77b85e29fde9f6da195741467ff2638dc62c8d3e014ea5686693c15ed0080b6de90354c137"), - MustHexID("e84027696d3f12f2de30a9311afea8fbd313c2360daff52bb5fc8c7094d5295758bec3134e4eef24e4cdf377b40da344993284628a7a346eba94f74160998feb"), - MustHexID("f1357a4f04f9d33753a57c0b65ba20a5d8777abbffd04e906014491c9103fb08590e45548d37aa4bd70965e2e81ddba94f31860348df01469eec8c1829200a68"), - MustHexID("4ab0a75941b12892369b4490a1928c8ca52a9ad6d3dffbd1d8c0b907bc200fe74c022d011ec39b64808a39c0ca41f1d3254386c3e7733e7044c44259486461b6"), - MustHexID("d45150a72dc74388773e68e03133a3b5f51447fe91837d566706b3c035ee4b56f160c878c6273394daee7f56cc398985269052f22f75a8057df2fe6172765354"), - }, - 248: { - MustHexID("6aadfce366a189bab08ac84721567483202c86590642ea6d6a14f37ca78d82bdb6509eb7b8b2f6f63c78ae3ae1d8837c89509e41497d719b23ad53dd81574afa"), - MustHexID("a605ecfd6069a4cf4cf7f5840e5bc0ce10d23a3ac59e2aaa70c6afd5637359d2519b4524f56fc2ca180cdbebe54262f720ccaae8c1b28fd553c485675831624d"), - MustHexID("29701451cb9448ca33fc33680b44b840d815be90146eb521641efbffed0859c154e8892d3906eae9934bfacee72cd1d2fa9dd050fd18888eea49da155ab0efd2"), - MustHexID("3ed426322dee7572b08592e1e079f8b6c6b30e10e6243edd144a6a48fdbdb83df73a6e41b1143722cb82604f2203a32758610b5d9544f44a1a7921ba001528c1"), - MustHexID("b2e2a2b7fdd363572a3256e75435fab1da3b16f7891a8bd2015f30995dae665d7eabfd194d87d99d5df628b4bbc7b04e5b492c596422dd8272746c7a1b0b8e4f"), - MustHexID("0c69c9756162c593e85615b814ce57a2a8ca2df6c690b9c4e4602731b61e1531a3bbe3f7114271554427ffabea80ad8f36fa95a49fa77b675ae182c6ccac1728"), - MustHexID("8d28be21d5a97b0876442fa4f5e5387f5bf3faad0b6f13b8607b64d6e448c0991ca28dd7fe2f64eb8eadd7150bff5d5666aa6ed868b84c71311f4ba9a38569dd"), - MustHexID("2c677e1c64b9c9df6359348a7f5f33dc79e22f0177042486d125f8b6ca7f0dc756b1f672aceee5f1746bcff80aaf6f92a8dc0c9fbeb259b3fa0da060de5ab7e8"), - MustHexID("3994880f94a8678f0cd247a43f474a8af375d2a072128da1ad6cae84a244105ff85e94fc7d8496f639468de7ee998908a91c7e33ef7585fff92e984b210941a1"), - MustHexID("b45a9153c08d002a48090d15d61a7c7dad8c2af85d4ff5bd36ce23a9a11e0709bf8d56614c7b193bc028c16cbf7f20dfbcc751328b64a924995d47b41e452422"), - MustHexID("057ab3a9e53c7a84b0f3fc586117a525cdd18e313f52a67bf31798d48078e325abe5cfee3f6c2533230cb37d0549289d692a29dd400e899b8552d4b928f6f907"), - MustHexID("0ddf663d308791eb92e6bd88a2f8cb45e4f4f35bb16708a0e6ff7f1362aa6a73fedd0a1b1557fb3365e38e1b79d6918e2fae2788728b70c9ab6b51a3b94a4338"), - MustHexID("f637e07ff50cc1e3731735841c4798411059f2023abcf3885674f3e8032531b0edca50fd715df6feb489b6177c345374d64f4b07d257a7745de393a107b013a5"), - MustHexID("e24ec7c6eec094f63c7b3239f56d311ec5a3e45bc4e622a1095a65b95eea6fe13e29f3b6b7a2cbfe40906e3989f17ac834c3102dd0cadaaa26e16ee06d782b72"), - MustHexID("b76ea1a6fd6506ef6e3506a4f1f60ed6287fff8114af6141b2ff13e61242331b54082b023cfea5b3083354a4fb3f9eb8be01fb4a518f579e731a5d0707291a6b"), - MustHexID("9b53a37950ca8890ee349b325032d7b672cab7eced178d3060137b24ef6b92a43977922d5bdfb4a3409a2d80128e02f795f9dae6d7d99973ad0e23a2afb8442f"), - }, - 249: { - MustHexID("675ae65567c3c72c50c73bc0fd4f61f202ea5f93346ca57b551de3411ccc614fad61cb9035493af47615311b9d44ee7a161972ee4d77c28fe1ec029d01434e6a"), - MustHexID("8eb81408389da88536ae5800392b16ef5109d7ea132c18e9a82928047ecdb502693f6e4a4cdd18b54296caf561db937185731456c456c98bfe7de0baf0eaa495"), - MustHexID("2adba8b1612a541771cb93a726a38a4b88e97b18eced2593eb7daf82f05a5321ca94a72cc780c306ff21e551a932fc2c6d791e4681907b5ceab7f084c3fa2944"), - MustHexID("b1b4bfbda514d9b8f35b1c28961da5d5216fe50548f4066f69af3b7666a3b2e06eac646735e963e5c8f8138a2fb95af15b13b23ff00c6986eccc0efaa8ee6fb4"), - MustHexID("d2139281b289ad0e4d7b4243c4364f5c51aac8b60f4806135de06b12b5b369c9e43a6eb494eab860d115c15c6fbb8c5a1b0e382972e0e460af395b8385363de7"), - MustHexID("4a693df4b8fc5bdc7cec342c3ed2e228d7c5b4ab7321ddaa6cccbeb45b05a9f1d95766b4002e6d4791c2deacb8a667aadea6a700da28a3eea810a30395701bbc"), - MustHexID("ab41611195ec3c62bb8cd762ee19fb182d194fd141f4a66780efbef4b07ce916246c022b841237a3a6b512a93431157edd221e854ed2a259b72e9c5351f44d0c"), - MustHexID("68e8e26099030d10c3c703ae7045c0a48061fb88058d853b3e67880014c449d4311014da99d617d3150a20f1a3da5e34bf0f14f1c51fe4dd9d58afd222823176"), - MustHexID("3fbcacf546fb129cd70fc48de3b593ba99d3c473798bc309292aca280320e0eacc04442c914cad5c4cf6950345ba79b0d51302df88285d4e83ee3fe41339eee7"), - MustHexID("1d4a623659f7c8f80b6c3939596afdf42e78f892f682c768ad36eb7bfba402dbf97aea3a268f3badd8fe7636be216edf3d67ee1e08789ebbc7be625056bd7109"), - MustHexID("a283c474ab09da02bbc96b16317241d0627646fcc427d1fe790b76a7bf1989ced90f92101a973047ae9940c92720dffbac8eff21df8cae468a50f72f9e159417"), - MustHexID("dbf7e5ad7f87c3dfecae65d87c3039e14ed0bdc56caf00ce81931073e2e16719d746295512ff7937a15c3b03603e7c41a4f9df94fcd37bb200dd8f332767e9cb"), - MustHexID("caaa070a26692f64fc77f30d7b5ae980d419b4393a0f442b1c821ef58c0862898b0d22f74a4f8c5d83069493e3ec0b92f17dc1fe6e4cd437c1ec25039e7ce839"), - MustHexID("874cc8d1213beb65c4e0e1de38ef5d8165235893ac74ab5ea937c885eaab25c8d79dad0456e9fd3e9450626cac7e107b004478fb59842f067857f39a47cee695"), - MustHexID("d94193f236105010972f5df1b7818b55846592a0445b9cdc4eaed811b8c4c0f7c27dc8cc9837a4774656d6b34682d6d329d42b6ebb55da1d475c2474dc3dfdf4"), - MustHexID("edd9af6aded4094e9785637c28fccbd3980cbe28e2eb9a411048a23c2ace4bd6b0b7088a7817997b49a3dd05fc6929ca6c7abbb69438dbdabe65e971d2a794b2"), - }, - 250: { - MustHexID("53a5bd1215d4ab709ae8fdc2ced50bba320bced78bd9c5dc92947fb402250c914891786db0978c898c058493f86fc68b1c5de8a5cb36336150ac7a88655b6c39"), - MustHexID("b7f79e3ab59f79262623c9ccefc8f01d682323aee56ffbe295437487e9d5acaf556a9c92e1f1c6a9601f2b9eb6b027ae1aeaebac71d61b9b78e88676efd3e1a3"), - MustHexID("d374bf7e8d7ffff69cc00bebff38ef5bc1dcb0a8d51c1a3d70e61ac6b2e2d6617109254b0ac224354dfbf79009fe4239e09020c483cc60c071e00b9238684f30"), - MustHexID("1e1eac1c9add703eb252eb991594f8f5a173255d526a855fab24ae57dc277e055bc3c7a7ae0b45d437c4f47a72d97eb7b126f2ba344ba6c0e14b2c6f27d4b1e6"), - MustHexID("ae28953f63d4bc4e706712a59319c111f5ff8f312584f65d7436b4cd3d14b217b958f8486bad666b4481fe879019fb1f767cf15b3e3e2711efc33b56d460448a"), - MustHexID("934bb1edf9c7a318b82306aca67feb3d6b434421fa275d694f0b4927afd8b1d3935b727fd4ff6e3d012e0c82f1824385174e8c6450ade59c2a43281a4b3446b6"), - MustHexID("9eef3f28f70ce19637519a0916555bf76d26de31312ac656cf9d3e379899ea44e4dd7ffcce923b4f3563f8a00489a34bd6936db0cbb4c959d32c49f017e07d05"), - MustHexID("82200872e8f871c48f1fad13daec6478298099b591bb3dbc4ef6890aa28ebee5860d07d70be62f4c0af85085a90ae8179ee8f937cf37915c67ea73e704b03ee7"), - MustHexID("6c75a5834a08476b7fc37ff3dc2011dc3ea3b36524bad7a6d319b18878fad813c0ba76d1f4555cacd3890c865438c21f0e0aed1f80e0a157e642124c69f43a11"), - MustHexID("995b873742206cb02b736e73a88580c2aacb0bd4a3c97a647b647bcab3f5e03c0e0736520a8b3600da09edf4248991fb01091ec7ff3ec7cdc8a1beae011e7aae"), - MustHexID("c773a056594b5cdef2e850d30891ff0e927c3b1b9c35cd8e8d53a1017001e237468e1ece3ae33d612ca3e6abb0a9169aa352e9dcda358e5af2ad982b577447db"), - MustHexID("2b46a5f6923f475c6be99ec6d134437a6d11f6bb4b4ac6bcd94572fa1092639d1c08aeefcb51f0912f0a060f71d4f38ee4da70ecc16010b05dd4a674aab14c3a"), - MustHexID("af6ab501366debbaa0d22e20e9688f32ef6b3b644440580fd78de4fe0e99e2a16eb5636bbae0d1c259df8ddda77b35b9a35cbc36137473e9c68fbc9d203ba842"), - MustHexID("c9f6f2dd1a941926f03f770695bda289859e85fabaf94baaae20b93e5015dc014ba41150176a36a1884adb52f405194693e63b0c464a6891cc9cc1c80d450326"), - MustHexID("5b116f0751526868a909b61a30b0c5282c37df6925cc03ddea556ef0d0602a9595fd6c14d371f8ed7d45d89918a032dcd22be4342a8793d88fdbeb3ca3d75bd7"), - MustHexID("50f3222fb6b82481c7c813b2172e1daea43e2710a443b9c2a57a12bd160dd37e20f87aa968c82ad639af6972185609d47036c0d93b4b7269b74ebd7073221c10"), - }, - 251: { - MustHexID("9b8f702a62d1bee67bedfeb102eca7f37fa1713e310f0d6651cc0c33ea7c5477575289ccd463e5a2574a00a676a1fdce05658ba447bb9d2827f0ba47b947e894"), - MustHexID("b97532eb83054ed054b4abdf413bb30c00e4205545c93521554dbe77faa3cfaa5bd31ef466a107b0b34a71ec97214c0c83919720142cddac93aa7a3e928d4708"), - MustHexID("2f7a5e952bfb67f2f90b8441b5fadc9ee13b1dcde3afeeb3dd64bf937f86663cc5c55d1fa83952b5422763c7df1b7f2794b751c6be316ebc0beb4942e65ab8c1"), - MustHexID("42c7483781727051a0b3660f14faf39e0d33de5e643702ae933837d036508ab856ce7eec8ec89c4929a4901256e5233a3d847d5d4893f91bcf21835a9a880fee"), - MustHexID("873bae27bf1dc854408fba94046a53ab0c965cebe1e4e12290806fc62b88deb1f4a47f9e18f78fc0e7913a0c6e42ac4d0fc3a20cea6bc65f0c8a0ca90b67521e"), - MustHexID("a7e3a370bbd761d413f8d209e85886f68bf73d5c3089b2dc6fa42aab1ecb5162635497eed95dee2417f3c9c74a3e76319625c48ead2e963c7de877cd4551f347"), - MustHexID("528597534776a40df2addaaea15b6ff832ce36b9748a265768368f657e76d58569d9f30dbb91e91cf0ae7efe8f402f17aa0ae15f5c55051ba03ba830287f4c42"), - MustHexID("461d8bd4f13c3c09031fdb84f104ed737a52f630261463ce0bdb5704259bab4b737dda688285b8444dbecaecad7f50f835190b38684ced5e90c54219e5adf1bc"), - MustHexID("6ec50c0be3fd232737090fc0111caaf0bb6b18f72be453428087a11a97fd6b52db0344acbf789a689bd4f5f50f79017ea784f8fd6fe723ad6ae675b9e3b13e21"), - MustHexID("12fc5e2f77a83fdcc727b79d8ae7fe6a516881138d3011847ee136b400fed7cfba1f53fd7a9730253c7aa4f39abeacd04f138417ba7fcb0f36cccc3514e0dab6"), - MustHexID("4fdbe75914ccd0bce02101606a1ccf3657ec963e3b3c20239d5fec87673fe446d649b4f15f1fe1a40e6cfbd446dda2d31d40bb602b1093b8fcd5f139ba0eb46a"), - MustHexID("3753668a0f6281e425ea69b52cb2d17ab97afbe6eb84cf5d25425bc5e53009388857640668fadd7c110721e6047c9697803bd8a6487b43bb343bfa32ebf24039"), - MustHexID("2e81b16346637dec4410fd88e527346145b9c0a849dbf2628049ac7dae016c8f4305649d5659ec77f1e8a0fac0db457b6080547226f06283598e3740ad94849a"), - MustHexID("802c3cc27f91c89213223d758f8d2ecd41135b357b6d698f24d811cdf113033a81c38e0bdff574a5c005b00a8c193dc2531f8c1fa05fa60acf0ab6f2858af09f"), - MustHexID("fcc9a2e1ac3667026ff16192876d1813bb75abdbf39b929a92863012fe8b1d890badea7a0de36274d5c1eb1e8f975785532c50d80fd44b1a4b692f437303393f"), - MustHexID("6d8b3efb461151dd4f6de809b62726f5b89e9b38e9ba1391967f61cde844f7528fecf821b74049207cee5a527096b31f3ad623928cd3ce51d926fa345a6b2951"), - }, - 252: { - MustHexID("f1ae93157cc48c2075dd5868fbf523e79e06caf4b8198f352f6e526680b78ff4227263de92612f7d63472bd09367bb92a636fff16fe46ccf41614f7a72495c2a"), - MustHexID("587f482d111b239c27c0cb89b51dd5d574db8efd8de14a2e6a1400c54d4567e77c65f89c1da52841212080b91604104768350276b6682f2f961cdaf4039581c7"), - MustHexID("e3f88274d35cefdaabdf205afe0e80e936cc982b8e3e47a84ce664c413b29016a4fb4f3a3ebae0a2f79671f8323661ed462bf4390af94c424dc8ace0c301b90f"), - MustHexID("0ddc736077da9a12ba410dc5ea63cbcbe7659dd08596485b2bff3435221f82c10d263efd9af938e128464be64a178b7cd22e19f400d5802f4c9df54bf89f2619"), - MustHexID("784aa34d833c6ce63fcc1279630113c3272e82c4ae8c126c5a52a88ac461b6baeed4244e607b05dc14e5b2f41c70a273c3804dea237f14f7a1e546f6d1309d14"), - MustHexID("f253a2c354ee0e27cfcae786d726753d4ad24be6516b279a936195a487de4a59dbc296accf20463749ff55293263ed8c1b6365eecb248d44e75e9741c0d18205"), - MustHexID("a1910b80357b3ad9b4593e0628922939614dc9056a5fbf477279c8b2c1d0b4b31d89a0c09d0d41f795271d14d3360ef08a3f821e65e7e1f56c07a36afe49c7c5"), - MustHexID("f1168552c2efe541160f0909b0b4a9d6aeedcf595cdf0e9b165c97e3e197471a1ee6320e93389edfba28af6eaf10de98597ad56e7ab1b504ed762451996c3b98"), - MustHexID("b0c8e5d2c8634a7930e1a6fd082e448c6cf9d2d8b7293558b59238815a4df926c286bf297d2049f14e8296a6eb3256af614ec1812c4f2bbe807673b58bf14c8c"), - MustHexID("0fb346076396a38badc342df3679b55bd7f40a609ab103411fe45082c01f12ea016729e95914b2b5540e987ff5c9b133e85862648e7f36abdfd23100d248d234"), - MustHexID("f736e0cc83417feaa280d9483f5d4d72d1b036cd0c6d9cbdeb8ac35ceb2604780de46dddaa32a378474e1d5ccdf79b373331c30c7911ade2ae32f98832e5de1f"), - MustHexID("8b02991457602f42b38b342d3f2259ae4100c354b3843885f7e4e07bd644f64dab94bb7f38a3915f8b7f11d8e3f81c28e07a0078cf79d7397e38a7b7e0c857e2"), - MustHexID("9221d9f04a8a184993d12baa91116692bb685f887671302999d69300ad103eb2d2c75a09d8979404c6dd28f12362f58a1a43619c493d9108fd47588a23ce5824"), - MustHexID("652797801744dada833fff207d67484742eea6835d695925f3e618d71b68ec3c65bdd85b4302b2cdcb835ad3f94fd00d8da07e570b41bc0d2bcf69a8de1b3284"), - MustHexID("d84f06fe64debc4cd0625e36d19b99014b6218375262cc2209202bdbafd7dffcc4e34ce6398e182e02fd8faeed622c3e175545864902dfd3d1ac57647cddf4c6"), - MustHexID("d0ed87b294f38f1d741eb601020eeec30ac16331d05880fe27868f1e454446de367d7457b41c79e202eaf9525b029e4f1d7e17d85a55f83a557c005c68d7328a"), - }, - 253: { - MustHexID("ad4485e386e3cc7c7310366a7c38fb810b8896c0d52e55944bfd320ca294e7912d6c53c0a0cf85e7ce226e92491d60430e86f8f15cda0161ed71893fb4a9e3a1"), - MustHexID("36d0e7e5b7734f98c6183eeeb8ac5130a85e910a925311a19c4941b1290f945d4fc3996b12ef4966960b6fa0fb29b1604f83a0f81bd5fd6398d2e1a22e46af0c"), - MustHexID("7d307d8acb4a561afa23bdf0bd945d35c90245e26345ec3a1f9f7df354222a7cdcb81339c9ed6744526c27a1a0c8d10857e98df942fa433602facac71ac68a31"), - MustHexID("d97bf55f88c83fae36232661af115d66ca600fc4bd6d1fb35ff9bb4dad674c02cf8c8d05f317525b5522250db58bb1ecafb7157392bf5aa61b178c61f098d995"), - MustHexID("7045d678f1f9eb7a4613764d17bd5698796494d0bf977b16f2dbc272b8a0f7858a60805c022fc3d1fe4f31c37e63cdaca0416c0d053ef48a815f8b19121605e0"), - MustHexID("14e1f21418d445748de2a95cd9a8c3b15b506f86a0acabd8af44bb968ce39885b19c8822af61b3dd58a34d1f265baec30e3ae56149dc7d2aa4a538f7319f69c8"), - MustHexID("b9453d78281b66a4eac95a1546017111eaaa5f92a65d0de10b1122940e92b319728a24edf4dec6acc412321b1c95266d39c7b3a5d265c629c3e49a65fb022c09"), - MustHexID("e8a49248419e3824a00d86af422f22f7366e2d4922b304b7169937616a01d9d6fa5abf5cc01061a352dc866f48e1fa2240dbb453d872b1d7be62bdfc1d5e248c"), - MustHexID("bebcff24b52362f30e0589ee573ce2d86f073d58d18e6852a592fa86ceb1a6c9b96d7fb9ec7ed1ed98a51b6743039e780279f6bb49d0a04327ac7a182d9a56f6"), - MustHexID("d0835e5a4291db249b8d2fca9f503049988180c7d247bedaa2cf3a1bad0a76709360a85d4f9a1423b2cbc82bb4d94b47c0cde20afc430224834c49fe312a9ae3"), - MustHexID("6b087fe2a2da5e4f0b0f4777598a4a7fb66bf77dbd5bfc44e8a7eaa432ab585a6e226891f56a7d4f5ed11a7c57b90f1661bba1059590ca4267a35801c2802913"), - MustHexID("d901e5bde52d1a0f4ddf010a686a53974cdae4ebe5c6551b3c37d6b6d635d38d5b0e5f80bc0186a2c7809dbf3a42870dd09643e68d32db896c6da8ba734579e7"), - MustHexID("96419fb80efae4b674402bb969ebaab86c1274f29a83a311e24516d36cdf148fe21754d46c97688cdd7468f24c08b13e4727c29263393638a3b37b99ff60ebca"), - MustHexID("7b9c1889ae916a5d5abcdfb0aaedcc9c6f9eb1c1a4f68d0c2d034fe79ac610ce917c3abc670744150fa891bfcd8ab14fed6983fca964de920aa393fa7b326748"), - MustHexID("7a369b2b8962cc4c65900be046482fbf7c14f98a135bbbae25152c82ad168fb2097b3d1429197cf46d3ce9fdeb64808f908a489cc6019725db040060fdfe5405"), - MustHexID("47bcae48288da5ecc7f5058dfa07cf14d89d06d6e449cb946e237aa6652ea050d9f5a24a65efdc0013ccf232bf88670979eddef249b054f63f38da9d7796dbd8"), - }, - 254: { - MustHexID("099739d7abc8abd38ecc7a816c521a1168a4dbd359fa7212a5123ab583ffa1cf485a5fed219575d6475dbcdd541638b2d3631a6c7fce7474e7fe3cba1d4d5853"), - MustHexID("c2b01603b088a7182d0cf7ef29fb2b04c70acb320fccf78526bf9472e10c74ee70b3fcfa6f4b11d167bd7d3bc4d936b660f2c9bff934793d97cb21750e7c3d31"), - MustHexID("20e4d8f45f2f863e94b45548c1ef22a11f7d36f263e4f8623761e05a64c4572379b000a52211751e2561b0f14f4fc92dd4130410c8ccc71eb4f0e95a700d4ca9"), - MustHexID("27f4a16cc085e72d86e25c98bd2eca173eaaee7565c78ec5a52e9e12b2211f35de81b5b45e9195de2ebfe29106742c59112b951a04eb7ae48822911fc1f9389e"), - MustHexID("55db5ee7d98e7f0b1c3b9d5be6f2bc619a1b86c3cdd513160ad4dcf267037a5fffad527ac15d50aeb32c59c13d1d4c1e567ebbf4de0d25236130c8361f9aac63"), - MustHexID("883df308b0130fc928a8559fe50667a0fff80493bc09685d18213b2db241a3ad11310ed86b0ef662b3ce21fc3d9aa7f3fc24b8d9afe17c7407e9afd3345ae548"), - MustHexID("c7af968cc9bc8200c3ee1a387405f7563be1dce6710a3439f42ea40657d0eae9d2b3c16c42d779605351fcdece4da637b9804e60ca08cfb89aec32c197beffa6"), - MustHexID("3e66f2b788e3ff1d04106b80597915cd7afa06c405a7ae026556b6e583dca8e05cfbab5039bb9a1b5d06083ffe8de5780b1775550e7218f5e98624bf7af9a0a8"), - MustHexID("4fc7f53764de3337fdaec0a711d35d3a923e72fa65025444d12230b3552ed43d9b2d1ad08ccb11f2d50c58809e6dd74dde910e195294fca3b47ae5a3967cc479"), - MustHexID("bafdfdcf6ccaa989436752fa97c77477b6baa7deb374b16c095492c529eb133e8e2f99e1977012b64767b9d34b2cf6d2048ed489bd822b5139b523f6a423167b"), - MustHexID("7f5d78008a4312fe059104ce80202c82b8915c2eb4411c6b812b16f7642e57c00f2c9425121f5cbac4257fe0b3e81ef5dea97ea2dbaa98f6a8b6fd4d1e5980bb"), - MustHexID("598c37fe78f922751a052f463aeb0cb0bc7f52b7c2a4cf2da72ec0931c7c32175d4165d0f8998f7320e87324ac3311c03f9382a5385c55f0407b7a66b2acd864"), - MustHexID("f758c4136e1c148777a7f3275a76e2db0b2b04066fd738554ec398c1c6cc9fb47e14a3b4c87bd47deaeab3ffd2110514c3855685a374794daff87b605b27ee2e"), - MustHexID("0307bb9e4fd865a49dcf1fe4333d1b944547db650ab580af0b33e53c4fef6c789531110fac801bbcbce21fc4d6f61b6d5b24abdf5b22e3030646d579f6dca9c2"), - MustHexID("82504b6eb49bb2c0f91a7006ce9cefdbaf6df38706198502c2e06601091fc9dc91e4f15db3410d45c6af355bc270b0f268d3dff560f956985c7332d4b10bd1ed"), - MustHexID("b39b5b677b45944ceebe76e76d1f051de2f2a0ec7b0d650da52135743e66a9a5dba45f638258f9a7545d9a790c7fe6d3fdf82c25425c7887323e45d27d06c057"), - }, - 255: { - MustHexID("5c4d58d46e055dd1f093f81ee60a675e1f02f54da6206720adee4dccef9b67a31efc5c2a2949c31a04ee31beadc79aba10da31440a1f9ff2a24093c63c36d784"), - MustHexID("ea72161ffdd4b1e124c7b93b0684805f4c4b58d617ed498b37a145c670dbc2e04976f8785583d9c805ffbf343c31d492d79f841652bbbd01b61ed85640b23495"), - MustHexID("51caa1d93352d47a8e531692a3612adac1e8ac68d0a200d086c1c57ae1e1a91aa285ab242e8c52ef9d7afe374c9485b122ae815f1707b875569d0433c1c3ce85"), - MustHexID("c08397d5751b47bd3da044b908be0fb0e510d3149574dff7aeab33749b023bb171b5769990fe17469dbebc100bc150e798aeda426a2dcc766699a225fddd75c6"), - MustHexID("0222c1c194b749736e593f937fad67ee348ac57287a15c7e42877aa38a9b87732a408bca370f812efd0eedbff13e6d5b854bf3ba1dec431a796ed47f32552b09"), - MustHexID("03d859cd46ef02d9bfad5268461a6955426845eef4126de6be0fa4e8d7e0727ba2385b78f1a883a8239e95ebb814f2af8379632c7d5b100688eebc5841209582"), - MustHexID("64d5004b7e043c39ff0bd10cb20094c287721d5251715884c280a612b494b3e9e1c64ba6f67614994c7d969a0d0c0295d107d53fc225d47c44c4b82852d6f960"), - MustHexID("b0a5eefb2dab6f786670f35bf9641eefe6dd87fd3f1362bcab4aaa792903500ab23d88fae68411372e0813b057535a601d46e454323745a948017f6063a47b1f"), - MustHexID("0cc6df0a3433d448b5684d2a3ffa9d1a825388177a18f44ad0008c7bd7702f1ec0fc38b83506f7de689c3b6ecb552599927e29699eed6bb867ff08f80068b287"), - MustHexID("50772f7b8c03a4e153355fbbf79c8a80cf32af656ff0c7873c99911099d04a0dae0674706c357e0145ad017a0ade65e6052cb1b0d574fcd6f67da3eee0ace66b"), - MustHexID("1ae37829c9ef41f8b508b82259ebac76b1ed900d7a45c08b7970f25d2d48ddd1829e2f11423a18749940b6dab8598c6e416cef0efd47e46e51f29a0bc65b37cd"), - MustHexID("ba973cab31c2af091fc1644a93527d62b2394999e2b6ccbf158dd5ab9796a43d408786f1803ef4e29debfeb62fce2b6caa5ab2b24d1549c822a11c40c2856665"), - MustHexID("bc413ad270dd6ea25bddba78f3298b03b8ba6f8608ac03d06007d4116fa78ef5a0cfe8c80155089382fc7a193243ee5500082660cb5d7793f60f2d7d18650964"), - MustHexID("5a6a9ef07634d9eec3baa87c997b529b92652afa11473dfee41ef7037d5c06e0ddb9fe842364462d79dd31cff8a59a1b8d5bc2b810dea1d4cbbd3beb80ecec83"), - MustHexID("f492c6ee2696d5f682f7f537757e52744c2ae560f1090a07024609e903d334e9e174fc01609c5a229ddbcac36c9d21adaf6457dab38a25bfd44f2f0ee4277998"), - MustHexID("459e4db99298cb0467a90acee6888b08bb857450deac11015cced5104853be5adce5b69c740968bc7f931495d671a70cad9f48546d7cd203357fe9af0e8d2164"), - }, - 256: { - MustHexID("a8593af8a4aef7b806b5197612017951bac8845a1917ca9a6a15dd6086d608505144990b245785c4cd2d67a295701c7aac2aa18823fb0033987284b019656268"), - MustHexID("d2eebef914928c3aad77fc1b2a495f52d2294acf5edaa7d8a530b540f094b861a68fe8348a46a7c302f08ab609d85912a4968eacfea0740847b29421b4795d9e"), - MustHexID("b14bfcb31495f32b650b63cf7d08492e3e29071fdc73cf2da0da48d4b191a70ba1a65f42ad8c343206101f00f8a48e8db4b08bf3f622c0853e7323b250835b91"), - MustHexID("7feaee0d818c03eb30e4e0bf03ade0f3c21ca38e938a761aa1781cf70bda8cc5cd631a6cc53dd44f1d4a6d3e2dae6513c6c66ee50cb2f0e9ad6f7e319b309fd9"), - MustHexID("4ca3b657b139311db8d583c25dd5963005e46689e1317620496cc64129c7f3e52870820e0ec7941d28809311df6db8a2867bbd4f235b4248af24d7a9c22d1232"), - MustHexID("1181defb1d16851d42dd951d84424d6bd1479137f587fa184d5a8152be6b6b16ed08bcdb2c2ed8539bcde98c80c432875f9f724737c316a2bd385a39d3cab1d8"), - MustHexID("d9dd818769fa0c3ec9f553c759b92476f082817252a04a47dc1777740b1731d280058c66f982812f173a294acf4944a85ba08346e2de153ba3ba41ce8a62cb64"), - MustHexID("bd7c4f8a9e770aa915c771b15e107ca123d838762da0d3ffc53aa6b53e9cd076cffc534ec4d2e4c334c683f1f5ea72e0e123f6c261915ed5b58ac1b59f003d88"), - MustHexID("3dd5739c73649d510456a70e9d6b46a855864a4a3f744e088fd8c8da11b18e4c9b5f2d7da50b1c147b2bae5ca9609ae01f7a3cdea9dce34f80a91d29cd82f918"), - MustHexID("f0d7df1efc439b4bcc0b762118c1cfa99b2a6143a9f4b10e3c9465125f4c9fca4ab88a2504169bbcad65492cf2f50da9dd5d077c39574a944f94d8246529066b"), - MustHexID("dd598b9ba441448e5fb1a6ec6c5f5aa9605bad6e223297c729b1705d11d05f6bfd3d41988b694681ae69bb03b9a08bff4beab5596503d12a39bffb5cd6e94c7c"), - MustHexID("3fce284ac97e567aebae681b15b7a2b6df9d873945536335883e4bbc26460c064370537f323fd1ada828ea43154992d14ac0cec0940a2bd2a3f42ec156d60c83"), - MustHexID("7c8dfa8c1311cb14fb29a8ac11bca23ecc115e56d9fcf7b7ac1db9066aa4eb39f8b1dabf46e192a65be95ebfb4e839b5ab4533fef414921825e996b210dd53bd"), - MustHexID("cafa6934f82120456620573d7f801390ed5e16ed619613a37e409e44ab355ef755e83565a913b48a9466db786f8d4fbd590bfec474c2524d4a2608d4eafd6abd"), - MustHexID("9d16600d0dd310d77045769fed2cb427f32db88cd57d86e49390c2ba8a9698cfa856f775be2013237226e7bf47b248871cf865d23015937d1edeb20db5e3e760"), - MustHexID("17be6b6ba54199b1d80eff866d348ea11d8a4b341d63ad9a6681d3ef8a43853ac564d153eb2a8737f0afc9ab320f6f95c55aa11aaa13bbb1ff422fd16bdf8188"), - }, - }, -} - -type preminedTestnet struct { - target NodeID - targetSha common.Hash // sha3(target) - dists [hashBits + 1][]NodeID - net *Network -} - -func (tn *preminedTestnet) sendFindnodeHash(to *Node, target common.Hash) { - // current log distance is encoded in port number - // fmt.Println("findnode query at dist", toaddr.Port) - if to.UDP <= lowPort { - panic("query to node at or below distance 0") - } - next := to.UDP - 1 - var result []rpcNode - for i, id := range tn.dists[to.UDP-lowPort] { - result = append(result, nodeToRPC(NewNode(id, net.ParseIP("10.0.2.99"), next, uint16(i)+1+lowPort))) - } - injectResponse(tn.net, to, neighborsPacket, &neighbors{Nodes: result}) -} - -func (tn *preminedTestnet) sendPing(to *Node, addr *net.UDPAddr, topics []Topic) []byte { - injectResponse(tn.net, to, pongPacket, &pong{ReplyTok: []byte{1}}) - return []byte{1} -} - -func (tn *preminedTestnet) send(to *Node, ptype nodeEvent, data interface{}) (hash []byte) { - switch ptype { - case pingPacket: - injectResponse(tn.net, to, pongPacket, &pong{ReplyTok: []byte{1}}) - case pongPacket: - // ignored - case findnodeHashPacket: - // current log distance is encoded in port number - // fmt.Println("findnode query at dist", toaddr.Port-lowPort) - if to.UDP <= lowPort { - panic("query to node at or below distance 0") - } - next := to.UDP - 1 - var result []rpcNode - for i, id := range tn.dists[to.UDP-lowPort] { - result = append(result, nodeToRPC(NewNode(id, net.ParseIP("10.0.2.99"), next, uint16(i)+1+lowPort))) - } - injectResponse(tn.net, to, neighborsPacket, &neighbors{Nodes: result}) - default: - panic("send(" + ptype.String() + ")") - } - return []byte{2} -} - -func (tn *preminedTestnet) sendNeighbours(to *Node, nodes []*Node) { - panic("sendNeighbours called") -} - -func (tn *preminedTestnet) sendTopicNodes(to *Node, queryHash common.Hash, nodes []*Node) { - panic("sendTopicNodes called") -} - -func (tn *preminedTestnet) sendTopicRegister(to *Node, topics []Topic, idx int, pong []byte) { - panic("sendTopicRegister called") -} - -func (*preminedTestnet) Close() {} - -func (*preminedTestnet) localAddr() *net.UDPAddr { - return &net.UDPAddr{IP: net.ParseIP("10.0.1.1"), Port: 40000} -} - -func injectResponse(net *Network, from *Node, ev nodeEvent, packet interface{}) { - go net.reqReadPacket(ingressPacket{remoteID: from.ID, remoteAddr: from.addr(), ev: ev, data: packet}) -} diff --git a/p2p/discv5/node.go b/p2p/discv5/node.go deleted file mode 100644 index 44d3025b70..0000000000 --- a/p2p/discv5/node.go +++ /dev/null @@ -1,413 +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 . - -package discv5 - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "encoding/hex" - "errors" - "fmt" - "math/big" - "math/rand" - "net" - "net/url" - "regexp" - "strconv" - "strings" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -// Node represents a host on the network. -// The public fields of Node may not be modified. -type Node struct { - IP net.IP // len 4 for IPv4 or 16 for IPv6 - UDP, TCP uint16 // port numbers - ID NodeID // the node's public key - - // Network-related fields are contained in nodeNetGuts. - // These fields are not supposed to be used off the - // Network.loop goroutine. - nodeNetGuts -} - -// NewNode creates a new node. It is mostly meant to be used for -// testing purposes. -func NewNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node { - if ipv4 := ip.To4(); ipv4 != nil { - ip = ipv4 - } - return &Node{ - IP: ip, - UDP: udpPort, - TCP: tcpPort, - ID: id, - nodeNetGuts: nodeNetGuts{sha: crypto.Keccak256Hash(id[:])}, - } -} - -func (n *Node) addr() *net.UDPAddr { - return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)} -} - -// Incomplete returns true for nodes with no IP address. -func (n *Node) Incomplete() bool { - return n.IP == nil -} - -// checks whether n is a valid complete node. -func (n *Node) validateComplete() error { - if n.Incomplete() { - return errors.New("incomplete node") - } - if n.UDP == 0 { - return errors.New("missing UDP port") - } - if n.TCP == 0 { - return errors.New("missing TCP port") - } - if n.IP.IsMulticast() || n.IP.IsUnspecified() { - return errors.New("invalid IP (multicast/unspecified)") - } - _, err := n.ID.Pubkey() // validate the key (on curve, etc.) - return err -} - -// The string representation of a Node is a URL. -// Please see ParseNode for a description of the format. -func (n *Node) String() string { - u := url.URL{Scheme: "enode"} - if n.Incomplete() { - u.Host = fmt.Sprintf("%x", n.ID[:]) - } else { - addr := net.TCPAddr{IP: n.IP, Port: int(n.TCP)} - u.User = url.User(fmt.Sprintf("%x", n.ID[:])) - u.Host = addr.String() - if n.UDP != n.TCP { - u.RawQuery = "discport=" + strconv.Itoa(int(n.UDP)) - } - } - return u.String() -} - -var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$") - -// ParseNode parses a node designator. -// -// There are two basic forms of node designators -// - incomplete nodes, which only have the public key (node ID) -// - complete nodes, which contain the public key and IP/Port information -// -// For incomplete nodes, the designator must look like one of these -// -// enode:// -// -// -// For complete nodes, the node ID is encoded in the username portion -// of the URL, separated from the host by an @ sign. The hostname can -// only be given as an IP address, DNS domain names are not allowed. -// The port in the host name section is the TCP listening port. If the -// TCP and UDP (discovery) ports differ, the UDP port is specified as -// query parameter "discport". -// -// In the following example, the node URL describes -// a node with IP address 10.3.58.6, TCP listening port 30303 -// and UDP discovery port 30301. -// -// enode://@10.3.58.6:30303?discport=30301 -func ParseNode(rawurl string) (*Node, error) { - if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil { - id, err := HexID(m[1]) - if err != nil { - return nil, fmt.Errorf("invalid node ID (%v)", err) - } - return NewNode(id, nil, 0, 0), nil - } - return parseComplete(rawurl) -} - -func parseComplete(rawurl string) (*Node, error) { - var ( - id NodeID - ip net.IP - tcpPort, udpPort uint64 - ) - u, err := url.Parse(rawurl) - if err != nil { - return nil, err - } - if u.Scheme != "enode" { - return nil, errors.New("invalid URL scheme, want \"enode\"") - } - // Parse the Node ID from the user portion. - if u.User == nil { - return nil, errors.New("does not contain node ID") - } - if id, err = HexID(u.User.String()); err != nil { - return nil, fmt.Errorf("invalid node ID (%v)", err) - } - // Parse the IP address. - host, port, err := net.SplitHostPort(u.Host) - if err != nil { - return nil, fmt.Errorf("invalid host: %v", err) - } - if ip = net.ParseIP(host); ip == nil { - return nil, errors.New("invalid IP address") - } - // Ensure the IP is 4 bytes long for IPv4 addresses. - if ipv4 := ip.To4(); ipv4 != nil { - ip = ipv4 - } - // Parse the port numbers. - if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil { - return nil, errors.New("invalid port") - } - udpPort = tcpPort - qv := u.Query() - if qv.Get("discport") != "" { - udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16) - if err != nil { - return nil, errors.New("invalid discport in query") - } - } - return NewNode(id, ip, uint16(udpPort), uint16(tcpPort)), nil -} - -// MustParseNode parses a node URL. It panics if the URL is not valid. -func MustParseNode(rawurl string) *Node { - n, err := ParseNode(rawurl) - if err != nil { - panic("invalid node URL: " + err.Error()) - } - 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. -// func (nl *nodeList) appendNew(n *Node) { -// for _, entry := range n { -// if entry == n { -// return -// } -// } -// *nq = append(*nq, n) -// } -// -// // popRandom removes a random node. Nodes closer to -// // to the head of the beginning of the have a slightly higher probability. -// func (nl *nodeList) popRandom() *Node { -// ix := rand.Intn(len(*nq)) -// //TODO: probability as mentioned above. -// nl.removeIndex(ix) -// } -// -// func (nl *nodeList) removeIndex(i int) *Node { -// slice = *nl -// if len(*slice) <= i { -// return nil -// } -// *nl = append(slice[:i], slice[i+1:]...) -// } - -const nodeIDBits = 512 - -// NodeID is a unique identifier for each node. -// The node identifier is a marshaled elliptic curve public key. -type NodeID [nodeIDBits / 8]byte - -// NodeID prints as a long hexadecimal number. -func (n NodeID) String() string { - return fmt.Sprintf("%x", n[:]) -} - -// The Go syntax representation of a NodeID is a call to HexID. -func (n NodeID) GoString() string { - return fmt.Sprintf("discover.HexID(\"%x\")", n[:]) -} - -// TerminalString returns a shortened hex string for terminal logging. -func (n NodeID) TerminalString() string { - return hex.EncodeToString(n[:8]) -} - -// HexID converts a hex string to a NodeID. -// The string may be prefixed with 0x. -func HexID(in string) (NodeID, error) { - var id NodeID - b, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) - if err != nil { - return id, err - } else if len(b) != len(id) { - return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2) - } - copy(id[:], b) - return id, nil -} - -// MustHexID converts a hex string to a NodeID. -// It panics if the string is not a valid NodeID. -func MustHexID(in string) NodeID { - id, err := HexID(in) - if err != nil { - panic(err) - } - return id -} - -// PubkeyID returns a marshaled representation of the given public key. -func PubkeyID(pub *ecdsa.PublicKey) NodeID { - var id NodeID - pbytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y) - if len(pbytes)-1 != len(id) { - panic(fmt.Errorf("need %d bit pubkey, got %d bits", (len(id)+1)*8, len(pbytes))) - } - copy(id[:], pbytes[1:]) - return id -} - -// Pubkey returns the public key represented by the node ID. -// It returns an error if the ID is not a point on the curve. -func (n NodeID) Pubkey() (*ecdsa.PublicKey, error) { - p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)} - half := len(n) / 2 - p.X.SetBytes(n[:half]) - p.Y.SetBytes(n[half:]) - if !p.Curve.IsOnCurve(p.X, p.Y) { - return nil, errors.New("id is invalid secp256k1 curve point") - } - return p, nil -} - -// recoverNodeID computes the public key used to sign the -// given hash from the signature. -func recoverNodeID(hash, sig []byte) (id NodeID, err error) { - pubkey, err := crypto.Ecrecover(hash, sig) - if err != nil { - return id, err - } - if len(pubkey)-1 != len(id) { - return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id)+1)*8) - } - for i := range id { - id[i] = pubkey[i+1] - } - return id, nil -} - -// distcmp compares the distances a->target and b->target. -// Returns -1 if a is closer to target, 1 if b is closer to target -// and 0 if they are equal. -func distcmp(target, a, b common.Hash) int { - for i := range target { - da := a[i] ^ target[i] - db := b[i] ^ target[i] - if da > db { - return 1 - } else if da < db { - return -1 - } - } - return 0 -} - -// table of leading zero counts for bytes [0..255] -var lzcount = [256]int{ - 8, 7, 6, 6, 5, 5, 5, 5, - 4, 4, 4, 4, 4, 4, 4, 4, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 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, 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, 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, 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, 0, 0, 0, 0, -} - -// logdist returns the logarithmic distance between a and b, log2(a ^ b). -func logdist(a, b common.Hash) int { - lz := 0 - for i := range a { - x := a[i] ^ b[i] - if x == 0 { - lz += 8 - } else { - lz += lzcount[x] - break - } - } - return len(a)*8 - lz -} - -// hashAtDistance returns a random hash such that logdist(a, b) == n -func hashAtDistance(a common.Hash, n int) (b common.Hash) { - if n == 0 { - return a - } - // flip bit at position n, fill the rest with random bits - b = a - pos := len(a) - n/8 - 1 - bit := byte(0x01) << (byte(n%8) - 1) - if bit == 0 { - pos++ - bit = 0x80 - } - b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits - for i := pos + 1; i < len(a); i++ { - b[i] = byte(rand.Intn(255)) - } - return b -} diff --git a/p2p/discv5/node_test.go b/p2p/discv5/node_test.go deleted file mode 100644 index 4e0fdbe3db..0000000000 --- a/p2p/discv5/node_test.go +++ /dev/null @@ -1,305 +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 . - -package discv5 - -import ( - "fmt" - "math/big" - "math/rand" - "net" - "reflect" - "strings" - "testing" - "testing/quick" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -func ExampleNewNode() { - id := MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439") - - // Complete nodes contain UDP and TCP endpoints: - n1 := NewNode(id, net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 52150, 30303) - fmt.Println("n1:", n1) - fmt.Println("n1.Incomplete() ->", n1.Incomplete()) - - // An incomplete node can be created by passing zero values - // for all parameters except id. - n2 := NewNode(id, nil, 0, 0) - fmt.Println("n2:", n2) - fmt.Println("n2.Incomplete() ->", n2.Incomplete()) - - // Output: - // n1: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:30303?discport=52150 - // n1.Incomplete() -> false - // n2: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439 - // n2.Incomplete() -> true -} - -var parseNodeTests = []struct { - rawurl string - wantError string - wantResult *Node -}{ - { - rawurl: "http://foobar", - wantError: `invalid URL scheme, want "enode"`, - }, - { - rawurl: "enode://01010101@123.124.125.126:3", - wantError: `invalid node ID (wrong length, want 128 hex chars)`, - }, - // Complete nodes with IP address. - { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@hostname:3", - wantError: `invalid IP address`, - }, - { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:foo", - wantError: `invalid port`, - }, - { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:3?discport=foo", - wantError: `invalid discport in query`, - }, - { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150", - wantResult: NewNode( - MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - net.IP{0x7f, 0x0, 0x0, 0x1}, - 52150, - 52150, - ), - }, - { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150", - wantResult: NewNode( - MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - net.ParseIP("::"), - 52150, - 52150, - ), - }, - { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150", - wantResult: NewNode( - MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), - 52150, - 52150, - ), - }, - { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334", - wantResult: NewNode( - MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - net.IP{0x7f, 0x0, 0x0, 0x1}, - 22334, - 52150, - ), - }, - // Incomplete nodes with no address. - { - rawurl: "1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439", - wantResult: NewNode( - MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - nil, 0, 0, - ), - }, - { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439", - wantResult: NewNode( - MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - nil, 0, 0, - ), - }, - // Invalid URLs - { - rawurl: "01010101", - wantError: `invalid node ID (wrong length, want 128 hex chars)`, - }, - { - rawurl: "enode://01010101", - wantError: `invalid node ID (wrong length, want 128 hex chars)`, - }, - { - // This test checks that errors from url.Parse are handled. - rawurl: "://foo", - wantError: `missing protocol scheme`, - }, -} - -func TestParseNode(t *testing.T) { - for _, test := range parseNodeTests { - n, err := ParseNode(test.rawurl) - if test.wantError != "" { - if err == nil { - t.Errorf("test %q:\n got nil error, expected %#q", test.rawurl, test.wantError) - continue - } else if !strings.Contains(err.Error(), test.wantError) { - t.Errorf("test %q:\n got error %#q, expected %#q", test.rawurl, err.Error(), test.wantError) - continue - } - } else { - if err != nil { - t.Errorf("test %q:\n unexpected error: %v", test.rawurl, err) - continue - } - if !reflect.DeepEqual(n, test.wantResult) { - t.Errorf("test %q:\n result mismatch:\ngot: %#v, want: %#v", test.rawurl, n, test.wantResult) - } - } - } -} - -func TestNodeString(t *testing.T) { - for i, test := range parseNodeTests { - if test.wantError == "" && strings.HasPrefix(test.rawurl, "enode://") { - str := test.wantResult.String() - if str != test.rawurl { - t.Errorf("test %d: Node.String() mismatch:\ngot: %s\nwant: %s", i, str, test.rawurl) - } - } - } -} - -func TestHexID(t *testing.T) { - ref := NodeID{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, 0, 0, 0, 0, 0, 0, 0, 0, 128, 106, 217, 182, 31, 165, 174, 1, 67, 7, 235, 220, 150, 66, 83, 173, 205, 159, 44, 10, 57, 42, 161, 26, 188} - id1 := MustHexID("0x000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") - id2 := MustHexID("000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") - - if id1 != ref { - t.Errorf("wrong id1\ngot %v\nwant %v", id1[:], ref[:]) - } - if id2 != ref { - t.Errorf("wrong id2\ngot %v\nwant %v", id2[:], ref[:]) - } -} - -func TestNodeID_recover(t *testing.T) { - prv := newkey() - hash := make([]byte, 32) - sig, err := crypto.Sign(hash, prv) - if err != nil { - t.Fatalf("signing error: %v", err) - } - - pub := PubkeyID(&prv.PublicKey) - recpub, err := recoverNodeID(hash, sig) - if err != nil { - t.Fatalf("recovery error: %v", err) - } - if pub != recpub { - t.Errorf("recovered wrong pubkey:\ngot: %v\nwant: %v", recpub, pub) - } - - ecdsa, err := pub.Pubkey() - if err != nil { - t.Errorf("Pubkey error: %v", err) - } - if !reflect.DeepEqual(ecdsa, &prv.PublicKey) { - t.Errorf("Pubkey mismatch:\n got: %#v\n want: %#v", ecdsa, &prv.PublicKey) - } -} - -func TestNodeID_pubkeyBad(t *testing.T) { - ecdsa, err := NodeID{}.Pubkey() - if err == nil { - t.Error("expected error for zero ID") - } - if ecdsa != nil { - t.Error("expected nil result") - } -} - -func TestNodeID_distcmp(t *testing.T) { - distcmpBig := func(target, a, b common.Hash) int { - tbig := new(big.Int).SetBytes(target[:]) - abig := new(big.Int).SetBytes(a[:]) - bbig := new(big.Int).SetBytes(b[:]) - return new(big.Int).Xor(tbig, abig).Cmp(new(big.Int).Xor(tbig, bbig)) - } - if err := quick.CheckEqual(distcmp, distcmpBig, quickcfg()); err != nil { - t.Error(err) - } -} - -// the random tests is likely to miss the case where they're equal. -func TestNodeID_distcmpEqual(t *testing.T) { - base := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} - x := common.Hash{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} - if distcmp(base, x, x) != 0 { - t.Errorf("distcmp(base, x, x) != 0") - } -} - -func TestNodeID_logdist(t *testing.T) { - logdistBig := func(a, b common.Hash) int { - abig, bbig := new(big.Int).SetBytes(a[:]), new(big.Int).SetBytes(b[:]) - return new(big.Int).Xor(abig, bbig).BitLen() - } - if err := quick.CheckEqual(logdist, logdistBig, quickcfg()); err != nil { - t.Error(err) - } -} - -// the random tests is likely to miss the case where they're equal. -func TestNodeID_logdistEqual(t *testing.T) { - x := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} - if logdist(x, x) != 0 { - t.Errorf("logdist(x, x) != 0") - } -} - -func TestNodeID_hashAtDistance(t *testing.T) { - // we don't use quick.Check here because its output isn't - // very helpful when the test fails. - cfg := quickcfg() - for i := 0; i < cfg.MaxCount; i++ { - a := gen(common.Hash{}, cfg.Rand).(common.Hash) - dist := cfg.Rand.Intn(len(common.Hash{}) * 8) - result := hashAtDistance(a, dist) - actualdist := logdist(result, a) - - if dist != actualdist { - t.Log("a: ", a) - t.Log("result:", result) - t.Fatalf("#%d: distance of result is %d, want %d", i, actualdist, dist) - } - } -} - -func quickcfg() *quick.Config { - return &quick.Config{ - MaxCount: 5000, - Rand: rand.New(rand.NewSource(time.Now().Unix())), - } -} - -// TODO: The Generate method can be dropped when we require Go >= 1.5 -// because testing/quick learned to generate arrays in 1.5. - -func (NodeID) Generate(rand *rand.Rand, size int) reflect.Value { - var id NodeID - m := rand.Intn(len(id)) - for i := len(id) - 1; i > m; i-- { - id[i] = byte(rand.Uint32()) - } - return reflect.ValueOf(id) -} diff --git a/p2p/discv5/nodeevent_string.go b/p2p/discv5/nodeevent_string.go deleted file mode 100644 index 38c1993bac..0000000000 --- a/p2p/discv5/nodeevent_string.go +++ /dev/null @@ -1,17 +0,0 @@ -// Code generated by "stringer -type=nodeEvent"; DO NOT EDIT. - -package discv5 - -import "strconv" - -const _nodeEvent_name = "pongTimeoutpingTimeoutneighboursTimeout" - -var _nodeEvent_index = [...]uint8{0, 11, 22, 39} - -func (i nodeEvent) String() string { - i -= 264 - if i >= nodeEvent(len(_nodeEvent_index)-1) { - return "nodeEvent(" + strconv.FormatInt(int64(i+264), 10) + ")" - } - return _nodeEvent_name[_nodeEvent_index[i]:_nodeEvent_index[i+1]] -} diff --git a/p2p/discv5/sim_run_test.go b/p2p/discv5/sim_run_test.go deleted file mode 100644 index bded0cc023..0000000000 --- a/p2p/discv5/sim_run_test.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package discv5 - -import ( - "bufio" - "bytes" - "encoding/binary" - "errors" - "fmt" - "io" - "os" - "os/exec" - "runtime" - "strings" - "testing" -) - -func getnacl() (string, error) { - switch runtime.GOARCH { - case "amd64": - _, err := exec.LookPath("sel_ldr_x86_64") - return "amd64p32", err - case "i386": - _, err := exec.LookPath("sel_ldr_i386") - return "i386", err - default: - return "", errors.New("nacl is not supported on " + runtime.GOARCH) - } -} - -// runWithPlaygroundTime executes the caller -// in the NaCl sandbox with faketime enabled. -// -// This function must be called from a Test* function -// and the caller must skip the actual test when isHost is true. -func runWithPlaygroundTime(t *testing.T) (isHost bool) { - if runtime.GOOS == "nacl" { - return false - } - - // Get the caller. - callerPC, _, _, ok := runtime.Caller(1) - if !ok { - panic("can't get caller") - } - callerFunc := runtime.FuncForPC(callerPC) - if callerFunc == nil { - panic("can't get caller") - } - callerName := callerFunc.Name()[strings.LastIndexByte(callerFunc.Name(), '.')+1:] - if !strings.HasPrefix(callerName, "Test") { - panic("must be called from witin a Test* function") - } - testPattern := "^" + callerName + "$" - - // Unfortunately runtime.faketime (playground time mode) only works on NaCl. The NaCl - // SDK must be installed and linked into PATH for this to work. - arch, err := getnacl() - if err != nil { - t.Skip(err) - } - - // Compile and run the calling test using NaCl. - // The extra tag ensures that the TestMain function in sim_main_test.go is used. - cmd := exec.Command("go", "test", "-v", "-tags", "faketime_simulation", "-timeout", "100h", "-run", testPattern, ".") - cmd.Env = append([]string{"GOOS=nacl", "GOARCH=" + arch}, os.Environ()...) - stdout, _ := cmd.StdoutPipe() - stderr, _ := cmd.StderrPipe() - go skipPlaygroundOutputHeaders(os.Stdout, stdout) - go skipPlaygroundOutputHeaders(os.Stderr, stderr) - if err := cmd.Run(); err != nil { - t.Error(err) - } - - // Ensure that the test function doesn't run in the (non-NaCl) host process. - return true -} - -func skipPlaygroundOutputHeaders(out io.Writer, in io.Reader) { - // Additional output can be printed without the headers - // before the NaCl binary starts running (e.g. compiler error messages). - bufin := bufio.NewReader(in) - output, err := bufin.ReadBytes(0) - output = bytes.TrimSuffix(output, []byte{0}) - if len(output) > 0 { - out.Write(output) - } - if err != nil { - return - } - bufin.UnreadByte() - - // Playback header: 0 0 P B <8-byte time> <4-byte data length> - head := make([]byte, 4+8+4) - for { - if _, err := io.ReadFull(bufin, head); err != nil { - if err != io.EOF { - fmt.Fprintln(out, "read error:", err) - } - return - } - if !bytes.HasPrefix(head, []byte{0x00, 0x00, 'P', 'B'}) { - fmt.Fprintf(out, "expected playback header, got %q\n", head) - io.Copy(out, bufin) - return - } - // Copy data until next header. - size := binary.BigEndian.Uint32(head[12:]) - io.CopyN(out, bufin, int64(size)) - } -} diff --git a/p2p/discv5/sim_test.go b/p2p/discv5/sim_test.go deleted file mode 100644 index 3d1e610d3a..0000000000 --- a/p2p/discv5/sim_test.go +++ /dev/null @@ -1,432 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package discv5 - -import ( - "crypto/ecdsa" - "encoding/binary" - "fmt" - "math/rand" - "net" - "strconv" - "sync" - "sync/atomic" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" -) - -// In this test, nodes try to randomly resolve each other. -func TestSimRandomResolve(t *testing.T) { - t.Skip("boring") - if runWithPlaygroundTime(t) { - return - } - - sim := newSimulation() - bootnode := sim.launchNode(false) - - // A new node joins every 10s. - launcher := time.NewTicker(10 * time.Second) - defer launcher.Stop() - go func() { - for range launcher.C { - net := sim.launchNode(false) - go randomResolves(t, sim, net) - if err := net.SetFallbackNodes([]*Node{bootnode.Self()}); err != nil { - panic(err) - } - t.Logf("launched @ %v: %x\n", time.Now(), net.Self().ID[:16]) - } - }() - - time.Sleep(3 * time.Hour) - sim.shutdown() - sim.printStats() -} - -func TestSimTopics(t *testing.T) { - t.Skip("NaCl test") - if runWithPlaygroundTime(t) { - return - } - sim := newSimulation() - bootnode := sim.launchNode(false) - - go func() { - nets := make([]*Network, 1024) - for i := range nets { - net := sim.launchNode(false) - nets[i] = net - if err := net.SetFallbackNodes([]*Node{bootnode.Self()}); err != nil { - panic(err) - } - time.Sleep(time.Second * 5) - } - - for i, net := range nets { - if i < 256 { - stop := make(chan struct{}) - go net.RegisterTopic(testTopic, stop) - go func() { - //time.Sleep(time.Second * 36000) - time.Sleep(time.Second * 40000) - close(stop) - }() - time.Sleep(time.Millisecond * 100) - } - // time.Sleep(time.Second * 10) - //time.Sleep(time.Second) - /*if i%500 == 499 { - time.Sleep(time.Second * 9501) - } else { - time.Sleep(time.Second) - }*/ - } - }() - - // A new node joins every 10s. - /* launcher := time.NewTicker(5 * time.Second) - cnt := 0 - var printNet *Network - go func() { - for range launcher.C { - cnt++ - if cnt <= 1000 { - log := false //(cnt == 500) - net := sim.launchNode(log) - if log { - printNet = net - } - if cnt > 500 { - go net.RegisterTopic(testTopic, nil) - } - if err := net.SetFallbackNodes([]*Node{bootnode.Self()}); err != nil { - panic(err) - } - } - //fmt.Printf("launched @ %v: %x\n", time.Now(), net.Self().ID[:16]) - } - }() - */ - time.Sleep(55000 * time.Second) - //launcher.Stop() - sim.shutdown() - //sim.printStats() - //printNet.log.printLogs() -} - -/*func testHierarchicalTopics(i int) []Topic { - digits := strconv.FormatInt(int64(256+i/4), 4) - res := make([]Topic, 5) - for i, _ := range res { - res[i] = Topic("foo" + digits[1:i+1]) - } - return res -}*/ - -func testHierarchicalTopics(i int) []Topic { - digits := strconv.FormatInt(int64(128+i/8), 2) - res := make([]Topic, 8) - for i := range res { - res[i] = Topic("foo" + digits[1:i+1]) - } - return res -} - -func TestSimTopicHierarchy(t *testing.T) { - t.Skip("NaCl test") - if runWithPlaygroundTime(t) { - return - } - sim := newSimulation() - bootnode := sim.launchNode(false) - - go func() { - nets := make([]*Network, 1024) - for i := range nets { - net := sim.launchNode(false) - nets[i] = net - if err := net.SetFallbackNodes([]*Node{bootnode.Self()}); err != nil { - panic(err) - } - time.Sleep(time.Second * 5) - } - - stop := make(chan struct{}) - for i, net := range nets { - //if i < 256 { - for _, topic := range testHierarchicalTopics(i)[:5] { - //fmt.Println("reg", topic) - go net.RegisterTopic(topic, stop) - } - time.Sleep(time.Millisecond * 100) - //} - } - time.Sleep(time.Second * 90000) - close(stop) - }() - - time.Sleep(100000 * time.Second) - sim.shutdown() -} - -func randomResolves(t *testing.T, s *simulation, net *Network) { - randtime := func() time.Duration { - return time.Duration(rand.Intn(50)+20) * time.Second - } - lookup := func(target NodeID) bool { - result := net.Resolve(target) - return result != nil && result.ID == target - } - - timer := time.NewTimer(randtime()) - defer timer.Stop() - for { - select { - case <-timer.C: - target := s.randomNode().Self().ID - if !lookup(target) { - t.Errorf("node %x: target %x not found", net.Self().ID[:8], target[:8]) - } - timer.Reset(randtime()) - case <-net.closed: - return - } - } -} - -type simulation struct { - mu sync.RWMutex - nodes map[NodeID]*Network - nodectr uint32 -} - -func newSimulation() *simulation { - return &simulation{nodes: make(map[NodeID]*Network)} -} - -func (s *simulation) shutdown() { - s.mu.RLock() - alive := make([]*Network, 0, len(s.nodes)) - for _, n := range s.nodes { - alive = append(alive, n) - } - defer s.mu.RUnlock() - - for _, n := range alive { - n.Close() - } -} - -func (s *simulation) printStats() { - s.mu.Lock() - defer s.mu.Unlock() - fmt.Println("node counter:", s.nodectr) - fmt.Println("alive nodes:", len(s.nodes)) - - // for _, n := range s.nodes { - // fmt.Printf("%x\n", n.tab.self.ID[:8]) - // transport := n.conn.(*simTransport) - // fmt.Println(" joined:", transport.joinTime) - // fmt.Println(" sends:", transport.hashctr) - // fmt.Println(" table size:", n.tab.count) - // } - - /*for _, n := range s.nodes { - fmt.Println() - fmt.Printf("*** Node %x\n", n.tab.self.ID[:8]) - n.log.printLogs() - }*/ - -} - -func (s *simulation) randomNode() *Network { - s.mu.Lock() - defer s.mu.Unlock() - - n := rand.Intn(len(s.nodes)) - for _, net := range s.nodes { - if n == 0 { - return net - } - n-- - } - return nil -} - -func (s *simulation) launchNode(log bool) *Network { - var ( - num = s.nodectr - key = newkey() - id = PubkeyID(&key.PublicKey) - ip = make(net.IP, 4) - ) - s.nodectr++ - binary.BigEndian.PutUint32(ip, num) - ip[0] = 10 - addr := &net.UDPAddr{IP: ip, Port: 30303} - - transport := &simTransport{joinTime: time.Now(), sender: id, senderAddr: addr, sim: s, priv: key} - net, err := newNetwork(transport, key.PublicKey, "", nil) - if err != nil { - panic("cannot launch new node: " + err.Error()) - } - - s.mu.Lock() - s.nodes[id] = net - s.mu.Unlock() - - return net -} - -type simTransport struct { - joinTime time.Time - sender NodeID - senderAddr *net.UDPAddr - sim *simulation - hashctr uint64 - priv *ecdsa.PrivateKey -} - -func (st *simTransport) localAddr() *net.UDPAddr { - return st.senderAddr -} - -func (st *simTransport) Close() {} - -func (st *simTransport) send(remote *Node, ptype nodeEvent, data interface{}) (hash []byte) { - hash = st.nextHash() - var raw []byte - if ptype == pongPacket { - var err error - raw, _, err = encodePacket(st.priv, byte(ptype), data) - if err != nil { - panic(err) - } - } - - st.sendPacket(remote.ID, ingressPacket{ - remoteID: st.sender, - remoteAddr: st.senderAddr, - hash: hash, - ev: ptype, - data: data, - rawData: raw, - }) - return hash -} - -func (st *simTransport) sendPing(remote *Node, remoteAddr *net.UDPAddr, topics []Topic) []byte { - hash := st.nextHash() - st.sendPacket(remote.ID, ingressPacket{ - remoteID: st.sender, - remoteAddr: st.senderAddr, - hash: hash, - ev: pingPacket, - data: &ping{ - Version: 4, - From: rpcEndpoint{IP: st.senderAddr.IP, UDP: uint16(st.senderAddr.Port), TCP: 30303}, - To: rpcEndpoint{IP: remoteAddr.IP, UDP: uint16(remoteAddr.Port), TCP: 30303}, - Expiration: uint64(time.Now().Unix() + int64(expiration)), - Topics: topics, - }, - }) - return hash -} - -func (st *simTransport) sendFindnodeHash(remote *Node, target common.Hash) { - st.sendPacket(remote.ID, ingressPacket{ - remoteID: st.sender, - remoteAddr: st.senderAddr, - hash: st.nextHash(), - ev: findnodeHashPacket, - data: &findnodeHash{ - Target: target, - Expiration: uint64(time.Now().Unix() + int64(expiration)), - }, - }) -} - -func (st *simTransport) sendTopicRegister(remote *Node, topics []Topic, idx int, pong []byte) { - //fmt.Println("send", topics, pong) - st.sendPacket(remote.ID, ingressPacket{ - remoteID: st.sender, - remoteAddr: st.senderAddr, - hash: st.nextHash(), - ev: topicRegisterPacket, - data: &topicRegister{ - Topics: topics, - Idx: uint(idx), - Pong: pong, - }, - }) -} - -func (st *simTransport) sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node) { - rnodes := make([]rpcNode, len(nodes)) - for i := range nodes { - rnodes[i] = nodeToRPC(nodes[i]) - } - st.sendPacket(remote.ID, ingressPacket{ - remoteID: st.sender, - remoteAddr: st.senderAddr, - hash: st.nextHash(), - ev: topicNodesPacket, - data: &topicNodes{Echo: queryHash, Nodes: rnodes}, - }) -} - -func (st *simTransport) sendNeighbours(remote *Node, nodes []*Node) { - // TODO: send multiple packets - rnodes := make([]rpcNode, len(nodes)) - for i := range nodes { - rnodes[i] = nodeToRPC(nodes[i]) - } - st.sendPacket(remote.ID, ingressPacket{ - remoteID: st.sender, - remoteAddr: st.senderAddr, - hash: st.nextHash(), - ev: neighborsPacket, - data: &neighbors{ - Nodes: rnodes, - Expiration: uint64(time.Now().Unix() + int64(expiration)), - }, - }) -} - -func (st *simTransport) nextHash() []byte { - v := atomic.AddUint64(&st.hashctr, 1) - var hash common.Hash - binary.BigEndian.PutUint64(hash[:], v) - return hash[:] -} - -const packetLoss = 0 // 1/1000 - -func (st *simTransport) sendPacket(remote NodeID, p ingressPacket) { - if rand.Int31n(1000) >= packetLoss { - st.sim.mu.RLock() - recipient := st.sim.nodes[remote] - st.sim.mu.RUnlock() - - time.AfterFunc(200*time.Millisecond, func() { - recipient.reqReadPacket(p) - }) - } -} diff --git a/p2p/discv5/table.go b/p2p/discv5/table.go deleted file mode 100644 index 64c3ecd1c7..0000000000 --- a/p2p/discv5/table.go +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package discv5 is a prototype implementation of Discvery v5. -// Deprecated: do not use this package. -package discv5 - -import ( - "crypto/rand" - "encoding/binary" - "fmt" - "net" - "sort" - - "github.com/ethereum/go-ethereum/common" -) - -const ( - alpha = 3 // Kademlia concurrency factor - bucketSize = 16 // Kademlia bucket size - hashBits = len(common.Hash{}) * 8 - nBuckets = hashBits + 1 // Number of buckets - - maxFindnodeFailures = 5 -) - -type Table struct { - count int // number of nodes - buckets [nBuckets]*bucket // index of known nodes by distance - nodeAddedHook func(*Node) // for testing - self *Node // metadata of the local node -} - -// bucket contains nodes, ordered by their last activity. the entry -// that was most recently active is the first element in entries. -type bucket struct { - entries []*Node - replacements []*Node -} - -func newTable(ourID NodeID, ourAddr *net.UDPAddr) *Table { - self := NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)) - tab := &Table{self: self} - for i := range tab.buckets { - tab.buckets[i] = new(bucket) - } - return tab -} - -const printTable = false - -// chooseBucketRefreshTarget selects random refresh targets to keep all Kademlia -// buckets filled with live connections and keep the network topology healthy. -// This requires selecting addresses closer to our own with a higher probability -// in order to refresh closer buckets too. -// -// This algorithm approximates the distance distribution of existing nodes in the -// table by selecting a random node from the table and selecting a target address -// with a distance less than twice of that of the selected node. -// This algorithm will be improved later to specifically target the least recently -// used buckets. -func (tab *Table) chooseBucketRefreshTarget() common.Hash { - entries := 0 - if printTable { - fmt.Println() - } - for i, b := range &tab.buckets { - entries += len(b.entries) - if printTable { - for _, e := range b.entries { - fmt.Println(i, e.state, e.addr().String(), e.ID.String(), e.sha.Hex()) - } - } - } - - prefix := binary.BigEndian.Uint64(tab.self.sha[0:8]) - dist := ^uint64(0) - entry := int(randUint(uint32(entries + 1))) - for _, b := range &tab.buckets { - if entry < len(b.entries) { - n := b.entries[entry] - dist = binary.BigEndian.Uint64(n.sha[0:8]) ^ prefix - break - } - entry -= len(b.entries) - } - - ddist := ^uint64(0) - if dist+dist > dist { - ddist = dist - } - targetPrefix := prefix ^ randUint64n(ddist) - - var target common.Hash - binary.BigEndian.PutUint64(target[0:8], targetPrefix) - rand.Read(target[8:]) - return target -} - -// readRandomNodes fills the given slice with random nodes from the -// table. It will not write the same node more than once. The nodes in -// the slice are copies and can be modified by the caller. -func (tab *Table) readRandomNodes(buf []*Node) (n int) { - // TODO: tree-based buckets would help here - // Find all non-empty buckets and get a fresh slice of their entries. - var buckets [][]*Node - for _, b := range &tab.buckets { - if len(b.entries) > 0 { - buckets = append(buckets, b.entries) - } - } - if len(buckets) == 0 { - return 0 - } - // Shuffle the buckets. - for i := uint32(len(buckets)) - 1; i > 0; i-- { - j := randUint(i) - buckets[i], buckets[j] = buckets[j], buckets[i] - } - // Move head of each bucket into buf, removing buckets that become empty. - var i, j int - for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) { - b := buckets[j] - buf[i] = &(*b[0]) - buckets[j] = b[1:] - if len(b) == 1 { - buckets = append(buckets[:j], buckets[j+1:]...) - } - if len(buckets) == 0 { - break - } - } - return i + 1 -} - -func randUint(max uint32) uint32 { - if max < 2 { - return 0 - } - var b [4]byte - rand.Read(b[:]) - return binary.BigEndian.Uint32(b[:]) % max -} - -func randUint64n(max uint64) uint64 { - if max < 2 { - return 0 - } - var b [8]byte - rand.Read(b[:]) - return binary.BigEndian.Uint64(b[:]) % max -} - -// closest returns the n nodes in the table that are closest to the -// given id. The caller must hold tab.mutex. -func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance { - // This is a very wasteful way to find the closest nodes but - // obviously correct. I believe that tree-based buckets would make - // this easier to implement efficiently. - close := &nodesByDistance{target: target} - for _, b := range &tab.buckets { - for _, n := range b.entries { - close.push(n, nresults) - } - } - return close -} - -// add attempts to add the given node its corresponding bucket. If the -// bucket has space available, adding the node succeeds immediately. -// Otherwise, the node is added to the replacement cache for the bucket. -func (tab *Table) add(n *Node) (contested *Node) { - //fmt.Println("add", n.addr().String(), n.ID.String(), n.sha.Hex()) - if n.ID == tab.self.ID { - return - } - b := tab.buckets[logdist(tab.self.sha, n.sha)] - switch { - case b.bump(n): - // n exists in b. - return nil - case len(b.entries) < bucketSize: - // b has space available. - b.addFront(n) - tab.count++ - if tab.nodeAddedHook != nil { - tab.nodeAddedHook(n) - } - return nil - default: - // b has no space left, add to replacement cache - // and revalidate the last entry. - // TODO: drop previous node - b.replacements = append(b.replacements, n) - if len(b.replacements) > bucketSize { - copy(b.replacements, b.replacements[1:]) - b.replacements = b.replacements[:len(b.replacements)-1] - } - return b.entries[len(b.entries)-1] - } -} - -// stuff adds nodes the table to the end of their corresponding bucket -// if the bucket is not full. -func (tab *Table) stuff(nodes []*Node) { -outer: - for _, n := range nodes { - if n.ID == tab.self.ID { - continue // don't add self - } - bucket := tab.buckets[logdist(tab.self.sha, n.sha)] - for i := range bucket.entries { - if bucket.entries[i].ID == n.ID { - continue outer // already in bucket - } - } - if len(bucket.entries) < bucketSize { - bucket.entries = append(bucket.entries, n) - tab.count++ - if tab.nodeAddedHook != nil { - tab.nodeAddedHook(n) - } - } - } -} - -// delete removes an entry from the node table (used to evacuate -// failed/non-bonded discovery peers). -func (tab *Table) delete(node *Node) { - //fmt.Println("delete", node.addr().String(), node.ID.String(), node.sha.Hex()) - bucket := tab.buckets[logdist(tab.self.sha, node.sha)] - for i := range bucket.entries { - if bucket.entries[i].ID == node.ID { - bucket.entries = append(bucket.entries[:i], bucket.entries[i+1:]...) - tab.count-- - return - } - } -} - -func (tab *Table) deleteReplace(node *Node) { - b := tab.buckets[logdist(tab.self.sha, node.sha)] - i := 0 - for i < len(b.entries) { - if b.entries[i].ID == node.ID { - b.entries = append(b.entries[:i], b.entries[i+1:]...) - tab.count-- - } else { - i++ - } - } - // refill from replacement cache - // TODO: maybe use random index - if len(b.entries) < bucketSize && len(b.replacements) > 0 { - ri := len(b.replacements) - 1 - b.addFront(b.replacements[ri]) - tab.count++ - b.replacements[ri] = nil - b.replacements = b.replacements[:ri] - } -} - -func (b *bucket) addFront(n *Node) { - b.entries = append(b.entries, nil) - copy(b.entries[1:], b.entries) - b.entries[0] = n -} - -func (b *bucket) bump(n *Node) bool { - for i := range b.entries { - if b.entries[i].ID == n.ID { - // move it to the front - copy(b.entries[1:], b.entries[:i]) - b.entries[0] = n - return true - } - } - return false -} - -// nodesByDistance is a list of nodes, ordered by -// distance to target. -type nodesByDistance struct { - entries []*Node - target common.Hash -} - -// push adds the given node to the list, keeping the total size below maxElems. -func (h *nodesByDistance) push(n *Node, maxElems int) { - ix := sort.Search(len(h.entries), func(i int) bool { - return distcmp(h.target, h.entries[i].sha, n.sha) > 0 - }) - if len(h.entries) < maxElems { - h.entries = append(h.entries, n) - } - if ix == len(h.entries) { - // farther away than all nodes we already have. - // if there was room for it, the node is now the last element. - } else { - // slide existing entries down to make room - // this will overwrite the entry we just appended. - copy(h.entries[ix+1:], h.entries[ix:]) - h.entries[ix] = n - } -} diff --git a/p2p/discv5/table_test.go b/p2p/discv5/table_test.go deleted file mode 100644 index 872a4f6836..0000000000 --- a/p2p/discv5/table_test.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package discv5 - -import ( - "crypto/ecdsa" - "fmt" - "math/rand" - - "net" - "reflect" - "testing" - "testing/quick" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -func TestBucket_bumpNoDuplicates(t *testing.T) { - t.Parallel() - cfg := &quick.Config{ - MaxCount: 1000, - Rand: rand.New(rand.NewSource(time.Now().Unix())), - Values: func(args []reflect.Value, rand *rand.Rand) { - // generate a random list of nodes. this will be the content of the bucket. - n := rand.Intn(bucketSize-1) + 1 - nodes := make([]*Node, n) - for i := range nodes { - nodes[i] = nodeAtDistance(common.Hash{}, 200) - } - args[0] = reflect.ValueOf(nodes) - // generate random bump positions. - bumps := make([]int, rand.Intn(100)) - for i := range bumps { - bumps[i] = rand.Intn(len(nodes)) - } - args[1] = reflect.ValueOf(bumps) - }, - } - - prop := func(nodes []*Node, bumps []int) (ok bool) { - b := &bucket{entries: make([]*Node, len(nodes))} - copy(b.entries, nodes) - for i, pos := range bumps { - b.bump(b.entries[pos]) - if hasDuplicates(b.entries) { - t.Logf("bucket has duplicates after %d/%d bumps:", i+1, len(bumps)) - for _, n := range b.entries { - t.Logf(" %p", n) - } - return false - } - } - return true - } - if err := quick.Check(prop, cfg); err != nil { - t.Error(err) - } -} - -// nodeAtDistance creates a node for which logdist(base, n.sha) == ld. -// The node's ID does not correspond to n.sha. -func nodeAtDistance(base common.Hash, ld int) (n *Node) { - n = new(Node) - n.sha = hashAtDistance(base, ld) - copy(n.ID[:], n.sha[:]) // ensure the node still has a unique ID - return n -} - -func TestTable_closest(t *testing.T) { - t.Parallel() - - test := func(test *closeTest) bool { - // for any node table, Target and N - tab := newTable(test.Self, &net.UDPAddr{}) - tab.stuff(test.All) - - // check that doClosest(Target, N) returns nodes - result := tab.closest(test.Target, test.N).entries - if hasDuplicates(result) { - t.Errorf("result contains duplicates") - return false - } - if !sortedByDistanceTo(test.Target, result) { - t.Errorf("result is not sorted by distance to target") - return false - } - - // check that the number of results is min(N, tablen) - wantN := test.N - if tab.count < test.N { - wantN = tab.count - } - if len(result) != wantN { - t.Errorf("wrong number of nodes: got %d, want %d", len(result), wantN) - return false - } else if len(result) == 0 { - return true // no need to check distance - } - - // check that the result nodes have minimum distance to target. - for _, b := range tab.buckets { - for _, n := range b.entries { - if contains(result, n.ID) { - continue // don't run the check below for nodes in result - } - farthestResult := result[len(result)-1].sha - if distcmp(test.Target, n.sha, farthestResult) < 0 { - t.Errorf("table contains node that is closer to target but it's not in result") - t.Logf(" Target: %v", test.Target) - t.Logf(" Farthest Result: %v", farthestResult) - t.Logf(" ID: %v", n.ID) - return false - } - } - } - return true - } - if err := quick.Check(test, quickcfg()); err != nil { - t.Error(err) - } -} - -func TestTable_ReadRandomNodesGetAll(t *testing.T) { - cfg := &quick.Config{ - MaxCount: 200, - Rand: rand.New(rand.NewSource(time.Now().Unix())), - Values: func(args []reflect.Value, rand *rand.Rand) { - args[0] = reflect.ValueOf(make([]*Node, rand.Intn(1000))) - }, - } - test := func(buf []*Node) bool { - tab := newTable(NodeID{}, &net.UDPAddr{}) - for i := 0; i < len(buf); i++ { - ld := cfg.Rand.Intn(len(tab.buckets)) - tab.stuff([]*Node{nodeAtDistance(tab.self.sha, ld)}) - } - gotN := tab.readRandomNodes(buf) - if gotN != tab.count { - t.Errorf("wrong number of nodes, got %d, want %d", gotN, tab.count) - return false - } - if hasDuplicates(buf[:gotN]) { - t.Errorf("result contains duplicates") - return false - } - return true - } - if err := quick.Check(test, cfg); err != nil { - t.Error(err) - } -} - -type closeTest struct { - Self NodeID - Target common.Hash - All []*Node - N int -} - -func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value { - t := &closeTest{ - Self: gen(NodeID{}, rand).(NodeID), - Target: gen(common.Hash{}, rand).(common.Hash), - N: rand.Intn(bucketSize), - } - for _, id := range gen([]NodeID{}, rand).([]NodeID) { - t.All = append(t.All, &Node{ID: id}) - } - return reflect.ValueOf(t) -} - -func hasDuplicates(slice []*Node) bool { - seen := make(map[NodeID]bool) - for i, e := range slice { - if e == nil { - panic(fmt.Sprintf("nil *Node at %d", i)) - } - if seen[e.ID] { - return true - } - seen[e.ID] = true - } - return false -} - -func sortedByDistanceTo(distbase common.Hash, slice []*Node) bool { - var last common.Hash - for i, e := range slice { - if i > 0 && distcmp(distbase, e.sha, last) < 0 { - return false - } - last = e.sha - } - return true -} - -func contains(ns []*Node, id NodeID) bool { - for _, n := range ns { - if n.ID == id { - return true - } - } - return false -} - -// gen wraps quick.Value so it's easier to use. -// it generates a random value of the given value's type. -func gen(typ interface{}, rand *rand.Rand) interface{} { - v, ok := quick.Value(reflect.TypeOf(typ), rand) - if !ok { - panic(fmt.Sprintf("couldn't generate random value of type %T", typ)) - } - return v.Interface() -} - -func newkey() *ecdsa.PrivateKey { - key, err := crypto.GenerateKey() - if err != nil { - panic("couldn't generate key: " + err.Error()) - } - return key -} diff --git a/p2p/discv5/ticket.go b/p2p/discv5/ticket.go deleted file mode 100644 index c5e3d6c08f..0000000000 --- a/p2p/discv5/ticket.go +++ /dev/null @@ -1,884 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package discv5 - -import ( - "bytes" - "encoding/binary" - "fmt" - "math" - "math/rand" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" -) - -const ( - ticketTimeBucketLen = time.Minute - collectFrequency = time.Second * 30 - registerFrequency = time.Second * 60 - maxCollectDebt = 10 - maxRegisterDebt = 5 - keepTicketConst = time.Minute * 10 - keepTicketExp = time.Minute * 5 - targetWaitTime = time.Minute * 10 - topicQueryTimeout = time.Second * 5 - topicQueryResend = time.Minute - // topic radius detection - maxRadius = 0xffffffffffffffff - radiusTC = time.Minute * 20 - radiusBucketsPerBit = 8 - minSlope = 1 - minPeakSize = 40 - maxNoAdjust = 20 - lookupWidth = 8 - minRightSum = 20 - searchForceQuery = 4 -) - -// timeBucket represents absolute monotonic time in minutes. -// It is used as the index into the per-topic ticket buckets. -type timeBucket int - -type ticket struct { - topics []Topic - regTime []mclock.AbsTime // Per-topic local absolute time when the ticket can be used. - - // The serial number that was issued by the server. - serial uint32 - // Used by registrar, tracks absolute time when the ticket was created. - issueTime mclock.AbsTime - - // Fields used only by registrants - node *Node // the registrar node that signed this ticket - refCnt int // tracks number of topics that will be registered using this ticket - pong []byte // encoded pong packet signed by the registrar -} - -// ticketRef refers to a single topic in a ticket. -type ticketRef struct { - t *ticket - idx int // index of the topic in t.topics and t.regTime -} - -func (ref ticketRef) topic() Topic { - return ref.t.topics[ref.idx] -} - -func (ref ticketRef) topicRegTime() mclock.AbsTime { - return ref.t.regTime[ref.idx] -} - -func pongToTicket(localTime mclock.AbsTime, topics []Topic, node *Node, p *ingressPacket) (*ticket, error) { - wps := p.data.(*pong).WaitPeriods - if len(topics) != len(wps) { - return nil, fmt.Errorf("bad wait period list: got %d values, want %d", len(topics), len(wps)) - } - if rlpHash(topics) != p.data.(*pong).TopicHash { - return nil, fmt.Errorf("bad topic hash") - } - t := &ticket{ - issueTime: localTime, - node: node, - topics: topics, - pong: p.rawData, - regTime: make([]mclock.AbsTime, len(wps)), - } - // Convert wait periods to local absolute time. - for i, wp := range wps { - t.regTime[i] = localTime + mclock.AbsTime(time.Second*time.Duration(wp)) - } - return t, nil -} - -func ticketToPong(t *ticket, pong *pong) { - pong.Expiration = uint64(t.issueTime / mclock.AbsTime(time.Second)) - pong.TopicHash = rlpHash(t.topics) - pong.TicketSerial = t.serial - pong.WaitPeriods = make([]uint32, len(t.regTime)) - for i, regTime := range t.regTime { - pong.WaitPeriods[i] = uint32(time.Duration(regTime-t.issueTime) / time.Second) - } -} - -type ticketStore struct { - // radius detector and target address generator - // exists for both searched and registered topics - radius map[Topic]*topicRadius - - // Contains buckets (for each absolute minute) of tickets - // that can be used in that minute. - // This is only set if the topic is being registered. - tickets map[Topic]*topicTickets - - regQueue []Topic // Topic registration queue for round robin attempts - regSet map[Topic]struct{} // Topic registration queue contents for fast filling - - nodes map[*Node]*ticket - nodeLastReq map[*Node]reqInfo - - lastBucketFetched timeBucket - nextTicketCached *ticketRef - - searchTopicMap map[Topic]searchTopic - nextTopicQueryCleanup mclock.AbsTime - queriesSent map[*Node]map[common.Hash]sentQuery -} - -type searchTopic struct { - foundChn chan<- *Node -} - -type sentQuery struct { - sent mclock.AbsTime - lookup lookupInfo -} - -type topicTickets struct { - buckets map[timeBucket][]ticketRef - nextLookup mclock.AbsTime - nextReg mclock.AbsTime -} - -func newTicketStore() *ticketStore { - return &ticketStore{ - radius: make(map[Topic]*topicRadius), - tickets: make(map[Topic]*topicTickets), - regSet: make(map[Topic]struct{}), - nodes: make(map[*Node]*ticket), - nodeLastReq: make(map[*Node]reqInfo), - searchTopicMap: make(map[Topic]searchTopic), - queriesSent: make(map[*Node]map[common.Hash]sentQuery), - } -} - -// addTopic starts tracking a topic. If register is true, -// the local node will register the topic and tickets will be collected. -func (s *ticketStore) addTopic(topic Topic, register bool) { - log.Trace("Adding discovery topic", "topic", topic, "register", register) - if s.radius[topic] == nil { - s.radius[topic] = newTopicRadius(topic) - } - if register && s.tickets[topic] == nil { - s.tickets[topic] = &topicTickets{buckets: make(map[timeBucket][]ticketRef)} - } -} - -func (s *ticketStore) addSearchTopic(t Topic, foundChn chan<- *Node) { - s.addTopic(t, false) - if s.searchTopicMap[t].foundChn == nil { - s.searchTopicMap[t] = searchTopic{foundChn: foundChn} - } -} - -func (s *ticketStore) removeSearchTopic(t Topic) { - if st := s.searchTopicMap[t]; st.foundChn != nil { - delete(s.searchTopicMap, t) - } -} - -// removeRegisterTopic deletes all tickets for the given topic. -func (s *ticketStore) removeRegisterTopic(topic Topic) { - log.Trace("Removing discovery topic", "topic", topic) - if s.tickets[topic] == nil { - log.Warn("Removing non-existent discovery topic", "topic", topic) - return - } - for _, list := range s.tickets[topic].buckets { - for _, ref := range list { - ref.t.refCnt-- - if ref.t.refCnt == 0 { - delete(s.nodes, ref.t.node) - delete(s.nodeLastReq, ref.t.node) - } - } - } - delete(s.tickets, topic) -} - -func (s *ticketStore) regTopicSet() []Topic { - topics := make([]Topic, 0, len(s.tickets)) - for topic := range s.tickets { - topics = append(topics, topic) - } - return topics -} - -// nextRegisterLookup returns the target of the next lookup for ticket collection. -func (s *ticketStore) nextRegisterLookup() (lookupInfo, time.Duration) { - // Queue up any new topics (or discarded ones), preserving iteration order - for topic := range s.tickets { - if _, ok := s.regSet[topic]; !ok { - s.regQueue = append(s.regQueue, topic) - s.regSet[topic] = struct{}{} - } - } - // Iterate over the set of all topics and look up the next suitable one - for len(s.regQueue) > 0 { - // Fetch the next topic from the queue, and ensure it still exists - topic := s.regQueue[0] - s.regQueue = s.regQueue[1:] - delete(s.regSet, topic) - - if s.tickets[topic] == nil { - continue - } - // If the topic needs more tickets, return it - if s.tickets[topic].nextLookup < mclock.Now() { - next, delay := s.radius[topic].nextTarget(false), 100*time.Millisecond - log.Trace("Found discovery topic to register", "topic", topic, "target", next.target, "delay", delay) - return next, delay - } - } - // No registration topics found or all exhausted, sleep - delay := 40 * time.Second - log.Trace("No topic found to register", "delay", delay) - return lookupInfo{}, delay -} - -func (s *ticketStore) nextSearchLookup(topic Topic) lookupInfo { - tr := s.radius[topic] - target := tr.nextTarget(tr.radiusLookupCnt >= searchForceQuery) - if target.radiusLookup { - tr.radiusLookupCnt++ - } else { - tr.radiusLookupCnt = 0 - } - return target -} - -func (s *ticketStore) addTicketRef(r ticketRef) { - topic := r.t.topics[r.idx] - tickets := s.tickets[topic] - if tickets == nil { - log.Warn("Adding ticket to non-existent topic", "topic", topic) - return - } - bucket := timeBucket(r.t.regTime[r.idx] / mclock.AbsTime(ticketTimeBucketLen)) - tickets.buckets[bucket] = append(tickets.buckets[bucket], r) - r.t.refCnt++ - - min := mclock.Now() - mclock.AbsTime(collectFrequency)*maxCollectDebt - if tickets.nextLookup < min { - tickets.nextLookup = min - } - tickets.nextLookup += mclock.AbsTime(collectFrequency) - - //s.removeExcessTickets(topic) -} - -func (s *ticketStore) nextFilteredTicket() (*ticketRef, time.Duration) { - now := mclock.Now() - for { - ticket, wait := s.nextRegisterableTicket() - if ticket == nil { - return ticket, wait - } - log.Trace("Found discovery ticket to register", "node", ticket.t.node, "serial", ticket.t.serial, "wait", wait) - - regTime := now + mclock.AbsTime(wait) - topic := ticket.t.topics[ticket.idx] - if s.tickets[topic] != nil && regTime >= s.tickets[topic].nextReg { - return ticket, wait - } - s.removeTicketRef(*ticket) - } -} - -func (s *ticketStore) ticketRegistered(ref ticketRef) { - now := mclock.Now() - - topic := ref.t.topics[ref.idx] - tickets := s.tickets[topic] - min := now - mclock.AbsTime(registerFrequency)*maxRegisterDebt - if min > tickets.nextReg { - tickets.nextReg = min - } - tickets.nextReg += mclock.AbsTime(registerFrequency) - s.tickets[topic] = tickets - - s.removeTicketRef(ref) -} - -// nextRegisterableTicket returns the next ticket that can be used -// to register. -// -// If the returned wait time <= zero the ticket can be used. For a positive -// wait time, the caller should requery the next ticket later. -// -// A ticket can be returned more than once with <= zero wait time in case -// the ticket contains multiple topics. -func (s *ticketStore) nextRegisterableTicket() (*ticketRef, time.Duration) { - now := mclock.Now() - if s.nextTicketCached != nil { - return s.nextTicketCached, time.Duration(s.nextTicketCached.topicRegTime() - now) - } - - for bucket := s.lastBucketFetched; ; bucket++ { - var ( - empty = true // true if there are no tickets - nextTicket ticketRef // uninitialized if this bucket is empty - ) - for _, tickets := range s.tickets { - //s.removeExcessTickets(topic) - if len(tickets.buckets) != 0 { - empty = false - - list := tickets.buckets[bucket] - for _, ref := range list { - //debugLog(fmt.Sprintf(" nrt bucket = %d node = %x sn = %v wait = %v", bucket, ref.t.node.ID[:8], ref.t.serial, time.Duration(ref.topicRegTime()-now))) - if nextTicket.t == nil || ref.topicRegTime() < nextTicket.topicRegTime() { - nextTicket = ref - } - } - } - } - if empty { - return nil, 0 - } - if nextTicket.t != nil { - s.nextTicketCached = &nextTicket - return &nextTicket, time.Duration(nextTicket.topicRegTime() - now) - } - s.lastBucketFetched = bucket - } -} - -// removeTicket removes a ticket from the ticket store -func (s *ticketStore) removeTicketRef(ref ticketRef) { - log.Trace("Removing discovery ticket reference", "node", ref.t.node.ID, "serial", ref.t.serial) - - // Make nextRegisterableTicket return the next available ticket. - s.nextTicketCached = nil - - topic := ref.topic() - tickets := s.tickets[topic] - - if tickets == nil { - log.Trace("Removing tickets from unknown topic", "topic", topic) - return - } - bucket := timeBucket(ref.t.regTime[ref.idx] / mclock.AbsTime(ticketTimeBucketLen)) - list := tickets.buckets[bucket] - idx := -1 - for i, bt := range list { - if bt.t == ref.t { - idx = i - break - } - } - if idx == -1 { - panic(nil) - } - list = append(list[:idx], list[idx+1:]...) - if len(list) != 0 { - tickets.buckets[bucket] = list - } else { - delete(tickets.buckets, bucket) - } - ref.t.refCnt-- - if ref.t.refCnt == 0 { - delete(s.nodes, ref.t.node) - delete(s.nodeLastReq, ref.t.node) - } -} - -type lookupInfo struct { - target common.Hash - topic Topic - radiusLookup bool -} - -type reqInfo struct { - pingHash []byte - lookup lookupInfo - time mclock.AbsTime -} - -// returns -1 if not found -func (t *ticket) findIdx(topic Topic) int { - for i, tt := range t.topics { - if tt == topic { - return i - } - } - return -1 -} - -func (s *ticketStore) registerLookupDone(lookup lookupInfo, nodes []*Node, ping func(n *Node) []byte) { - now := mclock.Now() - for i, n := range nodes { - if i == 0 || (binary.BigEndian.Uint64(n.sha[:8])^binary.BigEndian.Uint64(lookup.target[:8])) < s.radius[lookup.topic].minRadius { - if lookup.radiusLookup { - if lastReq, ok := s.nodeLastReq[n]; !ok || time.Duration(now-lastReq.time) > radiusTC { - s.nodeLastReq[n] = reqInfo{pingHash: ping(n), lookup: lookup, time: now} - } - } else { - if s.nodes[n] == nil { - s.nodeLastReq[n] = reqInfo{pingHash: ping(n), lookup: lookup, time: now} - } - } - } - } -} - -func (s *ticketStore) searchLookupDone(lookup lookupInfo, nodes []*Node, query func(n *Node, topic Topic) []byte) { - now := mclock.Now() - for i, n := range nodes { - if i == 0 || (binary.BigEndian.Uint64(n.sha[:8])^binary.BigEndian.Uint64(lookup.target[:8])) < s.radius[lookup.topic].minRadius { - if lookup.radiusLookup { - if lastReq, ok := s.nodeLastReq[n]; !ok || time.Duration(now-lastReq.time) > radiusTC { - s.nodeLastReq[n] = reqInfo{pingHash: nil, lookup: lookup, time: now} - } - } // else { - if s.canQueryTopic(n, lookup.topic) { - hash := query(n, lookup.topic) - if hash != nil { - s.addTopicQuery(common.BytesToHash(hash), n, lookup) - } - } - //} - } - } -} - -func (s *ticketStore) adjustWithTicket(now mclock.AbsTime, targetHash common.Hash, t *ticket) { - for i, topic := range t.topics { - if tt, ok := s.radius[topic]; ok { - tt.adjustWithTicket(now, targetHash, ticketRef{t, i}) - } - } -} - -func (s *ticketStore) addTicket(localTime mclock.AbsTime, pingHash []byte, ticket *ticket) { - log.Trace("Adding discovery ticket", "node", ticket.node.ID, "serial", ticket.serial) - - lastReq, ok := s.nodeLastReq[ticket.node] - if !(ok && bytes.Equal(pingHash, lastReq.pingHash)) { - return - } - s.adjustWithTicket(localTime, lastReq.lookup.target, ticket) - - if lastReq.lookup.radiusLookup || s.nodes[ticket.node] != nil { - return - } - - topic := lastReq.lookup.topic - topicIdx := ticket.findIdx(topic) - if topicIdx == -1 { - return - } - - bucket := timeBucket(localTime / mclock.AbsTime(ticketTimeBucketLen)) - if s.lastBucketFetched == 0 || bucket < s.lastBucketFetched { - s.lastBucketFetched = bucket - } - - if _, ok := s.tickets[topic]; ok { - wait := ticket.regTime[topicIdx] - localTime - rnd := rand.ExpFloat64() - if rnd > 10 { - rnd = 10 - } - if float64(wait) < float64(keepTicketConst)+float64(keepTicketExp)*rnd { - // use the ticket to register this topic - //fmt.Println("addTicket", ticket.node.ID[:8], ticket.node.addr().String(), ticket.serial, ticket.pong) - s.addTicketRef(ticketRef{ticket, topicIdx}) - } - } - - if ticket.refCnt > 0 { - s.nextTicketCached = nil - s.nodes[ticket.node] = ticket - } -} - -func (s *ticketStore) canQueryTopic(node *Node, topic Topic) bool { - qq := s.queriesSent[node] - if qq != nil { - now := mclock.Now() - for _, sq := range qq { - if sq.lookup.topic == topic && sq.sent > now-mclock.AbsTime(topicQueryResend) { - return false - } - } - } - return true -} - -func (s *ticketStore) addTopicQuery(hash common.Hash, node *Node, lookup lookupInfo) { - now := mclock.Now() - qq := s.queriesSent[node] - if qq == nil { - qq = make(map[common.Hash]sentQuery) - s.queriesSent[node] = qq - } - qq[hash] = sentQuery{sent: now, lookup: lookup} - s.cleanupTopicQueries(now) -} - -func (s *ticketStore) cleanupTopicQueries(now mclock.AbsTime) { - if s.nextTopicQueryCleanup > now { - return - } - exp := now - mclock.AbsTime(topicQueryResend) - for n, qq := range s.queriesSent { - for h, q := range qq { - if q.sent < exp { - delete(qq, h) - } - } - if len(qq) == 0 { - delete(s.queriesSent, n) - } - } - s.nextTopicQueryCleanup = now + mclock.AbsTime(topicQueryTimeout) -} - -func (s *ticketStore) gotTopicNodes(from *Node, hash common.Hash, nodes []rpcNode) (timeout bool) { - now := mclock.Now() - //fmt.Println("got", from.addr().String(), hash, len(nodes)) - qq := s.queriesSent[from] - if qq == nil { - return true - } - q, ok := qq[hash] - if !ok || now > q.sent+mclock.AbsTime(topicQueryTimeout) { - return true - } - inside := float64(0) - if len(nodes) > 0 { - inside = 1 - } - s.radius[q.lookup.topic].adjust(now, q.lookup.target, from.sha, inside) - chn := s.searchTopicMap[q.lookup.topic].foundChn - if chn == nil { - //fmt.Println("no channel") - return false - } - for _, node := range nodes { - ip := node.IP - if ip.IsUnspecified() || ip.IsLoopback() { - ip = from.IP - } - n := NewNode(node.ID, ip, node.UDP, node.TCP) - select { - case chn <- n: - default: - return false - } - } - return false -} - -type topicRadius struct { - topic Topic - topicHashPrefix uint64 - radius, minRadius uint64 - buckets []topicRadiusBucket - converged bool - radiusLookupCnt int -} - -type topicRadiusEvent int - -const ( - trOutside topicRadiusEvent = iota - trInside - trNoAdjust - trCount -) - -type topicRadiusBucket struct { - weights [trCount]float64 - lastTime mclock.AbsTime - value float64 - lookupSent map[common.Hash]mclock.AbsTime -} - -func (b *topicRadiusBucket) update(now mclock.AbsTime) { - if now == b.lastTime { - return - } - exp := math.Exp(-float64(now-b.lastTime) / float64(radiusTC)) - for i, w := range b.weights { - b.weights[i] = w * exp - } - b.lastTime = now - - for target, tm := range b.lookupSent { - if now-tm > mclock.AbsTime(respTimeout) { - b.weights[trNoAdjust] += 1 - delete(b.lookupSent, target) - } - } -} - -func (b *topicRadiusBucket) adjust(now mclock.AbsTime, inside float64) { - b.update(now) - if inside <= 0 { - b.weights[trOutside] += 1 - } else { - if inside >= 1 { - b.weights[trInside] += 1 - } else { - b.weights[trInside] += inside - b.weights[trOutside] += 1 - inside - } - } -} - -func newTopicRadius(t Topic) *topicRadius { - topicHash := crypto.Keccak256Hash([]byte(t)) - topicHashPrefix := binary.BigEndian.Uint64(topicHash[0:8]) - - return &topicRadius{ - topic: t, - topicHashPrefix: topicHashPrefix, - radius: maxRadius, - minRadius: maxRadius, - } -} - -func (r *topicRadius) getBucketIdx(addrHash common.Hash) int { - prefix := binary.BigEndian.Uint64(addrHash[0:8]) - var log2 float64 - if prefix != r.topicHashPrefix { - log2 = math.Log2(float64(prefix ^ r.topicHashPrefix)) - } - bucket := int((64 - log2) * radiusBucketsPerBit) - max := 64*radiusBucketsPerBit - 1 - if bucket > max { - return max - } - if bucket < 0 { - return 0 - } - return bucket -} - -func (r *topicRadius) targetForBucket(bucket int) common.Hash { - min := math.Pow(2, 64-float64(bucket+1)/radiusBucketsPerBit) - max := math.Pow(2, 64-float64(bucket)/radiusBucketsPerBit) - a := uint64(min) - b := randUint64n(uint64(max - min)) - xor := a + b - if xor < a { - xor = ^uint64(0) - } - prefix := r.topicHashPrefix ^ xor - var target common.Hash - binary.BigEndian.PutUint64(target[0:8], prefix) - globalRandRead(target[8:]) - return target -} - -// package rand provides a Read function in Go 1.6 and later, but -// we can't use it yet because we still support Go 1.5. -func globalRandRead(b []byte) { - pos := 0 - val := 0 - for n := 0; n < len(b); n++ { - if pos == 0 { - val = rand.Int() - pos = 7 - } - b[n] = byte(val) - val >>= 8 - pos-- - } -} - -func (r *topicRadius) chooseLookupBucket(a, b int) int { - if a < 0 { - a = 0 - } - if a > b { - return -1 - } - c := 0 - for i := a; i <= b; i++ { - if i >= len(r.buckets) || r.buckets[i].weights[trNoAdjust] < maxNoAdjust { - c++ - } - } - if c == 0 { - return -1 - } - rnd := randUint(uint32(c)) - for i := a; i <= b; i++ { - if i >= len(r.buckets) || r.buckets[i].weights[trNoAdjust] < maxNoAdjust { - if rnd == 0 { - return i - } - rnd-- - } - } - panic(nil) // should never happen -} - -func (r *topicRadius) needMoreLookups(a, b int, maxValue float64) bool { - var max float64 - if a < 0 { - a = 0 - } - if b >= len(r.buckets) { - b = len(r.buckets) - 1 - if r.buckets[b].value > max { - max = r.buckets[b].value - } - } - if b >= a { - for i := a; i <= b; i++ { - if r.buckets[i].value > max { - max = r.buckets[i].value - } - } - } - return maxValue-max < minPeakSize -} - -func (r *topicRadius) recalcRadius() (radius uint64, radiusLookup int) { - maxBucket := 0 - maxValue := float64(0) - now := mclock.Now() - v := float64(0) - for i := range r.buckets { - r.buckets[i].update(now) - v += r.buckets[i].weights[trOutside] - r.buckets[i].weights[trInside] - r.buckets[i].value = v - //fmt.Printf("%v %v | ", v, r.buckets[i].weights[trNoAdjust]) - } - //fmt.Println() - slopeCross := -1 - for i, b := range r.buckets { - v := b.value - if v < float64(i)*minSlope { - slopeCross = i - break - } - if v > maxValue { - maxValue = v - maxBucket = i + 1 - } - } - - minRadBucket := len(r.buckets) - sum := float64(0) - for minRadBucket > 0 && sum < minRightSum { - minRadBucket-- - b := r.buckets[minRadBucket] - sum += b.weights[trInside] + b.weights[trOutside] - } - r.minRadius = uint64(math.Pow(2, 64-float64(minRadBucket)/radiusBucketsPerBit)) - - lookupLeft := -1 - if r.needMoreLookups(0, maxBucket-lookupWidth-1, maxValue) { - lookupLeft = r.chooseLookupBucket(maxBucket-lookupWidth, maxBucket-1) - } - lookupRight := -1 - if slopeCross != maxBucket && (minRadBucket <= maxBucket || r.needMoreLookups(maxBucket+lookupWidth, len(r.buckets)-1, maxValue)) { - for len(r.buckets) <= maxBucket+lookupWidth { - r.buckets = append(r.buckets, topicRadiusBucket{lookupSent: make(map[common.Hash]mclock.AbsTime)}) - } - lookupRight = r.chooseLookupBucket(maxBucket, maxBucket+lookupWidth-1) - } - if lookupLeft == -1 { - radiusLookup = lookupRight - } else { - if lookupRight == -1 { - radiusLookup = lookupLeft - } else { - if randUint(2) == 0 { - radiusLookup = lookupLeft - } else { - radiusLookup = lookupRight - } - } - } - - //fmt.Println("mb", maxBucket, "sc", slopeCross, "mrb", minRadBucket, "ll", lookupLeft, "lr", lookupRight, "mv", maxValue) - - if radiusLookup == -1 { - // no more radius lookups needed at the moment, return a radius - r.converged = true - rad := maxBucket - if minRadBucket < rad { - rad = minRadBucket - } - radius = ^uint64(0) - if rad > 0 { - radius = uint64(math.Pow(2, 64-float64(rad)/radiusBucketsPerBit)) - } - r.radius = radius - } - - return -} - -func (r *topicRadius) nextTarget(forceRegular bool) lookupInfo { - if !forceRegular { - _, radiusLookup := r.recalcRadius() - if radiusLookup != -1 { - target := r.targetForBucket(radiusLookup) - r.buckets[radiusLookup].lookupSent[target] = mclock.Now() - return lookupInfo{target: target, topic: r.topic, radiusLookup: true} - } - } - - radExt := r.radius / 2 - if radExt > maxRadius-r.radius { - radExt = maxRadius - r.radius - } - rnd := randUint64n(r.radius) + randUint64n(2*radExt) - if rnd > radExt { - rnd -= radExt - } else { - rnd = radExt - rnd - } - - prefix := r.topicHashPrefix ^ rnd - var target common.Hash - binary.BigEndian.PutUint64(target[0:8], prefix) - globalRandRead(target[8:]) - return lookupInfo{target: target, topic: r.topic, radiusLookup: false} -} - -func (r *topicRadius) adjustWithTicket(now mclock.AbsTime, targetHash common.Hash, t ticketRef) { - wait := t.t.regTime[t.idx] - t.t.issueTime - inside := float64(wait)/float64(targetWaitTime) - 0.5 - if inside > 1 { - inside = 1 - } - if inside < 0 { - inside = 0 - } - r.adjust(now, targetHash, t.t.node.sha, inside) -} - -func (r *topicRadius) adjust(now mclock.AbsTime, targetHash, addrHash common.Hash, inside float64) { - bucket := r.getBucketIdx(addrHash) - //fmt.Println("adjust", bucket, len(r.buckets), inside) - if bucket >= len(r.buckets) { - return - } - r.buckets[bucket].adjust(now, inside) - delete(r.buckets[bucket].lookupSent, targetHash) -} diff --git a/p2p/discv5/topic.go b/p2p/discv5/topic.go deleted file mode 100644 index 609a41297f..0000000000 --- a/p2p/discv5/topic.go +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package discv5 - -import ( - "container/heap" - "fmt" - "math" - "math/rand" - "time" - - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/log" -) - -const ( - maxEntries = 10000 - maxEntriesPerTopic = 50 - - fallbackRegistrationExpiry = 1 * time.Hour -) - -type Topic string - -type topicEntry struct { - topic Topic - fifoIdx uint64 - node *Node - expire mclock.AbsTime -} - -type topicInfo struct { - entries map[uint64]*topicEntry - fifoHead, fifoTail uint64 - rqItem *topicRequestQueueItem - wcl waitControlLoop -} - -// removes tail element from the fifo -func (t *topicInfo) getFifoTail() *topicEntry { - for t.entries[t.fifoTail] == nil { - t.fifoTail++ - } - tail := t.entries[t.fifoTail] - t.fifoTail++ - return tail -} - -type nodeInfo struct { - entries map[Topic]*topicEntry - lastIssuedTicket, lastUsedTicket uint32 - // you can't register a ticket newer than lastUsedTicket before noRegUntil (absolute time) - noRegUntil mclock.AbsTime -} - -type topicTable struct { - db *nodeDB - self *Node - nodes map[*Node]*nodeInfo - topics map[Topic]*topicInfo - globalEntries uint64 - requested topicRequestQueue - requestCnt uint64 - lastGarbageCollection mclock.AbsTime -} - -func newTopicTable(db *nodeDB, self *Node) *topicTable { - if printTestImgLogs { - fmt.Printf("*N %016x\n", self.sha[:8]) - } - return &topicTable{ - db: db, - nodes: make(map[*Node]*nodeInfo), - topics: make(map[Topic]*topicInfo), - self: self, - } -} - -func (t *topicTable) getOrNewTopic(topic Topic) *topicInfo { - ti := t.topics[topic] - if ti == nil { - rqItem := &topicRequestQueueItem{ - topic: topic, - priority: t.requestCnt, - } - ti = &topicInfo{ - entries: make(map[uint64]*topicEntry), - rqItem: rqItem, - } - t.topics[topic] = ti - heap.Push(&t.requested, rqItem) - } - return ti -} - -func (t *topicTable) checkDeleteTopic(topic Topic) { - ti := t.topics[topic] - if ti == nil { - return - } - if len(ti.entries) == 0 && ti.wcl.hasMinimumWaitPeriod() { - delete(t.topics, topic) - heap.Remove(&t.requested, ti.rqItem.index) - } -} - -func (t *topicTable) getOrNewNode(node *Node) *nodeInfo { - n := t.nodes[node] - if n == nil { - //fmt.Printf("newNode %016x %016x\n", t.self.sha[:8], node.sha[:8]) - var issued, used uint32 - if t.db != nil { - issued, used = t.db.fetchTopicRegTickets(node.ID) - } - n = &nodeInfo{ - entries: make(map[Topic]*topicEntry), - lastIssuedTicket: issued, - lastUsedTicket: used, - } - t.nodes[node] = n - } - return n -} - -func (t *topicTable) checkDeleteNode(node *Node) { - if n, ok := t.nodes[node]; ok && len(n.entries) == 0 && n.noRegUntil < mclock.Now() { - //fmt.Printf("deleteNode %016x %016x\n", t.self.sha[:8], node.sha[:8]) - delete(t.nodes, node) - } -} - -func (t *topicTable) storeTicketCounters(node *Node) { - n := t.getOrNewNode(node) - if t.db != nil { - t.db.updateTopicRegTickets(node.ID, n.lastIssuedTicket, n.lastUsedTicket) - } -} - -func (t *topicTable) getEntries(topic Topic) []*Node { - t.collectGarbage() - - te := t.topics[topic] - if te == nil { - return nil - } - nodes := make([]*Node, len(te.entries)) - i := 0 - for _, e := range te.entries { - nodes[i] = e.node - i++ - } - t.requestCnt++ - t.requested.update(te.rqItem, t.requestCnt) - return nodes -} - -func (t *topicTable) addEntry(node *Node, topic Topic) { - n := t.getOrNewNode(node) - // clear previous entries by the same node - for _, e := range n.entries { - t.deleteEntry(e) - } - // *** - n = t.getOrNewNode(node) - - tm := mclock.Now() - te := t.getOrNewTopic(topic) - - if len(te.entries) == maxEntriesPerTopic { - t.deleteEntry(te.getFifoTail()) - } - - if t.globalEntries == maxEntries { - t.deleteEntry(t.leastRequested()) // not empty, no need to check for nil - } - - fifoIdx := te.fifoHead - te.fifoHead++ - entry := &topicEntry{ - topic: topic, - fifoIdx: fifoIdx, - node: node, - expire: tm + mclock.AbsTime(fallbackRegistrationExpiry), - } - if printTestImgLogs { - fmt.Printf("*+ %d %v %016x %016x\n", tm/1000000, topic, t.self.sha[:8], node.sha[:8]) - } - te.entries[fifoIdx] = entry - n.entries[topic] = entry - t.globalEntries++ - te.wcl.registered(tm) -} - -// removes least requested element from the fifo -func (t *topicTable) leastRequested() *topicEntry { - for t.requested.Len() > 0 && t.topics[t.requested[0].topic] == nil { - heap.Pop(&t.requested) - } - if t.requested.Len() == 0 { - return nil - } - return t.topics[t.requested[0].topic].getFifoTail() -} - -// entry should exist -func (t *topicTable) deleteEntry(e *topicEntry) { - if printTestImgLogs { - fmt.Printf("*- %d %v %016x %016x\n", mclock.Now()/1000000, e.topic, t.self.sha[:8], e.node.sha[:8]) - } - ne := t.nodes[e.node].entries - delete(ne, e.topic) - if len(ne) == 0 { - t.checkDeleteNode(e.node) - } - te := t.topics[e.topic] - delete(te.entries, e.fifoIdx) - if len(te.entries) == 0 { - t.checkDeleteTopic(e.topic) - } - t.globalEntries-- -} - -// It is assumed that topics and waitPeriods have the same length. -func (t *topicTable) useTicket(node *Node, serialNo uint32, topics []Topic, idx int, issueTime uint64, waitPeriods []uint32) (registered bool) { - log.Trace("Using discovery ticket", "serial", serialNo, "topics", topics, "waits", waitPeriods) - //fmt.Println("useTicket", serialNo, topics, waitPeriods) - t.collectGarbage() - - n := t.getOrNewNode(node) - if serialNo < n.lastUsedTicket { - return false - } - - tm := mclock.Now() - if serialNo > n.lastUsedTicket && tm < n.noRegUntil { - return false - } - if serialNo != n.lastUsedTicket { - n.lastUsedTicket = serialNo - n.noRegUntil = tm + mclock.AbsTime(noRegTimeout()) - t.storeTicketCounters(node) - } - - currTime := uint64(tm / mclock.AbsTime(time.Second)) - regTime := issueTime + uint64(waitPeriods[idx]) - relTime := int64(currTime - regTime) - if relTime >= -1 && relTime <= regTimeWindow+1 { // give clients a little security margin on both ends - if e := n.entries[topics[idx]]; e == nil { - t.addEntry(node, topics[idx]) - } else { - // if there is an active entry, don't move to the front of the FIFO but prolong expire time - e.expire = tm + mclock.AbsTime(fallbackRegistrationExpiry) - } - return true - } - - return false -} - -func (t *topicTable) getTicket(node *Node, topics []Topic) *ticket { - t.collectGarbage() - - now := mclock.Now() - n := t.getOrNewNode(node) - n.lastIssuedTicket++ - t.storeTicketCounters(node) - - tic := &ticket{ - issueTime: now, - topics: topics, - serial: n.lastIssuedTicket, - regTime: make([]mclock.AbsTime, len(topics)), - } - for i, topic := range topics { - var waitPeriod time.Duration - if topic := t.topics[topic]; topic != nil { - waitPeriod = topic.wcl.waitPeriod - } else { - waitPeriod = minWaitPeriod - } - - tic.regTime[i] = now + mclock.AbsTime(waitPeriod) - } - return tic -} - -const gcInterval = time.Minute - -func (t *topicTable) collectGarbage() { - tm := mclock.Now() - if time.Duration(tm-t.lastGarbageCollection) < gcInterval { - return - } - t.lastGarbageCollection = tm - - for node, n := range t.nodes { - for _, e := range n.entries { - if e.expire <= tm { - t.deleteEntry(e) - } - } - - t.checkDeleteNode(node) - } - - for topic := range t.topics { - t.checkDeleteTopic(topic) - } -} - -const ( - minWaitPeriod = time.Minute - regTimeWindow = 10 // seconds - avgnoRegTimeout = time.Minute * 10 - // target average interval between two incoming ad requests - wcTargetRegInterval = time.Minute * 10 / maxEntriesPerTopic - // - wcTimeConst = time.Minute * 10 -) - -// initialization is not required, will set to minWaitPeriod at first registration -type waitControlLoop struct { - lastIncoming mclock.AbsTime - waitPeriod time.Duration -} - -func (w *waitControlLoop) registered(tm mclock.AbsTime) { - w.waitPeriod = w.nextWaitPeriod(tm) - w.lastIncoming = tm -} - -func (w *waitControlLoop) nextWaitPeriod(tm mclock.AbsTime) time.Duration { - period := tm - w.lastIncoming - wp := time.Duration(float64(w.waitPeriod) * math.Exp((float64(wcTargetRegInterval)-float64(period))/float64(wcTimeConst))) - if wp < minWaitPeriod { - wp = minWaitPeriod - } - return wp -} - -func (w *waitControlLoop) hasMinimumWaitPeriod() bool { - return w.nextWaitPeriod(mclock.Now()) == minWaitPeriod -} - -func noRegTimeout() time.Duration { - e := rand.ExpFloat64() - if e > 100 { - e = 100 - } - return time.Duration(float64(avgnoRegTimeout) * e) -} - -type topicRequestQueueItem struct { - topic Topic - priority uint64 - index int -} - -// A topicRequestQueue implements heap.Interface and holds topicRequestQueueItems. -type topicRequestQueue []*topicRequestQueueItem - -func (tq topicRequestQueue) Len() int { return len(tq) } - -func (tq topicRequestQueue) Less(i, j int) bool { - return tq[i].priority < tq[j].priority -} - -func (tq topicRequestQueue) Swap(i, j int) { - tq[i], tq[j] = tq[j], tq[i] - tq[i].index = i - tq[j].index = j -} - -func (tq *topicRequestQueue) Push(x interface{}) { - n := len(*tq) - item := x.(*topicRequestQueueItem) - item.index = n - *tq = append(*tq, item) -} - -func (tq *topicRequestQueue) Pop() interface{} { - old := *tq - n := len(old) - item := old[n-1] - item.index = -1 - *tq = old[0 : n-1] - return item -} - -func (tq *topicRequestQueue) update(item *topicRequestQueueItem, priority uint64) { - item.priority = priority - heap.Fix(tq, item.index) -} diff --git a/p2p/discv5/topic_test.go b/p2p/discv5/topic_test.go deleted file mode 100644 index ba79993f29..0000000000 --- a/p2p/discv5/topic_test.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package discv5 - -import ( - "encoding/binary" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/mclock" -) - -func TestTopicRadius(t *testing.T) { - now := mclock.Now() - topic := Topic("qwerty") - rad := newTopicRadius(topic) - targetRad := (^uint64(0)) / 100 - - waitFn := func(addr common.Hash) time.Duration { - prefix := binary.BigEndian.Uint64(addr[0:8]) - dist := prefix ^ rad.topicHashPrefix - relDist := float64(dist) / float64(targetRad) - relTime := (1 - relDist/2) * 2 - if relTime < 0 { - relTime = 0 - } - return time.Duration(float64(targetWaitTime) * relTime) - } - - bcnt := 0 - cnt := 0 - var sum float64 - for cnt < 100 { - addr := rad.nextTarget(false).target - wait := waitFn(addr) - ticket := &ticket{ - topics: []Topic{topic}, - regTime: []mclock.AbsTime{mclock.AbsTime(wait)}, - node: &Node{nodeNetGuts: nodeNetGuts{sha: addr}}, - } - rad.adjustWithTicket(now, addr, ticketRef{ticket, 0}) - if rad.radius != maxRadius { - cnt++ - sum += float64(rad.radius) - } else { - bcnt++ - if bcnt > 500 { - t.Errorf("Radius did not converge in 500 iterations") - } - } - } - avgRel := sum / float64(cnt) / float64(targetRad) - if avgRel > 1.05 || avgRel < 0.95 { - t.Errorf("Average/target ratio is too far from 1 (%v)", avgRel) - } -} diff --git a/p2p/discv5/udp.go b/p2p/discv5/udp.go deleted file mode 100644 index 088f95cac6..0000000000 --- a/p2p/discv5/udp.go +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package discv5 - -import ( - "bytes" - "crypto/ecdsa" - "errors" - "fmt" - "net" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p/netutil" - "github.com/ethereum/go-ethereum/rlp" -) - -const Version = 4 - -// Errors -var ( - errPacketTooSmall = errors.New("too small") - errBadPrefix = errors.New("bad prefix") -) - -// Timeouts -const ( - respTimeout = 500 * time.Millisecond - expiration = 20 * time.Second -) - -// RPC request structures -type ( - ping struct { - Version uint - From, To rpcEndpoint - Expiration uint64 - - // v5 - Topics []Topic - - // Ignore additional fields (for forward compatibility). - Rest []rlp.RawValue `rlp:"tail"` - } - - // pong is the reply to ping. - pong struct { - // This field should mirror the UDP envelope address - // of the ping packet, which provides a way to discover the - // the external address (after NAT). - To rpcEndpoint - - ReplyTok []byte // This contains the hash of the ping packet. - Expiration uint64 // Absolute timestamp at which the packet becomes invalid. - - // v5 - TopicHash common.Hash - TicketSerial uint32 - WaitPeriods []uint32 - - // Ignore additional fields (for forward compatibility). - Rest []rlp.RawValue `rlp:"tail"` - } - - // findnode is a query for nodes close to the given target. - findnode struct { - Target NodeID // doesn't need to be an actual public key - Expiration uint64 - // Ignore additional fields (for forward compatibility). - Rest []rlp.RawValue `rlp:"tail"` - } - - // findnode is a query for nodes close to the given target. - findnodeHash struct { - Target common.Hash - Expiration uint64 - // Ignore additional fields (for forward compatibility). - Rest []rlp.RawValue `rlp:"tail"` - } - - // reply to findnode - neighbors struct { - Nodes []rpcNode - Expiration uint64 - // Ignore additional fields (for forward compatibility). - Rest []rlp.RawValue `rlp:"tail"` - } - - topicRegister struct { - Topics []Topic - Idx uint - Pong []byte - } - - topicQuery struct { - Topic Topic - Expiration uint64 - } - - // reply to topicQuery - topicNodes struct { - Echo common.Hash - Nodes []rpcNode - } - - rpcNode struct { - IP net.IP // len 4 for IPv4 or 16 for IPv6 - UDP uint16 // for discovery protocol - TCP uint16 // for RLPx protocol - ID NodeID - } - - rpcEndpoint struct { - IP net.IP // len 4 for IPv4 or 16 for IPv6 - UDP uint16 // for discovery protocol - TCP uint16 // for RLPx protocol - } -) - -var ( - versionPrefix = []byte("temporary discovery v5") - versionPrefixSize = len(versionPrefix) - sigSize = 520 / 8 - headSize = versionPrefixSize + sigSize // space of packet frame data -) - -// Neighbors replies are sent across multiple packets to -// stay below the 1280 byte limit. We compute the maximum number -// of entries by stuffing a packet until it grows too large. -var maxNeighbors = func() int { - p := neighbors{Expiration: ^uint64(0)} - maxSizeNode := rpcNode{IP: make(net.IP, 16), UDP: ^uint16(0), TCP: ^uint16(0)} - for n := 0; ; n++ { - p.Nodes = append(p.Nodes, maxSizeNode) - size, _, err := rlp.EncodeToReader(p) - if err != nil { - // If this ever happens, it will be caught by the unit tests. - panic("cannot encode: " + err.Error()) - } - if headSize+size+1 >= 1280 { - return n - } - } -}() - -var maxTopicNodes = func() int { - p := topicNodes{} - maxSizeNode := rpcNode{IP: make(net.IP, 16), UDP: ^uint16(0), TCP: ^uint16(0)} - for n := 0; ; n++ { - p.Nodes = append(p.Nodes, maxSizeNode) - size, _, err := rlp.EncodeToReader(p) - if err != nil { - // If this ever happens, it will be caught by the unit tests. - panic("cannot encode: " + err.Error()) - } - if headSize+size+1 >= 1280 { - return n - } - } -}() - -func makeEndpoint(addr *net.UDPAddr, tcpPort uint16) rpcEndpoint { - ip := addr.IP.To4() - if ip == nil { - ip = addr.IP.To16() - } - return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort} -} - -func nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) { - if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil { - return nil, err - } - n := NewNode(rn.ID, rn.IP, rn.UDP, rn.TCP) - err := n.validateComplete() - return n, err -} - -func nodeToRPC(n *Node) rpcNode { - return rpcNode{ID: n.ID, IP: n.IP, UDP: n.UDP, TCP: n.TCP} -} - -type ingressPacket struct { - remoteID NodeID - remoteAddr *net.UDPAddr - ev nodeEvent - hash []byte - data interface{} // one of the RPC structs - rawData []byte -} - -type conn interface { - ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) - WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) - Close() error - LocalAddr() net.Addr -} - -// udp implements the RPC protocol. -type udp struct { - conn conn - priv *ecdsa.PrivateKey - ourEndpoint rpcEndpoint - net *Network -} - -// ListenUDP returns a new table that listens for UDP packets on laddr. -func ListenUDP(priv *ecdsa.PrivateKey, conn conn, nodeDBPath string, netrestrict *netutil.Netlist) (*Network, error) { - realaddr := conn.LocalAddr().(*net.UDPAddr) - transport, err := listenUDP(priv, conn, realaddr) - if err != nil { - return nil, err - } - net, err := newNetwork(transport, priv.PublicKey, nodeDBPath, netrestrict) - if err != nil { - return nil, err - } - log.Info("UDP listener up", "net", net.tab.self) - transport.net = net - go transport.readLoop() - return net, nil -} - -func listenUDP(priv *ecdsa.PrivateKey, conn conn, realaddr *net.UDPAddr) (*udp, error) { - return &udp{conn: conn, priv: priv, ourEndpoint: makeEndpoint(realaddr, uint16(realaddr.Port))}, nil -} - -func (t *udp) localAddr() *net.UDPAddr { - return t.conn.LocalAddr().(*net.UDPAddr) -} - -func (t *udp) Close() { - t.conn.Close() -} - -func (t *udp) send(remote *Node, ptype nodeEvent, data interface{}) (hash []byte) { - hash, _ = t.sendPacket(remote.ID, remote.addr(), byte(ptype), data) - return hash -} - -func (t *udp) sendPing(remote *Node, toaddr *net.UDPAddr, topics []Topic) (hash []byte) { - hash, _ = t.sendPacket(remote.ID, toaddr, byte(pingPacket), ping{ - Version: Version, - From: t.ourEndpoint, - To: makeEndpoint(toaddr, uint16(toaddr.Port)), // TODO: maybe use known TCP port from DB - Expiration: uint64(time.Now().Add(expiration).Unix()), - Topics: topics, - }) - return hash -} - -func (t *udp) sendNeighbours(remote *Node, results []*Node) { - // Send neighbors in chunks with at most maxNeighbors per packet - // to stay below the 1280 byte limit. - p := neighbors{Expiration: uint64(time.Now().Add(expiration).Unix())} - for i, result := range results { - p.Nodes = append(p.Nodes, nodeToRPC(result)) - if len(p.Nodes) == maxNeighbors || i == len(results)-1 { - t.sendPacket(remote.ID, remote.addr(), byte(neighborsPacket), p) - p.Nodes = p.Nodes[:0] - } - } -} - -func (t *udp) sendFindnodeHash(remote *Node, target common.Hash) { - t.sendPacket(remote.ID, remote.addr(), byte(findnodeHashPacket), findnodeHash{ - Target: target, - Expiration: uint64(time.Now().Add(expiration).Unix()), - }) -} - -func (t *udp) sendTopicRegister(remote *Node, topics []Topic, idx int, pong []byte) { - t.sendPacket(remote.ID, remote.addr(), byte(topicRegisterPacket), topicRegister{ - Topics: topics, - Idx: uint(idx), - Pong: pong, - }) -} - -func (t *udp) sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node) { - p := topicNodes{Echo: queryHash} - var sent bool - for _, result := range nodes { - if result.IP.Equal(t.net.tab.self.IP) || netutil.CheckRelayIP(remote.IP, result.IP) == nil { - p.Nodes = append(p.Nodes, nodeToRPC(result)) - } - if len(p.Nodes) == maxTopicNodes { - t.sendPacket(remote.ID, remote.addr(), byte(topicNodesPacket), p) - p.Nodes = p.Nodes[:0] - sent = true - } - } - if !sent || len(p.Nodes) > 0 { - t.sendPacket(remote.ID, remote.addr(), byte(topicNodesPacket), p) - } -} - -func (t *udp) sendPacket(toid NodeID, toaddr *net.UDPAddr, ptype byte, req interface{}) (hash []byte, err error) { - //fmt.Println("sendPacket", nodeEvent(ptype), toaddr.String(), toid.String()) - packet, hash, err := encodePacket(t.priv, ptype, req) - if err != nil { - //fmt.Println(err) - return hash, err - } - log.Trace(fmt.Sprintf(">>> %v to %x@%v", nodeEvent(ptype), toid[:8], toaddr)) - if nbytes, err := t.conn.WriteToUDP(packet, toaddr); err != nil { - log.Trace(fmt.Sprint("UDP send failed:", err)) - } else { - egressTrafficMeter.Mark(int64(nbytes)) - } - //fmt.Println(err) - return hash, err -} - -// zeroed padding space for encodePacket. -var headSpace = make([]byte, headSize) - -func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) (p, hash []byte, err error) { - b := new(bytes.Buffer) - b.Write(headSpace) - b.WriteByte(ptype) - if err := rlp.Encode(b, req); err != nil { - log.Error(fmt.Sprint("error encoding packet:", err)) - return nil, nil, err - } - packet := b.Bytes() - sig, err := crypto.Sign(crypto.Keccak256(packet[headSize:]), priv) - if err != nil { - log.Error(fmt.Sprint("could not sign packet:", err)) - return nil, nil, err - } - copy(packet, versionPrefix) - copy(packet[versionPrefixSize:], sig) - hash = crypto.Keccak256(packet[versionPrefixSize:]) - return packet, hash, nil -} - -// readLoop runs in its own goroutine. it injects ingress UDP packets -// into the network loop. -func (t *udp) readLoop() { - defer t.conn.Close() - // Discovery packets are defined to be no larger than 1280 bytes. - // Packets larger than this size will be cut at the end and treated - // as invalid because their hash won't match. - buf := make([]byte, 1280) - for { - nbytes, from, err := t.conn.ReadFromUDP(buf) - ingressTrafficMeter.Mark(int64(nbytes)) - if netutil.IsTemporaryError(err) { - // Ignore temporary read errors. - log.Debug(fmt.Sprintf("Temporary read error: %v", err)) - continue - } else if err != nil { - // Shut down the loop for permament errors. - log.Debug(fmt.Sprintf("Read error: %v", err)) - return - } - t.handlePacket(from, buf[:nbytes]) - } -} - -func (t *udp) handlePacket(from *net.UDPAddr, buf []byte) error { - pkt := ingressPacket{remoteAddr: from} - if err := decodePacket(buf, &pkt); err != nil { - log.Debug(fmt.Sprintf("Bad packet from %v: %v", from, err)) - //fmt.Println("bad packet", err) - return err - } - t.net.reqReadPacket(pkt) - return nil -} - -func decodePacket(buffer []byte, pkt *ingressPacket) error { - if len(buffer) < headSize+1 { - return errPacketTooSmall - } - buf := make([]byte, len(buffer)) - copy(buf, buffer) - prefix, sig, sigdata := buf[:versionPrefixSize], buf[versionPrefixSize:headSize], buf[headSize:] - if !bytes.Equal(prefix, versionPrefix) { - return errBadPrefix - } - fromID, err := recoverNodeID(crypto.Keccak256(buf[headSize:]), sig) - if err != nil { - return err - } - pkt.rawData = buf - pkt.hash = crypto.Keccak256(buf[versionPrefixSize:]) - pkt.remoteID = fromID - switch pkt.ev = nodeEvent(sigdata[0]); pkt.ev { - case pingPacket: - pkt.data = new(ping) - case pongPacket: - pkt.data = new(pong) - case findnodePacket: - pkt.data = new(findnode) - case neighborsPacket: - pkt.data = new(neighbors) - case findnodeHashPacket: - pkt.data = new(findnodeHash) - case topicRegisterPacket: - pkt.data = new(topicRegister) - case topicQueryPacket: - pkt.data = new(topicQuery) - case topicNodesPacket: - pkt.data = new(topicNodes) - default: - return fmt.Errorf("unknown packet type: %d", sigdata[0]) - } - s := rlp.NewStream(bytes.NewReader(sigdata[1:]), 0) - err = s.Decode(pkt.data) - return err -} diff --git a/p2p/dnsdisc/client.go b/p2p/dnsdisc/client.go index b872784828..3e4b50aadd 100644 --- a/p2p/dnsdisc/client.go +++ b/p2p/dnsdisc/client.go @@ -37,9 +37,10 @@ import ( // Client discovers nodes by querying DNS servers. type Client struct { - cfg Config - clock mclock.Clock - entries *lru.Cache + cfg Config + clock mclock.Clock + entries *lru.Cache + ratelimit *rate.Limiter } // Config holds configuration options for the client. @@ -97,8 +98,12 @@ func NewClient(cfg Config) *Client { panic(err) } rlimit := rate.NewLimiter(rate.Limit(cfg.RateLimit), 10) - cfg.Resolver = &rateLimitResolver{cfg.Resolver, rlimit} - return &Client{cfg: cfg, entries: cache, clock: mclock.System{}} + return &Client{ + cfg: cfg, + entries: cache, + clock: mclock.System{}, + ratelimit: rlimit, + } } // SyncTree downloads the entire node tree at the given URL. @@ -157,6 +162,13 @@ func parseAndVerifyRoot(txt string, loc *linkEntry) (rootEntry, error) { // resolveEntry retrieves an entry from the cache or fetches it from the network // if it isn't cached. func (c *Client) resolveEntry(ctx context.Context, domain, hash string) (entry, error) { + // The rate limit always applies, even when the result might be cached. This is + // important because it avoids hot-spinning in consumers of node iterators created on + // this client. + if err := c.ratelimit.Wait(ctx); err != nil { + return nil, err + } + cacheKey := truncateHash(hash) if e, ok := c.entries.Get(cacheKey); ok { return e.(entry), nil @@ -196,19 +208,6 @@ func (c *Client) doResolveEntry(ctx context.Context, domain, hash string) (entry return nil, nameError{name, errNoEntry} } -// rateLimitResolver applies a rate limit to a Resolver. -type rateLimitResolver struct { - r Resolver - limiter *rate.Limiter -} - -func (r *rateLimitResolver) LookupTXT(ctx context.Context, domain string) ([]string, error) { - if err := r.limiter.Wait(ctx); err != nil { - return nil, err - } - return r.r.LookupTXT(ctx, domain) -} - // randomIterator traverses a set of trees and returns nodes found in them. type randomIterator struct { cur *enode.Node @@ -217,8 +216,11 @@ type randomIterator struct { c *Client mu sync.Mutex - trees map[string]*clientTree // all trees lc linkCache // tracks tree dependencies + trees map[string]*clientTree // all trees + // buffers for syncableTrees + syncableList []*clientTree + disabledList []*clientTree } func (c *Client) newRandomIterator() *randomIterator { @@ -238,10 +240,10 @@ func (it *randomIterator) Node() *enode.Node { // Close closes the iterator. func (it *randomIterator) Close() { + it.cancelFn() + it.mu.Lock() defer it.mu.Unlock() - - it.cancelFn() it.trees = nil } @@ -264,7 +266,7 @@ func (it *randomIterator) addTree(url string) error { // nextNode syncs random tree entries until it finds a node. func (it *randomIterator) nextNode() *enode.Node { for { - ct := it.nextTree() + ct := it.pickTree() if ct == nil { return nil } @@ -282,26 +284,79 @@ func (it *randomIterator) nextNode() *enode.Node { } } -// nextTree returns a random tree. -func (it *randomIterator) nextTree() *clientTree { +// pickTree returns a random tree to sync from. +func (it *randomIterator) pickTree() *clientTree { it.mu.Lock() defer it.mu.Unlock() + // Rebuild the trees map if any links have changed. if it.lc.changed { it.rebuildTrees() it.lc.changed = false } - if len(it.trees) == 0 { - return nil + + for { + canSync, trees := it.syncableTrees() + switch { + case canSync: + // Pick a random tree. + return trees[rand.Intn(len(trees))] + case len(trees) > 0: + // No sync action can be performed on any tree right now. The only meaningful + // thing to do is waiting for any root record to get updated. + if !it.waitForRootUpdates(trees) { + // Iterator was closed while waiting. + return nil + } + default: + // There are no trees left, the iterator was closed. + return nil + } } - limit := rand.Intn(len(it.trees)) +} + +// syncableTrees finds trees on which any meaningful sync action can be performed. +func (it *randomIterator) syncableTrees() (canSync bool, trees []*clientTree) { + // Resize tree lists. + it.syncableList = it.syncableList[:0] + it.disabledList = it.disabledList[:0] + + // Partition them into the two lists. for _, ct := range it.trees { - if limit == 0 { - return ct + if ct.canSyncRandom() { + it.syncableList = append(it.syncableList, ct) + } else { + it.disabledList = append(it.disabledList, ct) } - limit-- } - return nil + if len(it.syncableList) > 0 { + return true, it.syncableList + } + return false, it.disabledList +} + +// waitForRootUpdates waits for the closest scheduled root check time on the given trees. +func (it *randomIterator) waitForRootUpdates(trees []*clientTree) bool { + var minTree *clientTree + var nextCheck mclock.AbsTime + for _, ct := range trees { + check := ct.nextScheduledRootCheck() + if minTree == nil || check < nextCheck { + minTree = ct + nextCheck = check + } + } + + sleep := nextCheck.Sub(it.c.clock.Now()) + it.c.cfg.Logger.Debug("DNS iterator waiting for root updates", "sleep", sleep, "tree", minTree.loc.domain) + timeout := it.c.clock.NewTimer(sleep) + defer timeout.Stop() + select { + case <-timeout.C(): + return true + case <-it.ctx.Done(): + return false // Iterator was closed. + } } // rebuildTrees rebuilds the 'trees' map. diff --git a/p2p/dnsdisc/client_test.go b/p2p/dnsdisc/client_test.go index 6a6705abf2..741bee4230 100644 --- a/p2p/dnsdisc/client_test.go +++ b/p2p/dnsdisc/client_test.go @@ -231,6 +231,53 @@ func TestIteratorRootRecheckOnFail(t *testing.T) { checkIterator(t, it, nodes) } +// This test checks that the iterator works correctly when the tree is initially empty. +func TestIteratorEmptyTree(t *testing.T) { + var ( + clock = new(mclock.Simulated) + nodes = testNodes(nodesSeed1, 1) + resolver = newMapResolver() + c = NewClient(Config{ + Resolver: resolver, + Logger: testlog.Logger(t, log.LvlTrace), + RecheckInterval: 20 * time.Minute, + RateLimit: 500, + }) + ) + c.clock = clock + tree1, url := makeTestTree("n", nil, nil) + tree2, _ := makeTestTree("n", nodes, nil) + resolver.add(tree1.ToTXT("n")) + + // Start the iterator. + node := make(chan *enode.Node) + it, err := c.NewIterator(url) + if err != nil { + t.Fatal(err) + } + go func() { + it.Next() + node <- it.Node() + }() + + // Wait for the client to get stuck in waitForRootUpdates. + clock.WaitForTimers(1) + + // Now update the root. + resolver.add(tree2.ToTXT("n")) + + // Wait for it to pick up the root change. + clock.Run(c.cfg.RecheckInterval) + select { + case n := <-node: + if n.ID() != nodes[0].ID() { + t.Fatalf("wrong node returned") + } + case <-time.After(5 * time.Second): + t.Fatal("it.Next() did not unblock within 5s of real time") + } +} + // updateSomeNodes applies ENR updates to some of the given nodes. func updateSomeNodes(keySeed int64, nodes []*enode.Node) { keys := testKeys(nodesSeed1, len(nodes)) diff --git a/p2p/dnsdisc/sync.go b/p2p/dnsdisc/sync.go index 36f02acba6..073547c90d 100644 --- a/p2p/dnsdisc/sync.go +++ b/p2p/dnsdisc/sync.go @@ -25,9 +25,9 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" ) -const ( - rootRecheckFailCount = 5 // update root if this many leaf requests fail -) +// This is the number of consecutive leaf requests that may fail before +// we consider re-resolving the tree root. +const rootRecheckFailCount = 5 // clientTree is a full tree being synced. type clientTree struct { @@ -89,13 +89,22 @@ func (ct *clientTree) syncRandom(ctx context.Context) (n *enode.Node, err error) ct.gcLinks() // Sync next random entry in ENR tree. Once every node has been visited, we simply - // start over. This is fine because entries are cached. + // start over. This is fine because entries are cached internally by the client LRU + // also by DNS resolvers. if ct.enrs.done() { ct.enrs = newSubtreeSync(ct.c, ct.loc, ct.root.eroot, false) } return ct.syncNextRandomENR(ctx) } +// canSyncRandom checks if any meaningful action can be performed by syncRandom. +func (ct *clientTree) canSyncRandom() bool { + // Note: the check for non-zero leaf count is very important here. + // If we're done syncing all nodes, and no leaves were found, the tree + // is empty and we can't use it for sync. + return ct.rootUpdateDue() || !ct.links.done() || !ct.enrs.done() || ct.enrs.leaves != 0 +} + // gcLinks removes outdated links from the global link cache. GC runs once // when the link sync finishes. func (ct *clientTree) gcLinks() { @@ -184,10 +193,14 @@ func (ct *clientTree) updateRoot(ctx context.Context) error { // rootUpdateDue returns true when a root update is needed. func (ct *clientTree) rootUpdateDue() bool { tooManyFailures := ct.leafFailCount > rootRecheckFailCount - scheduledCheck := ct.c.clock.Now().Sub(ct.lastRootCheck) > ct.c.cfg.RecheckInterval + scheduledCheck := ct.c.clock.Now() >= ct.nextScheduledRootCheck() return ct.root == nil || tooManyFailures || scheduledCheck } +func (ct *clientTree) nextScheduledRootCheck() mclock.AbsTime { + return ct.lastRootCheck.Add(ct.c.cfg.RecheckInterval) +} + // slowdownRootUpdate applies a delay to root resolution if is tried // too frequently. This avoids busy polling when the client is offline. // Returns true if the timeout passed, false if sync was canceled. @@ -218,10 +231,11 @@ type subtreeSync struct { root string missing []string // missing tree node hashes link bool // true if this sync is for the link tree + leaves int // counter of synced leaves } func newSubtreeSync(c *Client, loc *linkEntry, root string, link bool) *subtreeSync { - return &subtreeSync{c, loc, root, []string{root}, link} + return &subtreeSync{c, loc, root, []string{root}, link, 0} } func (ts *subtreeSync) done() bool { @@ -253,10 +267,12 @@ func (ts *subtreeSync) resolveNext(ctx context.Context, hash string) (entry, err if ts.link { return nil, errENRInLinkTree } + ts.leaves++ case *linkEntry: if !ts.link { return nil, errLinkInENRTree } + ts.leaves++ case *branchEntry: ts.missing = append(ts.missing, e.children...) } diff --git a/p2p/dnsdisc/tree.go b/p2p/dnsdisc/tree.go index 82a935ca41..410ec3b854 100644 --- a/p2p/dnsdisc/tree.go +++ b/p2p/dnsdisc/tree.go @@ -113,10 +113,41 @@ func (t *Tree) Nodes() []*enode.Node { return nodes } +/* +We want to keep the UDP size below 512 bytes. The UDP size is roughly: +UDP length = 8 + UDP payload length ( 229 ) +UPD Payload length: + - dns.id 2 + - dns.flags 2 + - dns.count.queries 2 + - dns.count.answers 2 + - dns.count.auth_rr 2 + - dns.count.add_rr 2 + - queries (query-size + 6) + - answers : + - dns.resp.name 2 + - dns.resp.type 2 + - dns.resp.class 2 + - dns.resp.ttl 4 + - dns.resp.len 2 + - dns.txt.length 1 + - dns.txt resp_data_size + +So the total size is roughly a fixed overhead of `39`, and the size of the +query (domain name) and response. +The query size is, for example, FVY6INQ6LZ33WLCHO3BPR3FH6Y.snap.mainnet.ethdisco.net (52) + +We also have some static data in the response, such as `enrtree-branch:`, and potentially +splitting the response up with `" "`, leaving us with a size of roughly `400` that we need +to stay below. + +The number `370` is used to have some margin for extra overhead (for example, the dns query +may be larger - more subdomains). +*/ const ( - hashAbbrev = 16 - maxChildren = 300 / hashAbbrev * (13 / 8) - minHashLength = 12 + hashAbbrevSize = 1 + 16*13/8 // Size of an encoded hash (plus comma) + maxChildren = 370 / hashAbbrevSize // 13 children + minHashLength = 12 ) // MakeTree creates a tree containing the given nodes and links. diff --git a/p2p/enode/nodedb.go b/p2p/enode/nodedb.go index bd066ce857..d62f383f0b 100644 --- a/p2p/enode/nodedb.go +++ b/p2p/enode/nodedb.go @@ -61,6 +61,10 @@ const ( dbVersion = 9 ) +var ( + errInvalidIP = errors.New("invalid IP") +) + var zeroIP = make(net.IP, 16) // DB is the node database, storing previously seen nodes and any collected metadata about @@ -163,7 +167,7 @@ func splitNodeItemKey(key []byte) (id ID, ip net.IP, field string) { } key = key[len(dbDiscoverRoot)+1:] // Split out the IP. - ip = net.IP(key[:16]) + ip = key[:16] if ip4 := ip.To4(); ip4 != nil { ip = ip4 } @@ -359,16 +363,25 @@ func (db *DB) expireNodes() { // LastPingReceived retrieves the time of the last ping packet received from // a remote node. func (db *DB) LastPingReceived(id ID, ip net.IP) time.Time { + if ip = ip.To16(); ip == nil { + return time.Time{} + } return time.Unix(db.fetchInt64(nodeItemKey(id, ip, dbNodePing)), 0) } // UpdateLastPingReceived updates the last time we tried contacting a remote node. func (db *DB) UpdateLastPingReceived(id ID, ip net.IP, instance time.Time) error { + if ip = ip.To16(); ip == nil { + return errInvalidIP + } return db.storeInt64(nodeItemKey(id, ip, dbNodePing), instance.Unix()) } // LastPongReceived retrieves the time of the last successful pong from remote node. func (db *DB) LastPongReceived(id ID, ip net.IP) time.Time { + if ip = ip.To16(); ip == nil { + return time.Time{} + } // Launch expirer db.ensureExpirer() return time.Unix(db.fetchInt64(nodeItemKey(id, ip, dbNodePong)), 0) @@ -376,26 +389,41 @@ func (db *DB) LastPongReceived(id ID, ip net.IP) time.Time { // UpdateLastPongReceived updates the last pong time of a node. func (db *DB) UpdateLastPongReceived(id ID, ip net.IP, instance time.Time) error { + if ip = ip.To16(); ip == nil { + return errInvalidIP + } return db.storeInt64(nodeItemKey(id, ip, dbNodePong), instance.Unix()) } // FindFails retrieves the number of findnode failures since bonding. func (db *DB) FindFails(id ID, ip net.IP) int { + if ip = ip.To16(); ip == nil { + return 0 + } return int(db.fetchInt64(nodeItemKey(id, ip, dbNodeFindFails))) } // UpdateFindFails updates the number of findnode failures since bonding. func (db *DB) UpdateFindFails(id ID, ip net.IP, fails int) error { + if ip = ip.To16(); ip == nil { + return errInvalidIP + } return db.storeInt64(nodeItemKey(id, ip, dbNodeFindFails), int64(fails)) } // FindFailsV5 retrieves the discv5 findnode failure counter. func (db *DB) FindFailsV5(id ID, ip net.IP) int { + if ip = ip.To16(); ip == nil { + return 0 + } return int(db.fetchInt64(v5Key(id, ip, dbNodeFindFails))) } // UpdateFindFailsV5 stores the discv5 findnode failure counter. func (db *DB) UpdateFindFailsV5(id ID, ip net.IP, fails int) error { + if ip = ip.To16(); ip == nil { + return errInvalidIP + } return db.storeInt64(v5Key(id, ip, dbNodeFindFails), int64(fails)) } diff --git a/p2p/enr/enr.go b/p2p/enr/enr.go index c36ae9e3ed..05e43fd805 100644 --- a/p2p/enr/enr.go +++ b/p2p/enr/enr.go @@ -50,6 +50,7 @@ var ( errNotSorted = errors.New("record key/value pairs are not sorted by key") errDuplicateKey = errors.New("record contains duplicate key") errIncompletePair = errors.New("record contains incomplete k/v pair") + errIncompleteList = errors.New("record contains less than two list elements") errTooBig = fmt.Errorf("record bigger than %d bytes", SizeLimit) errEncodeUnsigned = errors.New("can't encode unsigned record") errNotFound = errors.New("no such key in record") @@ -209,9 +210,15 @@ func decodeRecord(s *rlp.Stream) (dec Record, raw []byte, err error) { return dec, raw, err } if err = s.Decode(&dec.signature); err != nil { + if err == rlp.EOL { + err = errIncompleteList + } return dec, raw, err } if err = s.Decode(&dec.seq); err != nil { + if err == rlp.EOL { + err = errIncompleteList + } return dec, raw, err } // The rest of the record contains sorted k/v pairs. diff --git a/p2p/enr/enr_test.go b/p2p/enr/enr_test.go index 96a9ced5cf..bf3f104744 100644 --- a/p2p/enr/enr_test.go +++ b/p2p/enr/enr_test.go @@ -231,6 +231,29 @@ func TestRecordTooBig(t *testing.T) { require.NoError(t, signTest([]byte{5}, &r)) } +// This checks that incomplete RLP inputs are handled correctly. +func TestDecodeIncomplete(t *testing.T) { + type decTest struct { + input []byte + err error + } + tests := []decTest{ + {[]byte{0xC0}, errIncompleteList}, + {[]byte{0xC1, 0x1}, errIncompleteList}, + {[]byte{0xC2, 0x1, 0x2}, nil}, + {[]byte{0xC3, 0x1, 0x2, 0x3}, errIncompletePair}, + {[]byte{0xC4, 0x1, 0x2, 0x3, 0x4}, nil}, + {[]byte{0xC5, 0x1, 0x2, 0x3, 0x4, 0x5}, errIncompletePair}, + } + for _, test := range tests { + var r Record + err := rlp.DecodeBytes(test.input, &r) + if err != test.err { + t.Errorf("wrong error for %X: %v", test.input, err) + } + } +} + // TestSignEncodeAndDecodeRandom tests encoding/decoding of records containing random key/value pairs. func TestSignEncodeAndDecodeRandom(t *testing.T) { var r Record diff --git a/p2p/message.go b/p2p/message.go index 10b55a939c..bd048138c3 100644 --- a/p2p/message.go +++ b/p2p/message.go @@ -70,6 +70,10 @@ func (msg Msg) Discard() error { return err } +func (msg Msg) Time() time.Time { + return msg.ReceivedAt +} + type MsgReader interface { ReadMsg() (Msg, error) } diff --git a/p2p/metrics.go b/p2p/metrics.go index 44946473fa..be0d2f495e 100644 --- a/p2p/metrics.go +++ b/p2p/metrics.go @@ -25,8 +25,17 @@ import ( ) const ( + // ingressMeterName is the prefix of the per-packet inbound metrics. ingressMeterName = "p2p/ingress" - egressMeterName = "p2p/egress" + + // egressMeterName is the prefix of the per-packet outbound metrics. + egressMeterName = "p2p/egress" + + // HandleHistName is the prefix of the per-packet serving time histograms. + HandleHistName = "p2p/handle" + + // WaitHistName is the prefix of the per-packet (req only) waiting time histograms. + WaitHistName = "p2p/wait" ) var ( diff --git a/p2p/nat/nat.go b/p2p/nat/nat.go index f47534784c..9d5519b9c4 100644 --- a/p2p/nat/nat.go +++ b/p2p/nat/nat.go @@ -219,9 +219,8 @@ func (n *autodisc) String() string { defer n.mu.Unlock() if n.found == nil { return n.what - } else { - return n.found.String() } + return n.found.String() } // wait blocks until auto-discovery has been performed. diff --git a/p2p/nodestate/nodestate.go b/p2p/nodestate/nodestate.go index ab28b47a15..9323d53cbd 100644 --- a/p2p/nodestate/nodestate.go +++ b/p2p/nodestate/nodestate.go @@ -599,6 +599,7 @@ func (ns *NodeStateMachine) updateEnode(n *enode.Node) (enode.ID, *nodeInfo) { node := ns.nodes[id] if node != nil && n.Seq() > node.node.Seq() { node.node = n + node.dirty = true } return id, node } @@ -725,7 +726,7 @@ func (ns *NodeStateMachine) opFinish() { } ns.opPending = nil ns.opFlag = false - ns.opWait.Signal() + ns.opWait.Broadcast() } // Operation calls the given function as an operation callback. This allows the caller @@ -857,6 +858,23 @@ func (ns *NodeStateMachine) GetField(n *enode.Node, field Field) interface{} { return nil } +// GetState retrieves the current state of the given node. Note that when used in a +// subscription callback the result can be out of sync with the state change represented +// by the callback parameters so extra safety checks might be necessary. +func (ns *NodeStateMachine) GetState(n *enode.Node) Flags { + ns.lock.Lock() + defer ns.lock.Unlock() + + ns.checkStarted() + if ns.closed { + return Flags{} + } + if _, node := ns.updateEnode(n); node != nil { + return Flags{mask: node.state, setup: ns.setup} + } + return Flags{} +} + // SetField sets the given field of the given node and blocks until the operation is finished func (ns *NodeStateMachine) SetField(n *enode.Node, field Field, value interface{}) error { ns.lock.Lock() diff --git a/p2p/nodestate/nodestate_test.go b/p2p/nodestate/nodestate_test.go index 5f99a3da74..d06ad755e2 100644 --- a/p2p/nodestate/nodestate_test.go +++ b/p2p/nodestate/nodestate_test.go @@ -240,9 +240,8 @@ func uint64FieldEnc(field interface{}) ([]byte, error) { if u, ok := field.(uint64); ok { enc, err := rlp.EncodeToBytes(&u) return enc, err - } else { - return nil, errors.New("invalid field type") } + return nil, errors.New("invalid field type") } func uint64FieldDec(enc []byte) (interface{}, error) { @@ -254,9 +253,8 @@ func uint64FieldDec(enc []byte) (interface{}, error) { func stringFieldEnc(field interface{}) ([]byte, error) { if s, ok := field.(string); ok { return []byte(s), nil - } else { - return nil, errors.New("invalid field type") } + return nil, errors.New("invalid field type") } func stringFieldDec(enc []byte) (interface{}, error) { diff --git a/p2p/peer.go b/p2p/peer.go index a9c3cf01da..8ebc858392 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -158,6 +158,20 @@ func (p *Peer) Caps() []Cap { return p.rw.caps } +// RunningCap returns true if the peer is actively connected using any of the +// enumerated versions of a specific protocol, meaning that at least one of the +// versions is supported by both this node and the peer p. +func (p *Peer) RunningCap(protocol string, versions []uint) bool { + if proto, ok := p.running[protocol]; ok { + for _, ver := range versions { + if proto.Version == ver { + return true + } + } + } + return false +} + // RemoteAddr returns the remote address of the network connection. func (p *Peer) RemoteAddr() net.Addr { return p.rw.fd.RemoteAddr() diff --git a/p2p/server.go b/p2p/server.go index dd52297f8a..f70ebf7216 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -35,7 +35,6 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/p2p/nat" @@ -105,7 +104,7 @@ type Config struct { // BootstrapNodesV5 are used to establish connectivity // with the rest of the network using the V5 discovery // protocol. - BootstrapNodesV5 []*discv5.Node `toml:",omitempty"` + BootstrapNodesV5 []*enode.Node `toml:",omitempty"` // Static nodes are used as pre-configured connections which are always // maintained and re-connected on disconnects. @@ -182,7 +181,7 @@ type Server struct { nodedb *enode.DB localnode *enode.LocalNode ntab *discover.UDPv4 - DiscV5 *discv5.Network + DiscV5 *discover.UDPv5 discmix *enode.FairMix dialsched *dialScheduler @@ -413,7 +412,7 @@ type sharedUDPConn struct { unhandled chan discover.ReadPacket } -// ReadFromUDP implements discv5.conn +// ReadFromUDP implements discover.UDPConn func (s *sharedUDPConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { packet, ok := <-s.unhandled if !ok { @@ -427,7 +426,7 @@ func (s *sharedUDPConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err err return l, packet.Addr, nil } -// Close implements discv5.conn +// Close implements discover.UDPConn func (s *sharedUDPConn) Close() error { return nil } @@ -586,7 +585,7 @@ func (srv *Server) setupDiscovery() error { Unhandled: unhandled, Log: srv.log, } - ntab, err := discover.ListenUDP(conn, srv.localnode, cfg) + ntab, err := discover.ListenV4(conn, srv.localnode, cfg) if err != nil { return err } @@ -596,20 +595,21 @@ func (srv *Server) setupDiscovery() error { // Discovery V5 if srv.DiscoveryV5 { - var ntab *discv5.Network + cfg := discover.Config{ + PrivateKey: srv.PrivateKey, + NetRestrict: srv.NetRestrict, + Bootnodes: srv.BootstrapNodesV5, + Log: srv.log, + } var err error if sconn != nil { - ntab, err = discv5.ListenUDP(srv.PrivateKey, sconn, "", srv.NetRestrict) + srv.DiscV5, err = discover.ListenV5(sconn, srv.localnode, cfg) } else { - ntab, err = discv5.ListenUDP(srv.PrivateKey, conn, "", srv.NetRestrict) + srv.DiscV5, err = discover.ListenV5(conn, srv.localnode, cfg) } if err != nil { return err } - if err := ntab.SetFallbackNodes(srv.BootstrapNodesV5); err != nil { - return err - } - srv.DiscV5 = ntab } return nil } @@ -854,13 +854,18 @@ func (srv *Server) listenLoop() { <-slots var ( - fd net.Conn - err error + fd net.Conn + err error + lastLog time.Time ) for { fd, err = srv.listener.Accept() if netutil.IsTemporaryError(err) { - srv.log.Debug("Temporary read error", "err", err) + if time.Since(lastLog) > 1*time.Second { + srv.log.Debug("Temporary read error", "err", err) + lastLog = time.Now() + } + time.Sleep(time.Millisecond * 200) continue } else if err != nil { srv.log.Debug("Read error", "err", err) @@ -871,8 +876,8 @@ func (srv *Server) listenLoop() { } remoteIP := netutil.AddrIP(fd.RemoteAddr()) - if err := srv.checkInboundConn(fd, remoteIP); err != nil { - srv.log.Debug("Rejected inbound connnection", "addr", fd.RemoteAddr(), "err", err) + if err := srv.checkInboundConn(remoteIP); err != nil { + srv.log.Debug("Rejected inbound connection", "addr", fd.RemoteAddr(), "err", err) fd.Close() slots <- struct{}{} continue @@ -892,7 +897,7 @@ func (srv *Server) listenLoop() { } } -func (srv *Server) checkInboundConn(fd net.Conn, remoteIP net.IP) error { +func (srv *Server) checkInboundConn(remoteIP net.IP) error { if remoteIP == nil { return nil } diff --git a/p2p/simulations/adapters/exec.go b/p2p/simulations/adapters/exec.go index 0ed3deab38..35ccdfb068 100644 --- a/p2p/simulations/adapters/exec.go +++ b/p2p/simulations/adapters/exec.go @@ -115,7 +115,6 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) { conf.Stack.P2P.EnableMsgEvents = config.EnableMsgEvents conf.Stack.P2P.NoDiscovery = true conf.Stack.P2P.NAT = nil - conf.Stack.NoUSB = true // Listen on a localhost port, which we set when we // initialise NodeConfig (usually a random port) diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index 4fc7abc06a..1cb26a8ea0 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/p2p/simulations/adapters/inproc.go @@ -100,7 +100,6 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { EnableMsgEvents: config.EnableMsgEvents, }, ExternalSigner: config.ExternalSigner, - NoUSB: true, Logger: log.New("node.id", id.String()), }) if err != nil { diff --git a/p2p/simulations/network.go b/p2p/simulations/network.go index a54db4ea68..9b5e2c37f5 100644 --- a/p2p/simulations/network.go +++ b/p2p/simulations/network.go @@ -454,9 +454,8 @@ func (net *Network) getNodeIDs(excludeIDs []enode.ID) []enode.ID { if len(excludeIDs) > 0 { // Return the difference of nodeIDs and excludeIDs return filterIDs(nodeIDs, excludeIDs) - } else { - return nodeIDs } + return nodeIDs } // GetNodes returns the existing nodes. @@ -472,9 +471,8 @@ func (net *Network) getNodes(excludeIDs []enode.ID) []*Node { if len(excludeIDs) > 0 { nodeIDs := net.getNodeIDs(excludeIDs) return net.getNodesByID(nodeIDs) - } else { - return net.Nodes } + return net.Nodes } // GetNodesByID returns existing nodes with the given enode.IDs. @@ -1098,7 +1096,6 @@ func (net *Network) executeNodeEvent(e *Event) error { func (net *Network) executeConnEvent(e *Event) error { if e.Conn.Up { return net.Connect(e.Conn.One, e.Conn.Other) - } else { - return net.Disconnect(e.Conn.One, e.Conn.Other) } + return net.Disconnect(e.Conn.One, e.Conn.Other) } diff --git a/params/alloc.go b/params/alloc.go index 5d6d5692b3..0db60faf5c 100644 --- a/params/alloc.go +++ b/params/alloc.go @@ -21,4 +21,4 @@ const MainnetAllocData = "\xfa\x04]X\u0793\r\x83b\x011\x8e\u0189\x9agT\x06\x908' 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 RinkebyAllocData = "\xf9\x03\xb7\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x941\xb9\x8d\x14\x00{\xde\xe67)\x80\x86\x98\x8a\v\xbd1\x18E#\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" const GoerliAllocData = "\xf9\x04\x06\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xe0\x94L*\xe4\x82Y5\x05\xf0\x16<\xde\xfc\a>\x81\xc6<\xdaA\a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xa8\xe8\xf1G2e\x8eKQ\xe8q\x191\x05:\x8ai\xba\xf2\xb1\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe1\x94\u0665\x17\x9f\t\x1d\x85\x05\x1d<\x98'\x85\xef\xd1E\\\uc199\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe1\x94\u08bdBX\xd2v\x887\xba\xa2j(\xfeq\xdc\a\x9f\x84\u01cbJG\xe3\xc1$H\xf4\xad\x00\x00\x00" -const YoloV2AllocData = "\xf9\x03\xb7\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x94\x8a7\x86o\xd3b|\x92\x05\xa3|\x86\x85fo2\xec\a\xbb\x1b\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +const YoloV3AllocData = "\xf9\x05o\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x94\x0e\x89\xe2\xae\xdb\x1c\xfc\u06d4$\xd4\x1a\x1f!\x8fA2s\x81r\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x10A\xaf\xbc\xb3Y\u0568\xdcX\xc1[/\xf5\x13T\xff\x8a!}\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94`\xad\xc0\xf8\x9aA\xaf#|\xe75T\xed\xe1p\xd73\xec\x14\xe0\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94y\x9d2\x9e_X4\x19\x16|\xd7\"\x96$\x85\x92n3\x8fJ\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94|\xf5\xb7\x9b\xfe)\x1ag\xab\x02\xb3\x93\xe4V\xcc\xc4\xc2f\xf7S\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x8a\x8e\xaf\xb1\xcfb\xbf\xbe\xb1t\x17i\xda\xe1\xa9\xddG\x99a\x92\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x8b\xa1\xf1\tU\x1b\xd42\x800\x12dZ\xc16\xdd\xd6M\xbar\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\xb0*.\xda\x1b1\u007f\xbd\x16v\x01(\x83k\n\u015bV\x0e\x9d\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\xdf\n\x88\xb2\xb6\x8cg7\x13\xa8\xec\x82`\x03go'.5s\xa0\x02\x00\x00\x00\x00\x00\x00\x00\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/params/bootnodes.go b/params/bootnodes.go index 8e787ae909..eb930a3f5b 100644 --- a/params/bootnodes.go +++ b/params/bootnodes.go @@ -59,12 +59,31 @@ var GoerliBootnodes = []string{ "enode://807b37ee4816ecf407e9112224494b74dd5933625f655962d892f2f0f02d7fbbb3e2a94cf87a96609526f30c998fd71e93e2f53015c558ffc8b03eceaf30ee33@51.15.119.157:30303", } -// YoloV2Bootnodes are the enode URLs of the P2P bootstrap nodes running on the -// YOLOv2 ephemeral test network. -var YoloV2Bootnodes = []string{ +// YoloV3Bootnodes are the enode URLs of the P2P bootstrap nodes running on the +// YOLOv3 ephemeral test network. +// TODO: Set Yolov3 bootnodes +var YoloV3Bootnodes = []string{ "enode://9e1096aa59862a6f164994cb5cb16f5124d6c992cdbf4535ff7dea43ea1512afe5448dca9df1b7ab0726129603f1a3336b631e4d7a1a44c94daddd03241587f9@3.9.20.133:30303", } +var V5Bootnodes = []string{ + // Teku team's bootnode + "enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2Gxb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNlY3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA", + "enr:-KG4QDyytgmE4f7AnvW-ZaUOIi9i79qX4JwjRAiXBZCU65wOfBu-3Nb5I7b_Rmg3KCOcZM_C3y5pg7EBU5XGrcLTduQEhGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQ2_DUbiXNlY3AyNTZrMaEDKnz_-ps3UUOfHWVYaskI5kWYO_vtYMGYCQRAR3gHDouDdGNwgiMog3VkcIIjKA", + // Prylab team's bootnodes + "enr:-Ku4QImhMc1z8yCiNJ1TyUxdcfNucje3BGwEHzodEZUan8PherEo4sF7pPHPSIB1NNuSg5fZy7qFsjmUKs2ea1Whi0EBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQOVphkDqal4QzPMksc5wnpuC3gvSC8AfbFOnZY_On34wIN1ZHCCIyg", + "enr:-Ku4QP2xDnEtUXIjzJ_DhlCRN9SN99RYQPJL92TMlSv7U5C1YnYLjwOQHgZIUXw6c-BvRg2Yc2QsZxxoS_pPRVe0yK8Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMeFF5GrS7UZpAH2Ly84aLK-TyvH-dRo0JM1i8yygH50YN1ZHCCJxA", + "enr:-Ku4QPp9z1W4tAO8Ber_NQierYaOStqhDqQdOPY3bB3jDgkjcbk6YrEnVYIiCBbTxuar3CzS528d2iE7TdJsrL-dEKoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMw5fqqkw2hHC4F5HZZDPsNmPdB1Gi8JPQK7pRc9XHh-oN1ZHCCKvg", + // Lighthouse team's bootnodes + "enr:-IS4QLkKqDMy_ExrpOEWa59NiClemOnor-krjp4qoeZwIw2QduPC-q7Kz4u1IOWf3DDbdxqQIgC4fejavBOuUPy-HE4BgmlkgnY0gmlwhCLzAHqJc2VjcDI1NmsxoQLQSJfEAHZApkm5edTCZ_4qps_1k_ub2CxHFxi-gr2JMIN1ZHCCIyg", + "enr:-IS4QDAyibHCzYZmIYZCjXwU9BqpotWmv2BsFlIq1V31BwDDMJPFEbox1ijT5c2Ou3kvieOKejxuaCqIcjxBjJ_3j_cBgmlkgnY0gmlwhAMaHiCJc2VjcDI1NmsxoQJIdpj_foZ02MXz4It8xKD7yUHTBx7lVFn3oeRP21KRV4N1ZHCCIyg", + // EF bootnodes + "enr:-Ku4QHqVeJ8PPICcWk1vSn_XcSkjOkNiTg6Fmii5j6vUQgvzMc9L1goFnLKgXqBJspJjIsB91LTOleFmyWWrFVATGngBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhAMRHkWJc2VjcDI1NmsxoQKLVXFOhp2uX6jeT0DvvDpPcU8FWMjQdR4wMuORMhpX24N1ZHCCIyg", + "enr:-Ku4QG-2_Md3sZIAUebGYT6g0SMskIml77l6yR-M_JXc-UdNHCmHQeOiMLbylPejyJsdAPsTHJyjJB2sYGDLe0dn8uYBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhBLY-NyJc2VjcDI1NmsxoQORcM6e19T1T9gi7jxEZjk_sjVLGFscUNqAY9obgZaxbIN1ZHCCIyg", + "enr:-Ku4QPn5eVhcoF1opaFEvg1b6JNFD2rqVkHQ8HApOKK61OIcIXD127bKWgAtbwI7pnxx6cDyk_nI88TrZKQaGMZj0q0Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDayLMaJc2VjcDI1NmsxoQK2sBOLGcUb4AwuYzFuAVCaNHA-dy24UuEKkeFNgCVCsIN1ZHCCIyg", + "enr:-Ku4QEWzdnVtXc2Q0ZVigfCGggOVB2Vc1ZCPEc6j21NIFLODSJbvNaef1g4PxhPwl_3kax86YPheFUSLXPRs98vvYsoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDZBrP2Jc2VjcDI1NmsxoQM6jr8Rb1ktLEsVcKAPa08wCsKUmvoQ8khiOl_SLozf9IN1ZHCCIyg", +} + const dnsPrefix = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@" // KnownDNSNetwork returns the address of a public DNS-based node list for the given diff --git a/params/config.go b/params/config.go index 51fa97c6fd..7f90e8e283 100644 --- a/params/config.go +++ b/params/config.go @@ -30,8 +30,7 @@ var ( RopstenGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177") GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a") - // TODO: update with yolov2 values - YoloV2GenesisHash = common.HexToHash("0xc3fd235071f24f93865b0850bd2a2119b30f7224d18a0e34c7bbf549ad7e3d36") + YoloV3GenesisHash = common.HexToHash("0x9e6d8e35cba2e476d30ecde41c102b042312aec1880dbcf5c137931a894161d8") ) // TrustedCheckpoints associates each known checkpoint with the genesis hash of @@ -55,30 +54,29 @@ var CheckpointOracles = map[common.Hash]*ctypes.CheckpointOracleConfig{ var ( // MainnetChainConfig is the chain parameters to run a node on the main network. MainnetChainConfig = &goethereum.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(1150000), - DAOForkBlock: big.NewInt(1920000), - DAOForkSupport: true, - EIP150Block: big.NewInt(2463000), - EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), - EIP155Block: big.NewInt(2675000), - EIP158Block: big.NewInt(2675000), - ByzantiumBlock: big.NewInt(4370000), - ConstantinopleBlock: big.NewInt(7280000), - PetersburgBlock: big.NewInt(7280000), - IstanbulBlock: big.NewInt(9069000), - MuirGlacierBlock: big.NewInt(9200000), - Ethash: new(ctypes.EthashConfig), - TrustedCheckpoint: MainnetTrustedCheckpoint, - TrustedCheckpointOracle: MainnetCheckpointOracle, + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(1_150_000), + DAOForkBlock: big.NewInt(1_920_000), + DAOForkSupport: true, + EIP150Block: big.NewInt(2_463_000), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(2_675_000), + EIP158Block: big.NewInt(2_675_000), + ByzantiumBlock: big.NewInt(4_370_000), + ConstantinopleBlock: big.NewInt(7_280_000), + PetersburgBlock: big.NewInt(7_280_000), + IstanbulBlock: big.NewInt(9_069_000), + MuirGlacierBlock: big.NewInt(9_200_000), + BerlinBlock: big.NewInt(12_244_000), + Ethash: new(ctypes.EthashConfig), } // MainnetTrustedCheckpoint contains the light client trusted checkpoint for the main network. MainnetTrustedCheckpoint = &ctypes.TrustedCheckpoint{ - SectionIndex: 336, - SectionHead: common.HexToHash("0xd42b78902b6527a80337bf1bc372a3ccc3db97e9cc7cf421ca047ae9076c716b"), - CHTRoot: common.HexToHash("0xd97f3b30f7e0cb958e4c67c53ec27745e5a165e33e56821b86523dfee62b783a"), - BloomRoot: common.HexToHash("0xf3cbfd070fababfe2adc9b23fc02c731f6ca2cce6646b3ede4ef2db06092ccce"), + SectionIndex: 371, + SectionHead: common.HexToHash("0x50fd3cec5376ede90ef9129772022690cd1467f22c18abb7faa11e793c51e9c9"), + CHTRoot: common.HexToHash("0xb57b4b22a77b5930847b1ca9f62daa11eae6578948cb7b18997f2c0fe5757025"), + BloomRoot: common.HexToHash("0xa338f8a868a194fa90327d0f5877f656a9f3640c618d2a01a01f2e76ef9ef954"), } // MainnetCheckpointOracle contains a set of configs for the main network oracle. @@ -104,20 +102,21 @@ var ( EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"), EIP155Block: big.NewInt(10), EIP158Block: big.NewInt(10), - ByzantiumBlock: big.NewInt(1700000), - ConstantinopleBlock: big.NewInt(4230000), - PetersburgBlock: big.NewInt(4939394), - IstanbulBlock: big.NewInt(6485846), - MuirGlacierBlock: big.NewInt(7117117), + ByzantiumBlock: big.NewInt(1_700_000), + ConstantinopleBlock: big.NewInt(4_230_000), + PetersburgBlock: big.NewInt(4_939_394), + IstanbulBlock: big.NewInt(6_485_846), + MuirGlacierBlock: big.NewInt(7_117_117), + BerlinBlock: big.NewInt(9_812_189), Ethash: new(ctypes.EthashConfig), } // RopstenTrustedCheckpoint contains the light client trusted checkpoint for the Ropsten test network. RopstenTrustedCheckpoint = &ctypes.TrustedCheckpoint{ - SectionIndex: 269, - SectionHead: common.HexToHash("0x290a9eb65e65c64601d1b05522533ed502098a246736b348502a170818a33d64"), - CHTRoot: common.HexToHash("0x530ebac02264227277d0a16b0819ef96a2011a6e1e66523ebff8040f4a3437ca"), - BloomRoot: common.HexToHash("0x480cd5b3198a0767022902130546854a2e8867cce573c1cf0ce54e67a7bf5efb"), + SectionIndex: 279, + SectionHead: common.HexToHash("0x4a4912848d4c06090097073357c10015d11c6f4544a0f93cbdd584701c3b7d58"), + CHTRoot: common.HexToHash("0x9053b7867ae921e80a4e2f5a4b15212e4af3d691ca712fb33dc150e9c6ea221c"), + BloomRoot: common.HexToHash("0x3dc04cb1be7ddc271f3f83469b47b76184a79d7209ef51d85b1539ea6d25a645"), } // RopstenCheckpointOracle contains a set of configs for the Ropsten test network oracle. @@ -143,11 +142,12 @@ var ( EIP150Hash: common.HexToHash("0x9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9"), EIP155Block: big.NewInt(3), EIP158Block: big.NewInt(3), - ByzantiumBlock: big.NewInt(1035301), - ConstantinopleBlock: big.NewInt(3660663), - PetersburgBlock: big.NewInt(4321234), - IstanbulBlock: big.NewInt(5435345), + ByzantiumBlock: big.NewInt(1_035_301), + ConstantinopleBlock: big.NewInt(3_660_663), + PetersburgBlock: big.NewInt(4_321_234), + IstanbulBlock: big.NewInt(5_435_345), MuirGlacierBlock: nil, + BerlinBlock: big.NewInt(8_290_928), TrustedCheckpoint: RinkebyTrustedCheckpoint, TrustedCheckpointOracle: RinkebyCheckpointOracle, Clique: &ctypes.CliqueConfig{ @@ -158,10 +158,10 @@ var ( // RinkebyTrustedCheckpoint contains the light client trusted checkpoint for the Rinkeby test network. RinkebyTrustedCheckpoint = &ctypes.TrustedCheckpoint{ - SectionIndex: 223, - SectionHead: common.HexToHash("0x03ca0d5e3a931c77cd7a97bbaa2d9e4edc4549c621dc1d223a29f10c86a4a16a"), - CHTRoot: common.HexToHash("0x6573dbdd91b2958b446bd04d67c23e5f14b2510ac96e8df1b6a894dc49e37c6c"), - BloomRoot: common.HexToHash("0x28a35042a4e88efbac55fe566faf7fce000dc436f17fd4cb4b081c9cd793e1a7"), + SectionIndex: 254, + SectionHead: common.HexToHash("0x0cba01dd71baa22ac8fa0b105bc908e94f9ecfbc79b4eb97427fe07b5851dd10"), + CHTRoot: common.HexToHash("0x5673d8fc49c9c7d8729068640e4b392d46952a5a38798973bac1cf1d0d27ad7d"), + BloomRoot: common.HexToHash("0x70e01232b66df9a7778ae3291c9217afb9a2d9f799f32d7b912bd37e7bce83a8"), } // RinkebyCheckpointOracle contains a set of configs for the Rinkeby test network oracle. @@ -188,8 +188,9 @@ var ( ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(1561651), + IstanbulBlock: big.NewInt(1_561_651), MuirGlacierBlock: nil, + BerlinBlock: big.NewInt(4_460_644), TrustedCheckpoint: GoerliTrustedCheckpoint, TrustedCheckpointOracle: GoerliCheckpointOracle, Clique: &ctypes.CliqueConfig{ @@ -200,10 +201,10 @@ var ( // GoerliTrustedCheckpoint contains the light client trusted checkpoint for the Görli test network. GoerliTrustedCheckpoint = &ctypes.TrustedCheckpoint{ - SectionIndex: 107, - SectionHead: common.HexToHash("0xff3ae39199fa191894de419e7f673c8627aa8cc7af924b90f36635b6add375f2"), - CHTRoot: common.HexToHash("0x27d59d60c652425b6b593a882f55a4ff57f24e470a810a6e3c8ba71833a20220"), - BloomRoot: common.HexToHash("0x3c14066d8bb3733780c06b8165768dbb9dd23b75f56012fe5f2fb3c2fb70cadb"), + SectionIndex: 138, + SectionHead: common.HexToHash("0xb7ea0566abd7d0def5b3c9afa3431debb7bb30b65af35f106ca93a59e6c859a7"), + CHTRoot: common.HexToHash("0x378c7ea9081242beb982e2e39567ba12f2ed3e59e5aba3f9db1d595646d7c9f4"), + BloomRoot: common.HexToHash("0x523c169286cfca52e8a6579d8c35dc8bf093412d8a7478163bfa81ae91c2492d"), } // GoerliCheckpointOracle contains a set of configs for the Goerli test network oracle. @@ -219,9 +220,9 @@ var ( Threshold: 2, } - // YoloV2ChainConfig contains the chain parameters to run a node on the YOLOv2 test network. - YoloV2ChainConfig = &goethereum.ChainConfig{ - ChainID: big.NewInt(133519467574834), + // YoloV3ChainConfig contains the chain parameters to run a node on the YOLOv3 test network. + YoloV3ChainConfig = &goethereum.ChainConfig{ + ChainID: new(big.Int).SetBytes([]byte("yolov3x")), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: true, @@ -233,7 +234,8 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: nil, - YoloV2Block: big.NewInt(0), + BerlinBlock: nil, // Don't enable Berlin directly, we're YOLOing it + YoloV3Block: big.NewInt(0), Clique: &ctypes.CliqueConfig{ Period: 15, Epoch: 30000, @@ -246,7 +248,7 @@ var ( // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. AllEthashProtocolChanges = &goethereum.ChainConfig{ - ChainID: big.NewInt(1137), + ChainID: big.NewInt(1337), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: false, @@ -258,8 +260,9 @@ var ( ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: nil, - YoloV2Block: nil, + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + YoloV3Block: big.NewInt(0), EWASMBlock: nil, Ethash: new(ctypes.EthashConfig), Clique: nil, @@ -268,62 +271,63 @@ var ( } /* - https://github.com/ethereum/go-ethereum/blob/master/params/config.go#L242 - - AllEthashProtocolChanges = &ChainConfig{ - big.NewInt(1337), - big.NewInt(0), - nil, - false, - big.NewInt(0), - common.Hash{}, - big.NewInt(0), - big.NewInt(0), - big.NewInt(0), - big.NewInt(0), - big.NewInt(0), - big.NewInt(0), - nil, - nil, - nil, - new(EthashConfig), - nil + https://github.com/ethereum/go-ethereum/blob/master/params/config.go#L242 + + AllEthashProtocolChanges = &ChainConfig{ + big.NewInt(1337), + big.NewInt(0), + nil, + false, + big.NewInt(0), + common.Hash{}, + big.NewInt(0), + big.NewInt(0), + big.NewInt(0), + big.NewInt(0), + big.NewInt(0), + big.NewInt(0), + nil, + nil, + nil, + new(EthashConfig), + nil + } + + + // ChainConfig is the core config which determines the blockchain settings. + // + // ChainConfig is stored in the database on a per block basis. This means + // that any network, identified by its genesis block, can have its own + // set of configuration options. + type ChainConfig struct { + ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection + + 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,omitempty"` // EIP150 HF block (nil = no fork) + EIP150Hash common.Hash `json:"eip150Hash,omitempty"` // EIP150 HF hash (needed for header only clients as only gas pricing changed) + + EIP155Block *big.Int `json:"eip155Block,omitempty"` // EIP155 HF block + EIP158Block *big.Int `json:"eip158Block,omitempty"` // EIP158 HF block + + ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium) + ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated) + PetersburgBlock *big.Int `json:"petersburgBlock,omitempty"` // Petersburg switch block (nil = same as Constantinople) + IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul) + MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated) + BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin) + + YoloV2Block *big.Int `json:"yoloV2Block,omitempty"` // YOLO v1: https://github.com/ethereum/EIPs/pull/2657 (Ephemeral testnet) + EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated) + + // Various consensus engines + Ethash *EthashConfig `json:"ethash,omitempty"` + Clique *CliqueConfig `json:"clique,omitempty"` } - - - // ChainConfig is the core config which determines the blockchain settings. - // - // ChainConfig is stored in the database on a per block basis. This means - // that any network, identified by its genesis block, can have its own - // set of configuration options. - type ChainConfig struct { - ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection - - 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,omitempty"` // EIP150 HF block (nil = no fork) - EIP150Hash common.Hash `json:"eip150Hash,omitempty"` // EIP150 HF hash (needed for header only clients as only gas pricing changed) - - EIP155Block *big.Int `json:"eip155Block,omitempty"` // EIP155 HF block - EIP158Block *big.Int `json:"eip158Block,omitempty"` // EIP158 HF block - - ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium) - ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated) - PetersburgBlock *big.Int `json:"petersburgBlock,omitempty"` // Petersburg switch block (nil = same as Constantinople) - IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul) - MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated) - - YoloV2Block *big.Int `json:"yoloV2Block,omitempty"` // YOLO v1: https://github.com/ethereum/EIPs/pull/2657 (Ephemeral testnet) - EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated) - - // Various consensus engines - Ethash *EthashConfig `json:"ethash,omitempty"` - Clique *CliqueConfig `json:"clique,omitempty"` - } */ // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced @@ -368,6 +372,7 @@ var ( ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), EWASMBlock: nil, Ethash: new(ctypes.EthashConfig), Clique: nil, diff --git a/params/config_classic.go b/params/config_classic.go index db09b2c595..8ef6253cc2 100644 --- a/params/config_classic.go +++ b/params/config_classic.go @@ -60,6 +60,8 @@ var ( EIP145FBlock: big.NewInt(9573000), EIP1014FBlock: big.NewInt(9573000), EIP1052FBlock: big.NewInt(9573000), + // EIP1283FBlock: big.NewInt(9573000), + // PetersburgBlock: big.NewInt(9573000), // Istanbul eq, aka Phoenix // ECIP-1088 diff --git a/params/config_test.go b/params/config_test.go index 717cee8979..277aff5681 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -21,11 +21,18 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params/confp" "github.com/ethereum/go-ethereum/params/types/coregeth" "github.com/ethereum/go-ethereum/params/types/ctypes" + "github.com/ethereum/go-ethereum/params/types/genesisT" "github.com/ethereum/go-ethereum/params/types/goethereum" "github.com/ethereum/go-ethereum/params/types/multigeth" + "github.com/ethereum/go-ethereum/params/vars" + "github.com/ethereum/go-ethereum/trie" ) func uint64P(n uint64) *uint64 { @@ -345,3 +352,70 @@ func TestFoundationIsEIP779(t *testing.T) { } } } + +// genesisToBlock is a helper function duplicating the logic at core.MustCommitGenesis, +// which cannot be imported into this package. +func genesisToBlock(g *genesisT.Genesis, db ethdb.Database) *types.Block { + if db == nil { + db = rawdb.NewMemoryDatabase() + } + statedb, _ := state.New(common.Hash{}, state.NewDatabase(db), nil) + 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) + } + } + root := statedb.IntermediateRoot(false) + head := &types.Header{ + Number: new(big.Int).SetUint64(g.Number), + Nonce: types.EncodeNonce(g.Nonce), + Time: g.Timestamp, + ParentHash: g.ParentHash, + Extra: g.ExtraData, + GasLimit: g.GasLimit, + GasUsed: g.GasUsed, + Difficulty: g.Difficulty, + MixDigest: g.Mixhash, + Coinbase: g.Coinbase, + Root: root, + } + if g.GasLimit == 0 { + head.GasLimit = vars.GenesisGasLimit + } + if g.Difficulty == nil { + head.Difficulty = vars.GenesisDifficulty + } + statedb.Commit(false) + statedb.Database().TrieDB().Commit(root, true, nil) + + return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)) +} + +func TestGenesisHashes(t *testing.T) { + cases := []struct { + genesis *genesisT.Genesis + hash common.Hash + }{ + { + genesis: DefaultClassicGenesisBlock(), + hash: MainnetGenesisHash, + }, + { + genesis: DefaultGoerliGenesisBlock(), + hash: GoerliGenesisHash, + }, + { + genesis: DefaultYoloV3GenesisBlock(), + hash: YoloV3GenesisHash, + }, + } + for i, c := range cases { + b := genesisToBlock(c.genesis, rawdb.NewMemoryDatabase()) + if got := b.Hash(); got != c.hash { + t.Errorf("case: %d, want: %s, got: %s", i, c.hash.Hex(), got.Hex()) + } + } +} diff --git a/params/coregeth.json.d/berlin_test.json b/params/coregeth.json.d/berlin_test.json new file mode 100755 index 0000000000..c186b56998 --- /dev/null +++ b/params/coregeth.json.d/berlin_test.json @@ -0,0 +1,81 @@ +{ + "config": { + "networkId": 1, + "chainId": 1, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip160Block": 0, + "eip161FBlock": 0, + "eip170FBlock": 0, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "eip145FBlock": 0, + "eip1014FBlock": 0, + "eip1052FBlock": 0, + "eip1283FBlock": 0, + "petersburgBlock": 0, + "eip152FBlock": 0, + "eip1108FBlock": 0, + "eip1344FBlock": 0, + "eip1884FBlock": 0, + "eip2028FBlock": 0, + "eip2200FBlock": 0, + "eip2565FBlock": 0, + "eip2718FBlock": 0, + "eip2929FBlock": 0, + "eip2930FBlock": 0, + "ethash": {}, + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "blockReward": { + "0x0": "0x1bc16d674ec80000" + }, + "requireBlockHashes": null + }, + "nonce": "0x0", + "timestamp": "0x3e8", + "extraData": "0x", + "gasLimit": "0x5f5e100", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "alloc": { + "000000000000000000000000000000000000c0de": { + "code": "0x6101206000f300", + "balance": "0xba1a9ce0ba1a9ce" + }, + "000000000000000000000000000000000f30c0de": { + "code": "0x6000600055600035602035f300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0xba1a9ce0ba1a9ce" + }, + "000000000000000000000000000000000ff0c0de": { + "code": "0x600035602035fd00", + "balance": "0xba1a9ce0ba1a9ce" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xba1a9ce0ba1a9ce" + }, + "cccccccccccccccccccccccccccccccccccccccc": { + "code": "0x6000602435146100115760005061001f565b600a61202052610100612040525b6001602435146100315760005061003f565b600061202052610100612040525b60026024351461005157600050610062565b600a60000361202052610100612040525b60036024351461007457600050610083565b61100061202052610100612040525b600a60243514610095576000506100a2565b600a612020526000612040525b600b602435146100b4576000506100c1565b6000612020526000612040525b600c602435146100d3576000506100e3565b600a600003612020526000612040525b600d602435146100f557600050610103565b611000612020526000612040525b60146024351461011557600050610125565b600561202052600a600003612040525b60156024351461013757600050610147565b600561202052637fffffff612040525b60166024351461015957600050610169565b6005612020526380000000612040525b60176024351461017b5760005061018b565b60056120205263ffffffff612040525b60186024351461019d576000506101ae565b600561202052640100000000612040525b6019602435146101c0576000506101d4565b600561202052677fffffffffffffff612040525b601a602435146101e6576000506101fa565b600561202052678000000000000000612040525b601b6024351461020c57600050610220565b60056120205267ffffffffffffffff612040525b601c6024351461023257600050610247565b60056120205268010000000000000000612040525b602060043514610258576000610262565b6120205161204051205b5060376004351461027557600050610281565b61202051600061204051375b6039600435146102935760005061029f565b61202051600061204051395b603c600435146102b1576000506102c0565b6120205160006120405161c0de3c5b603e600435146102d2576000506102de565b612020516000612040513e5b60a0600435146102f0576000506102fa565b6120205161204051a05b60a16004351461030c57600050610318565b60016120205161204051a15b60a26004351461032a57600050610338565b600260016120205161204051a25b60a36004351461034a5760005061035a565b6003600260016120205161204051a35b60a46004351461036c5760005061037e565b60046003600260016120205161204051a45b60f06004351461038f57600061039b565b61202051612040516000f05b5060f1600435146103ad5760006103c3565b600060006120205161204051600061c0de611000f15b506101f1600435146103d65760006103ec565b612020516120405160006000600061c0de611000f15b5060f2600435146103fe576000610414565b600060006120205161204051600061c0de611000f25b506101f26004351461042757600061043d565b612020516120405160006000600061c0de611000f25b5060f46004351461044f576000610464565b60006000612020516120405161c0de62100000f45b506101f46004351461047757600061048c565b61202051612040516000600061c0de62100000f45b5060f56004351461049e5760006104ad565b615a1761202051612040516000f55b5060fa600435146104bf5760006104d4565b60006000612020516120405161c0de62100000fa5b506101fa600435146104e75760006104fc565b61202051612040516000600061c0de62100000fa5b5061013e6004351461051057600050610530565b61010061010060006000600061c0de611000f150612020516000612040513e5b60f360043514610541576000610557565b6000600060406120206000630f30c0de62100000f15b5060ff6004351461056a57600050610585565b6000600060406120206000630ff0c0de62100000f1503d6000555b60006101005500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000100": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0xba1a9ce0ba1a9ce" + } + }, + "number": "0x1", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/params/coregeth.json.d/byzantium_difficulty_test.json b/params/coregeth.json.d/byzantium_difficulty_test.json new file mode 100755 index 0000000000..d13110aa5c --- /dev/null +++ b/params/coregeth.json.d/byzantium_difficulty_test.json @@ -0,0 +1,20 @@ +{ + "networkId": 1, + "chainId": null, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "ethash": {}, + "difficultyBombDelays": { + "0x0": "0x2dc6c0" + }, + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, + "requireBlockHashes": null +} \ No newline at end of file diff --git a/params/coregeth.json.d/byzantium_test.json b/params/coregeth.json.d/byzantium_test.json new file mode 100755 index 0000000000..4404aa6a89 --- /dev/null +++ b/params/coregeth.json.d/byzantium_test.json @@ -0,0 +1,49 @@ +{ + "config": { + "networkId": 1, + "chainId": 1, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip160Block": 0, + "eip161FBlock": 0, + "eip170FBlock": 0, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "ethash": {}, + "difficultyBombDelays": { + "0x0": "0x2dc6c0" + }, + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, + "requireBlockHashes": null + }, + "nonce": "0x0", + "timestamp": "0x3e8", + "extraData": "0x", + "gasLimit": "0x3d37ef", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x68795c4aa09d6f4ed3e5deddf8c2ad3049a601da", + "alloc": { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xde0b6b3a7640000", + "nonce": "0x1" + }, + "b94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "code": "0x600035600052602035602052604035604052606035606052604060c860406000600060066207a120f260005560c85160015560e851600255", + "balance": "0x0" + } + }, + "number": "0x1", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/params/coregeth.json.d/classic_agharta_difficulty_test.json b/params/coregeth.json.d/classic_agharta_difficulty_test.json new file mode 100755 index 0000000000..c839c1309b --- /dev/null +++ b/params/coregeth.json.d/classic_agharta_difficulty_test.json @@ -0,0 +1,20 @@ +{ + "networkId": 0, + "chainId": null, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "eip145FBlock": 0, + "eip1014FBlock": 0, + "eip1052FBlock": 0, + "eip1283FBlock": 0, + "eip2200FBlock": 0, + "disposalBlock": 0, + "ethash": {}, + "requireBlockHashes": null +} \ No newline at end of file diff --git a/params/coregeth.json.d/classic_atlantis_difficulty_test.json b/params/coregeth.json.d/classic_atlantis_difficulty_test.json new file mode 100755 index 0000000000..6ee63bfd04 --- /dev/null +++ b/params/coregeth.json.d/classic_atlantis_difficulty_test.json @@ -0,0 +1,15 @@ +{ + "networkId": 0, + "chainId": null, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "disposalBlock": 0, + "ethash": {}, + "requireBlockHashes": null +} \ No newline at end of file diff --git a/params/coregeth.json.d/classic_phoenix_difficulty_test.json b/params/coregeth.json.d/classic_phoenix_difficulty_test.json new file mode 100755 index 0000000000..c839c1309b --- /dev/null +++ b/params/coregeth.json.d/classic_phoenix_difficulty_test.json @@ -0,0 +1,20 @@ +{ + "networkId": 0, + "chainId": null, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "eip145FBlock": 0, + "eip1014FBlock": 0, + "eip1052FBlock": 0, + "eip1283FBlock": 0, + "eip2200FBlock": 0, + "disposalBlock": 0, + "ethash": {}, + "requireBlockHashes": null +} \ No newline at end of file diff --git a/params/coregeth.json.d/constantinople_difficulty_test.json b/params/coregeth.json.d/constantinople_difficulty_test.json new file mode 100755 index 0000000000..8855b38dad --- /dev/null +++ b/params/coregeth.json.d/constantinople_difficulty_test.json @@ -0,0 +1,26 @@ +{ + "networkId": 1, + "chainId": null, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "eip145FBlock": 0, + "eip1014FBlock": 0, + "eip1052FBlock": 0, + "eip1283FBlock": 0, + "ethash": {}, + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "blockReward": { + "0x0": "0x1bc16d674ec80000" + }, + "requireBlockHashes": null +} \ No newline at end of file diff --git a/params/coregeth.json.d/constantinople_fix_test.json b/params/coregeth.json.d/constantinople_fix_test.json new file mode 100755 index 0000000000..c2fd5a8ba1 --- /dev/null +++ b/params/coregeth.json.d/constantinople_fix_test.json @@ -0,0 +1,54 @@ +{ + "config": { + "networkId": 1, + "chainId": 1, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip160Block": 0, + "eip161FBlock": 0, + "eip170FBlock": 0, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "eip145FBlock": 0, + "eip1014FBlock": 0, + "eip1052FBlock": 0, + "eip1283FBlock": 0, + "petersburgBlock": 0, + "ethash": {}, + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "blockReward": { + "0x0": "0x1bc16d674ec80000" + }, + "requireBlockHashes": null + }, + "nonce": "0x0", + "timestamp": "0x3e8", + "extraData": "0x", + "gasLimit": "0x3d37ef", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x68795c4aa09d6f4ed3e5deddf8c2ad3049a601da", + "alloc": { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xde0b6b3a7640000", + "nonce": "0x1" + }, + "b94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "code": "0x600035600052602035602052604035604052606035606052604060c860806000600060066207a120f260005560c85160015560e851600255", + "balance": "0x0" + } + }, + "number": "0x1", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/params/coregeth.json.d/constantinople_test.json b/params/coregeth.json.d/constantinople_test.json new file mode 100755 index 0000000000..e0b3c097f7 --- /dev/null +++ b/params/coregeth.json.d/constantinople_test.json @@ -0,0 +1,108 @@ +{ + "config": { + "networkId": 1, + "chainId": 1, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip160Block": 0, + "eip161FBlock": 0, + "eip170FBlock": 0, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "eip145FBlock": 0, + "eip1014FBlock": 0, + "eip1052FBlock": 0, + "eip1283FBlock": 0, + "ethash": {}, + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "blockReward": { + "0x0": "0x1bc16d674ec80000" + }, + "requireBlockHashes": null + }, + "nonce": "0x0", + "timestamp": "0x3e8", + "extraData": "0x", + "gasLimit": "0x5f5e100", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x3535353535353535353535353535353535353535", + "alloc": { + "0000000000000000000000000000000000000000": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000001": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000002": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000003": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000004": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000005": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000006": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000007": { + "balance": "0x1" + }, + "10a1c1cb95c92ec31d3f22c66eef1d9f3f258c6b": { + "balance": "0xde0b6b3a7640000" + }, + "13cbb8d99c6c4e0f2728c7d72606e78a29c4e224": { + "balance": "0xde0b6b3a7640000" + }, + "24143873e0e0815fdcbcffdbe09c979cbf9ad013": { + "balance": "0xde0b6b3a7640000" + }, + "3535353535353535353535353535353535353535": { + "balance": "0x0" + }, + "598443f1880ef585b21f1d7585bd0577402861e5": { + "balance": "0xde0b6b3a7640000" + }, + "77db2bebba79db42a978f896968f4afce746ea1f": { + "balance": "0xde0b6b3a7640000" + }, + "7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { + "balance": "0xde0b6b3a7640000" + }, + "82a978b3f5962a5b0957d9ee9eef472ee55b42f1": { + "balance": "0xde0b6b3a760add2", + "nonce": "0x3" + }, + "90f0b1ebbba1c1936aff7aaf20a7878ff9e04b6c": { + "balance": "0xde0b6b3a7640000" + }, + "c305c901078781c232a2a521c2af7980f8385ee9": { + "code": "0x600035601c52740100000000000000000000000000000000000000006020526fffffffffffffffffffffffffffffffff6040527fffffffffffffffffffffffffffffffff000000000000000000000000000000016060527402540be3fffffffffffffffffffffffffdabf41c006080527ffffffffffffffffffffffffdabf41c00000000000000000000000002540be40060a0526330c8d1da600051141561012b5760c06004356004013511151558576004356004013560200160043560040161014037604061026061014051610160600060066305f5e0fff11558576040610240526102406060806102c0828460006004601bf15050506102c08051602082012090506000556102c060206020820352604081510160206001820306601f820103905060208203f350005b", + "balance": "0x0", + "nonce": "0x1" + }, + "dceceaf3fc5c0a63d195d69b1a90011b7b19650d": { + "balance": "0xde0b6b3a7640000" + }, + "e0fc04fa2d34a66b779fd5cee748268032a146c0": { + "balance": "0xde0b6b3a7640000" + } + }, + "number": "0x1", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/params/coregeth.json.d/custom_mainnetwork_difficulty_test.json b/params/coregeth.json.d/custom_mainnetwork_difficulty_test.json new file mode 100755 index 0000000000..566e6356e7 --- /dev/null +++ b/params/coregeth.json.d/custom_mainnetwork_difficulty_test.json @@ -0,0 +1,30 @@ +{ + "networkId": 1, + "chainId": 1, + "eip2FBlock": 1150000, + "eip7FBlock": 1150000, + "daoForkBlock": 1920000, + "eip150Block": 2463000, + "eip155Block": 2675000, + "eip160Block": 2675000, + "eip161FBlock": 2675000, + "eip170FBlock": 2675000, + "eip100FBlock": 4370000, + "eip140FBlock": 4370000, + "eip198FBlock": 4370000, + "eip211FBlock": 4370000, + "eip212FBlock": 4370000, + "eip213FBlock": 4370000, + "eip214FBlock": 4370000, + "eip658FBlock": 4370000, + "ethash": {}, + "difficultyBombDelays": { + "0x42ae50": "0x2dc6c0" + }, + "blockReward": { + "0x42ae50": "0x29a2241af62c0000" + }, + "requireBlockHashes": { + "2463000": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0" + } +} \ No newline at end of file diff --git a/params/coregeth.json.d/difficulty_json_difficulty_test.json b/params/coregeth.json.d/difficulty_json_difficulty_test.json new file mode 100755 index 0000000000..566e6356e7 --- /dev/null +++ b/params/coregeth.json.d/difficulty_json_difficulty_test.json @@ -0,0 +1,30 @@ +{ + "networkId": 1, + "chainId": 1, + "eip2FBlock": 1150000, + "eip7FBlock": 1150000, + "daoForkBlock": 1920000, + "eip150Block": 2463000, + "eip155Block": 2675000, + "eip160Block": 2675000, + "eip161FBlock": 2675000, + "eip170FBlock": 2675000, + "eip100FBlock": 4370000, + "eip140FBlock": 4370000, + "eip198FBlock": 4370000, + "eip211FBlock": 4370000, + "eip212FBlock": 4370000, + "eip213FBlock": 4370000, + "eip214FBlock": 4370000, + "eip658FBlock": 4370000, + "ethash": {}, + "difficultyBombDelays": { + "0x42ae50": "0x2dc6c0" + }, + "blockReward": { + "0x42ae50": "0x29a2241af62c0000" + }, + "requireBlockHashes": { + "2463000": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0" + } +} \ No newline at end of file diff --git a/params/coregeth.json.d/eip2384_difficulty_test.json b/params/coregeth.json.d/eip2384_difficulty_test.json new file mode 100755 index 0000000000..1b4163dc7b --- /dev/null +++ b/params/coregeth.json.d/eip2384_difficulty_test.json @@ -0,0 +1,33 @@ +{ + "networkId": 1, + "chainId": null, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "eip145FBlock": 0, + "eip1014FBlock": 0, + "eip1052FBlock": 0, + "eip1283FBlock": 0, + "eip152FBlock": 0, + "eip1108FBlock": 0, + "eip1344FBlock": 0, + "eip1884FBlock": 0, + "eip2028FBlock": 0, + "eip2200FBlock": 0, + "eip2384FBlock": 0, + "ethash": {}, + "difficultyBombDelays": { + "0x0": "0x895440" + }, + "blockReward": { + "0x0": "0x1bc16d674ec80000" + }, + "requireBlockHashes": null +} \ No newline at end of file diff --git a/params/coregeth.json.d/eip_150_test.json b/params/coregeth.json.d/eip_150_test.json new file mode 100755 index 0000000000..77e2ba4fa5 --- /dev/null +++ b/params/coregeth.json.d/eip_150_test.json @@ -0,0 +1,30 @@ +{ + "config": { + "networkId": 1, + "chainId": 1, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip150Block": 0, + "ethash": {}, + "requireBlockHashes": null + }, + "nonce": "0x0", + "timestamp": "0x3e8", + "extraData": "0x", + "gasLimit": "0x10000804025c", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "alloc": { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6": { + "code": "0x63ffffffff6000f3", + "balance": "0xde0b6b3a7640000" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x186a0c3b1e19a180" + } + }, + "number": "0x1", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/params/coregeth.json.d/eip_158_test.json b/params/coregeth.json.d/eip_158_test.json new file mode 100755 index 0000000000..c7161378e6 --- /dev/null +++ b/params/coregeth.json.d/eip_158_test.json @@ -0,0 +1,38 @@ +{ + "config": { + "networkId": 1, + "chainId": 1, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip160Block": 0, + "eip161FBlock": 0, + "eip170FBlock": 0, + "ethash": {}, + "requireBlockHashes": null + }, + "nonce": "0x0", + "timestamp": "0x3e8", + "extraData": "0x", + "gasLimit": "0xf4240", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "alloc": { + "1000000000000000000000000000000000000000": { + "code": "0x7f716860016000553360145560005260096017f36000526012600e6001f0600a556000527f6040600060406000600073ffe4ebd2a68c02d9dcb0a17283d13346beb2d8b6626020527f0186a0f2600b5500000000000000000000000000000000000000000000000000604052606060006001f0", + "balance": "0x2710" + }, + "1000000000000000000000000000000000000001": { + "code": "0x6460016000556000526005601b6001f0", + "balance": "0x3e8" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x2386f26fc10000" + } + }, + "number": "0x1", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/params/coregeth.json.d/etc_agharta_test.json b/params/coregeth.json.d/etc_agharta_test.json new file mode 100755 index 0000000000..546026725b --- /dev/null +++ b/params/coregeth.json.d/etc_agharta_test.json @@ -0,0 +1,106 @@ +{ + "config": { + "networkId": 1, + "chainId": 61, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip160Block": 0, + "eip161FBlock": 0, + "eip170FBlock": 0, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "eip145FBlock": 0, + "eip1014FBlock": 0, + "eip1052FBlock": 0, + "eip1283FBlock": 0, + "petersburgBlock": 0, + "ecip1017FBlock": 5000000, + "ecip1017EraRounds": 5000000, + "disposalBlock": 0, + "ethash": {}, + "requireBlockHashes": null + }, + "nonce": "0x0", + "timestamp": "0x3e8", + "extraData": "0x", + "gasLimit": "0x5f5e100", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x3535353535353535353535353535353535353535", + "alloc": { + "0000000000000000000000000000000000000000": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000001": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000002": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000003": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000004": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000005": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000006": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000007": { + "balance": "0x1" + }, + "10a1c1cb95c92ec31d3f22c66eef1d9f3f258c6b": { + "balance": "0xde0b6b3a7640000" + }, + "13cbb8d99c6c4e0f2728c7d72606e78a29c4e224": { + "balance": "0xde0b6b3a7640000" + }, + "24143873e0e0815fdcbcffdbe09c979cbf9ad013": { + "balance": "0xde0b6b3a7640000" + }, + "3535353535353535353535353535353535353535": { + "balance": "0x4a0ae" + }, + "598443f1880ef585b21f1d7585bd0577402861e5": { + "balance": "0xde0b6b3a7640000" + }, + "77db2bebba79db42a978f896968f4afce746ea1f": { + "balance": "0xde0b6b3a7640000" + }, + "7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { + "balance": "0xde0b6b3a7640000" + }, + "82a978b3f5962a5b0957d9ee9eef472ee55b42f1": { + "balance": "0xde0b6b3a75f5f52", + "nonce": "0x5" + }, + "90f0b1ebbba1c1936aff7aaf20a7878ff9e04b6c": { + "balance": "0xde0b6b3a7640000" + }, + "c305c901078781c232a2a521c2af7980f8385ee9": { + "code": "0x600035601c52740100000000000000000000000000000000000000006020526fffffffffffffffffffffffffffffffff6040527fffffffffffffffffffffffffffffffff000000000000000000000000000000016060527402540be3fffffffffffffffffffffffffdabf41c006080527ffffffffffffffffffffffffdabf41c00000000000000000000000002540be40060a0526330c8d1da600051141561012b5760c06004356004013511151558576004356004013560200160043560040161014037604061026061014051610160600060066305f5e0fff11558576040610240526102406060806102c0828460006004601bf15050506102c08051602082012090506000556102c060206020820352604081510160206001820306601f820103905060208203f350005b", + "balance": "0x0", + "nonce": "0x1" + }, + "dceceaf3fc5c0a63d195d69b1a90011b7b19650d": { + "balance": "0xde0b6b3a7640000" + }, + "e0fc04fa2d34a66b779fd5cee748268032a146c0": { + "balance": "0xde0b6b3a7640000" + } + }, + "number": "0x1", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/params/coregeth.json.d/etc_atlantis_test.json b/params/coregeth.json.d/etc_atlantis_test.json new file mode 100755 index 0000000000..f7e632a7da --- /dev/null +++ b/params/coregeth.json.d/etc_atlantis_test.json @@ -0,0 +1,101 @@ +{ + "config": { + "networkId": 1, + "chainId": 61, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip160Block": 0, + "eip161FBlock": 0, + "eip170FBlock": 0, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "ecip1017FBlock": 5000000, + "ecip1017EraRounds": 5000000, + "disposalBlock": 0, + "ethash": {}, + "requireBlockHashes": null + }, + "nonce": "0x0", + "timestamp": "0x3e8", + "extraData": "0x", + "gasLimit": "0x5f5e100", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x3535353535353535353535353535353535353535", + "alloc": { + "0000000000000000000000000000000000000000": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000001": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000002": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000003": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000004": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000005": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000006": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000007": { + "balance": "0x1" + }, + "10a1c1cb95c92ec31d3f22c66eef1d9f3f258c6b": { + "balance": "0xde0b6b3a7640000" + }, + "13cbb8d99c6c4e0f2728c7d72606e78a29c4e224": { + "balance": "0xde0b6b3a7640000" + }, + "24143873e0e0815fdcbcffdbe09c979cbf9ad013": { + "balance": "0xde0b6b3a7640000" + }, + "3535353535353535353535353535353535353535": { + "balance": "0x4a0ae" + }, + "598443f1880ef585b21f1d7585bd0577402861e5": { + "balance": "0xde0b6b3a7640000" + }, + "77db2bebba79db42a978f896968f4afce746ea1f": { + "balance": "0xde0b6b3a7640000" + }, + "7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { + "balance": "0xde0b6b3a7640000" + }, + "82a978b3f5962a5b0957d9ee9eef472ee55b42f1": { + "balance": "0xde0b6b3a75f5f52", + "nonce": "0x5" + }, + "90f0b1ebbba1c1936aff7aaf20a7878ff9e04b6c": { + "balance": "0xde0b6b3a7640000" + }, + "c305c901078781c232a2a521c2af7980f8385ee9": { + "code": "0x600035601c52740100000000000000000000000000000000000000006020526fffffffffffffffffffffffffffffffff6040527fffffffffffffffffffffffffffffffff000000000000000000000000000000016060527402540be3fffffffffffffffffffffffffdabf41c006080527ffffffffffffffffffffffffdabf41c00000000000000000000000002540be40060a0526330c8d1da600051141561012b5760c06004356004013511151558576004356004013560200160043560040161014037604061026061014051610160600060066305f5e0fff11558576040610240526102406060806102c0828460006004601bf15050506102c08051602082012090506000556102c060206020820352604081510160206001820306601f820103905060208203f350005b", + "balance": "0x0", + "nonce": "0x1" + }, + "dceceaf3fc5c0a63d195d69b1a90011b7b19650d": { + "balance": "0xde0b6b3a7640000" + }, + "e0fc04fa2d34a66b779fd5cee748268032a146c0": { + "balance": "0xde0b6b3a7640000" + } + }, + "number": "0x1", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/params/coregeth.json.d/etc_magneto_test.json b/params/coregeth.json.d/etc_magneto_test.json new file mode 100755 index 0000000000..4f9c91fedf --- /dev/null +++ b/params/coregeth.json.d/etc_magneto_test.json @@ -0,0 +1,1222 @@ +{ + "config": { + "networkId": 1, + "chainId": 61, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip160Block": 0, + "eip161FBlock": 0, + "eip170FBlock": 0, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "eip145FBlock": 0, + "eip1014FBlock": 0, + "eip1052FBlock": 0, + "eip1283FBlock": 0, + "petersburgBlock": 0, + "eip152FBlock": 0, + "eip1108FBlock": 0, + "eip1344FBlock": 0, + "eip1884FBlock": 0, + "eip2028FBlock": 0, + "eip2200FBlock": 0, + "ecip1017FBlock": 5000000, + "ecip1017EraRounds": 5000000, + "eip2565FBlock": 0, + "eip2718FBlock": 0, + "eip2929FBlock": 0, + "eip2930FBlock": 0, + "disposalBlock": 0, + "ethash": {}, + "requireBlockHashes": null + }, + "nonce": "0x0", + "timestamp": "0x3e8", + "extraData": "0x", + "gasLimit": "0x5f5e100", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "alloc": { + "0000000000000000000000000000000000000101": { + "code": "0x600160015560800100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000102": { + "code": "0x6001600155608060800100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000201": { + "code": "0x600160015560800200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000202": { + "code": "0x6001600155608060800200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000301": { + "code": "0x600160015560800300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000302": { + "code": "0x6001600155608060800300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000401": { + "code": "0x600160015560800400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000402": { + "code": "0x6001600155608060800400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000501": { + "code": "0x600160015560800500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000502": { + "code": "0x6001600155608060800500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000601": { + "code": "0x600160015560800600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000602": { + "code": "0x6001600155608060800600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000701": { + "code": "0x600160015560800700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000702": { + "code": "0x6001600155608060800700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000802": { + "code": "0x6001600155608060800800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000803": { + "code": "0x60016001556080608060800800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000902": { + "code": "0x6001600155608060800900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000903": { + "code": "0x60016001556080608060800900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000a01": { + "code": "0x600160015560800a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000a02": { + "code": "0x6001600155608060800a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000b01": { + "code": "0x600160015560800b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000b02": { + "code": "0x6001600155608060800b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001001": { + "code": "0x600160015560801000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001002": { + "code": "0x6001600155608060801000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001101": { + "code": "0x600160015560801100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001102": { + "code": "0x6001600155608060801100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001201": { + "code": "0x600160015560801200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001202": { + "code": "0x6001600155608060801200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001301": { + "code": "0x600160015560801300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001302": { + "code": "0x6001600155608060801300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001401": { + "code": "0x600160015560801400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001402": { + "code": "0x6001600155608060801400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001500": { + "code": "0x60016001551500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001501": { + "code": "0x600160015560801500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001601": { + "code": "0x600160015560801600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001602": { + "code": "0x6001600155608060801600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001701": { + "code": "0x600160015560801700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001702": { + "code": "0x6001600155608060801700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001801": { + "code": "0x600160015560801800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001802": { + "code": "0x6001600155608060801800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001900": { + "code": "0x60016001551900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001901": { + "code": "0x600160015560801900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001a01": { + "code": "0x600160015560801a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001a02": { + "code": "0x6001600155608060801a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001b01": { + "code": "0x600160015560801b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001b02": { + "code": "0x6001600155608060801b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001c01": { + "code": "0x600160015560801c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001c02": { + "code": "0x6001600155608060801c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001d01": { + "code": "0x600160015560801d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001d02": { + "code": "0x6001600155608060801d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000002001": { + "code": "0x600160015560802000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000002002": { + "code": "0x6001600155608060802000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003100": { + "code": "0x60016001553100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003101": { + "code": "0x600160015560803100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003500": { + "code": "0x60016001553500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003501": { + "code": "0x600160015560803500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003702": { + "code": "0x6001600155608060803700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003703": { + "code": "0x60016001556080608060803700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003902": { + "code": "0x6001600155608060803900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003903": { + "code": "0x60016001556080608060803900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003b00": { + "code": "0x60016001553b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003b01": { + "code": "0x600160015560803b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003c03": { + "code": "0x60016001556080608060803c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003c04": { + "code": "0x600160015560806080608060803c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003f00": { + "code": "0x60016001553f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003f01": { + "code": "0x600160015560803f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000004000": { + "code": "0x60016001554000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000004001": { + "code": "0x600160015560804000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005000": { + "code": "0x60016001555000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005001": { + "code": "0x600160015560805000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005100": { + "code": "0x60016001555100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005101": { + "code": "0x600160015560805100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005201": { + "code": "0x600160015560805200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005202": { + "code": "0x6001600155608060805200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005301": { + "code": "0x600160015560805300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005302": { + "code": "0x6001600155608060805300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005400": { + "code": "0x60016001555400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005401": { + "code": "0x600160015560805400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008000": { + "code": "0x60016001558000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008001": { + "code": "0x600160015560808000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008101": { + "code": "0x600160015560808100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008102": { + "code": "0x6001600155608060808100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008202": { + "code": "0x6001600155608060808200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008203": { + "code": "0x60016001556080608060808200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008303": { + "code": "0x60016001556080608060808300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008304": { + "code": "0x600160015560806080608060808300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008404": { + "code": "0x600160015560806080608060808400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008405": { + "code": "0x6001600155608060806080608060808400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008505": { + "code": "0x6001600155608060806080608060808500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008506": { + "code": "0x60016001556080608060806080608060808500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008606": { + "code": "0x60016001556080608060806080608060808600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008607": { + "code": "0x600160015560806080608060806080608060808600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008707": { + "code": "0x600160015560806080608060806080608060808700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008708": { + "code": "0x6001600155608060806080608060806080608060808700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008808": { + "code": "0x6001600155608060806080608060806080608060808800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008809": { + "code": "0x60016001556080608060806080608060806080608060808800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008909": { + "code": "0x60016001556080608060806080608060806080608060808900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000890a": { + "code": "0x600160015560806080608060806080608060806080608060808900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008a0a": { + "code": "0x600160015560806080608060806080608060806080608060808a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008a0b": { + "code": "0x6001600155608060806080608060806080608060806080608060808a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008b0b": { + "code": "0x6001600155608060806080608060806080608060806080608060808b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008b0c": { + "code": "0x60016001556080608060806080608060806080608060806080608060808b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008c0c": { + "code": "0x60016001556080608060806080608060806080608060806080608060808c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008c0d": { + "code": "0x600160015560806080608060806080608060806080608060806080608060808c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008d0d": { + "code": "0x600160015560806080608060806080608060806080608060806080608060808d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008d0e": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060808d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008e0e": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060808e00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008e0f": { + "code": "0x60016001556080608060806080608060806080608060806080608060806080608060808e00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008f0f": { + "code": "0x60016001556080608060806080608060806080608060806080608060806080608060808f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008f10": { + "code": "0x600160015560806080608060806080608060806080608060806080608060806080608060808f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009001": { + "code": "0x600160015560809000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009002": { + "code": "0x6001600155608060809000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009102": { + "code": "0x6001600155608060809100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009103": { + "code": "0x60016001556080608060809100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009203": { + "code": "0x60016001556080608060809200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009204": { + "code": "0x600160015560806080608060809200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009304": { + "code": "0x600160015560806080608060809300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009305": { + "code": "0x6001600155608060806080608060809300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009405": { + "code": "0x6001600155608060806080608060809400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009406": { + "code": "0x60016001556080608060806080608060809400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009506": { + "code": "0x60016001556080608060806080608060809500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009507": { + "code": "0x600160015560806080608060806080608060809500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009607": { + "code": "0x600160015560806080608060806080608060809600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009608": { + "code": "0x6001600155608060806080608060806080608060809600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009708": { + "code": "0x6001600155608060806080608060806080608060809700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009709": { + "code": "0x60016001556080608060806080608060806080608060809700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009809": { + "code": "0x60016001556080608060806080608060806080608060809800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000980a": { + "code": "0x600160015560806080608060806080608060806080608060809800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000990a": { + "code": "0x600160015560806080608060806080608060806080608060809900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000990b": { + "code": "0x6001600155608060806080608060806080608060806080608060809900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009a0b": { + "code": "0x6001600155608060806080608060806080608060806080608060809a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009a0c": { + "code": "0x60016001556080608060806080608060806080608060806080608060809a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009b0c": { + "code": "0x60016001556080608060806080608060806080608060806080608060809b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009b0d": { + "code": "0x600160015560806080608060806080608060806080608060806080608060809b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009c0d": { + "code": "0x600160015560806080608060806080608060806080608060806080608060809c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009c0e": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060809c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009d0e": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060809d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009d0f": { + "code": "0x60016001556080608060806080608060806080608060806080608060806080608060809d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009e0f": { + "code": "0x60016001556080608060806080608060806080608060806080608060806080608060809e00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009e10": { + "code": "0x600160015560806080608060806080608060806080608060806080608060806080608060809e00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009f10": { + "code": "0x600160015560806080608060806080608060806080608060806080608060806080608060809f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009f11": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060806080608060809f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a001": { + "code": "0x60016001556080a000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a002": { + "code": "0x600160015560806080a000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a102": { + "code": "0x600160015560806080a100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a103": { + "code": "0x6001600155608060806080a100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a203": { + "code": "0x6001600155608060806080a200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a204": { + "code": "0x60016001556080608060806080a200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a304": { + "code": "0x60016001556080608060806080a300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a305": { + "code": "0x600160015560806080608060806080a300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a405": { + "code": "0x600160015560806080608060806080a400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a406": { + "code": "0x6001600155608060806080608060806080a400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f002": { + "code": "0x600160015560806080f000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f003": { + "code": "0x6001600155608060806080f000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f106": { + "code": "0x6001600155608060806080608060806080f100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f107": { + "code": "0x60016001556080608060806080608060806080f100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f206": { + "code": "0x6001600155608060806080608060806080f200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f207": { + "code": "0x60016001556080608060806080608060806080f200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f301": { + "code": "0x60016001556080f300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f302": { + "code": "0x600160015560806080f300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f405": { + "code": "0x600160015560806080608060806080f400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f406": { + "code": "0x6001600155608060806080608060806080f400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f503": { + "code": "0x6001600155608060806080f500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f504": { + "code": "0x60016001556080608060806080f500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000fa05": { + "code": "0x600160015560806080608060806080fa00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000fa06": { + "code": "0x6001600155608060806080608060806080fa00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xba1a9ce0ba1a9ce" + }, + "cccccccccccccccccccccccccccccccccccccccc": { + "code": "0x6160a7600055600060006000600060006004355af1506160a760015500", + "balance": "0x0" + } + }, + "number": "0x1", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/params/coregeth.json.d/etc_phoenix_test.json b/params/coregeth.json.d/etc_phoenix_test.json new file mode 100755 index 0000000000..bd25858477 --- /dev/null +++ b/params/coregeth.json.d/etc_phoenix_test.json @@ -0,0 +1,1218 @@ +{ + "config": { + "networkId": 1, + "chainId": 61, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip160Block": 0, + "eip161FBlock": 0, + "eip170FBlock": 0, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "eip145FBlock": 0, + "eip1014FBlock": 0, + "eip1052FBlock": 0, + "eip1283FBlock": 0, + "petersburgBlock": 0, + "eip152FBlock": 0, + "eip1108FBlock": 0, + "eip1344FBlock": 0, + "eip1884FBlock": 0, + "eip2028FBlock": 0, + "eip2200FBlock": 0, + "ecip1017FBlock": 5000000, + "ecip1017EraRounds": 5000000, + "disposalBlock": 0, + "ethash": {}, + "requireBlockHashes": null + }, + "nonce": "0x0", + "timestamp": "0x3e8", + "extraData": "0x", + "gasLimit": "0x5f5e100", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "alloc": { + "0000000000000000000000000000000000000101": { + "code": "0x600160015560800100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000102": { + "code": "0x6001600155608060800100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000201": { + "code": "0x600160015560800200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000202": { + "code": "0x6001600155608060800200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000301": { + "code": "0x600160015560800300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000302": { + "code": "0x6001600155608060800300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000401": { + "code": "0x600160015560800400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000402": { + "code": "0x6001600155608060800400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000501": { + "code": "0x600160015560800500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000502": { + "code": "0x6001600155608060800500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000601": { + "code": "0x600160015560800600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000602": { + "code": "0x6001600155608060800600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000701": { + "code": "0x600160015560800700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000702": { + "code": "0x6001600155608060800700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000802": { + "code": "0x6001600155608060800800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000803": { + "code": "0x60016001556080608060800800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000902": { + "code": "0x6001600155608060800900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000903": { + "code": "0x60016001556080608060800900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000a01": { + "code": "0x600160015560800a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000a02": { + "code": "0x6001600155608060800a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000b01": { + "code": "0x600160015560800b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000b02": { + "code": "0x6001600155608060800b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001001": { + "code": "0x600160015560801000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001002": { + "code": "0x6001600155608060801000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001101": { + "code": "0x600160015560801100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001102": { + "code": "0x6001600155608060801100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001201": { + "code": "0x600160015560801200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001202": { + "code": "0x6001600155608060801200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001301": { + "code": "0x600160015560801300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001302": { + "code": "0x6001600155608060801300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001401": { + "code": "0x600160015560801400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001402": { + "code": "0x6001600155608060801400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001500": { + "code": "0x60016001551500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001501": { + "code": "0x600160015560801500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001601": { + "code": "0x600160015560801600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001602": { + "code": "0x6001600155608060801600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001701": { + "code": "0x600160015560801700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001702": { + "code": "0x6001600155608060801700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001801": { + "code": "0x600160015560801800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001802": { + "code": "0x6001600155608060801800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001900": { + "code": "0x60016001551900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001901": { + "code": "0x600160015560801900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001a01": { + "code": "0x600160015560801a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001a02": { + "code": "0x6001600155608060801a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001b01": { + "code": "0x600160015560801b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001b02": { + "code": "0x6001600155608060801b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001c01": { + "code": "0x600160015560801c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001c02": { + "code": "0x6001600155608060801c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001d01": { + "code": "0x600160015560801d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001d02": { + "code": "0x6001600155608060801d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000002001": { + "code": "0x600160015560802000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000002002": { + "code": "0x6001600155608060802000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003100": { + "code": "0x60016001553100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003101": { + "code": "0x600160015560803100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003500": { + "code": "0x60016001553500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003501": { + "code": "0x600160015560803500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003702": { + "code": "0x6001600155608060803700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003703": { + "code": "0x60016001556080608060803700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003902": { + "code": "0x6001600155608060803900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003903": { + "code": "0x60016001556080608060803900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003b00": { + "code": "0x60016001553b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003b01": { + "code": "0x600160015560803b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003c03": { + "code": "0x60016001556080608060803c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003c04": { + "code": "0x600160015560806080608060803c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003f00": { + "code": "0x60016001553f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003f01": { + "code": "0x600160015560803f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000004000": { + "code": "0x60016001554000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000004001": { + "code": "0x600160015560804000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005000": { + "code": "0x60016001555000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005001": { + "code": "0x600160015560805000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005100": { + "code": "0x60016001555100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005101": { + "code": "0x600160015560805100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005201": { + "code": "0x600160015560805200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005202": { + "code": "0x6001600155608060805200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005301": { + "code": "0x600160015560805300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005302": { + "code": "0x6001600155608060805300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005400": { + "code": "0x60016001555400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005401": { + "code": "0x600160015560805400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008000": { + "code": "0x60016001558000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008001": { + "code": "0x600160015560808000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008101": { + "code": "0x600160015560808100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008102": { + "code": "0x6001600155608060808100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008202": { + "code": "0x6001600155608060808200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008203": { + "code": "0x60016001556080608060808200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008303": { + "code": "0x60016001556080608060808300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008304": { + "code": "0x600160015560806080608060808300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008404": { + "code": "0x600160015560806080608060808400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008405": { + "code": "0x6001600155608060806080608060808400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008505": { + "code": "0x6001600155608060806080608060808500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008506": { + "code": "0x60016001556080608060806080608060808500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008606": { + "code": "0x60016001556080608060806080608060808600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008607": { + "code": "0x600160015560806080608060806080608060808600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008707": { + "code": "0x600160015560806080608060806080608060808700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008708": { + "code": "0x6001600155608060806080608060806080608060808700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008808": { + "code": "0x6001600155608060806080608060806080608060808800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008809": { + "code": "0x60016001556080608060806080608060806080608060808800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008909": { + "code": "0x60016001556080608060806080608060806080608060808900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000890a": { + "code": "0x600160015560806080608060806080608060806080608060808900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008a0a": { + "code": "0x600160015560806080608060806080608060806080608060808a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008a0b": { + "code": "0x6001600155608060806080608060806080608060806080608060808a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008b0b": { + "code": "0x6001600155608060806080608060806080608060806080608060808b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008b0c": { + "code": "0x60016001556080608060806080608060806080608060806080608060808b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008c0c": { + "code": "0x60016001556080608060806080608060806080608060806080608060808c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008c0d": { + "code": "0x600160015560806080608060806080608060806080608060806080608060808c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008d0d": { + "code": "0x600160015560806080608060806080608060806080608060806080608060808d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008d0e": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060808d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008e0e": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060808e00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008e0f": { + "code": "0x60016001556080608060806080608060806080608060806080608060806080608060808e00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008f0f": { + "code": "0x60016001556080608060806080608060806080608060806080608060806080608060808f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008f10": { + "code": "0x600160015560806080608060806080608060806080608060806080608060806080608060808f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009001": { + "code": "0x600160015560809000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009002": { + "code": "0x6001600155608060809000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009102": { + "code": "0x6001600155608060809100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009103": { + "code": "0x60016001556080608060809100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009203": { + "code": "0x60016001556080608060809200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009204": { + "code": "0x600160015560806080608060809200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009304": { + "code": "0x600160015560806080608060809300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009305": { + "code": "0x6001600155608060806080608060809300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009405": { + "code": "0x6001600155608060806080608060809400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009406": { + "code": "0x60016001556080608060806080608060809400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009506": { + "code": "0x60016001556080608060806080608060809500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009507": { + "code": "0x600160015560806080608060806080608060809500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009607": { + "code": "0x600160015560806080608060806080608060809600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009608": { + "code": "0x6001600155608060806080608060806080608060809600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009708": { + "code": "0x6001600155608060806080608060806080608060809700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009709": { + "code": "0x60016001556080608060806080608060806080608060809700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009809": { + "code": "0x60016001556080608060806080608060806080608060809800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000980a": { + "code": "0x600160015560806080608060806080608060806080608060809800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000990a": { + "code": "0x600160015560806080608060806080608060806080608060809900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000990b": { + "code": "0x6001600155608060806080608060806080608060806080608060809900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009a0b": { + "code": "0x6001600155608060806080608060806080608060806080608060809a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009a0c": { + "code": "0x60016001556080608060806080608060806080608060806080608060809a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009b0c": { + "code": "0x60016001556080608060806080608060806080608060806080608060809b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009b0d": { + "code": "0x600160015560806080608060806080608060806080608060806080608060809b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009c0d": { + "code": "0x600160015560806080608060806080608060806080608060806080608060809c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009c0e": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060809c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009d0e": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060809d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009d0f": { + "code": "0x60016001556080608060806080608060806080608060806080608060806080608060809d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009e0f": { + "code": "0x60016001556080608060806080608060806080608060806080608060806080608060809e00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009e10": { + "code": "0x600160015560806080608060806080608060806080608060806080608060806080608060809e00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009f10": { + "code": "0x600160015560806080608060806080608060806080608060806080608060806080608060809f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009f11": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060806080608060809f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a001": { + "code": "0x60016001556080a000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a002": { + "code": "0x600160015560806080a000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a102": { + "code": "0x600160015560806080a100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a103": { + "code": "0x6001600155608060806080a100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a203": { + "code": "0x6001600155608060806080a200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a204": { + "code": "0x60016001556080608060806080a200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a304": { + "code": "0x60016001556080608060806080a300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a305": { + "code": "0x600160015560806080608060806080a300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a405": { + "code": "0x600160015560806080608060806080a400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a406": { + "code": "0x6001600155608060806080608060806080a400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f002": { + "code": "0x600160015560806080f000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f003": { + "code": "0x6001600155608060806080f000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f106": { + "code": "0x6001600155608060806080608060806080f100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f107": { + "code": "0x60016001556080608060806080608060806080f100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f206": { + "code": "0x6001600155608060806080608060806080f200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f207": { + "code": "0x60016001556080608060806080608060806080f200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f301": { + "code": "0x60016001556080f300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f302": { + "code": "0x600160015560806080f300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f405": { + "code": "0x600160015560806080608060806080f400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f406": { + "code": "0x6001600155608060806080608060806080f400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f503": { + "code": "0x6001600155608060806080f500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f504": { + "code": "0x60016001556080608060806080f500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000fa05": { + "code": "0x600160015560806080608060806080fa00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000fa06": { + "code": "0x6001600155608060806080608060806080fa00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xba1a9ce0ba1a9ce" + }, + "cccccccccccccccccccccccccccccccccccccccc": { + "code": "0x6160a7600055600060006000600060006004355af1506160a760015500", + "balance": "0x0" + } + }, + "number": "0x1", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/params/coregeth.json.d/frontier_difficulty_test.json b/params/coregeth.json.d/frontier_difficulty_test.json new file mode 100755 index 0000000000..e114e35e6d --- /dev/null +++ b/params/coregeth.json.d/frontier_difficulty_test.json @@ -0,0 +1,6 @@ +{ + "networkId": 1, + "chainId": null, + "ethash": {}, + "requireBlockHashes": null +} \ No newline at end of file diff --git a/params/coregeth.json.d/frontier_test.json b/params/coregeth.json.d/frontier_test.json new file mode 100755 index 0000000000..7287d7ae47 --- /dev/null +++ b/params/coregeth.json.d/frontier_test.json @@ -0,0 +1,27 @@ +{ + "config": { + "networkId": 1, + "chainId": 1, + "ethash": {}, + "requireBlockHashes": null + }, + "nonce": "0x0", + "timestamp": "0x3e8", + "extraData": "0x", + "gasLimit": "0xf4240", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "alloc": { + "1000000000000000000000000000000000000000": { + "code": "0x7f6040600060406000600073945304eb96065b2a98b57a48a06ae28d285a71b5626000527f0186a0f260005500000000000000000000000000000000000000000000000000602052604060006001f0", + "balance": "0x2710" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x2386f26fc10000" + } + }, + "number": "0x1", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/params/coregeth.json.d/homestead_difficulty_test.json b/params/coregeth.json.d/homestead_difficulty_test.json new file mode 100755 index 0000000000..8f5158d999 --- /dev/null +++ b/params/coregeth.json.d/homestead_difficulty_test.json @@ -0,0 +1,8 @@ +{ + "networkId": 1, + "chainId": null, + "eip2FBlock": 0, + "eip7FBlock": 0, + "ethash": {}, + "requireBlockHashes": null +} \ No newline at end of file diff --git a/params/coregeth.json.d/homestead_test.json b/params/coregeth.json.d/homestead_test.json new file mode 100755 index 0000000000..66d49f9660 --- /dev/null +++ b/params/coregeth.json.d/homestead_test.json @@ -0,0 +1,30 @@ +{ + "config": { + "networkId": 1, + "chainId": 1, + "eip2FBlock": 0, + "eip7FBlock": 0, + "ethash": {}, + "requireBlockHashes": null + }, + "nonce": "0x0", + "timestamp": "0x3e8", + "extraData": "0x", + "gasLimit": "0x3d37ef", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x68795c4aa09d6f4ed3e5deddf8c2ad3049a601da", + "alloc": { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xde0b6b3a7640000", + "nonce": "0x1" + }, + "b94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "code": "0x600035600052602035602052604035604052606035606052604060c860406000600060066207a120f260005560c85160015560e851600255", + "balance": "0x0" + } + }, + "number": "0x1", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/params/coregeth.json.d/istanbul_test.json b/params/coregeth.json.d/istanbul_test.json new file mode 100755 index 0000000000..34ff82e587 --- /dev/null +++ b/params/coregeth.json.d/istanbul_test.json @@ -0,0 +1,1221 @@ +{ + "config": { + "networkId": 1, + "chainId": 1, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip160Block": 0, + "eip161FBlock": 0, + "eip170FBlock": 0, + "eip100FBlock": 0, + "eip140FBlock": 0, + "eip198FBlock": 0, + "eip211FBlock": 0, + "eip212FBlock": 0, + "eip213FBlock": 0, + "eip214FBlock": 0, + "eip658FBlock": 0, + "eip145FBlock": 0, + "eip1014FBlock": 0, + "eip1052FBlock": 0, + "eip1283FBlock": 0, + "petersburgBlock": 0, + "eip152FBlock": 0, + "eip1108FBlock": 0, + "eip1344FBlock": 0, + "eip1884FBlock": 0, + "eip2028FBlock": 0, + "eip2200FBlock": 0, + "ethash": {}, + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "blockReward": { + "0x0": "0x1bc16d674ec80000" + }, + "requireBlockHashes": null + }, + "nonce": "0x0", + "timestamp": "0x3e8", + "extraData": "0x", + "gasLimit": "0x5f5e100", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "alloc": { + "0000000000000000000000000000000000000101": { + "code": "0x600160015560800100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000102": { + "code": "0x6001600155608060800100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000201": { + "code": "0x600160015560800200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000202": { + "code": "0x6001600155608060800200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000301": { + "code": "0x600160015560800300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000302": { + "code": "0x6001600155608060800300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000401": { + "code": "0x600160015560800400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000402": { + "code": "0x6001600155608060800400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000501": { + "code": "0x600160015560800500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000502": { + "code": "0x6001600155608060800500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000601": { + "code": "0x600160015560800600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000602": { + "code": "0x6001600155608060800600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000701": { + "code": "0x600160015560800700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000702": { + "code": "0x6001600155608060800700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000802": { + "code": "0x6001600155608060800800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000803": { + "code": "0x60016001556080608060800800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000902": { + "code": "0x6001600155608060800900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000903": { + "code": "0x60016001556080608060800900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000a01": { + "code": "0x600160015560800a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000a02": { + "code": "0x6001600155608060800a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000b01": { + "code": "0x600160015560800b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000000b02": { + "code": "0x6001600155608060800b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001001": { + "code": "0x600160015560801000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001002": { + "code": "0x6001600155608060801000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001101": { + "code": "0x600160015560801100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001102": { + "code": "0x6001600155608060801100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001201": { + "code": "0x600160015560801200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001202": { + "code": "0x6001600155608060801200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001301": { + "code": "0x600160015560801300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001302": { + "code": "0x6001600155608060801300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001401": { + "code": "0x600160015560801400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001402": { + "code": "0x6001600155608060801400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001500": { + "code": "0x60016001551500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001501": { + "code": "0x600160015560801500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001601": { + "code": "0x600160015560801600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001602": { + "code": "0x6001600155608060801600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001701": { + "code": "0x600160015560801700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001702": { + "code": "0x6001600155608060801700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001801": { + "code": "0x600160015560801800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001802": { + "code": "0x6001600155608060801800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001900": { + "code": "0x60016001551900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001901": { + "code": "0x600160015560801900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001a01": { + "code": "0x600160015560801a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001a02": { + "code": "0x6001600155608060801a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001b01": { + "code": "0x600160015560801b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001b02": { + "code": "0x6001600155608060801b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001c01": { + "code": "0x600160015560801c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001c02": { + "code": "0x6001600155608060801c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001d01": { + "code": "0x600160015560801d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000001d02": { + "code": "0x6001600155608060801d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000002001": { + "code": "0x600160015560802000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000002002": { + "code": "0x6001600155608060802000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003100": { + "code": "0x60016001553100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003101": { + "code": "0x600160015560803100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003500": { + "code": "0x60016001553500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003501": { + "code": "0x600160015560803500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003702": { + "code": "0x6001600155608060803700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003703": { + "code": "0x60016001556080608060803700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003902": { + "code": "0x6001600155608060803900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003903": { + "code": "0x60016001556080608060803900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003b00": { + "code": "0x60016001553b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003b01": { + "code": "0x600160015560803b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003c03": { + "code": "0x60016001556080608060803c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003c04": { + "code": "0x600160015560806080608060803c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003f00": { + "code": "0x60016001553f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000003f01": { + "code": "0x600160015560803f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000004000": { + "code": "0x60016001554000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000004001": { + "code": "0x600160015560804000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005000": { + "code": "0x60016001555000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005001": { + "code": "0x600160015560805000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005100": { + "code": "0x60016001555100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005101": { + "code": "0x600160015560805100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005201": { + "code": "0x600160015560805200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005202": { + "code": "0x6001600155608060805200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005301": { + "code": "0x600160015560805300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005302": { + "code": "0x6001600155608060805300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005400": { + "code": "0x60016001555400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000005401": { + "code": "0x600160015560805400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008000": { + "code": "0x60016001558000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008001": { + "code": "0x600160015560808000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008101": { + "code": "0x600160015560808100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008102": { + "code": "0x6001600155608060808100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008202": { + "code": "0x6001600155608060808200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008203": { + "code": "0x60016001556080608060808200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008303": { + "code": "0x60016001556080608060808300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008304": { + "code": "0x600160015560806080608060808300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008404": { + "code": "0x600160015560806080608060808400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008405": { + "code": "0x6001600155608060806080608060808400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008505": { + "code": "0x6001600155608060806080608060808500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008506": { + "code": "0x60016001556080608060806080608060808500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008606": { + "code": "0x60016001556080608060806080608060808600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008607": { + "code": "0x600160015560806080608060806080608060808600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008707": { + "code": "0x600160015560806080608060806080608060808700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008708": { + "code": "0x6001600155608060806080608060806080608060808700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008808": { + "code": "0x6001600155608060806080608060806080608060808800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008809": { + "code": "0x60016001556080608060806080608060806080608060808800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008909": { + "code": "0x60016001556080608060806080608060806080608060808900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000890a": { + "code": "0x600160015560806080608060806080608060806080608060808900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008a0a": { + "code": "0x600160015560806080608060806080608060806080608060808a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008a0b": { + "code": "0x6001600155608060806080608060806080608060806080608060808a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008b0b": { + "code": "0x6001600155608060806080608060806080608060806080608060808b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008b0c": { + "code": "0x60016001556080608060806080608060806080608060806080608060808b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008c0c": { + "code": "0x60016001556080608060806080608060806080608060806080608060808c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008c0d": { + "code": "0x600160015560806080608060806080608060806080608060806080608060808c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008d0d": { + "code": "0x600160015560806080608060806080608060806080608060806080608060808d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008d0e": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060808d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008e0e": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060808e00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008e0f": { + "code": "0x60016001556080608060806080608060806080608060806080608060806080608060808e00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008f0f": { + "code": "0x60016001556080608060806080608060806080608060806080608060806080608060808f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000008f10": { + "code": "0x600160015560806080608060806080608060806080608060806080608060806080608060808f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009001": { + "code": "0x600160015560809000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009002": { + "code": "0x6001600155608060809000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009102": { + "code": "0x6001600155608060809100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009103": { + "code": "0x60016001556080608060809100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009203": { + "code": "0x60016001556080608060809200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009204": { + "code": "0x600160015560806080608060809200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009304": { + "code": "0x600160015560806080608060809300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009305": { + "code": "0x6001600155608060806080608060809300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009405": { + "code": "0x6001600155608060806080608060809400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009406": { + "code": "0x60016001556080608060806080608060809400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009506": { + "code": "0x60016001556080608060806080608060809500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009507": { + "code": "0x600160015560806080608060806080608060809500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009607": { + "code": "0x600160015560806080608060806080608060809600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009608": { + "code": "0x6001600155608060806080608060806080608060809600", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009708": { + "code": "0x6001600155608060806080608060806080608060809700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009709": { + "code": "0x60016001556080608060806080608060806080608060809700", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009809": { + "code": "0x60016001556080608060806080608060806080608060809800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000980a": { + "code": "0x600160015560806080608060806080608060806080608060809800", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000990a": { + "code": "0x600160015560806080608060806080608060806080608060809900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000990b": { + "code": "0x6001600155608060806080608060806080608060806080608060809900", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009a0b": { + "code": "0x6001600155608060806080608060806080608060806080608060809a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009a0c": { + "code": "0x60016001556080608060806080608060806080608060806080608060809a00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009b0c": { + "code": "0x60016001556080608060806080608060806080608060806080608060809b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009b0d": { + "code": "0x600160015560806080608060806080608060806080608060806080608060809b00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009c0d": { + "code": "0x600160015560806080608060806080608060806080608060806080608060809c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009c0e": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060809c00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009d0e": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060809d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009d0f": { + "code": "0x60016001556080608060806080608060806080608060806080608060806080608060809d00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009e0f": { + "code": "0x60016001556080608060806080608060806080608060806080608060806080608060809e00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009e10": { + "code": "0x600160015560806080608060806080608060806080608060806080608060806080608060809e00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009f10": { + "code": "0x600160015560806080608060806080608060806080608060806080608060806080608060809f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "0000000000000000000000000000000000009f11": { + "code": "0x6001600155608060806080608060806080608060806080608060806080608060806080608060809f00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a001": { + "code": "0x60016001556080a000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a002": { + "code": "0x600160015560806080a000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a102": { + "code": "0x600160015560806080a100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a103": { + "code": "0x6001600155608060806080a100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a203": { + "code": "0x6001600155608060806080a200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a204": { + "code": "0x60016001556080608060806080a200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a304": { + "code": "0x60016001556080608060806080a300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a305": { + "code": "0x600160015560806080608060806080a300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a405": { + "code": "0x600160015560806080608060806080a400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000a406": { + "code": "0x6001600155608060806080608060806080a400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f002": { + "code": "0x600160015560806080f000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f003": { + "code": "0x6001600155608060806080f000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f106": { + "code": "0x6001600155608060806080608060806080f100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f107": { + "code": "0x60016001556080608060806080608060806080f100", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f206": { + "code": "0x6001600155608060806080608060806080f200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f207": { + "code": "0x60016001556080608060806080608060806080f200", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f301": { + "code": "0x60016001556080f300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f302": { + "code": "0x600160015560806080f300", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f405": { + "code": "0x600160015560806080608060806080f400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f406": { + "code": "0x6001600155608060806080608060806080f400", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f503": { + "code": "0x6001600155608060806080f500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f504": { + "code": "0x60016001556080608060806080f500", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000fa05": { + "code": "0x600160015560806080608060806080fa00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000fa06": { + "code": "0x6001600155608060806080608060806080fa00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000060a7" + }, + "balance": "0x0" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xba1a9ce0ba1a9ce" + }, + "cccccccccccccccccccccccccccccccccccccccc": { + "code": "0x6160a7600055600060006000600060006004355af1506160a760015500", + "balance": "0x0" + } + }, + "number": "0x1", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/params/coregeth.json.d/mainnetwork_difficulty_test.json b/params/coregeth.json.d/mainnetwork_difficulty_test.json new file mode 100755 index 0000000000..566e6356e7 --- /dev/null +++ b/params/coregeth.json.d/mainnetwork_difficulty_test.json @@ -0,0 +1,30 @@ +{ + "networkId": 1, + "chainId": 1, + "eip2FBlock": 1150000, + "eip7FBlock": 1150000, + "daoForkBlock": 1920000, + "eip150Block": 2463000, + "eip155Block": 2675000, + "eip160Block": 2675000, + "eip161FBlock": 2675000, + "eip170FBlock": 2675000, + "eip100FBlock": 4370000, + "eip140FBlock": 4370000, + "eip198FBlock": 4370000, + "eip211FBlock": 4370000, + "eip212FBlock": 4370000, + "eip213FBlock": 4370000, + "eip214FBlock": 4370000, + "eip658FBlock": 4370000, + "ethash": {}, + "difficultyBombDelays": { + "0x42ae50": "0x2dc6c0" + }, + "blockReward": { + "0x42ae50": "0x29a2241af62c0000" + }, + "requireBlockHashes": { + "2463000": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0" + } +} \ No newline at end of file diff --git a/params/coregeth.json.d/ropsten_difficulty_test.json b/params/coregeth.json.d/ropsten_difficulty_test.json new file mode 100755 index 0000000000..490006ba1a --- /dev/null +++ b/params/coregeth.json.d/ropsten_difficulty_test.json @@ -0,0 +1,48 @@ +{ + "networkId": 3, + "chainId": 3, + "eip2FBlock": 0, + "eip7FBlock": 0, + "eip150Block": 0, + "eip155Block": 10, + "eip160Block": 10, + "eip161FBlock": 10, + "eip170FBlock": 10, + "eip100FBlock": 1700000, + "eip140FBlock": 1700000, + "eip198FBlock": 1700000, + "eip211FBlock": 1700000, + "eip212FBlock": 1700000, + "eip213FBlock": 1700000, + "eip214FBlock": 1700000, + "eip658FBlock": 1700000, + "eip145FBlock": 4230000, + "eip1014FBlock": 4230000, + "eip1052FBlock": 4230000, + "eip1283FBlock": 4230000, + "petersburgBlock": 4939394, + "eip152FBlock": 6485846, + "eip1108FBlock": 6485846, + "eip1344FBlock": 6485846, + "eip1884FBlock": 6485846, + "eip2028FBlock": 6485846, + "eip2200FBlock": 6485846, + "eip2384FBlock": 7117117, + "eip2565FBlock": 9812189, + "eip2718FBlock": 9812189, + "eip2929FBlock": 9812189, + "eip2930FBlock": 9812189, + "ethash": {}, + "difficultyBombDelays": { + "0x19f0a0": "0x2dc6c0", + "0x408b70": "0x1e8480", + "0x6c993d": "0x3d0900" + }, + "blockReward": { + "0x19f0a0": "0x29a2241af62c0000", + "0x408b70": "0x1bc16d674ec80000" + }, + "requireBlockHashes": { + "0": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d" + } +} \ No newline at end of file diff --git a/params/genesis.go b/params/genesis.go index d3293889f2..62302d0365 100644 --- a/params/genesis.go +++ b/params/genesis.go @@ -77,14 +77,14 @@ func DefaultGoerliGenesisBlock() *genesisT.Genesis { } } -func DefaultYoloV2GenesisBlock() *genesisT.Genesis { +func DefaultYoloV3GenesisBlock() *genesisT.Genesis { return &genesisT.Genesis{ - Config: YoloV2ChainConfig, - Timestamp: 0x5ed754f1, + Config: YoloV3ChainConfig, + Timestamp: 0x6027dd2e, ExtraData: hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000008a37866fd3627c9205a37c8685666f32ec07bb1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), GasLimit: 0x47b760, Difficulty: big.NewInt(1), - Alloc: genesisT.DecodePreAlloc(YoloV2AllocData), + Alloc: genesisT.DecodePreAlloc(YoloV3AllocData), } } diff --git a/params/parity.json.d/berlin.json b/params/parity.json.d/berlin.json new file mode 100755 index 0000000000..d15de89651 --- /dev/null +++ b/params/parity.json.d/berlin.json @@ -0,0 +1,454 @@ +{ + "name": "Berlin", + "dataDir": "berlin", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0", + "eip1283ReenableTransition": "0x0", + "eip1344Transition": "0x0", + "eip1884Transition": "0x0", + "eip2028Transition": "0x0", + "eip2929Transition": "0x0", + "eip2930Transition": "0x0", + "eip2565Transition": "0x0", + "eip2718Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x3535353535353535353535353535353535353535", + "timestamp": "0x3e8", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x5f5e100" + }, + "nodes": [], + "accounts": { + "0000000000000000000000000000000000000000": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000001": { + "balance": "0x1", + "builtin": { + "name": "ecrecover", + "pricing": { + "0x0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + } + } + }, + "0000000000000000000000000000000000000002": { + "balance": "0x1", + "builtin": { + "name": "sha256", + "pricing": { + "0x0": { + "price": { + "linear": { + "base": 60, + "word": 2 + } + } + } + } + } + }, + "0000000000000000000000000000000000000003": { + "balance": "0x1", + "builtin": { + "name": "ripemd160", + "pricing": { + "0x0": { + "price": { + "linear": { + "base": 600, + "word": 1 + } + } + } + } + } + }, + "0000000000000000000000000000000000000004": { + "balance": "0x1", + "builtin": { + "name": "identity", + "pricing": { + "0x0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } + } + } + } + }, + "0000000000000000000000000000000000000005": { + "balance": "0x1", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000009": { + "balance": "0x0", + "builtin": { + "name": "blake2_f", + "pricing": { + "0x0": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + }, + "0x0": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + }, + "10a1c1cb95c92ec31d3f22c66eef1d9f3f258c6b": { + "balance": "0xde0b6b3a7640000" + }, + "13cbb8d99c6c4e0f2728c7d72606e78a29c4e224": { + "balance": "0xde0b6b3a7640000" + }, + "24143873e0e0815fdcbcffdbe09c979cbf9ad013": { + "balance": "0xde0b6b3a7640000" + }, + "3535353535353535353535353535353535353535": { + "balance": "0x782d75" + }, + "598443f1880ef585b21f1d7585bd0577402861e5": { + "balance": "0xde0b6b3a7640000" + }, + "77db2bebba79db42a978f896968f4afce746ea1f": { + "balance": "0xde0b6b3a7640000" + }, + "7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { + "balance": "0xde0b6b3a7640000" + }, + "82a978b3f5962a5b0957d9ee9eef472ee55b42f1": { + "balance": "0xde0b6b3a6ebd28b", + "nonce": "0xb8" + }, + "90f0b1ebbba1c1936aff7aaf20a7878ff9e04b6c": { + "balance": "0xde0b6b3a7640000" + }, + "c305c901078781c232a2a521c2af7980f8385ee9": { + "balance": "0x0", + "nonce": "0x1" + }, + "dceceaf3fc5c0a63d195d69b1a90011b7b19650d": { + "balance": "0xde0b6b3a7640000" + }, + "e0fc04fa2d34a66b779fd5cee748268032a146c0": { + "balance": "0xde0b6b3a7640000" + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/berlin_test.json b/params/parity.json.d/berlin_test.json new file mode 100755 index 0000000000..05e9ca770f --- /dev/null +++ b/params/parity.json.d/berlin_test.json @@ -0,0 +1,291 @@ +{ + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0", + "eip1283ReenableTransition": "0x0", + "eip1344Transition": "0x0", + "eip1884Transition": "0x0", + "eip2028Transition": "0x0", + "eip2929Transition": "0x0", + "eip2930Transition": "0x0", + "eip2565Transition": "0x0", + "eip2718Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000009": { + "balance": "0x0", + "builtin": { + "name": "blake2_f", + "pricing": { + "0x0": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/byzantium.json b/params/parity.json.d/byzantium.json new file mode 100755 index 0000000000..2b0fb0ccc6 --- /dev/null +++ b/params/parity.json.d/byzantium.json @@ -0,0 +1,282 @@ +{ + "name": "Byzantium", + "dataDir": "byzantium", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, + "difficultyBombDelays": { + "0x0": "0x2dc6c0" + }, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "timestamp": "0x3e8", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0xf4240" + }, + "nodes": [], + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xde0b6b3a7640000" + }, + "af3ecba2fe09a4f6c19f16a9d119e44e08c2da01": { + "balance": "0x0" + }, + "e2b35478fdd26477cc576dd906e6277761246a3c": { + "balance": "0x0" + }, + "ec2c6832d00680ece8ff9254f81fdab0a5a2ac50": { + "balance": "0x0" + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/byzantium_difficulty_test.json b/params/parity.json.d/byzantium_difficulty_test.json old mode 100644 new mode 100755 index 0fd3c784ca..ba597a7967 --- a/params/parity.json.d/byzantium_difficulty_test.json +++ b/params/parity.json.d/byzantium_difficulty_test.json @@ -7,20 +7,138 @@ "minimumDifficulty": "0x20000", "difficultyBoundDivisor": "0x800", "durationLimit": "0xd", - "blockReward": "0x29a2241af62c0000", + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, "difficultyBombDelays": { "0x0": "0x2dc6c0" }, "homesteadTransition": null, "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], "bombDefuseTransition": null } }, "Clique": { - "params": { - "period": null, - "epoch": null - } + "params": {} } }, "params": { @@ -28,9 +146,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "gasLimitBoundDivisor": "0x400", - "networkID": "0x0", - "chainID": "0x0", - "eip98Transition": "0x7fffffffffffffff", + "networkID": "0x1", + "maxCodeSize": "0x6000", "eip140Transition": "0x0", "eip211Transition": "0x0", "eip214Transition": "0x0", @@ -60,9 +177,13 @@ "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0x0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -72,9 +193,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0x0": { + "price": { + "linear": { + "base": 60, + "word": 2 + } + } } } } @@ -84,9 +209,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0x0": { + "price": { + "linear": { + "base": 600, + "word": 1 + } + } } } } @@ -96,9 +225,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0x0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } @@ -108,11 +241,21 @@ "builtin": { "name": "modexp", "pricing": { - "modexp": { - "divisor": 20 + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } } - }, - "activate_at": "0x0" + } } }, "0000000000000000000000000000000000000006": { @@ -120,6 +263,13 @@ "builtin": { "name": "alt_bn128_add", "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, "0x0": { "price": { "alt_bn128_const_operations": { @@ -135,6 +285,13 @@ "builtin": { "name": "alt_bn128_mul", "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, "0x0": { "price": { "alt_bn128_const_operations": { @@ -150,6 +307,14 @@ "builtin": { "name": "alt_bn128_pairing", "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, "0x0": { "price": { "alt_bn128_pairing": { @@ -903,7 +1068,7 @@ "balance": "0x0" }, "874b54a8bd152966d63f706bae1ffeb0411921e5": { - "balance": "0xc9f2c9cd04674edea40000000" + "balance": "0x4674edea40000000" } } } \ No newline at end of file diff --git a/params/parity.json.d/byzantium_test.json b/params/parity.json.d/byzantium_test.json old mode 100644 new mode 100755 index bc5ac30207..4da3413b2e --- a/params/parity.json.d/byzantium_test.json +++ b/params/parity.json.d/byzantium_test.json @@ -1,100 +1,241 @@ { - "name": "Byzantium (Test)", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "blockReward": "0x29A2241AF62C0000", - "homesteadTransition": "0x0", - "eip100bTransition": "0x0", - "difficultyBombDelays": { - "0": 3000000 - } - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "accountStartNonce": "0x00", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x1", - "maxCodeSize": 24576, - "maxCodeSizeTransition": "0x0", - "eip150Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip140Transition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip155Transition": "0x0", - "eip658Transition": "0x0" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x400000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388" - }, - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { - "builtin": { - "name": "alt_bn128_add", - "pricing": { - "0": { - "price": { "alt_bn128_const_operations": { "price": 500 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 150 }} - } - } - } - }, - "0000000000000000000000000000000000000007": { - "builtin": { - "name": "alt_bn128_mul", - "pricing": { - "0": { - "price": { "alt_bn128_const_operations": { "price": 40000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 6000 }} - } - } - } - }, - "0000000000000000000000000000000000000008": { - "builtin": { - "name": "alt_bn128_pairing", - "pricing": { - "0": { - "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} - } - } - } - } - } -} + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, + "difficultyBombDelays": { + "0x0": "0x2dc6c0" + }, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/classic_agharta_difficulty_test.json b/params/parity.json.d/classic_agharta_difficulty_test.json old mode 100644 new mode 100755 index ab2acf1070..78c4dfa286 --- a/params/parity.json.d/classic_agharta_difficulty_test.json +++ b/params/parity.json.d/classic_agharta_difficulty_test.json @@ -7,12 +7,7 @@ "minimumDifficulty": "0x20000", "difficultyBoundDivisor": "0x800", "durationLimit": "0xd", - "blockReward": { - "0x0": "0x1bc16d674ec80000" - }, - "difficultyBombDelays": { - "0x0": "0x1e8480" - }, + "blockReward": {}, "homesteadTransition": null, "eip100bTransition": "0x0", "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", @@ -147,7 +142,7 @@ "minGasLimit": "0x1388", "gasLimitBoundDivisor": "0x400", "networkID": "0x0", - "chainID": "0x0", + "maxCodeSize": "0x6000", "eip140Transition": "0x0", "eip211Transition": "0x0", "eip214Transition": "0x0", @@ -155,7 +150,8 @@ "eip145Transition": "0x0", "eip1014Transition": "0x0", "eip1052Transition": "0x0", - "eip1283Transition": "0x0" + "eip1283Transition": "0x0", + "eip1283ReenableTransition": "0x0" }, "genesis": { "seal": { @@ -181,14 +177,6 @@ "builtin": { "name": "ecrecover", "pricing": { - "0x0": { - "price": { - "linear": { - "base": 3000, - "word": 0 - } - } - }, "0x0": { "price": { "linear": { @@ -205,14 +193,6 @@ "builtin": { "name": "sha256", "pricing": { - "0x0": { - "price": { - "linear": { - "base": 60, - "word": 2 - } - } - }, "0x0": { "price": { "linear": { @@ -229,14 +209,6 @@ "builtin": { "name": "ripemd160", "pricing": { - "0x0": { - "price": { - "linear": { - "base": 600, - "word": 1 - } - } - }, "0x0": { "price": { "linear": { @@ -253,14 +225,6 @@ "builtin": { "name": "identity", "pricing": { - "0x0": { - "price": { - "linear": { - "base": 15, - "word": 3 - } - } - }, "0x0": { "price": { "linear": { diff --git a/params/parity.json.d/classic_agharta_test.json b/params/parity.json.d/classic_agharta_test.json old mode 100644 new mode 100755 index f1a336fa2c..34794acf60 --- a/params/parity.json.d/classic_agharta_test.json +++ b/params/parity.json.d/classic_agharta_test.json @@ -1,104 +1,242 @@ { - "name": "ETC Atlantis (Test)", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x20000", - "difficultyBoundDivisor": "0x800", - "durationLimit": "0xd", - "blockReward": "0x3782dace9d900000", - "homesteadTransition": "0x0", - "eip100bTransition": "0x0", - "bombDefuseTransition": "0x0" - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x400", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "accountStartNonce": "0x0", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x1", - "chainID": "0x3d", - "maxCodeSize": "0x6000", - "maxCodeSizeTransition": "0x0", - "eip150Transition": "0x0", - "eip155Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip140Transition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip658Transition": "0x0", - "eip145Transition": "0x0", - "eip1014Transition": "0x0", - "eip1052Transition": "0x0", - "eip1283Transition": "0x0", - "eip1283DisableTransition": "0x0" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x400000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x0", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388" - }, - "accounts": { - "0x0000000000000000000000000000000000000001": { "balance": "0x1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0x0000000000000000000000000000000000000002": { "balance": "0x1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0x0000000000000000000000000000000000000003": { "balance": "0x1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0x0000000000000000000000000000000000000004": { "balance": "0x1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0x0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x0", "pricing": { "modexp": { "divisor": 20 } } } }, - "0x0000000000000000000000000000000000000006": { - "builtin": { - "name": "alt_bn128_add", - "pricing": { - "0x0": { - "price": { "alt_bn128_const_operations": { "price": 500 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 150 }} - } - } - } - }, - "0x0000000000000000000000000000000000000007": { - "builtin": { - "name": "alt_bn128_mul", - "pricing": { - "0x0": { - "price": { "alt_bn128_const_operations": { "price": 40000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 6000 }} - } - } - } - }, - "0x0000000000000000000000000000000000000008": { - "builtin": { - "name": "alt_bn128_pairing", - "pricing": { - "0x0": { - "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} - } - } - } - } - } -} + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": "0x0", + "ecip1017EraRounds": "0x4c4b40" + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x3d", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/classic_atlantis_difficulty_test.json b/params/parity.json.d/classic_atlantis_difficulty_test.json old mode 100644 new mode 100755 index c758c513de..46c9d513ca --- a/params/parity.json.d/classic_atlantis_difficulty_test.json +++ b/params/parity.json.d/classic_atlantis_difficulty_test.json @@ -7,12 +7,7 @@ "minimumDifficulty": "0x20000", "difficultyBoundDivisor": "0x800", "durationLimit": "0xd", - "blockReward": { - "0x0": "0x29a2241af62c0000" - }, - "difficultyBombDelays": { - "0x0": "0x2dc6c0" - }, + "blockReward": {}, "homesteadTransition": null, "eip100bTransition": "0x0", "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", @@ -147,7 +142,7 @@ "minGasLimit": "0x1388", "gasLimitBoundDivisor": "0x400", "networkID": "0x0", - "chainID": "0x0", + "maxCodeSize": "0x6000", "eip140Transition": "0x0", "eip211Transition": "0x0", "eip214Transition": "0x0", @@ -177,14 +172,6 @@ "builtin": { "name": "ecrecover", "pricing": { - "0x0": { - "price": { - "linear": { - "base": 3000, - "word": 0 - } - } - }, "0x0": { "price": { "linear": { @@ -201,14 +188,6 @@ "builtin": { "name": "sha256", "pricing": { - "0x0": { - "price": { - "linear": { - "base": 60, - "word": 2 - } - } - }, "0x0": { "price": { "linear": { @@ -225,14 +204,6 @@ "builtin": { "name": "ripemd160", "pricing": { - "0x0": { - "price": { - "linear": { - "base": 600, - "word": 1 - } - } - }, "0x0": { "price": { "linear": { @@ -249,14 +220,6 @@ "builtin": { "name": "identity", "pricing": { - "0x0": { - "price": { - "linear": { - "base": 15, - "word": 3 - } - } - }, "0x0": { "price": { "linear": { diff --git a/params/parity.json.d/classic_atlantis_test.json b/params/parity.json.d/classic_atlantis_test.json old mode 100644 new mode 100755 index cc24226023..1b4e1bfcaf --- a/params/parity.json.d/classic_atlantis_test.json +++ b/params/parity.json.d/classic_atlantis_test.json @@ -1,99 +1,237 @@ { - "name": "ETC Atlantis (Test)", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x20000", - "difficultyBoundDivisor": "0x800", - "durationLimit": "0xd", - "blockReward": "0x3782dace9d900000", - "homesteadTransition": "0x0", - "eip100bTransition": "0x0", - "bombDefuseTransition": "0x0" - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x400", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "accountStartNonce": "0x0", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x1", - "chainID": "0x3d", - "maxCodeSize": "0x6000", - "maxCodeSizeTransition": "0x0", - "eip150Transition": "0x0", - "eip155Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip140Transition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip658Transition": "0x0" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x400000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x0", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388" - }, - "accounts": { - "0x0000000000000000000000000000000000000001": { "balance": "0x1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0x0000000000000000000000000000000000000002": { "balance": "0x1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0x0000000000000000000000000000000000000003": { "balance": "0x1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0x0000000000000000000000000000000000000004": { "balance": "0x1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0x0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x0", "pricing": { "modexp": { "divisor": 20 } } } }, - "0x0000000000000000000000000000000000000006": { - "builtin": { - "name": "alt_bn128_add", - "pricing": { - "0x0": { - "price": { "alt_bn128_const_operations": { "price": 500 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 150 }} - } - } - } - }, - "0x0000000000000000000000000000000000000007": { - "builtin": { - "name": "alt_bn128_mul", - "pricing": { - "0x0": { - "price": { "alt_bn128_const_operations": { "price": 40000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 6000 }} - } - } - } - }, - "0x0000000000000000000000000000000000000008": { - "builtin": { - "name": "alt_bn128_pairing", - "pricing": { - "0x0": { - "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} - } - } - } - } - } -} + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": "0x0", + "ecip1017EraRounds": "0x4c4b40" + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x3d", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/classic_magneto_test.json b/params/parity.json.d/classic_magneto_test.json new file mode 100755 index 0000000000..a92b8b9bad --- /dev/null +++ b/params/parity.json.d/classic_magneto_test.json @@ -0,0 +1,287 @@ +{ + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": "0x0", + "ecip1017EraRounds": "0x4c4b40" + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x3d", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0", + "eip1283ReenableTransition": "0x0", + "eip1344Transition": "0x0", + "eip1884Transition": "0x0", + "eip2028Transition": "0x0", + "eip2929Transition": "0x0", + "eip2930Transition": "0x0", + "eip2565Transition": "0x0", + "eip2718Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000009": { + "balance": "0x0", + "builtin": { + "name": "blake2_f", + "pricing": { + "0x0": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/classic_phoenix_difficulty_test.json b/params/parity.json.d/classic_phoenix_difficulty_test.json index 2332032a7d..1c767627eb 100755 --- a/params/parity.json.d/classic_phoenix_difficulty_test.json +++ b/params/parity.json.d/classic_phoenix_difficulty_test.json @@ -142,7 +142,6 @@ "minGasLimit": "0x1388", "gasLimitBoundDivisor": "0x400", "networkID": "0x0", - "chainID": "0x0", "maxCodeSize": "0x6000", "eip140Transition": "0x0", "eip211Transition": "0x0", @@ -178,14 +177,6 @@ "builtin": { "name": "ecrecover", "pricing": { - "0x0": { - "price": { - "linear": { - "base": 3000, - "word": 0 - } - } - }, "0x0": { "price": { "linear": { @@ -202,14 +193,6 @@ "builtin": { "name": "sha256", "pricing": { - "0x0": { - "price": { - "linear": { - "base": 60, - "word": 2 - } - } - }, "0x0": { "price": { "linear": { @@ -226,14 +209,6 @@ "builtin": { "name": "ripemd160", "pricing": { - "0x0": { - "price": { - "linear": { - "base": 600, - "word": 1 - } - } - }, "0x0": { "price": { "linear": { @@ -250,14 +225,6 @@ "builtin": { "name": "identity", "pricing": { - "0x0": { - "price": { - "linear": { - "base": 15, - "word": 3 - } - } - }, "0x0": { "price": { "linear": { diff --git a/params/parity.json.d/classic_phoenix_test.json b/params/parity.json.d/classic_phoenix_test.json new file mode 100755 index 0000000000..db85a4f157 --- /dev/null +++ b/params/parity.json.d/classic_phoenix_test.json @@ -0,0 +1,283 @@ +{ + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": "0x0", + "ecip1017EraRounds": "0x4c4b40" + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x3d", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0", + "eip1283ReenableTransition": "0x0", + "eip1344Transition": "0x0", + "eip1884Transition": "0x0", + "eip2028Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000009": { + "balance": "0x0", + "builtin": { + "name": "blake2_f", + "pricing": { + "0x0": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/constantinople.json b/params/parity.json.d/constantinople.json new file mode 100755 index 0000000000..50dc994095 --- /dev/null +++ b/params/parity.json.d/constantinople.json @@ -0,0 +1,286 @@ +{ + "name": "Constantinople", + "dataDir": "constantinople", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "timestamp": "0x3e8", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0xf4240" + }, + "nodes": [], + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xde0b6b3a7640000" + }, + "af3ecba2fe09a4f6c19f16a9d119e44e08c2da01": { + "balance": "0x0" + }, + "e2b35478fdd26477cc576dd906e6277761246a3c": { + "balance": "0x0" + }, + "ec2c6832d00680ece8ff9254f81fdab0a5a2ac50": { + "balance": "0x0" + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/constantinople_difficulty_test.json b/params/parity.json.d/constantinople_difficulty_test.json old mode 100644 new mode 100755 index 5ebbaaf7cd..8efc7b2f00 --- a/params/parity.json.d/constantinople_difficulty_test.json +++ b/params/parity.json.d/constantinople_difficulty_test.json @@ -7,20 +7,138 @@ "minimumDifficulty": "0x20000", "difficultyBoundDivisor": "0x800", "durationLimit": "0xd", - "blockReward": "0x1bc16d674ec80000", + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, "difficultyBombDelays": { "0x0": "0x4c4b40" }, "homesteadTransition": "0x0", "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], "bombDefuseTransition": null } }, "Clique": { - "params": { - "period": null, - "epoch": null - } + "params": {} } }, "params": { @@ -28,9 +146,12 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "gasLimitBoundDivisor": "0x400", - "networkID": "0x0", - "chainID": "0x0", - "eip98Transition": "0x7fffffffffffffff", + "networkID": "0x1", + "maxCodeSize": "0x6000", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", "eip145Transition": "0x0", "eip1014Transition": "0x0", "eip1052Transition": "0x0", @@ -60,9 +181,13 @@ "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0x0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -72,9 +197,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0x0": { + "price": { + "linear": { + "base": 60, + "word": 2 + } + } } } } @@ -84,9 +213,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0x0": { + "price": { + "linear": { + "base": 600, + "word": 1 + } + } } } } @@ -96,24 +229,106 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0x0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } }, "0000000000000000000000000000000000000005": { - "balance": "0x1" + "balance": "0x1", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } }, "0000000000000000000000000000000000000006": { - "balance": "0x1" + "balance": "0x1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } }, "0000000000000000000000000000000000000007": { - "balance": "0x1" + "balance": "0x1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } }, "0000000000000000000000000000000000000008": { - "balance": "0x1" + "balance": "0x1", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } }, "0000000000000000000000000000000000000009": { "balance": "0x1" @@ -857,7 +1072,7 @@ "balance": "0x0" }, "874b54a8bd152966d63f706bae1ffeb0411921e5": { - "balance": "0xc9f2c9cd04674edea40000000" + "balance": "0x4674edea40000000" } } } \ No newline at end of file diff --git a/params/parity.json.d/constantinople_fix.json b/params/parity.json.d/constantinople_fix.json new file mode 100755 index 0000000000..fa300e2315 --- /dev/null +++ b/params/parity.json.d/constantinople_fix.json @@ -0,0 +1,287 @@ +{ + "name": "ConstantinopleFix", + "dataDir": "constantinoplefix", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "timestamp": "0x3e8", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0xf4240" + }, + "nodes": [], + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xde0b6b3a7640000" + }, + "af3ecba2fe09a4f6c19f16a9d119e44e08c2da01": { + "balance": "0x0" + }, + "e2b35478fdd26477cc576dd906e6277761246a3c": { + "balance": "0x0" + }, + "ec2c6832d00680ece8ff9254f81fdab0a5a2ac50": { + "balance": "0x0" + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/constantinople_fix_test.json b/params/parity.json.d/constantinople_fix_test.json old mode 100644 new mode 100755 index ac404c7dca..6c98d9924a --- a/params/parity.json.d/constantinople_fix_test.json +++ b/params/parity.json.d/constantinople_fix_test.json @@ -1,105 +1,246 @@ { - "name": "St. Peter's (test)", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "blockReward": "0x1BC16D674EC80000", - "homesteadTransition": "0x0", - "eip100bTransition": "0x0", - "difficultyBombDelays": { - "0": 5000000 - } - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "accountStartNonce": "0x00", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x1", - "maxCodeSize": 24576, - "maxCodeSizeTransition": "0x0", - "eip150Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip140Transition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip155Transition": "0x0", - "eip658Transition": "0x0", - "eip145Transition": "0x0", - "eip1014Transition": "0x0", - "eip1052Transition": "0x0", - "eip1283Transition": "0x0", - "eip1283DisableTransition": "0x0" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x400000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388" - }, - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { - "builtin": { - "name": "alt_bn128_add", - "pricing": { - "0": { - "price": { "alt_bn128_const_operations": { "price": 500 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 150 }} - } - } - } - }, - "0000000000000000000000000000000000000007": { - "builtin": { - "name": "alt_bn128_mul", - "pricing": { - "0": { - "price": { "alt_bn128_const_operations": { "price": 40000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 6000 }} - } - } - } - }, - "0000000000000000000000000000000000000008": { - "builtin": { - "name": "alt_bn128_pairing", - "pricing": { - "0": { - "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} - } - } - } - } - } -} + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/constantinople_test.json b/params/parity.json.d/constantinople_test.json old mode 100644 new mode 100755 index cc5690e0d3..2188945f62 --- a/params/parity.json.d/constantinople_test.json +++ b/params/parity.json.d/constantinople_test.json @@ -1,104 +1,245 @@ { - "name": "Constantinople (test)", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "blockReward": "0x1BC16D674EC80000", - "homesteadTransition": "0x0", - "eip100bTransition": "0x0", - "difficultyBombDelays": { - "0": 5000000 - } - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "accountStartNonce": "0x00", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x1", - "maxCodeSize": 24576, - "maxCodeSizeTransition": "0x0", - "eip150Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip140Transition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip155Transition": "0x0", - "eip658Transition": "0x0", - "eip145Transition": "0x0", - "eip1014Transition": "0x0", - "eip1052Transition": "0x0", - "eip1283Transition": "0x0" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x400000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388" - }, - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { - "builtin": { - "name": "alt_bn128_add", - "pricing": { - "0": { - "price": { "alt_bn128_const_operations": { "price": 500 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 150 }} - } - } - } - }, - "0000000000000000000000000000000000000007": { - "builtin": { - "name": "alt_bn128_mul", - "pricing": { - "0": { - "price": { "alt_bn128_const_operations": { "price": 40000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 6000 }} - } - } - } - }, - "0000000000000000000000000000000000000008": { - "builtin": { - "name": "alt_bn128_pairing", - "pricing": { - "0": { - "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} - } - } - } - } - } -} + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/custom_mainnetwork_difficulty_test.json b/params/parity.json.d/custom_mainnetwork_difficulty_test.json old mode 100644 new mode 100755 index 445d9db323..689a4621e5 --- a/params/parity.json.d/custom_mainnetwork_difficulty_test.json +++ b/params/parity.json.d/custom_mainnetwork_difficulty_test.json @@ -8,7 +8,6 @@ "difficultyBoundDivisor": "0x800", "durationLimit": "0xd", "blockReward": { - "0x0": "0x4563918244f40000", "0x42ae50": "0x29a2241af62c0000" }, "difficultyBombDelays": { @@ -16,14 +15,131 @@ }, "homesteadTransition": "0x118c30", "eip100bTransition": "0x42ae50", + "daoHardforkTransition": "0x1d4c00", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], "bombDefuseTransition": null } }, "Clique": { - "params": { - "period": null, - "epoch": null - } + "params": {} } }, "params": { @@ -31,11 +147,10 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "gasLimitBoundDivisor": "0x400", - "networkID": "0x0", + "networkID": "0x1", "chainID": "0x1", "maxCodeSize": "0x6000", "maxCodeSizeTransition": "0x28d138", - "eip98Transition": "0x7fffffffffffffff", "eip150Transition": "0x259518", "eip160Transition": "0x28d138", "eip161abcTransition": "0x28d138", @@ -44,7 +159,9 @@ "eip140Transition": "0x42ae50", "eip211Transition": "0x42ae50", "eip214Transition": "0x42ae50", - "eip658Transition": "0x42ae50" + "eip658Transition": "0x42ae50", + "forkBlock": "0x259518", + "forkCanonHash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0" }, "genesis": { "seal": { @@ -70,9 +187,13 @@ "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0x0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -82,9 +203,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0x0": { + "price": { + "linear": { + "base": 60, + "word": 2 + } + } } } } @@ -94,9 +219,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0x0": { + "price": { + "linear": { + "base": 600, + "word": 1 + } + } } } } @@ -106,9 +235,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0x0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } @@ -118,11 +251,21 @@ "builtin": { "name": "modexp", "pricing": { - "modexp": { - "divisor": 20 + "0x42ae50": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x42ae50": { + "price": { + "modexp": { + "divisor": 20 + } + } } - }, - "activate_at": "0x42ae50" + } } }, "0000000000000000000000000000000000000006": { @@ -130,6 +273,13 @@ "builtin": { "name": "alt_bn128_add", "pricing": { + "0x42ae50": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, "0x42ae50": { "price": { "alt_bn128_const_operations": { @@ -145,6 +295,13 @@ "builtin": { "name": "alt_bn128_mul", "pricing": { + "0x42ae50": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, "0x42ae50": { "price": { "alt_bn128_const_operations": { @@ -160,6 +317,14 @@ "builtin": { "name": "alt_bn128_pairing", "pricing": { + "0x42ae50": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, "0x42ae50": { "price": { "alt_bn128_pairing": { @@ -913,7 +1078,7 @@ "balance": "0x0" }, "874b54a8bd152966d63f706bae1ffeb0411921e5": { - "balance": "0xc9f2c9cd04674edea40000000" + "balance": "0x4674edea40000000" } } } \ No newline at end of file diff --git a/params/parity.json.d/difficulty_json_difficulty_test.json b/params/parity.json.d/difficulty_json_difficulty_test.json old mode 100644 new mode 100755 index 2621c3a324..feb728fcea --- a/params/parity.json.d/difficulty_json_difficulty_test.json +++ b/params/parity.json.d/difficulty_json_difficulty_test.json @@ -8,7 +8,6 @@ "difficultyBoundDivisor": "0x800", "durationLimit": "0xd", "blockReward": { - "0x0": "0x4563918244f40000", "0x42ae50": "0x29a2241af62c0000" }, "difficultyBombDelays": { @@ -16,14 +15,131 @@ }, "homesteadTransition": "0x118c30", "eip100bTransition": "0x42ae50", + "daoHardforkTransition": "0x1d4c00", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], "bombDefuseTransition": null } }, "Clique": { - "params": { - "period": null, - "epoch": null - } + "params": {} } }, "params": { @@ -31,11 +147,10 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "gasLimitBoundDivisor": "0x400", - "networkID": "0x0", + "networkID": "0x1", "chainID": "0x1", "maxCodeSize": "0x6000", "maxCodeSizeTransition": "0x28d138", - "eip98Transition": "0x7fffffffffffffff", "eip150Transition": "0x259518", "eip160Transition": "0x28d138", "eip161abcTransition": "0x28d138", @@ -44,7 +159,9 @@ "eip140Transition": "0x42ae50", "eip211Transition": "0x42ae50", "eip214Transition": "0x42ae50", - "eip658Transition": "0x42ae50" + "eip658Transition": "0x42ae50", + "forkBlock": "0x259518", + "forkCanonHash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0" }, "genesis": { "seal": { @@ -70,9 +187,13 @@ "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0x0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -82,9 +203,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0x0": { + "price": { + "linear": { + "base": 60, + "word": 2 + } + } } } } @@ -94,9 +219,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0x0": { + "price": { + "linear": { + "base": 600, + "word": 1 + } + } } } } @@ -106,9 +235,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0x0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } @@ -118,11 +251,21 @@ "builtin": { "name": "modexp", "pricing": { - "modexp": { - "divisor": 20 + "0x42ae50": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x42ae50": { + "price": { + "modexp": { + "divisor": 20 + } + } } - }, - "activate_at": "0x42ae50" + } } }, "0000000000000000000000000000000000000006": { @@ -130,6 +273,13 @@ "builtin": { "name": "alt_bn128_add", "pricing": { + "0x42ae50": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, "0x42ae50": { "price": { "alt_bn128_const_operations": { @@ -145,6 +295,13 @@ "builtin": { "name": "alt_bn128_mul", "pricing": { + "0x42ae50": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, "0x42ae50": { "price": { "alt_bn128_const_operations": { @@ -160,6 +317,14 @@ "builtin": { "name": "alt_bn128_pairing", "pricing": { + "0x42ae50": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, "0x42ae50": { "price": { "alt_bn128_pairing": { @@ -913,7 +1078,7 @@ "balance": "0x0" }, "874b54a8bd152966d63f706bae1ffeb0411921e5": { - "balance": "0xc9f2c9cd04674edea40000000" + "balance": "0x4674edea40000000" } } } \ No newline at end of file diff --git a/params/parity.json.d/eip150_test.json b/params/parity.json.d/eip150_test.json old mode 100644 new mode 100755 index 34b0338fa8..9cbd11096b --- a/params/parity.json.d/eip150_test.json +++ b/params/parity.json.d/eip150_test.json @@ -1,49 +1,165 @@ { - "name": "Homestead (Test)", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "blockReward": "0x4563918244F40000", - "homesteadTransition": "0x0" - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "accountStartNonce": "0x00", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x1", - "eip150Transition": "0x0", - "eip160Transition": "0x7fffffffffffffff", - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff", - "eip155Transition": "0x7fffffffffffffff", - "maxCodeSize": 24576, - "maxCodeSizeTransition": "0x7fffffffffffffff" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x400000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388" - }, - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } - } -} + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": null, + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "eip150Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": null +} \ No newline at end of file diff --git a/params/parity.json.d/eip158_to_byzantiumat5_test.json b/params/parity.json.d/eip158_to_byzantiumat5_test.json old mode 100644 new mode 100755 index cd90afa750..242feb1572 --- a/params/parity.json.d/eip158_to_byzantiumat5_test.json +++ b/params/parity.json.d/eip158_to_byzantiumat5_test.json @@ -1,104 +1,241 @@ { - "name": "EIP158 to byzantium (test)", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "blockReward": { - "0x0": "0x4563918244F40000", - "0x5": "0x29A2241AF62C0000" - }, - "homesteadTransition": "0", - "eip100bTransition": "5", - "difficultyBombDelays": { - "5": 3000000 - } - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "accountStartNonce": "0x00", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x1", - "maxCodeSize": 24576, - "maxCodeSizeTransition": "0", - "eip150Transition": "0", - "eip155Transition": "0", - "eip160Transition": "0", - "eip161abcTransition": "0", - "eip161dTransition": "0", - "eip98Transition": "5", - "eip140Transition": "5", - "eip211Transition": "5", - "eip214Transition": "5", - "eip658Transition": "5" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x400000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388" - }, - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x5", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { - "builtin": { - "name": "alt_bn128_add", - "pricing": { - "5": { - "price": { "alt_bn128_const_operations": { "price": 500 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 150 }} - } - } - } - }, - "0000000000000000000000000000000000000007": { - "builtin": { - "name": "alt_bn128_mul", - "pricing": { - "5": { - "price": { "alt_bn128_const_operations": { "price": 40000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 6000 }} - } - } - } - }, - "0000000000000000000000000000000000000008": { - "builtin": { - "name": "alt_bn128_pairing", - "pricing": { - "5": { - "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} - } - } - } - } - } -} + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": { + "0x5": "0x29a2241af62c0000" + }, + "difficultyBombDelays": { + "0x5": "0x2dc6c0" + }, + "homesteadTransition": "0x0", + "eip100bTransition": "0x5", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x5", + "eip211Transition": "0x5", + "eip214Transition": "0x5", + "eip658Transition": "0x5" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x5": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x5": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x5": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x5": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/eip161_test.json b/params/parity.json.d/eip161_test.json old mode 100644 new mode 100755 index c593db7356..3889d3d7b8 --- a/params/parity.json.d/eip161_test.json +++ b/params/parity.json.d/eip161_test.json @@ -1,49 +1,170 @@ { - "name": "Homestead (Test)", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "blockReward": "0x4563918244F40000", - "homesteadTransition": "0x0" - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "accountStartNonce": "0x00", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x1", - "eip150Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip155Transition": "0x0", - "maxCodeSize": 24576, - "maxCodeSizeTransition": "0x0" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x400000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388" - }, - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } - } -} + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": null, + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": null +} \ No newline at end of file diff --git a/params/parity.json.d/eip210_test.json b/params/parity.json.d/eip210_test.json deleted file mode 100644 index 2cfeb7c61c..0000000000 --- a/params/parity.json.d/eip210_test.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "name": "EIP210 (test)", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "blockReward": "0x4563918244F40000", - "homesteadTransition": "0x0" - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "accountStartNonce": "0x00", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x1", - "maxCodeSize": 24576, - "maxCodeSizeTransition": "0x0", - "eip150Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip210Transition": "0x0" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x400000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388" - }, - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 100 } } } }, - "0000000000000000000000000000000000000006": { - "builtin": { - "name": "alt_bn128_add", - "pricing": { - "0": { - "price": { "alt_bn128_const_operations": { "price": 500 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 150 }} - } - } - } - }, - "0000000000000000000000000000000000000007": { - "builtin": { - "name": "alt_bn128_mul", - "pricing": { - "0": { - "price": { "alt_bn128_const_operations": { "price": 40000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 6000 }} - } - } - } - }, - "0000000000000000000000000000000000000008": { - "builtin": { - "name": "alt_bn128_pairing", - "pricing": { - "0": { - "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} - }, - "0x7fffffffffffff": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} - } - } - } - } - } -} diff --git a/params/parity.json.d/eip2384_difficulty_test.json b/params/parity.json.d/eip2384_difficulty_test.json index 51f2a81217..44a1717800 100755 --- a/params/parity.json.d/eip2384_difficulty_test.json +++ b/params/parity.json.d/eip2384_difficulty_test.json @@ -343,14 +343,6 @@ "builtin": { "name": "alt_bn128_pairing", "pricing": { - "0x0": { - "price": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000 - } - } - }, "0x0": { "price": { "alt_bn128_pairing": { @@ -374,6 +366,14 @@ "pair": 34000 } } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } } } } diff --git a/params/parity.json.d/eip_150.json b/params/parity.json.d/eip_150.json new file mode 100755 index 0000000000..083e655847 --- /dev/null +++ b/params/parity.json.d/eip_150.json @@ -0,0 +1,175 @@ +{ + "name": "EIP150", + "dataDir": "eip150", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": null, + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "eip150Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "timestamp": "0x3e8", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x989680" + }, + "nodes": [], + "accounts": { + "1000000000000000000000000000000000000000": { + "balance": "0x1" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xe8d4a51000" + }, + "b94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xe8d4a51000" + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/eip_158.json b/params/parity.json.d/eip_158.json new file mode 100755 index 0000000000..dbc04f762f --- /dev/null +++ b/params/parity.json.d/eip_158.json @@ -0,0 +1,174 @@ +{ + "name": "EIP158", + "dataDir": "eip158", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": null, + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "timestamp": "0x3e8", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0xf4240" + }, + "nodes": [], + "accounts": { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xde0b6b3a7640000" + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/etc_agharta.json b/params/parity.json.d/etc_agharta.json new file mode 100755 index 0000000000..82eea1cbc3 --- /dev/null +++ b/params/parity.json.d/etc_agharta.json @@ -0,0 +1,274 @@ +{ + "name": "ETC_Agharta", + "dataDir": "etc_agharta", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": "0x0", + "ecip1017EraRounds": "0x4c4b40" + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x3d", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "timestamp": "0x3e8", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0xf4240" + }, + "nodes": [], + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xde0b6b3a7640000" + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/etc_atlantis.json b/params/parity.json.d/etc_atlantis.json new file mode 100755 index 0000000000..41df85daf1 --- /dev/null +++ b/params/parity.json.d/etc_atlantis.json @@ -0,0 +1,269 @@ +{ + "name": "ETC_Atlantis", + "dataDir": "etc_atlantis", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": "0x0", + "ecip1017EraRounds": "0x4c4b40" + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x3d", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "timestamp": "0x3e8", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0xf4240" + }, + "nodes": [], + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xde0b6b3a7640000" + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/etc_magneto.json b/params/parity.json.d/etc_magneto.json new file mode 100755 index 0000000000..8569e312a4 --- /dev/null +++ b/params/parity.json.d/etc_magneto.json @@ -0,0 +1,450 @@ +{ + "name": "ETC_Magneto", + "dataDir": "etc_magneto", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": "0x0", + "ecip1017EraRounds": "0x4c4b40" + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x3d", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0", + "eip1283ReenableTransition": "0x0", + "eip1344Transition": "0x0", + "eip1884Transition": "0x0", + "eip2028Transition": "0x0", + "eip2929Transition": "0x0", + "eip2930Transition": "0x0", + "eip2565Transition": "0x0", + "eip2718Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x3535353535353535353535353535353535353535", + "timestamp": "0x3e8", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x5f5e100" + }, + "nodes": [], + "accounts": { + "0000000000000000000000000000000000000000": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000001": { + "balance": "0x1", + "builtin": { + "name": "ecrecover", + "pricing": { + "0x0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + } + } + }, + "0000000000000000000000000000000000000002": { + "balance": "0x1", + "builtin": { + "name": "sha256", + "pricing": { + "0x0": { + "price": { + "linear": { + "base": 60, + "word": 2 + } + } + } + } + } + }, + "0000000000000000000000000000000000000003": { + "balance": "0x1", + "builtin": { + "name": "ripemd160", + "pricing": { + "0x0": { + "price": { + "linear": { + "base": 600, + "word": 1 + } + } + } + } + } + }, + "0000000000000000000000000000000000000004": { + "balance": "0x1", + "builtin": { + "name": "identity", + "pricing": { + "0x0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } + } + } + } + }, + "0000000000000000000000000000000000000005": { + "balance": "0x1", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000009": { + "balance": "0x0", + "builtin": { + "name": "blake2_f", + "pricing": { + "0x0": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + }, + "0x0": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + }, + "10a1c1cb95c92ec31d3f22c66eef1d9f3f258c6b": { + "balance": "0xde0b6b3a7640000" + }, + "13cbb8d99c6c4e0f2728c7d72606e78a29c4e224": { + "balance": "0xde0b6b3a7640000" + }, + "24143873e0e0815fdcbcffdbe09c979cbf9ad013": { + "balance": "0xde0b6b3a7640000" + }, + "3535353535353535353535353535353535353535": { + "balance": "0x3c40ee" + }, + "598443f1880ef585b21f1d7585bd0577402861e5": { + "balance": "0xde0b6b3a7640000" + }, + "77db2bebba79db42a978f896968f4afce746ea1f": { + "balance": "0xde0b6b3a7640000" + }, + "7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { + "balance": "0xde0b6b3a7640000" + }, + "82a978b3f5962a5b0957d9ee9eef472ee55b42f1": { + "balance": "0xde0b6b3a727bf12", + "nonce": "0x57" + }, + "90f0b1ebbba1c1936aff7aaf20a7878ff9e04b6c": { + "balance": "0xde0b6b3a7640000" + }, + "c305c901078781c232a2a521c2af7980f8385ee9": { + "balance": "0x0", + "nonce": "0x1" + }, + "dceceaf3fc5c0a63d195d69b1a90011b7b19650d": { + "balance": "0xde0b6b3a7640000" + }, + "e0fc04fa2d34a66b779fd5cee748268032a146c0": { + "balance": "0xde0b6b3a7640000" + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/etc_phoenix.json b/params/parity.json.d/etc_phoenix.json new file mode 100755 index 0000000000..9c82995fb0 --- /dev/null +++ b/params/parity.json.d/etc_phoenix.json @@ -0,0 +1,446 @@ +{ + "name": "ETC_Phoenix", + "dataDir": "etc_phoenix", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": "0x0", + "ecip1017EraRounds": "0x4c4b40" + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x3d", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0", + "eip1283ReenableTransition": "0x0", + "eip1344Transition": "0x0", + "eip1884Transition": "0x0", + "eip2028Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x3535353535353535353535353535353535353535", + "timestamp": "0x3e8", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x5f5e100" + }, + "nodes": [], + "accounts": { + "0000000000000000000000000000000000000000": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000001": { + "balance": "0x1", + "builtin": { + "name": "ecrecover", + "pricing": { + "0x0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + } + } + }, + "0000000000000000000000000000000000000002": { + "balance": "0x1", + "builtin": { + "name": "sha256", + "pricing": { + "0x0": { + "price": { + "linear": { + "base": 60, + "word": 2 + } + } + } + } + } + }, + "0000000000000000000000000000000000000003": { + "balance": "0x1", + "builtin": { + "name": "ripemd160", + "pricing": { + "0x0": { + "price": { + "linear": { + "base": 600, + "word": 1 + } + } + } + } + } + }, + "0000000000000000000000000000000000000004": { + "balance": "0x1", + "builtin": { + "name": "identity", + "pricing": { + "0x0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } + } + } + } + }, + "0000000000000000000000000000000000000005": { + "balance": "0x1", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000009": { + "balance": "0x0", + "builtin": { + "name": "blake2_f", + "pricing": { + "0x0": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + }, + "0x0": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + }, + "10a1c1cb95c92ec31d3f22c66eef1d9f3f258c6b": { + "balance": "0xde0b6b3a7640000" + }, + "13cbb8d99c6c4e0f2728c7d72606e78a29c4e224": { + "balance": "0xde0b6b3a7640000" + }, + "24143873e0e0815fdcbcffdbe09c979cbf9ad013": { + "balance": "0xde0b6b3a7640000" + }, + "3535353535353535353535353535353535353535": { + "balance": "0x3c40ee" + }, + "598443f1880ef585b21f1d7585bd0577402861e5": { + "balance": "0xde0b6b3a7640000" + }, + "77db2bebba79db42a978f896968f4afce746ea1f": { + "balance": "0xde0b6b3a7640000" + }, + "7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { + "balance": "0xde0b6b3a7640000" + }, + "82a978b3f5962a5b0957d9ee9eef472ee55b42f1": { + "balance": "0xde0b6b3a727bf12", + "nonce": "0x57" + }, + "90f0b1ebbba1c1936aff7aaf20a7878ff9e04b6c": { + "balance": "0xde0b6b3a7640000" + }, + "c305c901078781c232a2a521c2af7980f8385ee9": { + "balance": "0x0", + "nonce": "0x1" + }, + "dceceaf3fc5c0a63d195d69b1a90011b7b19650d": { + "balance": "0xde0b6b3a7640000" + }, + "e0fc04fa2d34a66b779fd5cee748268032a146c0": { + "balance": "0xde0b6b3a7640000" + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/frontier.json b/params/parity.json.d/frontier.json new file mode 100755 index 0000000000..a6fd1c36b6 --- /dev/null +++ b/params/parity.json.d/frontier.json @@ -0,0 +1,174 @@ +{ + "name": "Frontier", + "dataDir": "frontier", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": null, + "eip100bTransition": null, + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "timestamp": "0x3e8", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x989680" + }, + "nodes": [], + "accounts": { + "1000000000000000000000000000000000000000": { + "balance": "0x1" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xe8d4a51000" + }, + "b94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xe8d4a51000" + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/frontier_difficulty_test.json b/params/parity.json.d/frontier_difficulty_test.json old mode 100644 new mode 100755 index 6fb93bc7bc..ae0608ec95 --- a/params/parity.json.d/frontier_difficulty_test.json +++ b/params/parity.json.d/frontier_difficulty_test.json @@ -7,17 +7,133 @@ "minimumDifficulty": "0x20000", "difficultyBoundDivisor": "0x800", "durationLimit": "0xd", - "blockReward": "0x4563918244f40000", + "blockReward": {}, "homesteadTransition": null, "eip100bTransition": null, + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], "bombDefuseTransition": null } }, "Clique": { - "params": { - "period": null, - "epoch": null - } + "params": {} } }, "params": { @@ -25,9 +141,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "gasLimitBoundDivisor": "0x400", - "networkID": "0x0", - "chainID": "0x0", - "eip98Transition": "0x7fffffffffffffff" + "networkID": "0x1", + "maxCodeSize": "0x6000" }, "genesis": { "seal": { @@ -53,9 +168,13 @@ "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0x0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -65,9 +184,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0x0": { + "price": { + "linear": { + "base": 60, + "word": 2 + } + } } } } @@ -77,9 +200,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0x0": { + "price": { + "linear": { + "base": 600, + "word": 1 + } + } } } } @@ -89,9 +216,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0x0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } @@ -850,7 +981,7 @@ "balance": "0x0" }, "874b54a8bd152966d63f706bae1ffeb0411921e5": { - "balance": "0xc9f2c9cd04674edea40000000" + "balance": "0x4674edea40000000" } } } \ No newline at end of file diff --git a/params/parity.json.d/frontier_test.json b/params/parity.json.d/frontier_test.json old mode 100644 new mode 100755 index 7ee8134679..dc746da7db --- a/params/parity.json.d/frontier_test.json +++ b/params/parity.json.d/frontier_test.json @@ -1,47 +1,164 @@ { - "name": "Frontier (Test)", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "blockReward": "0x4563918244F40000", - "homesteadTransition": "0x7fffffffffffffff" - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "accountStartNonce": "0x00", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x1", - "eip150Transition": "0x7fffffffffffffff", - "eip160Transition": "0x7fffffffffffffff", - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff", - "eip155Transition": "0x7fffffffffffffff" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x400000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388" - }, - "accounts": { - "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } - } -} + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": null, + "eip100bTransition": null, + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": null +} \ No newline at end of file diff --git a/params/parity.json.d/goerli.json b/params/parity.json.d/goerli.json deleted file mode 100644 index 28cd3ef309..0000000000 --- a/params/parity.json.d/goerli.json +++ /dev/null @@ -1,937 +0,0 @@ -{ - "name": "Görli Testnet", - "dataDir": "goerli", - "engine": { - "clique": { - "params": { - "period": 15, - "epoch": 30000 - } - } - }, - "params": { - "accountStartNonce": "0x0", - "chainID": "0x5", - "eip140Transition": "0x0", - "eip145Transition": "0x0", - "eip150Transition": "0x0", - "eip155Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip658Transition": "0x0", - "eip1014Transition": "0x0", - "eip1052Transition": "0x0", - "eip1283Transition": "0x0", - "eip1283DisableTransition": "0x0", - "eip1283ReenableTransition": "0x17d433", - "eip1344Transition": "0x17d433", - "eip1706Transition": "0x17d433", - "eip1884Transition": "0x17d433", - "eip2028Transition": "0x17d433", - "gasLimitBoundDivisor": "0x400", - "maxCodeSize": "0x6000", - "maxCodeSizeTransition": "0x0", - "maximumExtraDataSize": "0xffff", - "minGasLimit": "0x1388", - "networkID": "0x5" - }, - "genesis": { - "author": "0x0000000000000000000000000000000000000000", - "difficulty": "0x1", - "extraData": "0x22466c6578692069732061207468696e6722202d204166726900000000000000e0a2bd4258d2768837baa26a28fe71dc079f84c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0xa00000", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "seal": { - "ethereum": { - "nonce": "0x0000000000000000", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "timestamp": "0x5c51a607" - }, - "nodes": [ - "enode://06333009fc9ef3c9e174768e495722a7f98fe7afd4660542e983005f85e556028410fd03278944f44cfe5437b1750b5e6bd1738f700fe7da3626d52010d2954c@51.141.15.254:30303", - "enode://176b9417f511d05b6b2cf3e34b756cf0a7096b3094572a8f6ef4cdcb9d1f9d00683bf0f83347eebdf3b81c3521c2332086d9592802230bf528eaf606a1d9677b@13.93.54.137:30303", - "enode://a61215641fb8714a373c80edbfa0ea8878243193f57c96eeb44d0bc019ef295abd4e044fd619bfc4c59731a73fb79afe84e9ab6da0c743ceb479cbb6d263fa91@3.11.147.67:30303", - "enode://67913271d14f445689e8310270c304d42f268428f2de7a4ac0275bea97690e021df6f549f462503ff4c7a81d9dd27288867bbfa2271477d0911378b8944fae55@157.230.239.163:30303", - "enode://a87685902a0622e9cf18c68e73a0ea45156ec53e857ef049b185a9db2296ca04d776417bf1901c0b4eacb5b26271d8694e88e3f17c20d49eb77e1a41ab26b5b3@51.141.78.53:30303", - "enode://ae8658da8d255d1992c3ec6e62e11d6e1c5899aa1566504bc1ff96a0c9c8bd44838372be643342553817f5cc7d78f1c83a8093dee13d77b3b0a583c050c81940@18.232.185.151:30303", - "enode://ae8658da8d255d1992c3ec6e62e11d6e1c5899aa1566504bc1ff96a0c9c8bd44838372be643342553817f5cc7d78f1c83a8093dee13d77b3b0a583c050c81940@18.232.185.151:30303", - "enode://b477ca6d507a3f57070783eb62ba838847635f8b1a0cbffb8b7f8173f5894cf550f0225a5c279341e2d862a606e778b57180a4f1db3db78c51eadcfa4fdc6963@40.68.240.160:30303" - ], - "accounts": { - "0x0000000000000000000000000000000000000000": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000001": { - "balance": "0x1", - "builtin": { - "name": "ecrecover", - "pricing": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - }, - "0x0000000000000000000000000000000000000002": { - "balance": "0x1", - "builtin": { - "name": "sha256", - "pricing": { - "linear": { - "base": 60, - "word": 12 - } - } - } - }, - "0x0000000000000000000000000000000000000003": { - "balance": "0x1", - "builtin": { - "name": "ripemd160", - "pricing": { - "linear": { - "base": 600, - "word": 120 - } - } - } - }, - "0x0000000000000000000000000000000000000004": { - "balance": "0x1", - "builtin": { - "name": "identity", - "pricing": { - "linear": { - "base": 15, - "word": 3 - } - } - } - }, - "0x0000000000000000000000000000000000000005": { - "balance": "0x1", - "builtin": { - "name": "modexp", - "activate_at": "0x0", - "pricing": { - "modexp": { - "divisor": 20 - } - } - } - }, - "0x0000000000000000000000000000000000000006": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_add", - "pricing": { - "0": { - "price": { "alt_bn128_const_operations": { "price": 500 }} - }, - "0x17d433": { - "info": "EIP 1108 transition at block 1_561_651 (0x17d433)", - "price": { "alt_bn128_const_operations": { "price": 150 }} - } - } - } - }, - "0x0000000000000000000000000000000000000007": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_mul", - "pricing": { - "0": { - "price": { "alt_bn128_const_operations": { "price": 40000 }} - }, - "0x17d433": { - "info": "EIP 1108 transition at block 1_561_651 (0x17d433)", - "price": { "alt_bn128_const_operations": { "price": 6000 }} - } - } - } - }, - "0x0000000000000000000000000000000000000008": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_pairing", - "pricing": { - "0": { - "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} - }, - "0x17d433": { - "info": "EIP 1108 transition at block 1_561_651 (0x17d433)", - "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} - } - } - } - }, - "0x0000000000000000000000000000000000000009": { - "balance": "0x1", - "builtin": { - "name": "blake2_f", - "activate_at": "0x17d433", - "pricing": { - "blake2_f": { - "gas_per_round": 1 - } - } - } - }, - "0x000000000000000000000000000000000000000a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000010": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000011": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000012": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000013": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000014": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000015": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000016": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000017": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000018": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000019": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000020": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000021": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000022": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000023": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000024": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000025": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000026": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000027": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000028": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000029": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000030": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000031": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000032": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000033": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000034": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000035": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000036": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000037": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000038": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000039": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000040": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000041": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000042": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000043": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000044": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000045": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000046": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000047": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000048": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000049": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000050": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000051": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000052": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000053": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000054": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000055": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000056": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000057": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000058": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000059": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000060": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000061": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000062": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000063": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000064": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000065": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000066": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000067": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000068": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000069": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000070": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000071": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000072": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000073": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000074": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000075": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000076": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000077": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000078": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000079": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000080": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000081": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000082": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000083": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000084": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000085": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000086": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000087": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000088": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000089": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000090": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000091": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000092": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000093": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000094": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000095": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000096": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000097": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000098": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000099": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009f": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000aa": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ab": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ac": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ad": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ae": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000af": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ba": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000be": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bf": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ca": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ce": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cf": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000da": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000db": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000dc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000dd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000de": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000df": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ea": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000eb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ec": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ed": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ee": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ef": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fa": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fe": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ff": { - "balance": "0x1" - }, - "0x4c2ae482593505f0163cdefc073e81c63cda4107": { - "balance": "0x152d02c7e14af6800000" - }, - "0xa8e8f14732658e4b51e8711931053a8a69baf2b1": { - "balance": "0x152d02c7e14af6800000" - }, - "0xd9a5179f091d85051d3c982785efd1455cec8699": { - "balance": "0x84595161401484a000000" - }, - "0xe0a2bd4258d2768837baa26a28fe71dc079f84c7": { - "balance": "0x4a47e3c12448f4ad000000" - } - } -} diff --git a/params/parity.json.d/homestead.json b/params/parity.json.d/homestead.json new file mode 100755 index 0000000000..291d8bb832 --- /dev/null +++ b/params/parity.json.d/homestead.json @@ -0,0 +1,174 @@ +{ + "name": "Homestead", + "dataDir": "homestead", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": null, + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "timestamp": "0x3e8", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x989680" + }, + "nodes": [], + "accounts": { + "1000000000000000000000000000000000000000": { + "balance": "0x1" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xe8d4a51000" + }, + "b94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xe8d4a51000" + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/homestead_difficulty_test.json b/params/parity.json.d/homestead_difficulty_test.json old mode 100644 new mode 100755 index e75b32ae89..8f7f5a163d --- a/params/parity.json.d/homestead_difficulty_test.json +++ b/params/parity.json.d/homestead_difficulty_test.json @@ -7,17 +7,133 @@ "minimumDifficulty": "0x20000", "difficultyBoundDivisor": "0x800", "durationLimit": "0xd", - "blockReward": "0x4563918244f40000", + "blockReward": {}, "homesteadTransition": "0x0", "eip100bTransition": null, + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], "bombDefuseTransition": null } }, "Clique": { - "params": { - "period": null, - "epoch": null - } + "params": {} } }, "params": { @@ -25,9 +141,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "gasLimitBoundDivisor": "0x400", - "networkID": "0x0", - "chainID": "0x0", - "eip98Transition": "0x7fffffffffffffff" + "networkID": "0x1", + "maxCodeSize": "0x6000" }, "genesis": { "seal": { @@ -53,9 +168,13 @@ "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0x0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -65,9 +184,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0x0": { + "price": { + "linear": { + "base": 60, + "word": 2 + } + } } } } @@ -77,9 +200,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0x0": { + "price": { + "linear": { + "base": 600, + "word": 1 + } + } } } } @@ -89,9 +216,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0x0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } @@ -850,7 +981,7 @@ "balance": "0x0" }, "874b54a8bd152966d63f706bae1ffeb0411921e5": { - "balance": "0xc9f2c9cd04674edea40000000" + "balance": "0x4674edea40000000" } } } \ No newline at end of file diff --git a/params/parity.json.d/homestead_test.json b/params/parity.json.d/homestead_test.json old mode 100644 new mode 100755 index 766e4fd229..09894d3b4c --- a/params/parity.json.d/homestead_test.json +++ b/params/parity.json.d/homestead_test.json @@ -1,47 +1,164 @@ { - "name": "Homestead (Test)", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "blockReward": "0x4563918244F40000", - "homesteadTransition": "0x0" - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "accountStartNonce": "0x00", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x1", - "eip155Transition": "0x7fffffffffffffff", - "eip150Transition": "0x7fffffffffffffff", - "eip160Transition": "0x7fffffffffffffff", - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x400000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388" - }, - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } - } -} + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": {}, + "homesteadTransition": "0x0", + "eip100bTransition": null, + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": null +} \ No newline at end of file diff --git a/params/parity.json.d/istanbul.json b/params/parity.json.d/istanbul.json new file mode 100755 index 0000000000..e934aeaafd --- /dev/null +++ b/params/parity.json.d/istanbul.json @@ -0,0 +1,352 @@ +{ + "name": "Istanbul", + "dataDir": "istanbul", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0", + "eip1283ReenableTransition": "0x0", + "eip1344Transition": "0x0", + "eip1884Transition": "0x0", + "eip2028Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x68795c4aa09d6f4ed3e5deddf8c2ad3049a601da", + "timestamp": "0x3e8", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x3d37ef" + }, + "nodes": [], + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000009": { + "balance": "0x0", + "builtin": { + "name": "blake2_f", + "pricing": { + "0x0": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + }, + "0x0": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xde0b6b3a7640000", + "nonce": "0x1" + }, + "b94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x0" + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/istanbul_test.json b/params/parity.json.d/istanbul_test.json old mode 100644 new mode 100755 index 0139d10c2d..d8554e2078 --- a/params/parity.json.d/istanbul_test.json +++ b/params/parity.json.d/istanbul_test.json @@ -1,121 +1,287 @@ { - "name": "Istanbul (test)", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "blockReward": "0x1BC16D674EC80000", - "homesteadTransition": "0x0", - "eip100bTransition": "0x0", - "difficultyBombDelays": { - "0": 5000000 - } - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "accountStartNonce": "0x00", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x1", - "maxCodeSize": 24576, - "maxCodeSizeTransition": "0x0", - "eip150Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip140Transition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip155Transition": "0x0", - "eip658Transition": "0x0", - "eip145Transition": "0x0", - "eip1014Transition": "0x0", - "eip1052Transition": "0x0", - "eip1283Transition": "0x0", - "eip1283DisableTransition": "0x0", - "eip1283ReenableTransition": "0x0", - "eip1344Transition": "0x0", - "eip1706Transition": "0x0", - "eip1884Transition": "0x0", - "eip2028Transition": "0x0" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x400000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388" - }, - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { - "builtin": { - "name": "alt_bn128_add", - "pricing": { - "0": { - "price": { "alt_bn128_const_operations": { "price": 500 }} - }, - "0": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 150 }} - } - } - } - }, - "0000000000000000000000000000000000000007": { - "builtin": { - "name": "alt_bn128_mul", - "pricing": { - "0": { - "price": { "alt_bn128_const_operations": { "price": 40000 }} - }, - "0": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_const_operations": { "price": 6000 }} - } - } - } - }, - "0000000000000000000000000000000000000008": { - "builtin": { - "name": "alt_bn128_pairing", - "pricing": { - "0": { - "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} - }, - "0": { - "info": "EIP 1108 transition", - "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} - } - } - } - }, - "0000000000000000000000000000000000000009": { - "builtin": { - "name": "blake2_f", - "activate_at": "0x00", - "pricing": { - "blake2_f": { - "gas_per_round": 1 - } - } - } - } - } -} + "name": "", + "dataDir": "", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": { + "0x0": "0x29a2241af62c0000" + }, + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], + "bombDefuseTransition": null + } + }, + "Clique": { + "params": {} + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x400", + "networkID": "0x1", + "chainID": "0x1", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0", + "eip1283ReenableTransition": "0x0", + "eip1344Transition": "0x0", + "eip1884Transition": "0x0", + "eip2028Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000000", + "mixHash": "0x" + } + }, + "difficulty": null, + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x0" + }, + "nodes": null, + "accounts": { + "0000000000000000000000000000000000000005": { + "balance": "0x0", + "builtin": { + "name": "modexp", + "pricing": { + "0x0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "0x0", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + }, + "0x0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000009": { + "balance": "0x0", + "builtin": { + "name": "blake2_f", + "pricing": { + "0x0": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/params/parity.json.d/mainnetwork_difficulty_test.json b/params/parity.json.d/mainnetwork_difficulty_test.json old mode 100644 new mode 100755 index ea30ce451d..27a433e5f9 --- a/params/parity.json.d/mainnetwork_difficulty_test.json +++ b/params/parity.json.d/mainnetwork_difficulty_test.json @@ -8,7 +8,6 @@ "difficultyBoundDivisor": "0x800", "durationLimit": "0xd", "blockReward": { - "0x0": "0x4563918244f40000", "0x42ae50": "0x29a2241af62c0000" }, "difficultyBombDelays": { @@ -16,14 +15,131 @@ }, "homesteadTransition": "0x118c30", "eip100bTransition": "0x42ae50", + "daoHardforkTransition": "0x1d4c00", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], "bombDefuseTransition": null } }, "Clique": { - "params": { - "period": null, - "epoch": null - } + "params": {} } }, "params": { @@ -31,11 +147,10 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "gasLimitBoundDivisor": "0x400", - "networkID": "0x0", + "networkID": "0x1", "chainID": "0x1", "maxCodeSize": "0x6000", "maxCodeSizeTransition": "0x28d138", - "eip98Transition": "0x7fffffffffffffff", "eip150Transition": "0x259518", "eip160Transition": "0x28d138", "eip161abcTransition": "0x28d138", @@ -44,7 +159,9 @@ "eip140Transition": "0x42ae50", "eip211Transition": "0x42ae50", "eip214Transition": "0x42ae50", - "eip658Transition": "0x42ae50" + "eip658Transition": "0x42ae50", + "forkBlock": "0x259518", + "forkCanonHash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0" }, "genesis": { "seal": { @@ -70,9 +187,13 @@ "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0x0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -82,9 +203,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0x0": { + "price": { + "linear": { + "base": 60, + "word": 2 + } + } } } } @@ -94,9 +219,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0x0": { + "price": { + "linear": { + "base": 600, + "word": 1 + } + } } } } @@ -106,9 +235,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0x0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } @@ -118,11 +251,21 @@ "builtin": { "name": "modexp", "pricing": { - "modexp": { - "divisor": 20 + "0x42ae50": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x42ae50": { + "price": { + "modexp": { + "divisor": 20 + } + } } - }, - "activate_at": "0x42ae50" + } } }, "0000000000000000000000000000000000000006": { @@ -130,6 +273,13 @@ "builtin": { "name": "alt_bn128_add", "pricing": { + "0x42ae50": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, "0x42ae50": { "price": { "alt_bn128_const_operations": { @@ -145,6 +295,13 @@ "builtin": { "name": "alt_bn128_mul", "pricing": { + "0x42ae50": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, "0x42ae50": { "price": { "alt_bn128_const_operations": { @@ -160,6 +317,14 @@ "builtin": { "name": "alt_bn128_pairing", "pricing": { + "0x42ae50": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, "0x42ae50": { "price": { "alt_bn128_pairing": { @@ -913,7 +1078,7 @@ "balance": "0x0" }, "874b54a8bd152966d63f706bae1ffeb0411921e5": { - "balance": "0xc9f2c9cd04674edea40000000" + "balance": "0x4674edea40000000" } } } \ No newline at end of file diff --git a/params/parity.json.d/morden_difficulty_test.json b/params/parity.json.d/morden_difficulty_test.json old mode 100644 new mode 100755 index b7f06591b8..bb80f989c1 --- a/params/parity.json.d/morden_difficulty_test.json +++ b/params/parity.json.d/morden_difficulty_test.json @@ -8,24 +8,140 @@ "difficultyBoundDivisor": "0x800", "durationLimit": "0xd", "blockReward": { - "0x0": "0x4563918244f40000", "0x19f0a0": "0x29a2241af62c0000", "0x408b70": "0x1bc16d674ec80000" }, "difficultyBombDelays": { "0x19f0a0": "0x2dc6c0", - "0x408b70": "0x1e8480" + "0x408b70": "0x1e8480", + "0x6c993d": "0x3d0900" }, "homesteadTransition": "0x0", "eip100bTransition": "0x19f0a0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], "bombDefuseTransition": null } }, "Clique": { - "params": { - "period": null, - "epoch": null - } + "params": {} } }, "params": { @@ -37,7 +153,6 @@ "chainID": "0x3", "maxCodeSize": "0x6000", "maxCodeSizeTransition": "0xa", - "eip98Transition": "0x7fffffffffffffff", "eip150Transition": "0x0", "eip160Transition": "0xa", "eip161abcTransition": "0xa", @@ -55,7 +170,13 @@ "eip1283ReenableTransition": "0x62f756", "eip1344Transition": "0x62f756", "eip1884Transition": "0x62f756", - "eip2028Transition": "0x62f756" + "eip2028Transition": "0x62f756", + "eip2929Transition": "0x95b8dd", + "eip2930Transition": "0x95b8dd", + "eip2565Transition": "0x95b8dd", + "eip2718Transition": "0x95b8dd", + "forkBlock": "0x0", + "forkCanonHash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d" }, "genesis": { "seal": { @@ -81,9 +202,13 @@ "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0x0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -93,9 +218,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0x0": { + "price": { + "linear": { + "base": 60, + "word": 2 + } + } } } } @@ -105,9 +234,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0x0": { + "price": { + "linear": { + "base": 600, + "word": 1 + } + } } } } @@ -117,9 +250,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0x0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } @@ -129,11 +266,21 @@ "builtin": { "name": "modexp", "pricing": { - "modexp": { - "divisor": 20 + "0x19f0a0": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x19f0a0": { + "price": { + "modexp": { + "divisor": 20 + } + } } - }, - "activate_at": "0x19f0a0" + } } }, "0000000000000000000000000000000000000006": { @@ -148,6 +295,20 @@ } } }, + "0x19f0a0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x62f756": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + }, "0x62f756": { "price": { "alt_bn128_const_operations": { @@ -170,6 +331,20 @@ } } }, + "0x19f0a0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x62f756": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + }, "0x62f756": { "price": { "alt_bn128_const_operations": { @@ -193,6 +368,22 @@ } } }, + "0x19f0a0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x62f756": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + }, "0x62f756": { "price": { "alt_bn128_pairing": { @@ -209,11 +400,21 @@ "builtin": { "name": "blake2_f", "pricing": { - "blake2_f": { - "gas_per_round": 1 + "0x62f756": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + }, + "0x62f756": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } } - }, - "activate_at": "0x62f756" + } } }, "000000000000000000000000000000000000000a": { @@ -955,7 +1156,7 @@ "balance": "0x0" }, "874b54a8bd152966d63f706bae1ffeb0411921e5": { - "balance": "0xc9f2c9cd04674edea40000000" + "balance": "0x4674edea40000000" } } } \ No newline at end of file diff --git a/params/parity.json.d/rinkeby.json b/params/parity.json.d/rinkeby.json deleted file mode 100644 index ee02408f6c..0000000000 --- a/params/parity.json.d/rinkeby.json +++ /dev/null @@ -1,923 +0,0 @@ -{ - "name": "Rinkeby", - "dataDir": "rinkeby", - "engine": { - "clique": { - "params": { - "period": 15, - "epoch": 30000 - } - } - }, - "params": { - "accountStartNonce": "0x0", - "chainID": "0x4", - "eip140Transition": "0xfcc25", - "eip145Transition": "0x37db77", - "eip150Transition": "0x2", - "eip155Transition": "0x3", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip211Transition": "0xfcc25", - "eip214Transition": "0xfcc25", - "eip658Transition": "0xfcc25", - "eip1014Transition": "0x37db77", - "eip1052Transition": "0x37db77", - "eip1283Transition": "0x37db77", - "eip1283DisableTransition": "0x41efd2", - "eip1283ReenableTransition": "0x52efd1", - "eip1344Transition": "0x52efd1", - "eip1706Transition": "0x52efd1", - "eip1884Transition": "0x52efd1", - "eip2028Transition": "0x52efd1", - "gasLimitBoundDivisor": "0x400", - "maxCodeSize": "0x6000", - "maxCodeSizeTransition": "0x0", - "maximumExtraDataSize": "0xffff", - "minGasLimit": "0x1388", - "networkID": "0x4" - }, - "genesis": { - "author": "0x0000000000000000000000000000000000000000", - "difficulty": "0x1", - "extraData": "0x52657370656374206d7920617574686f7269746168207e452e436172746d616e42eb768f2244c8811c63729a21a3569731535f067ffc57839b00206d1ad20c69a1981b489f772031b279182d99e65703f0076e4812653aab85fca0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x47b760", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "seal": { - "ethereum": { - "nonce": "0x0000000000000000", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "timestamp": "0x58ee40ba" - }, - "nodes": [ - "enode://a24ac7c5484ef4ed0c5eb2d36620ba4e4aa13b8c84684e1b4aab0cebea2ae45cb4d375b77eab56516d34bfbd3c1a833fc51296ff084b770b94fb9028c4d25ccf@52.169.42.101:30303", - "enode://343149e4feefa15d882d9fe4ac7d88f885bd05ebb735e547f12e12080a9fa07c8014ca6fd7f373123488102fe5e34111f8509cf0b7de3f5b44339c9f25e87cb8@52.3.158.184:30303", - "enode://b6b28890b006743680c52e64e0d16db57f28124885595fa03a562be1d2bf0f3a1da297d56b13da25fb992888fd556d4c1a27b1f39d531bde7de1921c90061cc6@159.89.28.211:30303" - ], - "accounts": { - "0x0000000000000000000000000000000000000000": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000001": { - "balance": "0x1", - "builtin": { - "name": "ecrecover", - "pricing": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - }, - "0x0000000000000000000000000000000000000002": { - "balance": "0x1", - "builtin": { - "name": "sha256", - "pricing": { - "linear": { - "base": 60, - "word": 12 - } - } - } - }, - "0x0000000000000000000000000000000000000003": { - "balance": "0x1", - "builtin": { - "name": "ripemd160", - "pricing": { - "linear": { - "base": 600, - "word": 120 - } - } - } - }, - "0x0000000000000000000000000000000000000004": { - "balance": "0x1", - "builtin": { - "name": "identity", - "pricing": { - "linear": { - "base": 15, - "word": 3 - } - } - } - }, - "0x0000000000000000000000000000000000000005": { - "balance": "0x1", - "builtin": { - "name": "modexp", - "activate_at": "0xfcc25", - "pricing": { - "modexp": { - "divisor": 20 - } - } - } - }, - "0x0000000000000000000000000000000000000006": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_add", - "pricing": { - "0xfcc25": { - "price": { "alt_bn128_const_operations": { "price": 500 }} - }, - "0x52efd1": { - "info": "EIP 1108 transition at block 5_435_345 (0x52efd1)", - "price": { "alt_bn128_const_operations": { "price": 150 }} - } - } - } - }, - "0x0000000000000000000000000000000000000007": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_mul", - "pricing": { - "0xfcc25": { - "price": { "alt_bn128_const_operations": { "price": 40000 }} - }, - "0x52efd1": { - "info": "EIP 1108 transition at block 5_435_345 (0x52efd1)", - "price": { "alt_bn128_const_operations": { "price": 6000 }} - } - } - } - }, - "0x0000000000000000000000000000000000000008": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_pairing", - "pricing": { - "0xfcc25": { - "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} - }, - "0x52efd1": { - "info": "EIP 1108 transition at block 5_435_345 (0x52efd1)", - "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} - } - } - } - }, - "0x0000000000000000000000000000000000000009": { - "balance": "0x1", - "builtin": { - "name": "blake2_f", - "activate_at": "0x52efd1", - "pricing": { - "blake2_f": { - "gas_per_round": 1 - } - } - } - }, - "0x000000000000000000000000000000000000000a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000010": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000011": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000012": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000013": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000014": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000015": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000016": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000017": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000018": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000019": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000020": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000021": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000022": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000023": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000024": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000025": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000026": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000027": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000028": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000029": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000030": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000031": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000032": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000033": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000034": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000035": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000036": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000037": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000038": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000039": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000040": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000041": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000042": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000043": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000044": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000045": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000046": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000047": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000048": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000049": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000050": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000051": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000052": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000053": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000054": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000055": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000056": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000057": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000058": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000059": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000060": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000061": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000062": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000063": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000064": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000065": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000066": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000067": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000068": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000069": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000070": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000071": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000072": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000073": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000074": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000075": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000076": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000077": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000078": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000079": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000080": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000081": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000082": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000083": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000084": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000085": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000086": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000087": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000088": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000089": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000090": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000091": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000092": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000093": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000094": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000095": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000096": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000097": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000098": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000099": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009f": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000aa": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ab": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ac": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ad": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ae": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000af": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ba": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000be": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bf": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ca": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ce": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cf": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000da": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000db": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000dc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000dd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000de": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000df": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ea": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000eb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ec": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ed": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ee": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ef": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fa": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fe": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ff": { - "balance": "0x1" - }, - "0x31b98d14007bdee637298086988a0bbd31184523": { - "balance": "0x200000000000000000000000000000000000000000000000000000000000000" - } - } -} diff --git a/params/parity.json.d/ropsten_difficulty_test.json b/params/parity.json.d/ropsten_difficulty_test.json old mode 100644 new mode 100755 index 3e01ba33bb..4ec64f4f71 --- a/params/parity.json.d/ropsten_difficulty_test.json +++ b/params/parity.json.d/ropsten_difficulty_test.json @@ -8,24 +8,140 @@ "difficultyBoundDivisor": "0x800", "durationLimit": "0xd", "blockReward": { - "0x0": "0x4563918244f40000", "0x19f0a0": "0x29a2241af62c0000", "0x408b70": "0x1bc16d674ec80000" }, "difficultyBombDelays": { "0x19f0a0": "0x2dc6c0", - "0x408b70": "0x1e8480" + "0x408b70": "0x1e8480", + "0x6c993d": "0x3d0900" }, "homesteadTransition": "0x0", "eip100bTransition": "0x19f0a0", + "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", + "daoHardforkAccounts": [ + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "0xecd135fa4f61a655311e86238c92adcd779555d2", + "0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "0x319f70bab6845585f412ec7724b744fec6095c85", + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "0x5c8536898fbb74fc7445814902fd08422eac56d0", + "0x6966ab0d485353095148a2155858910e0965b6f9", + "0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "0x9c50426be05db97f5d64fc54bf89eff947f0a321", + "0x200450f06520bdd6c527622a273333384d870efb", + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "0xf1385fb24aad0cd7432824085e42aff90886fef5", + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "0x24c4d950dfd4dd1902bbed3508144a54542bba94", + "0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "0x492ea3bb0f3315521c31f273e565b868fc090f17", + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", + "0xcc34673c6c40e791051898567a1222daf90be287", + "0x579a80d909f346fbfb1189493f521d7f48d52238", + "0xe308bd1ac5fda103967359b2712dd89deffb7973", + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "0xac1ecab32727358dba8962a0f3b261731aad9723", + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", + "0x440c59b325d2997a134c2c7c60a8c61611212bad", + "0x4486a3d68fac6967006d7a517b889fd3f98c102b", + "0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "0xb9637156d330c0d605a791f1c31ba5890582fe1c", + "0x6131c42fa982e56929107413a9d526fd99405560", + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", + "0x542a9515200d14b68e934e9830d91645a980dd7a", + "0xc4bbd073882dd2add2424cf47d35213405b01324", + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "0x97f43a37f595ab5dd318fb46e7a155eae057317a", + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "0x4863226780fe7c0356454236d3b1c8792785748d", + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "0x057b56736d32b86616a10f619859c6cd6f59092a", + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "0x304a554a310c7e546dfe434669c62820b7d83490", + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "0x4deb0033bb26bc534b197e61d19e0733e5679784", + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "0x4fa802324e929786dbda3b8820dc7834e9134a2a", + "0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "0x12e626b0eebfe86a56d633b9864e389b45dcb260", + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", + "0xbc07118b9ac290e4622f5e77a0853539789effbe", + "0x47e7aa56d6bdf3f36be34619660de61275420af8", + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", + "0x5524c55fb03cf21f549444ccbecb664d0acad706", + "0x40b803a9abce16f50f36a77ba41180eb90023925", + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "0x17802f43a0137c506ba92291391a8a8f207f487d", + "0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "0xb136707642a4ea12fb4bae820f03d2562ebff487", + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "0xf14c14075d6c4ed84b86798af0956deef67365b5", + "0xca544e5c4687d109611d0f8f928b53a25af72448", + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "0xd343b217de44030afaa275f54d31a9317c7f441e", + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "0xda2fef9e4a3230988ff17df2165440f37e8b1708", + "0xf4c64518ea10f995918a454158c6b61407ea345c", + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + ], "bombDefuseTransition": null } }, "Clique": { - "params": { - "period": null, - "epoch": null - } + "params": {} } }, "params": { @@ -37,7 +153,6 @@ "chainID": "0x3", "maxCodeSize": "0x6000", "maxCodeSizeTransition": "0xa", - "eip98Transition": "0x7fffffffffffffff", "eip150Transition": "0x0", "eip160Transition": "0xa", "eip161abcTransition": "0xa", @@ -55,7 +170,13 @@ "eip1283ReenableTransition": "0x62f756", "eip1344Transition": "0x62f756", "eip1884Transition": "0x62f756", - "eip2028Transition": "0x62f756" + "eip2028Transition": "0x62f756", + "eip2929Transition": "0x95b8dd", + "eip2930Transition": "0x95b8dd", + "eip2565Transition": "0x95b8dd", + "eip2718Transition": "0x95b8dd", + "forkBlock": "0x0", + "forkCanonHash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d" }, "genesis": { "seal": { @@ -81,9 +202,13 @@ "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0x0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -93,9 +218,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0x0": { + "price": { + "linear": { + "base": 60, + "word": 2 + } + } } } } @@ -105,9 +234,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0x0": { + "price": { + "linear": { + "base": 600, + "word": 1 + } + } } } } @@ -117,9 +250,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0x0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } @@ -129,11 +266,21 @@ "builtin": { "name": "modexp", "pricing": { - "modexp": { - "divisor": 20 + "0x19f0a0": { + "price": { + "modexp": { + "divisor": 20 + } + } + }, + "0x19f0a0": { + "price": { + "modexp": { + "divisor": 20 + } + } } - }, - "activate_at": "0x19f0a0" + } } }, "0000000000000000000000000000000000000006": { @@ -148,6 +295,20 @@ } } }, + "0x19f0a0": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x62f756": { + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + }, "0x62f756": { "price": { "alt_bn128_const_operations": { @@ -170,6 +331,20 @@ } } }, + "0x19f0a0": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x62f756": { + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + }, "0x62f756": { "price": { "alt_bn128_const_operations": { @@ -193,6 +368,22 @@ } } }, + "0x19f0a0": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x62f756": { + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + }, "0x62f756": { "price": { "alt_bn128_pairing": { @@ -209,11 +400,21 @@ "builtin": { "name": "blake2_f", "pricing": { - "blake2_f": { - "gas_per_round": 1 + "0x62f756": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + }, + "0x62f756": { + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } } - }, - "activate_at": "0x62f756" + } } }, "000000000000000000000000000000000000000a": { @@ -955,7 +1156,7 @@ "balance": "0x0" }, "874b54a8bd152966d63f706bae1ffeb0411921e5": { - "balance": "0xc9f2c9cd04674edea40000000" + "balance": "0x4674edea40000000" } } } \ No newline at end of file diff --git a/params/types/coregeth/chain_config.go b/params/types/coregeth/chain_config.go index 241bc6d121..2d313d6d45 100644 --- a/params/types/coregeth/chain_config.go +++ b/params/types/coregeth/chain_config.go @@ -180,10 +180,19 @@ type CoreGethChainConfig struct { // https://eips.ethereum.org/EIPS/eip-2315 EIP2315FBlock *big.Int `json:"eip2315FBlock,omitempty"` + // TODO: Document me. + EIP2565FBlock *big.Int `json:"eip2565FBlock,omitempty"` + + // EIP2718FBlock is typed tx envelopes + EIP2718FBlock *big.Int `json:"eip2718FBlock,omitempty"` + // EIP-2929: Gas cost increases for state access opcodes // https://eips.ethereum.org/EIPS/eip-2929 EIP2929FBlock *big.Int `json:"eip2929FBlock,omitempty"` + // TODO: Document me. + EIP2930FBlock *big.Int `json:"eip2930FBlock,omitempty"` + DisposalBlock *big.Int `json:"disposalBlock,omitempty"` // Bomb disposal HF block // Various consensus engines diff --git a/params/types/coregeth/chain_config_configurator.go b/params/types/coregeth/chain_config_configurator.go index 7f5ae56f64..ac77249516 100644 --- a/params/types/coregeth/chain_config_configurator.go +++ b/params/types/coregeth/chain_config_configurator.go @@ -408,6 +408,33 @@ func (c *CoreGethChainConfig) SetEIP2929Transition(n *uint64) error { return nil } +func (c *CoreGethChainConfig) GetEIP2930Transition() *uint64 { + return bigNewU64(c.EIP2930FBlock) +} + +func (c *CoreGethChainConfig) SetEIP2930Transition(n *uint64) error { + c.EIP2930FBlock = setBig(c.EIP2930FBlock, n) + return nil +} + +func (c *CoreGethChainConfig) GetEIP2565Transition() *uint64 { + return bigNewU64(c.EIP2565FBlock) +} + +func (c *CoreGethChainConfig) SetEIP2565Transition(n *uint64) error { + c.EIP2565FBlock = setBig(c.EIP2565FBlock, n) + return nil +} + +func (c *CoreGethChainConfig) GetEIP2718Transition() *uint64 { + return bigNewU64(c.EIP2718FBlock) +} + +func (c *CoreGethChainConfig) SetEIP2718Transition(n *uint64) error { + c.EIP2718FBlock = setBig(c.EIP2718FBlock, n) + return nil +} + func (c *CoreGethChainConfig) IsEnabled(fn func() *uint64, n *big.Int) bool { f := fn() if f == nil || n == nil { @@ -558,6 +585,9 @@ func (c *CoreGethChainConfig) GetEthashEIP649Transition() *uint64 { vars.EIP649DifficultyBombDelay, vars.EIP649FBlockReward, ) + if diffN == nil { + diffN = c.GetEthashEIP1234Transition() + } return diffN } @@ -573,6 +603,12 @@ func (c *CoreGethChainConfig) SetEthashEIP649Transition(n *uint64) error { return nil } + if eip1234 := c.GetEthashEIP1234Transition(); eip1234 != nil { + if *eip1234 <= *n { + return nil + } + } + c.ensureExistingRewardSchedule() c.BlockRewardSchedule[*n] = vars.EIP649FBlockReward diff --git a/params/types/ctypes/configurator_iface.go b/params/types/ctypes/configurator_iface.go index 15283bd68b..bd39768867 100644 --- a/params/types/ctypes/configurator_iface.go +++ b/params/types/ctypes/configurator_iface.go @@ -134,8 +134,22 @@ type ProtocolSpecifier interface { SetECBP1100Transition(n *uint64) error GetEIP2315Transition() *uint64 SetEIP2315Transition(n *uint64) error + + // ModExp gas cost + GetEIP2565Transition() *uint64 + SetEIP2565Transition(n *uint64) error + + // Gas cost increases for state access opcodes GetEIP2929Transition() *uint64 SetEIP2929Transition(n *uint64) error + + // Optional access lists + GetEIP2930Transition() *uint64 + SetEIP2930Transition(n *uint64) error + + // Typed transaction envelope + GetEIP2718Transition() *uint64 + SetEIP2718Transition(n *uint64) error } type Forker interface { diff --git a/params/types/ctypes/types_test.go b/params/types/ctypes/types_test.go index a194d6cfe6..a41eb29d63 100644 --- a/params/types/ctypes/types_test.go +++ b/params/types/ctypes/types_test.go @@ -247,3 +247,34 @@ func TestUint64BigMapEncodesHex_SetValueTotalForHeight(t *testing.T) { mgTestlike.SetValueTotalForHeight(&five, vars.EIP1234DifficultyBombDelay) check(mgTestlike, mgTestlike.SumValues(&zero), vars.EIP649DifficultyBombDelay.Uint64()) } + +func TestMapMeetsSpecification_1234(t *testing.T) { + data := `{ + "difficultyBombDelays": { + "0x0": "0x4c4b40" + }, + "blockReward": { + "0x0": "0x1bc16d674ec80000" + }}` + + im := struct { + DifficultyBombDelaySchedule Uint64BigMapEncodesHex `json:"difficultyBombDelays,omitempty"` // JSON tag matches Parity's + BlockRewardSchedule Uint64BigMapEncodesHex `json:"blockReward,omitempty"` // JSON tag matches Parity's + }{} + err := json.Unmarshal([]byte(data), &im) + if err != nil { + t.Fatal(err) + } + + n := MapMeetsSpecification( + im.DifficultyBombDelaySchedule, + im.BlockRewardSchedule, + vars.EIP1234DifficultyBombDelay, + vars.EIP1234FBlockReward, + ) + if n == nil || *n != 0 { + t.Fatal("n should be 0", n) + } + + t.Logf("%v n=%v", im, n) +} diff --git a/params/types/genesisT/genesis.go b/params/types/genesisT/genesis.go index 888e9a973c..18dcf581c5 100644 --- a/params/types/genesisT/genesis.go +++ b/params/types/genesisT/genesis.go @@ -560,6 +560,30 @@ func (g *Genesis) SetEIP2929Transition(n *uint64) error { return g.Config.SetEIP2929Transition(n) } +func (g *Genesis) GetEIP2930Transition() *uint64 { + return g.Config.GetEIP2930Transition() +} + +func (g *Genesis) SetEIP2930Transition(n *uint64) error { + return g.Config.SetEIP2930Transition(n) +} + +func (g *Genesis) GetEIP2565Transition() *uint64 { + return g.Config.GetEIP2565Transition() +} + +func (g *Genesis) SetEIP2565Transition(n *uint64) error { + return g.Config.SetEIP2565Transition(n) +} + +func (g *Genesis) GetEIP2718Transition() *uint64 { + return g.Config.GetEIP2718Transition() +} + +func (g *Genesis) SetEIP2718Transition(n *uint64) error { + return g.Config.SetEIP2718Transition(n) +} + func (g *Genesis) GetECBP1100Transition() *uint64 { return g.Config.GetECBP1100Transition() } diff --git a/params/types/goethereum/goethereum.go b/params/types/goethereum/goethereum.go index ea78a53aa7..0ec3ec9269 100644 --- a/params/types/goethereum/goethereum.go +++ b/params/types/goethereum/goethereum.go @@ -56,7 +56,8 @@ type ChainConfig struct { IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul) MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated) - YoloV2Block *big.Int `json:"yoloV2Block,omitempty"` // YOLO v2 (Ephemeral testnet) + BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block + YoloV3Block *big.Int `json:"yoloV3Block,omitempty"` // YOLO v3 (Ephemeral testnet) EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated) @@ -86,7 +87,7 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v ConstantinopleFix: %v Istanbul: %v MuirGlacier: %v Engine: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v ConstantinopleFix: %v Istanbul: %v MuirGlacier: %v Berlin: %v Engine: %v}", c.ChainID, c.HomesteadBlock, c.DAOForkBlock, @@ -99,6 +100,7 @@ func (c *ChainConfig) String() string { c.PetersburgBlock, c.IstanbulBlock, c.MuirGlacierBlock, + c.BerlinBlock, engine, ) } diff --git a/params/types/goethereum/goethereum_configurator.go b/params/types/goethereum/goethereum_configurator.go index 1f13aeba03..df6d29cf38 100644 --- a/params/types/goethereum/goethereum_configurator.go +++ b/params/types/goethereum/goethereum_configurator.go @@ -52,6 +52,19 @@ func setBig(i *big.Int, u *uint64) *big.Int { return i } +func bigNewU64Min(i, j *big.Int) *uint64 { + if i == nil { + return bigNewU64(j) + } + if j == nil { + return bigNewU64(i) + } + if j.Cmp(i) < 0 { + return bigNewU64(j) + } + return bigNewU64(i) +} + func (c *ChainConfig) GetAccountStartNonce() *uint64 { return internal.GlobalConfigurator().GetAccountStartNonce() } @@ -383,11 +396,11 @@ func (c *ChainConfig) SetEIP1706Transition(n *uint64) error { } func (c *ChainConfig) GetEIP2537Transition() *uint64 { - return bigNewU64(c.YoloV2Block) + return bigNewU64(c.YoloV3Block) } func (c *ChainConfig) SetEIP2537Transition(n *uint64) error { - c.YoloV2Block = setBig(c.YoloV2Block, n) + c.YoloV3Block = setBig(c.YoloV3Block, n) return nil } @@ -401,20 +414,47 @@ func (c *ChainConfig) SetECBP1100Transition(n *uint64) error { } func (c *ChainConfig) GetEIP2315Transition() *uint64 { - return bigNewU64(c.YoloV2Block) + return bigNewU64(c.YoloV3Block) } func (c *ChainConfig) SetEIP2315Transition(n *uint64) error { - c.YoloV2Block = setBig(c.YoloV2Block, n) + c.YoloV3Block = setBig(c.YoloV3Block, n) return nil } func (c *ChainConfig) GetEIP2929Transition() *uint64 { - return bigNewU64(c.YoloV2Block) + return bigNewU64Min(c.YoloV3Block, c.BerlinBlock) } func (c *ChainConfig) SetEIP2929Transition(n *uint64) error { - c.YoloV2Block = setBig(c.YoloV2Block, n) + c.BerlinBlock = setBig(c.BerlinBlock, n) + return nil +} + +func (c *ChainConfig) GetEIP2930Transition() *uint64 { + return bigNewU64Min(c.YoloV3Block, c.BerlinBlock) +} + +func (c *ChainConfig) SetEIP2930Transition(n *uint64) error { + c.BerlinBlock = setBig(c.BerlinBlock, n) + return nil +} + +func (c *ChainConfig) GetEIP2565Transition() *uint64 { + return bigNewU64Min(c.YoloV3Block, c.BerlinBlock) +} + +func (c *ChainConfig) SetEIP2565Transition(n *uint64) error { + c.BerlinBlock = setBig(c.BerlinBlock, n) + return nil +} + +func (c *ChainConfig) GetEIP2718Transition() *uint64 { + return bigNewU64Min(c.YoloV3Block, c.BerlinBlock) +} + +func (c *ChainConfig) SetEIP2718Transition(n *uint64) error { + c.BerlinBlock = setBig(c.BerlinBlock, n) return nil } diff --git a/params/types/multigeth/multigethv0_chain_config.go b/params/types/multigeth/multigethv0_chain_config.go index f1e091c808..d3ec6ad012 100644 --- a/params/types/multigeth/multigethv0_chain_config.go +++ b/params/types/multigeth/multigethv0_chain_config.go @@ -34,8 +34,10 @@ type ChainConfig struct { IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul) MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated) - YoloV2Block *big.Int `json:"yoloV2Block,omitempty"` // YOLO v2 (Ephemeral testnet) - EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated) + BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block + YoloV3Block *big.Int `json:"yoloV3Block,omitempty"` // YOLO v3 (Ephemeral testnet) + + EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated) // // EXP cost increase diff --git a/params/types/multigeth/multigethv0_chain_config_configurator.go b/params/types/multigeth/multigethv0_chain_config_configurator.go index e22e15138a..5958d98714 100644 --- a/params/types/multigeth/multigethv0_chain_config_configurator.go +++ b/params/types/multigeth/multigethv0_chain_config_configurator.go @@ -20,6 +20,19 @@ func bigNewU64(i *big.Int) *uint64 { return newU64(i.Uint64()) } +func bigNewU64Min(i, j *big.Int) *uint64 { + if i == nil { + return bigNewU64(j) + } + if j == nil { + return bigNewU64(i) + } + if j.Cmp(i) < 0 { + return bigNewU64(j) + } + return bigNewU64(i) +} + func setBig(i *big.Int, u *uint64) *big.Int { if u == nil { return nil @@ -300,6 +313,8 @@ func (c *ChainConfig) SetEIP1052Transition(n *uint64) error { } func (c *ChainConfig) GetEIP1283Transition() *uint64 { + // This is special case handling for multigeth configuration. + // It makes the tests pass. if c.ConstantinopleBlock != nil && c.PetersburgBlock != nil { if c.ConstantinopleBlock.Cmp(c.PetersburgBlock) == 0 { return nil @@ -428,20 +443,48 @@ func (c *ChainConfig) SetECBP1100Transition(n *uint64) error { } func (c *ChainConfig) GetEIP2315Transition() *uint64 { - return bigNewU64(c.YoloV2Block) + return bigNewU64(c.YoloV3Block) } func (c *ChainConfig) SetEIP2315Transition(n *uint64) error { - c.YoloV2Block = setBig(c.YoloV2Block, n) + c.YoloV3Block = setBig(c.YoloV3Block, n) return nil } func (c *ChainConfig) GetEIP2929Transition() *uint64 { - return bigNewU64(c.YoloV2Block) + return bigNewU64Min(c.YoloV3Block, c.BerlinBlock) } +// FIXME: Assigning BerlinBlock foregoes setting YoloV3. func (c *ChainConfig) SetEIP2929Transition(n *uint64) error { - c.YoloV2Block = setBig(c.YoloV2Block, n) + c.BerlinBlock = setBig(c.BerlinBlock, n) + return nil +} + +func (c *ChainConfig) GetEIP2930Transition() *uint64 { + return bigNewU64Min(c.YoloV3Block, c.BerlinBlock) +} + +func (c *ChainConfig) SetEIP2930Transition(n *uint64) error { + c.BerlinBlock = setBig(c.BerlinBlock, n) + return nil +} + +func (c *ChainConfig) GetEIP2565Transition() *uint64 { + return bigNewU64Min(c.YoloV3Block, c.BerlinBlock) +} + +func (c *ChainConfig) SetEIP2565Transition(n *uint64) error { + c.BerlinBlock = setBig(c.BerlinBlock, n) + return nil +} + +func (c *ChainConfig) GetEIP2718Transition() *uint64 { + return bigNewU64Min(c.YoloV3Block, c.BerlinBlock) +} + +func (c *ChainConfig) SetEIP2718Transition(n *uint64) error { + c.BerlinBlock = setBig(c.BerlinBlock, n) return nil } diff --git a/params/types/parity/parity.go b/params/types/parity/parity.go index 4b0cba0537..c7fedcc366 100644 --- a/params/types/parity/parity.go +++ b/params/types/parity/parity.go @@ -108,9 +108,12 @@ type ParityChainSpec struct { EIP2028Transition *ParityU64 `json:"eip2028Transition,omitempty"` EIP2315Transition *ParityU64 `json:"eip2315Transition,omitempty"` EIP2537Transition *ParityU64 `json:"eip2537Transition,omitempty"` - EIP1706Transition *ParityU64 `json:"-"` // FIXME, when and if i'm implemented in Parity - EIP2929Transition *ParityU64 `json:"-"` // FIXME, when and if i'm implemented in Parity - ECIP1080Transition *ParityU64 `json:"-"` // FIXME, when and if i'm implemented in Parity + EIP1706Transition *ParityU64 `json:"eip1706Transition,omitempty"` // FIXME, when and if i'm implemented in Parity + EIP2929Transition *ParityU64 `json:"eip2929Transition,omitempty"` // FIXME, when and if i'm implemented in Parity + EIP2930Transition *ParityU64 `json:"eip2930Transition,omitempty"` // FIXME, when and if i'm implemented in Parity + EIP2565Transition *ParityU64 `json:"eip2565Transition,omitempty"` // FIXME, when and if i'm implemented in Parity + EIP2718Transition *ParityU64 `json:"eip2718Transition,omitempty"` // FIXME, when and if i'm implemented in Parity + ECIP1080Transition *ParityU64 `json:"ecip1080Transition,omitempty"` // FIXME, when and if i'm implemented in Parity ForkBlock *ParityU64 `json:"forkBlock,omitempty"` ForkCanonHash *common.Hash `json:"forkCanonHash,omitempty"` diff --git a/params/types/parity/parity_configurator.go b/params/types/parity/parity_configurator.go index d8459dc58f..afb9b9e682 100644 --- a/params/types/parity/parity_configurator.go +++ b/params/types/parity/parity_configurator.go @@ -995,14 +995,39 @@ func (spec *ParityChainSpec) SetEIP2315Transition(i *uint64) error { } func (spec *ParityChainSpec) GetEIP2929Transition() *uint64 { - return nil + return spec.Params.EIP2929Transition.Uint64P() } func (spec *ParityChainSpec) SetEIP2929Transition(n *uint64) error { - if n == nil { - return nil - } - return ctypes.ErrUnsupportedConfigFatal + spec.Params.EIP2929Transition = new(ParityU64).SetUint64(n) + return nil +} + +func (spec *ParityChainSpec) GetEIP2930Transition() *uint64 { + return spec.Params.EIP2930Transition.Uint64P() +} + +func (spec *ParityChainSpec) SetEIP2930Transition(n *uint64) error { + spec.Params.EIP2930Transition = new(ParityU64).SetUint64(n) + return nil +} + +func (spec *ParityChainSpec) GetEIP2565Transition() *uint64 { + return spec.Params.EIP2565Transition.Uint64P() +} + +func (spec *ParityChainSpec) SetEIP2565Transition(n *uint64) error { + spec.Params.EIP2565Transition = new(ParityU64).SetUint64(n) + return nil +} + +func (spec *ParityChainSpec) GetEIP2718Transition() *uint64 { + return spec.Params.EIP2718Transition.Uint64P() +} + +func (spec *ParityChainSpec) SetEIP2718Transition(n *uint64) error { + spec.Params.EIP2718Transition = new(ParityU64).SetUint64(n) + return nil } func (spec *ParityChainSpec) GetEthashDifficultyBombDelaySchedule() ctypes.Uint64BigMapEncodesHex { diff --git a/params/types/tests/parity_test.go b/params/types/tests/parity_test.go new file mode 100644 index 0000000000..c7d3af99f3 --- /dev/null +++ b/params/types/tests/parity_test.go @@ -0,0 +1,51 @@ +package tests + +import ( + "testing" + + "github.com/ethereum/go-ethereum/params/confp" + "github.com/ethereum/go-ethereum/params/types/coregeth" + "github.com/ethereum/go-ethereum/params/types/parity" + "github.com/ethereum/go-ethereum/tests" + "github.com/go-test/deep" +) + +func TestParityChainspec_CoreGeth(t *testing.T) { + coregethA := tests.Forks["ETC_Magneto"] + + pspec := &parity.ParityChainSpec{} + err := confp.Convert(coregethA, pspec) + if err != nil { + t.Fatal(err) + } + + err = confp.Equivalent(coregethA, pspec) + if err != nil { + t.Fatal(err) + } + + coregethB := &coregeth.CoreGethChainConfig{} + err = confp.Convert(pspec, coregethB) + if err != nil { + t.Fatal(err) + } + + diff := deep.Equal(coregethA, coregethB) + for _, line := range diff { + t.Error(line) + } + + eip1108a := coregethA.GetEIP1108Transition() + eip1108b := coregethB.GetEIP1108Transition() + + ne := eip1108a == nil && eip1108b != nil + ne = ne || eip1108a != nil && eip1108b == nil + ne = ne || *eip1108a != *eip1108b + if ne { + t.Fatal("ne") + } + if eip1108a == nil { + t.Fatal("nil") + } + t.Logf("%v %v", *eip1108a, *eip1108b) +} diff --git a/params/vars/protocol_params.go b/params/vars/protocol_params.go index 8341d10645..a00feb7f67 100644 --- a/params/vars/protocol_params.go +++ b/params/vars/protocol_params.go @@ -74,20 +74,23 @@ var ( JumpdestGas uint64 = 1 // Once per JUMPDEST operation. EpochDuration uint64 = 30000 // Duration between proof-of-work epochs. - CreateDataGas uint64 = 200 // - CallCreateDepth uint64 = 1024 // Maximum depth of call/create stack. - ExpGas uint64 = 10 // Once per EXP instruction - LogGas uint64 = 375 // Per LOG* operation. - CopyGas uint64 = 3 // - StackLimit uint64 = 1024 // Maximum size of VM stack allowed. - TierStepGas uint64 = 0 // Once per operation, for a selection of them. - LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. - CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. - Create2Gas uint64 = 32000 // Once per CREATE2 operation - SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation. - MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. - TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. - TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul) + CreateDataGas uint64 = 200 // + CallCreateDepth uint64 = 1024 // Maximum depth of call/create stack. + ExpGas uint64 = 10 // Once per EXP instruction + LogGas uint64 = 375 // Per LOG* operation. + CopyGas uint64 = 3 // + StackLimit uint64 = 1024 // Maximum size of VM stack allowed. + TierStepGas uint64 = 0 // Once per operation, for a selection of them. + LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. + CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. + Create2Gas uint64 = 32000 // Once per CREATE2 operation + SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation. + MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. + + TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. + TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul) + TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list + TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list // These have been changed during the course of the chain CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction. @@ -130,7 +133,6 @@ var ( Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation IdentityBaseGas uint64 = 15 // Base price for a data copy operation IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation - ModExpQuadCoeffDiv uint64 = 20 // Divisor for the quadratic particle of the big int modular exponentiation Bn256AddGasByzantium uint64 = 500 // Byzantium gas needed for an elliptic curve addition Bn256AddGasIstanbul uint64 = 150 // Gas needed for an elliptic curve addition diff --git a/rlp/decode.go b/rlp/decode.go index 5f3f5eedfd..79b7ef0626 100644 --- a/rlp/decode.go +++ b/rlp/decode.go @@ -952,7 +952,13 @@ func (s *Stream) readFull(buf []byte) (err error) { n += nn } if err == io.EOF { - err = io.ErrUnexpectedEOF + if n < len(buf) { + err = io.ErrUnexpectedEOF + } else { + // Readers are allowed to give EOF even though the read succeeded. + // In such cases, we discard the EOF, like io.ReadFull() does. + err = nil + } } return err } diff --git a/rlp/decode_test.go b/rlp/decode_test.go index 167e9974b9..d94c3969b2 100644 --- a/rlp/decode_test.go +++ b/rlp/decode_test.go @@ -665,6 +665,26 @@ func TestDecodeWithByteReader(t *testing.T) { }) } +func testDecodeWithEncReader(t *testing.T, n int) { + s := strings.Repeat("0", n) + _, r, _ := EncodeToReader(s) + var decoded string + err := Decode(r, &decoded) + if err != nil { + t.Errorf("Unexpected decode error with n=%v: %v", n, err) + } + if decoded != s { + t.Errorf("Decode mismatch with n=%v", n) + } +} + +// This is a regression test checking that decoding from encReader +// works for RLP values of size 8192 bytes or more. +func TestDecodeWithEncReader(t *testing.T) { + testDecodeWithEncReader(t, 8188) // length with header is 8191 + testDecodeWithEncReader(t, 8189) // length with header is 8192 +} + // plainReader reads from a byte slice but does not // implement ReadByte. It is also not recognized by the // size validation. This is useful to test how the decoder diff --git a/rlp/encode_test.go b/rlp/encode_test.go index 6803745197..418ee10a35 100644 --- a/rlp/encode_test.go +++ b/rlp/encode_test.go @@ -39,9 +39,8 @@ func (e *testEncoder) EncodeRLP(w io.Writer) error { } if e.err != nil { return e.err - } else { - w.Write([]byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 1}) } + w.Write([]byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 1}) return nil } diff --git a/rlp/iterator.go b/rlp/iterator.go index c28866dbc1..559e03a868 100644 --- a/rlp/iterator.go +++ b/rlp/iterator.go @@ -23,6 +23,7 @@ type listIterator struct { } // NewListIterator creates an iterator for the (list) represented by data +// TODO: Consider removing this implementation, as it is no longer used. func NewListIterator(data RawValue) (*listIterator, error) { k, t, c, err := readKind(data) if err != nil { diff --git a/rpc/client.go b/rpc/client.go index 91e68e73e6..198ce63573 100644 --- a/rpc/client.go +++ b/rpc/client.go @@ -404,9 +404,8 @@ func (c *Client) Notify(ctx context.Context, method string, args ...interface{}) if c.isHTTP { return c.sendHTTP(ctx, op, msg) - } else { - return c.send(ctx, op, msg) } + return c.send(ctx, op, msg) } // EthSubscribe registers a subscripion under the "eth" namespace. @@ -415,6 +414,7 @@ func (c *Client) EthSubscribe(ctx context.Context, channel interface{}, args ... } // ShhSubscribe registers a subscripion under the "shh" namespace. +// Deprecated: use Subscribe(ctx, "shh", ...). func (c *Client) ShhSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) { return c.Subscribe(ctx, "shh", channel, args...) } diff --git a/rpc/client_test.go b/rpc/client_test.go index 5b1f960352..224eb0c5c8 100644 --- a/rpc/client_test.go +++ b/rpc/client_test.go @@ -18,6 +18,7 @@ package rpc import ( "context" + "encoding/json" "fmt" "math/rand" "net" @@ -376,6 +377,93 @@ func TestClientCloseUnsubscribeRace(t *testing.T) { } } +// unsubscribeRecorder collects the subscription IDs of *_unsubscribe calls. +type unsubscribeRecorder struct { + ServerCodec + unsubscribes map[string]bool +} + +func (r *unsubscribeRecorder) readBatch() ([]*jsonrpcMessage, bool, error) { + if r.unsubscribes == nil { + r.unsubscribes = make(map[string]bool) + } + + msgs, batch, err := r.ServerCodec.readBatch() + for _, msg := range msgs { + if msg.isUnsubscribe() { + var params []string + if err := json.Unmarshal(msg.Params, ¶ms); err != nil { + panic("unsubscribe decode error: " + err.Error()) + } + r.unsubscribes[params[0]] = true + } + } + return msgs, batch, err +} + +// This checks that Client calls the _unsubscribe method on the server when Unsubscribe is +// called on a subscription. +func TestClientSubscriptionUnsubscribeServer(t *testing.T) { + t.Parallel() + + // Create the server. + srv := NewServer() + srv.RegisterName("nftest", new(notificationTestService)) + p1, p2 := net.Pipe() + recorder := &unsubscribeRecorder{ServerCodec: NewCodec(p1)} + go srv.ServeCodec(recorder, OptionMethodInvocation|OptionSubscriptions) + defer srv.Stop() + + // Create the client on the other end of the pipe. + client, _ := newClient(context.Background(), func(context.Context) (ServerCodec, error) { + return NewCodec(p2), nil + }) + defer client.Close() + + // Create the subscription. + ch := make(chan int) + sub, err := client.Subscribe(context.Background(), "nftest", ch, "someSubscription", 1, 1) + if err != nil { + t.Fatal(err) + } + + // Unsubscribe and check that unsubscribe was called. + sub.Unsubscribe() + if !recorder.unsubscribes[sub.subid] { + t.Fatal("client did not call unsubscribe method") + } + if _, open := <-sub.Err(); open { + t.Fatal("subscription error channel not closed after unsubscribe") + } +} + +// This checks that the subscribed channel can be closed after Unsubscribe. +// It is the reproducer for https://github.com/ethereum/go-ethereum/issues/22322 +func TestClientSubscriptionChannelClose(t *testing.T) { + t.Parallel() + + var ( + srv = NewServer() + httpsrv = httptest.NewServer(srv.WebsocketHandler(nil)) + wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") + ) + defer srv.Stop() + defer httpsrv.Close() + + srv.RegisterName("nftest", new(notificationTestService)) + client, _ := Dial(wsURL) + + for i := 0; i < 100; i++ { + ch := make(chan int, 100) + sub, err := client.Subscribe(context.Background(), "nftest", ch, "someSubscription", maxClientSubscriptionBuffer-1, 1) + if err != nil { + t.Fatal(err) + } + sub.Unsubscribe() + close(ch) + } +} + // This test checks that Client doesn't lock up when a single subscriber // doesn't read subscription events. func TestClientNotificationStorm(t *testing.T) { @@ -427,7 +515,7 @@ func TestClientNotificationStorm(t *testing.T) { } doTest(8000, false) - doTest(23000, true) + doTest(24000, true) } func TestClientSetHeader(t *testing.T) { diff --git a/rpc/endpoints.go b/rpc/endpoints.go index 9fc0705172..d78ebe2858 100644 --- a/rpc/endpoints.go +++ b/rpc/endpoints.go @@ -18,6 +18,7 @@ package rpc import ( "net" + "strings" "github.com/ethereum/go-ethereum/log" ) @@ -25,13 +26,22 @@ import ( // StartIPCEndpoint starts an IPC endpoint. func StartIPCEndpoint(ipcEndpoint string, apis []API) (net.Listener, *Server, error) { // Register all the APIs exposed by the services. - handler := NewServer() + var ( + handler = NewServer() + regMap = make(map[string]struct{}) + registered []string + ) for _, api := range apis { if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + log.Info("IPC registration failed", "namespace", api.Namespace, "error", err) return nil, nil, err } - log.Debug("IPC registered", "namespace", api.Namespace) + if _, ok := regMap[api.Namespace]; !ok { + registered = append(registered, api.Namespace) + regMap[api.Namespace] = struct{}{} + } } + log.Debug("IPCs registered", "namespaces", strings.Join(registered, ",")) // All APIs registered, start the IPC listener. listener, err := ipcListen(ipcEndpoint) if err != nil { diff --git a/rpc/handler.go b/rpc/handler.go index 23023eaca1..85f774a1b7 100644 --- a/rpc/handler.go +++ b/rpc/handler.go @@ -189,7 +189,7 @@ func (h *handler) cancelAllRequests(err error, inflightReq *requestOp) { } for id, sub := range h.clientSubs { delete(h.clientSubs, id) - sub.quitWithError(false, err) + sub.close(err) } } @@ -281,7 +281,7 @@ func (h *handler) handleResponse(msg *jsonrpcMessage) { return } if op.err = json.Unmarshal(msg.Result, &op.sub.subid); op.err == nil { - go op.sub.start() + go op.sub.run() h.clientSubs[op.sub.subid] = op.sub } } diff --git a/rpc/http_test.go b/rpc/http_test.go index fc939ae48f..b75af67c52 100644 --- a/rpc/http_test.go +++ b/rpc/http_test.go @@ -98,3 +98,28 @@ func confirmHTTPRequestYieldsStatusCode(t *testing.T, method, contentType, body func TestHTTPResponseWithEmptyGet(t *testing.T) { confirmHTTPRequestYieldsStatusCode(t, http.MethodGet, "", "", http.StatusOK) } + +// This checks that maxRequestContentLength is not applied to the response of a request. +func TestHTTPRespBodyUnlimited(t *testing.T) { + const respLength = maxRequestContentLength * 3 + + s := NewServer() + defer s.Stop() + s.RegisterName("test", largeRespService{respLength}) + ts := httptest.NewServer(s) + defer ts.Close() + + c, err := DialHTTP(ts.URL) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + var r string + if err := c.Call(&r, "test_largeResp"); err != nil { + t.Fatal(err) + } + if len(r) != respLength { + t.Fatalf("response has wrong length %d, want %d", len(r), respLength) + } +} diff --git a/rpc/subscription.go b/rpc/subscription.go index 233215d792..942e764e5d 100644 --- a/rpc/subscription.go +++ b/rpc/subscription.go @@ -208,23 +208,37 @@ type ClientSubscription struct { channel reflect.Value namespace string subid string - in chan json.RawMessage - quitOnce sync.Once // ensures quit is closed once - quit chan struct{} // quit is closed when the subscription exits - errOnce sync.Once // ensures err is closed once - err chan error + // The in channel receives notification values from client dispatcher. + in chan json.RawMessage + + // The error channel receives the error from the forwarding loop. + // It is closed by Unsubscribe. + err chan error + errOnce sync.Once + + // Closing of the subscription is requested by sending on 'quit'. This is handled by + // the forwarding loop, which closes 'forwardDone' when it has stopped sending to + // sub.channel. Finally, 'unsubDone' is closed after unsubscribing on the server side. + quit chan error + forwardDone chan struct{} + unsubDone chan struct{} } +// This is the sentinel value sent on sub.quit when Unsubscribe is called. +var errUnsubscribed = errors.New("unsubscribed") + func newClientSubscription(c *Client, namespace string, channel reflect.Value) *ClientSubscription { sub := &ClientSubscription{ - client: c, - namespace: namespace, - etype: channel.Type().Elem(), - channel: channel, - quit: make(chan struct{}), - err: make(chan error, 1), - in: make(chan json.RawMessage), + client: c, + namespace: namespace, + etype: channel.Type().Elem(), + channel: channel, + in: make(chan json.RawMessage), + quit: make(chan error), + forwardDone: make(chan struct{}), + unsubDone: make(chan struct{}), + err: make(chan error, 1), } return sub } @@ -232,9 +246,9 @@ func newClientSubscription(c *Client, namespace string, channel reflect.Value) * // Err returns the subscription error channel. The intended use of Err is to schedule // resubscription when the client connection is closed unexpectedly. // -// The error channel receives a value when the subscription has ended due -// to an error. The received error is nil if Close has been called -// on the underlying client and no other error has occurred. +// The error channel receives a value when the subscription has ended due to an error. The +// received error is nil if Close has been called on the underlying client and no other +// error has occurred. // // The error channel is closed when Unsubscribe is called on the subscription. func (sub *ClientSubscription) Err() <-chan error { @@ -244,41 +258,63 @@ func (sub *ClientSubscription) Err() <-chan error { // Unsubscribe unsubscribes the notification and closes the error channel. // It can safely be called more than once. func (sub *ClientSubscription) Unsubscribe() { - sub.quitWithError(true, nil) - sub.errOnce.Do(func() { close(sub.err) }) -} - -func (sub *ClientSubscription) quitWithError(unsubscribeServer bool, err error) { - sub.quitOnce.Do(func() { - // The dispatch loop won't be able to execute the unsubscribe call - // if it is blocked on deliver. Close sub.quit first because it - // unblocks deliver. - close(sub.quit) - if unsubscribeServer { - sub.requestUnsubscribe() - } - if err != nil { - if err == ErrClientQuit { - err = nil // Adhere to subscription semantics. - } - sub.err <- err + sub.errOnce.Do(func() { + select { + case sub.quit <- errUnsubscribed: + <-sub.unsubDone + case <-sub.unsubDone: } + close(sub.err) }) } +// deliver is called by the client's message dispatcher to send a notification value. func (sub *ClientSubscription) deliver(result json.RawMessage) (ok bool) { select { case sub.in <- result: return true - case <-sub.quit: + case <-sub.forwardDone: return false } } -func (sub *ClientSubscription) start() { - sub.quitWithError(sub.forward()) +// close is called by the client's message dispatcher when the connection is closed. +func (sub *ClientSubscription) close(err error) { + select { + case sub.quit <- err: + case <-sub.forwardDone: + } +} + +// run is the forwarding loop of the subscription. It runs in its own goroutine and +// is launched by the client's handler after the subscription has been created. +func (sub *ClientSubscription) run() { + defer close(sub.unsubDone) + + unsubscribe, err := sub.forward() + + // The client's dispatch loop won't be able to execute the unsubscribe call if it is + // blocked in sub.deliver() or sub.close(). Closing forwardDone unblocks them. + close(sub.forwardDone) + + // Call the unsubscribe method on the server. + if unsubscribe { + sub.requestUnsubscribe() + } + + // Send the error. + if err != nil { + if err == ErrClientQuit { + // ErrClientQuit gets here when Client.Close is called. This is reported as a + // nil error because it's not an error, but we can't close sub.err here. + err = nil + } + sub.err <- err + } } +// forward is the forwarding loop. It takes in RPC notifications and sends them +// on the subscription channel. func (sub *ClientSubscription) forward() (unsubscribeServer bool, err error) { cases := []reflect.SelectCase{ {Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.quit)}, @@ -286,7 +322,7 @@ func (sub *ClientSubscription) forward() (unsubscribeServer bool, err error) { {Dir: reflect.SelectSend, Chan: sub.channel}, } buffer := list.New() - defer buffer.Init() + for { var chosen int var recv reflect.Value @@ -301,7 +337,15 @@ func (sub *ClientSubscription) forward() (unsubscribeServer bool, err error) { switch chosen { case 0: // <-sub.quit - return false, nil + if !recv.IsNil() { + err = recv.Interface().(error) + } + if err == errUnsubscribed { + // Exiting because Unsubscribe was called, unsubscribe on server. + return true, nil + } + return false, err + case 1: // <-sub.in val, err := sub.unmarshal(recv.Interface().(json.RawMessage)) if err != nil { @@ -311,6 +355,7 @@ func (sub *ClientSubscription) forward() (unsubscribeServer bool, err error) { return true, ErrSubscriptionQueueOverflow } buffer.PushBack(val) + case 2: // sub.channel<- cases[2].Send = reflect.Value{} // Don't hold onto the value. buffer.Remove(buffer.Front()) diff --git a/rpc/testservice_test.go b/rpc/testservice_test.go index 6f948a1bac..62afc1df44 100644 --- a/rpc/testservice_test.go +++ b/rpc/testservice_test.go @@ -20,6 +20,7 @@ import ( "context" "encoding/binary" "errors" + "strings" "sync" "time" ) @@ -194,3 +195,12 @@ func (s *notificationTestService) HangSubscription(ctx context.Context, val int) }() return subscription, nil } + +// largeRespService generates arbitrary-size JSON responses. +type largeRespService struct { + length int +} + +func (x largeRespService) LargeResp() string { + return strings.Repeat("x", x.length) +} diff --git a/rpc/websocket.go b/rpc/websocket.go index a716383be9..ab55ae69c1 100644 --- a/rpc/websocket.go +++ b/rpc/websocket.go @@ -37,6 +37,7 @@ const ( wsWriteBuffer = 1024 wsPingInterval = 60 * time.Second wsPingWriteTimeout = 5 * time.Second + wsMessageSizeLimit = 15 * 1024 * 1024 ) var wsBufferPool = new(sync.Pool) @@ -75,14 +76,14 @@ func wsHandshakeValidator(allowedOrigins []string) func(*http.Request) bool { allowAllOrigins = true } if origin != "" { - origins.Add(strings.ToLower(origin)) + origins.Add(origin) } } // allow localhost if no allowedOrigins are specified. if len(origins.ToSlice()) == 0 { origins.Add("http://localhost") if hostname, err := os.Hostname(); err == nil { - origins.Add("http://" + strings.ToLower(hostname)) + origins.Add("http://" + hostname) } } log.Debug(fmt.Sprintf("Allowed origin(s) for WS RPC interface %v", origins.ToSlice())) @@ -97,7 +98,7 @@ func wsHandshakeValidator(allowedOrigins []string) func(*http.Request) bool { } // Verify origin against whitelist. origin := strings.ToLower(req.Header.Get("Origin")) - if allowAllOrigins || origins.Contains(origin) { + if allowAllOrigins || originIsAllowed(origins, origin) { return true } log.Warn("Rejected WebSocket connection", "origin", origin) @@ -120,6 +121,65 @@ func (e wsHandshakeError) Error() string { return s } +func originIsAllowed(allowedOrigins mapset.Set, browserOrigin string) bool { + it := allowedOrigins.Iterator() + for origin := range it.C { + if ruleAllowsOrigin(origin.(string), browserOrigin) { + return true + } + } + return false +} + +func ruleAllowsOrigin(allowedOrigin string, browserOrigin string) bool { + var ( + allowedScheme, allowedHostname, allowedPort string + browserScheme, browserHostname, browserPort string + err error + ) + allowedScheme, allowedHostname, allowedPort, err = parseOriginURL(allowedOrigin) + if err != nil { + log.Warn("Error parsing allowed origin specification", "spec", allowedOrigin, "error", err) + return false + } + browserScheme, browserHostname, browserPort, err = parseOriginURL(browserOrigin) + if err != nil { + log.Warn("Error parsing browser 'Origin' field", "Origin", browserOrigin, "error", err) + return false + } + if allowedScheme != "" && allowedScheme != browserScheme { + return false + } + if allowedHostname != "" && allowedHostname != browserHostname { + return false + } + if allowedPort != "" && allowedPort != browserPort { + return false + } + return true +} + +func parseOriginURL(origin string) (string, string, string, error) { + parsedURL, err := url.Parse(strings.ToLower(origin)) + if err != nil { + return "", "", "", err + } + var scheme, hostname, port string + if strings.Contains(origin, "://") { + scheme = parsedURL.Scheme + hostname = parsedURL.Hostname() + port = parsedURL.Port() + } else { + scheme = "" + hostname = parsedURL.Scheme + port = parsedURL.Opaque + if hostname == "" { + hostname = origin + } + } + return scheme, hostname, port, nil +} + // DialWebsocketWithDialer creates a new RPC client that communicates with a JSON-RPC server // that is listening on the given endpoint using the provided dialer. func DialWebsocketWithDialer(ctx context.Context, endpoint, origin string, dialer websocket.Dialer) (*Client, error) { @@ -180,7 +240,7 @@ type websocketCodec struct { } func newWebsocketCodec(conn *websocket.Conn) ServerCodec { - conn.SetReadLimit(maxRequestContentLength) + conn.SetReadLimit(wsMessageSizeLimit) wc := &websocketCodec{ jsonCodec: NewFuncCodec(conn, conn.WriteJSON, conn.ReadJSON).(*jsonCodec), conn: conn, diff --git a/rpc/websocket_test.go b/rpc/websocket_test.go index f54fc3cd54..4976853baf 100644 --- a/rpc/websocket_test.go +++ b/rpc/websocket_test.go @@ -129,11 +129,15 @@ func TestClientWebsocketPing(t *testing.T) { if err != nil { t.Fatalf("client dial error: %v", err) } + defer client.Close() + resultChan := make(chan int) sub, err := client.EthSubscribe(ctx, resultChan, "foo") if err != nil { t.Fatalf("client subscribe error: %v", err) } + // Note: Unsubscribe is not called on this subscription because the mockup + // server can't handle the request. // Wait for the context's deadline to be reached before proceeding. // This is important for reproducing https://github.com/ethereum/go-ethereum/issues/19798 @@ -157,6 +161,33 @@ func TestClientWebsocketPing(t *testing.T) { } } +// This checks that the websocket transport can deal with large messages. +func TestClientWebsocketLargeMessage(t *testing.T) { + var ( + srv = NewServer() + httpsrv = httptest.NewServer(srv.WebsocketHandler(nil)) + wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") + ) + defer srv.Stop() + defer httpsrv.Close() + + respLength := wsMessageSizeLimit - 50 + srv.RegisterName("test", largeRespService{respLength}) + + c, err := DialWebsocket(context.Background(), wsURL, "") + if err != nil { + t.Fatal(err) + } + + var r string + if err := c.Call(&r, "test_largeResp"); err != nil { + t.Fatal("call failed:", err) + } + if len(r) != respLength { + t.Fatalf("response has wrong length %d, want %d", len(r), respLength) + } +} + // wsPingTestServer runs a WebSocket server which accepts a single subscription request. // When a value arrives on sendPing, the server sends a ping frame, waits for a matching // pong and finally delivers a single subscription result. diff --git a/signer/core/api.go b/signer/core/api.go index 43926a75ff..968dcfb2ed 100644 --- a/signer/core/api.go +++ b/signer/core/api.go @@ -33,7 +33,6 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/signer/storage" ) @@ -322,62 +321,65 @@ func (api *SignerAPI) openTrezor(url accounts.URL) { // startUSBListener starts a listener for USB events, for hardware wallet interaction func (api *SignerAPI) startUSBListener() { - events := make(chan accounts.WalletEvent, 16) + eventCh := make(chan accounts.WalletEvent, 16) am := api.am - am.Subscribe(events) - go func() { + am.Subscribe(eventCh) + // Open any wallets already attached + for _, wallet := range am.Wallets() { + if err := wallet.Open(""); err != nil { + log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err) + if err == usbwallet.ErrTrezorPINNeeded { + go api.openTrezor(wallet.URL()) + } + } + } + go api.derivationLoop(eventCh) +} - // Open any wallets already attached - for _, wallet := range am.Wallets() { - if err := wallet.Open(""); err != nil { - log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err) +// derivationLoop listens for wallet events +func (api *SignerAPI) derivationLoop(events chan accounts.WalletEvent) { + // Listen for wallet event till termination + for event := range events { + switch event.Kind { + case accounts.WalletArrived: + if err := event.Wallet.Open(""); err != nil { + log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err) if err == usbwallet.ErrTrezorPINNeeded { - go api.openTrezor(wallet.URL()) + go api.openTrezor(event.Wallet.URL()) } } - } - // Listen for wallet event till termination - for event := range events { - switch event.Kind { - case accounts.WalletArrived: - if err := event.Wallet.Open(""); err != nil { - log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err) - if err == usbwallet.ErrTrezorPINNeeded { - go api.openTrezor(event.Wallet.URL()) + case accounts.WalletOpened: + status, _ := event.Wallet.Status() + log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status) + var derive = func(limit int, next func() accounts.DerivationPath) { + // Derive first N accounts, hardcoded for now + for i := 0; i < limit; i++ { + path := next() + if acc, err := event.Wallet.Derive(path, true); err != nil { + log.Warn("Account derivation failed", "error", err) + } else { + log.Info("Derived account", "address", acc.Address, "path", path) } } - case accounts.WalletOpened: - status, _ := event.Wallet.Status() - log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status) - var derive = func(numToDerive int, base accounts.DerivationPath) { - // Derive first N accounts, hardcoded for now - var nextPath = make(accounts.DerivationPath, len(base)) - copy(nextPath[:], base[:]) - - for i := 0; i < numToDerive; i++ { - acc, err := event.Wallet.Derive(nextPath, true) - if err != nil { - log.Warn("Account derivation failed", "error", err) - } else { - log.Info("Derived account", "address", acc.Address, "path", nextPath) - } - nextPath[len(nextPath)-1]++ - } - } - if event.Wallet.URL().Scheme == "ledger" { - log.Info("Deriving ledger default paths") - derive(numberOfAccountsToDerive/2, accounts.DefaultBaseDerivationPath) - log.Info("Deriving ledger legacy paths") - derive(numberOfAccountsToDerive/2, accounts.LegacyLedgerBaseDerivationPath) - } else { - derive(numberOfAccountsToDerive, accounts.DefaultBaseDerivationPath) - } - case accounts.WalletDropped: - log.Info("Old wallet dropped", "url", event.Wallet.URL()) - event.Wallet.Close() } + log.Info("Deriving default paths") + derive(numberOfAccountsToDerive, accounts.DefaultIterator(accounts.DefaultBaseDerivationPath)) + if event.Wallet.URL().Scheme == "ledger" { + log.Info("Deriving ledger legacy paths") + derive(numberOfAccountsToDerive, accounts.DefaultIterator(accounts.LegacyLedgerBaseDerivationPath)) + log.Info("Deriving ledger live paths") + // For ledger live, since it's based off the same (DefaultBaseDerivationPath) + // as one we've already used, we need to step it forward one step to avoid + // hitting the same path again + nextFn := accounts.LedgerLiveIterator(accounts.DefaultBaseDerivationPath) + nextFn() + derive(numberOfAccountsToDerive, nextFn) + } + case accounts.WalletDropped: + log.Info("Old wallet dropped", "url", event.Wallet.URL()) + event.Wallet.Close() } - }() + } } // List returns the set of wallet this signer manages. Each wallet can contain @@ -436,7 +438,7 @@ func (api *SignerAPI) newAccount() (common.Address, error) { continue } if pwErr := ValidatePasswordFormat(resp.Text); pwErr != nil { - api.UI.ShowError(fmt.Sprintf("Account creation attempt #%d failed due to password requirements: %v", (i + 1), pwErr)) + api.UI.ShowError(fmt.Sprintf("Account creation attempt #%d failed due to password requirements: %v", i+1, pwErr)) } else { // No error acc, err := be[0].(*keystore.KeyStore).NewAccount(resp.Text) @@ -571,11 +573,11 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth return nil, err } - rlpdata, err := rlp.EncodeToBytes(signedTx) + data, err := signedTx.MarshalBinary() if err != nil { return nil, err } - response := ethapi.SignTransactionResult{Raw: rlpdata, Tx: signedTx} + response := ethapi.SignTransactionResult{Raw: data, Tx: signedTx} // Finally, send the signed tx to the UI api.UI.OnApprovedTx(response) diff --git a/signer/core/signed_data.go b/signer/core/signed_data.go index 19377a521b..3bff1e1f20 100644 --- a/signer/core/signed_data.go +++ b/signer/core/signed_data.go @@ -506,7 +506,7 @@ func parseBytes(encType interface{}) ([]byte, bool) { case []byte: return v, true case hexutil.Bytes: - return []byte(v), true + return v, true case string: bytes, err := hexutil.Decode(v) if err != nil { diff --git a/signer/fourbyte/abi_test.go b/signer/fourbyte/abi_test.go index 314c12735b..68c027ecea 100644 --- a/signer/fourbyte/abi_test.go +++ b/signer/fourbyte/abi_test.go @@ -68,7 +68,7 @@ func TestNewUnpacker(t *testing.T) { [10]byte{49, 50, 51, 52, 53, 54, 55, 56, 57, 48}, common.Hex2Bytes("48656c6c6f2c20776f726c6421"), }, - }, { // https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#examples + }, { // https://docs.soliditylang.org/en/develop/abi-spec.html#examples `[{"type":"function","name":"sam","inputs":[{"type":"bytes"},{"type":"bool"},{"type":"uint256[]"}]}]`, // "dave", true and [1,2,3] "a5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", @@ -124,7 +124,7 @@ func TestCalldataDecoding(t *testing.T) { "42958b5400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000042", // Too short compareAndApprove "a52c101e00ff0000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000042", - // From https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI + // From https://docs.soliditylang.org/en/develop/abi-spec.html // contains a bool with illegal values "a5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", } { @@ -135,7 +135,7 @@ func TestCalldataDecoding(t *testing.T) { } // Expected success for i, hexdata := range []string{ - // From https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI + // From https://docs.soliditylang.org/en/develop/abi-spec.html "a5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", "a52c101e0000000000000000000000000000000000000000000000000000000000000012", "a52c101eFFffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", diff --git a/tests/block_test.go b/tests/block_test.go index 8fa90e3e39..2649bae85a 100644 --- a/tests/block_test.go +++ b/tests/block_test.go @@ -25,7 +25,11 @@ func TestBlockchain(t *testing.T) { bt := new(testMatcher) // General state tests are 'exported' as blockchain tests, but we can run them natively. - bt.skipLoad(`^GeneralStateTests/`) + // For speedier CI-runs, the line below can be uncommented, so those are skipped. + // For now, in hardfork-times (Berlin), we run the tests both as StateTests and + // as blockchain tests, since the latter also covers things like receipt root + //bt.skipLoad(`^GeneralStateTests/`) + // Skip random failures due to selfish mining test bt.skipLoad(`.*bcForgedTest/bcForkUncle\.json`) @@ -43,7 +47,6 @@ func TestBlockchain(t *testing.T) { // test takes a lot for time and goes easily OOM because of sha3 calculation on a huge range, // using 4.6 TGas bt.skipLoad(`.*randomStatetest94.json.*`) - bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) { if err := bt.checkFailure(t, name+"/trie", test.Run(false)); err != nil { t.Errorf("test without snapshotter failed: %v", err) diff --git a/tests/block_test_util.go b/tests/block_test_util.go index dc62233203..bb5a5298fa 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -32,7 +32,6 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params/types/ctypes" @@ -148,7 +147,7 @@ func (t *BlockTest) Run(snapshotter bool) error { } // Cross-check the snapshot-to-hash against the trie hash if snapshotter { - if err := snapshot.VerifyState(chain.Snapshot(), chain.CurrentBlock().Root()); err != nil { + if err := chain.Snapshots().Verify(chain.CurrentBlock().Root()); err != nil { return err } } diff --git a/tests/difficulty_mgen.go b/tests/difficulty_mgen.go deleted file mode 100644 index d13b5aeff3..0000000000 --- a/tests/difficulty_mgen.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019 The multi-geth Authors -// This file is part of the multi-geth library. -// -// The multi-geth 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 multi-geth 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 multi-geth library. If not, see . - -package tests - -// writeDifficultyTestsReferencePairs defines pairs for test generation. -// TODO: move to more accessible api? -var writeDifficultyTestsReferencePairs = map[string]string{ - "Byzantium": "ETC_Atlantis", - "Constantinople": "ETC_Agharta", - "EIP2384": "ETC_Phoenix", -} diff --git a/tests/difficulty_mgen_test.go b/tests/difficulty_mgen_test.go index 7b60d9697c..2c27564555 100644 --- a/tests/difficulty_mgen_test.go +++ b/tests/difficulty_mgen_test.go @@ -18,6 +18,7 @@ package tests import ( "bytes" + "crypto/sha1" "encoding/json" "io/ioutil" "math/big" @@ -28,21 +29,128 @@ import ( "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/internal/build" + "github.com/ethereum/go-ethereum/params/confp" + "github.com/ethereum/go-ethereum/params/types/coregeth" "github.com/ethereum/go-ethereum/params/vars" ) +// outNDJSONFile is the file where difficulty tests get written to. +// The file is encoded as line-delimited JSON. var outNDJSONFile = filepath.Join(difficultyTestDir, "mgen_difficulty.ndjson") -func TestDifficultyGen(t *testing.T) { - generateTests := os.Getenv(CG_GENERATE_DIFFICULTY_TESTS_KEY) != "" - - if !generateTests { +// TestDifficultyTestConfigGen generates the difficulty test configuration files +// for all existing tests' configuration and the configuration for any to-be generated +// test configurations (via dt.generateFromReference). +func TestDifficultyTestConfigGen(t *testing.T) { + if os.Getenv(CG_GENERATE_DIFFICULTY_TEST_CONFIGS_KEY) == "" { t.Skip() } - if os.Getenv(CG_CHAINCONFIG_CHAINSPECS_OPENETHEREUM_KEY) == "" { - t.Fatal("Must run test generation with JSON file chain configurations.") + + head := build.RunGit("rev-parse", "HEAD") + head = strings.TrimSpace(head) + + dt := new(testMatcherGen) + dt.allConfigs = make(map[string]*coregeth.CoreGethChainConfig) + dt.testMatcher = new(testMatcher) + dt.noParallel = true // disable parallelism + dt.errorPanics = true + dt.gitHead = head + + // Not difficulty-tests + dt.skipLoad("hexencodetest.*") + dt.skipLoad("crypto.*") + dt.skipLoad("blockgenesistest\\.json") + dt.skipLoad("genesishashestest\\.json") + dt.skipLoad("keyaddrtest\\.json") + dt.skipLoad("txtest\\.json") + + // files are 2 years old, contains strange values + dt.skipLoad("difficultyCustomHomestead\\.json") + dt.skipLoad("difficultyMorden\\.json") + dt.skipLoad("difficultyOlimpic\\.json") + + dt.generateFromReference("Byzantium", "ETC_Atlantis") + dt.generateFromReference("Constantinople", "ETC_Agharta") + dt.generateFromReference("EIP2384", "ETC_Phoenix") + /* + My rationale for not adding ETC_Magneto was that difficulty hasn't changed for the Foundation since EIP2384 Muir Glacier, + and the tests haven't changed in at least that long. + This leads to me to think that adding an ETC_Magneto case would only duplicate the set of ETC_Phoenix. + */ + + for k, v := range difficultyChainConfigurations { + dt.config(k, v) } + dt.walk(t, difficultyTestDir, func(t *testing.T, name string, test *DifficultyTest) { + config, matchedName := dt.findConfig(name) + // t.Logf("name: %s, matchedName: %s", name, matchedName) + + fileBasename, ok := mapForkNameChainspecFileDifficulty[matchedName] + if !ok { + t.Fatalf("unmatched config name: %s", matchedName) + } + specPath := filepath.Join(coregethSpecsDir, fileBasename) + + cgConfig := &coregeth.CoreGethChainConfig{} + err := confp.Convert(config, cgConfig) + if err != nil { + t.Fatal(err) + } + + j, err := json.MarshalIndent(cgConfig, "", " ") + if err != nil { + t.Fatal(err) + } + err = ioutil.WriteFile(specPath, j, os.ModePerm) + if err != nil { + t.Fatal(err) + } + + targetForkName := dt.getGenerationTarget(matchedName) + if targetForkName == "" { + return + } + + targetConfiguration, ok := difficultyChainConfigurations[targetForkName] + if !ok { + t.Fatalf("config association failed; no existing Go chain config found: %s", targetForkName) + } + + targetConfigurationBasename := mapForkNameChainspecFileDifficulty[targetForkName] + specPath = filepath.Join(coregethSpecsDir, targetConfigurationBasename) + + cgConfig = &coregeth.CoreGethChainConfig{} + err = confp.Convert(targetConfiguration, cgConfig) + if err != nil { + t.Fatal(err) + } + + j, err = json.MarshalIndent(cgConfig, "", " ") + if err != nil { + t.Fatal(err) + } + err = ioutil.WriteFile(specPath, j, os.ModePerm) + if err != nil { + t.Fatal(err) + } + }) +} + +// TestDifficultyGen generated line-delimited JSON tests using the existing default +// difficulty tests suite as a base reference set. +// Reference:Target pairs are defined with dt.generateFromReference. +// The original test will be run and must pass in order for it to be copied to the +// generated set and for it to be used as a reference, if applicable, for test generation. +func TestDifficultyGen(t *testing.T) { + if os.Getenv(CG_GENERATE_DIFFICULTY_TESTS_KEY) == "" { + t.Skip() + } + + head := build.RunGit("rev-parse", "HEAD") + head = strings.TrimSpace(head) + err := os.MkdirAll(filepath.Dir(outNDJSONFile), os.ModePerm) if err != nil { t.Fatal(err) @@ -54,7 +162,21 @@ func TestDifficultyGen(t *testing.T) { t.Fatal(err) } - dt := new(testMatcher) + dt := new(testMatcherGen) + dt.allConfigs = make(map[string]*coregeth.CoreGethChainConfig) + dt.testMatcher = new(testMatcher) + dt.noParallel = true // disable parallelism + dt.errorPanics = true + dt.gitHead = head + + dt.generateFromReference("Byzantium", "ETC_Atlantis") + dt.generateFromReference("Constantinople", "ETC_Agharta") + dt.generateFromReference("EIP2384", "ETC_Phoenix") + /* + My rationale for not adding ETC_Magneto was that difficulty hasn't changed for the Foundation since EIP2384 Muir Glacier, + and the tests haven't changed in at least that long. + This leads to me to think that adding an ETC_Magneto case would only duplicate the set of ETC_Phoenix. + */ // Not difficulty-tests dt.skipLoad("hexencodetest.*") @@ -74,10 +196,15 @@ func TestDifficultyGen(t *testing.T) { dt.config(k, v) } - // Map will hold pairs of newConfigName: chainSpecrefs. - // It will be used during writing of associated chainspec config files. - // See comment below. - wroteNewChainConfigs := make(map[string]chainspecRef) + mustSha1SumForFile := func(filePath string) []byte { + b, err := ioutil.ReadFile(filePath) + if err != nil { + t.Fatal(err) + } + + s1s := sha1.Sum(b) + return s1s[:] + } dt.walk(t, difficultyTestDir, func(t *testing.T, name string, test *DifficultyTest) { cfg, key := dt.findConfig(name) @@ -90,58 +217,52 @@ func TestDifficultyGen(t *testing.T) { t.Fatalf("failed to run difficulty test, err=%v", err) } else { - // Collect all paired tests and originals. - // The output file will yield ALL tests, not just newly-generated ones. - specFile, ok := chainspecRefsDifficulty[key] + fileBasename, ok := mapForkNameChainspecFileDifficulty[key] if !ok { - t.Fatalf("missing spec ref; key=%s file=%s", key, specFile) + t.Fatalf("unmatched config name: %s", key) } - test.Chainspec = specFile + specPath := filepath.Join(coregethSpecsDir, fileBasename) + + test.Chainspec = chainspecRef{Filename: fileBasename, Sha1Sum: mustSha1SumForFile(specPath)} test.Name = strings.ReplaceAll(name, ".json", "") - mustAppendTestToFile(t, test, outNDJSONFile) - - // Kind of ugly reverse lookup from file -> fork name. - var forkName string - for k, v := range mapForkNameChainspecFileDifficulty { - if v == test.Chainspec.Filename { - forkName = k - break - } - } - if forkName == "" { - t.Fatal("missing fork/fileconf name", test, mapForkNameChainspecFileDifficulty) - } - // Is test(config) associated with a new test to be generated. - associateForkName, ok := writeDifficultyTestsReferencePairs[forkName] - if !ok { - t.Logf("OK [existing,nonref] %v", test) + mustAppendTestToFileNDJSON(t, test, outNDJSONFile) + // -- This is as far as ALL tests go. + + targetForkName := dt.getGenerationTarget(key) + if targetForkName == "" { + // This configuration is not a reference for a generated target. return } - conf, ok := difficultyChainConfigurations[associateForkName] + // Lookup Target configuration from canonical coded list, eg. + /* + "Ropsten": params.RopstenChainConfig, + "Morden": params.RopstenChainConfig, + "Frontier": &goethereum.ChainConfig{}, + */ + targetConfiguration, ok := difficultyChainConfigurations[targetForkName] if !ok { - t.Fatalf("config association failed; no existing Go chain config found: %s", associateForkName) + t.Fatalf("config association failed; no existing Go chain config found: %s", targetForkName) } - // If associated chainspec file has not been written at least once, write it. - // This ensures that for test generation, a chain spec file be written if it does not already exist. - // This is because it is more likely that we will want to write the chain spec in Go, then have the - // generator write the spec along with the tests to save the hurdle of manually building the chain spec - // file first as a dependency for test generation. - specref, done := wroteNewChainConfigs[associateForkName] - if !done || specFile.Filename == "" { - t.Logf("Writing chain spec file: %s", forkName) - specFilepath, sum, err := writeDifficultyConfigFile(conf, associateForkName) - if err != nil { - t.Fatalf("could not write difficulty file, err=%v", err) - } - specref = chainspecRef{ - Filename: specFilepath, - Sha1Sum: sum[:], - } - wroteNewChainConfigs[associateForkName] = specref - } + // Lookup respective Target configuration FILE name, eg. + /* + "Ropsten": "ropsten_difficulty_test.json", + "Morden": "morden_difficulty_test.json", + "Frontier": "frontier_difficulty_test.json", + */ + targetConfigurationBasename := mapForkNameChainspecFileDifficulty[targetForkName] + + // Establish the specification reference value. + // Note that this assumes that the files sourced here are indeed consistent with the + // the coded configurations (ie. that TestDifficultyTestConfigGen has already been + // run and has done its job properly). + // There is no coded relationship or test between the test file and the configuration + // actually used here. + targetSpecRef := chainspecRef{ + Filename: targetConfigurationBasename, + Sha1Sum: mustSha1SumForFile(filepath.Join(coregethSpecsDir, targetConfigurationBasename))} newTest := &DifficultyTest{ ParentTimestamp: test.ParentTimestamp, @@ -149,28 +270,29 @@ func TestDifficultyGen(t *testing.T) { UncleHash: test.UncleHash, CurrentTimestamp: test.CurrentTimestamp, CurrentBlockNumber: test.CurrentBlockNumber, - CurrentDifficulty: ethash.CalcDifficulty(conf, test.CurrentTimestamp, &types.Header{ + CurrentDifficulty: ethash.CalcDifficulty(targetConfiguration, test.CurrentTimestamp, &types.Header{ Difficulty: test.ParentDifficulty, Time: test.ParentTimestamp, Number: big.NewInt(int64(test.CurrentBlockNumber - 1)), UncleHash: test.UncleHash, }), - Chainspec: specref, - Name: strings.ReplaceAll(test.Name, forkName, associateForkName), + Chainspec: targetSpecRef, + Name: strings.ReplaceAll(test.Name, key, targetForkName), } // "Dogfood". - if err := newTest.Run(conf); err != nil { + if err := newTest.Run(targetConfiguration); err != nil { t.Fatal(err) } - mustAppendTestToFile(t, newTest, outNDJSONFile) + mustAppendTestToFileNDJSON(t, newTest, outNDJSONFile) t.Logf("OK [generated] %v", newTest) } }) } -func mustAppendTestToFile(t *testing.T, test *DifficultyTest, filep string) { +// mustAppendTestToFileNDJSON appends a difficulty test to a file as newline-delimited JSON. +func mustAppendTestToFileNDJSON(t *testing.T, test *DifficultyTest, filep string) { b, _ := json.Marshal(test) out := []byte{} buf := bytes.NewBuffer(out) diff --git a/tests/difficulty_test.go b/tests/difficulty_test.go index 179aa1cdbf..20cf93ae34 100644 --- a/tests/difficulty_test.go +++ b/tests/difficulty_test.go @@ -46,6 +46,7 @@ func TestDifficulty(t *testing.T) { dt.walk(t, difficultyTestDir, func(t *testing.T, name string, test *DifficultyTest) { cfg, _ := dt.findConfig(name) + if test.ParentDifficulty.Cmp(vars.MinimumDifficulty) < 0 { t.Skip("difficulty below minimum") return @@ -56,7 +57,7 @@ func TestDifficulty(t *testing.T) { }) } -func TestDifficulty2(t *testing.T) { +func TestDifficultyNDJSON(t *testing.T) { t.Parallel() dt := new(testMatcher) diff --git a/tests/fuzzers/abi/abifuzzer.go b/tests/fuzzers/abi/abifuzzer.go index 76d3c800f7..60233d158a 100644 --- a/tests/fuzzers/abi/abifuzzer.go +++ b/tests/fuzzers/abi/abifuzzer.go @@ -17,38 +17,53 @@ package abi import ( - "bytes" "fmt" - "math/rand" "reflect" "strings" "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/crypto" fuzz "github.com/google/gofuzz" ) -func unpackPack(abi abi.ABI, method string, inputType []interface{}, input []byte) bool { - outptr := reflect.New(reflect.TypeOf(inputType)) - if err := abi.UnpackIntoInterface(outptr.Interface(), method, input); err == nil { - output, err := abi.Pack(method, input) +var ( + names = []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"} + stateMut = []string{"", "pure", "view", "payable"} + stateMutabilites = []*string{&stateMut[0], &stateMut[1], &stateMut[2], &stateMut[3]} + pays = []string{"", "true", "false"} + payables = []*string{&pays[0], &pays[1]} + vNames = []string{"a", "b", "c", "d", "e", "f", "g"} + varNames = append(vNames, names...) + varTypes = []string{"bool", "address", "bytes", "string", + "uint8", "int8", "uint8", "int8", "uint16", "int16", + "uint24", "int24", "uint32", "int32", "uint40", "int40", "uint48", "int48", "uint56", "int56", + "uint64", "int64", "uint72", "int72", "uint80", "int80", "uint88", "int88", "uint96", "int96", + "uint104", "int104", "uint112", "int112", "uint120", "int120", "uint128", "int128", "uint136", "int136", + "uint144", "int144", "uint152", "int152", "uint160", "int160", "uint168", "int168", "uint176", "int176", + "uint184", "int184", "uint192", "int192", "uint200", "int200", "uint208", "int208", "uint216", "int216", + "uint224", "int224", "uint232", "int232", "uint240", "int240", "uint248", "int248", "uint256", "int256", + "bytes1", "bytes2", "bytes3", "bytes4", "bytes5", "bytes6", "bytes7", "bytes8", "bytes9", "bytes10", "bytes11", + "bytes12", "bytes13", "bytes14", "bytes15", "bytes16", "bytes17", "bytes18", "bytes19", "bytes20", "bytes21", + "bytes22", "bytes23", "bytes24", "bytes25", "bytes26", "bytes27", "bytes28", "bytes29", "bytes30", "bytes31", + "bytes32", "bytes"} +) + +func unpackPack(abi abi.ABI, method string, input []byte) ([]interface{}, bool) { + if out, err := abi.Unpack(method, input); err == nil { + _, err := abi.Pack(method, out...) if err != nil { // We have some false positives as we can unpack these type successfully, but not pack them if err.Error() == "abi: cannot use []uint8 as type [0]int8 as argument" || err.Error() == "abi: cannot use uint8 as type int8 as argument" { - return false + return out, false } panic(err) } - if !bytes.Equal(input, output[4:]) { - panic(fmt.Sprintf("unpackPack is not equal, \ninput : %x\noutput: %x", input, output[4:])) - } - return true + return out, true } - return false + return nil, false } -func packUnpack(abi abi.ABI, method string, input []interface{}) bool { +func packUnpack(abi abi.ABI, method string, input *[]interface{}) bool { if packed, err := abi.Pack(method, input); err == nil { outptr := reflect.New(reflect.TypeOf(input)) err := abi.UnpackIntoInterface(outptr.Interface(), method, packed) @@ -100,64 +115,23 @@ func createABI(name string, stateMutability, payable *string, inputs []args) (ab return abi.JSON(strings.NewReader(sig)) } -func fillStruct(structs []interface{}, data []byte) { - if structs != nil && len(data) != 0 { - fuzz.NewFromGoFuzz(data).Fuzz(&structs) - } -} - -func createStructs(args []args) []interface{} { - structs := make([]interface{}, len(args)) - for i, arg := range args { - t, err := abi.NewType(arg.typ, "", nil) - if err != nil { - panic(err) - } - structs[i] = reflect.New(t.GetType()).Elem() - } - return structs -} - func runFuzzer(input []byte) int { good := false + fuzzer := fuzz.NewFromGoFuzz(input) - names := []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"} - stateMut := []string{"", "pure", "view", "payable"} - stateMutabilites := []*string{nil, &stateMut[0], &stateMut[1], &stateMut[2], &stateMut[3]} - pays := []string{"true", "false"} - payables := []*string{nil, &pays[0], &pays[1]} - varNames := []string{"a", "b", "c", "d", "e", "f", "g"} - varNames = append(varNames, names...) - varTypes := []string{"bool", "address", "bytes", "string", - "uint8", "int8", "uint8", "int8", "uint16", "int16", - "uint24", "int24", "uint32", "int32", "uint40", "int40", "uint48", "int48", "uint56", "int56", - "uint64", "int64", "uint72", "int72", "uint80", "int80", "uint88", "int88", "uint96", "int96", - "uint104", "int104", "uint112", "int112", "uint120", "int120", "uint128", "int128", "uint136", "int136", - "uint144", "int144", "uint152", "int152", "uint160", "int160", "uint168", "int168", "uint176", "int176", - "uint184", "int184", "uint192", "int192", "uint200", "int200", "uint208", "int208", "uint216", "int216", - "uint224", "int224", "uint232", "int232", "uint240", "int240", "uint248", "int248", "uint256", "int256", - "bytes1", "bytes2", "bytes3", "bytes4", "bytes5", "bytes6", "bytes7", "bytes8", "bytes9", "bytes10", "bytes11", - "bytes12", "bytes13", "bytes14", "bytes15", "bytes16", "bytes17", "bytes18", "bytes19", "bytes20", "bytes21", - "bytes22", "bytes23", "bytes24", "bytes25", "bytes26", "bytes27", "bytes28", "bytes29", "bytes30", "bytes31", - "bytes32", "bytes"} - rnd := rand.New(rand.NewSource(123456)) - if len(input) > 0 { - kec := crypto.Keccak256(input) - rnd = rand.New(rand.NewSource(int64(kec[0]))) - } - name := names[rnd.Intn(len(names))] - stateM := stateMutabilites[rnd.Intn(len(stateMutabilites))] - payable := payables[rnd.Intn(len(payables))] + name := names[getUInt(fuzzer)%len(names)] + stateM := stateMutabilites[getUInt(fuzzer)%len(stateMutabilites)] + payable := payables[getUInt(fuzzer)%len(payables)] maxLen := 5 for k := 1; k < maxLen; k++ { var arg []args for i := k; i > 0; i-- { argName := varNames[i] - argTyp := varTypes[rnd.Int31n(int32(len(varTypes)))] - if rnd.Int31n(10) == 0 { + argTyp := varTypes[getUInt(fuzzer)%len(varTypes)] + if getUInt(fuzzer)%10 == 0 { argTyp += "[]" - } else if rnd.Int31n(10) == 0 { - arrayArgs := rnd.Int31n(30) + 1 + } else if getUInt(fuzzer)%10 == 0 { + arrayArgs := getUInt(fuzzer)%30 + 1 argTyp += fmt.Sprintf("[%d]", arrayArgs) } arg = append(arg, args{ @@ -169,10 +143,8 @@ func runFuzzer(input []byte) int { if err != nil { continue } - structs := createStructs(arg) - b := unpackPack(abi, name, structs, input) - fillStruct(structs, input) - c := packUnpack(abi, name, structs) + structs, b := unpackPack(abi, name, input) + c := packUnpack(abi, name, &structs) good = good || b || c } if good { @@ -184,3 +156,15 @@ func runFuzzer(input []byte) int { func Fuzz(input []byte) int { return runFuzzer(input) } + +func getUInt(fuzzer *fuzz.Fuzzer) int { + var i int + fuzzer.Fuzz(&i) + if i < 0 { + i = -i + if i < 0 { + return 0 + } + } + return i +} diff --git a/tests/fuzzers/abi/abifuzzer_test.go b/tests/fuzzers/abi/abifuzzer_test.go index c72577e9ee..423a3cd232 100644 --- a/tests/fuzzers/abi/abifuzzer_test.go +++ b/tests/fuzzers/abi/abifuzzer_test.go @@ -23,14 +23,7 @@ import ( // TestReplicate can be used to replicate crashers from the fuzzing tests. // Just replace testString with the data in .quoted func TestReplicate(t *testing.T) { - testString := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00000000000" + - "00000000000000000000" + - "00000000000000000000" + - "00000001" - + testString := "\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00" data := []byte(testString) runFuzzer(data) } diff --git a/common/bitutil/compress_fuzz.go b/tests/fuzzers/bitutil/compress_fuzz.go similarity index 68% rename from common/bitutil/compress_fuzz.go rename to tests/fuzzers/bitutil/compress_fuzz.go index 1b87f50edc..5903cf2f93 100644 --- a/common/bitutil/compress_fuzz.go +++ b/tests/fuzzers/bitutil/compress_fuzz.go @@ -14,17 +14,19 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build gofuzz - package bitutil -import "bytes" +import ( + "bytes" + + "github.com/ethereum/go-ethereum/common/bitutil" +) // Fuzz implements a go-fuzz fuzzer method to test various encoding method // invocations. func Fuzz(data []byte) int { if len(data) == 0 { - return -1 + return 0 } if data[0]%2 == 0 { return fuzzEncode(data[1:]) @@ -35,22 +37,34 @@ func Fuzz(data []byte) int { // fuzzEncode implements a go-fuzz fuzzer method to test the bitset encoding and // decoding algorithm. func fuzzEncode(data []byte) int { - proc, _ := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data)) + proc, _ := bitutil.DecompressBytes(bitutil.CompressBytes(data), len(data)) if !bytes.Equal(data, proc) { panic("content mismatch") } - return 0 + return 1 } // fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and // reencoding algorithm. func fuzzDecode(data []byte) int { - blob, err := bitsetDecodeBytes(data, 1024) + blob, err := bitutil.DecompressBytes(data, 1024) if err != nil { return 0 } - if comp := bitsetEncodeBytes(blob); !bytes.Equal(comp, data) { + // re-compress it (it's OK if the re-compressed differs from the + // original - the first input may not have been compressed at all) + comp := bitutil.CompressBytes(blob) + if len(comp) > len(blob) { + // After compression, it must be smaller or equal + panic("bad compression") + } + // But decompressing it once again should work + decomp, err := bitutil.DecompressBytes(data, 1024) + if err != nil { + panic(err) + } + if !bytes.Equal(decomp, blob) { panic("content mismatch") } - return 0 + return 1 } diff --git a/tests/fuzzers/bls12381/bls_fuzzer.go b/tests/fuzzers/bls12381/bls_fuzzer.go new file mode 100644 index 0000000000..bc3c456526 --- /dev/null +++ b/tests/fuzzers/bls12381/bls_fuzzer.go @@ -0,0 +1,101 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls + +import ( + "bytes" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" +) + +const ( + blsG1Add = byte(10) + blsG1Mul = byte(11) + blsG1MultiExp = byte(12) + blsG2Add = byte(13) + blsG2Mul = byte(14) + blsG2MultiExp = byte(15) + blsPairing = byte(16) + blsMapG1 = byte(17) + blsMapG2 = byte(18) +) + +func FuzzG1Add(data []byte) int { return fuzz(blsG1Add, data) } +func FuzzG1Mul(data []byte) int { return fuzz(blsG1Mul, data) } +func FuzzG1MultiExp(data []byte) int { return fuzz(blsG1MultiExp, data) } +func FuzzG2Add(data []byte) int { return fuzz(blsG2Add, data) } +func FuzzG2Mul(data []byte) int { return fuzz(blsG2Mul, data) } +func FuzzG2MultiExp(data []byte) int { return fuzz(blsG2MultiExp, data) } +func FuzzPairing(data []byte) int { return fuzz(blsPairing, data) } +func FuzzMapG1(data []byte) int { return fuzz(blsMapG1, data) } +func FuzzMapG2(data []byte) int { return fuzz(blsMapG2, data) } + +func checkInput(id byte, inputLen int) bool { + switch id { + case blsG1Add: + return inputLen == 256 + case blsG1Mul: + return inputLen == 160 + case blsG1MultiExp: + return inputLen%160 == 0 + case blsG2Add: + return inputLen == 512 + case blsG2Mul: + return inputLen == 288 + case blsG2MultiExp: + return inputLen%288 == 0 + case blsPairing: + return inputLen%384 == 0 + case blsMapG1: + return inputLen == 64 + case blsMapG2: + return inputLen == 128 + } + panic("programmer error") +} + +// The fuzzer functions must return +// 1 if the fuzzer should increase priority of the +// given input during subsequent fuzzing (for example, the input is lexically +// correct and was parsed successfully); +// -1 if the input must not be added to corpus even if gives new coverage; and +// 0 otherwise +// other values are reserved for future use. +func fuzz(id byte, data []byte) int { + // Even on bad input, it should not crash, so we still test the gas calc + precompile := vm.PrecompiledContractsBLS[common.BytesToAddress([]byte{id})] + gas := precompile.RequiredGas(data) + if !checkInput(id, len(data)) { + return 0 + } + // If the gas cost is too large (25M), bail out + if gas > 25*1000*1000 { + return 0 + } + cpy := make([]byte, len(data)) + copy(cpy, data) + _, err := precompile.Run(cpy) + if !bytes.Equal(cpy, data) { + panic(fmt.Sprintf("input data modified, precompile %d: %x %x", id, data, cpy)) + } + if err != nil { + return 0 + } + return 1 +} diff --git a/tests/fuzzers/bls12381/testdata/fuzz_g1_add_seed_corpus.zip b/tests/fuzzers/bls12381/testdata/fuzz_g1_add_seed_corpus.zip new file mode 100644 index 0000000000..16498c1cba Binary files /dev/null and b/tests/fuzzers/bls12381/testdata/fuzz_g1_add_seed_corpus.zip differ diff --git a/tests/fuzzers/bls12381/testdata/fuzz_g1_mul_seed_corpus.zip b/tests/fuzzers/bls12381/testdata/fuzz_g1_mul_seed_corpus.zip new file mode 100644 index 0000000000..57f9d6696d Binary files /dev/null and b/tests/fuzzers/bls12381/testdata/fuzz_g1_mul_seed_corpus.zip differ diff --git a/tests/fuzzers/bls12381/testdata/fuzz_g1_multiexp_seed_corpus.zip b/tests/fuzzers/bls12381/testdata/fuzz_g1_multiexp_seed_corpus.zip new file mode 100644 index 0000000000..7271f040f3 Binary files /dev/null and b/tests/fuzzers/bls12381/testdata/fuzz_g1_multiexp_seed_corpus.zip differ diff --git a/tests/fuzzers/bls12381/testdata/fuzz_g2_add_seed_corpus.zip b/tests/fuzzers/bls12381/testdata/fuzz_g2_add_seed_corpus.zip new file mode 100644 index 0000000000..cd5206ca0b Binary files /dev/null and b/tests/fuzzers/bls12381/testdata/fuzz_g2_add_seed_corpus.zip differ diff --git a/tests/fuzzers/bls12381/testdata/fuzz_g2_mul_seed_corpus.zip b/tests/fuzzers/bls12381/testdata/fuzz_g2_mul_seed_corpus.zip new file mode 100644 index 0000000000..f784a5a3d7 Binary files /dev/null and b/tests/fuzzers/bls12381/testdata/fuzz_g2_mul_seed_corpus.zip differ diff --git a/tests/fuzzers/bls12381/testdata/fuzz_g2_multiexp_seed_corpus.zip b/tests/fuzzers/bls12381/testdata/fuzz_g2_multiexp_seed_corpus.zip new file mode 100644 index 0000000000..c205117a46 Binary files /dev/null and b/tests/fuzzers/bls12381/testdata/fuzz_g2_multiexp_seed_corpus.zip differ diff --git a/tests/fuzzers/bls12381/testdata/fuzz_map_g1_seed_corpus.zip b/tests/fuzzers/bls12381/testdata/fuzz_map_g1_seed_corpus.zip new file mode 100644 index 0000000000..70382fbe53 Binary files /dev/null and b/tests/fuzzers/bls12381/testdata/fuzz_map_g1_seed_corpus.zip differ diff --git a/tests/fuzzers/bls12381/testdata/fuzz_map_g2_seed_corpus.zip b/tests/fuzzers/bls12381/testdata/fuzz_map_g2_seed_corpus.zip new file mode 100644 index 0000000000..67adc5b5e8 Binary files /dev/null and b/tests/fuzzers/bls12381/testdata/fuzz_map_g2_seed_corpus.zip differ diff --git a/tests/fuzzers/bls12381/testdata/fuzz_pairing_seed_corpus.zip b/tests/fuzzers/bls12381/testdata/fuzz_pairing_seed_corpus.zip new file mode 100644 index 0000000000..e24d2b0a52 Binary files /dev/null and b/tests/fuzzers/bls12381/testdata/fuzz_pairing_seed_corpus.zip differ diff --git a/tests/fuzzers/bn256/bn256_fuzz.go b/tests/fuzzers/bn256/bn256_fuzz.go new file mode 100644 index 0000000000..c98fbc33ae --- /dev/null +++ b/tests/fuzzers/bn256/bn256_fuzz.go @@ -0,0 +1,154 @@ +// Copyright 2018 Péter Szilágyi. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +// +build gofuzz + +package bn256 + +import ( + "bytes" + "fmt" + "io" + "math/big" + + gurvy "github.com/consensys/gurvy/bn256" + cloudflare "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" + google "github.com/ethereum/go-ethereum/crypto/bn256/google" +) + +func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *gurvy.G1Affine) { + _, xc, err := cloudflare.RandomG1(input) + if err != nil { + // insufficient input + return nil, nil, nil + } + xg := new(google.G1) + if _, err := xg.Unmarshal(xc.Marshal()); err != nil { + panic(fmt.Sprintf("Could not marshal cloudflare -> google:", err)) + } + xs := new(gurvy.G1Affine) + if err := xs.Unmarshal(xc.Marshal()); err != nil { + panic(fmt.Sprintf("Could not marshal cloudflare -> consensys:", err)) + } + return xc, xg, xs +} + +func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *gurvy.G2Affine) { + _, xc, err := cloudflare.RandomG2(input) + if err != nil { + // insufficient input + return nil, nil, nil + } + xg := new(google.G2) + if _, err := xg.Unmarshal(xc.Marshal()); err != nil { + panic(fmt.Sprintf("Could not marshal cloudflare -> google:", err)) + } + xs := new(gurvy.G2Affine) + if err := xs.Unmarshal(xc.Marshal()); err != nil { + panic(fmt.Sprintf("Could not marshal cloudflare -> consensys:", err)) + } + return xc, xg, xs +} + +// FuzzAdd fuzzez bn256 addition between the Google and Cloudflare libraries. +func FuzzAdd(data []byte) int { + input := bytes.NewReader(data) + xc, xg, xs := getG1Points(input) + if xc == nil { + return 0 + } + yc, yg, ys := getG1Points(input) + if yc == nil { + return 0 + } + // Ensure both libs can parse the second curve point + // Add the two points and ensure they result in the same output + rc := new(cloudflare.G1) + rc.Add(xc, yc) + + rg := new(google.G1) + rg.Add(xg, yg) + + tmpX := new(gurvy.G1Jac).FromAffine(xs) + tmpY := new(gurvy.G1Jac).FromAffine(ys) + rs := new(gurvy.G1Affine).FromJacobian(tmpX.AddAssign(tmpY)) + + if !bytes.Equal(rc.Marshal(), rg.Marshal()) { + panic("add mismatch: cloudflare/google") + } + + if !bytes.Equal(rc.Marshal(), rs.Marshal()) { + panic("add mismatch: cloudflare/consensys") + } + return 1 +} + +// FuzzMul fuzzez bn256 scalar multiplication between the Google and Cloudflare +// libraries. +func FuzzMul(data []byte) int { + input := bytes.NewReader(data) + pc, pg, ps := getG1Points(input) + if pc == nil { + return 0 + } + // Add the two points and ensure they result in the same output + remaining := input.Len() + if remaining == 0 { + return 0 + } + if remaining > 128 { + // The evm only ever uses 32 byte integers, we need to cap this otherwise + // we run into slow exec. A 236Kb byte integer cause oss-fuzz to report it as slow. + // 128 bytes should be fine though + return 0 + } + buf := make([]byte, remaining) + input.Read(buf) + + rc := new(cloudflare.G1) + rc.ScalarMult(pc, new(big.Int).SetBytes(buf)) + + rg := new(google.G1) + rg.ScalarMult(pg, new(big.Int).SetBytes(buf)) + + rs := new(gurvy.G1Jac) + psJac := new(gurvy.G1Jac).FromAffine(ps) + rs.ScalarMultiplication(psJac, new(big.Int).SetBytes(buf)) + rsAffine := new(gurvy.G1Affine).FromJacobian(rs) + + if !bytes.Equal(rc.Marshal(), rg.Marshal()) { + panic("scalar mul mismatch: cloudflare/google") + } + if !bytes.Equal(rc.Marshal(), rsAffine.Marshal()) { + panic("scalar mul mismatch: cloudflare/consensys") + } + return 1 +} + +func FuzzPair(data []byte) int { + input := bytes.NewReader(data) + pc, pg, ps := getG1Points(input) + if pc == nil { + return 0 + } + tc, tg, ts := getG2Points(input) + if tc == nil { + return 0 + } + // Pair the two points and ensure they result in the same output + clPair := cloudflare.PairingCheck([]*cloudflare.G1{pc}, []*cloudflare.G2{tc}) + if clPair != google.PairingCheck([]*google.G1{pg}, []*google.G2{tg}) { + panic("pairing mismatch: cloudflare/google") + } + + coPair, err := gurvy.PairingCheck([]gurvy.G1Affine{*ps}, []gurvy.G2Affine{*ts}) + if err != nil { + panic(fmt.Sprintf("gurvy encountered error: %v", err)) + } + if clPair != coPair { + panic("pairing mismatch: cloudflare/consensys") + } + + return 1 +} diff --git a/tests/fuzzers/difficulty/debug/main.go b/tests/fuzzers/difficulty/debug/main.go new file mode 100644 index 0000000000..23516b3a0d --- /dev/null +++ b/tests/fuzzers/difficulty/debug/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/ethereum/go-ethereum/tests/fuzzers/difficulty" +) + +func main() { + if len(os.Args) != 2 { + fmt.Fprintf(os.Stderr, "Usage: debug ") + os.Exit(1) + } + crasher := os.Args[1] + data, err := ioutil.ReadFile(crasher) + if err != nil { + fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err) + os.Exit(1) + } + difficulty.Fuzz(data) +} diff --git a/tests/fuzzers/difficulty/difficulty-fuzz.go b/tests/fuzzers/difficulty/difficulty-fuzz.go new file mode 100644 index 0000000000..e4c5dcf57c --- /dev/null +++ b/tests/fuzzers/difficulty/difficulty-fuzz.go @@ -0,0 +1,145 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package difficulty + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "math/big" + + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/types" +) + +type fuzzer struct { + input io.Reader + exhausted bool + debugging bool +} + +func (f *fuzzer) read(size int) []byte { + out := make([]byte, size) + if _, err := f.input.Read(out); err != nil { + f.exhausted = true + } + return out +} + +func (f *fuzzer) readSlice(min, max int) []byte { + var a uint16 + binary.Read(f.input, binary.LittleEndian, &a) + size := min + int(a)%(max-min) + out := make([]byte, size) + if _, err := f.input.Read(out); err != nil { + f.exhausted = true + } + return out +} + +func (f *fuzzer) readUint64(min, max uint64) uint64 { + if min == max { + return min + } + var a uint64 + if err := binary.Read(f.input, binary.LittleEndian, &a); err != nil { + f.exhausted = true + } + a = min + a%(max-min) + return a +} +func (f *fuzzer) readBool() bool { + return f.read(1)[0]&0x1 == 0 +} + +// The function must return +// 1 if the fuzzer should increase priority of the +// given input during subsequent fuzzing (for example, the input is lexically +// correct and was parsed successfully); +// -1 if the input must not be added to corpus even if gives new coverage; and +// 0 otherwise +// other values are reserved for future use. +func Fuzz(data []byte) int { + f := fuzzer{ + input: bytes.NewReader(data), + exhausted: false, + } + return f.fuzz() +} + +var minDifficulty = big.NewInt(0x2000) + +type calculator func(time uint64, parent *types.Header) *big.Int + +func (f *fuzzer) fuzz() int { + // A parent header + header := &types.Header{} + if f.readBool() { + header.UncleHash = types.EmptyUncleHash + } + // Difficulty can range between 0x2000 (2 bytes) and up to 32 bytes + { + diff := new(big.Int).SetBytes(f.readSlice(2, 32)) + if diff.Cmp(minDifficulty) < 0 { + diff.Set(minDifficulty) + } + header.Difficulty = diff + } + // Number can range between 0 and up to 32 bytes (but not so that the child exceeds it) + { + // However, if we use astronomic numbers, then the bomb exp karatsuba calculation + // in the legacy methods) + // times out, so we limit it to fit within reasonable bounds + number := new(big.Int).SetBytes(f.readSlice(0, 4)) // 4 bytes: 32 bits: block num max 4 billion + header.Number = number + } + // Both parent and child time must fit within uint64 + var time uint64 + { + childTime := f.readUint64(1, 0xFFFFFFFFFFFFFFFF) + //fmt.Printf("childTime: %x\n",childTime) + delta := f.readUint64(1, childTime) + //fmt.Printf("delta: %v\n", delta) + pTime := childTime - delta + header.Time = pTime + time = childTime + } + // Bomb delay will never exceed uint64 + bombDelay := new(big.Int).SetUint64(f.readUint64(1, 0xFFFFFFFFFFFFFFFe)) + + if f.exhausted { + return 0 + } + + for i, pair := range []struct { + bigFn calculator + u256Fn calculator + }{ + {ethash.FrontierDifficultyCalulator, ethash.CalcDifficultyFrontierU256}, + {ethash.HomesteadDifficultyCalulator, ethash.CalcDifficultyHomesteadU256}, + {ethash.DynamicDifficultyCalculator(bombDelay), ethash.MakeDifficultyCalculatorU256(bombDelay)}, + } { + want := pair.bigFn(time, header) + have := pair.u256Fn(time, header) + if want.Cmp(have) != 0 { + panic(fmt.Sprintf("pair %d: want %x have %x\nparent.Number: %x\np.Time: %x\nc.Time: %x\nBombdelay: %v\n", i, want, have, + header.Number, header.Time, time, bombDelay)) + } + } + return 1 +} diff --git a/tests/fuzzers/keystore/keystore-fuzzer.go b/tests/fuzzers/keystore/keystore-fuzzer.go index 704f29dc48..e3bcae92e1 100644 --- a/tests/fuzzers/keystore/keystore-fuzzer.go +++ b/tests/fuzzers/keystore/keystore-fuzzer.go @@ -33,5 +33,5 @@ func Fuzz(input []byte) int { panic(err) } os.Remove(a.URL.Path) - return 0 + return 1 } diff --git a/p2p/discv5/sim_testmain_test.go b/tests/fuzzers/les/debug/main.go similarity index 54% rename from p2p/discv5/sim_testmain_test.go rename to tests/fuzzers/les/debug/main.go index 77e751c419..09e087d4c8 100644 --- a/p2p/discv5/sim_testmain_test.go +++ b/tests/fuzzers/les/debug/main.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// Copyright 2020 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,30 +14,28 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build go1.4,nacl,faketime_simulation - -package discv5 +package main import ( + "fmt" + "io/ioutil" "os" - "runtime" - "testing" - "unsafe" -) - -// Enable fake time mode in the runtime, like on the go playground. -// There is a slight chance that this won't work because some go code -// might have executed before the variable is set. -//go:linkname faketime runtime.faketime -var faketime = 1 - -func TestMain(m *testing.M) { - // We need to use unsafe somehow in order to get access to go:linkname. - _ = unsafe.Sizeof(0) + "github.com/ethereum/go-ethereum/tests/fuzzers/les" +) - // Run the actual test. runWithPlaygroundTime ensures that the only test - // that runs is the one calling it. - runtime.GOMAXPROCS(8) - os.Exit(m.Run()) +func main() { + if len(os.Args) != 2 { + fmt.Fprintf(os.Stderr, "Usage: debug \n") + fmt.Fprintf(os.Stderr, "Example\n") + fmt.Fprintf(os.Stderr, " $ debug ../crashers/4bbef6857c733a87ecf6fd8b9e7238f65eb9862a\n") + os.Exit(1) + } + crasher := os.Args[1] + data, err := ioutil.ReadFile(crasher) + if err != nil { + fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err) + os.Exit(1) + } + les.Fuzz(data) } diff --git a/tests/fuzzers/les/les-fuzzer.go b/tests/fuzzers/les/les-fuzzer.go new file mode 100644 index 0000000000..f8fe8c78c4 --- /dev/null +++ b/tests/fuzzers/les/les-fuzzer.go @@ -0,0 +1,409 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "bytes" + "encoding/binary" + "io" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + l "github.com/ethereum/go-ethereum/les" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/params/types/genesisT" + "github.com/ethereum/go-ethereum/params/vars" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +var ( + bankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + bankAddr = crypto.PubkeyToAddress(bankKey.PublicKey) + bankFunds = new(big.Int).Mul(big.NewInt(100), big.NewInt(vars.Ether)) + + testChainLen = 256 + testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056") + + chain *core.BlockChain + addrHashes []common.Hash + txHashes []common.Hash + + chtTrie *trie.Trie + bloomTrie *trie.Trie + chtKeys [][]byte + bloomKeys [][]byte +) + +func makechain() (bc *core.BlockChain, addrHashes, txHashes []common.Hash) { + db := rawdb.NewMemoryDatabase() + gspec := &genesisT.Genesis{ + Config: params.TestChainConfig, + Alloc: genesisT.GenesisAlloc{bankAddr: {Balance: bankFunds}}, + GasLimit: 100000000, + } + genesis := core.MustCommitGenesis(db, gspec) + signer := types.HomesteadSigner{} + blocks, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, testChainLen, + func(i int, gen *core.BlockGen) { + var ( + tx *types.Transaction + addr common.Address + ) + nonce := uint64(i) + if i%4 == 0 { + tx, _ = types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 200000, big.NewInt(0), testContractCode), signer, bankKey) + addr = crypto.CreateAddress(bankAddr, nonce) + } else { + addr = common.BigToAddress(big.NewInt(int64(i))) + tx, _ = types.SignTx(types.NewTransaction(nonce, addr, big.NewInt(10000), vars.TxGas, big.NewInt(vars.GWei), nil), signer, bankKey) + } + gen.AddTx(tx) + addrHashes = append(addrHashes, crypto.Keccak256Hash(addr[:])) + txHashes = append(txHashes, tx.Hash()) + }) + bc, _ = core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + if _, err := bc.InsertChain(blocks); err != nil { + panic(err) + } + return +} + +func makeTries() (chtTrie *trie.Trie, bloomTrie *trie.Trie, chtKeys, bloomKeys [][]byte) { + chtTrie, _ = trie.New(common.Hash{}, trie.NewDatabase(rawdb.NewMemoryDatabase())) + bloomTrie, _ = trie.New(common.Hash{}, trie.NewDatabase(rawdb.NewMemoryDatabase())) + for i := 0; i < testChainLen; i++ { + // The element in CHT is -> + key := make([]byte, 8) + binary.BigEndian.PutUint64(key, uint64(i+1)) + chtTrie.Update(key, []byte{0x1, 0xf}) + chtKeys = append(chtKeys, key) + + // The element in Bloom trie is <2 byte bit index> + -> bloom + key2 := make([]byte, 10) + binary.BigEndian.PutUint64(key2[2:], uint64(i+1)) + bloomTrie.Update(key2, []byte{0x2, 0xe}) + bloomKeys = append(bloomKeys, key2) + } + return +} + +func init() { + chain, addrHashes, txHashes = makechain() + chtTrie, bloomTrie, chtKeys, bloomKeys = makeTries() +} + +type fuzzer struct { + chain *core.BlockChain + pool *core.TxPool + + chainLen int + addr, txs []common.Hash + nonce uint64 + + chtKeys [][]byte + bloomKeys [][]byte + chtTrie *trie.Trie + bloomTrie *trie.Trie + + input io.Reader + exhausted bool +} + +func newFuzzer(input []byte) *fuzzer { + return &fuzzer{ + chain: chain, + chainLen: testChainLen, + addr: addrHashes, + txs: txHashes, + chtTrie: chtTrie, + bloomTrie: bloomTrie, + chtKeys: chtKeys, + bloomKeys: bloomKeys, + nonce: uint64(len(txHashes)), + pool: core.NewTxPool(core.DefaultTxPoolConfig, params.TestChainConfig, chain), + input: bytes.NewReader(input), + } +} + +func (f *fuzzer) read(size int) []byte { + out := make([]byte, size) + if _, err := f.input.Read(out); err != nil { + f.exhausted = true + } + return out +} + +func (f *fuzzer) randomByte() byte { + d := f.read(1) + return d[0] +} + +func (f *fuzzer) randomBool() bool { + d := f.read(1) + return d[0]&1 == 1 +} + +func (f *fuzzer) randomInt(max int) int { + if max == 0 { + return 0 + } + if max <= 256 { + return int(f.randomByte()) % max + } + var a uint16 + if err := binary.Read(f.input, binary.LittleEndian, &a); err != nil { + f.exhausted = true + } + return int(a % uint16(max)) +} + +func (f *fuzzer) randomX(max int) uint64 { + var a uint16 + if err := binary.Read(f.input, binary.LittleEndian, &a); err != nil { + f.exhausted = true + } + if a < 0x8000 { + return uint64(a%uint16(max+1)) - 1 + } + return (uint64(1)<<(a%64+1) - 1) & (uint64(a) * 343897772345826595) +} + +func (f *fuzzer) randomBlockHash() common.Hash { + h := f.chain.GetCanonicalHash(uint64(f.randomInt(3 * f.chainLen))) + if h != (common.Hash{}) { + return h + } + return common.BytesToHash(f.read(common.HashLength)) +} + +func (f *fuzzer) randomAddrHash() []byte { + i := f.randomInt(3 * len(f.addr)) + if i < len(f.addr) { + return f.addr[i].Bytes() + } + return f.read(common.HashLength) +} + +func (f *fuzzer) randomCHTTrieKey() []byte { + i := f.randomInt(3 * len(f.chtKeys)) + if i < len(f.chtKeys) { + return f.chtKeys[i] + } + return f.read(8) +} + +func (f *fuzzer) randomBloomTrieKey() []byte { + i := f.randomInt(3 * len(f.bloomKeys)) + if i < len(f.bloomKeys) { + return f.bloomKeys[i] + } + return f.read(10) +} + +func (f *fuzzer) randomTxHash() common.Hash { + i := f.randomInt(3 * len(f.txs)) + if i < len(f.txs) { + return f.txs[i] + } + return common.BytesToHash(f.read(common.HashLength)) +} + +func (f *fuzzer) BlockChain() *core.BlockChain { + return f.chain +} + +func (f *fuzzer) TxPool() *core.TxPool { + return f.pool +} + +func (f *fuzzer) ArchiveMode() bool { + return false +} + +func (f *fuzzer) AddTxsSync() bool { + return false +} + +func (f *fuzzer) GetHelperTrie(typ uint, index uint64) *trie.Trie { + if typ == 0 { + return f.chtTrie + } else if typ == 1 { + return f.bloomTrie + } + return nil +} + +type dummyMsg struct { + data []byte +} + +func (d dummyMsg) Decode(val interface{}) error { + return rlp.DecodeBytes(d.data, val) +} + +func (f *fuzzer) doFuzz(msgCode uint64, packet interface{}) { + enc, err := rlp.EncodeToBytes(packet) + if err != nil { + panic(err) + } + version := f.randomInt(3) + 2 // [LES2, LES3, LES4] + peer, closeFn := l.NewFuzzerPeer(version) + defer closeFn() + fn, _, _, err := l.Les3[msgCode].Handle(dummyMsg{enc}) + if err != nil { + panic(err) + } + fn(f, peer, func() bool { return true }) +} + +func Fuzz(input []byte) int { + // We expect some large inputs + if len(input) < 100 { + return -1 + } + f := newFuzzer(input) + if f.exhausted { + return -1 + } + for !f.exhausted { + switch f.randomInt(8) { + case 0: + req := &l.GetBlockHeadersPacket{ + Query: l.GetBlockHeadersData{ + Amount: f.randomX(l.MaxHeaderFetch + 1), + Skip: f.randomX(10), + Reverse: f.randomBool(), + }, + } + if f.randomBool() { + req.Query.Origin.Hash = f.randomBlockHash() + } else { + req.Query.Origin.Number = uint64(f.randomInt(f.chainLen * 2)) + } + f.doFuzz(l.GetBlockHeadersMsg, req) + + case 1: + req := &l.GetBlockBodiesPacket{Hashes: make([]common.Hash, f.randomInt(l.MaxBodyFetch+1))} + for i := range req.Hashes { + req.Hashes[i] = f.randomBlockHash() + } + f.doFuzz(l.GetBlockBodiesMsg, req) + + case 2: + req := &l.GetCodePacket{Reqs: make([]l.CodeReq, f.randomInt(l.MaxCodeFetch+1))} + for i := range req.Reqs { + req.Reqs[i] = l.CodeReq{ + BHash: f.randomBlockHash(), + AccKey: f.randomAddrHash(), + } + } + f.doFuzz(l.GetCodeMsg, req) + + case 3: + req := &l.GetReceiptsPacket{Hashes: make([]common.Hash, f.randomInt(l.MaxReceiptFetch+1))} + for i := range req.Hashes { + req.Hashes[i] = f.randomBlockHash() + } + f.doFuzz(l.GetReceiptsMsg, req) + + case 4: + req := &l.GetProofsPacket{Reqs: make([]l.ProofReq, f.randomInt(l.MaxProofsFetch+1))} + for i := range req.Reqs { + if f.randomBool() { + req.Reqs[i] = l.ProofReq{ + BHash: f.randomBlockHash(), + AccKey: f.randomAddrHash(), + Key: f.randomAddrHash(), + FromLevel: uint(f.randomX(3)), + } + } else { + req.Reqs[i] = l.ProofReq{ + BHash: f.randomBlockHash(), + Key: f.randomAddrHash(), + FromLevel: uint(f.randomX(3)), + } + } + } + f.doFuzz(l.GetProofsV2Msg, req) + + case 5: + req := &l.GetHelperTrieProofsPacket{Reqs: make([]l.HelperTrieReq, f.randomInt(l.MaxHelperTrieProofsFetch+1))} + for i := range req.Reqs { + switch f.randomInt(3) { + case 0: + // Canonical hash trie + req.Reqs[i] = l.HelperTrieReq{ + Type: 0, + TrieIdx: f.randomX(3), + Key: f.randomCHTTrieKey(), + FromLevel: uint(f.randomX(3)), + AuxReq: uint(2), + } + case 1: + // Bloom trie + req.Reqs[i] = l.HelperTrieReq{ + Type: 1, + TrieIdx: f.randomX(3), + Key: f.randomBloomTrieKey(), + FromLevel: uint(f.randomX(3)), + AuxReq: 0, + } + default: + // Random trie + req.Reqs[i] = l.HelperTrieReq{ + Type: 2, + TrieIdx: f.randomX(3), + Key: f.randomCHTTrieKey(), + FromLevel: uint(f.randomX(3)), + AuxReq: 0, + } + } + } + f.doFuzz(l.GetHelperTrieProofsMsg, req) + + case 6: + req := &l.SendTxPacket{Txs: make([]*types.Transaction, f.randomInt(l.MaxTxSend+1))} + signer := types.HomesteadSigner{} + for i := range req.Txs { + var nonce uint64 + if f.randomBool() { + nonce = uint64(f.randomByte()) + } else { + nonce = f.nonce + f.nonce += 1 + } + req.Txs[i], _ = types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(10000), vars.TxGas, big.NewInt(1000000000*int64(f.randomByte())), nil), signer, bankKey) + } + f.doFuzz(l.SendTxV2Msg, req) + + case 7: + req := &l.GetTxStatusPacket{Hashes: make([]common.Hash, f.randomInt(l.MaxTxStatus+1))} + for i := range req.Hashes { + req.Hashes[i] = f.randomTxHash() + } + f.doFuzz(l.GetTxStatusMsg, req) + } + } + return 0 +} diff --git a/tests/fuzzers/rangeproof/corpus/1c14030f26872e57bf1481084f151d71eed8161c-1 b/tests/fuzzers/rangeproof/corpus/1c14030f26872e57bf1481084f151d71eed8161c-1 new file mode 100644 index 0000000000..31c08bafaf Binary files /dev/null and b/tests/fuzzers/rangeproof/corpus/1c14030f26872e57bf1481084f151d71eed8161c-1 differ diff --git a/tests/fuzzers/rangeproof/corpus/27e54254422543060a13ea8a4bc913d768e4adb6-2 b/tests/fuzzers/rangeproof/corpus/27e54254422543060a13ea8a4bc913d768e4adb6-2 new file mode 100644 index 0000000000..7bce13ef80 Binary files /dev/null and b/tests/fuzzers/rangeproof/corpus/27e54254422543060a13ea8a4bc913d768e4adb6-2 differ diff --git a/tests/fuzzers/rangeproof/corpus/6bfc2cbe2d7a43361e240118439785445a0fdfb7-5 b/tests/fuzzers/rangeproof/corpus/6bfc2cbe2d7a43361e240118439785445a0fdfb7-5 new file mode 100644 index 0000000000..613e76a020 Binary files /dev/null and b/tests/fuzzers/rangeproof/corpus/6bfc2cbe2d7a43361e240118439785445a0fdfb7-5 differ diff --git a/tests/fuzzers/rangeproof/corpus/a67e63bc0c0004bd009944a6061297cb7d4ac238-1 b/tests/fuzzers/rangeproof/corpus/a67e63bc0c0004bd009944a6061297cb7d4ac238-1 new file mode 100644 index 0000000000..805ad8df77 Binary files /dev/null and b/tests/fuzzers/rangeproof/corpus/a67e63bc0c0004bd009944a6061297cb7d4ac238-1 differ diff --git a/tests/fuzzers/rangeproof/corpus/ae892bbae0a843950bc8316496e595b1a194c009-4 b/tests/fuzzers/rangeproof/corpus/ae892bbae0a843950bc8316496e595b1a194c009-4 new file mode 100644 index 0000000000..605acf81c1 Binary files /dev/null and b/tests/fuzzers/rangeproof/corpus/ae892bbae0a843950bc8316496e595b1a194c009-4 differ diff --git a/tests/fuzzers/rangeproof/corpus/ee05d0d813f6261b3dba16506f9ea03d9c5e993d-2 b/tests/fuzzers/rangeproof/corpus/ee05d0d813f6261b3dba16506f9ea03d9c5e993d-2 new file mode 100644 index 0000000000..8f32dd775a Binary files /dev/null and b/tests/fuzzers/rangeproof/corpus/ee05d0d813f6261b3dba16506f9ea03d9c5e993d-2 differ diff --git a/tests/fuzzers/rangeproof/corpus/f50a6d57a46d30184aa294af5b252ab9701af7c9-2 b/tests/fuzzers/rangeproof/corpus/f50a6d57a46d30184aa294af5b252ab9701af7c9-2 new file mode 100644 index 0000000000..af96210f20 Binary files /dev/null and b/tests/fuzzers/rangeproof/corpus/f50a6d57a46d30184aa294af5b252ab9701af7c9-2 differ diff --git a/tests/fuzzers/rangeproof/corpus/random.dat b/tests/fuzzers/rangeproof/corpus/random.dat new file mode 100644 index 0000000000..2c998ad812 Binary files /dev/null and b/tests/fuzzers/rangeproof/corpus/random.dat differ diff --git a/tests/fuzzers/rangeproof/debug/main.go b/tests/fuzzers/rangeproof/debug/main.go new file mode 100644 index 0000000000..a81c69fea5 --- /dev/null +++ b/tests/fuzzers/rangeproof/debug/main.go @@ -0,0 +1,41 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package main + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/ethereum/go-ethereum/tests/fuzzers/rangeproof" +) + +func main() { + if len(os.Args) != 2 { + fmt.Fprintf(os.Stderr, "Usage: debug \n") + fmt.Fprintf(os.Stderr, "Example\n") + fmt.Fprintf(os.Stderr, " $ debug ../crashers/4bbef6857c733a87ecf6fd8b9e7238f65eb9862a\n") + os.Exit(1) + } + crasher := os.Args[1] + data, err := ioutil.ReadFile(crasher) + if err != nil { + fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err) + os.Exit(1) + } + rangeproof.Fuzz(data) +} diff --git a/tests/fuzzers/rangeproof/rangeproof-fuzzer.go b/tests/fuzzers/rangeproof/rangeproof-fuzzer.go new file mode 100644 index 0000000000..b82a380723 --- /dev/null +++ b/tests/fuzzers/rangeproof/rangeproof-fuzzer.go @@ -0,0 +1,218 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rangeproof + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "sort" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb/memorydb" + "github.com/ethereum/go-ethereum/trie" +) + +type kv struct { + k, v []byte + t bool +} + +type entrySlice []*kv + +func (p entrySlice) Len() int { return len(p) } +func (p entrySlice) Less(i, j int) bool { return bytes.Compare(p[i].k, p[j].k) < 0 } +func (p entrySlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +type fuzzer struct { + input io.Reader + exhausted bool +} + +func (f *fuzzer) randBytes(n int) []byte { + r := make([]byte, n) + if _, err := f.input.Read(r); err != nil { + f.exhausted = true + } + return r +} + +func (f *fuzzer) readInt() uint64 { + var x uint64 + if err := binary.Read(f.input, binary.LittleEndian, &x); err != nil { + f.exhausted = true + } + return x +} + +func (f *fuzzer) randomTrie(n int) (*trie.Trie, map[string]*kv) { + + trie := new(trie.Trie) + vals := make(map[string]*kv) + size := f.readInt() + // Fill it with some fluff + for i := byte(0); i < byte(size); i++ { + value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false} + value2 := &kv{common.LeftPadBytes([]byte{i + 10}, 32), []byte{i}, false} + trie.Update(value.k, value.v) + trie.Update(value2.k, value2.v) + vals[string(value.k)] = value + vals[string(value2.k)] = value2 + } + if f.exhausted { + return nil, nil + } + // And now fill with some random + for i := 0; i < n; i++ { + k := f.randBytes(32) + v := f.randBytes(20) + value := &kv{k, v, false} + trie.Update(k, v) + vals[string(k)] = value + if f.exhausted { + return nil, nil + } + } + return trie, vals +} + +func (f *fuzzer) fuzz() int { + maxSize := 200 + tr, vals := f.randomTrie(1 + int(f.readInt())%maxSize) + if f.exhausted { + return 0 // input too short + } + var entries entrySlice + for _, kv := range vals { + entries = append(entries, kv) + } + if len(entries) <= 1 { + return 0 + } + sort.Sort(entries) + + var ok = 0 + for { + start := int(f.readInt() % uint64(len(entries))) + end := 1 + int(f.readInt()%uint64(len(entries)-1)) + testcase := int(f.readInt() % uint64(6)) + index := int(f.readInt() & 0xFFFFFFFF) + index2 := int(f.readInt() & 0xFFFFFFFF) + if f.exhausted { + break + } + proof := memorydb.New() + if err := tr.Prove(entries[start].k, 0, proof); err != nil { + panic(fmt.Sprintf("Failed to prove the first node %v", err)) + } + if err := tr.Prove(entries[end-1].k, 0, proof); err != nil { + panic(fmt.Sprintf("Failed to prove the last node %v", err)) + } + var keys [][]byte + var vals [][]byte + for i := start; i < end; i++ { + keys = append(keys, entries[i].k) + vals = append(vals, entries[i].v) + } + if len(keys) == 0 { + return 0 + } + var first, last = keys[0], keys[len(keys)-1] + testcase %= 6 + switch testcase { + case 0: + // Modified key + keys[index%len(keys)] = f.randBytes(32) // In theory it can't be same + case 1: + // Modified val + vals[index%len(vals)] = f.randBytes(20) // In theory it can't be same + case 2: + // Gapped entry slice + index = index % len(keys) + keys = append(keys[:index], keys[index+1:]...) + vals = append(vals[:index], vals[index+1:]...) + case 3: + // Out of order + index1 := index % len(keys) + index2 := index2 % len(keys) + keys[index1], keys[index2] = keys[index2], keys[index1] + vals[index1], vals[index2] = vals[index2], vals[index1] + case 4: + // Set random key to nil, do nothing + keys[index%len(keys)] = nil + case 5: + // Set random value to nil, deletion + vals[index%len(vals)] = nil + + // Other cases: + // Modify something in the proof db + // add stuff to proof db + // drop stuff from proof db + + } + if f.exhausted { + break + } + ok = 1 + //nodes, subtrie + nodes, subtrie, notary, hasMore, err := trie.VerifyRangeProof(tr.Hash(), first, last, keys, vals, proof) + if err != nil { + if nodes != nil { + panic("err != nil && nodes != nil") + } + if subtrie != nil { + panic("err != nil && subtrie != nil") + } + if notary != nil { + panic("err != nil && notary != nil") + } + if hasMore { + panic("err != nil && hasMore == true") + } + } else { + if nodes == nil { + panic("err == nil && nodes == nil") + } + if subtrie == nil { + panic("err == nil && subtrie == nil") + } + if notary == nil { + panic("err == nil && subtrie == nil") + } + } + } + return ok +} + +// The function must return +// 1 if the fuzzer should increase priority of the +// given input during subsequent fuzzing (for example, the input is lexically +// correct and was parsed successfully); +// -1 if the input must not be added to corpus even if gives new coverage; and +// 0 otherwise; other values are reserved for future use. +func Fuzz(input []byte) int { + if len(input) < 100 { + return 0 + } + r := bytes.NewReader(input) + f := fuzzer{ + input: r, + exhausted: false, + } + return f.fuzz() +} diff --git a/tests/fuzzers/rlp/rlp_fuzzer.go b/tests/fuzzers/rlp/rlp_fuzzer.go index 534540476c..18b36287b5 100644 --- a/tests/fuzzers/rlp/rlp_fuzzer.go +++ b/tests/fuzzers/rlp/rlp_fuzzer.go @@ -37,17 +37,17 @@ func decodeEncode(input []byte, val interface{}, i int) { } func Fuzz(input []byte) int { + if len(input) == 0 { + return 0 + } + var i int { - if len(input) > 0 { - rlp.Split(input) - } + rlp.Split(input) } { - if len(input) > 0 { - if elems, _, err := rlp.SplitList(input); err == nil { - rlp.CountValues(elems) - } + if elems, _, err := rlp.SplitList(input); err == nil { + rlp.CountValues(elems) } } @@ -123,5 +123,5 @@ func Fuzz(input []byte) int { var rs types.Receipts decodeEncode(input, &rs, i) } - return 0 + return 1 } diff --git a/core/vm/runtime/fuzz.go b/tests/fuzzers/runtime/runtime_fuzz.go similarity index 82% rename from core/vm/runtime/fuzz.go rename to tests/fuzzers/runtime/runtime_fuzz.go index cb9ff08b5b..9b96045752 100644 --- a/core/vm/runtime/fuzz.go +++ b/tests/fuzzers/runtime/runtime_fuzz.go @@ -14,23 +14,23 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build gofuzz - package runtime +import ( + "github.com/ethereum/go-ethereum/core/vm/runtime" +) + // Fuzz is the basic entry point for the go-fuzz tool // // This returns 1 for valid parsable/runable code, 0 // for invalid opcode. func Fuzz(input []byte) int { - _, _, err := Execute(input, input, &Config{ - GasLimit: 3000000, + _, _, err := runtime.Execute(input, input, &runtime.Config{ + GasLimit: 12000000, }) - // invalid opcode - if err != nil && len(err.Error()) > 6 && string(err.Error()[:7]) == "invalid" { + if err != nil && len(err.Error()) > 6 && err.Error()[:7] == "invalid" { return 0 } - return 1 } diff --git a/tests/fuzzers/stacktrie/trie_fuzzer.go b/tests/fuzzers/stacktrie/trie_fuzzer.go index a072ff772d..5cea7769c2 100644 --- a/tests/fuzzers/stacktrie/trie_fuzzer.go +++ b/tests/fuzzers/stacktrie/trie_fuzzer.go @@ -148,6 +148,8 @@ func (f *fuzzer) fuzz() int { vals kvs useful bool maxElements = 10000 + // operate on unique keys only + keys = make(map[string]struct{}) ) // Fill the trie with elements for i := 0; !f.exhausted && i < maxElements; i++ { @@ -158,6 +160,11 @@ func (f *fuzzer) fuzz() int { // thus 'deletion' which is not supported on stacktrie break } + if _, present := keys[string(k)]; present { + // This key is a duplicate, ignore it + continue + } + keys[string(k)] = struct{}{} vals = append(vals, kv{k: k, v: v}) trieA.Update(k, v) useful = true diff --git a/tests/fuzzers/txfetcher/txfetcher_fuzzer.go b/tests/fuzzers/txfetcher/txfetcher_fuzzer.go index 10c7eb9424..d1d6fdc665 100644 --- a/tests/fuzzers/txfetcher/txfetcher_fuzzer.go +++ b/tests/fuzzers/txfetcher/txfetcher_fuzzer.go @@ -51,8 +51,9 @@ func init() { func Fuzz(input []byte) int { // Don't generate insanely large test cases, not much value in them if len(input) > 16*1024 { - return -1 + return 0 } + verbose := false r := bytes.NewReader(input) // Reduce the problem space for certain fuzz runs. Small tx space is better @@ -124,7 +125,9 @@ func Fuzz(input []byte) int { announceIdxs[i] = (int(annBuf[0])*256 + int(annBuf[1])) % len(txs) announces[i] = txs[announceIdxs[i]].Hash() } - fmt.Println("Notify", peer, announceIdxs) + if verbose { + fmt.Println("Notify", peer, announceIdxs) + } if err := f.Notify(peer, announces); err != nil { panic(err) } @@ -163,8 +166,9 @@ func Fuzz(input []byte) int { return 0 } direct := (directFlag % 2) == 0 - - fmt.Println("Enqueue", peer, deliverIdxs, direct) + if verbose { + fmt.Println("Enqueue", peer, deliverIdxs, direct) + } if err := f.Enqueue(peer, deliveries, direct); err != nil { panic(err) } @@ -177,8 +181,9 @@ func Fuzz(input []byte) int { return 0 } peer := peers[int(peerIdx)%len(peers)] - - fmt.Println("Drop", peer) + if verbose { + fmt.Println("Drop", peer) + } if err := f.Drop(peer); err != nil { panic(err) } @@ -191,8 +196,9 @@ func Fuzz(input []byte) int { return 0 } tick := time.Duration(tickCnt) * 100 * time.Millisecond - - fmt.Println("Sleep", tick) + if verbose { + fmt.Println("Sleep", tick) + } clock.Run(tick) } } diff --git a/tests/fuzzers/vflux/clientpool-fuzzer.go b/tests/fuzzers/vflux/clientpool-fuzzer.go new file mode 100644 index 0000000000..41b8627348 --- /dev/null +++ b/tests/fuzzers/vflux/clientpool-fuzzer.go @@ -0,0 +1,289 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vflux + +import ( + "bytes" + "encoding/binary" + "io" + "math" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/ethdb/memorydb" + "github.com/ethereum/go-ethereum/les/vflux" + vfs "github.com/ethereum/go-ethereum/les/vflux/server" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" + "github.com/ethereum/go-ethereum/rlp" +) + +type fuzzer struct { + peers [256]*clientPeer + disconnectList []*clientPeer + input io.Reader + exhausted bool + activeCount, activeCap uint64 + maxCount, maxCap uint64 +} + +type clientPeer struct { + fuzzer *fuzzer + node *enode.Node + freeID string + timeout time.Duration + + balance vfs.ConnectedBalance + capacity uint64 +} + +func (p *clientPeer) Node() *enode.Node { + return p.node +} + +func (p *clientPeer) FreeClientId() string { + return p.freeID +} + +func (p *clientPeer) InactiveAllowance() time.Duration { + return p.timeout +} + +func (p *clientPeer) UpdateCapacity(newCap uint64, requested bool) { + p.fuzzer.activeCap -= p.capacity + if p.capacity != 0 { + p.fuzzer.activeCount-- + } + p.capacity = newCap + p.fuzzer.activeCap += p.capacity + if p.capacity != 0 { + p.fuzzer.activeCount++ + } +} + +func (p *clientPeer) Disconnect() { + p.fuzzer.disconnectList = append(p.fuzzer.disconnectList, p) + p.fuzzer.activeCap -= p.capacity + if p.capacity != 0 { + p.fuzzer.activeCount-- + } + p.capacity = 0 + p.balance = nil +} + +func newFuzzer(input []byte) *fuzzer { + f := &fuzzer{ + input: bytes.NewReader(input), + } + for i := range f.peers { + f.peers[i] = &clientPeer{ + fuzzer: f, + node: enode.SignNull(new(enr.Record), enode.ID{byte(i)}), + freeID: string([]byte{byte(i)}), + timeout: f.randomDelay(), + } + } + return f +} + +func (f *fuzzer) read(size int) []byte { + out := make([]byte, size) + if _, err := f.input.Read(out); err != nil { + f.exhausted = true + } + return out +} + +func (f *fuzzer) randomByte() byte { + d := f.read(1) + return d[0] +} + +func (f *fuzzer) randomBool() bool { + d := f.read(1) + return d[0]&1 == 1 +} + +func (f *fuzzer) randomInt(max int) int { + if max == 0 { + return 0 + } + if max <= 256 { + return int(f.randomByte()) % max + } + var a uint16 + if err := binary.Read(f.input, binary.LittleEndian, &a); err != nil { + f.exhausted = true + } + return int(a % uint16(max)) +} + +func (f *fuzzer) randomTokenAmount(signed bool) int64 { + x := uint64(f.randomInt(65000)) + x = x * x * x * x + + if signed && (x&1) == 1 { + if x <= math.MaxInt64 { + return -int64(x) + } + return math.MinInt64 + } + if x <= math.MaxInt64 { + return int64(x) + } + return math.MaxInt64 +} + +func (f *fuzzer) randomDelay() time.Duration { + delay := f.randomByte() + if delay < 128 { + return time.Duration(delay) * time.Second + } + return 0 +} + +func (f *fuzzer) randomFactors() vfs.PriceFactors { + return vfs.PriceFactors{ + TimeFactor: float64(f.randomByte()) / 25500, + CapacityFactor: float64(f.randomByte()) / 255, + RequestFactor: float64(f.randomByte()) / 255, + } +} + +func (f *fuzzer) connectedBalanceOp(balance vfs.ConnectedBalance) { + switch f.randomInt(3) { + case 0: + balance.RequestServed(uint64(f.randomTokenAmount(false))) + case 1: + balance.SetPriceFactors(f.randomFactors(), f.randomFactors()) + case 2: + balance.GetBalance() + balance.GetRawBalance() + balance.GetPriceFactors() + } +} + +func (f *fuzzer) atomicBalanceOp(balance vfs.AtomicBalanceOperator) { + switch f.randomInt(3) { + case 0: + balance.AddBalance(f.randomTokenAmount(true)) + case 1: + balance.SetBalance(uint64(f.randomTokenAmount(false)), uint64(f.randomTokenAmount(false))) + case 2: + balance.GetBalance() + balance.GetRawBalance() + balance.GetPriceFactors() + } +} + +func FuzzClientPool(input []byte) int { + if len(input) > 10000 { + return -1 + } + f := newFuzzer(input) + if f.exhausted { + return 0 + } + clock := &mclock.Simulated{} + db := memorydb.New() + pool := vfs.NewClientPool(db, 10, f.randomDelay(), clock, func() bool { return true }) + pool.Start() + defer pool.Stop() + + count := 0 + for !f.exhausted && count < 1000 { + count++ + switch f.randomInt(11) { + case 0: + i := int(f.randomByte()) + f.peers[i].balance = pool.Register(f.peers[i]) + case 1: + i := int(f.randomByte()) + f.peers[i].Disconnect() + case 2: + f.maxCount = uint64(f.randomByte()) + f.maxCap = uint64(f.randomByte()) + f.maxCap *= f.maxCap + pool.SetLimits(f.maxCount, f.maxCap) + case 3: + pool.SetConnectedBias(f.randomDelay()) + case 4: + pool.SetDefaultFactors(f.randomFactors(), f.randomFactors()) + case 5: + pool.SetExpirationTCs(uint64(f.randomInt(50000)), uint64(f.randomInt(50000))) + case 6: + if _, err := pool.SetCapacity(f.peers[f.randomByte()].node, uint64(f.randomByte()), f.randomDelay(), f.randomBool()); err == vfs.ErrCantFindMaximum { + panic(nil) + } + case 7: + if balance := f.peers[f.randomByte()].balance; balance != nil { + f.connectedBalanceOp(balance) + } + case 8: + pool.BalanceOperation(f.peers[f.randomByte()].node.ID(), f.peers[f.randomByte()].freeID, func(balance vfs.AtomicBalanceOperator) { + count := f.randomInt(4) + for i := 0; i < count; i++ { + f.atomicBalanceOp(balance) + } + }) + case 9: + pool.TotalTokenAmount() + pool.GetExpirationTCs() + pool.Active() + pool.Limits() + pool.GetPosBalanceIDs(f.peers[f.randomByte()].node.ID(), f.peers[f.randomByte()].node.ID(), f.randomInt(100)) + case 10: + req := vflux.CapacityQueryReq{ + Bias: uint64(f.randomByte()), + AddTokens: make([]vflux.IntOrInf, f.randomInt(vflux.CapacityQueryMaxLen+1)), + } + for i := range req.AddTokens { + v := vflux.IntOrInf{Type: uint8(f.randomInt(4))} + if v.Type < 2 { + v.Value = *big.NewInt(f.randomTokenAmount(false)) + } + req.AddTokens[i] = v + } + reqEnc, err := rlp.EncodeToBytes(&req) + if err != nil { + panic(err) + } + p := int(f.randomByte()) + if p < len(reqEnc) { + reqEnc[p] = f.randomByte() + } + pool.Handle(f.peers[f.randomByte()].node.ID(), f.peers[f.randomByte()].freeID, vflux.CapacityQueryName, reqEnc) + } + + for _, peer := range f.disconnectList { + pool.Unregister(peer) + } + f.disconnectList = nil + if d := f.randomDelay(); d > 0 { + clock.Run(d) + } + //fmt.Println(f.activeCount, f.maxCount, f.activeCap, f.maxCap) + if activeCount, activeCap := pool.Active(); activeCount != f.activeCount || activeCap != f.activeCap { + panic(nil) + } + if f.activeCount > f.maxCount || f.activeCap > f.maxCap { + panic(nil) + } + } + return 0 +} diff --git a/tests/fuzzers/vflux/debug/main.go b/tests/fuzzers/vflux/debug/main.go new file mode 100644 index 0000000000..de0b5d4124 --- /dev/null +++ b/tests/fuzzers/vflux/debug/main.go @@ -0,0 +1,41 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package main + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/ethereum/go-ethereum/tests/fuzzers/vflux" +) + +func main() { + if len(os.Args) != 2 { + fmt.Fprintf(os.Stderr, "Usage: debug \n") + fmt.Fprintf(os.Stderr, "Example\n") + fmt.Fprintf(os.Stderr, " $ debug ../crashers/4bbef6857c733a87ecf6fd8b9e7238f65eb9862a\n") + os.Exit(1) + } + crasher := os.Args[1] + data, err := ioutil.ReadFile(crasher) + if err != nil { + fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err) + os.Exit(1) + } + vflux.FuzzClientPool(data) +} diff --git a/tests/gen_stenv.go b/tests/gen_stenv.go index 1d4baf2fd7..bfecc145b4 100644 --- a/tests/gen_stenv.go +++ b/tests/gen_stenv.go @@ -13,6 +13,7 @@ import ( var _ = (*stEnvMarshaling)(nil) +// MarshalJSON marshals as JSON. func (s stEnv) MarshalJSON() ([]byte, error) { type stEnv struct { Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` @@ -30,6 +31,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) { return json.Marshal(&enc) } +// UnmarshalJSON unmarshals from JSON. func (s *stEnv) UnmarshalJSON(input []byte) error { type stEnv struct { Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` diff --git a/tests/gen_sttransaction.go b/tests/gen_sttransaction.go index 451ffcbf43..2670f4f9c8 100644 --- a/tests/gen_sttransaction.go +++ b/tests/gen_sttransaction.go @@ -8,25 +8,29 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/types" ) var _ = (*stTransactionMarshaling)(nil) +// MarshalJSON marshals as JSON. func (s stTransaction) MarshalJSON() ([]byte, error) { type stTransaction struct { - GasPrice *math.HexOrDecimal256 `json:"gasPrice"` - Nonce math.HexOrDecimal64 `json:"nonce"` - To string `json:"to"` - Data []string `json:"data"` - GasLimit []math.HexOrDecimal64 `json:"gasLimit"` - Value []string `json:"value"` - PrivateKey hexutil.Bytes `json:"secretKey"` + GasPrice *math.HexOrDecimal256 `json:"gasPrice"` + Nonce math.HexOrDecimal64 `json:"nonce"` + To string `json:"to"` + Data []string `json:"data"` + AccessLists []*types.AccessList `json:"accessLists,omitempty"` + GasLimit []math.HexOrDecimal64 `json:"gasLimit"` + Value []string `json:"value"` + PrivateKey hexutil.Bytes `json:"secretKey"` } var enc stTransaction enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice) enc.Nonce = math.HexOrDecimal64(s.Nonce) enc.To = s.To enc.Data = s.Data + enc.AccessLists = s.AccessLists if s.GasLimit != nil { enc.GasLimit = make([]math.HexOrDecimal64, len(s.GasLimit)) for k, v := range s.GasLimit { @@ -38,15 +42,17 @@ func (s stTransaction) MarshalJSON() ([]byte, error) { return json.Marshal(&enc) } +// UnmarshalJSON unmarshals from JSON. func (s *stTransaction) UnmarshalJSON(input []byte) error { type stTransaction struct { - GasPrice *math.HexOrDecimal256 `json:"gasPrice"` - Nonce *math.HexOrDecimal64 `json:"nonce"` - To *string `json:"to"` - Data []string `json:"data"` - GasLimit []math.HexOrDecimal64 `json:"gasLimit"` - Value []string `json:"value"` - PrivateKey *hexutil.Bytes `json:"secretKey"` + GasPrice *math.HexOrDecimal256 `json:"gasPrice"` + Nonce *math.HexOrDecimal64 `json:"nonce"` + To *string `json:"to"` + Data []string `json:"data"` + AccessLists []*types.AccessList `json:"accessLists,omitempty"` + GasLimit []math.HexOrDecimal64 `json:"gasLimit"` + Value []string `json:"value"` + PrivateKey *hexutil.Bytes `json:"secretKey"` } var dec stTransaction if err := json.Unmarshal(input, &dec); err != nil { @@ -64,6 +70,9 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error { if dec.Data != nil { s.Data = dec.Data } + if dec.AccessLists != nil { + s.AccessLists = dec.AccessLists + } if dec.GasLimit != nil { s.GasLimit = make([]uint64, len(dec.GasLimit)) for k, v := range dec.GasLimit { diff --git a/tests/gen_vmexec.go b/tests/gen_vmexec.go index a5f01cf456..2fe155152d 100644 --- a/tests/gen_vmexec.go +++ b/tests/gen_vmexec.go @@ -14,6 +14,7 @@ import ( var _ = (*vmExecMarshaling)(nil) +// MarshalJSON marshals as JSON. func (v vmExec) MarshalJSON() ([]byte, error) { type vmExec struct { Address common.UnprefixedAddress `json:"address" gencodec:"required"` @@ -37,6 +38,7 @@ func (v vmExec) MarshalJSON() ([]byte, error) { return json.Marshal(&enc) } +// UnmarshalJSON unmarshals from JSON. func (v *vmExec) UnmarshalJSON(input []byte) error { type vmExec struct { Address *common.UnprefixedAddress `json:"address" gencodec:"required"` diff --git a/tests/init.go b/tests/init.go index 6732d23be9..87cc8e1451 100644 --- a/tests/init.go +++ b/tests/init.go @@ -253,7 +253,7 @@ var Forks = map[string]ctypes.ChainConfigurator{ PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(5), }, - "YOLOv2": &goethereum.ChainConfig{ + "YOLOv3": &goethereum.ChainConfig{ Clique: new(ctypes.CliqueConfig), ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), @@ -264,11 +264,12 @@ var Forks = map[string]ctypes.ChainConfigurator{ ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), - YoloV2Block: big.NewInt(0), + YoloV3Block: big.NewInt(0), }, - // This specification is subject to change, but is for now identical to YOLOv2 + // This specification is subject to change, but is for now identical to YOLOv3 // for cross-client testing purposes "Berlin": &goethereum.ChainConfig{ + Ethash: new(ctypes.EthashConfig), ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), EIP150Block: big.NewInt(0), @@ -278,7 +279,52 @@ var Forks = map[string]ctypes.ChainConfigurator{ ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), - YoloV2Block: big.NewInt(0), + BerlinBlock: big.NewInt(0), + }, + "ETC_Magneto": &coregeth.CoreGethChainConfig{ + NetworkID: 1, + Ethash: new(ctypes.EthashConfig), + ChainID: big.NewInt(61), + EIP2FBlock: big.NewInt(0), + EIP7FBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP160FBlock: big.NewInt(0), + EIP161FBlock: big.NewInt(0), + EIP170FBlock: big.NewInt(0), + EIP100FBlock: big.NewInt(0), + EIP140FBlock: big.NewInt(0), + EIP198FBlock: big.NewInt(0), + EIP211FBlock: big.NewInt(0), + EIP212FBlock: big.NewInt(0), + EIP213FBlock: big.NewInt(0), + EIP214FBlock: big.NewInt(0), + EIP658FBlock: big.NewInt(0), + EIP145FBlock: big.NewInt(0), + EIP1014FBlock: big.NewInt(0), + EIP1052FBlock: big.NewInt(0), + EIP1283FBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + // Istanbul eq, aka Phoenix + // ECIP-1088 + EIP152FBlock: big.NewInt(0), + EIP1108FBlock: big.NewInt(0), + EIP1344FBlock: big.NewInt(0), + EIP1884FBlock: big.NewInt(0), + EIP2028FBlock: big.NewInt(0), + EIP2200FBlock: big.NewInt(0), // RePetersburg (=~ re-1283) + + // Berlin + EIP2565FBlock: big.NewInt(0), + EIP2929FBlock: big.NewInt(0), + EIP2718FBlock: big.NewInt(0), + EIP2930FBlock: big.NewInt(0), + + DisposalBlock: big.NewInt(0), + ECIP1017FBlock: big.NewInt(5000000), // FIXME(meows) maybe + ECIP1017EraRounds: big.NewInt(5000000), + ECIP1010PauseBlock: nil, + ECIP1010Length: nil, }, } diff --git a/tests/init_mgen.go b/tests/init_mgen.go index 85880a08d4..bc47db4152 100644 --- a/tests/init_mgen.go +++ b/tests/init_mgen.go @@ -19,18 +19,17 @@ package tests import ( "encoding/json" "fmt" - "strings" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/internal/build" ) // This file holds variable and type relating specifically // to the task of generating tests. var ( - CG_GENERATE_STATE_TESTS_KEY = "COREGETH_TESTS_GENERATE_STATE_TESTS" - CG_GENERATE_DIFFICULTY_TESTS_KEY = "COREGETH_TESTS_GENERATE_DIFFICULTY_TESTS" + CG_GENERATE_STATE_TESTS_KEY = "COREGETH_TESTS_GENERATE_STATE_TESTS" + CG_GENERATE_DIFFICULTY_TESTS_KEY = "COREGETH_TESTS_GENERATE_DIFFICULTY_TESTS" + CG_GENERATE_DIFFICULTY_TEST_CONFIGS_KEY = "COREGETH_TESTS_GENERATE_DIFFICULTY_TESTS_CONFIGS" // Feature Equivalence tests use convert.Convert to // run tests using alternating ChainConfig data type implementations. @@ -44,6 +43,11 @@ var ( // when applicable as equivalent config implementations for the default Go data type // configs. CG_CHAINCONFIG_CHAINSPECS_OPENETHEREUM_KEY = "COREGETH_TESTS_CHAINCONFIG_OPENETHEREUM_SPECS" + + // CG_CHAINCONFIG_CHAINSPECS_COREGETH_KEY uses core-geth config data (in params/core-geth.json.d) + // when applicable as equivalent config implementations for the default Go data type + // configs. + CG_CHAINCONFIG_CHAINSPECS_COREGETH_KEY = "COREGETH_TESTS_CHAINCONFIG_COREGETH_SPECS" ) type chainspecRefsT map[string]chainspecRef @@ -86,11 +90,3 @@ func (c chainspecRef) MarshalJSON() ([]byte, error) { return json.MarshalIndent(x, "", " ") } - -// submoduleParentRef captures the current git status of the tests submodule. -// This is used for reference when writing tests. -var submoduleParentRef = func() string { - subModOut := build.RunGit("submodule", "status") - subModOut = strings.ReplaceAll(strings.TrimSpace(subModOut), " ", "_") - return subModOut -}() diff --git a/tests/init_test.go b/tests/init_test.go index ec6196de41..7165fd7d30 100644 --- a/tests/init_test.go +++ b/tests/init_test.go @@ -122,6 +122,7 @@ type testMatcher struct { slowpat []*regexp.Regexp skipforkpat []*regexp.Regexp whitelistpat *regexp.Regexp + noParallel bool } type testConfig struct { @@ -188,7 +189,7 @@ func (tm *testMatcher) findSkip(name string) (reason string, skipload bool) { } // findConfig returns the chain config matching defined patterns. -func (tm *testMatcher) findConfig(name string) (ctypes.ChainConfigurator, string) { +func (tm *testMatcher) findConfig(name string) (config ctypes.ChainConfigurator, configRegexKey string) { // TODO(fjl): name can be derived from testing.T when min Go version is 1.8 for _, m := range tm.configpat { if m.p.MatchString(name) { @@ -258,12 +259,15 @@ func (tm *testMatcher) runTestFile(t *testing.T, path, name string, runTest inte t.Skip("Skipped by whitelist") } } - t.Parallel() + if !tm.noParallel { + t.Parallel() + } // Load the file as map[string]. m := makeMapFromTestFunc(runTest) if err := readJSONFile(path, m.Addr().Interface()); err != nil { - t.Fatal(err) + panic(err) + // t.Fatal(err) } // Run all tests from the map. Don't wrap in a subtest if there is only one test in the file. diff --git a/tests/params.go b/tests/params.go index 87a3b559b3..4365462461 100644 --- a/tests/params.go +++ b/tests/params.go @@ -30,12 +30,20 @@ import ( "github.com/ethereum/go-ethereum/params/confp/tconvert" "github.com/ethereum/go-ethereum/params/types/coregeth" "github.com/ethereum/go-ethereum/params/types/ctypes" + "github.com/ethereum/go-ethereum/params/types/genesisT" "github.com/ethereum/go-ethereum/params/types/multigeth" "github.com/ethereum/go-ethereum/params/types/parity" + "github.com/iancoleman/strcase" ) +// paritySpecsDir is where parity specs for testing are stored. +// These are DEPRECATED. +// Use core-geth configurations instead. var paritySpecsDir = filepath.Join("..", "params", "parity.json.d") +// paritySpecPath is a test utility function to get the path +// for a parity configuration file for testing. +// DEPRECATED. Brittle. Confusing. Ugly. Sorry. func paritySpecPath(name string) string { p := filepath.Join(paritySpecsDir, name) if fi, err := os.Open(p); err == nil { @@ -51,6 +59,13 @@ func paritySpecPath(name string) string { return p } +// coregethSpecsDir is where core-geth-style configuration files for testing are stored. +var coregethSpecsDir = filepath.Join("..", "params", "coregeth.json.d") + +// MapForkNameChainspecFileState is a dictionary pairing Fork names with respective +// file base names. +// These are used for StateTests, BlockchainTests, but not Difficulty tests. +// These files are expected to be found in coregethSpecsDir. var MapForkNameChainspecFileState = map[string]string{ "Frontier": "frontier_test.json", "Homestead": "homestead_test.json", @@ -59,12 +74,19 @@ var MapForkNameChainspecFileState = map[string]string{ "Byzantium": "byzantium_test.json", "Constantinople": "constantinople_test.json", "ConstantinopleFix": "constantinople_fix_test.json", - "EIP158ToByzantiumAt5": "eip158_to_byzantiumat5_test.json", + "EIP158ToByzantiumAt5": "eip_158_to_byzantium_at_5_test.json", "Istanbul": "istanbul_test.json", - "ETC_Atlantis": "classic_atlantis_test.json", - "ETC_Agharta": "classic_agharta_test.json", + "Berlin": "berlin_test.json", + "ETC_Atlantis": "etc_atlantis_test.json", + "ETC_Agharta": "etc_agharta_test.json", + "ETC_Phoenix": "etc_phoenix_test.json", + "ETC_Magneto": "etc_magneto_test.json", } +// mapForkNameChainspecFileDifficulty is a dictionary pairing fork names with respective +// file base name. +// These configurations are used exclusively for Difficulty tests. +// These files are expected to be found in coregethSpecsDir. var mapForkNameChainspecFileDifficulty = map[string]string{ "Ropsten": "ropsten_difficulty_test.json", "Morden": "morden_difficulty_test.json", @@ -81,10 +103,13 @@ var mapForkNameChainspecFileDifficulty = map[string]string{ "ETC_Phoenix": "classic_phoenix_difficulty_test.json", } -func readConfigFromSpecFile(name string) (spec ctypes.ChainConfigurator, sha1sum []byte, err error) { - spec = &parity.ParityChainSpec{} +// readJSONFromFile is a utility function to read (unmarshaling) a value from a JSON file, +// which tries to return helpful errors if it is unable to, which can be useful for debugging. +// Additionally, it returns the SHA1 sum of the file if it does not error otherwise. +// This is floozy logic, but I don't really care right now. +func readJSONFromFile(name string, value interface{}) (sha1sum []byte, err error) { if fi, err := os.Open(name); os.IsNotExist(err) { - return nil, nil, err + return nil, err } else { fi.Close() } @@ -92,7 +117,7 @@ func readConfigFromSpecFile(name string) (spec ctypes.ChainConfigurator, sha1sum if err != nil { panic(fmt.Sprintf("%s err: %s\n%s", name, err, b)) } - err = json.Unmarshal(b, spec) + err = json.Unmarshal(b, value) if err != nil { if jsonError, ok := err.(*json.SyntaxError); ok { line, character, lcErr := lineAndCharacter(string(b), int(jsonError.Offset)) @@ -111,10 +136,13 @@ func readConfigFromSpecFile(name string) (spec ctypes.ChainConfigurator, sha1sum panic(fmt.Sprintf("%s err: %s\n%s", name, err, b)) } bb := sha1.Sum(b) - return spec, bb[:], nil + return bb[:], nil } -func writeDifficultyConfigFile(conf ctypes.ChainConfigurator, forkName string) (string, [20]byte, error) { +// writeDifficultyConfigFileParity writes a difficulty-test configuration file in the parity format. +// This feature is DEPRECATED. +// Write core-geth format configuration for testing instead. +func writeDifficultyConfigFileParity(conf ctypes.ChainConfigurator, forkName string) (string, [20]byte, error) { genesis := params.DefaultRopstenGenesisBlock() genesis.Config = conf @@ -200,17 +228,79 @@ func init() { difficultyChainConfigurations[k] = pspec } + } else if os.Getenv(CG_CHAINCONFIG_CHAINSPECS_COREGETH_KEY) != "" { + // This logic reads Forks (used by [General]StateTests) and Difficulty configurations + // from their respective coregeth.json.d/.json files. + // This implementation differs from that of this scope's predecessor CG_CHAINCONFIG_CHAINSPECS_OPENETHEREUM_KEY + // because it only replaces Go values when it finds a corresponding configuration file + // (it does not demand to replace all available configurations). + // This avoids some unnecessary overhead for establishing configurations + // that aren't really relevant, like Morden testnets. + log.Println("Setting chain configurations from core-geth chainspecs") + + // newForks avoid write+iterate on Forks map. + // All key:values in newForks will be written back to Forks. + newForks := map[string]ctypes.ChainConfigurator{} + for name := range Forks { + gen := &genesisT.Genesis{ + Config: &coregeth.CoreGethChainConfig{}, + } + specPath := filepath.Join(coregethSpecsDir, strcase.ToSnake(name)+"_test.json") + + sha1sum, err := readJSONFromFile(specPath, gen) + if err != nil { + log.Printf("Failed to read core-geth state config file for %s: %s", name, specPath) + continue + } + chainspecRefsState[name] = chainspecRef{filepath.Base(specPath), sha1sum} + newForks[name] = gen.Config + } + for name, conf := range newForks { + Forks[name] = conf + } + + for k, v := range mapForkNameChainspecFileDifficulty { + conf := &coregeth.CoreGethChainConfig{} + specPath := filepath.Join(coregethSpecsDir, v) + sha1sum, err := readJSONFromFile(specPath, conf) + if err != nil { + log.Printf("Failed to read core-geth difficulty file for %s: %s", k, specPath) + continue + } + if len(sha1sum) == 0 { + panic("empty sha1 sum") + } + chainspecRefsDifficulty[k] = chainspecRef{filepath.Base(v), sha1sum} + difficultyChainConfigurations[k] = conf + } + + // DEPRECATED. + // Use CG_CHAINCONFIG_CHAINSPECS_COREGETH_KEY instead. } else if os.Getenv(CG_CHAINCONFIG_CHAINSPECS_OPENETHEREUM_KEY) != "" { log.Println("Setting chain configurations from Parity chainspecs") for k, v := range MapForkNameChainspecFileState { - config, sha1sum, err := readConfigFromSpecFile(paritySpecPath(v)) + config := &parity.ParityChainSpec{} + sha1sum, err := readJSONFromFile(paritySpecPath(v), config) if os.IsNotExist(err) { wd, wde := os.Getwd() if wde != nil { panic(wde) } - panic(fmt.Sprintf("failed to find chainspec, wd: %s, config: %v/file: %v", wd, k, v)) + fconfig := Forks[k] + pspec := &parity.ParityChainSpec{} + if err := confp.Convert(fconfig, pspec); ctypes.IsFatalUnsupportedErr(err) { + panic(err) + } + config = pspec + b, _ := json.MarshalIndent(pspec, "", " ") + writePath := filepath.Join(paritySpecsDir, v) + err := ioutil.WriteFile(writePath, b, os.ModePerm) + if err != nil { + panic(fmt.Sprintf("failed to write chainspec; wd: %s, config: %v/file: %v", wd, k, writePath)) + } + // + // panic(fmt.Sprintf("failed to find chainspec, wd: %s, config: %v/file: %v", wd, k, v)) } else if err != nil { panic(err) } @@ -219,11 +309,12 @@ func init() { } for k, v := range mapForkNameChainspecFileDifficulty { - config, sha1sum, err := readConfigFromSpecFile(paritySpecPath(v)) + config := &parity.ParityChainSpec{} + sha1sum, err := readJSONFromFile(paritySpecPath(v), config) if os.IsNotExist(err) && os.Getenv(CG_GENERATE_DIFFICULTY_TESTS_KEY) != "" { log.Println("Will generate chainspec file for", k, v) conf := difficultyChainConfigurations[k] - _, sha, err := writeDifficultyConfigFile(conf, k) + _, sha, err := writeDifficultyConfigFileParity(conf, k) if err != nil { panic(fmt.Sprintf("error writing difficulty config file: %s: %s %v", k, v, err)) } @@ -267,84 +358,6 @@ func init() { } } -//func convertMetaForkBlocksToFeatures(config *paramtypes.CoreGethChainConfig) { -// if config.HomesteadBlock != nil { -// config.EIP2FBlock = config.HomesteadBlock -// config.EIP7FBlock = config.HomesteadBlock -// config.HomesteadBlock = nil -// } -// if config.EIP158Block != nil { -// config.EIP160FBlock = config.EIP158Block -// config.EIP161FBlock = config.EIP158Block -// config.EIP170FBlock = config.EIP158Block -// config.EIP158Block = nil -// } -// if config.ByzantiumBlock != nil { -// // Difficulty adjustment to target mean block time including uncles -// // https://github.com/ethereum/EIPs/issues/100 -// config.EIP100FBlock = config.ByzantiumBlock -// // Opcode REVERT -// // https://eips.ethereum.org/EIPS/eip-140 -// config.EIP140FBlock = config.ByzantiumBlock -// // Precompiled contract for bigint_modexp -// // https://github.com/ethereum/EIPs/issues/198 -// config.EIP198FBlock = config.ByzantiumBlock -// // Opcodes RETURNDATACOPY, RETURNDATASIZE -// // https://github.com/ethereum/EIPs/issues/211 -// config.EIP211FBlock = config.ByzantiumBlock -// // Precompiled contract for pairing check -// // https://github.com/ethereum/EIPs/issues/212 -// config.EIP212FBlock = config.ByzantiumBlock -// // Precompiled contracts for addition and scalar multiplication on the elliptic curve alt_bn128 -// // https://github.com/ethereum/EIPs/issues/213 -// config.EIP213FBlock = config.ByzantiumBlock -// // Opcode STATICCALL -// // https://github.com/ethereum/EIPs/issues/214 -// config.EIP214FBlock = config.ByzantiumBlock -// // Metropolis diff bomb delay and reducing block reward -// // https://github.com/ethereum/EIPs/issues/649 -// // note that this is closely related to EIP100. -// // In fact, EIP100 is bundled in -// config.EIP649FBlock = config.ByzantiumBlock -// // Transaction receipt status -// // https://github.com/ethereum/EIPs/issues/658 -// config.EIP658FBlock = config.ByzantiumBlock -// // NOT CONFIGURABLE: prevent overwriting contracts -// // https://github.com/ethereum/EIPs/issues/684 -// // EIP684FBlock *big.Int `json:"eip684BFlock,omitempty"` -// -// config.ByzantiumBlock = nil -// } -// if config.ConstantinopleBlock != nil { -// // Opcodes SHR, SHL, SAR -// // https://eips.ethereum.org/EIPS/eip-145 -// config.EIP145FBlock = config.ConstantinopleBlock -// // Opcode CREATE2 -// // https://eips.ethereum.org/EIPS/eip-1014 -// config.EIP1014FBlock = config.ConstantinopleBlock -// // Opcode EXTCODEHASH -// // https://eips.ethereum.org/EIPS/eip-1052 -// config.EIP1052FBlock = config.ConstantinopleBlock -// // Constantinople difficulty bomb delay and block reward adjustment -// // https://eips.ethereum.org/EIPS/eip-1234 -// config.EIP1234FBlock = config.ConstantinopleBlock -// // Net gas metering -// // https://eips.ethereum.org/EIPS/eip-1283 -// config.EIP1283FBlock = config.ConstantinopleBlock -// -// config.ConstantinopleBlock = nil -// } -// if config.IstanbulBlock != nil { -// config.EIP152FBlock = config.IstanbulBlock -// config.EIP1108FBlock = config.IstanbulBlock -// config.EIP1344FBlock = config.IstanbulBlock -// config.EIP1884FBlock = config.IstanbulBlock -// config.EIP2028FBlock = config.IstanbulBlock -// config.EIP2200FBlock = config.IstanbulBlock -// config.IstanbulBlock = nil -// } -//} - // https://adrianhesketh.com/2017/03/18/getting-line-and-character-positions-from-gos-json-unmarshal-errors/ func lineAndCharacter(input string, offset int) (line int, character int, err error) { lf := rune(0x0A) diff --git a/tests/state_mgen.go b/tests/state_mgen.go index 11257efa44..3e74199051 100644 --- a/tests/state_mgen.go +++ b/tests/state_mgen.go @@ -21,15 +21,6 @@ import ( "github.com/ethereum/go-ethereum/core/vm" ) -// writeStateTestsReferencePairs defines reference pairs for use when writing tests. -// The reference (key) is used to define the environment and parameters, while the -// output from these tests run against the state is actually written. -var writeStateTestsReferencePairs = map[string]string{ - "Byzantium": "ETC_Atlantis", - "ConstantinopleFix": "ETC_Agharta", - "Istanbul": "ETC_Phoenix", -} - // RunSetPost runs the state subtest for a given config, and writes the resulting // state to the corresponding subtest post field. func (t *StateTest) RunSetPost(subtest StateSubtest, vmconfig vm.Config) error { @@ -39,15 +30,6 @@ func (t *StateTest) RunSetPost(subtest StateSubtest, vmconfig vm.Config) error { } t.json.Post[subtest.Fork][subtest.Index].Root = common.UnprefixedHash(root) t.json.Post[subtest.Fork][subtest.Index].Logs = common.UnprefixedHash(rlpHash(statedb.Logs())) + t.json.Post[subtest.Fork][subtest.Index].filled = true return nil } - -// filledPostStates returns true if all poststate elems are filled (non zero-valued) -func filledPostStates(s []stPostState) bool { - for _, l := range s { - if common.Hash(l.Root) == (common.Hash{}) { - return false - } - } - return true -} diff --git a/tests/state_mgen_test.go b/tests/state_mgen_test.go index 3b99b09215..18c8bd3118 100644 --- a/tests/state_mgen_test.go +++ b/tests/state_mgen_test.go @@ -17,71 +17,74 @@ package tests import ( - "crypto/sha1" "encoding/json" "fmt" + "io" "io/ioutil" "os" "path/filepath" + "regexp" "strings" + "sync" "testing" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/internal/build" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/params/confp/tconvert" + "github.com/ethereum/go-ethereum/params/confp" + "github.com/ethereum/go-ethereum/params/types/coregeth" + "github.com/ethereum/go-ethereum/params/types/genesisT" "github.com/iancoleman/strcase" ) -func TestGenState(t *testing.T) { - if os.Getenv(CG_GENERATE_STATE_TESTS_KEY) == "" { - t.Skip() - } - if os.Getenv(CG_CHAINCONFIG_CHAINSPECS_OPENETHEREUM_KEY) == "" { - t.Fatal("Must use chainspec files for fork configurations.") - } +// testMatcherGen embeds the common testMatcher struct, extending it to include information +// necessary for test generation. +type testMatcherGen struct { + *testMatcher + references []*regexp.Regexp + targets []string - st := new(testMatcher) + gitHead string + errorPanics bool - // Generating tests should NOT skip slow or time consuming tests. - - // Long tests: - //st.slow(`^stAttackTest/ContractCreationSpam`) - //st.slow(`^stBadOpcode/badOpcodes`) - //st.slow(`^stPreCompiledContracts/modexp`) - //st.slow(`^stQuadraticComplexityTest/`) - //st.slow(`^stStaticCall/static_Call50000`) - //st.slow(`^stStaticCall/static_Return50000`) - //st.slow(`^stStaticCall/static_Call1MB`) - //st.slow(`^stSystemOperationsTest/CallRecursiveBomb`) - //st.slow(`^stTransactionTest/Opcodes_TransactionInit`) - - // Very time consuming - //st.skipLoad(`^stTimeConsuming/`) - - // Broken tests: - // Expected failures: - //st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Byzantium/0`, "bug in test") - //st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Byzantium/3`, "bug in test") - //st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Constantinople/0`, "bug in test") - //st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Constantinople/3`, "bug in test") - //st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/ConstantinopleFix/0`, "bug in test") - //st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/ConstantinopleFix/3`, "bug in test") - - st.walkFullName(t, stateTestDir, withWritingTests) - - // For Istanbul, older tests were moved into LegacyTests - st.walkFullName(t, legacyStateTestDir, withWritingTests) + // allConfigs tracks each unique configuration used in a test-generating suite. + // This configuration map will be written to a file included with the tests as a reference + // so that cross-client tests can define what eg. "Berlin" means. + allConfigsMu sync.Mutex // safety first + allConfigs map[string]*coregeth.CoreGethChainConfig } -func withWritingTests(t *testing.T, name string, test *StateTest) { +// generateFromReference assigns reference:target pairs for test generation by fork name. +func (tg *testMatcherGen) generateFromReference(ref, target string) { + if tg.references == nil { + tg.references = []*regexp.Regexp{} + } + tg.references = append(tg.references, regexp.MustCompile(ref)) + if tg.targets == nil { + tg.targets = []string{} + } + tg.targets = append(tg.targets, target) +} - // Test output is written here. - //fpath := filepath.Join(currentTestDir, name) - //test.Name = strings.TrimSuffix(filepath.Base(fpath), ".json") +func (tg *testMatcherGen) getGenerationTarget(ref string) string { + // Lookup the reference:target pairing, if any. + for i, r := range tg.references { + if r.MatchString(ref) { + return tg.targets[i] + } + } + return "" +} - fpath := name - test.Name = strings.TrimSuffix(filepath.Base(name), ".json") +// TestGenStateAll generates tests for all State tests. +func TestGenStateAll(t *testing.T) { + if os.Getenv(CG_GENERATE_STATE_TESTS_KEY) == "" { + t.Skip() + } + if os.Getenv(CG_CHAINCONFIG_FEATURE_EQ_COREGETH_KEY) == "" { + t.Fatal("Must use core-geth chain configs for test generation, converting if necessary.") + } // There is no need to run this git command for every test, but // speed is not really a big deal here, and it's nice to keep as much logic out @@ -89,84 +92,380 @@ func withWritingTests(t *testing.T, name string, test *StateTest) { head := build.RunGit("rev-parse", "HEAD") head = strings.TrimSpace(head) - // For tests using a config that does not have an associated chainspec file, - // then generate that file. - for _, subtest := range test.Subtests(nil) { - subtest := subtest - if _, ok := MapForkNameChainspecFileState[subtest.Fork]; !ok { - genesis := test.genesis(Forks[subtest.Fork]) - pspec, err := tconvert.NewParityChainSpec(subtest.Fork, genesis, []string{}) - if err != nil { + tm := new(testMatcherGen) + tm.allConfigs = make(map[string]*coregeth.CoreGethChainConfig) + tm.testMatcher = new(testMatcher) + tm.noParallel = true + tm.errorPanics = true + tm.gitHead = head + + tm.generateFromReference("Byzantium", "ETC_Atlantis") + tm.generateFromReference("ConstantinopleFix", "ETC_Agharta") + tm.generateFromReference("Berlin", "ETC_Magneto") + tm.generateFromReference("Istanbul", "ETC_Phoenix") + + for _, dir := range []string{ + stateTestDir, + legacyStateTestDir, + } { + tm.walkFullName(t, dir, tm.testWriteTest) + + // Write the chain config file. + // testdata/GeneralStateTests -> testdata/GeneralStateTests_configs.json + b, err := json.MarshalIndent(tm.allConfigs, "", " ") + if err != nil { + t.Fatal(err) + } + err = ioutil.WriteFile(fmt.Sprintf("%s_configs.json", dir), b, os.ModePerm) + if err != nil { + t.Fatal(err) + } + // Reset map as to only write config pertinent to forks in a tests directory. + tm.allConfigs = make(map[string]*coregeth.CoreGethChainConfig) + } +} + +// TestGenStateSingles generates tests for a one or a few test files. +// This should only be used as a debugging tool. +// For production use, use TestGenStateAll. +func TestGenStateSingles(t *testing.T) { + if os.Getenv(CG_GENERATE_STATE_TESTS_KEY) == "" { + t.Skip() + } + if os.Getenv(CG_CHAINCONFIG_FEATURE_EQ_COREGETH_KEY) == "" { + t.Fatal("Must use core-geth chain configs for test generation, converting if necessary.") + } + + head := build.RunGit("rev-parse", "HEAD") + head = strings.TrimSpace(head) + + files := []string{ + filepath.Join(stateTestDir, "stStaticFlagEnabled/DelegatecallToPrecompileFromContractInitialization.json"), + filepath.Join(stateTestDir, "stStaticCall/StaticcallToPrecompileFromCalledContract.json"), + } + + tm := new(testMatcherGen) + tm.allConfigs = make(map[string]*coregeth.CoreGethChainConfig) + tm.testMatcher = new(testMatcher) + tm.noParallel = true // disable parallelism + tm.errorPanics = true + tm.gitHead = head + + tm.generateFromReference("Byzantium", "ETC_Atlantis") + tm.generateFromReference("ConstantinopleFix", "ETC_Agharta") + tm.generateFromReference("Berlin", "ETC_Magneto") + tm.generateFromReference("Istanbul", "ETC_Phoenix") + + for _, f := range files { + tm.runTestFile(t, f, f, tm.testWriteTest) + } +} + +func (tm *testMatcherGen) testWriteTest(t *testing.T, name string, test *StateTest) { + // testWriteTest generates a file-based tests (writing a new file), and re-runs (testing) the generated test file. + + // Set up a temporary file to write the generated test(s) to. + // We have to use an actual file because the on-write callback re-runs the + // test using the newly-generated file to ensure consistency. + // If we didn't rerun the tests after writing, we could use a buffer as the WriteCloser. + // Note that parallelism can cause greasy bugs around file during read/write which is why + // we use a temporary file instead of immediately overwriting the canonical file in the first place; + // for example, I saw regular encoding errors without this pattern. + tmpFile, err := ioutil.TempFile(os.TempDir(), "geth-state-test-generation") + if err != nil { + t.Fatal(err) + } + tmpFileName := tmpFile.Name() + + tm.runTestFile(t, name, name, tm.stateTestsGen(tmpFile, + // On-Write: + // After generating the tests, rerun the test using the new test file, ensuring consistency. + // If the test passes (and it damn well should), then move the file to the canonical location. + func() { + tm.runTestFile(t, tmpFileName, tmpFileName, tm.stateTestRunner) + if err := os.Rename(tmpFileName, name); err != nil { t.Fatal(err) } - b, err := json.MarshalIndent(pspec, "", " ") - if err != nil { + }, + // On-Skip: + // This test is skipped by some condition in the test matcher, probably by skip-fork. + // In this case we just clean up the inoperative temp file. + func() { + if err := os.RemoveAll(tmpFileName); err != nil { t.Fatal(err) } - filename := paritySpecPath(strcase.ToSnake(subtest.Fork) + ".json") - err = ioutil.WriteFile(filename, b, os.ModePerm) + })) +} + +// stateTestsGen generates state tests using a reference fork and targeting a target fork. +// The reference fork is used to the pre-state and tested transaction(s) (which are schematized as indexes), +// replacing the reference fork's chain config and genesis with that of the target fork. +// The resulting post-state is assigned to the test's post.Root and post.Logs hashes. +func (tm *testMatcherGen) stateTestsGen(w io.WriteCloser, writeCallback, skipCallback func()) func(t *testing.T, name string, test *StateTest) { + return func(t *testing.T, name string, test *StateTest) { + + subtests := test.Subtests(nil) + + targets := map[string][]stPostState{} + + for _, s := range subtests { + + // Prior to test-generation logic, record the genesis+chain config at the testmatcher level. + // This will allow us to generate a complete map of chain configurations for the test suite, + // whether the subtest's fork was used to generate any tests or not. + // Record the genesis+chain config at the testmatcher level. + config, _, err := GetChainConfig(s.Fork) if err != nil { - t.Fatal(err) + t.Fatal(UnsupportedForkError{s.Fork}) + } + + tm.allConfigsMu.Lock() + // This will panic if type isn't expected. + tm.allConfigs[s.Fork] = config.(*coregeth.CoreGethChainConfig) + tm.allConfigsMu.Unlock() + + // Lookup the reference:target pairing, if any. + targetFork := tm.getGenerationTarget(s.Fork) + + // If target fork is empty we know that this subtest is not intended for as a reference + // for any target generated test. + if targetFork == "" { + continue + } + referenceFork := s.Fork + + if _, ok := Forks[targetFork]; !ok { + t.Fatalf("missing target fork config: %s, reference: %s", targetFork, referenceFork) + } + + if _, ok := targets[targetFork]; !ok { + subtestsLen := len(test.json.Post[referenceFork]) + targets[targetFork] = make([]stPostState, subtestsLen) + } + + targetSubtest := StateSubtest{ + Fork: targetFork, + Index: s.Index, + } + + refPostState := test.json.Post[referenceFork] + + // Initialize the post state with reference indexes. + // These indexes (containing gas, value, and data) are used to construct the message (tx) run + // by the test. + stPost := stPostState{ + Indexes: stPostStateIndexes{ + Data: refPostState[s.Index].Indexes.Data, + Gas: refPostState[s.Index].Indexes.Gas, + Value: refPostState[s.Index].Indexes.Value, + }, + } + + // vmConfig is constructed using global variables for possible EVM and EWASM interpreters. + // These interpreters are configured with environment variables and are assigned in an init() function. + vmConfig := vm.Config{EVMInterpreter: *testEVM, EWASMInterpreter: *testEWASM} + + // Since we know that tests run with and without the snapshotter features are equivalent, either boolean state + // is valid and 'false' is arbitrary. + _, statedb, root, err := test.RunNoVerifyWithPost(targetSubtest, vmConfig, false, stPost) + if err != nil { + t.Fatalf("Error encountered at RunSetPost: %v", err) } - sum := sha1.Sum(b) - chainspecRefsState[subtest.Fork] = chainspecRef{filepath.Base(filename), sum[:]} - t.Logf("Created new fork chainspec file: %v", chainspecRefsState[subtest.Fork]) + + // Assign the generated testable values. + stPost.Root = common.UnprefixedHash(root) + stPost.Logs = common.UnprefixedHash(rlpHash(statedb.Logs())) + + targets[targetFork][s.Index] = stPost } - } - for _, subtest := range test.Subtests(nil) { - subtest := subtest + if len(targets) == 0 { + t.Skip() + skipCallback() + return + } - // Only proceed with test forks which are destined for writing. - // Note that using this function implies that you trust the test runner - // to give valid output, ie. only generate tests after you're sure the - // reference tests themselves are passing. - forkPair, ok := writeStateTestsReferencePairs[subtest.Fork] - if !ok { - continue + // Install the generated cases to the test. + for k, v := range targets { + test.json.Post[k] = v } - if _, ok := test.json.Post[forkPair]; !ok { - test.json.Post[forkPair] = make([]stPostState, len(test.json.Post[subtest.Fork])) + // Assign provenance metadata to the test. + test.Name = strings.TrimSuffix(filepath.Base(name), filepath.Ext(name)) + test.json.Info.FilledWith = fmt.Sprintf("%s-%s-%s", params.VersionName, params.VersionWithMeta, tm.gitHead) + + // Write the augmented test to the writer. + generatedJSON, err := json.MarshalIndent(test, "", " ") + if err != nil { + panic(err) } + _, err = w.Write(generatedJSON) + if err != nil { + panic(err) + } + if err := w.Close(); err != nil { + panic(err) + } + writeCallback() + } +} - // Initialize the subtest/index data by copy from reference. - reftestFork := subtest.Fork - test.json.Post[forkPair][subtest.Index] = test.json.Post[reftestFork][subtest.Index] - - // Set new fork name, so new test config will be used instead. - subtest.Fork = forkPair - - key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index) - - t.Run(key, func(t *testing.T) { - withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error { - err := test.RunSetPost(subtest, vmconfig) - - // Only write the test once, after all subtests have been written. - if err == nil && filledPostStates(test.json.Post[subtest.Fork]) { - b, err := json.MarshalIndent(test, "", " ") - if err != nil { - return err - } - fi, err := ioutil.ReadFile(fpath) - if err != nil { - t.Fatal("Not writing file: ", fpath, "test", string(b)) - return nil - } - test.json.Info.WrittenWith = fmt.Sprintf("%s-%s-%s", params.VersionName, params.VersionWithMeta, head) - test.json.Info.Parent = submoduleParentRef - test.json.Info.ParentSha1Sum = fmt.Sprintf("%x", sha1.Sum(fi)) - test.json.Info.Chainspecs = chainspecRefsState - - err = ioutil.WriteFile(fpath, b, os.ModePerm) - if err != nil { - panic(err) - } - t.Log("Wrote test file: ", fpath) +func (tm *testMatcherGen) stateTestRunner(t *testing.T, name string, test *StateTest) { + subtests := test.Subtests(nil) + for _, subtest := range subtests { + st := subtest + + key := fmt.Sprintf("%s/%d", st.Fork, st.Index) + name := name + "/" + key + + t.Run(key+"/trie", func(t *testing.T) { + // vmConfig is constructed using global variables for possible EVM and EWASM interpreters. + // These interpreters are configured with environment variables and are assigned in an init() function. + vmConfig := vm.Config{EVMInterpreter: *testEVM, EWASMInterpreter: *testEWASM} + _, _, err := test.Run(st, vmConfig, false) + checkedErr := tm.checkFailure(t, name+"/trie", err) + if checkedErr != nil && *testEWASM != "" { + checkedErr = fmt.Errorf("%w ewasm=%s", checkedErr, *testEWASM) + } + if checkedErr != nil { + if tm.errorPanics { + panic(err) + } else { + t.Fatal(err) } - return nil - }) + } + }) + } +} + +// TestGenStateCoreGethConfigs generates core-geth-style configurations. +// This isn't a test. It generates configs. +// Skip should be installed so this function will only be run by developers as needed. +func TestGenStateCoreGethConfigs(t *testing.T) { + t.Skip() + st := new(testMatcher) + + // FYI: Possibly the slowest and stupidest way to write 14 files: read 42189 test to do it + // and write each file 1486 times. + for _, d := range []string{stateTestDir, legacyStateTestDir} { + st.walkFullName(t, d, func(t *testing.T, name string, test *StateTest) { + + subtests := test.Subtests(nil) + for _, subtest := range subtests { + subtest := subtest + + genesis := test.genesis(Forks[subtest.Fork]) + + cgConfig := &coregeth.CoreGethChainConfig{} + err := confp.Convert(genesis.Config, cgConfig) + if err != nil { + t.Fatal(err) + } + genesis.Config = cgConfig + + b, err := json.MarshalIndent(genesis, "", " ") + if err != nil { + t.Fatal(err) + } + filename := filepath.Join( + coregethSpecsDir, + strcase.ToSnake(subtest.Fork)+"_test.json", + ) + err = ioutil.WriteFile(filename, b, os.ModePerm) + if err != nil { + t.Fatal(err) + } + } + }) + } +} + +// TestGeneratedConfigsEq tests that the CoreGeth configuration described in +// ../params/coregeth.json.d/berlin_test.json (a configuration which is relevant to +// state tests) is equivalent to the configuration coded at Forks["Berlin"]. +// Note that when run with COREGETH_TESTS_CHAINCONFIG_FEATURE_EQUIVALENCE_COREGETH, for example, +// the coded configuration will have been init'd with a CoreGeth-struct value (as opposed +// to the default go-ethereum value). +func TestGeneratedConfigsEq(t *testing.T) { + specPath := filepath.Join(coregethSpecsDir, "berlin_test.json") + gen := &genesisT.Genesis{ + Config: &coregeth.CoreGethChainConfig{}, + } + _, err := readJSONFromFile(specPath, gen) + if err != nil { + t.Fatal(err) + } + + coded := Forks["Berlin"] + + // Special case handling for EIP1283. + if coded.GetEIP1283Transition() == nil && coded.GetEIP1283DisableTransition() == nil { + + e1283 := gen.Config.GetEIP1283Transition() + d1283 := gen.Config.GetEIP1283DisableTransition() + + if (e1283 == nil && d1283 == nil) || + (*e1283 == *d1283) { + gen.Config.SetEIP1283Transition(nil) + gen.Config.SetEIP1283DisableTransition(nil) + } + + } + + err = confp.Equivalent(coded, gen.Config) + if err != nil { + t.Error(err) + } +} + +// TestConvertDefaultsBounce tests that for a few default Forks configuration values (which are used in tests), +// that the convert-marshal-unmarshal cycle (here, a "bounce") results in equivalent chain configurations. +// EIP1234 is used as a canary test since I have seen that fail most lately, and it involved some complicated +// encoding and interdependencies (is Ethash, rel EIP649, map encoding vs. inferrable boolean field). +func TestConvertDefaultsBounce(t *testing.T) { + safePrint := func(n *uint64) string { + if n == nil { + return "nil" + } + return fmt.Sprintf("%d", *n) + } + for _, forkName := range []string{"Constantinople", "Istanbul", "Berlin"} { + t.Run(forkName, func(t *testing.T) { + berlin := Forks[forkName] + eip1234 := berlin.GetEthashEIP1234Transition() + + cg := &coregeth.CoreGethChainConfig{} + err := confp.Convert(berlin, cg) + if err != nil { + t.Fatal(err) + } + + cg1234 := cg.GetEthashEIP1234Transition() + + t.Log(safePrint(eip1234), safePrint(cg1234)) + + err = confp.Equivalent(berlin, cg) + if err != nil { + t.Fatal(err) + } + + b, _ := json.MarshalIndent(cg, "", " ") + t.Log(string(b)) + + cg2 := &coregeth.CoreGethChainConfig{} + err = json.Unmarshal(b, cg2) + if err != nil { + t.Fatal(err) + } + + cg2_1234 := cg2.GetEthashEIP1234Transition() + + t.Log(safePrint(eip1234), safePrint(cg1234), safePrint(cg2_1234)) + + err = confp.Equivalent(berlin, cg2) + if err != nil { + t.Fatal(forkName, err) + } }) } } diff --git a/tests/state_test.go b/tests/state_test.go index 7d393f53ea..36729be3b9 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -61,6 +61,12 @@ func TestState(t *testing.T) { st.skipLoad(`^stQuadraticComplexityTest/`) st.skipLoad(`^stStaticCall/static_Call50000`) } + if *testEVM != "" || *testEWASM != "" { + // Berlin tests are not expected to pass for external EVMs, yet. + // + st.skipFork("^Berlin$") + st.skipFork("Magneto") + } // Broken tests: // Expected failures: diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 7825139d3b..cbdc8e3488 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -25,14 +25,13 @@ import ( "strconv" "strings" - "github.com/ethereum/go-ethereum/core/state/snapshot" - "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" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -79,27 +78,30 @@ type stJSON struct { } type stInfo struct { - Comment string `json:"comment"` - FillingRPCServer string `json:"filling-rpc-server"` - FillingToolVersion string `json:"filling-tool-version"` - FilledWith string `json:"filledWith"` - LLLCVersion string `json:"lllcversion"` - Source string `json:"source"` - SourceHash string `json:"sourceHash"` - WrittenWith string `json:"written-with"` - Parent string `json:"parent"` - ParentSha1Sum string `json:"parentSha1Sum"` - Chainspecs chainspecRefsT `json:"chainspecs"` + Comment string `json:"comment"` + FillingRPCServer string `json:"filling-rpc-server"` + FillingToolVersion string `json:"filling-tool-version"` + FilledWith string `json:"filledWith"` + LLLCVersion string `json:"lllcversion"` + Source string `json:"source"` + SourceHash string `json:"sourceHash"` + Labels map[string]interface{} `json:"labels,omitempty"` } type stPostState struct { Root common.UnprefixedHash `json:"hash"` Logs common.UnprefixedHash `json:"logs"` - Indexes struct { - Data int `json:"data"` - Gas int `json:"gas"` - Value int `json:"value"` - } + Indexes stPostStateIndexes `json:"indexes"` + + // filled can be set to true when the subtest has been written, + // which can be helpful to distiguish unfilled tests from potentially filled zero-value tests. + filled bool +} + +type stPostStateIndexes struct { + Data int `json:"data"` + Gas int `json:"gas"` + Value int `json:"value"` } //go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go @@ -123,13 +125,14 @@ type stEnvMarshaling struct { //go:generate gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go type stTransaction struct { - GasPrice *big.Int `json:"gasPrice"` - Nonce uint64 `json:"nonce"` - To string `json:"to"` - Data []string `json:"data"` - GasLimit []uint64 `json:"gasLimit"` - Value []string `json:"value"` - PrivateKey []byte `json:"secretKey"` + GasPrice *big.Int `json:"gasPrice"` + Nonce uint64 `json:"nonce"` + To string `json:"to"` + Data []string `json:"data"` + AccessLists []*types.AccessList `json:"accessLists,omitempty"` + GasLimit []uint64 `json:"gasLimit"` + Value []string `json:"value"` + PrivateKey []byte `json:"secretKey"` } type stTransactionMarshaling struct { @@ -201,7 +204,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bo } // RunNoVerify runs a specific subtest and returns the statedb and post-state root -func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapshotter bool) (*snapshot.Tree, *state.StateDB, common.Hash, error) { +func (t *StateTest) RunNoVerifyWithPost(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, post stPostState) (*snapshot.Tree, *state.StateDB, common.Hash, error) { config, eips, err := GetChainConfig(subtest.Fork) if err != nil { return nil, nil, common.Hash{}, UnsupportedForkError{subtest.Fork} @@ -210,32 +213,69 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh block := core.GenesisToBlock(t.genesis(config), nil) snaps, statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter) - post := t.json.Post[subtest.Fork][subtest.Index] + // post := t.json.Post[subtest.Fork][subtest.Index] msg, err := t.json.Tx.toMessage(post) if err != nil { return nil, nil, common.Hash{}, err } - context := core.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase) + + // Prepare the EVM. + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) context.GetHash = vmTestBlockHash + evm := vm.NewEVM(context, txContext, statedb, config, vmconfig) - evm := vm.NewEVM(context, statedb, config, vmconfig) + // Execute the message. + snapshot := statedb.Snapshot() + gaspool := new(core.GasPool) + gaspool.AddGas(block.GasLimit()) + if _, err := core.ApplyMessage(evm, msg, gaspool); err != nil { + statedb.RevertToSnapshot(snapshot) + } - if config.IsEnabled(config.GetEIP2929Transition, block.Number()) { - statedb.AddAddressToAccessList(msg.From()) - if dst := msg.To(); dst != nil { - statedb.AddAddressToAccessList(*dst) - // If it's a create-tx, the destination will be added inside evm.create - } - for addr := range vm.PrecompiledContractsForConfig(config, block.Number()) { - statedb.AddAddressToAccessList(addr) - } + // Commit block + statedb.Commit(config.IsEnabled(config.GetEIP161dTransition, block.Number())) + // Add 0-value mining reward. This only makes a difference in the cases + // where + // - the coinbase suicided, or + // - there are only 'bad' transactions, which aren't executed. In those cases, + // the coinbase gets no txfee, so isn't created, and thus needs to be touched + statedb.AddBalance(block.Coinbase(), new(big.Int)) + // And _now_ get the state root + root := statedb.IntermediateRoot(config.IsEnabled(config.GetEIP161dTransition, block.Number())) + return snaps, statedb, root, nil +} + +// RunNoVerify runs a specific subtest and returns the statedb and post-state root +func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapshotter bool) (*snapshot.Tree, *state.StateDB, common.Hash, error) { + config, eips, err := GetChainConfig(subtest.Fork) + if err != nil { + return nil, nil, common.Hash{}, UnsupportedForkError{subtest.Fork} + } + vmconfig.ExtraEips = eips + block := core.GenesisToBlock(t.genesis(config), nil) + snaps, statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter) + + post := t.json.Post[subtest.Fork][subtest.Index] + msg, err := t.json.Tx.toMessage(post) + if err != nil { + return nil, nil, common.Hash{}, err } + + // Prepare the EVM. + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) + context.GetHash = vmTestBlockHash + evm := vm.NewEVM(context, txContext, statedb, config, vmconfig) + + // Execute the message. + snapshot := statedb.Snapshot() gaspool := new(core.GasPool) gaspool.AddGas(block.GasLimit()) - snapshot := statedb.Snapshot() if _, err := core.ApplyMessage(evm, msg, gaspool); err != nil { statedb.RevertToSnapshot(snapshot) } + // Commit block statedb.Commit(config.IsEnabled(config.GetEIP161dTransition, block.Number())) // Add 0-value mining reward. This only makes a difference in the cases @@ -269,7 +309,7 @@ func MakePreState(db ethdb.Database, accounts genesisT.GenesisAlloc, snapshotter var snaps *snapshot.Tree if snapshotter { - snaps = snapshot.New(db, sdb.TrieDB(), 1, root, false, false) + snaps, _ = snapshot.New(db, sdb.TrieDB(), 1, root, false, true, false) } statedb, _ = state.New(root, sdb, snaps) return snaps, statedb @@ -332,8 +372,11 @@ func (tx *stTransaction) toMessage(ps stPostState) (core.Message, error) { if err != nil { return nil, fmt.Errorf("invalid tx data %q", dataHex) } - - msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, true) + var accessList types.AccessList + if tx.AccessLists != nil && tx.AccessLists[ps.Indexes.Data] != nil { + accessList = *tx.AccessLists[ps.Indexes.Data] + } + msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, accessList, true) return msg, nil } diff --git a/tests/testdata b/tests/testdata index 72bdca9504..fb9f42e244 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit 72bdca9504c73ea4b017451fa90f3b6d527730f9 +Subproject commit fb9f42e2448c928e71744fa900694d081d3eb6c6 diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index 8cb2287408..8241f97141 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -56,7 +56,7 @@ func (tt *TransactionTest) Run(config ctypes.ChainConfigurator) error { return nil, nil, err } // Intrinsic gas - requiredGas, err := core.IntrinsicGas(tx.Data(), tx.To() == nil, isEIP2F, isEIP2028F) + requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isEIP2F, isEIP2028F) if err != nil { return nil, nil, err } diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index cbfde4c23d..88dbf863af 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -139,20 +139,22 @@ func (t *VMTest) newEVM(statedb *state.StateDB, vmconfig vm.Config) *vm.EVM { return core.CanTransfer(db, address, amount) } transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {} - context := vm.Context{ + txContext := vm.TxContext{ + Origin: t.json.Exec.Origin, + GasPrice: t.json.Exec.GasPrice, + } + context := vm.BlockContext{ CanTransfer: canTransfer, Transfer: transfer, GetHash: vmTestBlockHash, - Origin: t.json.Exec.Origin, Coinbase: t.json.Env.Coinbase, BlockNumber: new(big.Int).SetUint64(t.json.Env.Number), Time: new(big.Int).SetUint64(t.json.Env.Timestamp), GasLimit: t.json.Env.GasLimit, Difficulty: t.json.Env.Difficulty, - GasPrice: t.json.Exec.GasPrice, } vmconfig.NoRecursion = true - return vm.NewEVM(context, statedb, params.MainnetChainConfig, vmconfig) + return vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vmconfig) } func vmTestBlockHash(n uint64) common.Hash { diff --git a/trie/committer.go b/trie/committer.go index 20c95bed08..33fd9e9823 100644 --- a/trie/committer.go +++ b/trie/committer.go @@ -267,6 +267,5 @@ func estimateSize(n node) int { return 1 + len(n) default: panic(fmt.Sprintf("node type %T", n)) - } } diff --git a/trie/database.go b/trie/database.go index c0c8870f8f..b18665770e 100644 --- a/trie/database.go +++ b/trie/database.go @@ -100,7 +100,7 @@ func (n rawNode) cache() (hashNode, bool) { panic("this should never end up in func (n rawNode) fstring(ind string) string { panic("this should never end up in a live trie") } func (n rawNode) EncodeRLP(w io.Writer) error { - _, err := w.Write([]byte(n)) + _, err := w.Write(n) return err } @@ -272,33 +272,43 @@ func expandNode(hash hashNode, n node) node { } } +// Config defines all necessary options for database. +type Config struct { + Cache int // Memory allowance (MB) to use for caching trie nodes in memory + Journal string // Journal of clean cache to survive node restarts + Preimages bool // Flag whether the preimage of trie key is recorded +} + // NewDatabase creates a new trie database to store ephemeral trie content before // its written out to disk or garbage collected. No read cache is created, so all // data retrievals will hit the underlying disk database. func NewDatabase(diskdb ethdb.KeyValueStore) *Database { - return NewDatabaseWithCache(diskdb, 0, "") + return NewDatabaseWithConfig(diskdb, nil) } -// NewDatabaseWithCache creates a new trie database to store ephemeral trie content +// NewDatabaseWithConfig creates a new trie database to store ephemeral trie content // before its written out to disk or garbage collected. It also acts as a read cache // for nodes loaded from disk. -func NewDatabaseWithCache(diskdb ethdb.KeyValueStore, cache int, journal string) *Database { +func NewDatabaseWithConfig(diskdb ethdb.KeyValueStore, config *Config) *Database { var cleans *fastcache.Cache - if cache > 0 { - if journal == "" { - cleans = fastcache.New(cache * 1024 * 1024) + if config != nil && config.Cache > 0 { + if config.Journal == "" { + cleans = fastcache.New(config.Cache * 1024 * 1024) } else { - cleans = fastcache.LoadFromFileOrNew(journal, cache*1024*1024) + cleans = fastcache.LoadFromFileOrNew(config.Journal, config.Cache*1024*1024) } } - return &Database{ + db := &Database{ diskdb: diskdb, cleans: cleans, dirties: map[common.Hash]*cachedNode{{}: { children: make(map[common.Hash]uint16), }}, - preimages: make(map[common.Hash][]byte), } + if config == nil || config.Preimages { // TODO(karalabe): Flip to default off in the future + db.preimages = make(map[common.Hash][]byte) + } + return db } // DiskDB retrieves the persistent storage backing the trie database. @@ -345,6 +355,11 @@ func (db *Database) insert(hash common.Hash, size int, node node) { // // Note, this method assumes that the database's lock is held! func (db *Database) insertPreimage(hash common.Hash, preimage []byte) { + // Short circuit if preimage collection is disabled + if db.preimages == nil { + return + } + // Track the preimage if a yet unknown one if _, ok := db.preimages[hash]; ok { return } @@ -431,6 +446,10 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) { // preimage retrieves a cached trie node pre-image from memory. If it cannot be // found cached, the method queries the persistent database for the content. func (db *Database) preimage(hash common.Hash) []byte { + // Short circuit if preimage collection is disabled + if db.preimages == nil { + return nil + } // Retrieve the node from cache if available db.lock.RLock() preimage := db.preimages[hash] @@ -588,12 +607,16 @@ func (db *Database) Cap(limit common.StorageSize) error { // leave for later to deduplicate writes. flushPreimages := db.preimagesSize > 4*1024*1024 if flushPreimages { - rawdb.WritePreimages(batch, db.preimages) - if batch.ValueSize() > ethdb.IdealBatchSize { - if err := batch.Write(); err != nil { - return err + if db.preimages == nil { + log.Error("Attempted to write preimages whilst disabled") + } else { + rawdb.WritePreimages(batch, db.preimages) + if batch.ValueSize() > ethdb.IdealBatchSize { + if err := batch.Write(); err != nil { + return err + } + batch.Reset() } - batch.Reset() } } // Keep committing nodes from the flush-list until we're below allowance @@ -630,7 +653,11 @@ func (db *Database) Cap(limit common.StorageSize) error { defer db.lock.Unlock() if flushPreimages { - db.preimages, db.preimagesSize = make(map[common.Hash][]byte), 0 + if db.preimages == nil { + log.Error("Attempted to reset preimage cache whilst disabled") + } else { + db.preimages, db.preimagesSize = make(map[common.Hash][]byte), 0 + } } for db.oldest != oldest { node := db.dirties[db.oldest] @@ -674,20 +701,21 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H batch := db.diskdb.NewBatch() // Move all of the accumulated preimages into a write batch - rawdb.WritePreimages(batch, db.preimages) - if batch.ValueSize() > ethdb.IdealBatchSize { + if db.preimages != nil { + rawdb.WritePreimages(batch, db.preimages) + if batch.ValueSize() > ethdb.IdealBatchSize { + if err := batch.Write(); err != nil { + return err + } + batch.Reset() + } + // Since we're going to replay trie node writes into the clean cache, flush out + // any batched pre-images before continuing. if err := batch.Write(); err != nil { return err } batch.Reset() } - // Since we're going to replay trie node writes into the clean cache, flush out - // any batched pre-images before continuing. - if err := batch.Write(); err != nil { - return err - } - batch.Reset() - // Move the trie itself into the batch, flushing if enough data is accumulated nodes, storage := len(db.dirties), db.dirtiesSize @@ -708,9 +736,10 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H batch.Replay(uncacher) batch.Reset() - // Reset the storage counters and bumpd metrics - db.preimages, db.preimagesSize = make(map[common.Hash][]byte), 0 - + // Reset the storage counters and bumped metrics + if db.preimages != nil { + db.preimages, db.preimagesSize = make(map[common.Hash][]byte), 0 + } memcacheCommitTimeTimer.Update(time.Since(start)) memcacheCommitSizeMeter.Mark(int64(storage - db.dirtiesSize)) memcacheCommitNodesMeter.Mark(int64(nodes - len(db.dirties))) diff --git a/trie/iterator.go b/trie/iterator.go index bb4025d8f3..76d437c403 100644 --- a/trie/iterator.go +++ b/trie/iterator.go @@ -173,7 +173,7 @@ func (it *nodeIterator) LeafKey() []byte { func (it *nodeIterator) LeafBlob() []byte { if len(it.stack) > 0 { if node, ok := it.stack[len(it.stack)-1].node.(valueNode); ok { - return []byte(node) + return node } } panic("not at leaf") diff --git a/trie/notary.go b/trie/notary.go new file mode 100644 index 0000000000..5a64727aa7 --- /dev/null +++ b/trie/notary.go @@ -0,0 +1,57 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethdb/memorydb" +) + +// KeyValueNotary tracks which keys have been accessed through a key-value reader +// with te scope of verifying if certain proof datasets are maliciously bloated. +type KeyValueNotary struct { + ethdb.KeyValueReader + reads map[string]struct{} +} + +// NewKeyValueNotary wraps a key-value database with an access notary to track +// which items have bene accessed. +func NewKeyValueNotary(db ethdb.KeyValueReader) *KeyValueNotary { + return &KeyValueNotary{ + KeyValueReader: db, + reads: make(map[string]struct{}), + } +} + +// Get retrieves an item from the underlying database, but also tracks it as an +// accessed slot for bloat checks. +func (k *KeyValueNotary) Get(key []byte) ([]byte, error) { + k.reads[string(key)] = struct{}{} + return k.KeyValueReader.Get(key) +} + +// Accessed returns s snapshot of the original key-value store containing only the +// data accessed through the notary. +func (k *KeyValueNotary) Accessed() ethdb.KeyValueStore { + db := memorydb.New() + for keystr := range k.reads { + key := []byte(keystr) + val, _ := k.KeyValueReader.Get(key) + db.Put(key, val) + } + return db +} diff --git a/trie/proof.go b/trie/proof.go index 2f52438f98..61c35a8423 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -216,7 +216,7 @@ func proofToPath(rootHash common.Hash, root node, key []byte, proofDb ethdb.KeyV // // Note we have the assumption here the given boundary keys are different // and right is larger than left. -func unsetInternal(n node, left []byte, right []byte) error { +func unsetInternal(n node, left []byte, right []byte) (bool, error) { left, right = keybytesToHex(left), keybytesToHex(right) // Step down to the fork point. There are two scenarios can happen: @@ -278,45 +278,55 @@ findFork: // - left proof points to the shortnode, but right proof is greater // - right proof points to the shortnode, but left proof is less if shortForkLeft == -1 && shortForkRight == -1 { - return errors.New("empty range") + return false, errors.New("empty range") } if shortForkLeft == 1 && shortForkRight == 1 { - return errors.New("empty range") + return false, errors.New("empty range") } if shortForkLeft != 0 && shortForkRight != 0 { + // The fork point is root node, unset the entire trie + if parent == nil { + return true, nil + } parent.(*fullNode).Children[left[pos-1]] = nil - return nil + return false, nil } // Only one proof points to non-existent key. if shortForkRight != 0 { - // Unset left proof's path if _, ok := rn.Val.(valueNode); ok { + // The fork point is root node, unset the entire trie + if parent == nil { + return true, nil + } parent.(*fullNode).Children[left[pos-1]] = nil - return nil + return false, nil } - return unset(rn, rn.Val, left[pos:], len(rn.Key), false) + return false, unset(rn, rn.Val, left[pos:], len(rn.Key), false) } if shortForkLeft != 0 { - // Unset right proof's path. if _, ok := rn.Val.(valueNode); ok { + // The fork point is root node, unset the entire trie + if parent == nil { + return true, nil + } parent.(*fullNode).Children[right[pos-1]] = nil - return nil + return false, nil } - return unset(rn, rn.Val, right[pos:], len(rn.Key), true) + return false, unset(rn, rn.Val, right[pos:], len(rn.Key), true) } - return nil + return false, nil case *fullNode: // unset all internal nodes in the forkpoint for i := left[pos] + 1; i < right[pos]; i++ { rn.Children[i] = nil } if err := unset(rn, rn.Children[left[pos]], left[pos:], 1, false); err != nil { - return err + return false, err } if err := unset(rn, rn.Children[right[pos]], right[pos:], 1, true); err != nil { - return err + return false, err } - return nil + return false, nil default: panic(fmt.Sprintf("%T: invalid node: %v", n, n)) } @@ -426,7 +436,7 @@ func hasRightElement(node node, key []byte) bool { // VerifyRangeProof checks whether the given leaf nodes and edge proof // can prove the given trie leaves range is matched with the specific root. -// Besides, the range should be consecutive(no gap inside) and monotonic +// Besides, the range should be consecutive (no gap inside) and monotonic // increasing. // // Note the given proof actually contains two edge proofs. Both of them can @@ -454,96 +464,140 @@ func hasRightElement(node node, key []byte) bool { // // Except returning the error to indicate the proof is valid or not, the function will // also return a flag to indicate whether there exists more accounts/slots in the trie. -func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, keys [][]byte, values [][]byte, proof ethdb.KeyValueReader) (error, bool) { +func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, keys [][]byte, values [][]byte, proof ethdb.KeyValueReader) (ethdb.KeyValueStore, *Trie, *KeyValueNotary, bool, error) { if len(keys) != len(values) { - return fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values)), false + return nil, nil, nil, false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values)) } // Ensure the received batch is monotonic increasing. for i := 0; i < len(keys)-1; i++ { if bytes.Compare(keys[i], keys[i+1]) >= 0 { - return errors.New("range is not monotonically increasing"), false + return nil, nil, nil, false, errors.New("range is not monotonically increasing") } } + // Create a key-value notary to track which items from the given proof the + // range prover actually needed to verify the data + notary := NewKeyValueNotary(proof) + // Special case, there is no edge proof at all. The given range is expected // to be the whole leaf-set in the trie. if proof == nil { - emptytrie, err := New(common.Hash{}, NewDatabase(memorydb.New())) + var ( + diskdb = memorydb.New() + triedb = NewDatabase(diskdb) + ) + tr, err := New(common.Hash{}, triedb) if err != nil { - return err, false + return nil, nil, nil, false, err } for index, key := range keys { - emptytrie.TryUpdate(key, values[index]) + tr.TryUpdate(key, values[index]) + } + if tr.Hash() != rootHash { + return nil, nil, nil, false, fmt.Errorf("invalid proof, want hash %x, got %x", rootHash, tr.Hash()) + } + // Proof seems valid, serialize all the nodes into the database + if _, err := tr.Commit(nil); err != nil { + return nil, nil, nil, false, err } - if emptytrie.Hash() != rootHash { - return fmt.Errorf("invalid proof, want hash %x, got %x", rootHash, emptytrie.Hash()), false + if err := triedb.Commit(rootHash, false, nil); err != nil { + return nil, nil, nil, false, err } - return nil, false // no more element. + return diskdb, tr, notary, false, nil // No more elements } // Special case, there is a provided edge proof but zero key/value // pairs, ensure there are no more accounts / slots in the trie. if len(keys) == 0 { - root, val, err := proofToPath(rootHash, nil, firstKey, proof, true) + root, val, err := proofToPath(rootHash, nil, firstKey, notary, true) if err != nil { - return err, false + return nil, nil, nil, false, err } if val != nil || hasRightElement(root, firstKey) { - return errors.New("more entries available"), false + return nil, nil, nil, false, errors.New("more entries available") } - return nil, false + // Since the entire proof is a single path, we can construct a trie and a + // node database directly out of the inputs, no need to generate them + diskdb := notary.Accessed() + tr := &Trie{ + db: NewDatabase(diskdb), + root: root, + } + return diskdb, tr, notary, hasRightElement(root, firstKey), nil } // Special case, there is only one element and two edge keys are same. // In this case, we can't construct two edge paths. So handle it here. if len(keys) == 1 && bytes.Equal(firstKey, lastKey) { - root, val, err := proofToPath(rootHash, nil, firstKey, proof, false) + root, val, err := proofToPath(rootHash, nil, firstKey, notary, false) if err != nil { - return err, false + return nil, nil, nil, false, err } if !bytes.Equal(firstKey, keys[0]) { - return errors.New("correct proof but invalid key"), false + return nil, nil, nil, false, errors.New("correct proof but invalid key") } if !bytes.Equal(val, values[0]) { - return errors.New("correct proof but invalid data"), false + return nil, nil, nil, false, errors.New("correct proof but invalid data") + } + // Since the entire proof is a single path, we can construct a trie and a + // node database directly out of the inputs, no need to generate them + diskdb := notary.Accessed() + tr := &Trie{ + db: NewDatabase(diskdb), + root: root, } - return nil, hasRightElement(root, firstKey) + return diskdb, tr, notary, hasRightElement(root, firstKey), nil } // Ok, in all other cases, we require two edge paths available. // First check the validity of edge keys. if bytes.Compare(firstKey, lastKey) >= 0 { - return errors.New("invalid edge keys"), false + return nil, nil, nil, false, errors.New("invalid edge keys") } // todo(rjl493456442) different length edge keys should be supported if len(firstKey) != len(lastKey) { - return errors.New("inconsistent edge keys"), false + return nil, nil, nil, false, errors.New("inconsistent edge keys") } // Convert the edge proofs to edge trie paths. Then we can // have the same tree architecture with the original one. // For the first edge proof, non-existent proof is allowed. - root, _, err := proofToPath(rootHash, nil, firstKey, proof, true) + root, _, err := proofToPath(rootHash, nil, firstKey, notary, true) if err != nil { - return err, false + return nil, nil, nil, false, err } // Pass the root node here, the second path will be merged // with the first one. For the last edge proof, non-existent // proof is also allowed. - root, _, err = proofToPath(rootHash, root, lastKey, proof, true) + root, _, err = proofToPath(rootHash, root, lastKey, notary, true) if err != nil { - return err, false + return nil, nil, nil, false, err } // Remove all internal references. All the removed parts should // be re-filled(or re-constructed) by the given leaves range. - if err := unsetInternal(root, firstKey, lastKey); err != nil { - return err, false + empty, err := unsetInternal(root, firstKey, lastKey) + if err != nil { + return nil, nil, nil, false, err } - // Rebuild the trie with the leave stream, the shape of trie + // Rebuild the trie with the leaf stream, the shape of trie // should be same with the original one. - newtrie := &Trie{root: root, db: NewDatabase(memorydb.New())} + var ( + diskdb = memorydb.New() + triedb = NewDatabase(diskdb) + ) + tr := &Trie{root: root, db: triedb} + if empty { + tr.root = nil + } for index, key := range keys { - newtrie.TryUpdate(key, values[index]) + tr.TryUpdate(key, values[index]) + } + if tr.Hash() != rootHash { + return nil, nil, nil, false, fmt.Errorf("invalid proof, want hash %x, got %x", rootHash, tr.Hash()) + } + // Proof seems valid, serialize all the nodes into the database + if _, err := tr.Commit(nil); err != nil { + return nil, nil, nil, false, err } - if newtrie.Hash() != rootHash { - return fmt.Errorf("invalid proof, want hash %x, got %x", rootHash, newtrie.Hash()), false + if err := triedb.Commit(rootHash, false, nil); err != nil { + return nil, nil, nil, false, err } - return nil, hasRightElement(root, keys[len(keys)-1]) + return diskdb, tr, notary, hasRightElement(root, keys[len(keys)-1]), nil } // get returns the child of the given node. Return nil if the diff --git a/trie/proof_test.go b/trie/proof_test.go index 6cdc242d9a..304affa9f2 100644 --- a/trie/proof_test.go +++ b/trie/proof_test.go @@ -19,6 +19,7 @@ package trie import ( "bytes" crand "crypto/rand" + "encoding/binary" mrand "math/rand" "sort" "testing" @@ -181,7 +182,7 @@ func TestRangeProof(t *testing.T) { keys = append(keys, entries[i].k) vals = append(vals, entries[i].v) } - err, _ := VerifyRangeProof(trie.Hash(), keys[0], keys[len(keys)-1], keys, vals, proof) + _, _, _, _, err := VerifyRangeProof(trie.Hash(), keys[0], keys[len(keys)-1], keys, vals, proof) if err != nil { t.Fatalf("Case %d(%d->%d) expect no error, got %v", i, start, end-1, err) } @@ -232,7 +233,7 @@ func TestRangeProofWithNonExistentProof(t *testing.T) { keys = append(keys, entries[i].k) vals = append(vals, entries[i].v) } - err, _ := VerifyRangeProof(trie.Hash(), first, last, keys, vals, proof) + _, _, _, _, err := VerifyRangeProof(trie.Hash(), first, last, keys, vals, proof) if err != nil { t.Fatalf("Case %d(%d->%d) expect no error, got %v", i, start, end-1, err) } @@ -253,7 +254,7 @@ func TestRangeProofWithNonExistentProof(t *testing.T) { k = append(k, entries[i].k) v = append(v, entries[i].v) } - err, _ := VerifyRangeProof(trie.Hash(), first, last, k, v, proof) + _, _, _, _, err := VerifyRangeProof(trie.Hash(), first, last, k, v, proof) if err != nil { t.Fatal("Failed to verify whole rang with non-existent edges") } @@ -288,7 +289,7 @@ func TestRangeProofWithInvalidNonExistentProof(t *testing.T) { k = append(k, entries[i].k) v = append(v, entries[i].v) } - err, _ := VerifyRangeProof(trie.Hash(), first, k[len(k)-1], k, v, proof) + _, _, _, _, err := VerifyRangeProof(trie.Hash(), first, k[len(k)-1], k, v, proof) if err == nil { t.Fatalf("Expected to detect the error, got nil") } @@ -310,7 +311,7 @@ func TestRangeProofWithInvalidNonExistentProof(t *testing.T) { k = append(k, entries[i].k) v = append(v, entries[i].v) } - err, _ = VerifyRangeProof(trie.Hash(), k[0], last, k, v, proof) + _, _, _, _, err = VerifyRangeProof(trie.Hash(), k[0], last, k, v, proof) if err == nil { t.Fatalf("Expected to detect the error, got nil") } @@ -334,7 +335,7 @@ func TestOneElementRangeProof(t *testing.T) { if err := trie.Prove(entries[start].k, 0, proof); err != nil { t.Fatalf("Failed to prove the first node %v", err) } - err, _ := VerifyRangeProof(trie.Hash(), entries[start].k, entries[start].k, [][]byte{entries[start].k}, [][]byte{entries[start].v}, proof) + _, _, _, _, err := VerifyRangeProof(trie.Hash(), entries[start].k, entries[start].k, [][]byte{entries[start].k}, [][]byte{entries[start].v}, proof) if err != nil { t.Fatalf("Expected no error, got %v", err) } @@ -349,7 +350,7 @@ func TestOneElementRangeProof(t *testing.T) { if err := trie.Prove(entries[start].k, 0, proof); err != nil { t.Fatalf("Failed to prove the last node %v", err) } - err, _ = VerifyRangeProof(trie.Hash(), first, entries[start].k, [][]byte{entries[start].k}, [][]byte{entries[start].v}, proof) + _, _, _, _, err = VerifyRangeProof(trie.Hash(), first, entries[start].k, [][]byte{entries[start].k}, [][]byte{entries[start].v}, proof) if err != nil { t.Fatalf("Expected no error, got %v", err) } @@ -364,7 +365,7 @@ func TestOneElementRangeProof(t *testing.T) { if err := trie.Prove(last, 0, proof); err != nil { t.Fatalf("Failed to prove the last node %v", err) } - err, _ = VerifyRangeProof(trie.Hash(), entries[start].k, last, [][]byte{entries[start].k}, [][]byte{entries[start].v}, proof) + _, _, _, _, err = VerifyRangeProof(trie.Hash(), entries[start].k, last, [][]byte{entries[start].k}, [][]byte{entries[start].v}, proof) if err != nil { t.Fatalf("Expected no error, got %v", err) } @@ -379,7 +380,26 @@ func TestOneElementRangeProof(t *testing.T) { if err := trie.Prove(last, 0, proof); err != nil { t.Fatalf("Failed to prove the last node %v", err) } - err, _ = VerifyRangeProof(trie.Hash(), first, last, [][]byte{entries[start].k}, [][]byte{entries[start].v}, proof) + _, _, _, _, err = VerifyRangeProof(trie.Hash(), first, last, [][]byte{entries[start].k}, [][]byte{entries[start].v}, proof) + if err != nil { + t.Fatalf("Expected no error, got %v", err) + } + + // Test the mini trie with only a single element. + tinyTrie := new(Trie) + entry := &kv{randBytes(32), randBytes(20), false} + tinyTrie.Update(entry.k, entry.v) + + first = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000").Bytes() + last = entry.k + proof = memorydb.New() + if err := tinyTrie.Prove(first, 0, proof); err != nil { + t.Fatalf("Failed to prove the first node %v", err) + } + if err := tinyTrie.Prove(last, 0, proof); err != nil { + t.Fatalf("Failed to prove the last node %v", err) + } + _, _, _, _, err = VerifyRangeProof(tinyTrie.Hash(), first, last, [][]byte{entry.k}, [][]byte{entry.v}, proof) if err != nil { t.Fatalf("Expected no error, got %v", err) } @@ -401,7 +421,7 @@ func TestAllElementsProof(t *testing.T) { k = append(k, entries[i].k) v = append(v, entries[i].v) } - err, _ := VerifyRangeProof(trie.Hash(), nil, nil, k, v, nil) + _, _, _, _, err := VerifyRangeProof(trie.Hash(), nil, nil, k, v, nil) if err != nil { t.Fatalf("Expected no error, got %v", err) } @@ -414,7 +434,7 @@ func TestAllElementsProof(t *testing.T) { if err := trie.Prove(entries[len(entries)-1].k, 0, proof); err != nil { t.Fatalf("Failed to prove the last node %v", err) } - err, _ = VerifyRangeProof(trie.Hash(), k[0], k[len(k)-1], k, v, proof) + _, _, _, _, err = VerifyRangeProof(trie.Hash(), k[0], k[len(k)-1], k, v, proof) if err != nil { t.Fatalf("Expected no error, got %v", err) } @@ -429,7 +449,7 @@ func TestAllElementsProof(t *testing.T) { if err := trie.Prove(last, 0, proof); err != nil { t.Fatalf("Failed to prove the last node %v", err) } - err, _ = VerifyRangeProof(trie.Hash(), first, last, k, v, proof) + _, _, _, _, err = VerifyRangeProof(trie.Hash(), first, last, k, v, proof) if err != nil { t.Fatalf("Expected no error, got %v", err) } @@ -462,7 +482,7 @@ func TestSingleSideRangeProof(t *testing.T) { k = append(k, entries[i].k) v = append(v, entries[i].v) } - err, _ := VerifyRangeProof(trie.Hash(), common.Hash{}.Bytes(), k[len(k)-1], k, v, proof) + _, _, _, _, err := VerifyRangeProof(trie.Hash(), common.Hash{}.Bytes(), k[len(k)-1], k, v, proof) if err != nil { t.Fatalf("Expected no error, got %v", err) } @@ -498,7 +518,7 @@ func TestReverseSingleSideRangeProof(t *testing.T) { k = append(k, entries[i].k) v = append(v, entries[i].v) } - err, _ := VerifyRangeProof(trie.Hash(), k[0], last.Bytes(), k, v, proof) + _, _, _, _, err := VerifyRangeProof(trie.Hash(), k[0], last.Bytes(), k, v, proof) if err != nil { t.Fatalf("Expected no error, got %v", err) } @@ -570,7 +590,7 @@ func TestBadRangeProof(t *testing.T) { index = mrand.Intn(end - start) vals[index] = nil } - err, _ := VerifyRangeProof(trie.Hash(), first, last, keys, vals, proof) + _, _, _, _, err := VerifyRangeProof(trie.Hash(), first, last, keys, vals, proof) if err == nil { t.Fatalf("%d Case %d index %d range: (%d->%d) expect error, got nil", i, testcase, index, start, end-1) } @@ -604,7 +624,7 @@ func TestGappedRangeProof(t *testing.T) { keys = append(keys, entries[i].k) vals = append(vals, entries[i].v) } - err, _ := VerifyRangeProof(trie.Hash(), keys[0], keys[len(keys)-1], keys, vals, proof) + _, _, _, _, err := VerifyRangeProof(trie.Hash(), keys[0], keys[len(keys)-1], keys, vals, proof) if err == nil { t.Fatal("expect error, got nil") } @@ -631,7 +651,7 @@ func TestSameSideProofs(t *testing.T) { if err := trie.Prove(last, 0, proof); err != nil { t.Fatalf("Failed to prove the last node %v", err) } - err, _ := VerifyRangeProof(trie.Hash(), first, last, [][]byte{entries[pos].k}, [][]byte{entries[pos].v}, proof) + _, _, _, _, err := VerifyRangeProof(trie.Hash(), first, last, [][]byte{entries[pos].k}, [][]byte{entries[pos].v}, proof) if err == nil { t.Fatalf("Expected error, got nil") } @@ -647,7 +667,7 @@ func TestSameSideProofs(t *testing.T) { if err := trie.Prove(last, 0, proof); err != nil { t.Fatalf("Failed to prove the last node %v", err) } - err, _ = VerifyRangeProof(trie.Hash(), first, last, [][]byte{entries[pos].k}, [][]byte{entries[pos].v}, proof) + _, _, _, _, err = VerifyRangeProof(trie.Hash(), first, last, [][]byte{entries[pos].k}, [][]byte{entries[pos].v}, proof) if err == nil { t.Fatalf("Expected error, got nil") } @@ -715,7 +735,7 @@ func TestHasRightElement(t *testing.T) { k = append(k, entries[i].k) v = append(v, entries[i].v) } - err, hasMore := VerifyRangeProof(trie.Hash(), firstKey, lastKey, k, v, proof) + _, _, _, hasMore, err := VerifyRangeProof(trie.Hash(), firstKey, lastKey, k, v, proof) if err != nil { t.Fatalf("Expected no error, got %v", err) } @@ -748,13 +768,57 @@ func TestEmptyRangeProof(t *testing.T) { if err := trie.Prove(first, 0, proof); err != nil { t.Fatalf("Failed to prove the first node %v", err) } - err, _ := VerifyRangeProof(trie.Hash(), first, nil, nil, nil, proof) + db, tr, not, _, err := VerifyRangeProof(trie.Hash(), first, nil, nil, nil, proof) if c.err && err == nil { t.Fatalf("Expected error, got nil") } if !c.err && err != nil { t.Fatalf("Expected no error, got %v", err) } + // If no error was returned, ensure the returned trie and database contains + // the entire proof, since there's no value + if !c.err { + if err := tr.Prove(first, 0, memorydb.New()); err != nil { + t.Errorf("returned trie doesn't contain original proof: %v", err) + } + if memdb := db.(*memorydb.Database); memdb.Len() != proof.Len() { + t.Errorf("database entry count mismatch: have %d, want %d", memdb.Len(), proof.Len()) + } + if not == nil { + t.Errorf("missing notary") + } + } + } +} + +// TestBloatedProof tests a malicious proof, where the proof is more or less the +// whole trie. +func TestBloatedProof(t *testing.T) { + // Use a small trie + trie, kvs := nonRandomTrie(100) + var entries entrySlice + for _, kv := range kvs { + entries = append(entries, kv) + } + sort.Sort(entries) + var keys [][]byte + var vals [][]byte + + proof := memorydb.New() + for i, entry := range entries { + trie.Prove(entry.k, 0, proof) + if i == 50 { + keys = append(keys, entry.k) + vals = append(vals, entry.v) + } + } + want := memorydb.New() + trie.Prove(keys[0], 0, want) + trie.Prove(keys[len(keys)-1], 0, want) + + _, _, notary, _, _ := VerifyRangeProof(trie.Hash(), keys[0], keys[len(keys)-1], keys, vals, proof) + if used := notary.Accessed().(*memorydb.Database); used.Len() != want.Len() { + t.Fatalf("notary proof size mismatch: have %d, want %d", used.Len(), want.Len()) } } @@ -858,7 +922,7 @@ func benchmarkVerifyRangeProof(b *testing.B, size int) { b.ResetTimer() for i := 0; i < b.N; i++ { - err, _ := VerifyRangeProof(trie.Hash(), keys[0], keys[len(keys)-1], keys, values, proof) + _, _, _, _, err := VerifyRangeProof(trie.Hash(), keys[0], keys[len(keys)-1], keys, values, proof) if err != nil { b.Fatalf("Case %d(%d->%d) expect no error, got %v", i, start, end-1, err) } @@ -889,3 +953,20 @@ func randBytes(n int) []byte { crand.Read(r) return r } + +func nonRandomTrie(n int) (*Trie, map[string]*kv) { + trie := new(Trie) + vals := make(map[string]*kv) + max := uint64(0xffffffffffffffff) + for i := uint64(0); i < uint64(n); i++ { + value := make([]byte, 32) + key := make([]byte, 32) + binary.LittleEndian.PutUint64(key, i) + binary.LittleEndian.PutUint64(value, i-max) + //value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false} + elem := &kv{key, value, false} + trie.Update(elem.k, elem.v) + vals[string(elem.k)] = elem + } + return trie, vals +} diff --git a/trie/secure_trie.go b/trie/secure_trie.go index 87b364fb1b..e38471c1b7 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -147,12 +147,13 @@ func (t *SecureTrie) GetKey(shaKey []byte) []byte { func (t *SecureTrie) Commit(onleaf LeafCallback) (root common.Hash, err error) { // Write all the pre-images to the actual disk database if len(t.getSecKeyCache()) > 0 { - t.trie.db.lock.Lock() - for hk, key := range t.secKeyCache { - t.trie.db.insertPreimage(common.BytesToHash([]byte(hk)), key) + if t.trie.db.preimages != nil { // Ugly direct check but avoids the below write lock + t.trie.db.lock.Lock() + for hk, key := range t.secKeyCache { + t.trie.db.insertPreimage(common.BytesToHash([]byte(hk)), key) + } + t.trie.db.lock.Unlock() } - t.trie.db.lock.Unlock() - t.secKeyCache = make(map[string][]byte) } // Commit the trie to its intermediate node database diff --git a/trie/stacktrie.go b/trie/stacktrie.go index 575a04022f..a198eb204b 100644 --- a/trie/stacktrie.go +++ b/trie/stacktrie.go @@ -35,7 +35,7 @@ var stPool = sync.Pool{ }, } -func stackTrieFromPool(db ethdb.KeyValueStore) *StackTrie { +func stackTrieFromPool(db ethdb.KeyValueWriter) *StackTrie { st := stPool.Get().(*StackTrie) st.db = db return st @@ -50,24 +50,23 @@ func returnToPool(st *StackTrie) { // in order. Once it determines that a subtree will no longer be inserted // into, it will hash it and free up the memory it uses. type StackTrie struct { - nodeType uint8 // node type (as in branch, ext, leaf) - val []byte // value contained by this node if it's a leaf - key []byte // key chunk covered by this (full|ext) node - keyOffset int // offset of the key chunk inside a full key - children [16]*StackTrie // list of children (for fullnodes and exts) - - db ethdb.KeyValueStore // Pointer to the commit db, can be nil + nodeType uint8 // node type (as in branch, ext, leaf) + val []byte // value contained by this node if it's a leaf + key []byte // key chunk covered by this (full|ext) node + keyOffset int // offset of the key chunk inside a full key + children [16]*StackTrie // list of children (for fullnodes and exts) + db ethdb.KeyValueWriter // Pointer to the commit db, can be nil } // NewStackTrie allocates and initializes an empty trie. -func NewStackTrie(db ethdb.KeyValueStore) *StackTrie { +func NewStackTrie(db ethdb.KeyValueWriter) *StackTrie { return &StackTrie{ nodeType: emptyNode, db: db, } } -func newLeaf(ko int, key, val []byte, db ethdb.KeyValueStore) *StackTrie { +func newLeaf(ko int, key, val []byte, db ethdb.KeyValueWriter) *StackTrie { st := stackTrieFromPool(db) st.nodeType = leafNode st.keyOffset = ko @@ -76,7 +75,7 @@ func newLeaf(ko int, key, val []byte, db ethdb.KeyValueStore) *StackTrie { return st } -func newExt(ko int, key []byte, child *StackTrie, db ethdb.KeyValueStore) *StackTrie { +func newExt(ko int, key []byte, child *StackTrie, db ethdb.KeyValueWriter) *StackTrie { st := stackTrieFromPool(db) st.nodeType = extNode st.keyOffset = ko diff --git a/trie/stacktrie_test.go b/trie/stacktrie_test.go index d4488b4029..29706f2e9d 100644 --- a/trie/stacktrie_test.go +++ b/trie/stacktrie_test.go @@ -1,16 +1,9 @@ package trie import ( - "bytes" - "fmt" - "math/big" - mrand "math/rand" "testing" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb/memorydb" ) @@ -78,169 +71,6 @@ func TestValLength56(t *testing.T) { } } -func genTxs(num uint64) (types.Transactions, error) { - key, err := crypto.HexToECDSA("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") - if err != nil { - return nil, err - } - var addr = crypto.PubkeyToAddress(key.PublicKey) - newTx := func(i uint64) (*types.Transaction, error) { - signer := types.NewEIP155Signer(big.NewInt(18)) - tx, err := types.SignTx(types.NewTransaction(i, addr, new(big.Int), 0, new(big.Int).SetUint64(10000000), nil), signer, key) - return tx, err - } - var txs types.Transactions - for i := uint64(0); i < num; i++ { - tx, err := newTx(i) - if err != nil { - return nil, err - } - txs = append(txs, tx) - } - return txs, nil -} - -func TestDeriveSha(t *testing.T) { - txs, err := genTxs(0) - if err != nil { - t.Fatal(err) - } - for len(txs) < 1000 { - exp := types.DeriveSha(txs, newEmpty()) - got := types.DeriveSha(txs, NewStackTrie(nil)) - if !bytes.Equal(got[:], exp[:]) { - t.Fatalf("%d txs: got %x exp %x", len(txs), got, exp) - } - newTxs, err := genTxs(uint64(len(txs) + 1)) - if err != nil { - t.Fatal(err) - } - txs = append(txs, newTxs...) - } -} - -func BenchmarkDeriveSha200(b *testing.B) { - txs, err := genTxs(200) - if err != nil { - b.Fatal(err) - } - var exp common.Hash - var got common.Hash - b.Run("std_trie", func(b *testing.B) { - b.ResetTimer() - b.ReportAllocs() - for i := 0; i < b.N; i++ { - exp = types.DeriveSha(txs, newEmpty()) - } - }) - - b.Run("stack_trie", func(b *testing.B) { - b.ResetTimer() - b.ReportAllocs() - for i := 0; i < b.N; i++ { - got = types.DeriveSha(txs, NewStackTrie(nil)) - } - }) - if got != exp { - b.Errorf("got %x exp %x", got, exp) - } -} - -type dummyDerivableList struct { - len int - seed int -} - -func newDummy(seed int) *dummyDerivableList { - d := &dummyDerivableList{} - src := mrand.NewSource(int64(seed)) - // don't use lists longer than 4K items - d.len = int(src.Int63() & 0x0FFF) - d.seed = seed - return d -} - -func (d *dummyDerivableList) Len() int { - return d.len -} - -func (d *dummyDerivableList) GetRlp(i int) []byte { - src := mrand.NewSource(int64(d.seed + i)) - // max item size 256, at least 1 byte per item - size := 1 + src.Int63()&0x00FF - data := make([]byte, size) - _, err := mrand.New(src).Read(data) - if err != nil { - panic(err) - } - return data -} - -func printList(l types.DerivableList) { - fmt.Printf("list length: %d\n", l.Len()) - fmt.Printf("{\n") - for i := 0; i < l.Len(); i++ { - v := l.GetRlp(i) - fmt.Printf("\"0x%x\",\n", v) - } - fmt.Printf("},\n") -} - -func TestFuzzDeriveSha(t *testing.T) { - // increase this for longer runs -- it's set to quite low for travis - rndSeed := mrand.Int() - for i := 0; i < 10; i++ { - seed := rndSeed + i - exp := types.DeriveSha(newDummy(i), newEmpty()) - got := types.DeriveSha(newDummy(i), NewStackTrie(nil)) - if !bytes.Equal(got[:], exp[:]) { - printList(newDummy(seed)) - t.Fatalf("seed %d: got %x exp %x", seed, got, exp) - } - } -} - -type flatList struct { - rlpvals []string -} - -func newFlatList(rlpvals []string) *flatList { - return &flatList{rlpvals} -} -func (f *flatList) Len() int { - return len(f.rlpvals) -} -func (f *flatList) GetRlp(i int) []byte { - return hexutil.MustDecode(f.rlpvals[i]) -} - -// TestDerivableList contains testcases found via fuzzing -func TestDerivableList(t *testing.T) { - type tcase []string - tcs := []tcase{ - { - "0xc041", - }, - { - "0xf04cf757812428b0763112efb33b6f4fad7deb445e", - "0xf04cf757812428b0763112efb33b6f4fad7deb445e", - }, - { - "0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d", - "0x6cd850eca0a7ac46bb1748d7b9cb88aa3bd21c57d852c28198ad8fa422c4595032e88a4494b4778b36b944fe47a52b8c5cd312910139dfcb4147ab8e972cc456bcb063f25dd78f54c4d34679e03142c42c662af52947d45bdb6e555751334ace76a5080ab5a0256a1d259855dfc5c0b8023b25befbb13fd3684f9f755cbd3d63544c78ee2001452dd54633a7593ade0b183891a0a4e9c7844e1254005fbe592b1b89149a502c24b6e1dca44c158aebedf01beae9c30cabe16a", - "0x14abd5c47c0be87b0454596baad2", - "0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d", - }, - } - for i, tc := range tcs[1:] { - exp := types.DeriveSha(newFlatList(tc), newEmpty()) - got := types.DeriveSha(newFlatList(tc), NewStackTrie(nil)) - if !bytes.Equal(got[:], exp[:]) { - t.Fatalf("case %d: got %x exp %x", i, got, exp) - } - } -} - // TestUpdateSmallNodes tests a case where the leaves are small (both key and value), // which causes a lot of node-within-node. This case was found via fuzzing. func TestUpdateSmallNodes(t *testing.T) { diff --git a/trie/sync.go b/trie/sync.go index bc93ddd3fb..dd8279b665 100644 --- a/trie/sync.go +++ b/trie/sync.go @@ -313,11 +313,15 @@ func (s *Sync) Commit(dbw ethdb.Batch) error { // Dump the membatch into a database dbw for key, value := range s.membatch.nodes { rawdb.WriteTrieNode(dbw, key, value) - s.bloom.Add(key[:]) + if s.bloom != nil { + s.bloom.Add(key[:]) + } } for key, value := range s.membatch.codes { rawdb.WriteCode(dbw, key, value) - s.bloom.Add(key[:]) + if s.bloom != nil { + s.bloom.Add(key[:]) + } } // Drop the membatch data and return s.membatch = newSyncMemBatch() @@ -410,7 +414,7 @@ func (s *Sync) children(req *request, object node) ([]*request, error) { // Bloom filter says this might be a duplicate, double check. // If database says yes, then at least the trie node is present // and we hold the assumption that it's NOT legacy contract code. - if blob := rawdb.ReadTrieNode(s.database, common.BytesToHash(node)); len(blob) > 0 { + if blob := rawdb.ReadTrieNode(s.database, hash); len(blob) > 0 { continue } // False positive, bump fault meter diff --git a/trie/sync_bloom.go b/trie/sync_bloom.go index 89f61d66d9..1afcce21da 100644 --- a/trie/sync_bloom.go +++ b/trie/sync_bloom.go @@ -19,7 +19,6 @@ package trie import ( "encoding/binary" "fmt" - "math" "sync" "sync/atomic" "time" @@ -29,7 +28,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" - "github.com/steakknife/bloomfilter" + bloomfilter "github.com/holiman/bloomfilter/v2" ) var ( @@ -41,18 +40,6 @@ var ( bloomErrorGauge = metrics.NewRegisteredGauge("trie/bloom/error", nil) ) -// syncBloomHasher is a wrapper around a byte blob to satisfy the interface API -// requirements of the bloom library used. It's used to convert a trie hash or -// contract code hash into a 64 bit mini hash. -type syncBloomHasher []byte - -func (f syncBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") } -func (f syncBloomHasher) Sum(b []byte) []byte { panic("not implemented") } -func (f syncBloomHasher) Reset() { panic("not implemented") } -func (f syncBloomHasher) BlockSize() int { panic("not implemented") } -func (f syncBloomHasher) Size() int { return 8 } -func (f syncBloomHasher) Sum64() uint64 { return binary.BigEndian.Uint64(f) } - // SyncBloom is a bloom filter used during fast sync to quickly decide if a trie // node or contract code already exists on disk or not. It self populates from the // provided disk database on creation in a background thread and will only start @@ -69,7 +56,7 @@ type SyncBloom struct { // initializes it from the database. The bloom is hard coded to use 3 filters. func NewSyncBloom(memory uint64, database ethdb.Iteratee) *SyncBloom { // Create the bloom filter to track known trie nodes - bloom, err := bloomfilter.New(memory*1024*1024*8, 3) + bloom, err := bloomfilter.New(memory*1024*1024*8, 4) if err != nil { panic(fmt.Sprintf("failed to create bloom: %v", err)) } @@ -110,12 +97,11 @@ func (b *SyncBloom) init(database ethdb.Iteratee) { // If the database entry is a trie node, add it to the bloom key := it.Key() if len(key) == common.HashLength { - b.bloom.Add(syncBloomHasher(key)) + b.bloom.AddHash(binary.BigEndian.Uint64(key)) bloomLoadMeter.Mark(1) - } - // If the database entry is a contract code, add it to the bloom - if ok, hash := rawdb.IsCodeKey(key); ok { - b.bloom.Add(syncBloomHasher(hash)) + } else if ok, hash := rawdb.IsCodeKey(key); ok { + // If the database entry is a contract code, add it to the bloom + b.bloom.AddHash(binary.BigEndian.Uint64(hash)) bloomLoadMeter.Mark(1) } // If enough time elapsed since the last iterator swap, restart @@ -125,14 +111,14 @@ func (b *SyncBloom) init(database ethdb.Iteratee) { it.Release() it = database.NewIterator(nil, key) - log.Info("Initializing fast sync bloom", "items", b.bloom.N(), "errorrate", b.errorRate(), "elapsed", common.PrettyDuration(time.Since(start))) + log.Info("Initializing state bloom", "items", b.bloom.N(), "errorrate", b.bloom.FalsePosititveProbability(), "elapsed", common.PrettyDuration(time.Since(start))) swap = time.Now() } } it.Release() // Mark the bloom filter inited and return - log.Info("Initialized fast sync bloom", "items", b.bloom.N(), "errorrate", b.errorRate(), "elapsed", common.PrettyDuration(time.Since(start))) + log.Info("Initialized state bloom", "items", b.bloom.N(), "errorrate", b.bloom.FalsePosititveProbability(), "elapsed", common.PrettyDuration(time.Since(start))) atomic.StoreUint32(&b.inited, 1) } @@ -141,7 +127,7 @@ func (b *SyncBloom) init(database ethdb.Iteratee) { func (b *SyncBloom) meter() { for { // Report the current error ration. No floats, lame, scale it up. - bloomErrorGauge.Update(int64(b.errorRate() * 100000)) + bloomErrorGauge.Update(int64(b.bloom.FalsePosititveProbability() * 100000)) // Wait one second, but check termination more frequently for i := 0; i < 10; i++ { @@ -162,7 +148,7 @@ func (b *SyncBloom) Close() error { b.pend.Wait() // Wipe the bloom, but mark it "uninited" just in case someone attempts an access - log.Info("Deallocated fast sync bloom", "items", b.bloom.N(), "errorrate", b.errorRate()) + log.Info("Deallocated state bloom", "items", b.bloom.N(), "errorrate", b.bloom.FalsePosititveProbability()) atomic.StoreUint32(&b.inited, 0) b.bloom = nil @@ -175,7 +161,7 @@ func (b *SyncBloom) Add(hash []byte) { if atomic.LoadUint32(&b.closed) == 1 { return } - b.bloom.Add(syncBloomHasher(hash)) + b.bloom.AddHash(binary.BigEndian.Uint64(hash)) bloomAddMeter.Mark(1) } @@ -193,22 +179,9 @@ func (b *SyncBloom) Contains(hash []byte) bool { return true } // Bloom initialized, check the real one and report any successful misses - maybe := b.bloom.Contains(syncBloomHasher(hash)) + maybe := b.bloom.ContainsHash(binary.BigEndian.Uint64(hash)) if !maybe { bloomMissMeter.Mark(1) } return maybe } - -// errorRate calculates the probability of a random containment test returning a -// false positive. -// -// We're calculating it ourselves because the bloom library we used missed a -// parentheses in the formula and calculates it wrong. And it's discontinued... -func (b *SyncBloom) errorRate() float64 { - k := float64(b.bloom.K()) - n := float64(b.bloom.N()) - m := float64(b.bloom.M()) - - return math.Pow(1.0-math.Exp((-k)*(n+0.5)/(m-1)), k) -} diff --git a/trie/sync_test.go b/trie/sync_test.go index 39e0f9575e..cb3283875d 100644 --- a/trie/sync_test.go +++ b/trie/sync_test.go @@ -377,7 +377,6 @@ func TestIncompleteSync(t *testing.T) { nodes, _, codes := sched.Missing(1) queue := append(append([]common.Hash{}, nodes...), codes...) - for len(queue) > 0 { // Fetch a batch of trie nodes results := make([]SyncResult, len(queue)) @@ -401,10 +400,8 @@ func TestIncompleteSync(t *testing.T) { batch.Write() for _, result := range results { added = append(added, result.Hash) - } - // Check that all known sub-tries in the synced trie are complete - for _, root := range added { - if err := checkTrieConsistency(triedb, root); err != nil { + // Check that all known sub-tries in the synced trie are complete + if err := checkTrieConsistency(triedb, result.Hash); err != nil { t.Fatalf("trie inconsistent: %v", err) } } diff --git a/trie/trie.go b/trie/trie.go index 6ddbbd78d3..87b72ecf17 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -19,13 +19,13 @@ package trie import ( "bytes" + "errors" "fmt" "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rlp" ) var ( @@ -159,29 +159,26 @@ func (t *Trie) TryGetNode(path []byte) ([]byte, int, error) { if item == nil { return nil, resolved, nil } - enc, err := rlp.EncodeToBytes(item) - if err != nil { - log.Error("Encoding existing trie node failed", "err", err) - return nil, resolved, err - } - return enc, resolved, err + return item, resolved, err } -func (t *Trie) tryGetNode(origNode node, path []byte, pos int) (item node, newnode node, resolved int, err error) { +func (t *Trie) tryGetNode(origNode node, path []byte, pos int) (item []byte, newnode node, resolved int, err error) { // If we reached the requested path, return the current node if pos >= len(path) { - // Don't return collapsed hash nodes though - if _, ok := origNode.(hashNode); !ok { - // Short nodes have expanded keys, compact them before returning - item := origNode - if sn, ok := item.(*shortNode); ok { - item = &shortNode{ - Key: hexToCompact(sn.Key), - Val: sn.Val, - } - } - return item, origNode, 0, nil + // Although we most probably have the original node expanded, encoding + // that into consensus form can be nasty (needs to cascade down) and + // time consuming. Instead, just pull the hash up from disk directly. + var hash hashNode + if node, ok := origNode.(hashNode); ok { + hash = node + } else { + hash, _ = origNode.cache() + } + if hash == nil { + return nil, origNode, 0, errors.New("non-consensus node") } + blob, err := t.db.Node(common.BytesToHash(hash)) + return blob, origNode, 1, err } // Path still needs to be traversed, descend into children switch n := (origNode).(type) { @@ -491,7 +488,7 @@ func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) { // Hash returns the root hash of the trie. It does not write to the // database and can be used even if the trie doesn't have one. func (t *Trie) Hash() common.Hash { - hash, cached, _ := t.hashRoot(nil) + hash, cached, _ := t.hashRoot() t.root = cached return common.BytesToHash(hash.(hashNode)) } @@ -545,7 +542,7 @@ func (t *Trie) Commit(onleaf LeafCallback) (root common.Hash, err error) { } // hashRoot calculates the root hash of the given trie -func (t *Trie) hashRoot(db *Database) (node, node, error) { +func (t *Trie) hashRoot() (node, node, error) { if t.root == nil { return hashNode(emptyRoot.Bytes()), nil, nil } diff --git a/trie/trie_test.go b/trie/trie_test.go index 682dec157c..3aa4098d14 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -322,7 +322,7 @@ func TestLargeValue(t *testing.T) { // TestRandomCases tests som cases that were found via random fuzzing func TestRandomCases(t *testing.T) { - var rt []randTestStep = []randTestStep{ + var rt = []randTestStep{ {op: 6, key: common.Hex2Bytes(""), value: common.Hex2Bytes("")}, // step 0 {op: 6, key: common.Hex2Bytes(""), value: common.Hex2Bytes("")}, // step 1 {op: 0, key: common.Hex2Bytes("d51b182b95d677e5f1c82508c0228de96b73092d78ce78b2230cd948674f66fd1483bd"), value: common.Hex2Bytes("0000000000000002")}, // step 2 @@ -594,21 +594,20 @@ func benchmarkCommitAfterHash(b *testing.B, onleaf LeafCallback) { func TestTinyTrie(t *testing.T) { // Create a realistic account trie to hash - _, accounts := makeAccounts(10000) + _, accounts := makeAccounts(5) trie := newEmpty() trie.Update(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001337"), accounts[3]) - if exp, root := common.HexToHash("4fa6efd292cffa2db0083b8bedd23add2798ae73802442f52486e95c3df7111c"), trie.Hash(); exp != root { - t.Fatalf("1: got %x, exp %x", root, exp) + if exp, root := common.HexToHash("8c6a85a4d9fda98feff88450299e574e5378e32391f75a055d470ac0653f1005"), trie.Hash(); exp != root { + t.Errorf("1: got %x, exp %x", root, exp) } trie.Update(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001338"), accounts[4]) - if exp, root := common.HexToHash("cb5fb1213826dad9e604f095f8ceb5258fe6b5c01805ce6ef019a50699d2d479"), trie.Hash(); exp != root { - t.Fatalf("2: got %x, exp %x", root, exp) + if exp, root := common.HexToHash("ec63b967e98a5720e7f720482151963982890d82c9093c0d486b7eb8883a66b1"), trie.Hash(); exp != root { + t.Errorf("2: got %x, exp %x", root, exp) } trie.Update(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001339"), accounts[4]) - if exp, root := common.HexToHash("ed7e06b4010057d8703e7b9a160a6d42cf4021f9020da3c8891030349a646987"), trie.Hash(); exp != root { - t.Fatalf("3: got %x, exp %x", root, exp) + if exp, root := common.HexToHash("0608c1d1dc3905fa22204c7a0e43644831c3b6d3def0f274be623a948197e64a"), trie.Hash(); exp != root { + t.Errorf("3: got %x, exp %x", root, exp) } - checktr, _ := New(common.Hash{}, trie.db) it := NewIterator(trie.NodeIterator(nil)) for it.Next() { @@ -630,7 +629,7 @@ func TestCommitAfterHash(t *testing.T) { trie.Hash() trie.Commit(nil) root := trie.Hash() - exp := common.HexToHash("e5e9c29bb50446a4081e6d1d748d2892c6101c1e883a1f77cf21d4094b697822") + exp := common.HexToHash("72f9d3f3fe1e1dd7b8936442e7642aef76371472d94319900790053c493f3fe6") if exp != root { t.Errorf("got %x, exp %x", root, exp) } @@ -646,19 +645,27 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) { // Create a realistic account trie to hash addresses = make([][20]byte, size) for i := 0; i < len(addresses); i++ { - for j := 0; j < len(addresses[i]); j++ { - addresses[i][j] = byte(random.Intn(256)) - } + data := make([]byte, 20) + random.Read(data) + copy(addresses[i][:], data) } accounts = make([][]byte, len(addresses)) for i := 0; i < len(accounts); i++ { var ( - nonce = uint64(random.Int63()) - balance = new(big.Int).Rand(random, new(big.Int).Exp(common.Big2, common.Big256, nil)) - root = emptyRoot - code = crypto.Keccak256(nil) + nonce = uint64(random.Int63()) + root = emptyRoot + code = crypto.Keccak256(nil) ) - accounts[i], _ = rlp.EncodeToBytes(&account{nonce, balance, root, code}) + // The big.Rand function is not deterministic with regards to 64 vs 32 bit systems, + // and will consume different amount of data from the rand source. + //balance = new(big.Int).Rand(random, new(big.Int).Exp(common.Big2, common.Big256, nil)) + // Therefore, we instead just read via byte buffer + numBytes := random.Uint32() % 33 // [0, 32] bytes + balanceBytes := make([]byte, numBytes) + random.Read(balanceBytes) + balance := new(big.Int).SetBytes(balanceBytes) + data, _ := rlp.EncodeToBytes(&account{nonce, balance, root, code}) + accounts[i] = data } return addresses, accounts } @@ -714,12 +721,12 @@ func TestCommitSequence(t *testing.T) { expWriteSeqHash []byte expCallbackSeqHash []byte }{ - {20, common.FromHex("68c495e45209e243eb7e4f4e8ca8f9f7be71003bd9cafb8061b4534373740193"), - common.FromHex("01783213033d6b7781a641ab499e680d959336d025ac16f44d02f4f0c021bbf5")}, - {200, common.FromHex("3b20d16c13c4bc3eb3b8d0ad7a169fef3b1600e056c0665895d03d3d2b2ff236"), - common.FromHex("fb8db0ec82e8f02729f11228940885b181c3047ab0d654ed0110291ca57111a8")}, - {2000, common.FromHex("34eff3d1048bebdf77e9ae8bd939f2e7c742edc3dcd1173cff1aad9dbd20451a"), - common.FromHex("1c981604b1a9f8ffa40e0ae66b14830a87f5a4ed8345146a3912e6b2dcb05e63")}, + {20, common.FromHex("873c78df73d60e59d4a2bcf3716e8bfe14554549fea2fc147cb54129382a8066"), + common.FromHex("ff00f91ac05df53b82d7f178d77ada54fd0dca64526f537034a5dbe41b17df2a")}, + {200, common.FromHex("ba03d891bb15408c940eea5ee3d54d419595102648d02774a0268d892add9c8e"), + common.FromHex("f3cd509064c8d319bbdd1c68f511850a902ad275e6ed5bea11547e23d492a926")}, + {2000, common.FromHex("f7a184f20df01c94f09537401d11e68d97ad0c00115233107f51b9c287ce60c7"), + common.FromHex("ff795ea898ba1e4cfed4a33b4cf5535a347a02cf931f88d88719faf810f9a1c9")}, } { addresses, accounts := makeAccounts(tc.count) // This spongeDb is used to check the sequence of disk-db-writes @@ -740,10 +747,10 @@ func TestCommitSequence(t *testing.T) { callbackSponge.Write(c[:]) }) if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) { - t.Fatalf("test %d, disk write sequence wrong:\ngot %x exp %x\n", i, got, exp) + t.Errorf("test %d, disk write sequence wrong:\ngot %x exp %x\n", i, got, exp) } if got, exp := callbackSponge.Sum(nil), tc.expCallbackSeqHash; !bytes.Equal(got, exp) { - t.Fatalf("test %d, call back sequence wrong:\ngot: %x exp %x\n", i, got, exp) + t.Errorf("test %d, call back sequence wrong:\ngot: %x exp %x\n", i, got, exp) } } } @@ -1053,7 +1060,7 @@ func tempDB() (string, *Database) { if err != nil { panic(fmt.Sprintf("can't create temporary directory: %v", err)) } - diskdb, err := leveldb.New(dir, 256, 0, "") + diskdb, err := leveldb.New(dir, 256, 0, "", false) if err != nil { panic(fmt.Sprintf("can't create temporary database: %v", err)) }