diff --git a/.github/labeler.yml b/.github/labeler.yml index 55b78caf22..7ab7550300 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -16,8 +16,6 @@ crate-geth-utils: - geth-utils/**/* crate-integration-tests: - integration-tests/**/* -crate-keccak: - - keccak256/**/* crate-mock: - mock/**/* crate-prover: diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 950aa422ea..0000000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,29 +0,0 @@ -categories: - - title: "Changes in the bus-mapping" - label: "crate-bus-mapping" - - title: "Changes in circuit-benchmarks" - label: "crate-circuit-benchmarks" - - title: "Changes in eth-types" - label: "crate-eth-types" - - title: "Changes in external-tracer" - label: "crate-external-tracer" - - title: "Changes in gadgets" - label: "crate-gadgets" - - title: "Changes in geth-utils" - label: "crate-geth-utils" - - title: "Changes in integration-tests" - label: "crate-integration-tests" - - title: "Changes in Keccak" - label: "crate-keccak" - - title: "Changes in mock" - label: "crate-mock" - - title: "Changes in prover" - label: "crate-prover" - - title: "Changes in the zkevm-circuits" - label: "crate-zkevm-circuits" -version-resolver: - default: minor -prerelease: true -template: | - ## What’s Changed - $CHANGES diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf354642cb..0887779a97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: - name: Setup golang uses: actions/setup-go@v3 with: - go-version: ~1.18 + go-version: ~1.19 # Go cache for building geth-utils - name: Go cache uses: actions/cache@v3 diff --git a/.github/workflows/draft.yml b/.github/workflows/draft.yml deleted file mode 100644 index bb67d4a5b6..0000000000 --- a/.github/workflows/draft.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Release Drafter - -on: - push: - branches: - - main - -jobs: - update_release_draft: - runs-on: ubuntu-latest - steps: - - uses: release-drafter/release-drafter@v5 - with: - config-name: release-drafter.yml - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 777407ab11..6839d3cb76 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -61,7 +61,7 @@ jobs: - name: Setup golang uses: actions/setup-go@v3 with: - go-version: ~1.18 + go-version: ~1.19 # Go cache for building geth-utils - name: Go cache uses: actions/cache@v3 diff --git a/Cargo.lock b/Cargo.lock index 7dc3f0d4b2..0f6c082a77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "aes" version = "0.7.5" @@ -51,15 +45,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anyhow" version = "1.0.69" @@ -123,7 +108,7 @@ checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" dependencies = [ "futures", "pharos", - "rustc_version 0.4.0", + "rustc_version", ] [[package]] @@ -161,6 +146,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base58" version = "0.1.0" @@ -412,15 +403,15 @@ dependencies = [ "ethers-core", "ethers-providers", "gadgets", - "halo2_proofs 0.2.0", + "halo2_proofs", "hex", "itertools", - "keccak256", "lazy_static", "log", "mock", "pretty_assertions", "rand", + "revm-precompile", "serde", "serde_json", "strum", @@ -468,12 +459,6 @@ dependencies = [ "syn", ] -[[package]] -name = "bytemuck" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" - [[package]] name = "byteorder" version = "1.4.3" @@ -489,12 +474,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - [[package]] name = "cc" version = "1.0.79" @@ -513,13 +492,8 @@ version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ - "iana-time-zone", - "js-sys", "num-integer", "num-traits", - "time", - "wasm-bindgen", - "winapi", ] [[package]] @@ -540,9 +514,8 @@ dependencies = [ "env_logger", "eth-types", "ethers-signers", - "halo2_proofs 0.2.0", + "halo2_proofs", "itertools", - "keccak256", "mock", "rand", "rand_chacha", @@ -550,17 +523,6 @@ dependencies = [ "zkevm-circuits", ] -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "bitflags", - "textwrap 0.11.0", - "unicode-width", -] - [[package]] name = "clap" version = "3.2.23" @@ -573,9 +535,9 @@ dependencies = [ "clap_lex", "indexmap", "once_cell", - "strsim 0.10.0", + "strsim", "termcolor", - "textwrap 0.16.0", + "textwrap", ] [[package]] @@ -623,25 +585,6 @@ dependencies = [ "syn", ] -[[package]] -name = "cmake" -version = "0.1.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db34956e100b30725f2eb215f90d4871051239535632f84fea3bc92722c66b7c" -dependencies = [ - "cc", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "coins-bip32" version = "0.7.0" @@ -654,7 +597,7 @@ dependencies = [ "digest 0.10.6", "getrandom", "hmac 0.12.1", - "k256", + "k256 0.11.6", "lazy_static", "serde", "sha2 0.10.6", @@ -695,16 +638,10 @@ dependencies = [ "serde", "serde_derive", "sha2 0.10.6", - "sha3 0.10.6", + "sha3 0.10.7", "thiserror", ] -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "colored" version = "2.0.0" @@ -716,17 +653,11 @@ dependencies = [ "winapi", ] -[[package]] -name = "const-cstr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" - [[package]] name = "const-oid" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "constant_time_eq" @@ -736,62 +667,15 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "convert_case" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" -dependencies = [ - "bitflags", - "core-foundation", - "foreign-types", - "libc", -] +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] -name = "core-text" -version = "19.2.0" +name = "convert_case" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" -dependencies = [ - "core-foundation", - "core-graphics", - "foreign-types", - "libc", -] +checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" [[package]] name = "cpufeatures" @@ -802,51 +686,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "criterion" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" -dependencies = [ - "atty", - "cast", - "clap 2.34.0", - "criterion-plot", - "csv", - "itertools", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_cbor", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" -dependencies = [ - "cast", - "itertools", -] - [[package]] name = "crossbeam-channel" version = "0.5.6" @@ -908,6 +747,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "crypto-bigint" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" +dependencies = [ + "generic-array 0.14.6", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -969,82 +820,14 @@ dependencies = [ "cipher", ] -[[package]] -name = "cxx" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "darling" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" -dependencies = [ - "darling_core 0.10.2", - "darling_macro 0.10.2", -] - [[package]] name = "darling" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - -[[package]] -name = "darling_core" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.9.3", - "syn", + "darling_core", + "darling_macro", ] [[package]] @@ -1057,18 +840,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" -dependencies = [ - "darling_core 0.10.2", - "quote", + "strsim", "syn", ] @@ -1078,7 +850,7 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core 0.13.4", + "darling_core", "quote", "syn", ] @@ -1094,27 +866,25 @@ dependencies = [ ] [[package]] -name = "derive_builder" -version = "0.9.0" +name = "der" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0" +checksum = "05e58dffcdcc8ee7b22f0c1f71a69243d7c2d9ad87b5a14361f2424a1565c219" dependencies = [ - "darling 0.10.2", - "derive_builder_core", - "proc-macro2", - "quote", - "syn", + "const-oid", + "zeroize", ] [[package]] -name = "derive_builder_core" -version = "0.9.0" +name = "derive_more" +version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "darling 0.10.2", + "convert_case 0.4.0", "proc-macro2", "quote", + "rustc_version", "syn", ] @@ -1158,6 +928,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.3", + "const-oid", "crypto-common", "subtle", ] @@ -1183,39 +954,17 @@ dependencies = [ "winapi", ] -[[package]] -name = "dlib" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" -dependencies = [ - "libloading", -] - [[package]] name = "dunce" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" -[[package]] -name = "dwrote" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" -dependencies = [ - "lazy_static", - "libc", - "winapi", - "wio", -] - [[package]] name = "ecc" version = "0.1.0" -source = "git+https://github.com/privacy-scaling-explorations/halo2wrong?tag=v2023_02_02#5905a20b62fcd9f6c269416a39c80de7ced8fb02" +source = "git+https://github.com/privacy-scaling-explorations/halo2wrong?tag=v2023_04_20#f72db265aa3cebe297c9b9816e940d0e1d400886" dependencies = [ - "group", "integer", "num-bigint", "num-integer", @@ -1227,10 +976,9 @@ dependencies = [ [[package]] name = "ecdsa" version = "0.1.0" -source = "git+https://github.com/privacy-scaling-explorations/halo2wrong?tag=v2023_02_02#5905a20b62fcd9f6c269416a39c80de7ced8fb02" +source = "git+https://github.com/privacy-scaling-explorations/halo2wrong?tag=v2023_04_20#f72db265aa3cebe297c9b9816e940d0e1d400886" dependencies = [ "ecc", - "group", "num-bigint", "num-integer", "num-traits", @@ -1244,10 +992,23 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" +dependencies = [ + "der 0.7.5", + "digest 0.10.6", + "elliptic-curve 0.13.4", + "rfc6979 0.4.0", + "signature 2.1.0", ] [[package]] @@ -1262,16 +1023,34 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", - "der", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", "digest 0.10.6", - "ff", + "ff 0.12.1", "generic-array 0.14.6", - "group", + "group 0.12.1", "pkcs8", "rand_core", - "sec1", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.1", + "digest 0.10.6", + "ff 0.13.0", + "generic-array 0.14.6", + "group 0.13.0", + "rand_core", + "sec1 0.7.1", "subtle", "zeroize", ] @@ -1300,6 +1079,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enumn" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1940ea32e14d489b401074558be4567f35ca9507c4628b4b3fd6fe6eb2ca7b88" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "env_logger" version = "0.9.3" @@ -1351,7 +1141,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.6", - "sha3 0.10.6", + "sha3 0.10.7", "thiserror", "uuid", ] @@ -1362,7 +1152,7 @@ version = "0.1.0" dependencies = [ "ethers-core", "ethers-signers", - "halo2_proofs 0.2.0", + "halo2_proofs", "hex", "itertools", "lazy_static", @@ -1373,7 +1163,6 @@ dependencies = [ "serde", "serde_json", "serde_with", - "sha3 0.10.6", "strum", "strum_macros", "subtle", @@ -1392,7 +1181,7 @@ dependencies = [ "regex", "serde", "serde_json", - "sha3 0.10.6", + "sha3 0.10.7", "thiserror", "uint", ] @@ -1404,7 +1193,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11da94e443c60508eb62cf256243a64da87304c2802ac2528847f79d750007ef" dependencies = [ "crunchy", - "fixed-hash", + "fixed-hash 0.7.0", "impl-rlp", "impl-serde", "tiny-keccak", @@ -1417,10 +1206,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2827b94c556145446fcce834ca86b7abf0c39a805883fe20e72c5bfdb5a0dc6" dependencies = [ "ethbloom", - "fixed-hash", + "fixed-hash 0.7.0", "impl-rlp", "impl-serde", - "primitive-types", + "primitive-types 0.11.1", "uint", ] @@ -1478,13 +1267,13 @@ dependencies = [ "arrayvec", "bytes", "chrono", - "convert_case", - "elliptic-curve", + "convert_case 0.5.0", + "elliptic-curve 0.12.3", "ethabi", "fastrlp", "generic-array 0.14.6", "hex", - "k256", + "k256 0.11.6", "proc-macro2", "rand", "rlp", @@ -1508,7 +1297,7 @@ dependencies = [ "ethers-core", "getrandom", "reqwest", - "semver 1.0.16", + "semver", "serde", "serde-aux", "serde_json", @@ -1587,7 +1376,7 @@ dependencies = [ "async-trait", "coins-bip32", "coins-bip39", - "elliptic-curve", + "elliptic-curve 0.12.3", "eth-keystore", "ethers-core", "hex", @@ -1616,7 +1405,7 @@ dependencies = [ "path-slash", "rayon", "regex", - "semver 1.0.16", + "semver", "serde", "serde_json", "solang-parser", @@ -1673,6 +1462,16 @@ name = "ff" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ "bitvec 1.0.1", "rand_core", @@ -1692,26 +1491,22 @@ dependencies = [ ] [[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.25" +name = "fixed-hash" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ - "crc32fast", - "miniz_oxide", + "byteorder", + "rand", + "rustc-hex", + "static_assertions", ] [[package]] -name = "float-ord" -version = "0.2.0" +name = "fixedbitset" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "fnv" @@ -1719,46 +1514,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "font-kit" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21fe28504d371085fae9ac7a3450f0b289ab71e07c8e57baa3fb68b9e57d6ce5" -dependencies = [ - "bitflags", - "byteorder", - "core-foundation", - "core-graphics", - "core-text", - "dirs-next", - "dwrote", - "float-ord", - "freetype", - "lazy_static", - "libc", - "log", - "pathfinder_geometry", - "pathfinder_simd", - "walkdir", - "winapi", - "yeslogic-fontconfig-sys", -] - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.1.0" @@ -1768,27 +1523,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "freetype" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" -dependencies = [ - "freetype-sys", - "libc", -] - -[[package]] -name = "freetype-sys" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" -dependencies = [ - "cmake", - "libc", - "pkg-config", -] - [[package]] name = "funty" version = "2.0.0" @@ -1916,7 +1650,7 @@ version = "0.1.0" dependencies = [ "digest 0.7.6", "eth-types", - "halo2_proofs 0.2.0", + "halo2_proofs", "rand", "rand_xorshift", "sha3 0.7.3", @@ -1949,6 +1683,7 @@ checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1969,20 +1704,10 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] -[[package]] -name = "gif" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "glob" version = "0.3.1" @@ -2004,7 +1729,18 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", + "rand_core", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", "rand_core", "subtle", ] @@ -2028,37 +1764,16 @@ dependencies = [ "tracing", ] -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - -[[package]] -name = "halo2_proofs" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e925780549adee8364c7f2b685c753f6f3df23bde520c67416e93bf615933760" -dependencies = [ - "blake2b_simd", - "ff", - "group", - "pasta_curves", - "plotters", - "rand_core", - "rayon", - "tabbycat", -] - [[package]] name = "halo2_proofs" version = "0.2.0" -source = "git+https://github.com/privacy-scaling-explorations/halo2.git?tag=v2023_02_02#0a8646b78286a13d320759b1c585262d6536dce4" +source = "git+https://github.com/privacy-scaling-explorations/halo2.git?tag=v2023_04_20#be955686f86eb618f55d2320c0e042485b313d22" dependencies = [ "blake2b_simd", - "ff", - "group", - "halo2curves 0.3.1", + "ff 0.13.0", + "group 0.13.0", + "halo2curves", + "rand_chacha", "rand_core", "rayon", "sha3 0.9.1", @@ -2067,32 +1782,16 @@ dependencies = [ [[package]] name = "halo2curves" -version = "0.2.1" -source = "git+https://github.com/privacy-scaling-explorations/halo2curves?tag=0.3.0#83c72d49762343ffc9576ca11a2aa615efe1029b" -dependencies = [ - "ff", - "group", - "lazy_static", - "num-bigint", - "num-traits", - "pasta_curves", - "rand", - "rand_core", - "static_assertions", - "subtle", -] - -[[package]] -name = "halo2curves" -version = "0.3.1" -source = "git+https://github.com/privacy-scaling-explorations/halo2curves.git?tag=0.3.1#9b67e19bca30a35208b0c1b41c1723771e2c9f49" +version = "0.3.2" +source = "git+https://github.com/privacy-scaling-explorations/halo2curves?tag=0.3.2#9f5c50810bbefe779ee5cf1d852b2fe85dc35d5e" dependencies = [ - "ff", - "group", + "ff 0.13.0", + "group 0.13.0", "lazy_static", "num-bigint", "num-traits", "pasta_curves", + "paste", "rand", "rand_core", "static_assertions", @@ -2102,10 +1801,9 @@ dependencies = [ [[package]] name = "halo2wrong" version = "0.1.0" -source = "git+https://github.com/privacy-scaling-explorations/halo2wrong?tag=v2023_02_02#5905a20b62fcd9f6c269416a39c80de7ced8fb02" +source = "git+https://github.com/privacy-scaling-explorations/halo2wrong?tag=v2023_04_20#f72db265aa3cebe297c9b9816e940d0e1d400886" dependencies = [ - "group", - "halo2_proofs 0.2.0", + "halo2_proofs", "num-bigint", "num-integer", "num-traits", @@ -2188,6 +1886,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hmac" version = "0.8.1" @@ -2304,30 +2008,6 @@ dependencies = [ "tokio-rustls", ] -[[package]] -name = "iana-time-zone" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" -dependencies = [ - "cxx", - "cxx-build", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -2344,21 +2024,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "image" -version = "0.24.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "jpeg-decoder", - "num-rational", - "num-traits", - "png", -] - [[package]] name = "impl-codec" version = "0.6.0" @@ -2422,9 +2087,8 @@ dependencies = [ [[package]] name = "integer" version = "0.1.0" -source = "git+https://github.com/privacy-scaling-explorations/halo2wrong?tag=v2023_02_02#5905a20b62fcd9f6c269416a39c80de7ced8fb02" +source = "git+https://github.com/privacy-scaling-explorations/halo2wrong?tag=v2023_04_20#f72db265aa3cebe297c9b9816e940d0e1d400886" dependencies = [ - "group", "maingate", "num-bigint", "num-integer", @@ -2441,7 +2105,7 @@ dependencies = [ "env_logger", "eth-types", "ethers", - "halo2_proofs 0.2.0", + "halo2_proofs", "lazy_static", "log", "mock", @@ -2506,12 +2170,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" -[[package]] -name = "jpeg-decoder" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" - [[package]] name = "js-sys" version = "0.3.61" @@ -2529,48 +2187,43 @@ checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if", "ecdsa 0.14.8", - "elliptic-curve", + "elliptic-curve 0.12.3", "sha2 0.10.6", - "sha3 0.10.6", + "sha3 0.10.7", ] [[package]] -name = "keccak" -version = "0.1.3" +name = "k256" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ - "cpufeatures", + "cfg-if", + "ecdsa 0.16.6", + "elliptic-curve 0.13.4", + "sha2 0.10.6", ] [[package]] -name = "keccak256" -version = "0.1.0" -dependencies = [ - "env_logger", - "eth-types", - "halo2_proofs 0.1.0", - "itertools", - "lazy_static", - "log", - "num-bigint", - "num-traits", - "plotters", - "pretty_assertions", - "rand", +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", ] [[package]] name = "lalrpop" -version = "0.19.8" +version = "0.19.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30455341b0e18f276fa64540aff54deafb54c589de6aca68659c63dd2d5d823" +checksum = "f34313ec00c2eb5c3c87ca6732ea02dcf3af99c3ff7a8fb622ffb99c9d860a87" dependencies = [ "ascii-canvas", - "atty", "bit-set", "diff", "ena", + "is-terminal", "itertools", "lalrpop-util", "petgraph", @@ -2585,9 +2238,9 @@ dependencies = [ [[package]] name = "lalrpop-util" -version = "0.19.8" +version = "0.19.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf796c978e9b4d983414f4caedc9273aa33ee214c5b887bd55fde84c85d2dc4" +checksum = "3d3b45d694c8074f77bc24fc26e47633c862a9cd3b48dd51209c02ba4c434d68" dependencies = [ "regex", ] @@ -2597,6 +2250,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -2604,16 +2260,6 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - [[package]] name = "libsecp256k1" version = "0.7.1" @@ -2662,15 +2308,6 @@ dependencies = [ "libsecp256k1-core", ] -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - [[package]] name = "linked-hash-map" version = "0.5.6" @@ -2705,9 +2342,8 @@ dependencies = [ [[package]] name = "maingate" version = "0.1.0" -source = "git+https://github.com/privacy-scaling-explorations/halo2wrong?tag=v2023_02_02#5905a20b62fcd9f6c269416a39c80de7ced8fb02" +source = "git+https://github.com/privacy-scaling-explorations/halo2wrong?tag=v2023_04_20#f72db265aa3cebe297c9b9816e940d0e1d400886" dependencies = [ - "group", "halo2wrong", "num-bigint", "num-integer", @@ -2746,15 +2382,6 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - [[package]] name = "mio" version = "0.8.5" @@ -2763,7 +2390,7 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.42.0", ] @@ -2910,12 +2537,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - [[package]] name = "opaque-debug" version = "0.2.3" @@ -3041,13 +2662,13 @@ dependencies = [ [[package]] name = "pasta_curves" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc65faf8e7313b4b1fbaa9f7ca917a0eed499a9663be71477f87993604341d8" +checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" dependencies = [ "blake2b_simd", - "ff", - "group", + "ff 0.13.0", + "group 0.13.0", "lazy_static", "rand", "static_assertions", @@ -3066,25 +2687,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" -[[package]] -name = "pathfinder_geometry" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" -dependencies = [ - "log", - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_simd" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" -dependencies = [ - "rustc_version 0.3.3", -] - [[package]] name = "pbkdf2" version = "0.10.1" @@ -3173,7 +2775,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" dependencies = [ "futures", - "rustc_version 0.4.0", + "rustc_version", ] [[package]] @@ -3264,81 +2866,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", + "der 0.6.1", "spki", ] -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - -[[package]] -name = "plotters" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" -dependencies = [ - "chrono", - "font-kit", - "image", - "lazy_static", - "num-traits", - "pathfinder_geometry", - "plotters-backend", - "plotters-bitmap", - "plotters-svg", - "ttf-parser", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" - -[[package]] -name = "plotters-bitmap" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4a1f21490a6cf4a84c272ad20bd7844ed99a3178187a4c5ab7f2051295beef" -dependencies = [ - "gif", - "image", - "plotters-backend", -] - -[[package]] -name = "plotters-svg" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "png" -version = "0.17.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" -dependencies = [ - "bitflags", - "crc32fast", - "flate2", - "miniz_oxide", -] - [[package]] name = "poseidon" version = "0.2.0" -source = "git+https://github.com/privacy-scaling-explorations/poseidon.git?tag=v2022_10_22#5d29df01a95e3df6334080d28e983407f56b5da3" +source = "git+https://github.com/privacy-scaling-explorations/poseidon.git?tag=v2023_04_20#807f8f555313f726ca03bdf941f798098f488ba4" dependencies = [ - "group", - "halo2curves 0.2.1", + "halo2curves", "subtle", ] @@ -3386,13 +2923,24 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" dependencies = [ - "fixed-hash", + "fixed-hash 0.7.0", "impl-codec", "impl-rlp", "impl-serde", "uint", ] +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash 0.8.0", + "impl-codec", + "uint", +] + [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -3644,17 +3192,65 @@ dependencies = [ "winreg", ] +[[package]] +name = "revm-precompile" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a3eabf08ea9e4063f5531b8735e29344d9d6eaebaa314c58253f6c17fcdf2d" +dependencies = [ + "k256 0.13.1", + "num", + "once_cell", + "revm-primitives", + "ripemd", + "secp256k1", + "sha2 0.10.6", + "sha3 0.10.7", + "substrate-bn", +] + +[[package]] +name = "revm-primitives" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180427e1169b860ab63eaa5bcff158010073646abf3312aed11a1d7aa1aa8291" +dependencies = [ + "auto_impl", + "bitvec 1.0.1", + "bytes", + "derive_more", + "enumn", + "fixed-hash 0.8.0", + "hashbrown 0.13.2", + "hex", + "hex-literal", + "primitive-types 0.12.1", + "rlp", + "ruint", + "sha3 0.10.7", +] + [[package]] name = "rfc6979" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.4.9", "hmac 0.12.1", "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + [[package]] name = "ring" version = "0.16.20" @@ -3725,6 +3321,26 @@ dependencies = [ "syn", ] +[[package]] +name = "ruint" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d470e29e933dac4101180fd6574971892315c414cf2961a192729089687cc9b" +dependencies = [ + "derive_more", + "primitive-types 0.12.1", + "rlp", + "ruint-macro", + "rustc_version", + "thiserror", +] + +[[package]] +name = "ruint-macro" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cc5760263ea229d367e7dff3c0cbf09e4797a125bd87059a6c095804f3b2d1" + [[package]] name = "rust_decimal" version = "1.28.1" @@ -3749,22 +3365,13 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.16", + "semver", ] [[package]] @@ -3838,12 +3445,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "scratch" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" - [[package]] name = "scrypt" version = "0.8.1" @@ -3879,8 +3480,8 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", - "der", + "base16ct 0.1.1", + "der 0.6.1", "generic-array 0.14.6", "pkcs8", "subtle", @@ -3888,30 +3489,43 @@ dependencies = [ ] [[package]] -name = "semver" -version = "0.11.0" +name = "sec1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" dependencies = [ - "semver-parser", + "base16ct 0.2.0", + "der 0.7.5", + "generic-array 0.14.6", + "subtle", + "zeroize", ] [[package]] -name = "semver" -version = "1.0.16" +name = "secp256k1" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" dependencies = [ - "serde", + "secp256k1-sys", ] [[package]] -name = "semver-parser" -version = "0.10.2" +name = "secp256k1-sys" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" dependencies = [ - "pest", + "cc", +] + +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +dependencies = [ + "serde", ] [[package]] @@ -3939,16 +3553,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - [[package]] name = "serde_derive" version = "1.0.152" @@ -3999,7 +3603,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ - "darling 0.13.4", + "darling", "proc-macro2", "quote", "syn", @@ -4078,9 +3682,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" dependencies = [ "digest 0.10.6", "keccak", @@ -4096,6 +3700,16 @@ dependencies = [ "rand_core", ] +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.6", + "rand_core", +] + [[package]] name = "siphasher" version = "0.3.10" @@ -4120,11 +3734,11 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "snark-verifier" version = "0.1.0" -source = "git+https://github.com/privacy-scaling-explorations/snark-verifier?tag=v2023_02_02#df03d898b841f71cbc36c2fb9fa07b8196f9623e" +source = "git+https://github.com/privacy-scaling-explorations/snark-verifier?tag=v2023_04_20#e5d5e4a6ccff2bba71baf77ab7a12b124d6364a1" dependencies = [ "ecc", - "halo2_proofs 0.2.0", - "halo2curves 0.3.1", + "halo2_proofs", + "halo2curves", "hex", "itertools", "lazy_static", @@ -4171,7 +3785,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", ] [[package]] @@ -4193,12 +3807,6 @@ dependencies = [ "precomputed-hash", ] -[[package]] -name = "strsim" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" - [[package]] name = "strsim" version = "0.10.0" @@ -4227,6 +3835,19 @@ dependencies = [ "syn", ] +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand", + "rustc-hex", +] + [[package]] name = "subtle" version = "2.4.1" @@ -4244,17 +3865,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tabbycat" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c45590f0f859197b4545be1b17b2bc3cc7bb075f7d1cc0ea1dc6521c0bf256a3" -dependencies = [ - "anyhow", - "derive_builder", - "regex", -] - [[package]] name = "tap" version = "1.0.1" @@ -4287,17 +3897,16 @@ version = "0.1.0" dependencies = [ "anyhow", "bus-mapping", - "clap 3.2.23", + "clap", "env_logger", "eth-types", "ethers-core", "ethers-signers", "external-tracer", "glob", - "halo2_proofs 0.2.0", + "halo2_proofs", "handlebars", "hex", - "keccak256", "log", "mock", "once_cell", @@ -4317,15 +3926,6 @@ dependencies = [ "zkevm-circuits", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "textwrap" version = "0.16.0" @@ -4352,17 +3952,6 @@ dependencies = [ "syn", ] -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - [[package]] name = "tiny-keccak" version = "2.0.2" @@ -4372,16 +3961,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -4547,12 +4126,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" -[[package]] -name = "ttf-parser" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" - [[package]] name = "tungstenite" version = "0.17.3" @@ -4697,12 +4270,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -4819,12 +4386,6 @@ dependencies = [ "webpki", ] -[[package]] -name = "weezl" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" - [[package]] name = "winapi" version = "0.3.9" @@ -4946,15 +4507,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi", -] - [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -4966,7 +4518,7 @@ dependencies = [ "js-sys", "log", "pharos", - "rustc_version 0.4.0", + "rustc_version", "send_wrapper", "thiserror", "wasm-bindgen", @@ -4998,18 +4550,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" -[[package]] -name = "yeslogic-fontconfig-sys" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2bbd69036d397ebbff671b1b8e4d918610c181c5a16073b96f984a38d08c386" -dependencies = [ - "const-cstr", - "dlib", - "once_cell", - "pkg-config", -] - [[package]] name = "zeroize" version = "1.5.7" @@ -5023,7 +4563,6 @@ dependencies = [ "array-init", "bus-mapping", "cli-table", - "criterion", "ctor", "ecc", "ecdsa 0.1.0", @@ -5032,11 +4571,10 @@ dependencies = [ "ethers-core", "ethers-signers", "gadgets", - "halo2_proofs 0.2.0", + "halo2_proofs", "hex", "integer", "itertools", - "keccak256", "lazy_static", "libsecp256k1", "log", @@ -5051,9 +4589,8 @@ dependencies = [ "rand_xorshift", "serde", "serde_json", - "sha3 0.10.6", + "sha3 0.10.7", "snark-verifier", "strum", "strum_macros", - "subtle", ] diff --git a/Cargo.toml b/Cargo.toml index 100836d5ce..c8155a1bfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ members = [ "zkevm-circuits", "bus-mapping", "geth-utils", - "keccak256", "gadgets", "integration-tests", "circuit-benchmarks", @@ -14,7 +13,7 @@ members = [ ] [patch.crates-io] -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_02_02" } +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_04_20" } # Definition of benchmarks profile to use. [profile.bench] diff --git a/Makefile b/Makefile index f8dabed664..6dcf6e8420 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,10 @@ +CARGO = cargo + +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) + CARGO += --config 'build.rustdocflags = ["-C", "link-args=-framework CoreFoundation -framework Security"]' +endif + help: ## Display this help screen @grep -h \ -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ @@ -20,8 +27,9 @@ test: ## Run tests for all the workspace members # Run heavy tests serially to avoid OOM @cargo test --release --all --all-features --exclude integration-tests --exclude circuit-benchmarks serial_ -- --ignored --test-threads 1 + test_doc: ## Test the docs - @cargo test --release --all --all-features --doc + @$(CARGO) test --release --all --all-features --doc test_benches: ## Compiles the benchmarks @cargo test --verbose --release --all-features -p circuit-benchmarks --no-run @@ -58,15 +66,15 @@ exp_bench: ## Run Exp Circuit benchmarks circuit_benches: evm_bench state_bench ## Run All Circuit benchmarks stats_state_circuit: # Print a table with State Circuit stats by ExecState/opcode - @cargo test -p zkevm-circuits --features=test,warn-unimplemented get_state_states_stats -- --nocapture --ignored + @cargo run --bin stats --features stats -- state stats_evm_circuit: # Print a table with EVM Circuit stats by ExecState/opcode - @cargo test -p zkevm-circuits --features=test,warn-unimplemented get_evm_states_stats -- --nocapture --ignored + @cargo run --bin stats --features stats -- evm stats_copy_circuit: # Print a table with Copy Circuit stats by ExecState/opcode - @cargo test -p zkevm-circuits --features=test,warn-unimplemented get_copy_states_stats -- --nocapture --ignored + @cargo run --bin stats --features stats -- copy evm_exec_steps_occupancy: # Print a table for each EVM-CellManager CellType with the top 10 occupancy ExecutionSteps associated - @cargo test -p zkevm-circuits --release get_exec_steps_occupancy --features=test,warn-unimplemented -- --nocapture --ignored + @cargo run --bin stats --features stats -- exec .PHONY: clippy doc fmt test test_benches test-all evm_bench state_bench circuit_benches evm_exec_steps_occupancy stats_state_circuit stats_evm_circuit stats_copy_circuit help diff --git a/bus-mapping/Cargo.toml b/bus-mapping/Cargo.toml index 65b0b411da..3632275fa6 100644 --- a/bus-mapping/Cargo.toml +++ b/bus-mapping/Cargo.toml @@ -8,12 +8,11 @@ license = "MIT OR Apache-2.0" [dependencies] eth-types = { path = "../eth-types" } gadgets = { path = "../gadgets" } -keccak256 = { path = "../keccak256" } mock = { path = "../mock", optional = true } ethers-core = "0.17.0" ethers-providers = "0.17.0" -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_02_02" } +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_04_20" } itertools = "0.10" lazy_static = "1.4" log = "0.4.14" @@ -23,6 +22,8 @@ serde_json = "1.0.66" strum = "0.24" strum_macros = "0.24" +revm-precompile = "2.0.2" + [dev-dependencies] hex = "0.4.3" pretty_assertions = "1.0.0" diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index 91696dead0..11e676d561 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -13,7 +13,7 @@ mod transaction; use self::access::gen_state_access_trace; use crate::{ error::Error, - evm::opcodes::{gen_associated_ops, gen_begin_tx_ops, gen_end_tx_ops}, + evm::opcodes::{gen_associated_ops, gen_associated_steps}, operation::{CallContextField, Operation, RWCounter, StartOp, RW}, rpc::GethClient, state_db::{self, CodeDB, StateDB}, @@ -215,7 +215,7 @@ impl<'a> CircuitInputBuilder { end_block_not_last.rwc = self.block_ctx.rwc; end_block_last.rwc = self.block_ctx.rwc; - let mut dummy_tx = Transaction::dummy(); + let mut dummy_tx = Transaction::default(); let mut dummy_tx_ctx = TransactionContext::default(); let mut state = self.state_ref(&mut dummy_tx, &mut dummy_tx_ctx); @@ -270,11 +270,11 @@ impl<'a> CircuitInputBuilder { let mut tx = self.new_tx(eth_tx, !geth_trace.failed)?; let mut tx_ctx = TransactionContext::new(eth_tx, geth_trace, is_last_tx)?; - // TODO: Move into gen_associated_steps with - // - execution_state: BeginTx - // - op: None // Generate BeginTx step - let begin_tx_step = gen_begin_tx_ops(&mut self.state_ref(&mut tx, &mut tx_ctx))?; + let begin_tx_step = gen_associated_steps( + &mut self.state_ref(&mut tx, &mut tx_ctx), + ExecState::BeginTx, + )?; tx.steps_mut().push(begin_tx_step); for (index, geth_step) in geth_trace.struct_logs.iter().enumerate() { @@ -288,11 +288,9 @@ impl<'a> CircuitInputBuilder { tx.steps_mut().extend(exec_steps); } - // TODO: Move into gen_associated_steps with - // - execution_state: EndTx - // - op: None // Generate EndTx step - let end_tx_step = gen_end_tx_ops(&mut self.state_ref(&mut tx, &mut tx_ctx))?; + let end_tx_step = + gen_associated_steps(&mut self.state_ref(&mut tx, &mut tx_ctx), ExecState::EndTx)?; tx.steps_mut().push(end_tx_step); self.sdb.commit_tx(); @@ -307,7 +305,7 @@ impl<'a> CircuitInputBuilder { pub fn keccak_inputs(block: &Block, code_db: &CodeDB) -> Result>, Error> { let mut keccak_inputs = Vec::new(); // Tx Circuit - let txs: Vec = block.txs.iter().map(|tx| tx.into()).collect(); + let txs: Vec = block.txs.iter().map(|tx| tx.tx.clone()).collect(); keccak_inputs.extend_from_slice(&keccak_inputs_tx_circuit(&txs, block.chain_id.as_u64())?); // Bytecode Circuit for bytecode in code_db.0.values() { @@ -364,14 +362,21 @@ pub fn keccak_inputs_tx_circuit( } /// Retrieve the init_code from memory for {CREATE, CREATE2} -pub fn get_create_init_code<'a, 'b>( +pub fn get_create_init_code<'a>( call_ctx: &'a CallContext, - step: &'b GethExecStep, + step: &GethExecStep, ) -> Result<&'a [u8], Error> { - let offset = step.stack.nth_last(1)?; - let length = step.stack.nth_last(2)?; - Ok(&call_ctx.memory.0 - [offset.low_u64() as usize..(offset.low_u64() + length.low_u64()) as usize]) + let offset = step.stack.nth_last(1)?.low_u64() as usize; + let length = step.stack.nth_last(2)?.as_usize(); + + let mem_len = call_ctx.memory.0.len(); + if offset >= mem_len { + return Ok(&[]); + } + + let offset_end = offset.checked_add(length).unwrap_or(mem_len); + + Ok(&call_ctx.memory.0[offset..offset_end]) } /// Retrieve the memory offset and length of call. @@ -433,7 +438,7 @@ pub fn build_state_code_db( sdb.set_account( &proof.address, state_db::Account { - nonce: proof.nonce, + nonce: proof.nonce.as_u64(), balance: proof.balance, storage, code_hash: proof.code_hash, diff --git a/bus-mapping/src/circuit_input_builder/access.rs b/bus-mapping/src/circuit_input_builder/access.rs index 272eac3acf..1626d6f97a 100644 --- a/bus-mapping/src/circuit_input_builder/access.rs +++ b/bus-mapping/src/circuit_input_builder/access.rs @@ -3,6 +3,9 @@ use eth_types::{evm_types::OpcodeId, Address, GethExecStep, GethExecTrace, ToAdd use ethers_core::utils::get_contract_address; use std::collections::{hash_map::Entry, HashMap, HashSet}; +use AccessValue::{Account, Code, Storage}; +use RW::{READ, WRITE}; + /// State and Code Access with "keys/index" used in the access operation. #[derive(Debug, PartialEq, Eq)] pub enum AccessValue { @@ -92,7 +95,7 @@ impl From> for AccessSet { } /// Source of the code in the EVM execution. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CodeSource { /// Code comes from a deployed contract at `Address`. Address(Address), @@ -116,9 +119,6 @@ pub fn gen_state_access_trace( tx: ð_types::Transaction, geth_trace: &GethExecTrace, ) -> Result, Error> { - use AccessValue::{Account, Code, Storage}; - use RW::{READ, WRITE}; - let mut call_stack: Vec<(Address, CodeSource)> = Vec::new(); let mut accs = vec![Access::new(None, WRITE, Account { address: tx.from })]; if let Some(to) = tx.to { @@ -145,122 +145,129 @@ pub fn gen_state_access_trace( pop_call_stack = step.depth - 1 == next_step.depth; } - match step.op { - OpcodeId::SSTORE => { - let address = contract_address; - let key = step.stack.nth_last(0)?; - accs.push(Access::new(i, WRITE, Storage { address, key })); - } - OpcodeId::SLOAD => { - let address = contract_address; - let key = step.stack.nth_last(0)?; - accs.push(Access::new(i, READ, Storage { address, key })); - } - OpcodeId::SELFBALANCE => { - let address = contract_address; - accs.push(Access::new(i, READ, Account { address })); - } - OpcodeId::CODESIZE => { - if let CodeSource::Address(address) = code_source { + let result: Result<(), Error> = (|| { + match step.op { + OpcodeId::SSTORE => { + let address = contract_address; + let key = step.stack.nth_last(0)?; + accs.push(Access::new(i, WRITE, Storage { address, key })); + } + OpcodeId::SLOAD => { + let address = contract_address; + let key = step.stack.nth_last(0)?; + accs.push(Access::new(i, READ, Storage { address, key })); + } + OpcodeId::SELFBALANCE => { + let address = contract_address; + accs.push(Access::new(i, READ, Account { address })); + } + OpcodeId::CODESIZE => { + if let CodeSource::Address(address) = code_source { + accs.push(Access::new(i, READ, Code { address })); + } + } + OpcodeId::CODECOPY => { + if let CodeSource::Address(address) = code_source { + accs.push(Access::new(i, READ, Code { address })); + } + } + OpcodeId::BALANCE => { + let address = step.stack.nth_last(0)?.to_address(); + accs.push(Access::new(i, READ, Account { address })); + } + OpcodeId::EXTCODEHASH => { + let address = step.stack.nth_last(0)?.to_address(); + accs.push(Access::new(i, READ, Account { address })); + } + OpcodeId::EXTCODESIZE => { + let address = step.stack.nth_last(0)?.to_address(); accs.push(Access::new(i, READ, Code { address })); } - } - OpcodeId::CODECOPY => { - if let CodeSource::Address(address) = code_source { + OpcodeId::EXTCODECOPY => { + let address = step.stack.nth_last(0)?.to_address(); accs.push(Access::new(i, READ, Code { address })); } - } - OpcodeId::BALANCE => { - let address = step.stack.nth_last(0)?.to_address(); - accs.push(Access::new(i, READ, Account { address })); - } - OpcodeId::EXTCODEHASH => { - let address = step.stack.nth_last(0)?.to_address(); - accs.push(Access::new(i, READ, Account { address })); - } - OpcodeId::EXTCODESIZE => { - let address = step.stack.nth_last(0)?.to_address(); - accs.push(Access::new(i, READ, Code { address })); - } - OpcodeId::EXTCODECOPY => { - let address = step.stack.nth_last(0)?.to_address(); - accs.push(Access::new(i, READ, Code { address })); - } - OpcodeId::SELFDESTRUCT => { - let address = contract_address; - accs.push(Access::new(i, WRITE, Account { address })); - let address = step.stack.nth_last(0)?.to_address(); - accs.push(Access::new(i, WRITE, Account { address })); - } - OpcodeId::CREATE => { - if push_call_stack { - // Find CREATE result - let address = get_call_result(&geth_trace.struct_logs[index..]) - .unwrap_or_else(Word::zero) - .to_address(); - if !address.is_zero() { - accs.push(Access::new(i, WRITE, Account { address })); - accs.push(Access::new(i, WRITE, Code { address })); + OpcodeId::SELFDESTRUCT => { + let address = contract_address; + accs.push(Access::new(i, WRITE, Account { address })); + let address = step.stack.nth_last(0)?.to_address(); + accs.push(Access::new(i, WRITE, Account { address })); + } + OpcodeId::CREATE => { + if push_call_stack { + // Find CREATE result + let address = get_call_result(&geth_trace.struct_logs[index..]) + .unwrap_or_else(Word::zero) + .to_address(); + if !address.is_zero() { + accs.push(Access::new(i, WRITE, Account { address })); + accs.push(Access::new(i, WRITE, Code { address })); + } + call_stack.push((address, CodeSource::Address(address))); } - call_stack.push((address, CodeSource::Address(address))); } - } - OpcodeId::CREATE2 => { - if push_call_stack { - // Find CREATE2 result - let address = get_call_result(&geth_trace.struct_logs[index..]) - .unwrap_or_else(Word::zero) - .to_address(); - if !address.is_zero() { - accs.push(Access::new(i, WRITE, Account { address })); - accs.push(Access::new(i, WRITE, Code { address })); + OpcodeId::CREATE2 => { + if push_call_stack { + // Find CREATE2 result + let address = get_call_result(&geth_trace.struct_logs[index..]) + .unwrap_or_else(Word::zero) + .to_address(); + if !address.is_zero() { + accs.push(Access::new(i, WRITE, Account { address })); + accs.push(Access::new(i, WRITE, Code { address })); + } + call_stack.push((address, CodeSource::Address(address))); } - call_stack.push((address, CodeSource::Address(address))); } - } - OpcodeId::CALL => { - let address = contract_address; - accs.push(Access::new(i, WRITE, Account { address })); + OpcodeId::CALL => { + let address = contract_address; + accs.push(Access::new(i, WRITE, Account { address })); - let address = step.stack.nth_last(1)?.to_address(); - accs.push(Access::new(i, WRITE, Account { address })); - accs.push(Access::new(i, READ, Code { address })); - if push_call_stack { - call_stack.push((address, CodeSource::Address(address))); + let address = step.stack.nth_last(1)?.to_address(); + accs.push(Access::new(i, WRITE, Account { address })); + accs.push(Access::new(i, READ, Code { address })); + if push_call_stack { + call_stack.push((address, CodeSource::Address(address))); + } } - } - OpcodeId::CALLCODE => { - let address = contract_address; - accs.push(Access::new(i, WRITE, Account { address })); + OpcodeId::CALLCODE => { + let address = contract_address; + accs.push(Access::new(i, WRITE, Account { address })); - let address = step.stack.nth_last(1)?.to_address(); - accs.push(Access::new(i, WRITE, Account { address })); - accs.push(Access::new(i, READ, Code { address })); - if push_call_stack { - call_stack.push((address, CodeSource::Address(address))); + let address = step.stack.nth_last(1)?.to_address(); + accs.push(Access::new(i, WRITE, Account { address })); + accs.push(Access::new(i, READ, Code { address })); + if push_call_stack { + call_stack.push((address, CodeSource::Address(address))); + } } - } - OpcodeId::DELEGATECALL => { - let address = step.stack.nth_last(1)?.to_address(); - accs.push(Access::new(i, READ, Code { address })); - if push_call_stack { - call_stack.push((contract_address, CodeSource::Address(address))); + OpcodeId::DELEGATECALL => { + let address = step.stack.nth_last(1)?.to_address(); + accs.push(Access::new(i, READ, Code { address })); + if push_call_stack { + call_stack.push((contract_address, CodeSource::Address(address))); + } } - } - OpcodeId::STATICCALL => { - let address = step.stack.nth_last(1)?.to_address(); - accs.push(Access::new(i, READ, Code { address })); - if push_call_stack { - call_stack.push((address, CodeSource::Address(address))); + OpcodeId::STATICCALL => { + let address = step.stack.nth_last(1)?.to_address(); + accs.push(Access::new(i, READ, Code { address })); + if push_call_stack { + call_stack.push((address, CodeSource::Address(address))); + } } + _ => {} } - _ => {} + Ok(()) + })(); + if let Err(e) = result { + log::warn!("err when parsing access: {:?}, step {:?}", e, step); } + if pop_call_stack { if call_stack.len() == 1 { return Err(Error::InvalidGethExecStep( "gen_state_access_trace: call stack will be empty", - step.clone(), + Box::new(step.clone()), )); } call_stack.pop().expect("call stack is empty"); diff --git a/bus-mapping/src/circuit_input_builder/block.rs b/bus-mapping/src/circuit_input_builder/block.rs index cf3f62070d..650c7344bc 100644 --- a/bus-mapping/src/circuit_input_builder/block.rs +++ b/bus-mapping/src/circuit_input_builder/block.rs @@ -7,7 +7,7 @@ use crate::{ operation::{OperationContainer, RWCounter}, Error, }; -use eth_types::{evm_unimplemented, Address, Hash, Word}; +use eth_types::{evm_unimplemented, Address, Word}; use std::collections::HashMap; /// Context of a [`Block`] which can mutate in a [`Transaction`]. @@ -86,7 +86,6 @@ pub struct Block { pub sha3_inputs: Vec>, /// Exponentiation events in the block. pub exp_events: Vec, - code: HashMap>, /// Circuits Setup Paramteres pub circuits_params: CircuitsParams, /// Original block from geth @@ -139,7 +138,6 @@ impl Block { }, copy_events: Vec::new(), exp_events: Vec::new(), - code: HashMap::new(), sha3_inputs: Vec::new(), circuits_params, eth_block: eth_block.clone(), diff --git a/bus-mapping/src/circuit_input_builder/call.rs b/bus-mapping/src/circuit_input_builder/call.rs index 8f4098134c..39a484ea14 100644 --- a/bus-mapping/src/circuit_input_builder/call.rs +++ b/bus-mapping/src/circuit_input_builder/call.rs @@ -51,7 +51,7 @@ impl TryFrom for CallKind { } /// Circuit Input related to an Ethereum Call -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct Call { /// Unique call identifier within the Block. pub call_id: usize, @@ -108,6 +108,18 @@ impl Call { pub fn is_delegatecall(&self) -> bool { matches!(self.kind, CallKind::DelegateCall) } + + /// Get the code address if possible + pub fn code_address(&self) -> Option
{ + match self.kind { + CallKind::Call | CallKind::StaticCall => Some(self.address), + CallKind::CallCode | CallKind::DelegateCall => match self.code_source { + CodeSource::Address(address) => Some(address), + _ => None, + }, + CallKind::Create | CallKind::Create2 => None, + } + } } /// Context of a [`Call`]. diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index 2ffd2a4b52..488a6127ff 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -4,33 +4,30 @@ use crate::{ circuit_input_builder::CallContext, error::ExecError, exec_trace::OperationRef, operation::RWCounter, }; -use eth_types::{ - evm_types::{Gas, GasCost, OpcodeId, ProgramCounter}, - GethExecStep, Word, H256, -}; +use eth_types::{evm_types::OpcodeId, GethExecStep, Word, H256}; use gadgets::impl_expr; use halo2_proofs::plonk::Expression; use strum_macros::EnumIter; /// An execution step of the EVM. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct ExecStep { /// Execution state pub exec_state: ExecState, /// Program Counter - pub pc: ProgramCounter, + pub pc: u64, /// Stack size pub stack_size: usize, /// Memory size pub memory_size: usize, /// Gas left - pub gas_left: Gas, + pub gas_left: u64, /// Gas cost of the step. If the error is OutOfGas caused by a "gas uint64 /// overflow", this value will **not** be the actual Gas cost of the /// step. - pub gas_cost: GasCost, + pub gas_cost: u64, /// Accumulated gas refund - pub gas_refund: Gas, + pub gas_refund: u64, /// Call index within the Transaction. pub call_index: usize, /// The global counter when this step was executed. @@ -86,28 +83,40 @@ impl ExecStep { Some(ExecError::OutOfGas(_) | ExecError::StackOverflow | ExecError::StackUnderflow) ) } -} -impl Default for ExecStep { - fn default() -> Self { - Self { - exec_state: ExecState::Op(OpcodeId::INVALID(0)), - pc: ProgramCounter(0), - stack_size: 0, - memory_size: 0, - gas_left: Gas(0), - gas_cost: GasCost(0), - gas_refund: Gas(0), - call_index: 0, - rwc: RWCounter(0), - reversible_write_counter: 0, - reversible_write_counter_delta: 0, - log_id: 0, - bus_mapping_instance: Vec::new(), - copy_rw_counter_delta: 0, - error: None, + /// Try get opcode, if possible + pub fn opcode(&self) -> Option { + match self.exec_state { + ExecState::Op(op) => Some(op), + _ => None, } } + + /// get rw index + pub fn rw_index(&self, index: usize) -> OperationRef { + self.bus_mapping_instance[index] + } + + /// Get the size of read and writes + pub fn rw_indices_len(&self) -> usize { + self.bus_mapping_instance.len() + } + + /// Get stack pointer + pub fn stack_pointer(&self) -> u64 { + 1024 - self.stack_size as u64 + } + + /// The memory size in word **before** this step + pub fn memory_word_size(&self) -> u64 { + let n_bytes_word = 32u64; + let memory_size = self.memory_size as u64; + // EVM always pads the memory size to word size + // https://github.com/ethereum/go-ethereum/blob/a340721aa909ea4b541ffd1ea5e9c7bd441ff769/core/vm/interpreter.go#L201-L205 + // Thus, the memory size must be a multiple of 32 bytes. + assert_eq!(memory_size % n_bytes_word, 0); + memory_size / n_bytes_word + } } /// Execution state @@ -123,6 +132,12 @@ pub enum ExecState { EndBlock, } +impl Default for ExecState { + fn default() -> Self { + ExecState::Op(OpcodeId::STOP) + } +} + impl ExecState { /// Returns `true` if `ExecState` is an opcode and the opcode is a `PUSHn`. pub fn is_push(&self) -> bool { diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index 9f13cb56e7..20faf1bcdf 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -6,7 +6,10 @@ use super::{ TransactionContext, }; use crate::{ - error::{get_step_reported_error, ExecError}, + error::{ + get_step_reported_error, DepthError, ExecError, InsufficientBalanceError, + NonceUintOverflowError, + }, exec_trace::OperationRef, operation::{ AccountField, AccountOp, CallContextField, CallContextOp, MemoryOp, Op, OpEnum, Operation, @@ -18,9 +21,9 @@ use crate::{ }; use eth_types::{ evm_types::{ - gas_utils::memory_expansion_gas_cost, Gas, GasCost, MemoryAddress, OpcodeId, StackAddress, + gas_utils::memory_expansion_gas_cost, GasCost, MemoryAddress, OpcodeId, StackAddress, }, - Address, GethExecStep, ToAddress, ToBigEndian, ToWord, Word, H256, U256, + Address, Bytecode, GethExecStep, ToAddress, ToBigEndian, ToWord, Word, H256, U256, }; use ethers_core::utils::{get_contract_address, get_create2_address}; use std::cmp::max; @@ -60,7 +63,7 @@ impl<'a> CircuitInputStateRef<'a> { pub fn new_begin_tx_step(&self) -> ExecStep { ExecStep { exec_state: ExecState::BeginTx, - gas_left: Gas(self.tx.gas), + gas_left: self.tx.gas(), rwc: self.block_ctx.rwc, ..Default::default() } @@ -76,10 +79,10 @@ impl<'a> CircuitInputStateRef<'a> { ExecStep { exec_state: ExecState::EndTx, gas_left: if prev_step.error.is_none() { - Gas(prev_step.gas_left.0 - prev_step.gas_cost.0) + prev_step.gas_left - prev_step.gas_cost } else { // consume all remaining gas when non revert err happens - Gas(0) + 0 }, rwc: self.block_ctx.rwc, // For tx without code execution @@ -276,7 +279,7 @@ impl<'a> CircuitInputStateRef<'a> { // accounts, but the corresponding account in the state DB is empty // (which means code_hash=EMPTY_HASH). let account_value_prev = match op.field { - AccountField::Nonce => account.nonce, + AccountField::Nonce => account.nonce.to_word(), AccountField::Balance => account.balance, AccountField::CodeHash => { if account.is_empty() { @@ -318,7 +321,7 @@ impl<'a> CircuitInputStateRef<'a> { // Perform the write to the account in the StateDB if matches!(rw, RW::WRITE) { match op.field { - AccountField::Nonce => account.nonce = op.value, + AccountField::Nonce => account.nonce = op.value.as_u64(), AccountField::Balance => account.balance = op.value, AccountField::CodeHash => account.code_hash = H256::from(op.value.to_be_bytes()), } @@ -901,9 +904,19 @@ impl<'a> CircuitInputStateRef<'a> { } } - /// Handle a return step caused by any opcode that causes a return to the + /// Handle a restore and a return step caused by any opcode that causes a return to the /// previous call context. - pub fn handle_return(&mut self, step: &GethExecStep) -> Result<(), Error> { + pub fn handle_return( + &mut self, + exec_step: &mut ExecStep, + geth_steps: &[GethExecStep], + need_restore: bool, + ) -> Result<(), Error> { + if need_restore { + self.handle_restore_context(exec_step, geth_steps)?; + } + + let step = &geth_steps[0]; // handle return_data let (return_data_offset, return_data_length) = { if !self.call()?.is_root { @@ -986,15 +999,46 @@ impl<'a> CircuitInputStateRef<'a> { } /// Bus mapping for the RestoreContextGadget as used in RETURN. - // TODO: unify this with restore context bus mapping for STOP. - // TODO: unify this with the `handle return function above.` pub fn handle_restore_context( &mut self, - steps: &[GethExecStep], exec_step: &mut ExecStep, + steps: &[GethExecStep], ) -> Result<(), Error> { let call = self.call()?.clone(); + let geth_step = steps + .get(0) + .ok_or(Error::InternalError("invalid index 0"))?; + let is_return_revert = geth_step.op == OpcodeId::REVERT || geth_step.op == OpcodeId::RETURN; + + if !is_return_revert && !call.is_success { + // add call failure ops for exception cases + self.call_context_read( + exec_step, + call.call_id, + CallContextField::IsSuccess, + 0u64.into(), + ); + + // Even call.rw_counter_end_of_reversion is zero for now, it will set in + // set_value_ops_call_context_rwc_eor later + // if call fails, no matter root or internal, read RwCounterEndOfReversion for + // circuit constraint. + self.call_context_read( + exec_step, + call.call_id, + CallContextField::RwCounterEndOfReversion, + call.rw_counter_end_of_reversion.into(), + ); + + if call.is_root { + return Ok(()); + } + } + let caller = self.caller()?.clone(); + let geth_step_next = steps + .get(1) + .ok_or(Error::InternalError("invalid index 1"))?; self.call_context_read( exec_step, call.call_id, @@ -1002,9 +1046,6 @@ impl<'a> CircuitInputStateRef<'a> { caller.call_id.into(), ); - let geth_step = &steps[0]; - let geth_step_next = &steps[1]; - let [last_callee_return_data_offset, last_callee_return_data_length] = match geth_step.op { OpcodeId::STOP => [Word::zero(); 2], OpcodeId::REVERT | OpcodeId::RETURN => { @@ -1018,7 +1059,7 @@ impl<'a> CircuitInputStateRef<'a> { [offset, length] } } - _ => unreachable!(), + _ => [Word::zero(), Word::zero()], }; let curr_memory_word_size = (exec_step.memory_size as u64) / 32; @@ -1035,13 +1076,17 @@ impl<'a> CircuitInputStateRef<'a> { let memory_expansion_gas_cost = memory_expansion_gas_cost(curr_memory_word_size, next_memory_word_size); let code_deposit_cost = if call.is_create() && call.is_success { - GasCost::CODE_DEPOSIT_BYTE_COST.as_u64() * last_callee_return_data_length.as_u64() + GasCost::CODE_DEPOSIT_BYTE_COST * last_callee_return_data_length.as_u64() } else { 0 }; - let gas_refund = geth_step.gas.0 - memory_expansion_gas_cost - code_deposit_cost; + let gas_refund = geth_step.gas - memory_expansion_gas_cost - code_deposit_cost; - let caller_gas_left = geth_step_next.gas.0 - gas_refund; + let caller_gas_left = if is_return_revert || call.is_success { + geth_step_next.gas - gas_refund + } else { + geth_step_next.gas + }; for (field, value) in [ (CallContextField::IsRoot, (caller.is_root as u64).into()), @@ -1050,7 +1095,7 @@ impl<'a> CircuitInputStateRef<'a> { (caller.is_create() as u64).into(), ), (CallContextField::CodeHash, caller.code_hash.to_word()), - (CallContextField::ProgramCounter, geth_step_next.pc.0.into()), + (CallContextField::ProgramCounter, geth_step_next.pc.into()), ( CallContextField::StackPointer, geth_step_next.stack.stack_pointer().0.into(), @@ -1183,7 +1228,7 @@ impl<'a> CircuitInputStateRef<'a> { _ => { return Err(Error::UnexpectedExecStepError( "call failure without return", - step.clone(), + Box::new(step.clone()), )); } }); @@ -1199,18 +1244,18 @@ impl<'a> CircuitInputStateRef<'a> { && call_ctx.memory.0.get(offset.low_u64() as usize) == Some(&0xef) { return Ok(Some(ExecError::InvalidCreationCode)); - } else if Word::from(200u64) * length > Word::from(step.gas.0) { + } else if Word::from(200u64) * length > Word::from(step.gas) { return Ok(Some(ExecError::CodeStoreOutOfGas)); } else { return Err(Error::UnexpectedExecStepError( "failure in RETURN from {CREATE, CREATE2}", - step.clone(), + Box::new(step.clone()), )); } } else { return Err(Error::UnexpectedExecStepError( "failure in RETURN", - step.clone(), + Box::new(step.clone()), )); } } @@ -1230,12 +1275,12 @@ impl<'a> CircuitInputStateRef<'a> { { return Err(Error::UnexpectedExecStepError( "success result without {RETURN, STOP, SELFDESTRUCT}", - step.clone(), + Box::new(step.clone()), )); } // The *CALL*/CREATE* code was not executed - let next_pc = next_step.map(|s| s.pc.0).unwrap_or(1); + let next_pc = next_step.map(|s| s.pc).unwrap_or(1); if matches!( step.op, OpcodeId::CALL @@ -1248,7 +1293,14 @@ impl<'a> CircuitInputStateRef<'a> { && next_pc != 0 { if step.depth == 1025 { - return Ok(Some(ExecError::Depth)); + return Ok(Some(ExecError::Depth(match step.op { + OpcodeId::CALL | OpcodeId::CALLCODE => DepthError::Call, + OpcodeId::CREATE => DepthError::Create, + OpcodeId::CREATE2 => DepthError::Create2, + op => { + unreachable!("Depth error unexpected for opcode: {:?}", op) + } + }))); } let sender = self.call()?.address; @@ -1257,7 +1309,26 @@ impl<'a> CircuitInputStateRef<'a> { return Err(Error::AccountNotFound(sender)); } if account.balance < value { - return Ok(Some(ExecError::InsufficientBalance)); + return Ok(Some(ExecError::InsufficientBalance(match step.op { + OpcodeId::CALL | OpcodeId::CALLCODE => InsufficientBalanceError::Call, + OpcodeId::CREATE => InsufficientBalanceError::Create, + OpcodeId::CREATE2 => InsufficientBalanceError::Create2, + op => { + unreachable!("insufficient balance error unexpected for opcode: {:?}", op) + } + }))); + } + + // Nonce Uint overflow + // If user's nonce is equal u64::MAX, nonce will be overflow in this call + // Nonce is u64 so it's impossible to larger than u64::MAX, that's why we're using `==` + // here. + if account.nonce == u64::MAX { + return Ok(Some(ExecError::NonceUintOverflow(match step.op { + OpcodeId::CREATE => NonceUintOverflowError::Create, + OpcodeId::CREATE2 => NonceUintOverflowError::Create2, + op => unreachable!("Nonce Uint overflow error unexpected for opcode: {:?}", op), + }))); } // Address collision @@ -1272,7 +1343,7 @@ impl<'a> CircuitInputStateRef<'a> { return Err(Error::UnexpectedExecStepError( "*CALL*/CREATE* code not executed", - step.clone(), + Box::new(step.clone()), )); } @@ -1307,88 +1378,62 @@ impl<'a> CircuitInputStateRef<'a> { Ok(()) } - /// gen bus mapping operations for context restore purpose - pub(crate) fn gen_restore_context_ops( + /// Generate copy steps for bytecode. + pub(crate) fn gen_copy_steps_for_bytecode( &mut self, exec_step: &mut ExecStep, - geth_steps: &[GethExecStep], - ) -> Result<(), Error> { - let geth_step = &geth_steps[0]; - let call = self.call()?.clone(); - if !call.is_success { - // add call failure ops for exception cases - self.call_context_read( - exec_step, - call.call_id, - CallContextField::IsSuccess, - 0u64.into(), - ); - - // Even call.rw_counter_end_of_reversion is zero for now, it will set in - // set_value_ops_call_context_rwc_eor later - // if call fails, no matter root or internal, read RwCounterEndOfReversion for - // circuit constraint. - self.call_context_read( - exec_step, - call.call_id, - CallContextField::RwCounterEndOfReversion, - call.rw_counter_end_of_reversion.into(), - ); - - if call.is_root { - return Ok(()); - } + bytecode: &Bytecode, + src_addr: u64, + dst_addr: u64, + src_addr_end: u64, + bytes_left: u64, + ) -> Result, Error> { + let mut copy_steps = Vec::with_capacity(bytes_left as usize); + for idx in 0..bytes_left { + let addr = src_addr.checked_add(idx).unwrap_or(src_addr_end); + let step = if addr < src_addr_end { + let code = bytecode.code.get(addr as usize).unwrap(); + (code.value, code.is_code) + } else { + (0, false) + }; + copy_steps.push(step); + self.memory_write(exec_step, (dst_addr + idx).into(), step.0)?; } - let caller = self.caller()?.clone(); - self.call_context_read( - exec_step, - call.call_id, - CallContextField::CallerId, - caller.call_id.into(), - ); - - let geth_step_next = &geth_steps[1]; - let caller_ctx = self.caller_ctx()?; - let caller_gas_left = if call.is_success { - geth_step_next.gas.0 - geth_step.gas.0 - } else { - geth_step_next.gas.0 - }; - - for (field, value) in [ - (CallContextField::IsRoot, (caller.is_root as u64).into()), - ( - CallContextField::IsCreate, - (caller.is_create() as u64).into(), - ), - (CallContextField::CodeHash, caller.code_hash.to_word()), - (CallContextField::ProgramCounter, geth_step_next.pc.0.into()), - ( - CallContextField::StackPointer, - geth_step_next.stack.stack_pointer().0.into(), - ), - (CallContextField::GasLeft, caller_gas_left.into()), - ( - CallContextField::MemorySize, - caller_ctx.memory.word_size().into(), - ), - ( - CallContextField::ReversibleWriteCounter, - self.caller_ctx()?.reversible_write_counter.into(), - ), - ] { - self.call_context_read(exec_step, caller.call_id, field, value); - } + Ok(copy_steps) + } - for (field, value) in [ - (CallContextField::LastCalleeId, call.call_id.into()), - (CallContextField::LastCalleeReturnDataOffset, 0.into()), - (CallContextField::LastCalleeReturnDataLength, 0.into()), - ] { - self.call_context_write(exec_step, caller.call_id, field, value); + /// Generate copy steps for call data. + pub(crate) fn gen_copy_steps_for_call_data( + &mut self, + exec_step: &mut ExecStep, + src_addr: u64, + dst_addr: u64, + src_addr_end: u64, + bytes_left: u64, + ) -> Result, Error> { + let mut copy_steps = Vec::with_capacity(bytes_left as usize); + for idx in 0..bytes_left { + let addr = src_addr.checked_add(idx).unwrap_or(src_addr_end); + let value = if addr < src_addr_end { + let byte = + self.call_ctx()?.call_data[(addr - self.call()?.call_data_offset) as usize]; + if !self.call()?.is_root { + self.push_op( + exec_step, + RW::READ, + MemoryOp::new(self.call()?.caller_id, addr.into(), byte), + ); + } + byte + } else { + 0 + }; + copy_steps.push((value, false)); + self.memory_write(exec_step, (dst_addr + idx).into(), value)?; } - Ok(()) + Ok(copy_steps) } } diff --git a/bus-mapping/src/circuit_input_builder/tracer_tests.rs b/bus-mapping/src/circuit_input_builder/tracer_tests.rs index 2033ddd23f..35366d1033 100644 --- a/bus-mapping/src/circuit_input_builder/tracer_tests.rs +++ b/bus-mapping/src/circuit_input_builder/tracer_tests.rs @@ -1,17 +1,16 @@ use super::*; use crate::{ circuit_input_builder::access::gen_state_access_trace, - error::{ExecError, OogError}, + error::{DepthError, ExecError, InsufficientBalanceError, OogError}, geth_errors::{ GETH_ERR_GAS_UINT_OVERFLOW, GETH_ERR_OUT_OF_GAS, GETH_ERR_STACK_OVERFLOW, GETH_ERR_STACK_UNDERFLOW, }, - operation::RWCounter, state_db::Account, }; use eth_types::{ address, bytecode, - evm_types::{stack::Stack, Gas, OpcodeId}, + evm_types::{stack::Stack, OpcodeId}, geth_types::GethData, word, Bytecode, Hash, ToAddress, ToWord, Word, }; @@ -31,7 +30,6 @@ struct CircuitInputBuilderTx { builder: CircuitInputBuilder, tx: Transaction, pub(crate) tx_ctx: TransactionContext, - step: ExecStep, } impl CircuitInputBuilderTx { @@ -44,7 +42,7 @@ impl CircuitInputBuilderTx { let tx_ctx = TransactionContext::new( &block.eth_block.transactions[0], &GethExecTrace { - gas: Gas(0), + gas: 0, failed: false, return_value: "".to_owned(), struct_logs: vec![geth_step.clone()], @@ -53,19 +51,10 @@ impl CircuitInputBuilderTx { ) .unwrap(); - let prev_log_id = if tx.is_steps_empty() { - 0 - } else { - tx.last_step().log_id - }; - - let call_ctx = tx_ctx.call_ctx().unwrap(); - let exec_step = ExecStep::new(geth_step, call_ctx, RWCounter::new(), 0, prev_log_id); Self { builder, tx, tx_ctx, - step: exec_step, } } @@ -219,7 +208,7 @@ fn tracer_err_depth() { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( builder.state_ref().get_step_err(step, next_step).unwrap(), - Some(ExecError::Depth) + Some(ExecError::Depth(DepthError::Call)) ); } @@ -261,10 +250,7 @@ fn tracer_err_insufficient_balance() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -288,7 +274,9 @@ fn tracer_err_insufficient_balance() { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( builder.state_ref().get_step_err(step, next_step).unwrap(), - Some(ExecError::InsufficientBalance) + Some(ExecError::InsufficientBalance( + InsufficientBalanceError::Call + )) ); } @@ -357,12 +345,8 @@ fn tracer_err_address_collision() { // that outputs the same, which will lead to the same new // contract address. let code_creator = bytecode! { - PUSH1(0x00) // value - PUSH1(0x00) // offset - MSTORE - PUSH1(0x01) // length - PUSH1(0x00) // offset - RETURN + .op_mstore(0x00, 0x00) + .op_return(0x00, 0x01) }; // code_a calls code_b which executes code_creator in CREATE2 @@ -389,9 +373,7 @@ fn tracer_err_address_collision() { .chain(0u8..((32 - len % 32) as u8)) .collect(); for (index, word) in code_creator.chunks(32).enumerate() { - code_b.push(32, Word::from_big_endian(word)); - code_b.push(32, Word::from(index * 32)); - code_b.write_op(OpcodeId::MSTORE); + code_b.op_mstore(index * 32, Word::from_big_endian(word)); } let code_b_end = bytecode! { PUSH3(0x123456) // salt @@ -423,10 +405,7 @@ fn tracer_err_address_collision() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -488,12 +467,8 @@ fn tracer_create_collision_free() { // that outputs not the same, which will lead to the different new // contract address. let code_creator = bytecode! { - PUSH1(0x00) // value - PUSH1(0x00) // offset - MSTORE - PUSH1(0x01) // length - PUSH1(0x00) // offset - RETURN + .op_mstore(0x00, 0x00) + .op_return(0x00, 0x01) }; // code_a calls code_b which executes code_creator in CREATE2 @@ -520,9 +495,7 @@ fn tracer_create_collision_free() { .chain(0u8..((32 - len % 32) as u8)) .collect(); for (index, word) in code_creator.chunks(32).enumerate() { - code_b.push(32, Word::from_big_endian(word)); - code_b.push(32, Word::from(index * 32)); - code_b.write_op(OpcodeId::MSTORE); + code_b.op_mstore(index * 32, Word::from_big_endian(word)); } let code_b_end = bytecode! { PUSH1(len) // length @@ -552,10 +525,7 @@ fn tracer_create_collision_free() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -596,7 +566,7 @@ fn tracer_create_collision_free() { builder.builder.sdb.set_account( &ADDR_B, Account { - nonce: Word::zero(), + nonce: 0, balance: Word::from(555u64), /* same value as in * `mock::new_tracer_account` */ storage: HashMap::new(), @@ -606,7 +576,7 @@ fn tracer_create_collision_free() { builder.builder.sdb.set_account( &create_address, Account { - nonce: Word::zero(), + nonce: 0, balance: Word::zero(), storage: HashMap::new(), code_hash: Hash::zero(), @@ -623,7 +593,7 @@ fn check_err_code_store_out_of_gas(step: &GethExecStep, next_step: Option<&GethE step.op == OpcodeId::RETURN && step.error.is_none() && result(next_step).is_zero() - && Word::from(200) * length > Word::from(step.gas.0) + && Word::from(200) * length > Word::from(step.gas) } #[test] @@ -632,12 +602,8 @@ fn tracer_err_code_store_out_of_gas() { // exhaust the gas to store the code. let code_len = 0x100; let code_creator = bytecode! { - PUSH1(Word::zero()) // value - PUSH32(code_len) // offset - MSTORE - PUSH32(code_len) // length - PUSH1(0x00) // offset - RETURN + .op_mstore(code_len, Word::zero()) + .op_return(0x00, code_len) }; // code_a calls code_b which executes code_creator in CREATE @@ -664,9 +630,7 @@ fn tracer_err_code_store_out_of_gas() { .chain(0..(32 - len % 32) as u8) .collect(); for (index, word) in code_creator.chunks(32).enumerate() { - code_b.push(32, Word::from_big_endian(word)); - code_b.push(32, Word::from(index * 32)); - code_b.write_op(OpcodeId::MSTORE); + code_b.op_mstore(index * 32, Word::from_big_endian(word)); } let code_b_end = bytecode! { PUSH32(len) // length @@ -691,10 +655,7 @@ fn tracer_err_code_store_out_of_gas() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -729,12 +690,8 @@ fn tracer_err_code_store_out_of_gas_tx_deploy() { // exhaust the gas to store the code. let code_len = 0x100; let code_creator = bytecode! { - PUSH1(Word::zero()) // value - PUSH32(code_len) // offset - MSTORE - PUSH32(code_len) // length - PUSH1(0x00) // offset - RETURN + .op_mstore(code_len, Word::zero()) + .op_return(0x00, code_len) }; // Get the execution steps from the external tracer @@ -748,7 +705,7 @@ fn tracer_err_code_store_out_of_gas_tx_deploy() { txs[0] .from(accs[1].address) .gas(55000u64.into()) - .nonce(Word::zero()) + .nonce(0) .input(code_creator.into()); }, |block, _tx| block.number(0x0264), @@ -794,12 +751,8 @@ fn tracer_err_invalid_code() { // code_creator outputs byte array that starts with 0xef, which is // invalid code. let code_creator = bytecode! { - PUSH32(word!("0xef00000000000000000000000000000000000000000000000000000000000000")) // value - PUSH1(0x00) // offset - MSTORE - PUSH1(0x01) // length - PUSH1(0x00) // offset - RETURN + .op_mstore(0x00, word!("0xef00000000000000000000000000000000000000000000000000000000000000")) + .op_return(0x00, 0x01) }; // code_a calls code_b which executes code_creator in CREATE @@ -826,9 +779,7 @@ fn tracer_err_invalid_code() { .chain(0u8..((32 - len % 32) as u8)) .collect(); for (index, word) in code_creator.chunks(32).enumerate() { - code_b.push(32, Word::from_big_endian(word)); - code_b.push(32, Word::from(index * 32)); - code_b.write_op(OpcodeId::MSTORE); + code_b.op_mstore(index * 32, Word::from_big_endian(word)); } let code_b_end = bytecode! { PUSH1(len) // length @@ -853,10 +804,7 @@ fn tracer_err_invalid_code() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -900,12 +848,8 @@ fn tracer_err_max_code_size_exceeded() { // trigger the max code size limit. let code_len = 0x6000 + 1; let code_creator = bytecode! { - PUSH1(Word::zero()) // value - PUSH32(code_len) // offset - MSTORE - PUSH32(code_len) // length - PUSH1(0x00) // offset - RETURN + .op_mstore(code_len, Word::zero()) + .op_return(0x00, code_len) }; // code_a calls code_b which executes code_creator in CREATE @@ -932,9 +876,7 @@ fn tracer_err_max_code_size_exceeded() { .chain(0u8..((32 - len % 32) as u8)) .collect(); for (index, word) in code_creator.chunks(32).enumerate() { - code_b.push(32, Word::from_big_endian(word)); - code_b.push(32, Word::from(index * 32)); - code_b.write_op(OpcodeId::MSTORE); + code_b.op_mstore(index * 32, Word::from_big_endian(word)); } let code_b_end = bytecode! { PUSH32(len) // length @@ -959,10 +901,7 @@ fn tracer_err_max_code_size_exceeded() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -997,12 +936,8 @@ fn tracer_err_max_code_size_exceeded_tx_deploy() { // trigger the max code size limit. let code_len = 0x6000 + 1; let code_creator = bytecode! { - PUSH1(Word::zero()) // value - PUSH32(code_len) // offset - MSTORE - PUSH32(code_len) // length - PUSH1(0x00) // offset - RETURN + .op_mstore(code_len, Word::zero()) + .op_return(0x00, code_len) }; // Get the execution steps from the external tracer @@ -1016,7 +951,7 @@ fn tracer_err_max_code_size_exceeded_tx_deploy() { txs[0] .from(accs[1].address) .gas(60000u64.into()) - .nonce(Word::zero()) + .nonce(0) .input(code_creator.into()); }, |block, _tx| block.number(0x0264), @@ -1050,9 +985,7 @@ fn tracer_err_max_code_size_exceeded_tx_deploy() { fn tracer_create_stop() { // code_creator doesn't output anything because it stops. let code_creator = bytecode! { - PUSH32(word!("0xef00000000000000000000000000000000000000000000000000000000000000")) // value - PUSH1(0x00) // offset - MSTORE + .op_mstore(0x00, word!("0xef00000000000000000000000000000000000000000000000000000000000000")) PUSH1(0x01) // length PUSH1(0x00) // offset STOP @@ -1082,9 +1015,7 @@ fn tracer_create_stop() { .chain(0u8..((32 - len % 32) as u8)) .collect(); for (index, word) in code_creator.chunks(32).enumerate() { - code_b.push(32, Word::from_big_endian(word)); - code_b.push(32, Word::from(index * 32)); - code_b.write_op(OpcodeId::MSTORE); + code_b.op_mstore(index * 32, Word::from_big_endian(word)); } let code_b_end = bytecode! { PUSH1(len) // length @@ -1111,10 +1042,7 @@ fn tracer_create_stop() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1232,10 +1160,7 @@ fn tracer_err_invalid_jump() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1335,10 +1260,7 @@ fn tracer_err_execution_reverted() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1397,10 +1319,7 @@ fn tracer_stop() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1451,12 +1370,8 @@ fn tracer_err_return_data_out_of_bounds() { PUSH2(0xaa) }; let code_b = bytecode! { - PUSH2(0x42) // value - PUSH2(0x00) // offset - MSTORE - PUSH1(0x01) // length - PUSH1(0x00) // offset - RETURN + .op_mstore(0x00, 0x42) + .op_return(0x00, 0x01) }; // Get the execution steps from the external tracer let block: GethData = TestContext::<3, 2>::new_with_logger_config( @@ -1472,10 +1387,7 @@ fn tracer_err_return_data_out_of_bounds() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1510,9 +1422,7 @@ fn tracer_err_gas_uint_overflow() { // MSTORE a value at an offset so high that the gast cost is big enough // to overflow an uint64 let code = bytecode! { - PUSH32(0x42) // value - PUSH32(0x100_0000_0000_0000_0000_u128) // offset - MSTORE + .op_mstore(0x100_0000_0000_0000_0000_u128, 0x42) }; let block: GethData = TestContext::<2, 1>::new_with_logger_config( None, @@ -1551,7 +1461,7 @@ fn tracer_err_gas_uint_overflow() { fn tracer_err_invalid_opcode() { // The second opcode is invalid (0x0f) let mut code = bytecode::Bytecode::default(); - code.write_op(OpcodeId::PC); + code.op_pc(); code.write(0x0f, true); let block: GethData = TestContext::<2, 1>::new_with_logger_config( None, @@ -1608,19 +1518,11 @@ fn tracer_err_write_protection(is_call: bool) { PUSH2(0xaa) }; - let mut code_b = bytecode! { - PUSH1(0x01) // value - PUSH1(0x02) // key - }; + let mut code_b = Bytecode::default(); if is_call { - code_b.push(1, Word::zero()); - code_b.push(1, Word::from(0x20)); - code_b.push(1, Word::from(0x10)); // value - code_b.push(32, *WORD_ADDR_B); // addr - code_b.push(32, Word::from(0x1000)); // gas - code_b.write_op(OpcodeId::CALL); + code_b.op_call(0x1000, *WORD_ADDR_B, 0x10, 0x20, 0, 0x02, 0x01); } else { - code_b.write_op(OpcodeId::SSTORE); + code_b.op_sstore(0x02, 0x01); } code_b.push(2, Word::from(0xbb)); @@ -1638,10 +1540,7 @@ fn tracer_err_write_protection(is_call: bool) { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1789,12 +1688,8 @@ fn tracer_err_stack_underflow() { fn create2_address() { // code_creator outputs 0x6050. let code_creator = bytecode! { - PUSH32(word!("0x6050000000000000000000000000000000000000000000000000000000000000")) // value - PUSH1(0x00) // offset - MSTORE - PUSH1(0x02) // length - PUSH1(0x00) // offset - RETURN + .op_mstore(0x00, word!("0x6050000000000000000000000000000000000000000000000000000000000000")) + .op_return(0x00, 0x02) }; // code_a calls code_b which executes code_creator in CREATE @@ -1821,9 +1716,7 @@ fn create2_address() { .chain(0u8..((32 - len % 32) as u8)) .collect(); for (index, word) in code_creator.chunks(32).enumerate() { - code_b.push(32, Word::from_big_endian(word)); - code_b.push(32, Word::from(index * 32)); - code_b.write_op(OpcodeId::MSTORE); + code_b.op_mstore(index * 32, Word::from_big_endian(word)); } let code_b_end = bytecode! { PUSH3(0x123456) // salt @@ -1849,10 +1742,7 @@ fn create2_address() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1891,12 +1781,8 @@ fn create2_address() { fn create_address() { // code_creator outputs 0x6050. let code_creator = bytecode! { - PUSH32(word!("0x6050000000000000000000000000000000000000000000000000000000000000")) // value - PUSH1(0x00) // offset - MSTORE - PUSH1(0x02) // length - PUSH1(0x00) // offset - RETURN + .op_mstore(0x00, word!("0x6050000000000000000000000000000000000000000000000000000000000000")) + .op_return(0x00, 0x02) }; // code_a calls code_b which executes code_creator in CREATE @@ -1923,9 +1809,7 @@ fn create_address() { .chain(0u8..((32 - len % 32) as u8)) .collect(); for (index, word) in code_creator.chunks(32).enumerate() { - code_b.push(32, Word::from_big_endian(word)); - code_b.push(32, Word::from(index * 32)); - code_b.write_op(OpcodeId::MSTORE); + code_b.op_mstore(index * 32, Word::from_big_endian(word)); } // We do CREATE 2 times to use a nonce != 0 in the second one. let code_b_end = bytecode! { @@ -1956,10 +1840,7 @@ fn create_address() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1992,7 +1873,7 @@ fn create_address() { builder.builder.sdb.set_account( &ADDR_B, Account { - nonce: Word::from(1), + nonce: 1, ..Account::zero() }, ); @@ -2021,9 +1902,7 @@ fn test_gen_access_trace() { PUSH2(0xaa) }; let code_b = bytecode! { - PUSH32(word!("0x1234567890000000000000000000abcdef000000000000000000112233445566")) // value - PUSH1(0x01) // offset - MSTORE + .op_mstore(0x01, word!("0x1234567890000000000000000000abcdef000000000000000000112233445566")) PUSH1(0x01) // value PUSH1(0x02) // key SSTORE @@ -2045,10 +1924,7 @@ fn test_gen_access_trace() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -2248,9 +2124,7 @@ fn test_gen_access_trace_create_push_call_stack() { .chain(0u8..((32 - len % 32) as u8)) .collect(); for (index, word) in code_creator.chunks(32).enumerate() { - code_b.push(32, Word::from_big_endian(word)); - code_b.push(32, Word::from(index * 32)); - code_b.write_op(OpcodeId::MSTORE); + code_b.op_mstore(index * 32, Word::from_big_endian(word)); } let code_b_end = bytecode! { PUSH1(len) // length @@ -2272,10 +2146,7 @@ fn test_gen_access_trace_create_push_call_stack() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), diff --git a/bus-mapping/src/circuit_input_builder/transaction.rs b/bus-mapping/src/circuit_input_builder/transaction.rs index 1718772cfd..dae4f1db35 100644 --- a/bus-mapping/src/circuit_input_builder/transaction.rs +++ b/bus-mapping/src/circuit_input_builder/transaction.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; -use eth_types::{evm_types::Memory, geth_types, Address, GethExecTrace, Signature, Word}; +use eth_types::{evm_types::Memory, geth_types, GethExecTrace}; use ethers_core::utils::get_contract_address; use crate::{ @@ -176,70 +176,18 @@ impl TransactionContext { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] /// Result of the parsing of an Ethereum Transaction. pub struct Transaction { - /// Nonce - pub nonce: u64, - /// Gas - pub gas: u64, - /// Gas price - pub gas_price: Word, - /// From / Caller Address - pub from: Address, - /// To / Callee Address - pub to: Address, - /// Value - pub value: Word, - /// Input / Call Data - pub input: Vec, - /// Signature - pub signature: Signature, + /// The raw transaction fields + pub tx: geth_types::Transaction, /// Calls made in the transaction pub(crate) calls: Vec, /// Execution steps steps: Vec, } -impl From<&Transaction> for geth_types::Transaction { - fn from(tx: &Transaction) -> geth_types::Transaction { - geth_types::Transaction { - from: tx.from, - to: Some(tx.to), - nonce: Word::from(tx.nonce), - gas_limit: Word::from(tx.gas), - value: tx.value, - gas_price: tx.gas_price, - call_data: tx.input.clone().into(), - v: tx.signature.v, - r: tx.signature.r, - s: tx.signature.s, - ..Default::default() - } - } -} - impl Transaction { - /// Create a dummy Transaction with zero values - pub fn dummy() -> Self { - Self { - nonce: 0, - gas: 0, - gas_price: Word::zero(), - from: Address::zero(), - to: Address::zero(), - value: Word::zero(), - input: Vec::new(), - signature: Signature { - r: Word::zero(), - s: Word::zero(), - v: 0, - }, - calls: Vec::new(), - steps: Vec::new(), - } - } - /// Create a new Self. pub fn new( call_id: usize, @@ -296,22 +244,9 @@ impl Transaction { }; Ok(Self { - nonce: eth_tx.nonce.as_u64(), - gas: eth_tx.gas.as_u64(), - gas_price: eth_tx.gas_price.unwrap_or_default(), - from: eth_tx.from, - to: eth_tx - .to - .unwrap_or_else(|| get_contract_address(eth_tx.from, eth_tx.nonce)), - value: eth_tx.value, - input: eth_tx.input.to_vec(), + tx: eth_tx.into(), calls: vec![call], steps: Vec::new(), - signature: Signature { - v: eth_tx.v.as_u64(), - r: eth_tx.r, - s: eth_tx.s, - }, }) } @@ -359,4 +294,9 @@ impl Transaction { pub fn is_steps_empty(&self) -> bool { self.steps.is_empty() } + + /// Convinient method for gas limit + pub fn gas(&self) -> u64 { + self.tx.gas_limit.as_u64() + } } diff --git a/bus-mapping/src/error.rs b/bus-mapping/src/error.rs index 8fe916ce33..dca1721456 100644 --- a/bus-mapping/src/error.rs +++ b/bus-mapping/src/error.rs @@ -28,12 +28,12 @@ pub enum Error { /// Code not found in the CodeDB CodeNotFound(H256), /// Unable to figure out error at a [`GethExecStep`] - UnexpectedExecStepError(&'static str, GethExecStep), + UnexpectedExecStepError(&'static str, Box), /// Invalid [`eth_types::GethExecTrace`] due to an invalid/unexpected value /// in it. InvalidGethExecTrace(&'static str), /// Invalid [`GethExecStep`] due to an invalid/unexpected value in it. - InvalidGethExecStep(&'static str, GethExecStep), + InvalidGethExecStep(&'static str, Box), /// Eth type related error. EthTypeError(eth_types::Error), /// EVM Execution error @@ -73,8 +73,8 @@ pub enum OogError { /// Out of Gas for CREATE, RETURN, REVERT, which have dynamic memory /// expansion gas cost DynamicMemoryExpansion, - /// Out of Gas for CALLDATACOPY, CODECOPY, RETURNDATACOPY, which copy a - /// specified chunk of memory + /// Out of Gas for CALLDATACOPY, CODECOPY, EXTCODECOPY, RETURNDATACOPY, + /// which copy a specified chunk of memory MemoryCopy, /// Out of Gas for BALANCE, EXTCODESIZE, EXTCODEHASH, which possibly touch /// an extra account @@ -88,8 +88,6 @@ pub enum OogError { Exp, /// Out of Gas for SHA3 Sha3, - /// Out of Gas for EXTCODECOPY - ExtCodeCopy, /// Out of Gas for SLOAD and SSTORE SloadSstore, /// Out of Gas for CALL, CALLCODE, DELEGATECALL and STATICCALL @@ -100,6 +98,37 @@ pub enum OogError { SelfDestruct, } +/// Insufficient balance errors by opcode/state. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum InsufficientBalanceError { + /// Insufficient balance during CALL/CALLCODE opcode. + Call, + /// Insufficient balance during CREATE opcode. + Create, + /// Insufficient balance during CREATE2 opcode. + Create2, +} + +/// Nonce uint overflow errors by opcode/state. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum NonceUintOverflowError { + /// Nonce uint overflow during CREATE opcode. + Create, + /// Nonce uint overflow during CREATE2 opcode. + Create2, +} + +/// Call depth errors by opcode/state. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum DepthError { + /// Call depth errors in CALL/CALLCODE opcode. + Call, + /// Call depth errors in CREATE opcode. + Create, + /// Call depth errors in CREATE2 opcode. + Create2, +} + /// EVM Execution Error #[derive(Clone, Debug, PartialEq, Eq)] pub enum ExecError { @@ -114,11 +143,11 @@ pub enum ExecError { /// For SSTORE, LOG0, LOG1, LOG2, LOG3, LOG4, CREATE, CALL, CREATE2, /// SELFDESTRUCT WriteProtection, - /// For CALL, CALLCODE, DELEGATECALL, STATICCALL - Depth, - /// For CALL, CALLCODE - InsufficientBalance, - /// For CREATE, CREATE2 + /// For CALL, CALLCODE, DELEGATECALL, STATICCALL, CREATE, CREATE2 + Depth(DepthError), + /// For CALL, CALLCODE, CREATE, CREATE2 + InsufficientBalance(InsufficientBalanceError), + /// For CREATE2 ContractAddressCollision, /// contract must not begin with 0xef due to EIP #3541 EVM Object Format /// (EOF) @@ -131,6 +160,8 @@ pub enum ExecError { CodeStoreOutOfGas, /// For RETURN in a CREATE, CREATE2 MaxCodeSizeExceeded, + /// For CREATE, CREATE2 + NonceUintOverflow(NonceUintOverflowError), } // TODO: Move to impl block. @@ -144,9 +175,10 @@ pub(crate) fn get_step_reported_error(op: &OpcodeId, error: &str) -> ExecError { OpcodeId::CREATE | OpcodeId::RETURN | OpcodeId::REVERT => { OogError::DynamicMemoryExpansion } - OpcodeId::CALLDATACOPY | OpcodeId::CODECOPY | OpcodeId::RETURNDATACOPY => { - OogError::MemoryCopy - } + OpcodeId::CALLDATACOPY + | OpcodeId::CODECOPY + | OpcodeId::EXTCODECOPY + | OpcodeId::RETURNDATACOPY => OogError::MemoryCopy, OpcodeId::BALANCE | OpcodeId::EXTCODESIZE | OpcodeId::EXTCODEHASH => { OogError::AccountAccess } @@ -155,7 +187,6 @@ pub(crate) fn get_step_reported_error(op: &OpcodeId, error: &str) -> ExecError { } OpcodeId::EXP => OogError::Exp, OpcodeId::SHA3 => OogError::Sha3, - OpcodeId::EXTCODECOPY => OogError::ExtCodeCopy, OpcodeId::CALL | OpcodeId::CALLCODE | OpcodeId::DELEGATECALL | OpcodeId::STATICCALL => { OogError::Call } diff --git a/bus-mapping/src/evm.rs b/bus-mapping/src/evm.rs index 319040887a..39cd478c9f 100644 --- a/bus-mapping/src/evm.rs +++ b/bus-mapping/src/evm.rs @@ -5,5 +5,4 @@ pub(crate) mod opcodes; pub use eth_types::evm_types::opcode_ids::OpcodeId; pub use opcodes::Opcode; -#[cfg(any(feature = "test", test))] -pub use opcodes::{gen_sha3_code, MemoryKind}; +pub use opcodes::Sha3CodeGen; diff --git a/bus-mapping/src/evm/opcodes.rs b/bus-mapping/src/evm/opcodes.rs index 0229726412..cb4326ac66 100644 --- a/bus-mapping/src/evm/opcodes.rs +++ b/bus-mapping/src/evm/opcodes.rs @@ -1,27 +1,19 @@ //! Definition of each opcode of the EVM. use crate::{ - circuit_input_builder::{CircuitInputStateRef, ExecStep}, - error::{ExecError, OogError}, + circuit_input_builder::{CircuitInputStateRef, ExecState, ExecStep}, + error::{DepthError, ExecError, InsufficientBalanceError, NonceUintOverflowError, OogError}, evm::OpcodeId, - operation::{ - AccountField, AccountOp, CallContextField, TxAccessListAccountOp, TxReceiptField, - TxRefundOp, RW, - }, - state_db::CodeDB, + operation::TxAccessListAccountOp, Error, }; use core::fmt::Debug; -use eth_types::{ - evm_types::{GasCost, MAX_REFUND_QUOTIENT_OF_GAS_USED}, - evm_unimplemented, GethExecStep, ToAddress, ToWord, Word, -}; -use ethers_core::utils::get_contract_address; +use eth_types::{evm_unimplemented, GethExecStep, ToAddress}; -#[cfg(any(feature = "test", test))] -pub use self::sha3::sha3_tests::{gen_sha3_code, MemoryKind}; +pub use self::sha3::Sha3CodeGen; mod address; mod balance; +mod begin_end_tx; mod calldatacopy; mod calldataload; mod calldatasize; @@ -58,6 +50,7 @@ mod error_invalid_jump; mod error_oog_call; mod error_oog_exp; mod error_oog_log; +mod error_oog_memory_copy; mod error_oog_sload_sstore; mod error_return_data_outofbound; mod error_simple; @@ -69,6 +62,7 @@ mod memory_expansion_test; use self::sha3::Sha3; use address::Address; use balance::Balance; +use begin_end_tx::BeginEndTx; use calldatacopy::Calldatacopy; use calldataload::Calldataload; use calldatasize::Calldatasize; @@ -83,6 +77,7 @@ use error_invalid_jump::InvalidJump; use error_oog_call::OOGCall; use error_oog_exp::OOGExp; use error_oog_log::ErrorOOGLog; +use error_oog_memory_copy::OOGMemoryCopy; use error_oog_sload_sstore::OOGSloadSstore; use error_return_data_outofbound::ErrorReturnDataOutOfBound; use error_simple::ErrorSimple; @@ -121,6 +116,15 @@ pub trait Opcode: Debug { ) -> Result, Error>; } +/// Generic trait for tx execution steps +/// which only supports ExecState::BeginTx and ExecState:EndTx +pub trait TxExecSteps: Debug { + fn gen_associated_steps( + state: &mut CircuitInputStateRef, + execution_step: ExecState, + ) -> Result; +} + #[derive(Debug, Copy, Clone)] struct Dummy; @@ -275,14 +279,36 @@ fn fn_gen_error_state_associated_ops(error: &ExecError) -> Option Some(ErrorSimple::gen_associated_ops), ExecError::OutOfGas(OogError::Exp) => Some(OOGExp::gen_associated_ops), ExecError::OutOfGas(OogError::Log) => Some(ErrorOOGLog::gen_associated_ops), + ExecError::OutOfGas(OogError::MemoryCopy) => Some(OOGMemoryCopy::gen_associated_ops), ExecError::OutOfGas(OogError::SloadSstore) => Some(OOGSloadSstore::gen_associated_ops), ExecError::StackOverflow => Some(ErrorSimple::gen_associated_ops), ExecError::StackUnderflow => Some(ErrorSimple::gen_associated_ops), // call & callcode can encounter InsufficientBalance error, Use pop-7 generic CallOpcode - ExecError::InsufficientBalance => Some(CallOpcode::<7>::gen_associated_ops), + ExecError::InsufficientBalance(InsufficientBalanceError::Call) => { + Some(CallOpcode::<7>::gen_associated_ops) + } + // create & create2 can encounter insufficient balance. + ExecError::InsufficientBalance(InsufficientBalanceError::Create) => { + Some(DummyCreate::::gen_associated_ops) + } + ExecError::InsufficientBalance(InsufficientBalanceError::Create2) => { + Some(DummyCreate::::gen_associated_ops) + } + // only create2 may cause ContractAddressCollision error, so use DummyCreate::. + ExecError::ContractAddressCollision => Some(DummyCreate::::gen_associated_ops), + // create & create2 can encounter nonce uint overflow. + ExecError::NonceUintOverflow(NonceUintOverflowError::Create) => { + Some(DummyCreate::::gen_associated_ops) + } + ExecError::NonceUintOverflow(NonceUintOverflowError::Create2) => { + Some(DummyCreate::::gen_associated_ops) + } ExecError::WriteProtection => Some(ErrorWriteProtection::gen_associated_ops), ExecError::ReturnDataOutOfBounds => Some(ErrorReturnDataOutOfBound::gen_associated_ops), - + // call, callcode, create & create2 can encounter DepthError error, + ExecError::Depth(DepthError::Call) => Some(CallOpcode::<7>::gen_associated_ops), + ExecError::Depth(DepthError::Create) => Some(DummyCreate::::gen_associated_ops), + ExecError::Depth(DepthError::Create2) => Some(DummyCreate::::gen_associated_ops), // more future errors place here _ => { evm_unimplemented!("TODO: error state {:?} not implemented", error); @@ -331,17 +357,19 @@ pub fn gen_associated_ops( if let Some(fn_gen_error_ops) = fn_gen_error_state_associated_ops(&exec_error) { return fn_gen_error_ops(state, geth_steps); } else { + // For exceptions that fail to enter next call context, we need + // to restore call context of current caller + let mut need_restore = true; + // For exceptions that already enter next call context, but fail immediately // (e.g. Depth, InsufficientBalance), we still need to parse the call. if geth_step.op.is_call_or_create() { let call = state.parse_call(geth_step)?; state.push_call(call); - // For exceptions that fail to enter next call context, we need - // to restore call context of current caller - } else { - state.gen_restore_context_ops(&mut exec_step, geth_steps)?; + need_restore = false; } - state.handle_return(geth_step)?; + + state.handle_return(&mut exec_step, geth_steps, need_restore)?; return Ok(vec![exec_step]); } } @@ -350,307 +378,18 @@ pub fn gen_associated_ops( fn_gen_associated_ops(state, geth_steps) } -pub fn gen_begin_tx_ops(state: &mut CircuitInputStateRef) -> Result { - let mut exec_step = state.new_begin_tx_step(); - let call = state.call()?.clone(); - - for (field, value) in [ - (CallContextField::TxId, state.tx_ctx.id().into()), - ( - CallContextField::RwCounterEndOfReversion, - call.rw_counter_end_of_reversion.into(), - ), - ( - CallContextField::IsPersistent, - (call.is_persistent as usize).into(), - ), - (CallContextField::IsSuccess, call.is_success.to_word()), - ] { - state.call_context_write(&mut exec_step, call.call_id, field, value); - } - - // Increase caller's nonce - let caller_address = call.caller_address; - let nonce_prev = state.sdb.get_account(&caller_address).1.nonce; - state.account_write( - &mut exec_step, - caller_address, - AccountField::Nonce, - nonce_prev + 1, - nonce_prev, - )?; - - // Add caller and callee into access list - for address in [call.caller_address, call.address] { - let is_warm_prev = !state.sdb.add_account_to_access_list(address); - state.tx_accesslist_account_write( - &mut exec_step, - state.tx_ctx.id(), - address, - true, - is_warm_prev, - )?; - } - - // Calculate intrinsic gas cost - let call_data_gas_cost = state - .tx - .input - .iter() - .fold(0, |acc, byte| acc + if *byte == 0 { 4 } else { 16 }); - let intrinsic_gas_cost = if state.tx.is_create() { - GasCost::CREATION_TX.as_u64() - } else { - GasCost::TX.as_u64() - } + call_data_gas_cost; - exec_step.gas_cost = GasCost(intrinsic_gas_cost); - - // Get code_hash of callee - let (_, callee_account) = state.sdb.get_account(&call.address); - let callee_exists = !callee_account.is_empty(); - let (callee_code_hash, is_empty_code_hash) = if callee_exists { - ( - call.code_hash.to_word(), - call.code_hash == CodeDB::empty_code_hash(), - ) - } else { - (Word::zero(), true) - }; - if !state.is_precompiled(&call.address) && !call.is_create() { - state.account_read( - &mut exec_step, - call.address, - AccountField::CodeHash, - callee_code_hash, - ); - } - - // Transfer with fee - state.transfer_with_fee( - &mut exec_step, - call.caller_address, - call.address, - callee_exists, - call.is_create(), - call.value, - Some(state.tx.gas_price * state.tx.gas), - )?; - - // In case of contract creation we wish to verify the correctness of the - // contract's address (callee). This address is defined as: - // - // Keccak256(RLP([tx_caller, tx_nonce]))[12:] - // - // We feed the RLP-encoded bytes to the block's SHA3 inputs, which gets assigned - // to the Keccak circuit, so that the BeginTxGadget can do a lookup to the - // Keccak table and verify the contract address. - if state.tx.is_create() { - state.block.sha3_inputs.push({ - let mut stream = ethers_core::utils::rlp::RlpStream::new(); - stream.begin_list(2); - stream.append(&caller_address); - stream.append(&nonce_prev); - stream.out().to_vec() - }); - } - - // There are 4 branches from here. - match ( - call.is_create(), - state.is_precompiled(&call.address), - is_empty_code_hash, - ) { - // 1. Creation transaction. - (true, _, _) => { - state.push_op_reversible( - &mut exec_step, - AccountOp { - address: call.address, - field: AccountField::Nonce, - value: 1.into(), - value_prev: 0.into(), - }, - )?; - for (field, value) in [ - (CallContextField::Depth, call.depth.into()), - ( - CallContextField::CallerAddress, - call.caller_address.to_word(), - ), - ( - CallContextField::CalleeAddress, - get_contract_address(caller_address, nonce_prev).to_word(), - ), - ( - CallContextField::CallDataOffset, - call.call_data_offset.into(), - ), - ( - CallContextField::CallDataLength, - state.tx.input.len().into(), - ), - (CallContextField::Value, call.value), - (CallContextField::IsStatic, (call.is_static as usize).into()), - (CallContextField::LastCalleeId, 0.into()), - (CallContextField::LastCalleeReturnDataOffset, 0.into()), - (CallContextField::LastCalleeReturnDataLength, 0.into()), - (CallContextField::IsRoot, 1.into()), - (CallContextField::IsCreate, 1.into()), - (CallContextField::CodeHash, call.code_hash.to_word()), - ] { - state.call_context_write(&mut exec_step, call.call_id, field, value); - } - Ok(exec_step) - } - // 2. Call to precompiled. - (_, true, _) => { - evm_unimplemented!("Call to precompiled is left unimplemented"); - Ok(exec_step) - } - (_, _, is_empty_code_hash) => { - // 3. Call to account with empty code. - if is_empty_code_hash { - return Ok(exec_step); - } - - // 4. Call to account with non-empty code. - for (field, value) in [ - (CallContextField::Depth, call.depth.into()), - ( - CallContextField::CallerAddress, - call.caller_address.to_word(), - ), - (CallContextField::CalleeAddress, call.address.to_word()), - ( - CallContextField::CallDataOffset, - call.call_data_offset.into(), - ), - ( - CallContextField::CallDataLength, - call.call_data_length.into(), - ), - (CallContextField::Value, call.value), - (CallContextField::IsStatic, (call.is_static as usize).into()), - (CallContextField::LastCalleeId, 0.into()), - (CallContextField::LastCalleeReturnDataOffset, 0.into()), - (CallContextField::LastCalleeReturnDataLength, 0.into()), - (CallContextField::IsRoot, 1.into()), - (CallContextField::IsCreate, 0.into()), - (CallContextField::CodeHash, callee_code_hash), - ] { - state.call_context_write(&mut exec_step, call.call_id, field, value); - } - - Ok(exec_step) +pub fn gen_associated_steps( + state: &mut CircuitInputStateRef, + execution_step: ExecState, +) -> Result { + let fn_gen_associated_steps = match execution_step { + ExecState::BeginTx | ExecState::EndTx => BeginEndTx::gen_associated_steps, + _ => { + unreachable!() } - } -} - -pub fn gen_end_tx_ops(state: &mut CircuitInputStateRef) -> Result { - let mut exec_step = state.new_end_tx_step(); - let call = state.tx.calls()[0].clone(); - - state.call_context_read( - &mut exec_step, - call.call_id, - CallContextField::TxId, - state.tx_ctx.id().into(), - ); - state.call_context_read( - &mut exec_step, - call.call_id, - CallContextField::IsPersistent, - Word::from(call.is_persistent as u8), - ); - - let refund = state.sdb.refund(); - state.push_op( - &mut exec_step, - RW::READ, - TxRefundOp { - tx_id: state.tx_ctx.id(), - value: refund, - value_prev: refund, - }, - ); - - let effective_refund = - refund.min((state.tx.gas - exec_step.gas_left.0) / MAX_REFUND_QUOTIENT_OF_GAS_USED as u64); - let (found, caller_account) = state.sdb.get_account(&call.caller_address); - if !found { - return Err(Error::AccountNotFound(call.caller_address)); - } - let caller_balance_prev = caller_account.balance; - let caller_balance = - caller_balance_prev + state.tx.gas_price * (exec_step.gas_left.0 + effective_refund); - state.account_write( - &mut exec_step, - call.caller_address, - AccountField::Balance, - caller_balance, - caller_balance_prev, - )?; - - let effective_tip = state.tx.gas_price - state.block.base_fee; - let (found, coinbase_account) = state.sdb.get_account(&state.block.coinbase); - if !found { - return Err(Error::AccountNotFound(state.block.coinbase)); - } - let coinbase_balance_prev = coinbase_account.balance; - let coinbase_balance = - coinbase_balance_prev + effective_tip * (state.tx.gas - exec_step.gas_left.0); - state.account_write( - &mut exec_step, - state.block.coinbase, - AccountField::Balance, - coinbase_balance, - coinbase_balance_prev, - )?; - - // handle tx receipt tag - state.tx_receipt_write( - &mut exec_step, - state.tx_ctx.id(), - TxReceiptField::PostStateOrStatus, - call.is_persistent as u64, - )?; - - let log_id = exec_step.log_id; - state.tx_receipt_write( - &mut exec_step, - state.tx_ctx.id(), - TxReceiptField::LogLength, - log_id as u64, - )?; - - if state.tx_ctx.id() > 1 { - // query pre tx cumulative gas - state.tx_receipt_read( - &mut exec_step, - state.tx_ctx.id() - 1, - TxReceiptField::CumulativeGasUsed, - state.block_ctx.cumulative_gas_used, - )?; - } - - state.block_ctx.cumulative_gas_used += state.tx.gas - exec_step.gas_left.0; - state.tx_receipt_write( - &mut exec_step, - state.tx_ctx.id(), - TxReceiptField::CumulativeGasUsed, - state.block_ctx.cumulative_gas_used, - )?; - - if !state.tx_ctx.is_last_tx() { - state.call_context_write( - &mut exec_step, - state.block_ctx.rwc.0 + 1, - CallContextField::TxId, - (state.tx_ctx.id() + 1).into(), - ); - } + }; - Ok(exec_step) + fn_gen_associated_steps(state, execution_step) } #[derive(Debug, Copy, Clone)] @@ -701,6 +440,6 @@ fn dummy_gen_selfdestruct_ops( state.sdb.destruct_account(sender); } - state.handle_return(geth_step)?; + state.handle_return(&mut exec_step, geth_steps, false)?; Ok(vec![exec_step]) } diff --git a/bus-mapping/src/evm/opcodes/balance.rs b/bus-mapping/src/evm/opcodes/balance.rs index 32a4a6c597..1f58cf6712 100644 --- a/bus-mapping/src/evm/opcodes/balance.rs +++ b/bus-mapping/src/evm/opcodes/balance.rs @@ -125,14 +125,12 @@ mod balance_tests { let mut code = Bytecode::default(); if is_warm { code.append(&bytecode! { - PUSH20(address.to_word()) - BALANCE + .op_balance(address) POP }); } code.append(&bytecode! { - PUSH20(address.to_word()) - BALANCE + .op_balance(address) STOP }); diff --git a/bus-mapping/src/evm/opcodes/begin_end_tx.rs b/bus-mapping/src/evm/opcodes/begin_end_tx.rs new file mode 100644 index 0000000000..e8baf7f88f --- /dev/null +++ b/bus-mapping/src/evm/opcodes/begin_end_tx.rs @@ -0,0 +1,335 @@ +use super::TxExecSteps; +use crate::{ + circuit_input_builder::{CircuitInputStateRef, ExecState, ExecStep}, + operation::{AccountField, AccountOp, CallContextField, TxReceiptField, TxRefundOp, RW}, + state_db::CodeDB, + Error, +}; +use eth_types::{ + evm_types::{GasCost, MAX_REFUND_QUOTIENT_OF_GAS_USED}, + evm_unimplemented, ToWord, Word, +}; +use ethers_core::utils::get_contract_address; + +#[derive(Clone, Copy, Debug)] +pub(crate) struct BeginEndTx; + +impl TxExecSteps for BeginEndTx { + fn gen_associated_steps( + state: &mut CircuitInputStateRef, + execution_step: ExecState, + ) -> Result { + match execution_step { + ExecState::BeginTx => gen_begin_tx_steps(state), + ExecState::EndTx => gen_end_tx_steps(state), + _ => { + unreachable!() + } + } + } +} + +fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result { + let mut exec_step = state.new_begin_tx_step(); + let call = state.call()?.clone(); + + for (field, value) in [ + (CallContextField::TxId, state.tx_ctx.id().into()), + ( + CallContextField::RwCounterEndOfReversion, + call.rw_counter_end_of_reversion.into(), + ), + ( + CallContextField::IsPersistent, + (call.is_persistent as usize).into(), + ), + (CallContextField::IsSuccess, call.is_success.to_word()), + ] { + state.call_context_write(&mut exec_step, call.call_id, field, value); + } + + // Increase caller's nonce + let caller_address = call.caller_address; + let nonce_prev = state.sdb.get_account(&caller_address).1.nonce; + state.account_write( + &mut exec_step, + caller_address, + AccountField::Nonce, + (nonce_prev + 1).into(), + nonce_prev.into(), + )?; + + // Add caller, callee and coinbase (for EIP-3651) to access list. + for address in [call.caller_address, call.address, state.block.coinbase] { + let is_warm_prev = !state.sdb.add_account_to_access_list(address); + state.tx_accesslist_account_write( + &mut exec_step, + state.tx_ctx.id(), + address, + true, + is_warm_prev, + )?; + } + + let init_code_gas_cost = if state.tx.is_create() { + // Calculate gas cost of init code for EIP-3860. + (state.tx.tx.call_data.len() as u64 + 31) / 32 * eth_types::evm_types::INIT_CODE_WORD_GAS + } else { + 0 + }; + + let intrinsic_gas_cost = if state.tx.is_create() { + GasCost::CREATION_TX + } else { + GasCost::TX + } + state.tx.tx.call_data_gas_cost() + + init_code_gas_cost; + exec_step.gas_cost = intrinsic_gas_cost; + + // Get code_hash of callee + let (_, callee_account) = state.sdb.get_account(&call.address); + let callee_exists = !callee_account.is_empty(); + let (callee_code_hash, is_empty_code_hash) = if callee_exists { + ( + call.code_hash.to_word(), + call.code_hash == CodeDB::empty_code_hash(), + ) + } else { + (Word::zero(), true) + }; + if !state.is_precompiled(&call.address) && !call.is_create() { + state.account_read( + &mut exec_step, + call.address, + AccountField::CodeHash, + callee_code_hash, + ); + } + + // Transfer with fee + state.transfer_with_fee( + &mut exec_step, + call.caller_address, + call.address, + callee_exists, + call.is_create(), + call.value, + Some(state.tx.tx.gas_price * state.tx.gas()), + )?; + + // In case of contract creation we wish to verify the correctness of the + // contract's address (callee). This address is defined as: + // + // Keccak256(RLP([tx_caller, tx_nonce]))[12:] + // + // We feed the RLP-encoded bytes to the block's SHA3 inputs, which gets assigned + // to the Keccak circuit, so that the BeginTxGadget can do a lookup to the + // Keccak table and verify the contract address. + if state.tx.is_create() { + state.block.sha3_inputs.push({ + let mut stream = ethers_core::utils::rlp::RlpStream::new(); + stream.begin_list(2); + stream.append(&caller_address); + stream.append(&nonce_prev); + stream.out().to_vec() + }); + } + + // There are 4 branches from here. + match ( + call.is_create(), + state.is_precompiled(&call.address), + is_empty_code_hash, + ) { + // 1. Creation transaction. + (true, _, _) => { + state.push_op_reversible( + &mut exec_step, + AccountOp { + address: call.address, + field: AccountField::Nonce, + value: 1.into(), + value_prev: 0.into(), + }, + )?; + for (field, value) in [ + (CallContextField::Depth, call.depth.into()), + ( + CallContextField::CallerAddress, + call.caller_address.to_word(), + ), + ( + CallContextField::CalleeAddress, + get_contract_address(caller_address, nonce_prev).to_word(), + ), + ( + CallContextField::CallDataOffset, + call.call_data_offset.into(), + ), + ( + CallContextField::CallDataLength, + state.tx.tx.call_data.len().into(), + ), + (CallContextField::Value, call.value), + (CallContextField::IsStatic, (call.is_static as usize).into()), + (CallContextField::LastCalleeId, 0.into()), + (CallContextField::LastCalleeReturnDataOffset, 0.into()), + (CallContextField::LastCalleeReturnDataLength, 0.into()), + (CallContextField::IsRoot, 1.into()), + (CallContextField::IsCreate, 1.into()), + (CallContextField::CodeHash, call.code_hash.to_word()), + ] { + state.call_context_write(&mut exec_step, call.call_id, field, value); + } + Ok(exec_step) + } + // 2. Call to precompiled. + (_, true, _) => { + evm_unimplemented!("Call to precompiled is left unimplemented"); + Ok(exec_step) + } + (_, _, is_empty_code_hash) => { + // 3. Call to account with empty code. + if is_empty_code_hash { + return Ok(exec_step); + } + + // 4. Call to account with non-empty code. + for (field, value) in [ + (CallContextField::Depth, call.depth.into()), + ( + CallContextField::CallerAddress, + call.caller_address.to_word(), + ), + (CallContextField::CalleeAddress, call.address.to_word()), + ( + CallContextField::CallDataOffset, + call.call_data_offset.into(), + ), + ( + CallContextField::CallDataLength, + call.call_data_length.into(), + ), + (CallContextField::Value, call.value), + (CallContextField::IsStatic, (call.is_static as usize).into()), + (CallContextField::LastCalleeId, 0.into()), + (CallContextField::LastCalleeReturnDataOffset, 0.into()), + (CallContextField::LastCalleeReturnDataLength, 0.into()), + (CallContextField::IsRoot, 1.into()), + (CallContextField::IsCreate, 0.into()), + (CallContextField::CodeHash, callee_code_hash), + ] { + state.call_context_write(&mut exec_step, call.call_id, field, value); + } + + Ok(exec_step) + } + } +} + +fn gen_end_tx_steps(state: &mut CircuitInputStateRef) -> Result { + let mut exec_step = state.new_end_tx_step(); + let call = state.tx.calls()[0].clone(); + + state.call_context_read( + &mut exec_step, + call.call_id, + CallContextField::TxId, + state.tx_ctx.id().into(), + ); + state.call_context_read( + &mut exec_step, + call.call_id, + CallContextField::IsPersistent, + Word::from(call.is_persistent as u8), + ); + + let refund = state.sdb.refund(); + state.push_op( + &mut exec_step, + RW::READ, + TxRefundOp { + tx_id: state.tx_ctx.id(), + value: refund, + value_prev: refund, + }, + ); + + let effective_refund = + refund.min((state.tx.gas() - exec_step.gas_left) / MAX_REFUND_QUOTIENT_OF_GAS_USED as u64); + let (found, caller_account) = state.sdb.get_account(&call.caller_address); + if !found { + return Err(Error::AccountNotFound(call.caller_address)); + } + let caller_balance_prev = caller_account.balance; + let caller_balance = + caller_balance_prev + state.tx.tx.gas_price * (exec_step.gas_left + effective_refund); + state.account_write( + &mut exec_step, + call.caller_address, + AccountField::Balance, + caller_balance, + caller_balance_prev, + )?; + + let effective_tip = state.tx.tx.gas_price - state.block.base_fee; + let (found, coinbase_account) = state.sdb.get_account(&state.block.coinbase); + if !found { + return Err(Error::AccountNotFound(state.block.coinbase)); + } + let coinbase_balance_prev = coinbase_account.balance; + let coinbase_balance = + coinbase_balance_prev + effective_tip * (state.tx.gas() - exec_step.gas_left); + state.account_write( + &mut exec_step, + state.block.coinbase, + AccountField::Balance, + coinbase_balance, + coinbase_balance_prev, + )?; + + // handle tx receipt tag + state.tx_receipt_write( + &mut exec_step, + state.tx_ctx.id(), + TxReceiptField::PostStateOrStatus, + call.is_persistent as u64, + )?; + + let log_id = exec_step.log_id; + state.tx_receipt_write( + &mut exec_step, + state.tx_ctx.id(), + TxReceiptField::LogLength, + log_id as u64, + )?; + + if state.tx_ctx.id() > 1 { + // query pre tx cumulative gas + state.tx_receipt_read( + &mut exec_step, + state.tx_ctx.id() - 1, + TxReceiptField::CumulativeGasUsed, + state.block_ctx.cumulative_gas_used, + )?; + } + + state.block_ctx.cumulative_gas_used += state.tx.gas() - exec_step.gas_left; + state.tx_receipt_write( + &mut exec_step, + state.tx_ctx.id(), + TxReceiptField::CumulativeGasUsed, + state.block_ctx.cumulative_gas_used, + )?; + + if !state.tx_ctx.is_last_tx() { + state.call_context_write( + &mut exec_step, + state.block_ctx.rwc.0 + 1, + CallContextField::TxId, + (state.tx_ctx.id() + 1).into(), + ); + } + + Ok(exec_step) +} diff --git a/bus-mapping/src/evm/opcodes/calldatacopy.rs b/bus-mapping/src/evm/opcodes/calldatacopy.rs index 22e319afe7..eb724c7e5e 100644 --- a/bus-mapping/src/evm/opcodes/calldatacopy.rs +++ b/bus-mapping/src/evm/opcodes/calldatacopy.rs @@ -3,7 +3,7 @@ use crate::{ circuit_input_builder::{ CircuitInputStateRef, CopyDataType, CopyEvent, ExecStep, NumberOrHash, }, - operation::{CallContextField, MemoryOp, RW}, + operation::CallContextField, Error, }; use eth_types::GethExecStep; @@ -20,13 +20,13 @@ impl Opcode for Calldatacopy { let mut exec_steps = vec![gen_calldatacopy_step(state, geth_step)?]; // reconstruction - let memory_offset = geth_step.stack.nth_last(0)?.as_u64(); - let data_offset = geth_step.stack.nth_last(1)?.as_u64(); - let length = geth_step.stack.nth_last(2)?.as_usize(); + let memory_offset = geth_step.stack.nth_last(0)?; + let data_offset = geth_step.stack.nth_last(1)?; + let length = geth_step.stack.nth_last(2)?; let call_ctx = state.call_ctx_mut()?; let memory = &mut call_ctx.memory; - memory.copy_from(memory_offset, &call_ctx.call_data, data_offset, length); + memory.copy_from(memory_offset, data_offset, length, &call_ctx.call_data); let copy_event = gen_copy_event(state, geth_step)?; state.push_copy(&mut exec_steps[0], copy_event); @@ -92,64 +92,38 @@ fn gen_calldatacopy_step( Ok(exec_step) } -fn gen_copy_steps( - state: &mut CircuitInputStateRef, - exec_step: &mut ExecStep, - src_addr: u64, - dst_addr: u64, - src_addr_end: u64, - bytes_left: u64, - is_root: bool, -) -> Result, Error> { - let mut copy_steps = Vec::with_capacity(bytes_left as usize); - for idx in 0..bytes_left { - let addr = src_addr + idx; - let value = if addr < src_addr_end { - let byte = - state.call_ctx()?.call_data[(addr - state.call()?.call_data_offset) as usize]; - if !is_root { - state.push_op( - exec_step, - RW::READ, - MemoryOp::new(state.call()?.caller_id, addr.into(), byte), - ); - } - byte - } else { - 0 - }; - copy_steps.push((value, false)); - state.memory_write(exec_step, (dst_addr + idx).into(), value)?; - } - - Ok(copy_steps) -} - fn gen_copy_event( state: &mut CircuitInputStateRef, geth_step: &GethExecStep, ) -> Result { let rw_counter_start = state.block_ctx.rwc; - let memory_offset = geth_step.stack.nth_last(0)?.as_u64(); - let data_offset = geth_step.stack.nth_last(1)?.as_u64(); + + let memory_offset = geth_step.stack.nth_last(0)?; + let data_offset = geth_step.stack.nth_last(1)?; let length = geth_step.stack.nth_last(2)?.as_u64(); let call_data_offset = state.call()?.call_data_offset; let call_data_length = state.call()?.call_data_length; - let (src_addr, src_addr_end) = ( - call_data_offset + data_offset, - call_data_offset + call_data_length, - ); + + // Get low Uint64 of memory offset to generate copy steps. Since memory + // offset could be Uint64 overflow if memory length is zero. + let dst_addr = memory_offset.low_u64(); + let src_addr_end = call_data_offset.checked_add(call_data_length).unwrap(); + + // Reset start offset to end offset if overflow. + let src_addr = u64::try_from(data_offset) + .ok() + .and_then(|offset| offset.checked_add(call_data_offset)) + .unwrap_or(src_addr_end) + .min(src_addr_end); let mut exec_step = state.new_step(geth_step)?; - let copy_steps = gen_copy_steps( - state, + let copy_steps = state.gen_copy_steps_for_call_data( &mut exec_step, src_addr, - memory_offset, + dst_addr, src_addr_end, length, - state.call()?.is_root, )?; let (src_type, src_id) = if state.call()?.is_root { @@ -165,7 +139,7 @@ fn gen_copy_event( src_addr_end, dst_type: CopyDataType::Memory, dst_id: NumberOrHash::Number(state.call()?.call_id), - dst_addr: memory_offset, + dst_addr, log_id: None, rw_counter_start, bytes: copy_steps, @@ -183,10 +157,14 @@ mod calldatacopy_tests { bytecode, evm_types::{OpcodeId, StackAddress}, geth_types::GethData, - ToWord, Word, + Word, }; - use mock::test_ctx::{helpers::*, TestContext}; + use mock::{ + generate_mock_call_bytecode, + test_ctx::{helpers::*, TestContext}, + MockCallBytecodeParams, + }; use pretty_assertions::assert_eq; #[test] @@ -198,10 +176,7 @@ mod calldatacopy_tests { let offset = 0x00usize; let copy_size = 0x10usize; let code_b = bytecode! { - PUSH32(copy_size) // size - PUSH32(offset) // offset - PUSH32(dst_offset) // dst_offset - CALLDATACOPY + .op_calldatacopy(dst_offset, offset, copy_size) STOP }; @@ -211,24 +186,16 @@ mod calldatacopy_tests { .take(24) .chain(pushdata.clone()) .collect::>(); + let call_data_length = 0x20usize; let call_data_offset = 0x10usize; - let code_a = bytecode! { - // populate memory in A's context. - PUSH8(Word::from_big_endian(&pushdata)) - PUSH1(0x00) // offset - MSTORE - // call addr_b. - PUSH1(0x00) // retLength - PUSH1(0x00) // retOffset - PUSH1(call_data_length) // argsLength - PUSH1(call_data_offset) // argsOffset - PUSH1(0x00) // value - PUSH32(addr_b.to_word()) // addr - PUSH32(0x1_0000) // gas - CALL - STOP - }; + let code_a = generate_mock_call_bytecode(MockCallBytecodeParams { + address: addr_b, + pushdata, + call_data_length, + call_data_offset, + ..MockCallBytecodeParams::default() + }); // Get the execution steps from the external tracer let block: GethData = TestContext::<3, 1>::new( @@ -384,14 +351,8 @@ mod calldatacopy_tests { let (addr_a, addr_b) = (mock::MOCK_ACCOUNTS[0], mock::MOCK_ACCOUNTS[1]); // code B gets called by code A, so the call is an internal call. - let dst_offset = 0x00usize; - let offset = 0x00usize; - let copy_size = 0x50usize; let code_b = bytecode! { - PUSH32(copy_size) // size - PUSH32(offset) // offset - PUSH32(dst_offset) // dst_offset - CALLDATACOPY + .op_calldatacopy(0x00usize, 0x00usize, 0x50usize) STOP }; @@ -401,24 +362,13 @@ mod calldatacopy_tests { .take(24) .chain(pushdata.clone()) .collect::>(); - let call_data_length = 0x20usize; - let call_data_offset = 0x10usize; - let code_a = bytecode! { - // populate memory in A's context. - PUSH8(Word::from_big_endian(&pushdata)) - PUSH1(0x00) // offset - MSTORE - // call addr_b. - PUSH1(0x00) // retLength - PUSH1(0x00) // retOffset - PUSH1(call_data_length) // argsLength - PUSH1(call_data_offset) // argsOffset - PUSH1(0x00) // value - PUSH32(addr_b.to_word()) // addr - PUSH32(0x1_0000) // gas - CALL - STOP - }; + let code_a = generate_mock_call_bytecode(MockCallBytecodeParams { + address: addr_b, + pushdata, + call_data_length: 0x20usize, + call_data_offset: 0x10usize, + ..MockCallBytecodeParams::default() + }); // Get the execution steps from the external tracer let block: GethData = TestContext::<3, 1>::new( @@ -452,10 +402,7 @@ mod calldatacopy_tests { let calldata = vec![1, 3, 5, 7, 9, 2, 4, 6, 8]; let calldata_len = calldata.len(); let code = bytecode! { - PUSH32(size) - PUSH32(offset) - PUSH32(dst_offset) - CALLDATACOPY + .op_calldatacopy(dst_offset, offset, size) STOP }; @@ -567,7 +514,7 @@ mod calldatacopy_tests { assert_eq!(copy_events[0].bytes.len(), size); for (idx, (value, is_code)) in copy_events[0].bytes.iter().enumerate() { - assert_eq!(value, calldata.get(offset as usize + idx).unwrap_or(&0)); + assert_eq!(value, calldata.get(offset + idx).unwrap_or(&0)); assert!(!is_code); } } diff --git a/bus-mapping/src/evm/opcodes/calldataload.rs b/bus-mapping/src/evm/opcodes/calldataload.rs index dacee4e721..b28ae5d07c 100644 --- a/bus-mapping/src/evm/opcodes/calldataload.rs +++ b/bus-mapping/src/evm/opcodes/calldataload.rs @@ -23,73 +23,79 @@ impl Opcode for Calldataload { let offset = geth_step.stack.nth_last(0)?; state.stack_read(&mut exec_step, geth_step.stack.nth_last_filled(0), offset)?; - let is_root = state.call()?.is_root; - if is_root { - state.call_context_read( - &mut exec_step, - state.call()?.call_id, - CallContextField::TxId, - state.tx_ctx.id().into(), + // Check if offset is Uint64 overflow. + let calldata_word = if let Ok(offset) = u64::try_from(offset) { + let is_root = state.call()?.is_root; + let call_id = state.call()?.call_id; + if is_root { + state.call_context_read( + &mut exec_step, + call_id, + CallContextField::TxId, + state.tx_ctx.id().into(), + ); + state.call_context_read( + &mut exec_step, + call_id, + CallContextField::CallDataLength, + state.call()?.call_data_length.into(), + ); + } else { + state.call_context_read( + &mut exec_step, + call_id, + CallContextField::CallerId, + state.call()?.caller_id.into(), + ); + state.call_context_read( + &mut exec_step, + call_id, + CallContextField::CallDataLength, + state.call()?.call_data_length.into(), + ); + state.call_context_read( + &mut exec_step, + call_id, + CallContextField::CallDataOffset, + state.call()?.call_data_offset.into(), + ); + } + + let call_data_offset = state.call()?.call_data_offset; + let call_data_length = state.call()?.call_data_length; + let (src_addr, src_addr_end, caller_id, call_data) = ( + call_data_offset + offset.min(call_data_length), + call_data_offset + call_data_length, + state.call()?.caller_id, + state.call_ctx()?.call_data.to_vec(), ); - state.call_context_read( - &mut exec_step, - state.call()?.call_id, - CallContextField::CallDataLength, - state.call()?.call_data_length.into(), - ); - } else { - state.call_context_read( - &mut exec_step, - state.call()?.call_id, - CallContextField::CallerId, - state.call()?.caller_id.into(), - ); - state.call_context_read( - &mut exec_step, - state.call()?.call_id, - CallContextField::CallDataLength, - state.call()?.call_data_length.into(), - ); - state.call_context_read( - &mut exec_step, - state.call()?.call_id, - CallContextField::CallDataOffset, - state.call()?.call_data_offset.into(), - ); - } - let call = state.call()?.clone(); - let (src_addr, src_addr_end, caller_id, call_data) = ( - call.call_data_offset as usize + offset.as_usize(), - call.call_data_offset as usize + call.call_data_length as usize, - call.caller_id, - state.call_ctx()?.call_data.to_vec(), - ); - let calldata_word = (0..32) - .map(|idx| { - let addr = src_addr + idx; - if addr < src_addr_end { - let byte = call_data[addr - call.call_data_offset as usize]; - if !is_root { - // caller id as call_id - state.push_op( - &mut exec_step, - RW::READ, - MemoryOp::new(caller_id, (src_addr + idx).into(), byte), - ); + let calldata: Vec<_> = (0..32) + .map(|idx| { + let addr = src_addr.checked_add(idx).unwrap_or(src_addr_end); + if addr < src_addr_end { + let byte = call_data[(addr - call_data_offset) as usize]; + if !is_root { + state.push_op( + &mut exec_step, + RW::READ, + MemoryOp::new(caller_id, (src_addr + idx).into(), byte), + ); + } + byte + } else { + 0 } - byte - } else { - 0 - } - }) - .collect::>(); + }) + .collect(); - state.stack_write( - &mut exec_step, - geth_step.stack.last_filled(), - U256::from_big_endian(&calldata_word), - )?; + U256::from_big_endian(&calldata) + } else { + // Stack push `0` as result if overflow. + U256::zero() + }; + + state.stack_write(&mut exec_step, geth_step.stack.last_filled(), calldata_word)?; Ok(vec![exec_step]) } @@ -102,9 +108,12 @@ mod calldataload_tests { bytecode, evm_types::{OpcodeId, StackAddress}, geth_types::GethData, - ToWord, Word, + Word, + }; + use mock::{ + generate_mock_call_bytecode, test_ctx::helpers::account_0_code_account_1_no_code, + MockCallBytecodeParams, TestContext, }; - use mock::{test_ctx::helpers::account_0_code_account_1_no_code, TestContext}; use rand::random; use crate::{circuit_input_builder::ExecState, mock::BlockData, operation::StackOp}; @@ -138,22 +147,13 @@ mod calldataload_tests { if memory_a.len() < call_data_length { memory_a.resize(call_data_length, 0); } - let code_a = bytecode! { - // populate memory in A's context. - PUSH32(Word::from_big_endian(&pushdata)) - PUSH1(0x00) // offset - MSTORE - // call addr_b - PUSH1(0x00) // retLength - PUSH1(0x00) // retOffset - PUSH1(call_data_length) // argsLength - PUSH1(call_data_offset) // argsOffset - PUSH1(0x00) // value - PUSH32(addr_b.to_word()) // addr - PUSH32(0x1_0000) // gas - CALL - STOP - }; + let code_a = generate_mock_call_bytecode(MockCallBytecodeParams { + address: addr_b, + pushdata, + call_data_length, + call_data_offset, + ..MockCallBytecodeParams::default() + }); // Get the execution steps from the external tracer let block: GethData = TestContext::<3, 1>::new( diff --git a/bus-mapping/src/evm/opcodes/callop.rs b/bus-mapping/src/evm/opcodes/callop.rs index db6e6b8c4a..25d0221bbc 100644 --- a/bus-mapping/src/evm/opcodes/callop.rs +++ b/bus-mapping/src/evm/opcodes/callop.rs @@ -2,6 +2,7 @@ use super::Opcode; use crate::{ circuit_input_builder::{CallKind, CircuitInputStateRef, CodeSource, ExecStep}, operation::{AccountField, CallContextField, TxAccessListAccountOp}, + precompile::{execute_precompiled, is_precompiled}, state_db::CodeDB, Error, }; @@ -10,8 +11,9 @@ use eth_types::{ gas_utils::{eip150_gas, memory_expansion_gas_cost}, GasCost, }, - evm_unimplemented, GethExecStep, ToWord, Word, + GethExecStep, ToWord, Word, }; +use std::cmp::min; /// Placeholder structure used to implement [`Opcode`] trait over it /// corresponding to the `OpcodeId::CALL`, `OpcodeId::CALLCODE`, @@ -29,9 +31,11 @@ impl Opcode for CallOpcode { let geth_step = &geth_steps[0]; let mut exec_step = state.new_step(geth_step)?; - let args_offset = geth_step.stack.nth_last(N_ARGS - 4)?.as_usize(); + // In offset and length are truncated to Uint64 for call opcodes as: + // + let args_offset = geth_step.stack.nth_last(N_ARGS - 4)?.low_u64() as usize; let args_length = geth_step.stack.nth_last(N_ARGS - 3)?.as_usize(); - let ret_offset = geth_step.stack.nth_last(N_ARGS - 2)?.as_usize(); + let ret_offset = geth_step.stack.nth_last(N_ARGS - 2)?.low_u64() as usize; let ret_length = geth_step.stack.nth_last(N_ARGS - 1)?.as_usize(); // we need to keep the memory until parse_call complete @@ -142,11 +146,14 @@ impl Opcode for CallOpcode { let caller_balance = sender_account.balance; let is_call_or_callcode = call.kind == CallKind::Call || call.kind == CallKind::CallCode; - let insufficient_balance = call.value > caller_balance && is_call_or_callcode; + + // Precheck is OK when depth is in range and caller balance is sufficient + let is_precheck_ok = + geth_step.depth < 1025 && (!is_call_or_callcode || caller_balance >= call.value); log::debug!( - "insufficient_balance: {}, call type: {:?}, sender_account: {:?} ", - insufficient_balance, + "is_precheck_ok: {}, call type: {:?}, sender_account: {:?} ", + is_precheck_ok, call.kind, call.caller_address ); @@ -162,14 +169,18 @@ impl Opcode for CallOpcode { caller_balance, ); + let code_address = call.code_address(); + let is_precompile = code_address + .map(|ref addr| is_precompiled(addr)) + .unwrap_or(false); // TODO: What about transfer for CALLCODE? - // Transfer value only for CALL opcode, insufficient_balance = false. - if call.kind == CallKind::Call && !insufficient_balance { + // Transfer value only for CALL opcode, is_precheck_ok = true. + if call.kind == CallKind::Call && is_precheck_ok { state.transfer( &mut exec_step, call.caller_address, call.address, - callee_exists, + callee_exists || is_precompile, false, call.value, )?; @@ -192,13 +203,13 @@ impl Opcode for CallOpcode { let memory_expansion_gas_cost = memory_expansion_gas_cost(curr_memory_word_size, next_memory_word_size); let gas_cost = if is_warm { - GasCost::WARM_ACCESS.as_u64() + GasCost::WARM_ACCESS } else { - GasCost::COLD_ACCOUNT_ACCESS.as_u64() + GasCost::COLD_ACCOUNT_ACCESS } + if has_value { - GasCost::CALL_WITH_VALUE.as_u64() + GasCost::CALL_WITH_VALUE + if call.kind == CallKind::Call && !callee_exists { - GasCost::NEW_ACCOUNT.as_u64() + GasCost::NEW_ACCOUNT } else { 0 } @@ -206,18 +217,61 @@ impl Opcode for CallOpcode { 0 } + memory_expansion_gas_cost; let gas_specified = geth_step.stack.last()?; - let callee_gas_left = eip150_gas(geth_step.gas.0 - gas_cost, gas_specified); + let callee_gas_left = eip150_gas(geth_step.gas - gas_cost, gas_specified); // There are 4 branches from here. // add failure case for insufficient balance or error depth in the future. - match ( - insufficient_balance, - state.is_precompiled(&call.address), - is_empty_code_hash, - ) { + match (!is_precheck_ok, is_precompile, is_empty_code_hash) { // 1. Call to precompiled. (false, true, _) => { - evm_unimplemented!("Call to precompiled is left unimplemented"); + assert!(call.is_success, "call to precompile should not fail"); + let caller_ctx = state.caller_ctx_mut()?; + let code_address = code_address.unwrap(); + let (result, contract_gas_cost) = execute_precompiled( + &code_address, + if args_length != 0 { + &caller_ctx.memory.0[args_offset..args_offset + args_length] + } else { + &[] + }, + callee_gas_left, + ); + log::trace!( + "precompile return data len {} gas {}", + result.len(), + contract_gas_cost + ); + caller_ctx.return_data = result.clone(); + let length = min(result.len(), ret_length); + if length != 0 { + caller_ctx.memory.extend_at_least(ret_offset + length); + } + caller_ctx.memory.0[ret_offset..ret_offset + length] + .copy_from_slice(&result[..length]); + for (field, value) in [ + (CallContextField::LastCalleeId, call.call_id.into()), + (CallContextField::LastCalleeReturnDataOffset, 0.into()), + ( + CallContextField::LastCalleeReturnDataLength, + result.len().into(), + ), + ] { + state.call_context_write(&mut exec_step, current_call.call_id, field, value); + } + + log::warn!("missing circuit part of precompile"); + state.handle_return(&mut exec_step, geth_steps, false)?; + + let real_cost = geth_steps[0].gas - geth_steps[1].gas; + if real_cost != exec_step.gas_cost { + log::warn!( + "precompile gas fixed from {} to {}, step {:?}", + exec_step.gas_cost, + real_cost, + geth_steps[0] + ); + } + exec_step.gas_cost = real_cost; Ok(vec![exec_step]) } // 2. Call to account with empty code. @@ -229,23 +283,20 @@ impl Opcode for CallOpcode { ] { state.call_context_write(&mut exec_step, current_call.call_id, field, value); } - state.handle_return(geth_step)?; + state.handle_return(&mut exec_step, geth_steps, false)?; Ok(vec![exec_step]) } // 3. Call to account with non-empty code. (false, _, false) => { for (field, value) in [ - ( - CallContextField::ProgramCounter, - (geth_step.pc.0 + 1).into(), - ), + (CallContextField::ProgramCounter, (geth_step.pc + 1).into()), ( CallContextField::StackPointer, (geth_step.stack.stack_pointer().0 + N_ARGS - 1).into(), ), ( CallContextField::GasLeft, - (geth_step.gas.0 - gas_cost - callee_gas_left).into(), + (geth_step.gas - gas_cost - callee_gas_left).into(), ), (CallContextField::MemorySize, next_memory_word_size.into()), ( @@ -306,9 +357,394 @@ impl Opcode for CallOpcode { ] { state.call_context_write(&mut exec_step, current_call.call_id, field, value); } - state.handle_return(geth_step)?; + state.handle_return(&mut exec_step, geth_steps, false)?; Ok(vec![exec_step]) } // } } } + +#[cfg(test)] +mod tests { + use crate::{circuit_input_builder::CircuitsParams, mock::BlockData}; + use eth_types::{bytecode, evm_types::OpcodeId, geth_types::GethData, word, Bytecode, Word}; + use mock::{ + test_ctx::{ + helpers::{account_0_code_account_1_no_code, tx_from_1_to_0}, + LoggerConfig, + }, + TestContext, + }; + + // move this to circuit after circuit part is complete + #[test] + fn test_precompiled_call() { + struct PrecompileCall { + name: &'static str, + setup_code: Bytecode, + ret_size: Word, + ret_offset: Word, + call_data_offset: Word, + call_data_length: Word, + address: Word, + value: Word, + gas: Word, + stack_value: Vec<(Word, Word)>, + max_rws: usize, + } + + impl Default for PrecompileCall { + fn default() -> Self { + PrecompileCall { + name: "precompiled call", + setup_code: Bytecode::default(), + ret_size: Word::from(0), + ret_offset: Word::from(0), + call_data_offset: Word::from(0), + call_data_length: Word::from(0), + address: Word::from(0), + value: Word::from(0), + gas: Word::from(0xFFFFFFF), + stack_value: vec![], + max_rws: 500, + } + } + } + + impl PrecompileCall { + fn with_call_op(&self, call_op: OpcodeId) -> Bytecode { + assert!( + call_op.is_call(), + "invalid setup, {:?} is not a call op", + call_op + ); + let mut code = self.setup_code.clone(); + code.push(32, self.ret_size) + .push(32, self.ret_offset) + .push(32, self.call_data_length) + .push(32, self.call_data_offset); + if call_op == OpcodeId::CALL || call_op == OpcodeId::CALLCODE { + code.push(32, self.value); + } + code.push(32, self.address) + .push(32, self.gas) + .write_op(call_op) + .write_op(OpcodeId::POP); + for (offset, _) in self.stack_value.iter().rev() { + code.push(32, *offset).write_op(OpcodeId::MLOAD); + } + + code + } + } + + let test_vector = [ + PrecompileCall { + name: "ecRecover", + setup_code: bytecode! { + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) // hash + PUSH1(0x0) + MSTORE + PUSH1(28) // v + PUSH1(0x20) + MSTORE + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) // r + PUSH1(0x40) + MSTORE + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) // s + PUSH1(0x60) + MSTORE + }, + ret_size: Word::from(0x20), + ret_offset: Word::from(0x80), + call_data_length: Word::from(0x80), + address: Word::from(0x1), + stack_value: vec![( + Word::from(0x80), + word!("7156526fbd7a3c72969b54f64e42c10fbb768c8a"), + )], + ..Default::default() + }, + PrecompileCall { + name: "SHA2-256", + setup_code: bytecode! { + PUSH1(0xFF) // data + PUSH1(0) + MSTORE + }, + ret_size: Word::from(0x20), + ret_offset: Word::from(0x20), + call_data_length: Word::from(0x1), + call_data_offset: Word::from(0x1F), + address: Word::from(0x2), + stack_value: vec![( + Word::from(0x20), + word!("a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89"), + )], + ..Default::default() + }, + PrecompileCall { + name: "RIPEMD-160", + setup_code: bytecode! { + PUSH1(0xFF) // data + PUSH1(0) + MSTORE + }, + ret_size: Word::from(0x20), + ret_offset: Word::from(0x20), + call_data_length: Word::from(0x1), + call_data_offset: Word::from(0x1F), + address: Word::from(0x3), + stack_value: vec![( + Word::from(0x20), + word!("2c0c45d3ecab80fe060e5f1d7057cd2f8de5e557"), + )], + ..Default::default() + }, + PrecompileCall { + name: "identity", + setup_code: bytecode! { + PUSH16(word!("0123456789ABCDEF0123456789ABCDEF")) + PUSH1(0x00) + MSTORE + }, + ret_size: Word::from(0x20), + ret_offset: Word::from(0x20), + call_data_length: Word::from(0x20), + address: Word::from(0x4), + stack_value: vec![(Word::from(0x20), word!("0123456789ABCDEF0123456789ABCDEF"))], + ..Default::default() + }, + PrecompileCall { + name: "modexp", + setup_code: bytecode! { + PUSH1(1) // Bsize + PUSH1(0) + MSTORE + PUSH1(1) // Esize + PUSH1(0x20) + MSTORE + PUSH1(1) // Msize + PUSH1(0x40) + MSTORE + PUSH32(word!("0x08090A0000000000000000000000000000000000000000000000000000000000")) // B, E and M + PUSH1(0x60) + MSTORE + }, + ret_size: Word::from(0x01), + ret_offset: Word::from(0x9F), + call_data_length: Word::from(0x63), + address: Word::from(0x5), + stack_value: vec![(Word::from(0x80), Word::from(8))], + ..Default::default() + }, + PrecompileCall { + name: "ecAdd", + setup_code: bytecode! { + PUSH1(1) // x1 + PUSH1(0) + MSTORE + PUSH1(2) // y1 + PUSH1(0x20) + MSTORE + PUSH1(1) // x2 + PUSH1(0x40) + MSTORE + PUSH1(2) // y2 + PUSH1(0x60) + MSTORE + }, + ret_size: Word::from(0x40), + ret_offset: Word::from(0x80), + call_data_length: Word::from(0x80), + address: Word::from(0x6), + stack_value: vec![ + ( + Word::from(0x80), + word!("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd3"), + ), + ( + Word::from(0xA0), + word!("15ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4"), + ), + ], + ..Default::default() + }, + PrecompileCall { + name: "ecMul", + setup_code: bytecode! { + PUSH1(1) // x1 + PUSH1(0) + MSTORE + PUSH1(2) // y1 + PUSH1(0x20) + MSTORE + PUSH1(2) // s + PUSH1(0x40) + MSTORE + }, + ret_size: Word::from(0x40), + ret_offset: Word::from(0x60), + call_data_length: Word::from(0x60), + address: Word::from(0x7), + stack_value: vec![ + ( + Word::from(0x60), + word!("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd3"), + ), + ( + Word::from(0x80), + word!("15ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4"), + ), + ], + ..Default::default() + }, + PrecompileCall { + name: "ecPairing", + setup_code: bytecode! { + PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) + PUSH32(word!("0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2")) + PUSH32(word!("0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7")) + PUSH32(word!("0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4")) + PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000001")) + PUSH32(word!("0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e")) + PUSH32(word!("0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90")) + PUSH32(word!("0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9")) + PUSH32(word!("0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc")) + PUSH32(word!("0x2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f6")) + PUSH32(word!("0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da")) + + PUSH1(12) + PUSH2(0x200) + MSTORE + + JUMPDEST + + PUSH2(0x200) + MLOAD + PUSH1(12) + SUB + PUSH1(0x20) + MUL + MSTORE + PUSH1(1) + PUSH2(0x200) + MLOAD + SUB + DUP1 + PUSH2(0x200) + MSTORE + PUSH2(0x192) + JUMPI + }, + ret_size: Word::from(0x20), + call_data_length: Word::from(0x180), + address: Word::from(0x8), + stack_value: vec![(Word::from(0x0), Word::from(1))], + max_rws: 3000, + ..Default::default() + }, + PrecompileCall { + name: "blake2f", + setup_code: bytecode! { + PUSH32(word!("0000000003000000000000000000000000000000010000000000000000000000")) + PUSH32(word!("0000000000000000000000000000000000000000000000000000000000000000")) + PUSH32(word!("0000000000000000000000000000000000000000000000000000000000000000")) + PUSH32(word!("0000000000000000000000000000000000000000000000000000000000000000")) + PUSH32(word!("19cde05b61626300000000000000000000000000000000000000000000000000")) + PUSH32(word!("3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e13")) + PUSH32(word!("0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f")) + + PUSH1(7) + PUSH2(0x160) + MSTORE + + JUMPDEST + + PUSH2(0x160) + MLOAD + PUSH1(7) + SUB + PUSH1(0x20) + MUL + MSTORE + PUSH1(1) + PUSH2(0x160) + MLOAD + SUB + DUP1 + PUSH2(0x160) + MSTORE + PUSH2(0xed) + JUMPI + }, + ret_size: Word::from(0x40), + ret_offset: Word::from(0x0), + call_data_length: Word::from(0xd5), + address: Word::from(0x9), + stack_value: vec![ + ( + Word::from(0x20), + word!("d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"), + ), + ( + Word::from(0x0), + word!("8c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5"), + ), + ], + max_rws: 1500, + ..Default::default() + }, + ]; + + let call_ops = [ + OpcodeId::CALL, + OpcodeId::CALLCODE, + OpcodeId::DELEGATECALL, + OpcodeId::STATICCALL, + ]; + + for (test_call, call_op) in itertools::iproduct!(test_vector.iter(), call_ops.iter()) { + let code = test_call.with_call_op(*call_op); + let block: GethData = TestContext::<2, 1>::new_with_logger_config( + None, + account_0_code_account_1_no_code(code), + tx_from_1_to_0, + |block, _tx| block.number(0xcafeu64), + LoggerConfig { + enable_memory: true, + ..Default::default() + }, + ) + .unwrap() + .into(); + + let mut builder = BlockData::new_from_geth_data_with_params( + block.clone(), + CircuitsParams { + max_rws: test_call.max_rws, + ..Default::default() + }, + ) + .new_circuit_input_builder(); + builder + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); + + let step = block.geth_traces[0] + .struct_logs + .last() + .expect("at least one step"); + log::debug!("{:?}", step.stack); + for (offset, (_, stack_value)) in test_call.stack_value.iter().enumerate() { + assert_eq!( + *stack_value, + step.stack.nth_last(offset).expect("stack value not found"), + "stack output mismatch {}", + test_call.name + ); + } + } + } +} diff --git a/bus-mapping/src/evm/opcodes/codecopy.rs b/bus-mapping/src/evm/opcodes/codecopy.rs index cf7364d6db..372c8d22af 100644 --- a/bus-mapping/src/evm/opcodes/codecopy.rs +++ b/bus-mapping/src/evm/opcodes/codecopy.rs @@ -20,10 +20,9 @@ impl Opcode for Codecopy { let mut exec_steps = vec![gen_codecopy_step(state, geth_step)?]; // reconstruction - - let dest_offset = geth_step.stack.nth_last(0)?.as_u64(); - let code_offset = geth_step.stack.nth_last(1)?.as_u64(); - let length = geth_step.stack.nth_last(2)?.as_u64(); + let dst_offset = geth_step.stack.nth_last(0)?; + let code_offset = geth_step.stack.nth_last(1)?; + let length = geth_step.stack.nth_last(2)?; let code_hash = state.call()?.code_hash; let code = state.code(code_hash)?; @@ -31,7 +30,7 @@ impl Opcode for Codecopy { let call_ctx = state.call_ctx_mut()?; let memory = &mut call_ctx.memory; - memory.copy_from(dest_offset, &code, code_offset, length as usize); + memory.copy_from(dst_offset, code_offset, length, &code); let copy_event = gen_copy_event(state, geth_step)?; state.push_copy(&mut exec_steps[0], copy_event); @@ -65,56 +64,48 @@ fn gen_codecopy_step( Ok(exec_step) } -fn gen_copy_steps( - state: &mut CircuitInputStateRef, - exec_step: &mut ExecStep, - src_addr: u64, - dst_addr: u64, - bytes_left: u64, - bytecode: &Bytecode, -) -> Result, Error> { - let mut steps = Vec::with_capacity(bytes_left as usize); - for idx in 0..bytes_left { - let addr = src_addr + idx; - let bytecode_element = bytecode.get(addr as usize).unwrap_or_default(); - steps.push((bytecode_element.value, bytecode_element.is_code)); - state.memory_write(exec_step, (dst_addr + idx).into(), bytecode_element.value)?; - } - Ok(steps) -} - fn gen_copy_event( state: &mut CircuitInputStateRef, geth_step: &GethExecStep, ) -> Result { let rw_counter_start = state.block_ctx.rwc; - let dst_offset = geth_step.stack.nth_last(0)?.as_u64(); - let code_offset = geth_step.stack.nth_last(1)?.as_u64(); + let dst_offset = geth_step.stack.nth_last(0)?; + let code_offset = geth_step.stack.nth_last(1)?; let length = geth_step.stack.nth_last(2)?.as_u64(); let code_hash = state.call()?.code_hash; let bytecode: Bytecode = state.code(code_hash)?.into(); - let src_addr_end = bytecode.to_vec().len() as u64; + let code_size = bytecode.code.len() as u64; + + // Get low Uint64 of offset to generate copy steps. Since offset could be + // Uint64 overflow if length is zero. + let dst_addr = dst_offset.low_u64(); + let src_addr_end = code_size; + + // Reset start offset to end offset if overflow. + let src_addr = u64::try_from(code_offset) + .unwrap_or(src_addr_end) + .min(src_addr_end); let mut exec_step = state.new_step(geth_step)?; - let copy_steps = gen_copy_steps( - state, + let copy_steps = state.gen_copy_steps_for_bytecode( &mut exec_step, - code_offset, - dst_offset, - length, &bytecode, + src_addr, + dst_addr, + src_addr_end, + length, )?; Ok(CopyEvent { src_type: CopyDataType::Bytecode, src_id: NumberOrHash::Hash(code_hash), - src_addr: code_offset, + src_addr, src_addr_end, dst_type: CopyDataType::Memory, dst_id: NumberOrHash::Number(state.call()?.call_id), - dst_addr: dst_offset, + dst_addr, log_id: None, rw_counter_start, bytes: copy_steps, diff --git a/bus-mapping/src/evm/opcodes/create.rs b/bus-mapping/src/evm/opcodes/create.rs index dff4bd2f81..3966f64162 100644 --- a/bus-mapping/src/evm/opcodes/create.rs +++ b/bus-mapping/src/evm/opcodes/create.rs @@ -18,7 +18,9 @@ impl Opcode for DummyCreate { // TODO: replace dummy create here let geth_step = &geth_steps[0]; - let offset = geth_step.stack.nth_last(1)?.as_usize(); + // Get low Uint64 of offset to generate copy steps. Since offset could + // be Uint64 overflow if length is zero. + let offset = geth_step.stack.nth_last(1)?.low_u64() as usize; let length = geth_step.stack.nth_last(2)?.as_usize(); let curr_memory_word_size = (state.call_ctx()?.memory.len() as u64) / 32; @@ -131,14 +133,10 @@ impl Opcode for DummyCreate { memory_expansion_gas_cost(curr_memory_word_size, next_memory_word_size); // EIP-150: all but one 64th of the caller's gas is sent to the callee. - let caller_gas_left = - (geth_step.gas.0 - geth_step.gas_cost.0 - memory_expansion_gas_cost) / 64; + let caller_gas_left = (geth_step.gas - geth_step.gas_cost - memory_expansion_gas_cost) / 64; for (field, value) in [ - ( - CallContextField::ProgramCounter, - (geth_step.pc.0 + 1).into(), - ), + (CallContextField::ProgramCounter, (geth_step.pc + 1).into()), ( CallContextField::StackPointer, geth_step.stack.nth_last_filled(n_pop - 1).0.into(), @@ -176,7 +174,7 @@ impl Opcode for DummyCreate { if call.code_hash == CodeDB::empty_code_hash() { // 1. Create with empty initcode. - state.handle_return(geth_step)?; + state.handle_return(&mut exec_step, geth_steps, false)?; Ok(vec![exec_step]) } else { // 2. Create with non-empty initcode. diff --git a/bus-mapping/src/evm/opcodes/error_invalid_jump.rs b/bus-mapping/src/evm/opcodes/error_invalid_jump.rs index e9d202eb60..0e72a9b949 100644 --- a/bus-mapping/src/evm/opcodes/error_invalid_jump.rs +++ b/bus-mapping/src/evm/opcodes/error_invalid_jump.rs @@ -40,10 +40,9 @@ impl Opcode for InvalidJump { condition, )?; } - // `IsSuccess` call context operation is added in gen_restore_context_ops - state.gen_restore_context_ops(&mut exec_step, geth_steps)?; - state.handle_return(geth_step)?; + // `IsSuccess` call context operation is added in handle_return + state.handle_return(&mut exec_step, geth_steps, true)?; Ok(vec![exec_step]) } } diff --git a/bus-mapping/src/evm/opcodes/error_oog_call.rs b/bus-mapping/src/evm/opcodes/error_oog_call.rs index 706a71504c..a46423f097 100644 --- a/bus-mapping/src/evm/opcodes/error_oog_call.rs +++ b/bus-mapping/src/evm/opcodes/error_oog_call.rs @@ -90,8 +90,7 @@ impl Opcode for OOGCall { }, ); - state.gen_restore_context_ops(&mut exec_step, geth_steps)?; - state.handle_return(geth_step)?; + state.handle_return(&mut exec_step, geth_steps, true)?; Ok(vec![exec_step]) } } diff --git a/bus-mapping/src/evm/opcodes/error_oog_exp.rs b/bus-mapping/src/evm/opcodes/error_oog_exp.rs index 657c2f9c6d..a0d7f5bdc2 100644 --- a/bus-mapping/src/evm/opcodes/error_oog_exp.rs +++ b/bus-mapping/src/evm/opcodes/error_oog_exp.rs @@ -30,9 +30,7 @@ impl Opcode for OOGExp { )?; } - state.gen_restore_context_ops(&mut exec_step, geth_steps)?; - state.handle_return(geth_step)?; - + state.handle_return(&mut exec_step, geth_steps, true)?; Ok(vec![exec_step]) } } diff --git a/bus-mapping/src/evm/opcodes/error_oog_log.rs b/bus-mapping/src/evm/opcodes/error_oog_log.rs index a65fdd103b..cad2fcfa9e 100644 --- a/bus-mapping/src/evm/opcodes/error_oog_log.rs +++ b/bus-mapping/src/evm/opcodes/error_oog_log.rs @@ -43,9 +43,8 @@ impl Opcode for ErrorOOGLog { CallContextField::IsStatic, Word::from(state.call()?.is_static as u8), ); - // common error handling - state.gen_restore_context_ops(&mut exec_step, geth_steps)?; - state.handle_return(geth_step)?; + + state.handle_return(&mut exec_step, geth_steps, true)?; Ok(vec![exec_step]) } } diff --git a/bus-mapping/src/evm/opcodes/error_oog_memory_copy.rs b/bus-mapping/src/evm/opcodes/error_oog_memory_copy.rs new file mode 100644 index 0000000000..b334414231 --- /dev/null +++ b/bus-mapping/src/evm/opcodes/error_oog_memory_copy.rs @@ -0,0 +1,73 @@ +use crate::{ + circuit_input_builder::{CircuitInputStateRef, ExecStep}, + error::{ExecError, OogError}, + evm::Opcode, + operation::{CallContextField, TxAccessListAccountOp, RW}, + Error, +}; +use eth_types::{evm_types::OpcodeId, GethExecStep, ToAddress}; + +/// Placeholder structure used to implement [`Opcode`] trait over it +/// corresponding to the +/// [`OogError::MemoryCopy`](crate::error::OogError::MemoryCopy). +#[derive(Clone, Copy, Debug)] +pub(crate) struct OOGMemoryCopy; + +impl Opcode for OOGMemoryCopy { + fn gen_associated_ops( + state: &mut CircuitInputStateRef, + geth_steps: &[GethExecStep], + ) -> Result, Error> { + let geth_step = &geth_steps[0]; + debug_assert!([ + OpcodeId::CALLDATACOPY, + OpcodeId::CODECOPY, + OpcodeId::EXTCODECOPY, + OpcodeId::RETURNDATACOPY + ] + .contains(&geth_step.op)); + + let mut exec_step = state.new_step(geth_step)?; + exec_step.error = Some(ExecError::OutOfGas(OogError::MemoryCopy)); + + let is_extcodecopy = geth_step.op == OpcodeId::EXTCODECOPY; + + // According to EIP-2929, EXTCODECOPY constant gas cost is different for cold + // and warm accounts. + if is_extcodecopy { + state.call_context_read( + &mut exec_step, + state.call()?.call_id, + CallContextField::TxId, + state.tx_ctx.id().into(), + ); + + let external_address = geth_step.stack.last()?.to_address(); + let is_warm = state.sdb.check_account_in_access_list(&external_address); + state.push_op( + &mut exec_step, + RW::READ, + TxAccessListAccountOp { + tx_id: state.tx_ctx.id(), + address: external_address, + is_warm, + is_warm_prev: is_warm, + }, + ); + } + + // Each of CALLDATACOPY, CODECOPY and RETURNDATACOPY has 3 stack read values. + // But EXTCODECOPY has 4. It has an extra stack pop for external address. + let stack_read_num = if is_extcodecopy { 4 } else { 3 }; + for i in 0..stack_read_num { + state.stack_read( + &mut exec_step, + geth_step.stack.nth_last_filled(i), + geth_step.stack.nth_last(i)?, + )?; + } + + state.handle_return(&mut exec_step, geth_steps, true)?; + Ok(vec![exec_step]) + } +} diff --git a/bus-mapping/src/evm/opcodes/error_oog_sload_sstore.rs b/bus-mapping/src/evm/opcodes/error_oog_sload_sstore.rs index cd35a07370..6fbb24b83a 100644 --- a/bus-mapping/src/evm/opcodes/error_oog_sload_sstore.rs +++ b/bus-mapping/src/evm/opcodes/error_oog_sload_sstore.rs @@ -89,9 +89,7 @@ impl Opcode for OOGSloadSstore { ); } - state.gen_restore_context_ops(&mut exec_step, geth_steps)?; - state.handle_return(geth_step)?; - + state.handle_return(&mut exec_step, geth_steps, true)?; Ok(vec![exec_step]) } } diff --git a/bus-mapping/src/evm/opcodes/error_return_data_outofbound.rs b/bus-mapping/src/evm/opcodes/error_return_data_outofbound.rs index f432499111..e2479924d5 100644 --- a/bus-mapping/src/evm/opcodes/error_return_data_outofbound.rs +++ b/bus-mapping/src/evm/opcodes/error_return_data_outofbound.rs @@ -68,10 +68,8 @@ impl Opcode for ErrorReturnDataOutOfBound { return_data.len().into(), ); - // `IsSuccess` call context operation is added in gen_restore_context_ops - - state.gen_restore_context_ops(&mut exec_step, geth_steps)?; - state.handle_return(geth_step)?; + // `IsSuccess` call context operation is added in handle_return + state.handle_return(&mut exec_step, geth_steps, true)?; Ok(vec![exec_step]) } } @@ -80,16 +78,16 @@ impl Opcode for ErrorReturnDataOutOfBound { mod tests { use super::*; use crate::{circuit_input_builder::ExecState, mock::BlockData, operation::RW}; - use eth_types::{bytecode, evm_types::OpcodeId, geth_types::GethData, word}; + use eth_types::{bytecode, evm_types::OpcodeId, geth_types::GethData}; use mock::{ test_ctx::helpers::{account_0_code_account_1_no_code, tx_from_1_to_0}, - TestContext, + TestContext, MOCK_DEPLOYED_CONTRACT_BYTECODE, }; #[test] fn test_returndata_error() { let code = bytecode! { - PUSH21(word!("6B6020600060003760206000F3600052600C6014F3")) + PUSH21(*MOCK_DEPLOYED_CONTRACT_BYTECODE) PUSH1(0) MSTORE diff --git a/bus-mapping/src/evm/opcodes/error_simple.rs b/bus-mapping/src/evm/opcodes/error_simple.rs index 1c1840d94b..c27dd06e33 100644 --- a/bus-mapping/src/evm/opcodes/error_simple.rs +++ b/bus-mapping/src/evm/opcodes/error_simple.rs @@ -22,10 +22,7 @@ impl Opcode for ErrorSimple { let next_step = geth_steps.get(1); exec_step.error = state.get_step_err(geth_step, next_step).unwrap(); - // handles all required steps - state.gen_restore_context_ops(&mut exec_step, geth_steps)?; - state.handle_return(geth_step)?; - + state.handle_return(&mut exec_step, geth_steps, true)?; Ok(vec![exec_step]) } } diff --git a/bus-mapping/src/evm/opcodes/error_write_protection.rs b/bus-mapping/src/evm/opcodes/error_write_protection.rs index a80e795b71..58c4f937de 100644 --- a/bus-mapping/src/evm/opcodes/error_write_protection.rs +++ b/bus-mapping/src/evm/opcodes/error_write_protection.rs @@ -62,9 +62,8 @@ impl Opcode for ErrorWriteProtection { (current_call.is_static as u64).into(), ); - // `IsSuccess` call context operation is added in gen_restore_context_ops - state.gen_restore_context_ops(&mut exec_step, geth_steps)?; - state.handle_return(geth_step)?; + // `IsSuccess` call context operation is added in handle_return + state.handle_return(&mut exec_step, geth_steps, true)?; Ok(vec![exec_step]) } } diff --git a/bus-mapping/src/evm/opcodes/extcodecopy.rs b/bus-mapping/src/evm/opcodes/extcodecopy.rs index 2beaf34f12..108f5f0d08 100644 --- a/bus-mapping/src/evm/opcodes/extcodecopy.rs +++ b/bus-mapping/src/evm/opcodes/extcodecopy.rs @@ -11,9 +11,6 @@ use eth_types::{Bytecode, GethExecStep, ToAddress, ToWord, H256, U256}; #[derive(Clone, Copy, Debug)] pub(crate) struct Extcodecopy; -// TODO: Update to treat code_hash == 0 as account not_exists once the circuit -// is implemented https://github.com/privacy-scaling-explorations/zkevm-circuits/pull/720 - impl Opcode for Extcodecopy { fn gen_associated_ops( state: &mut CircuitInputStateRef, @@ -24,9 +21,9 @@ impl Opcode for Extcodecopy { // reconstruction let address = geth_steps[0].stack.nth_last(0)?.to_address(); - let dest_offset = geth_steps[0].stack.nth_last(1)?.as_u64(); - let code_offset = geth_steps[0].stack.nth_last(2)?.as_u64(); - let length = geth_steps[0].stack.nth_last(3)?.as_u64(); + let dst_offset = geth_steps[0].stack.nth_last(1)?; + let code_offset = geth_step.stack.nth_last(2)?; + let length = geth_steps[0].stack.nth_last(3)?; let (_, account) = state.sdb.get_account(&address); let code_hash = account.code_hash; @@ -35,7 +32,7 @@ impl Opcode for Extcodecopy { let call_ctx = state.call_ctx_mut()?; let memory = &mut call_ctx.memory; - memory.copy_from(dest_offset, &code, code_offset, length as usize); + memory.copy_from(dst_offset, code_offset, length, &code); let copy_event = gen_copy_event(state, geth_step)?; state.push_copy(&mut exec_steps[0], copy_event); @@ -111,39 +108,15 @@ fn gen_extcodecopy_step( Ok(exec_step) } -fn gen_copy_steps( - state: &mut CircuitInputStateRef, - exec_step: &mut ExecStep, - src_addr: u64, - dst_addr: u64, - src_addr_end: u64, - bytes_left: u64, - code: &Bytecode, -) -> Result, Error> { - let mut copy_steps = Vec::with_capacity(bytes_left as usize); - for idx in 0..bytes_left { - let addr = src_addr + idx; - let step = if addr < src_addr_end { - let code = code.code.get(addr as usize).unwrap(); - (code.value, code.is_code) - } else { - (0, false) - }; - copy_steps.push(step); - state.memory_write(exec_step, (dst_addr + idx).into(), step.0)?; - } - - Ok(copy_steps) -} - fn gen_copy_event( state: &mut CircuitInputStateRef, geth_step: &GethExecStep, ) -> Result { let rw_counter_start = state.block_ctx.rwc; + let external_address = geth_step.stack.nth_last(0)?.to_address(); - let memory_offset = geth_step.stack.nth_last(1)?.as_u64(); - let data_offset = geth_step.stack.nth_last(2)?.as_u64(); + let dst_offset = geth_step.stack.nth_last(1)?; + let code_offset = geth_step.stack.nth_last(2)?; let length = geth_step.stack.nth_last(3)?.as_u64(); let account = state.sdb.get_account(&external_address).1; @@ -154,28 +127,39 @@ fn gen_copy_event( H256::zero() }; - let code: Bytecode = if exists { + let bytecode: Bytecode = if exists { state.code(code_hash)?.into() } else { Bytecode::default() }; - let src_addr_end = code.code.len() as u64; + let code_size = bytecode.code.len() as u64; + + // Get low Uint64 of offset to generate copy steps. Since offset could be + // Uint64 overflow if length is zero. + let dst_addr = dst_offset.low_u64(); + let src_addr_end = code_size; + + // Reset start offset to end offset if overflow. + let src_addr = u64::try_from(code_offset) + .unwrap_or(src_addr_end) + .min(src_addr_end); + let mut exec_step = state.new_step(geth_step)?; - let copy_steps = gen_copy_steps( - state, + let copy_steps = state.gen_copy_steps_for_bytecode( &mut exec_step, - data_offset, - memory_offset, + &bytecode, + src_addr, + dst_addr, src_addr_end, length, - &code, )?; + Ok(CopyEvent { - src_addr: data_offset, + src_addr, src_addr_end, src_type: CopyDataType::Bytecode, src_id: NumberOrHash::Hash(code_hash), - dst_addr: memory_offset, + dst_addr, dst_type: CopyDataType::Memory, dst_id: NumberOrHash::Number(state.call()?.call_id), log_id: None, diff --git a/bus-mapping/src/evm/opcodes/extcodehash.rs b/bus-mapping/src/evm/opcodes/extcodehash.rs index eeaacf2a8b..7b6bc9e24f 100644 --- a/bus-mapping/src/evm/opcodes/extcodehash.rs +++ b/bus-mapping/src/evm/opcodes/extcodehash.rs @@ -130,12 +130,12 @@ mod extcodehash_tests { EXTCODEHASH STOP }); - let mut nonce = Word::from(300u64); + let mut nonce = 300u64; let mut balance = Word::from(800u64); let mut code_ext = Bytes::from([34, 54, 56]); if !exists { - nonce = Word::zero(); + nonce = 0; balance = Word::zero(); code_ext = Bytes::default(); } diff --git a/bus-mapping/src/evm/opcodes/extcodesize.rs b/bus-mapping/src/evm/opcodes/extcodesize.rs index 440e6607e2..e8f526884a 100644 --- a/bus-mapping/src/evm/opcodes/extcodesize.rs +++ b/bus-mapping/src/evm/opcodes/extcodesize.rs @@ -97,7 +97,7 @@ mod extcodesize_tests { geth_types::{Account, GethData}, Bytecode, U256, }; - use mock::{TestContext, MOCK_1_ETH, MOCK_ACCOUNTS, MOCK_CODES}; + use mock::{TestContext, MOCK_1_ETH, MOCK_ACCOUNTS, MOCK_CODES, MOCK_COINBASE}; use pretty_assertions::assert_eq; #[test] @@ -151,7 +151,7 @@ mod extcodesize_tests { |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); }, - |block, _tx| block.number(0xcafeu64), + |block, _tx| block.author(*MOCK_COINBASE).number(0xcafeu64), ) .unwrap() .into(); diff --git a/bus-mapping/src/evm/opcodes/logs.rs b/bus-mapping/src/evm/opcodes/logs.rs index 3cf22faf95..70b1a98041 100644 --- a/bus-mapping/src/evm/opcodes/logs.rs +++ b/bus-mapping/src/evm/opcodes/logs.rs @@ -252,7 +252,7 @@ mod log_tests { code.push(32, Word::from(msize)); code.push(32, Word::from(mstart)); code.write_op(cur_op_code); - code.write_op(OpcodeId::STOP); + code.op_stop(); // prepare memory data let pushdata = hex::decode("1234567890abcdef1234567890abcdef").unwrap(); @@ -291,7 +291,7 @@ mod log_tests { .unwrap(); let is_persistent = builder.block.txs()[0].calls()[0].is_persistent; - let callee_address = builder.block.txs()[0].to; + let callee_address = builder.block.txs()[0].tx.to_or_contract_addr(); let step = builder.block.txs()[0] .steps() diff --git a/bus-mapping/src/evm/opcodes/memory_expansion_test.rs b/bus-mapping/src/evm/opcodes/memory_expansion_test.rs index 36eb5ad5a0..48cdcbc2d2 100644 --- a/bus-mapping/src/evm/opcodes/memory_expansion_test.rs +++ b/bus-mapping/src/evm/opcodes/memory_expansion_test.rs @@ -7,14 +7,6 @@ use mock::{ TestContext, }; -fn might_neg_index(index: isize, len: usize) -> usize { - if index < 0 { - (len as isize + index) as usize - } else { - index as usize - } -} - fn assert_expanded(_traces: &[GethExecStep], _before: isize, _after: isize) { // FIXME: memory is removed // let traces_len = traces.len(); diff --git a/bus-mapping/src/evm/opcodes/return_revert.rs b/bus-mapping/src/evm/opcodes/return_revert.rs index 484b1c0ef3..d81d63b021 100644 --- a/bus-mapping/src/evm/opcodes/return_revert.rs +++ b/bus-mapping/src/evm/opcodes/return_revert.rs @@ -40,7 +40,9 @@ impl Opcode for ReturnRevert { call.is_success.to_word(), ); - let offset = offset.as_usize(); + // Get low Uint64 of offset to generate copy steps. Since offset could + // be Uint64 overflow if length is zero. + let offset = offset.low_u64() as usize; let length = length.as_usize(); // Case A in the spec. @@ -92,7 +94,7 @@ impl Opcode for ReturnRevert { // Case C in the specs. if !call.is_root { - state.handle_restore_context(steps, &mut exec_step)?; + state.handle_restore_context(&mut exec_step, steps)?; } // Case D in the specs. @@ -132,7 +134,7 @@ impl Opcode for ReturnRevert { } } - state.handle_return(step)?; + state.handle_return(&mut exec_step, steps, false)?; Ok(vec![exec_step]) } } @@ -242,33 +244,13 @@ mod return_tests { use eth_types::{bytecode, geth_types::GethData, word}; use mock::{ test_ctx::helpers::{account_0_code_account_1_no_code, tx_from_1_to_0}, - TestContext, + TestContext, MOCK_DEPLOYED_CONTRACT_BYTECODE, }; #[test] fn test_ok() { - // // deployed contract - // PUSH1 0x20 - // PUSH1 0 - // PUSH1 0 - // CALLDATACOPY - // PUSH1 0x20 - // PUSH1 0 - // RETURN - // - // bytecode: 0x6020600060003760206000F3 - // - // // constructor - // PUSH12 0x6020600060003760206000F3 - // PUSH1 0 - // MSTORE - // PUSH1 0xC - // PUSH1 0x14 - // RETURN - // - // bytecode: 0x6B6020600060003760206000F3600052600C6014F3 let code = bytecode! { - PUSH21(word!("6B6020600060003760206000F3600052600C6014F3")) + PUSH21(*MOCK_DEPLOYED_CONTRACT_BYTECODE) PUSH1(0) MSTORE diff --git a/bus-mapping/src/evm/opcodes/returndatacopy.rs b/bus-mapping/src/evm/opcodes/returndatacopy.rs index 7c63965bef..a7830523d5 100644 --- a/bus-mapping/src/evm/opcodes/returndatacopy.rs +++ b/bus-mapping/src/evm/opcodes/returndatacopy.rs @@ -21,17 +21,16 @@ impl Opcode for Returndatacopy { // reconstruction let geth_step = &geth_steps[0]; - let dest_offset = geth_step.stack.nth_last(0)?; - let offset = geth_step.stack.nth_last(1)?; - let size = geth_step.stack.nth_last(2)?; + let dst_offset = geth_step.stack.nth_last(0)?; + let src_offset = geth_step.stack.nth_last(1)?; + let length = geth_step.stack.nth_last(2)?; // can we reduce this clone? let return_data = state.call_ctx()?.return_data.clone(); let call_ctx = state.call_ctx_mut()?; let memory = &mut call_ctx.memory; - let length = size.as_usize(); - memory.copy_from(dest_offset.as_u64(), &return_data, offset.as_u64(), length); + memory.copy_from(dst_offset, src_offset, length, &return_data); let copy_event = gen_copy_event(state, geth_step)?; state.push_copy(&mut exec_steps[0], copy_event); @@ -124,7 +123,9 @@ fn gen_copy_event( state: &mut CircuitInputStateRef, geth_step: &GethExecStep, ) -> Result { - let dst_addr = geth_step.stack.nth_last(0)?.as_u64(); + // Get low Uint64 of destination offset to generate copy steps. Since it + // could be Uint64 overflow if length is zero. + let dst_addr = geth_step.stack.nth_last(0)?.low_u64(); let data_offset = geth_step.stack.nth_last(1)?.as_u64(); let length = geth_step.stack.nth_last(2)?.as_u64(); @@ -170,36 +171,16 @@ fn gen_copy_event( #[cfg(test)] mod return_tests { use crate::mock::BlockData; - use eth_types::{bytecode, geth_types::GethData, word}; + use eth_types::{bytecode, geth_types::GethData}; use mock::{ test_ctx::helpers::{account_0_code_account_1_no_code, tx_from_1_to_0}, - TestContext, + TestContext, MOCK_DEPLOYED_CONTRACT_BYTECODE, }; #[test] fn test_ok() { - // // deployed contract - // PUSH1 0x20 - // PUSH1 0 - // PUSH1 0 - // CALLDATACOPY - // PUSH1 0x20 - // PUSH1 0 - // RETURN - // - // bytecode: 0x6020600060003760206000F3 - // - // // constructor - // PUSH12 0x6020600060003760206000F3 - // PUSH1 0 - // MSTORE - // PUSH1 0xC - // PUSH1 0x14 - // RETURN - // - // bytecode: 0x6B6020600060003760206000F3600052600C6014F3 let code = bytecode! { - PUSH21(word!("6B6020600060003760206000F3600052600C6014F3")) + PUSH21(*MOCK_DEPLOYED_CONTRACT_BYTECODE) PUSH1(0) MSTORE diff --git a/bus-mapping/src/evm/opcodes/returndatasize.rs b/bus-mapping/src/evm/opcodes/returndatasize.rs index 69017e44a5..ca576be396 100644 --- a/bus-mapping/src/evm/opcodes/returndatasize.rs +++ b/bus-mapping/src/evm/opcodes/returndatasize.rs @@ -47,37 +47,19 @@ mod returndatasize_tests { bytecode, evm_types::{OpcodeId, StackAddress}, geth_types::GethData, - word, Word, + Word, + }; + use mock::{ + test_ctx::{helpers::*, TestContext}, + MOCK_DEPLOYED_CONTRACT_BYTECODE, }; - use mock::test_ctx::{helpers::*, TestContext}; use pretty_assertions::assert_eq; #[test] fn test_ok() { let return_data_size = 0x20; - - // // deployed contract - // PUSH1 0x20 - // PUSH1 0 - // PUSH1 0 - // CALLDATACOPY - // PUSH1 0x20 - // PUSH1 0 - // RETURN - // - // bytecode: 0x6020600060003760206000F3 - // - // // constructor - // PUSH12 0x6020600060003760206000F3 - // PUSH1 0 - // MSTORE - // PUSH1 0xC - // PUSH1 0x14 - // RETURN - // - // bytecode: 0x6B6020600060003760206000F3600052600C6014F3 let code = bytecode! { - PUSH21(word!("6B6020600060003760206000F3600052600C6014F3")) + PUSH21(*MOCK_DEPLOYED_CONTRACT_BYTECODE) PUSH1(0) MSTORE diff --git a/bus-mapping/src/evm/opcodes/selfbalance.rs b/bus-mapping/src/evm/opcodes/selfbalance.rs index 671ebfff11..e77294b859 100644 --- a/bus-mapping/src/evm/opcodes/selfbalance.rs +++ b/bus-mapping/src/evm/opcodes/selfbalance.rs @@ -91,7 +91,7 @@ mod selfbalance_tests { .unwrap(); let call_id = builder.block.txs()[0].calls()[0].call_id; - let callee_address = builder.block.txs()[0].to; + let callee_address = builder.block.txs()[0].tx.to_or_contract_addr(); let self_balance = builder.sdb.get_account(&callee_address).1.balance; assert_eq!( diff --git a/bus-mapping/src/evm/opcodes/sha3.rs b/bus-mapping/src/evm/opcodes/sha3.rs index 47c964b9f4..ca2cc1a430 100644 --- a/bus-mapping/src/evm/opcodes/sha3.rs +++ b/bus-mapping/src/evm/opcodes/sha3.rs @@ -1,13 +1,13 @@ +use super::Opcode; use crate::{ circuit_input_builder::{ CircuitInputStateRef, CopyDataType, CopyEvent, ExecStep, NumberOrHash, }, Error, }; -use eth_types::{GethExecStep, Word, U256}; +use eth_types::{bytecode, Bytecode, GethExecStep, Word, U256}; use ethers_core::utils::keccak256; - -use super::Opcode; +use rand::{rngs::ThreadRng, Rng}; #[derive(Clone, Copy, Debug)] pub(crate) struct Sha3; @@ -40,7 +40,9 @@ impl Opcode for Sha3 { let memory = state .call_ctx()? .memory - .read_chunk(offset.as_usize().into(), size.as_usize().into()); + // Get low Uint64 of offset to generate copy steps. Since offset + // could be Uint64 overflow if length is zero. + .read_chunk(offset.low_u64().into(), size.as_usize().into()); // keccak-256 hash of the given data in memory. let sha3 = keccak256(&memory); @@ -65,8 +67,11 @@ impl Opcode for Sha3 { state.push_copy( &mut exec_step, CopyEvent { - src_addr: offset.as_u64(), - src_addr_end: offset.as_u64() + size.as_u64(), + src_addr: offset.low_u64(), + src_addr_end: offset + .low_u64() + .checked_add(size.as_u64()) + .unwrap_or(u64::MAX), src_type: CopyDataType::Memory, src_id: NumberOrHash::Number(call_id), dst_addr: 0, @@ -82,49 +87,78 @@ impl Opcode for Sha3 { } } -#[cfg(any(feature = "test", test))] -pub mod sha3_tests { - use eth_types::{bytecode, evm_types::OpcodeId, geth_types::GethData, Bytecode, Word}; - use ethers_core::utils::keccak256; - use mock::{ - test_ctx::helpers::{account_0_code_account_1_no_code, tx_from_1_to_0}, - TestContext, - }; - use rand::{random, Rng}; - - use crate::{ - circuit_input_builder::{CircuitsParams, ExecState}, - mock::BlockData, - operation::{MemoryOp, StackOp, RW}, - }; - +/// Generate Sha3 opcode +pub struct Sha3CodeGen { + /// The offset + pub offset: usize, + /// The size + pub size: usize, + data_len: usize, + rng: ThreadRng, +} +impl Sha3CodeGen { + /// Construct with memory less than size + pub fn mem_lt_size(offset: usize, size: usize) -> Self { + let mut rng = rand::thread_rng(); + let data_len = offset + + if size.gt(&0) { + rng.gen_range(0..size) + } else { + 0 + }; + Self { + offset, + size, + data_len, + rng, + } + } + /// Construct with memory equal to size + pub fn mem_eq_size(offset: usize, size: usize) -> Self { + let data_len = offset + size; + Self { + offset, + size, + data_len, + rng: rand::thread_rng(), + } + } + /// Construct with memory greater than size + pub fn mem_gt_size(offset: usize, size: usize) -> Self { + let mut rng = rand::thread_rng(); + let data_len = offset + + size + + if size.gt(&0) { + rng.gen_range(0..size) + } else { + 0 + }; + Self { + offset, + size, + data_len, + rng, + } + } + /// Construct with empty memory + pub fn mem_empty(offset: usize, size: usize) -> Self { + Self { + offset, + size, + data_len: 0, + rng: rand::thread_rng(), + } + } + fn rand_bytes(&mut self) -> Vec { + (0..self.data_len) + .map(|_| self.rng.gen()) + .collect::>() + } /// Generate bytecode for SHA3 opcode after having populated sufficient /// memory given the offset and size arguments for SHA3. - pub fn gen_sha3_code(offset: usize, size: usize, mem_kind: MemoryKind) -> (Bytecode, Vec) { - let mut rng = rand::thread_rng(); - let data_len = match mem_kind { - MemoryKind::LessThanSize => { - offset - + if size.gt(&0) { - rng.gen_range(0..size) - } else { - 0 - } - } - MemoryKind::EqualToSize => offset + size, - MemoryKind::MoreThanSize => { - offset - + size - + if size.gt(&0) { - rng.gen_range(0..size) - } else { - 0 - } - } - MemoryKind::Empty => 0, - }; - let data = rand_bytes(data_len); - let mut memory = Vec::with_capacity(data_len); + pub fn gen_sha3_code(&mut self) -> (Bytecode, Vec) { + let data = self.rand_bytes(); + let mut memory = Vec::with_capacity(self.data_len); // add opcodes to populate memory in the current context. let mut code = Bytecode::default(); @@ -138,39 +172,39 @@ pub mod sha3_tests { mem_chunk.to_vec() }; memory.extend_from_slice(&mem_value); - code.push(32, Word::from_big_endian(&mem_value)); - code.push(32, (32 * i).into()); - code.write_op(OpcodeId::MSTORE); + code.op_mstore(32 * i, Word::from_big_endian(&mem_value)); } // append SHA3 related opcodes at the tail end. let code_tail = bytecode! { - PUSH32(size) - PUSH32(offset) + PUSH32(self.size) + PUSH32(self.offset) SHA3 STOP }; code.append(&code_tail); (code, memory) } +} - /// Memory of a context with respect to the input size to SHA3. - pub enum MemoryKind { - /// Variant defining empty memory. - Empty, - /// Variant defining memory length being less than size. - LessThanSize, - /// Variant defining memory length being equal to size. - EqualToSize, - /// Variant defining memory length being more than size. - MoreThanSize, - } +#[cfg(test)] +pub(crate) mod sha3_tests { + use super::Sha3CodeGen; + use eth_types::{evm_types::OpcodeId, geth_types::GethData, Word}; + use ethers_core::utils::keccak256; + use mock::{ + test_ctx::helpers::{account_0_code_account_1_no_code, tx_from_1_to_0}, + TestContext, + }; - fn rand_bytes(size: usize) -> Vec { - (0..size).map(|_| random()).collect::>() - } + use crate::{ + circuit_input_builder::{CircuitsParams, ExecState}, + mock::BlockData, + operation::{MemoryOp, StackOp, RW}, + }; - fn test_ok(offset: usize, size: usize, mem_kind: MemoryKind) { - let (code, memory) = gen_sha3_code(offset, size, mem_kind); + fn test_ok(mut gen: Sha3CodeGen) { + let (code, memory) = gen.gen_sha3_code(); + let (size, offset) = (gen.size, gen.offset); let memory_len = memory.len(); // The memory that is hashed. @@ -267,9 +301,9 @@ pub mod sha3_tests { #[test] fn sha3_opcode_ok() { - test_ok(0x10, 0x32, MemoryKind::Empty); - test_ok(0x34, 0x44, MemoryKind::LessThanSize); - test_ok(0x222, 0x111, MemoryKind::EqualToSize); - test_ok(0x20, 0x30, MemoryKind::MoreThanSize); + test_ok(Sha3CodeGen::mem_empty(0x10, 0x32)); + test_ok(Sha3CodeGen::mem_lt_size(0x34, 0x44)); + test_ok(Sha3CodeGen::mem_eq_size(0x222, 0x111)); + test_ok(Sha3CodeGen::mem_gt_size(0x20, 0x30)); } } diff --git a/bus-mapping/src/evm/opcodes/sstore.rs b/bus-mapping/src/evm/opcodes/sstore.rs index 53feaf3549..db2393227c 100644 --- a/bus-mapping/src/evm/opcodes/sstore.rs +++ b/bus-mapping/src/evm/opcodes/sstore.rs @@ -102,7 +102,7 @@ impl Opcode for Sstore { TxRefundOp { tx_id: state.tx_ctx.id(), value_prev: state.sdb.refund(), - value: geth_step.refund.0, + value: geth_step.refund, }, )?; diff --git a/bus-mapping/src/evm/opcodes/stop.rs b/bus-mapping/src/evm/opcodes/stop.rs index 93f2db9fc2..4ee0b3f47b 100644 --- a/bus-mapping/src/evm/opcodes/stop.rs +++ b/bus-mapping/src/evm/opcodes/stop.rs @@ -32,16 +32,7 @@ impl Opcode for Stop { 1.into(), ); - if !call.is_root { - // The following part corresponds to - // Instruction.step_state_transition_to_restored_context - // in python spec, and should be reusable among all expected halting opcodes or - // exceptions. - state.gen_restore_context_ops(&mut exec_step, geth_steps)?; - } - - state.handle_return(geth_step)?; - + state.handle_return(&mut exec_step, geth_steps, !call.is_root)?; Ok(vec![exec_step]) } } diff --git a/bus-mapping/src/lib.rs b/bus-mapping/src/lib.rs index ceef2e9fc7..1cf31e1068 100644 --- a/bus-mapping/src/lib.rs +++ b/bus-mapping/src/lib.rs @@ -54,7 +54,6 @@ //! self, address, Address, Word, Hash, U64, GethExecTrace, GethExecStep, geth_types::GethData, bytecode //! }; //! use mock::test_ctx::{TestContext, helpers::*}; -//! use eth_types::evm_types::Gas; //! use bus_mapping::circuit_input_builder::{Block, CircuitInputBuilder}; //! //! let input_trace = r#" @@ -139,7 +138,7 @@ //! let geth_steps: Vec = serde_json::from_str(input_trace).unwrap(); //! let geth_trace = GethExecTrace { //! return_value: "".to_string(), -//! gas: Gas(block.eth_block.transactions[0].gas.as_u64()), +//! gas: block.eth_block.transactions[0].gas.as_u64(), //! failed: false, //! struct_logs: geth_steps, //! }; @@ -213,8 +212,6 @@ //! See: #![cfg_attr(docsrs, feature(doc_cfg))] -// Temporary until we have more of the crate implemented. -#![allow(dead_code)] // We want to have UPPERCASE idents sometimes. #![allow(non_snake_case)] // Catch documentation errors caused by code changes. @@ -234,6 +231,7 @@ pub mod exec_trace; pub(crate) mod geth_errors; pub mod mock; pub mod operation; +pub mod precompile; pub mod rpc; pub mod state_db; pub use error::Error; diff --git a/bus-mapping/src/mock.rs b/bus-mapping/src/mock.rs index 4428994f7e..57ecae2475 100644 --- a/bus-mapping/src/mock.rs +++ b/bus-mapping/src/mock.rs @@ -60,16 +60,8 @@ impl BlockData { } for account in geth_data.accounts { - let code_hash = code_db.insert(account.code.to_vec()); - sdb.set_account( - &account.address, - state_db::Account { - nonce: account.nonce, - balance: account.balance, - storage: account.storage, - code_hash, - }, - ); + code_db.insert(account.code.to_vec()); + sdb.set_account(&account.address, state_db::Account::from(account.clone())); } Self { diff --git a/bus-mapping/src/operation.rs b/bus-mapping/src/operation.rs index 8b39ce7219..0de8e6cff3 100644 --- a/bus-mapping/src/operation.rs +++ b/bus-mapping/src/operation.rs @@ -6,6 +6,9 @@ pub(crate) mod container; pub use container::OperationContainer; pub use eth_types::evm_types::{MemoryAddress, StackAddress}; +use gadgets::impl_expr; +use halo2_proofs::plonk::Expression; +use strum_macros::EnumIter; use core::{cmp::Ordering, fmt, fmt::Debug}; use eth_types::{Address, Word}; @@ -50,6 +53,12 @@ impl From for usize { } } +impl From for u64 { + fn from(addr: RWCounter) -> u64 { + addr.0.try_into().expect("rwc should not overflow") + } +} + impl From for RWCounter { fn from(rwc: usize) -> Self { RWCounter(rwc) @@ -82,10 +91,10 @@ impl RWCounter { } /// Enum used to differenciate between EVM Stack, Memory and Storage operations. -#[derive(Debug, Clone, PartialEq, Eq, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Copy, EnumIter, Hash)] pub enum Target { /// Start is a padding operation. - Start, + Start = 1, /// Means the target of the operation is the Memory. Memory, /// Means the target of the operation is the Stack. @@ -108,6 +117,28 @@ pub enum Target { TxLog, } +impl_expr!(Target); + +impl From for usize { + fn from(value: Target) -> usize { + value as usize + } +} + +impl Target { + /// Returns true if the RwTable operation is reversible + pub fn is_reversible(self) -> bool { + matches!( + self, + Target::TxAccessListAccount + | Target::TxAccessListAccountStorage + | Target::TxRefund + | Target::Account + | Target::Storage + ) + } +} + /// Trait used for Operation Kinds. pub trait Op: Clone + Eq + Ord { diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs new file mode 100644 index 0000000000..f082224e5d --- /dev/null +++ b/bus-mapping/src/precompile.rs @@ -0,0 +1,23 @@ +//! precompile helpers + +use eth_types::Address; +use revm_precompile::{Precompile, Precompiles}; + +/// Check if address is a precompiled or not. +pub fn is_precompiled(address: &Address) -> bool { + Precompiles::berlin() + .get(address.as_fixed_bytes()) + .is_some() +} + +pub(crate) fn execute_precompiled(address: &Address, input: &[u8], gas: u64) -> (Vec, u64) { + let Some(Precompile::Standard(precompile_fn)) = Precompiles::berlin() + .get(address.as_fixed_bytes()) else { + panic!("calling non-exist precompiled contract address") + }; + + match precompile_fn(input, gas) { + Ok((gas_cost, return_value)) => (return_value, gas_cost), + Err(_) => (vec![], gas), + } +} diff --git a/bus-mapping/src/state_db.rs b/bus-mapping/src/state_db.rs index 33a563a424..905e41093b 100644 --- a/bus-mapping/src/state_db.rs +++ b/bus-mapping/src/state_db.rs @@ -1,13 +1,14 @@ //! Implementation of an in-memory key-value database to represent the //! Ethereum State Trie. -use eth_types::{Address, Hash, Word, H256, U256}; +use eth_types::{geth_types, Address, Hash, Word, H256, U256}; use ethers_core::utils::keccak256; use lazy_static::lazy_static; use std::collections::{HashMap, HashSet}; lazy_static! { static ref ACCOUNT_ZERO: Account = Account::zero(); + /// Hash value for empty code hash. static ref EMPTY_CODE_HASH: Hash = CodeDB::hash(&[]); /// bytes of empty code hash, in little endian order. pub static ref EMPTY_CODE_HASH_LE: [u8; 32] = { @@ -43,7 +44,7 @@ impl CodeDB { /// Compute hash of given code. pub fn hash(code: &[u8]) -> Hash { - H256(keccak256(&code)) + H256(keccak256(code)) } /// Code hash of empty code. @@ -57,7 +58,7 @@ impl CodeDB { #[derive(Debug, PartialEq, Eq, Clone)] pub struct Account { /// Nonce - pub nonce: Word, + pub nonce: u64, /// Balance pub balance: Word, /// Storage key-value map @@ -66,11 +67,22 @@ pub struct Account { pub code_hash: Hash, } +impl From for Account { + fn from(account: geth_types::Account) -> Self { + Self { + nonce: account.nonce.as_u64(), + balance: account.balance, + storage: account.storage.clone(), + code_hash: CodeDB::hash(&account.code.to_vec()), + } + } +} + impl Account { /// Return an empty account, with all values set at zero. pub fn zero() -> Self { Self { - nonce: Word::zero(), + nonce: 0, balance: Word::zero(), storage: HashMap::new(), code_hash: *EMPTY_CODE_HASH, @@ -79,7 +91,7 @@ impl Account { /// Return if account is empty or not. pub fn is_empty(&self) -> bool { - self.nonce.is_zero() && self.balance.is_zero() && self.code_hash.eq(&EMPTY_CODE_HASH) + self.nonce == 0 && self.balance.is_zero() && self.code_hash.eq(&EMPTY_CODE_HASH) } } @@ -187,14 +199,14 @@ impl StateDB { /// Get nonce of account with `addr`. pub fn get_nonce(&self, addr: &Address) -> u64 { let (_, account) = self.get_account(addr); - account.nonce.as_u64() + account.nonce } /// Increase nonce of account with `addr` and return the previous value. pub fn increase_nonce(&mut self, addr: &Address) -> u64 { let (_, account) = self.get_account_mut(addr); - let nonce = account.nonce.as_u64(); - account.nonce = account.nonce + 1; + let nonce = account.nonce; + account.nonce += 1; nonce } @@ -291,12 +303,12 @@ mod statedb_tests { let (found, acc) = statedb.get_account_mut(&addr_a); assert!(!found); assert_eq!(acc, &Account::zero()); - acc.nonce = Word::from(100); + acc.nonce = 100; // Get existing account and check nonce let (found, acc) = statedb.get_account(&addr_a); assert!(found); - assert_eq!(acc.nonce, Word::from(100)); + assert_eq!(acc.nonce, 100); // Get non-existing storage key for existing account and set value let (found, value) = statedb.get_storage_mut(&addr_a, &Word::from(2)); @@ -318,7 +330,7 @@ mod statedb_tests { // Get existing account and check nonce let (found, acc) = statedb.get_account(&addr_b); assert!(found); - assert_eq!(acc.nonce, Word::zero()); + assert_eq!(acc.nonce, 0); // Get existing storage key and check value let (found, value) = statedb.get_storage(&addr_b, &Word::from(3)); diff --git a/circuit-benchmarks/Cargo.toml b/circuit-benchmarks/Cargo.toml index 3646c329a5..8f813e749b 100644 --- a/circuit-benchmarks/Cargo.toml +++ b/circuit-benchmarks/Cargo.toml @@ -7,11 +7,10 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_02_02" } +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_04_20" } ark-std = { version = "0.3", features = ["print-trace"] } zkevm-circuits = { path = "../zkevm-circuits", features = ["test"]} -keccak256 = { path = "../keccak256" } -bus-mapping = { path = "../bus-mapping" } +bus-mapping = { path = "../bus-mapping", features = ["test"] } rand_xorshift = "0.3" rand = "0.8" itertools = "0.10" diff --git a/circuit-benchmarks/src/bytecode_circuit.rs b/circuit-benchmarks/src/bytecode_circuit.rs index 0142ce3f9c..42ca583f35 100644 --- a/circuit-benchmarks/src/bytecode_circuit.rs +++ b/circuit-benchmarks/src/bytecode_circuit.rs @@ -23,9 +23,13 @@ mod tests { use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::env::var; - use zkevm_circuits::bytecode_circuit::bytecode_unroller::{unroll, UnrolledBytecode}; - - use zkevm_circuits::bytecode_circuit::circuit::BytecodeCircuit; + use zkevm_circuits::{ + bytecode_circuit::{ + bytecode_unroller::{unroll, UnrolledBytecode}, + TestBytecodeCircuit, + }, + util::SubCircuit, + }; #[cfg_attr(not(feature = "benches"), ignore)] #[test] @@ -45,13 +49,12 @@ mod tests { const MAX_BYTECODE_LEN: usize = 24576; let num_rows = 1 << degree; - const NUM_BLINDING_ROWS: usize = 7 - 1; - let max_bytecode_row_num = num_rows - NUM_BLINDING_ROWS; + let max_bytecode_row_num = num_rows - TestBytecodeCircuit::::unusable_rows(); let bytecode_len = std::cmp::min(MAX_BYTECODE_LEN, max_bytecode_row_num); let bytecodes_num: usize = max_bytecode_row_num / bytecode_len; // Create the circuit - let bytecode_circuit = BytecodeCircuit::::new( + let bytecode_circuit = TestBytecodeCircuit::::new( fillup_codebytes(bytecodes_num, bytecode_len), 2usize.pow(degree), ); @@ -88,7 +91,7 @@ mod tests { Challenge255, XorShiftRng, Blake2bWrite, G1Affine, Challenge255>, - BytecodeCircuit, + TestBytecodeCircuit, >( &general_params, &pk, diff --git a/circuit-benchmarks/src/copy_circuit.rs b/circuit-benchmarks/src/copy_circuit.rs index fc6d57c39e..e08e4aa8fd 100644 --- a/circuit-benchmarks/src/copy_circuit.rs +++ b/circuit-benchmarks/src/copy_circuit.rs @@ -25,7 +25,7 @@ mod tests { use rand_xorshift::XorShiftRng; use std::env::var; use zkevm_circuits::{ - copy_circuit::CopyCircuit, + copy_circuit::TestCopyCircuit, evm_circuit::witness::{block_convert, Block}, util::SubCircuit, }; @@ -52,7 +52,7 @@ mod tests { // Create the circuit let block = generate_full_events_block(degree); - let circuit = CopyCircuit::::new_from_block(&block); + let circuit = TestCopyCircuit::::new_from_block(&block); // Bench setup generation let setup_message = format!("{} {} with degree = {}", BENCHMARK_ID, setup_prfx, degree); @@ -79,7 +79,7 @@ mod tests { Challenge255, XorShiftRng, Blake2bWrite, G1Affine, Challenge255>, - CopyCircuit, + TestCopyCircuit, >(&general_params, &pk, &[circuit], &[], rng, &mut transcript) .expect("proof generation should not fail"); let proof = transcript.finalize(); diff --git a/circuit-benchmarks/src/evm_circuit.rs b/circuit-benchmarks/src/evm_circuit.rs index f055ad5873..aaf887261c 100644 --- a/circuit-benchmarks/src/evm_circuit.rs +++ b/circuit-benchmarks/src/evm_circuit.rs @@ -24,7 +24,7 @@ mod evm_circ_benches { use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::env::var; - use zkevm_circuits::evm_circuit::{witness::block_convert, EvmCircuit}; + use zkevm_circuits::evm_circuit::{witness::block_convert, TestEvmCircuit}; #[cfg_attr(not(feature = "benches"), ignore)] #[test] @@ -56,7 +56,7 @@ mod evm_circ_benches { let block = block_convert(&builder.block, &builder.code_db).unwrap(); - let circuit = EvmCircuit::::new(block); + let circuit = TestEvmCircuit::::new(block); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -87,7 +87,7 @@ mod evm_circ_benches { Challenge255, XorShiftRng, Blake2bWrite, G1Affine, Challenge255>, - EvmCircuit, + TestEvmCircuit, >( &general_params, &pk, diff --git a/circuit-benchmarks/src/exp_circuit.rs b/circuit-benchmarks/src/exp_circuit.rs index 2ad14f3812..35cb68f8a6 100644 --- a/circuit-benchmarks/src/exp_circuit.rs +++ b/circuit-benchmarks/src/exp_circuit.rs @@ -27,7 +27,7 @@ mod tests { use std::env::var; use zkevm_circuits::{ evm_circuit::witness::{block_convert, Block}, - exp_circuit::ExpCircuit, + exp_circuit::TestExpCircuit, }; #[cfg_attr(not(feature = "benches"), ignore)] @@ -50,7 +50,7 @@ mod tests { let base = Word::from(132); let exponent = Word::from(27); let block = generate_full_events_block(degree, base, exponent); - let circuit = ExpCircuit::::new( + let circuit = TestExpCircuit::::new( block.exp_events.clone(), block.circuits_params.max_exp_steps, ); @@ -64,7 +64,7 @@ mod tests { // Bench setup generation let setup_message = format!("{} {} with degree = {}", BENCHMARK_ID, setup_prfx, degree); let start1 = start_timer!(|| setup_message); - let general_params = ParamsKZG::::setup(degree as u32, &mut rng); + let general_params = ParamsKZG::::setup(degree, &mut rng); let verifier_params: ParamsVerifierKZG = general_params.verifier_params().clone(); end_timer!(start1); @@ -86,7 +86,7 @@ mod tests { Challenge255, XorShiftRng, Blake2bWrite, G1Affine, Challenge255>, - ExpCircuit, + TestExpCircuit, >( &general_params, &pk, diff --git a/circuit-benchmarks/src/packed_multi_keccak.rs b/circuit-benchmarks/src/packed_multi_keccak.rs index 1a3f6c4b12..2810fbd984 100644 --- a/circuit-benchmarks/src/packed_multi_keccak.rs +++ b/circuit-benchmarks/src/packed_multi_keccak.rs @@ -21,7 +21,7 @@ mod tests { use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::env::var; - use zkevm_circuits::keccak_circuit::KeccakCircuit; + use zkevm_circuits::{keccak_circuit::TestKeccakCircuit, util::SubCircuit}; #[cfg_attr(not(feature = "benches"), ignore)] #[test] @@ -41,7 +41,10 @@ mod tests { let inputs = vec![(0u8..135).collect::>(); 3]; // Create the circuit. Leave last dozens of rows for blinding. - let circuit = KeccakCircuit::new(2usize.pow(degree) - 64, inputs); + let circuit = TestKeccakCircuit::new( + 2usize.pow(degree) - TestKeccakCircuit::::unusable_rows(), + inputs, + ); // Initialize the polynomial commitment parameters let mut rng = XorShiftRng::from_seed([ @@ -74,7 +77,7 @@ mod tests { Challenge255, XorShiftRng, Blake2bWrite, G1Affine, Challenge255>, - KeccakCircuit, + TestKeccakCircuit, >( &general_params, &pk, diff --git a/circuit-benchmarks/src/pi_circuit.rs b/circuit-benchmarks/src/pi_circuit.rs index 1ca601296c..eb1295e129 100644 --- a/circuit-benchmarks/src/pi_circuit.rs +++ b/circuit-benchmarks/src/pi_circuit.rs @@ -24,7 +24,7 @@ mod tests { use rand_xorshift::XorShiftRng; use std::env::var; use zkevm_circuits::{ - pi_circuit::{PiCircuit, PiTestCircuit, PublicData}, + pi_circuit::{PiCircuit, PublicData}, util::SubCircuit, }; @@ -48,15 +48,10 @@ mod tests { let mut rng = ChaCha20Rng::seed_from_u64(2); let randomness = Fr::random(&mut rng); let rand_rpi = Fr::random(&mut rng); - let public_data = generate_publicdata::(); - let circuit = PiTestCircuit::(PiCircuit::::new( - MAX_TXS, - MAX_CALLDATA, - randomness, - rand_rpi, - public_data, - )); - let public_inputs = circuit.0.instance(); + let public_data = generate_publicdata(MAX_TXS); + let circuit = + PiCircuit::::new(MAX_TXS, MAX_CALLDATA, randomness, rand_rpi, public_data); + let public_inputs = circuit.instance(); let instance: Vec<&[Fr]> = public_inputs.iter().map(|input| &input[..]).collect(); let instances = &[&instance[..]]; @@ -68,7 +63,7 @@ mod tests { // Bench setup generation let setup_message = format!("{} {} with degree = {}", BENCHMARK_ID, setup_prfx, degree); let start1 = start_timer!(|| setup_message); - let general_params = ParamsKZG::::setup(degree as u32, &mut rng); + let general_params = ParamsKZG::::setup(degree, &mut rng); let verifier_params: ParamsVerifierKZG = general_params.verifier_params().clone(); end_timer!(start1); @@ -90,7 +85,7 @@ mod tests { Challenge255, XorShiftRng, Blake2bWrite, G1Affine, Challenge255>, - PiTestCircuit, + PiCircuit, >( &general_params, &pk, @@ -125,12 +120,12 @@ mod tests { end_timer!(start3); } - fn generate_publicdata() -> PublicData { + fn generate_publicdata(max_txs: usize) -> PublicData { let mut public_data = PublicData::default(); let chain_id = 1337u64; public_data.chain_id = Word::from(chain_id); - let n_tx = MAX_TXS; + let n_tx = max_txs; for _ in 0..n_tx { let eth_tx = eth_types::Transaction::from(mock::CORRECT_MOCK_TXS[0].clone()); public_data.transactions.push(eth_tx); diff --git a/circuit-benchmarks/src/super_circuit.rs b/circuit-benchmarks/src/super_circuit.rs index bac05c75e1..a37ffa82d8 100644 --- a/circuit-benchmarks/src/super_circuit.rs +++ b/circuit-benchmarks/src/super_circuit.rs @@ -79,11 +79,9 @@ mod tests { block.sign(&wallets); - const MAX_TXS: usize = 1; - const MAX_CALLDATA: usize = 32; let circuits_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, + max_txs: 1, + max_calldata: 32, max_rws: 256, max_copy_rows: 256, max_exp_steps: 256, @@ -92,7 +90,7 @@ mod tests { max_keccak_rows: 0, }; let (_, circuit, instance, _) = - SuperCircuit::<_, MAX_TXS, MAX_CALLDATA, 0x100>::build(block, circuits_params).unwrap(); + SuperCircuit::build(block, circuits_params, Fr::from(0x100)).unwrap(); let instance_refs: Vec<&[Fr]> = instance.iter().map(|v| &v[..]).collect(); // Bench setup generation @@ -120,7 +118,7 @@ mod tests { Challenge255, ChaChaRng, Blake2bWrite, G1Affine, Challenge255>, - SuperCircuit, + SuperCircuit, >( &general_params, &pk, diff --git a/circuit-benchmarks/src/tx_circuit.rs b/circuit-benchmarks/src/tx_circuit.rs index 193803c937..4aa76da7ad 100644 --- a/circuit-benchmarks/src/tx_circuit.rs +++ b/circuit-benchmarks/src/tx_circuit.rs @@ -55,7 +55,7 @@ mod tests { // Bench setup generation let setup_message = format!("{} {} with degree = {}", BENCHMARK_ID, setup_prfx, degree); let start1 = start_timer!(|| setup_message); - let general_params = ParamsKZG::::setup(degree as u32, &mut rng); + let general_params = ParamsKZG::::setup(degree, &mut rng); let verifier_params: ParamsVerifierKZG = general_params.verifier_params().clone(); end_timer!(start1); diff --git a/eth-types/Cargo.toml b/eth-types/Cargo.toml index 078c1fee99..1050f3efe8 100644 --- a/eth-types/Cargo.toml +++ b/eth-types/Cargo.toml @@ -10,7 +10,7 @@ ethers-core = "0.17.0" ethers-signers = "0.17.0" hex = "0.4" lazy_static = "1.4" -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_02_02" } +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_04_20" } regex = "1.5.4" serde = {version = "1.0.130", features = ["derive"] } serde_json = "1.0.66" @@ -19,7 +19,6 @@ uint = "0.9.1" itertools = "0.10" libsecp256k1 = "0.7" subtle = "2.4" -sha3 = "0.10" num = "0.4" num-bigint = { version = "0.4" } strum_macros = "0.24" diff --git a/eth-types/src/bytecode.rs b/eth-types/src/bytecode.rs index 406a9f51a6..a72b30c682 100644 --- a/eth-types/src/bytecode.rs +++ b/eth-types/src/bytecode.rs @@ -1,6 +1,6 @@ //! EVM byte code generator -use crate::{evm_types::OpcodeId, Bytes, Word}; +use crate::{evm_types::OpcodeId, Bytes, ToWord, Word}; use std::{collections::HashMap, str::FromStr}; /// Error type for Bytecode related failures @@ -95,8 +95,9 @@ impl Bytecode { } /// Push - pub fn push(&mut self, n: u8, value: Word) -> &mut Self { + pub fn push(&mut self, n: u8, value: T) -> &mut Self { debug_assert!((1..=32).contains(&n), "invalid push"); + let value = value.to_word(); // Write the op code self.write_op((OpcodeId::push_n(n)).expect("valid push size")); @@ -148,31 +149,6 @@ impl Bytecode { self } - /// Call a contract - #[allow(clippy::too_many_arguments)] - pub fn call( - &mut self, - gas: Word, - address: Word, - value: Word, - mem_in: Word, - mem_in_size: Word, - mem_out: Word, - mem_out_size: Word, - ) -> &mut Self { - self.append(&crate::bytecode! { - PUSH32(mem_out_size) - PUSH32(mem_out) - PUSH32(mem_in_size) - PUSH32(mem_in) - PUSH32(value) - PUSH32(address) - PUSH32(gas) - CALL - }); - self - } - /// Generate the diassembly pub fn disasm(&self) -> String { let mut asm = String::new(); @@ -209,6 +185,12 @@ impl Bytecode { pub fn iter(&self) -> BytecodeIterator<'_> { BytecodeIterator(self.code.iter()) } + + /// JUMPDEST opcode + pub fn op_jumpdest(&mut self) -> usize { + self.write_op(OpcodeId::JUMPDEST); + self.code.len() + } } /// An ASM entry @@ -335,7 +317,7 @@ macro_rules! bytecode_internal { ($code:ident, $x:ident ($v:expr) $($rest:tt)*) => {{ debug_assert!($crate::evm_types::OpcodeId::$x.is_push(), "invalid push"); let n = $crate::evm_types::OpcodeId::$x.postfix().expect("opcode with postfix"); - $code.push(n, $v.into()); + $code.push(n, $v); $crate::bytecode_internal!($code, $($rest)*); }}; // Default opcode without any inputs @@ -350,12 +332,203 @@ macro_rules! bytecode_internal { $crate::bytecode_internal!($code, $($rest)*); }}; // Function calls - ($code:ident, .$function:ident ($($args:expr),*) $($rest:tt)*) => {{ - $code.$function($($args.into(),)*); + ($code:ident, .$function:ident ($($args:expr),* $(,)?) $($rest:tt)*) => {{ + $code.$function($($args,)*); $crate::bytecode_internal!($code, $($rest)*); }}; } +macro_rules! impl_push_n { + ($($push_n:ident, $n:expr)*) => { + #[allow(missing_docs)] + impl Bytecode { + $( + pub fn $push_n(&mut self, value: T) -> &mut Self { + self.push($n, value) + } + )* + } + }; +} + +impl_push_n! { + op_push1, 1 + op_push2, 2 + op_push3, 3 + op_push4, 4 + op_push5, 5 + op_push6, 6 + op_push7, 7 + op_push8, 8 + op_push9, 9 + op_push10, 10 + op_push11, 11 + op_push12, 12 + op_push13, 13 + op_push14, 14 + op_push15, 15 + op_push16, 16 + op_push17, 17 + op_push18, 18 + op_push19, 19 + op_push20, 20 + op_push21, 21 + op_push22, 22 + op_push23, 23 + op_push24, 24 + op_push25, 25 + op_push26, 26 + op_push27, 27 + op_push28, 28 + op_push29, 29 + op_push30, 30 + op_push31, 31 + op_push32, 32 +} + +macro_rules! impl_other_opcodes_inner { + ($self:ident, ) => {}; + ($self:ident, $arg:ident) => { + $self.op_push32($arg); + }; + ($self:ident, $arg:ident $($tail:ident)+) => { + impl_other_opcodes_inner!($self, $($tail)*); + $self.op_push32($arg); + } +} + +macro_rules! impl_other_opcodes { + ($(($op:ident, $x:ident $(, $arg:ident : $arg_ty:ident)*)),* $(,)?) => { + #[allow(missing_docs)] + #[allow(clippy::too_many_arguments)] + impl Bytecode { + $( + pub fn $op<$( + $arg_ty: ToWord, + )*>(&mut self, $($arg: $arg_ty),*) -> &mut Self { + impl_other_opcodes_inner!(self, $($arg)*); + self.write_op($crate::evm_types::OpcodeId::$x) + } + )* + } + }; +} + +impl_other_opcodes! { + (op_stop, STOP), + (op_add, ADD, a: A, b: B), + (op_mul, MUL, a: A, b: B), + (op_sub, SUB, a: A, b: B), + (op_div, DIV, a: A, b: B), + (op_sdiv, SDIV, a: A, b: B), + (op_mod, MOD, a: A, b: B), + (op_smod, SMOD, a: A, b: B), + (op_addmod, ADDMOD, a: A, b: B, n: N), + (op_mulmod, MULMOD, a: A, b: B, n: N), + (op_exp, EXP, a: A, exponent: B), + (op_signextend, SIGNEXTEND, b: A, x: B), + (op_lt, LT, a: A, b: B), + (op_gt, GT, a: A, b: B), + (op_slt, SLT, a: A, b: B), + (op_sgt, SGT, a: A, b: B), + (op_eq, EQ, a: A, b: B), + (op_iszero, ISZERO, a: A), + (op_and, AND, a: A, b: B), + (op_or, OR, a: A, b: B), + (op_xor, XOR, a: A, b: B), + (op_not, NOT, a: A), + (op_byte, BYTE, i: I, x: X), + (op_shl, SHL, shift: S, value: V), + (op_shr, SHR, shift: S, value: V), + (op_sar, SAR, shift: S, value: V), + (op_sha3, SHA3, offset: O, size: S), + (op_address, ADDRESS), + (op_balance, BALANCE, address: A), + (op_origin, ORIGIN), + (op_caller, CALLER), + (op_callvalue, CALLVALUE), + (op_calldataload, CALLDATALOAD, i: I), + (op_calldatasize, CALLDATASIZE), + (op_calldatacopy, CALLDATACOPY, dest_offset: D, offset: B, size: C), + (op_codesize, CODESIZE), + (op_codecopy, CODECOPY, dest_offset: D, offset: B, size: C), + (op_gasprice, GASPRICE), + (op_extcodesize, EXTCODESIZE, address: A), + (op_extcodecopy, EXTCODECOPY, address: A, dest_offset: D, offset: B, size: C), + (op_returndatasize, RETURNDATASIZE), + (op_returndatacopy, RETURNDATACOPY, dest_offset: D, offset: B, size: C), + (op_extcodehash, EXTCODEHASH, address: A), + (op_blockhash, BLOCKHASH, blocknumber: B), + (op_coinbase, COINBASE), + (op_timestamp, TIMESTAMP), + (op_number, NUMBER), + (op_prevrandao, DIFFICULTY), // alias for DIFFICULTY + (op_difficulty, DIFFICULTY), + (op_gaslimit, GASLIMIT), + (op_chainid, CHAINID), + (op_selfbalance, SELFBALANCE), + // (op_basefee, BASEFEE), ignored + (op_pop, POP), + (op_mload, MLOAD, offset: O), + (op_mstore, MSTORE, offset: O, value: V), + (op_mstore8, MSTORE8, offset: O, value: V), + (op_sload, SLOAD, offset: O), + (op_sstore, SSTORE, offset: O, value: V), + (op_jump, JUMP, counter: C), + (op_jumpi, JUMPI, counter: C), // branch not included + (op_pc, PC), + (op_msize, MSIZE), + (op_gas, GAS), + // (op_jumpdest, JUMPDEST), manually implemented + (op_dup1, DUP1), + (op_dup2, DUP2), + (op_dup3, DUP3), + (op_dup4, DUP4), + (op_dup5, DUP5), + (op_dup6, DUP6), + (op_dup7, DUP7), + (op_dup8, DUP8), + (op_dup9, DUP9), + (op_dup10, DUP10), + (op_dup11, DUP11), + (op_dup12, DUP12), + (op_dup13, DUP13), + (op_dup14, DUP14), + (op_dup15, DUP15), + (op_dup16, DUP16), + (op_swap1, SWAP1), + (op_swap2, SWAP2), + (op_swap3, SWAP3), + (op_swap4, SWAP4), + (op_swap5, SWAP5), + (op_swap6, SWAP6), + (op_swap7, SWAP7), + (op_swap8, SWAP8), + (op_swap9, SWAP9), + (op_swap10, SWAP10), + (op_swap11, SWAP11), + (op_swap12, SWAP12), + (op_swap13, SWAP13), + (op_swap14, SWAP14), + (op_swap15, SWAP15), + (op_swap16, SWAP16), + (op_log0, LOG0, offset: O, size: S), + (op_log1, LOG1, offset: O, size: S, topic1: T1), + (op_log2, LOG2, offset: O, size: S, topic1: T1, topic2: T2), + (op_log3, LOG3, offset: O, size: S, topic1: T1, topic2: T2, topic3: T3), + (op_log4, LOG4, offset: O, size: S, topic1: T1, topic2: T2, topic3: T3, topic4: T4), + (op_create, CREATE, value: V, offset: O, size: S), + (op_call, CALL, gas: G, address: A, value: V, args_offset: AO, args_size: AS, ret_offset: RO, ret_size: RS), + (op_callcode, CALLCODE, gas: G, address: A, value: V, args_offset: AO, args_size: AS, ret_offset: RO, ret_size: RS), + (op_return, RETURN, offset: O, size: S), + (op_delegatecall, DELEGATECALL, gas: G, address: A, args_offset: AO, args_size: AS, ret_offset: RO, ret_size: RS), + (op_create2, CREATE2, value: V, offset: O, size: SI, salt: SA), + (op_staticcall, STATICCALL, gas: G, address: A, args_offset: AO, args_size: AS, ret_offset: RO, ret_size: RS), + (op_revert, REVERT, offset: O, size: S), + // (op_invalid, INVALID), ignored + // (op_selfdestruct, SELFDESTRUCT), ignored +} + #[cfg(test)] mod tests { use super::*; diff --git a/eth-types/src/evm_types.rs b/eth-types/src/evm_types.rs index bea6ef9319..ba1c694967 100644 --- a/eth-types/src/evm_types.rs +++ b/eth-types/src/evm_types.rs @@ -1,7 +1,7 @@ //! Evm types needed for parsing instruction sets as well -use serde::{Deserialize, Serialize}; -use std::fmt; +// use serde::{Deserialize, Serialize}; +// use std::fmt; pub mod gas_utils; pub mod memory; @@ -14,153 +14,76 @@ pub use opcode_ids::OpcodeId; pub use stack::{Stack, StackAddress}; pub use storage::Storage; -/// Wrapper type over `usize` which represents the program counter of the Evm. -#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord)] -pub struct ProgramCounter(pub usize); - -impl fmt::Debug for ProgramCounter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_fmt(format_args!("0x{:06x}", self.0)) - } -} - -impl From for usize { - fn from(addr: ProgramCounter) -> usize { - addr.0 - } -} - -impl From for ProgramCounter { - fn from(pc: usize) -> Self { - ProgramCounter(pc) - } -} - -impl ProgramCounter { - /// Increase Self by one - pub fn inc(&mut self) { - self.0 += 1; - } - - /// Increase Self by one and return the value before the increase. - pub fn inc_pre(&mut self) -> Self { - let pre = *self; - self.inc(); - pre - } -} - -/// Defines the gas left to perate. -#[derive(Default, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Gas(pub u64); - -impl fmt::Debug for Gas { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_fmt(format_args!("{}", self.0)) - } -} - +/// Once per word of the init code when creating a contract. +pub const INIT_CODE_WORD_GAS: u64 = 2; /// Quotient for max refund of gas used pub const MAX_REFUND_QUOTIENT_OF_GAS_USED: usize = 5; /// Gas stipend when CALL or CALLCODE is attached with value. pub const GAS_STIPEND_CALL_WITH_VALUE: u64 = 2300; /// Defines the gas consumption. -#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct GasCost(pub u64); - -impl fmt::Debug for GasCost { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_fmt(format_args!("{}", self.0)) - } -} +pub struct GasCost; impl GasCost { /// Constant cost for free step - pub const ZERO: Self = Self(0); + pub const ZERO: u64 = 0; /// Constant cost for jumpdest step, only takes one gas - pub const ONE: Self = Self(1); + pub const ONE: u64 = 1; /// Constant cost for quick step - pub const QUICK: Self = Self(2); + pub const QUICK: u64 = 2; /// Constant cost for fastest step - pub const FASTEST: Self = Self(3); + pub const FASTEST: u64 = 3; /// Constant cost for fast step - pub const FAST: Self = Self(5); + pub const FAST: u64 = 5; /// Constant cost for mid step - pub const MID: Self = Self(8); + pub const MID: u64 = 8; /// Constant cost for slow step - pub const SLOW: Self = Self(10); + pub const SLOW: u64 = 10; /// Constant cost for ext step - pub const EXT: Self = Self(20); + pub const EXT: u64 = 20; /// Constant cost for SHA3 - pub const SHA3: Self = Self(30); + pub const SHA3: u64 = 30; /// Constant cost for SELFDESTRUCT - pub const SELFDESTRUCT: Self = Self(5000); + pub const SELFDESTRUCT: u64 = 5000; /// Constant cost for CREATE and CREATE2 - pub const CREATE: Self = Self(32000); + pub const CREATE: u64 = 32000; /// Constant cost for copying every word - pub const COPY: Self = Self(3); + pub const COPY: u64 = 3; /// Constant cost for copying every word, specifically in the case of SHA3 /// opcode. - pub const COPY_SHA3: Self = Self(6); + pub const COPY_SHA3: u64 = 6; /// Constant cost for accessing account or storage key - pub const WARM_ACCESS: Self = Self(100); + pub const WARM_ACCESS: u64 = 100; /// Constant cost for a cold SLOAD - pub const COLD_SLOAD: Self = Self(2100); + pub const COLD_SLOAD: u64 = 2100; /// Constant cost for a cold account access - pub const COLD_ACCOUNT_ACCESS: Self = Self(2600); + pub const COLD_ACCOUNT_ACCESS: u64 = 2600; /// SSTORE reentrancy sentry - pub const SSTORE_SENTRY: Self = Self(2300); + pub const SSTORE_SENTRY: u64 = 2300; /// Constant cost for a storage set - pub const SSTORE_SET: Self = Self(20000); + pub const SSTORE_SET: u64 = 20000; /// Constant cost for a storage reset - pub const SSTORE_RESET: Self = Self(2900); + pub const SSTORE_RESET: u64 = 2900; /// Constant cost for a storage clear. EIP-3529 changed it to 4800 from /// 15000. - pub const SSTORE_CLEARS_SCHEDULE: Self = Self(4800); + pub const SSTORE_CLEARS_SCHEDULE: u64 = 4800; /// Constant cost for a non-creation transaction - pub const TX: Self = Self(21000); + pub const TX: u64 = 21000; /// Constant cost for a creation transaction - pub const CREATION_TX: Self = Self(53000); + pub const CREATION_TX: u64 = 53000; /// Constant cost for calling with non-zero value - pub const CALL_WITH_VALUE: Self = Self(9000); + pub const CALL_WITH_VALUE: u64 = 9000; /// Constant cost for turning empty account into non-empty account - pub const NEW_ACCOUNT: Self = Self(25000); + pub const NEW_ACCOUNT: u64 = 25000; /// Cost per byte of deploying a new contract - pub const CODE_DEPOSIT_BYTE_COST: Self = Self(200); + pub const CODE_DEPOSIT_BYTE_COST: u64 = 200; /// Denominator of quadratic part of memory expansion gas cost - pub const MEMORY_EXPANSION_QUAD_DENOMINATOR: Self = Self(512); + pub const MEMORY_EXPANSION_QUAD_DENOMINATOR: u64 = 512; /// Coefficient of linear part of memory expansion gas cost - pub const MEMORY_EXPANSION_LINEAR_COEFF: Self = Self(3); + pub const MEMORY_EXPANSION_LINEAR_COEFF: u64 = 3; /// Constant gas for LOG[0-4] op codes - pub const LOG: Self = Self(375); + pub const LOG: u64 = 375; /// Times ceil exponent byte size for the EXP instruction, EIP-158 changed /// it from 10 to 50. - pub const EXP_BYTE_TIMES: Self = Self(50); -} - -impl GasCost { - /// Returns the `GasCost` as a `u64`. - #[inline] - pub const fn as_u64(&self) -> u64 { - self.0 - } - - /// Returns the `GasCost` as a `usize`. - #[inline] - pub const fn as_usize(&self) -> usize { - self.0 as usize - } -} - -impl From for GasCost { - fn from(cost: u8) -> Self { - GasCost(cost as u64) - } -} - -impl From for GasCost { - fn from(cost: u64) -> Self { - GasCost(cost) - } + pub const EXP_BYTE_TIMES: u64 = 50; } diff --git a/eth-types/src/evm_types/gas_utils.rs b/eth-types/src/evm_types/gas_utils.rs index d1e032d669..18240f9bf4 100644 --- a/eth-types/src/evm_types/gas_utils.rs +++ b/eth-types/src/evm_types/gas_utils.rs @@ -8,11 +8,11 @@ pub fn memory_expansion_gas_cost(curr_memory_word_size: u64, next_memory_word_si if next_memory_word_size == curr_memory_word_size { 0 } else { - GasCost::MEMORY_EXPANSION_LINEAR_COEFF.0 * (next_memory_word_size - curr_memory_word_size) + GasCost::MEMORY_EXPANSION_LINEAR_COEFF * (next_memory_word_size - curr_memory_word_size) + next_memory_word_size * next_memory_word_size - / GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR.0 + / GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR - curr_memory_word_size * curr_memory_word_size - / GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR.0 + / GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR } } @@ -24,8 +24,14 @@ pub fn memory_copier_gas_cost( num_copy_bytes: u64, ) -> u64 { let num_words = (num_copy_bytes + 31) / 32; - num_words * GasCost::COPY.as_u64() - + memory_expansion_gas_cost(curr_memory_word_size, next_memory_word_size) + num_words * GasCost::COPY + + // Note that opcodes with a byte size parameter of 0 will not trigger + // memory expansion, regardless of their offset parameters. + if num_words > 0 { + memory_expansion_gas_cost(curr_memory_word_size, next_memory_word_size) + } else { + 0 + } } /// Calculate EIP 150 gas passed to callee. diff --git a/eth-types/src/evm_types/memory.rs b/eth-types/src/evm_types/memory.rs index 7b0c599a8e..ab0e7e5eef 100644 --- a/eth-types/src/evm_types/memory.rs +++ b/eth-types/src/evm_types/memory.rs @@ -329,17 +329,29 @@ impl Memory { } /// Copy source data to memory. Used in (ext)codecopy/calldatacopy. - pub fn copy_from(&mut self, dst_offset: u64, data: &[u8], data_offset: u64, length: usize) { - // https://github.com/ethereum/go-ethereum/blob/df52967ff6080a27243569020ff64cd956fb8362/core/vm/instructions.go#L312 + pub fn copy_from(&mut self, dst_offset: Word, src_offset: Word, length: Word, data: &[u8]) { + // Reference go-ethereum `opCallDataCopy` function for defails. + // https://github.com/ethereum/go-ethereum/blob/bb4ac2d396de254898a5f44b1ea2086bfe5bd193/core/vm/instructions.go#L299 + + // `length` should be checked for overflow during gas cost calculation. + // Otherwise should return an out of gas error previously. + let length = length.as_usize(); if length != 0 { + // `dst_offset` should be within range if length is non-zero. + // https://github.com/ethereum/go-ethereum/blob/bb4ac2d396de254898a5f44b1ea2086bfe5bd193/core/vm/common.go#L37 + let dst_offset = dst_offset.as_u64(); + + // Reset data offset to the maximum value of Uint64 if overflow. + let src_offset = u64::try_from(src_offset).unwrap_or(u64::MAX); + let minimal_length = dst_offset as usize + length; self.extend_at_least(minimal_length); let mem_starts = dst_offset as usize; - let mem_ends = mem_starts + length as usize; + let mem_ends = mem_starts + length; let dst_slice = &mut self.0[mem_starts..mem_ends]; dst_slice.fill(0); - let data_starts = data_offset as usize; + let data_starts = src_offset as usize; let actual_length = std::cmp::min( length, data.len().checked_sub(data_starts).unwrap_or_default(), @@ -373,10 +385,10 @@ mod memory_tests { let first_usize = 64536usize; // Parsing on both ways works. assert_eq!( - MemoryAddress::from_le_bytes(&first_usize.to_le_bytes())?, - MemoryAddress::from_be_bytes(&first_usize.to_be_bytes())? + MemoryAddress::from_le_bytes(first_usize.to_le_bytes())?, + MemoryAddress::from_be_bytes(first_usize.to_be_bytes())? ); - let addr = MemoryAddress::from_le_bytes(&first_usize.to_le_bytes())?; + let addr = MemoryAddress::from_le_bytes(first_usize.to_le_bytes())?; assert_eq!(addr, MemoryAddress::from(first_usize)); // Little endian export diff --git a/eth-types/src/evm_types/opcode_ids.rs b/eth-types/src/evm_types/opcode_ids.rs index d9192fcae4..dfd60db4ca 100644 --- a/eth-types/src/evm_types/opcode_ids.rs +++ b/eth-types/src/evm_types/opcode_ids.rs @@ -514,7 +514,7 @@ impl OpcodeId { } /// Returns the constant gas cost of `OpcodeId` - pub const fn constant_gas_cost(&self) -> GasCost { + pub const fn constant_gas_cost(&self) -> u64 { match self { OpcodeId::STOP => GasCost::ZERO, OpcodeId::ADD => GasCost::FASTEST, diff --git a/eth-types/src/geth_types.rs b/eth-types/src/geth_types.rs index 818c544163..0b16ab307d 100644 --- a/eth-types/src/geth_types.rs +++ b/eth-types/src/geth_types.rs @@ -1,18 +1,21 @@ //! Types needed for generating Ethereum traces use crate::{ + keccak256, sign_types::{biguint_to_32bytes_le, ct_option_ok_or, recover_pk, SignData, SECP256K1_Q}, AccessList, Address, Block, Bytes, Error, GethExecTrace, Hash, ToBigEndian, ToLittleEndian, - Word, U64, + ToWord, Word, U64, +}; +use ethers_core::{ + types::{transaction::response, NameOrAddress, TransactionRequest}, + utils::get_contract_address, }; -use ethers_core::types::{NameOrAddress, TransactionRequest}; use ethers_signers::{LocalWallet, Signer}; use halo2_proofs::halo2curves::{group::ff::PrimeField, secp256k1}; use num::Integer; use num_bigint::BigUint; use serde::{Serialize, Serializer}; use serde_with::serde_as; -use sha3::{Digest, Keccak256}; use std::collections::HashMap; /// Definition of all of the data related to an account. @@ -21,8 +24,9 @@ use std::collections::HashMap; pub struct Account { /// Address pub address: Address, - /// nonce - pub nonce: Word, + /// Nonce. + /// U64 type is required to serialize into proper hex with 0x prefix + pub nonce: U64, /// Balance pub balance: Word, /// EVM Code @@ -61,7 +65,8 @@ pub struct BlockConstants { pub coinbase: Address, /// time pub timestamp: Word, - /// number + /// Block number + /// U64 type is required to serialize into proper hex with 0x prefix pub number: U64, /// difficulty pub difficulty: Word, @@ -113,11 +118,14 @@ pub struct Transaction { /// Sender address pub from: Address, /// Recipient address (None for contract creation) + /// Avoid direct read from this field. We set this field public to construct the struct pub to: Option
, /// Transaction nonce - pub nonce: Word, + /// U64 type is required to serialize into proper hex with 0x prefix + pub nonce: U64, /// Gas Limit / Supplied gas - pub gas_limit: Word, + /// U64 type is required to serialize into proper hex with 0x prefix + pub gas_limit: U64, /// Transfered value pub value: Word, /// Gas Price @@ -146,8 +154,8 @@ impl From<&Transaction> for crate::Transaction { crate::Transaction { from: tx.from, to: tx.to, - nonce: tx.nonce, - gas: tx.gas_limit, + nonce: tx.nonce.to_word(), + gas: tx.gas_limit.to_word(), value: tx.value, gas_price: Some(tx.gas_price), max_priority_fee_per_gas: Some(tx.gas_fee_cap), @@ -167,8 +175,8 @@ impl From<&crate::Transaction> for Transaction { Transaction { from: tx.from, to: tx.to, - nonce: tx.nonce, - gas_limit: tx.gas, + nonce: tx.nonce.as_u64().into(), + gas_limit: tx.gas.as_u64().into(), value: tx.value, gas_price: tx.gas_price.unwrap_or_default(), gas_fee_cap: tx.max_priority_fee_per_gas.unwrap_or_default(), @@ -187,11 +195,11 @@ impl From<&Transaction> for TransactionRequest { TransactionRequest { from: Some(tx.from), to: tx.to.map(NameOrAddress::Address), - gas: Some(tx.gas_limit), + gas: Some(tx.gas_limit.to_word()), gas_price: Some(tx.gas_price), value: Some(tx.value), data: Some(tx.call_data.clone()), - nonce: Some(tx.nonce), + nonce: Some(tx.nonce.to_word()), ..Default::default() } } @@ -213,11 +221,7 @@ impl Transaction { // msg = rlp([nonce, gasPrice, gas, to, value, data, sig_v, r, s]) let req: TransactionRequest = self.into(); let msg = req.chain_id(chain_id).rlp(); - let msg_hash: [u8; 32] = Keccak256::digest(&msg) - .as_slice() - .to_vec() - .try_into() - .expect("hash length isn't 32 bytes"); + let msg_hash: [u8; 32] = keccak256(&msg); let v = self .v .checked_sub(35 + chain_id * 2) @@ -237,6 +241,53 @@ impl Transaction { msg_hash, }) } + + /// Compute call data gas cost from call data + pub fn call_data_gas_cost(&self) -> u64 { + self.call_data + .iter() + .fold(0, |acc, byte| acc + if *byte == 0 { 4 } else { 16 }) + } + + /// Get the "to" address. If `to` is None then zero adddress + pub fn to_or_zero(&self) -> Address { + self.to.unwrap_or_default() + } + /// Get the "to" address. If `to` is None then compute contract adddress + pub fn to_or_contract_addr(&self) -> Address { + self.to + .unwrap_or_else(|| get_contract_address(self.from, self.nonce.to_word())) + } + /// Determine if this transaction is a contract create transaction + pub fn is_create(&self) -> bool { + self.to.is_none() + } + + /// Convert to transaction response + pub fn to_response( + &self, + transaction_index: U64, + chain_id: Word, + block_number: U64, + ) -> response::Transaction { + response::Transaction { + from: self.from, + to: self.to, + value: self.value, + input: self.call_data.clone(), + gas_price: Some(self.gas_price), + access_list: self.access_list.clone(), + nonce: self.nonce.to_word(), + gas: self.gas_limit.to_word(), + transaction_index: Some(transaction_index), + r: self.r, + s: self.s, + v: U64::from(self.v), + block_number: Some(block_number), + chain_id: Some(chain_id), + ..response::Transaction::default() + } + } } /// GethData is a type that contains all the information of a Ethereum block diff --git a/keccak256/src/plain.rs b/eth-types/src/keccak.rs similarity index 59% rename from keccak256/src/plain.rs rename to eth-types/src/keccak.rs index 8540479505..3bef09e94a 100644 --- a/keccak256/src/plain.rs +++ b/eth-types/src/keccak.rs @@ -1,6 +1,51 @@ -use crate::common::*; +//! Plain keccak256 implementation + use itertools::Itertools; +/// The State is a 5x5 matrix of 64 bit lanes. +type State = [[u64; 5]; 5]; + +/// The number of rounds for the 1600 bits permutation used in Keccak-256. See [here](https://github.com/Legrandin/pycryptodome/blob/016252bde04456614b68d4e4e8798bc124d91e7a/src/keccak.c#L230) +const PERMUTATION: usize = 24; + +/// The Keccak [round constants](https://github.com/Legrandin/pycryptodome/blob/016252bde04456614b68d4e4e8798bc124d91e7a/src/keccak.c#L257-L282) +static ROUND_CONSTANTS: [u64; PERMUTATION] = [ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, +]; + +/// The Keccak [rotation offsets](https://github.com/Legrandin/pycryptodome/blob/016252bde04456614b68d4e4e8798bc124d91e7a/src/keccak.c#L232-L255) +static ROTATION_CONSTANTS: [[u32; 5]; 5] = [ + [0, 36, 3, 41, 18], + [1, 44, 10, 45, 2], + [62, 6, 43, 15, 61], + [28, 55, 25, 21, 56], + [27, 20, 39, 8, 14], +]; + +/// The main keccak object pub struct Keccak { state: State, sponge: Sponge, @@ -21,6 +66,7 @@ impl Default for Keccak { } impl Keccak { + /// Take more input bytes to the state pub fn update(&mut self, input: &[u8]) { let rate = self.sponge.rate; // offset for `input` @@ -67,10 +113,10 @@ impl Keccak { } #[derive(Default)] -pub struct KeccakF {} +struct KeccakF {} impl KeccakF { - pub fn permutations(&self, a: &mut State) { + fn permutations(&self, a: &mut State) { for rc in ROUND_CONSTANTS.iter().take(PERMUTATION) { *a = KeccakF::round_b(*a, *rc); } @@ -84,7 +130,7 @@ impl KeccakF { KeccakF::iota(s4, rc) } - pub fn theta(a: State) -> State { + fn theta(a: State) -> State { let mut c: [u64; 5] = [0; 5]; let mut out: State = [[0; 5]; 5]; @@ -98,7 +144,7 @@ impl KeccakF { out } - pub fn rho(a: State) -> State { + fn rho(a: State) -> State { let mut out: State = [[0; 5]; 5]; for (x, y) in (0..5).cartesian_product(0..5) { out[x][y] = a[x][y].rotate_left(ROTATION_CONSTANTS[x][y]); @@ -106,7 +152,7 @@ impl KeccakF { out } - pub fn pi(a: State) -> State { + fn pi(a: State) -> State { let mut out: State = [[0; 5]; 5]; for (x, y) in (0..5).cartesian_product(0..5) { @@ -115,7 +161,7 @@ impl KeccakF { out } - pub fn xi(a: State) -> State { + fn xi(a: State) -> State { let mut out: State = [[0; 5]; 5]; for (x, y) in (0..5).cartesian_product(0..5) { out[x][y] = a[x][y] ^ (!a[(x + 1) % 5][y] & a[(x + 2) % 5][y]); @@ -123,21 +169,21 @@ impl KeccakF { out } - pub fn iota(a: State, rc: u64) -> State { + fn iota(a: State, rc: u64) -> State { let mut out = a; out[0][0] ^= rc; out } } -pub struct Sponge { +struct Sponge { rate: usize, capacity: usize, keccak_f: KeccakF, } impl Sponge { - pub fn new(rate: usize, capacity: usize) -> Sponge { + fn new(rate: usize, capacity: usize) -> Sponge { Sponge { rate, capacity, @@ -145,7 +191,7 @@ impl Sponge { } } - pub fn absorb(&self, state: &mut State, message: &[u8]) { + fn absorb(&self, state: &mut State, message: &[u8]) { debug_assert!( message.len() % self.rate == 0, "Message is not divisible entirely by bytes rate" @@ -173,7 +219,7 @@ impl Sponge { } } - pub fn squeeze(&self, state: &mut State) -> Vec { + fn squeeze(&self, state: &mut State) -> Vec { let mut output: Vec = vec![]; let output_len: usize = self.capacity / 2; @@ -206,64 +252,57 @@ impl Sponge { words } } -#[cfg(test)] -fn keccak256(msg: &[u8]) -> Vec { - let mut keccak = Keccak::default(); - keccak.update(msg); - let a = keccak.digest(); +/// Convinient method to get 32 bytes digest +pub fn keccak256(msg: &[u8]) -> [u8; 32] { let mut keccak = Keccak::default(); - for byte in msg { - keccak.update(&[*byte]); - } - let b = keccak.digest(); - - assert_eq!(a, b); - - a + keccak.update(msg); + keccak.digest().try_into().expect("keccak outputs 32 bytes") } #[test] -fn test_empty_input() { - let output = [ - 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, - 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112, - ]; - assert_eq!(keccak256(&[]), output); -} +fn test_keccak256() { + fn keccak256(msg: &[u8]) -> Vec { + let mut keccak = Keccak::default(); + keccak.update(msg); + let a = keccak.digest(); + + let mut keccak = Keccak::default(); + for byte in msg { + keccak.update(&[*byte]); + } + let b = keccak.digest(); -#[test] -fn test_short_input() { - let output = [ - 56, 209, 138, 203, 103, 210, 92, 139, 185, 148, 39, 100, 182, 47, 24, 225, 112, 84, 246, - 106, 129, 123, 212, 41, 84, 35, 173, 249, 237, 152, 135, 62, - ]; - assert_eq!(keccak256(&[102, 111, 111, 98, 97, 114]), output); -} + assert_eq!(a, b); -#[test] -fn test_long_input() { - let input = [ - 65, 108, 105, 99, 101, 32, 119, 97, 115, 32, 98, 101, 103, 105, 110, 110, 105, 110, 103, - 32, 116, 111, 32, 103, 101, 116, 32, 118, 101, 114, 121, 32, 116, 105, 114, 101, 100, 32, - 111, 102, 32, 115, 105, 116, 116, 105, 110, 103, 32, 98, 121, 32, 104, 101, 114, 32, 115, - 105, 115, 116, 101, 114, 32, 111, 110, 32, 116, 104, 101, 32, 98, 97, 110, 107, 44, 32, 97, - 110, 100, 32, 111, 102, 32, 104, 97, 118, 105, 110, 103, 32, 110, 111, 116, 104, 105, 110, - 103, 32, 116, 111, 32, 100, 111, 58, 32, 111, 110, 99, 101, 32, 111, 114, 32, 116, 119, - 105, 99, 101, 32, 115, 104, 101, 32, 104, 97, 100, 32, 112, 101, 101, 112, 101, 100, 32, - 105, 110, 116, 111, 32, 116, 104, 101, 32, 98, 111, 111, 107, 32, 104, 101, 114, 32, 115, - 105, 115, 116, 101, 114, 32, 119, 97, 115, 32, 114, 101, 97, 100, 105, 110, 103, 44, 32, - 98, 117, 116, 32, 105, 116, 32, 104, 97, 100, 32, 110, 111, 32, 112, 105, 99, 116, 117, - 114, 101, 115, 32, 111, 114, 32, 99, 111, 110, 118, 101, 114, 115, 97, 116, 105, 111, 110, - 115, 32, 105, 110, 32, 105, 116, 44, 32, 97, 110, 100, 32, 119, 104, 97, 116, 32, 105, 115, - 32, 116, 104, 101, 32, 117, 115, 101, 32, 111, 102, 32, 97, 32, 98, 111, 111, 107, 44, 32, - 116, 104, 111, 117, 103, 104, 116, 32, 65, 108, 105, 99, 101, 32, 119, 105, 116, 104, 111, - 117, 116, 32, 112, 105, 99, 116, 117, 114, 101, 115, 32, 111, 114, 32, 99, 111, 110, 118, - 101, 114, 115, 97, 116, 105, 111, 110, 115, 63, - ]; - let output = [ - 60, 227, 142, 8, 143, 135, 108, 85, 13, 254, 190, 58, 30, 106, 153, 194, 188, 6, 208, 49, - 16, 102, 150, 120, 100, 130, 224, 177, 64, 98, 53, 252, + a + } + let pairs = [ + ( + "", + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + ), + ( + "666f6f626172", + "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e", + ), + ( + "416c6963652077617320626567696e6e696e6720746f20676574207665727920\ + 7469726564206f662073697474696e672062792068657220736973746572206f\ + 6e207468652062616e6b2c20616e64206f6620686176696e67206e6f7468696e\ + 6720746f20646f3a206f6e6365206f7220747769636520736865206861642070\ + 656570656420696e746f2074686520626f6f6b20686572207369737465722077\ + 61732072656164696e672c2062757420697420686164206e6f20706963747572\ + 6573206f7220636f6e766572736174696f6e7320696e2069742c20616e642077\ + 6861742069732074686520757365206f66206120626f6f6b2c2074686f756768\ + 7420416c69636520776974686f7574207069637475726573206f7220636f6e76\ + 6572736174696f6e733f", + "3ce38e088f876c550dfebe3a1e6a99c2bc06d031106696786482e0b1406235fc", + ), ]; - assert_eq!(keccak256(&input), output); + for (input, output) in pairs { + let input = hex::decode(input).unwrap(); + let output = hex::decode(output).unwrap(); + assert_eq!(keccak256(&input), output); + } } diff --git a/eth-types/src/lib.rs b/eth-types/src/lib.rs index b59f614f0c..d201028b9f 100644 --- a/eth-types/src/lib.rs +++ b/eth-types/src/lib.rs @@ -1,8 +1,6 @@ //! Ethereum and Evm types used to deserialize responses from web3 / geth. #![cfg_attr(docsrs, feature(doc_cfg))] -// Temporary until we have more of the crate implemented. -#![allow(dead_code)] // We want to have UPPERCASE idents sometimes. #![allow(non_snake_case)] // Catch documentation errors caused by code changes. @@ -20,21 +18,18 @@ pub mod error; pub mod bytecode; pub mod evm_types; pub mod geth_types; +pub mod keccak; pub mod sign_types; +pub use keccak::{keccak256, Keccak}; pub use bytecode::Bytecode; pub use error::Error; -use halo2_proofs::{ - arithmetic::{Field as Halo2Field, FieldExt}, - halo2curves::{ - bn256::{Fq, Fr}, - group::ff::PrimeField, - }, +use halo2_proofs::halo2curves::{ + bn256::{Fq, Fr}, + ff::{Field as Halo2Field, FromUniformBytes, PrimeField}, }; -use crate::evm_types::{ - memory::Memory, stack::Stack, storage::Storage, Gas, GasCost, OpcodeId, ProgramCounter, -}; +use crate::evm_types::{memory::Memory, stack::Stack, storage::Storage, OpcodeId}; use ethers_core::types; pub use ethers_core::{ abi::ethereum_types::{BigEndianHash, U512}, @@ -47,9 +42,28 @@ pub use ethers_core::{ use serde::{de, Deserialize, Serialize}; use std::{collections::HashMap, fmt, str::FromStr}; -/// Trait used to reduce verbosity with the declaration of the [`FieldExt`] +/// Trait used to reduce verbosity with the declaration of the [`PrimeField`] /// trait and its repr. -pub trait Field: FieldExt + Halo2Field + PrimeField {} +pub trait Field: Halo2Field + PrimeField + FromUniformBytes<64> + Ord { + /// Gets the lower 128 bits of this field element when expressed + /// canonically. + fn get_lower_128(&self) -> u128 { + let bytes = self.to_repr(); + bytes[..16] + .iter() + .rev() + .fold(0u128, |acc, value| acc * 256u128 + *value as u128) + } + /// Gets the lower 32 bits of this field element when expressed + /// canonically. + fn get_lower_32(&self) -> u32 { + let bytes = self.to_repr(); + bytes[..4] + .iter() + .rev() + .fold(0u32, |acc, value| acc * 256u32 + *value as u32) + } +} // Impl custom `Field` trait for BN256 Fr to be used and consistent with the // rest of the workspace. @@ -202,6 +216,12 @@ impl ToWord for u64 { } } +impl ToWord for u128 { + fn to_word(&self) -> Word { + Word::from(*self) + } +} + impl ToWord for usize { fn to_word(&self) -> Word { u64::try_from(*self) @@ -210,6 +230,29 @@ impl ToWord for usize { } } +impl ToWord for i32 { + fn to_word(&self) -> Word { + let value = Word::from(self.unsigned_abs() as u64); + if self.is_negative() { + value.overflowing_neg().0 + } else { + value + } + } +} + +impl ToWord for U64 { + fn to_word(&self) -> Word { + self.as_u64().into() + } +} + +impl ToWord for Word { + fn to_word(&self) -> Word { + *self + } +} + impl ToScalar for Address { fn to_scalar(&self) -> Option { let mut bytes = [0u8; 32]; @@ -259,7 +302,7 @@ pub struct EIP1186ProofResponse { /// The hash of the code of the account pub code_hash: H256, /// The nonce of the account - pub nonce: U256, + pub nonce: U64, /// SHA3 of the StorageRoot pub storage_hash: H256, /// Array of rlp-serialized MerkleTree-Nodes @@ -271,13 +314,13 @@ pub struct EIP1186ProofResponse { #[derive(Deserialize)] #[doc(hidden)] struct GethExecStepInternal { - pc: ProgramCounter, + pc: u64, op: OpcodeId, - gas: Gas, + gas: u64, #[serde(default)] - refund: Gas, + refund: u64, #[serde(rename = "gasCost")] - gas_cost: GasCost, + gas_cost: u64, depth: u16, error: Option, // stack is in hex 0x prefixed @@ -295,11 +338,11 @@ struct GethExecStepInternal { #[derive(Clone, Eq, PartialEq, Serialize)] #[doc(hidden)] pub struct GethExecStep { - pub pc: ProgramCounter, + pub pc: u64, pub op: OpcodeId, - pub gas: Gas, - pub gas_cost: GasCost, - pub refund: Gas, + pub gas: u64, + pub gas_cost: u64, + pub refund: u64, pub depth: u16, pub error: Option, // stack is in hex 0x prefixed @@ -332,10 +375,10 @@ impl<'a> fmt::Debug for DebugWord<'a> { impl fmt::Debug for GethExecStep { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Step") - .field("pc", &format_args!("0x{:04x}", self.pc.0)) + .field("pc", &format_args!("0x{:04x}", self.pc)) .field("op", &self.op) - .field("gas", &format_args!("{}", self.gas.0)) - .field("gas_cost", &format_args!("{}", self.gas_cost.0)) + .field("gas", &format_args!("{}", self.gas)) + .field("gas_cost", &format_args!("{}", self.gas_cost)) .field("depth", &self.depth) .field("error", &self.error) .field("stack", &self.stack) @@ -400,7 +443,7 @@ pub struct ResultGethExecTrace { #[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)] pub struct GethExecTrace { /// Used gas - pub gas: Gas, + pub gas: u64, /// True when the transaction has failed. pub failed: bool, /// Return value of execution which is a hex encoded byte array @@ -516,16 +559,16 @@ mod tests { assert_eq!( trace, GethExecTrace { - gas: Gas(26809), + gas: 26809, failed: false, return_value: "".to_owned(), struct_logs: vec![ GethExecStep { - pc: ProgramCounter(0), + pc: 0, op: OpcodeId::PUSH1, - gas: Gas(22705), - refund: Gas(0), - gas_cost: GasCost(3), + gas: 22705, + refund: 0, + gas_cost: 3, depth: 1, error: None, stack: Stack::new(), @@ -533,11 +576,11 @@ mod tests { memory: Memory::new(), }, GethExecStep { - pc: ProgramCounter(163), + pc: 163, op: OpcodeId::SLOAD, - gas: Gas(5217), - refund: Gas(0), - gas_cost: GasCost(2100), + gas: 5217, + refund: 0, + gas_cost: 2100, depth: 1, error: None, stack: Stack(vec![word!("0x1003e2d2"), word!("0x2a"), word!("0x0")]), @@ -545,11 +588,11 @@ mod tests { memory: Memory::from(vec![word!("0x0"), word!("0x0"), word!("0x080")]), }, GethExecStep { - pc: ProgramCounter(189), + pc: 189, op: OpcodeId::SHA3, - gas: Gas(178805), - refund: Gas(0), - gas_cost: GasCost(42), + gas: 178805, + refund: 0, + gas_cost: 42, depth: 1, error: None, stack: Stack(vec![ diff --git a/eth-types/src/sign_types.rs b/eth-types/src/sign_types.rs index f6bee6980b..49df1b1531 100644 --- a/eth-types/src/sign_types.rs +++ b/eth-types/src/sign_types.rs @@ -2,10 +2,10 @@ use crate::{ToBigEndian, Word}; use halo2_proofs::{ - arithmetic::{CurveAffine, FieldExt}, + arithmetic::{CurveAffine, Field}, halo2curves::{ group::{ - ff::{Field as GroupField, PrimeField}, + ff::{FromUniformBytes, PrimeField}, Curve, }, secp256k1::{self, Secp256k1Affine}, @@ -36,7 +36,7 @@ pub fn sign( let mut x_bytes = [0u8; 64]; x_bytes[..32].copy_from_slice(&x_repr[..]); - let sig_r = secp256k1::Fq::from_bytes_wide(&x_bytes); // get x cordinate (E::Base) on E::Scalar + let sig_r = secp256k1::Fq::from_uniform_bytes(&x_bytes); // get x cordinate (E::Base) on E::Scalar let sig_s = randomness_inv * (msg_hash + sig_r * sk); (sig_r, sig_s) } @@ -56,11 +56,11 @@ pub struct SignData { lazy_static! { static ref SIGN_DATA_DEFAULT: SignData = { let generator = Secp256k1Affine::generator(); - let sk = secp256k1::Fq::one(); + let sk = secp256k1::Fq::ONE; let pk = generator * sk; let pk = pk.to_affine(); - let msg_hash = secp256k1::Fq::one(); - let randomness = secp256k1::Fq::one(); + let msg_hash = secp256k1::Fq::ONE; + let randomness = secp256k1::Fq::ONE; let (sig_r, sig_s) = sign(randomness, sk, msg_hash); SignData { @@ -123,7 +123,7 @@ pub fn recover_pk( lazy_static! { /// Secp256k1 Curve Scalar. Referece: Section 2.4.1 (parameter `n`) in "SEC 2: Recommended /// Elliptic Curve Domain Parameters" document at http://www.secg.org/sec2-v2.pdf - pub static ref SECP256K1_Q: BigUint = BigUint::from_bytes_le(&(secp256k1::Fq::zero() - secp256k1::Fq::one()).to_repr()) + 1u64; + pub static ref SECP256K1_Q: BigUint = BigUint::from_bytes_le(&(secp256k1::Fq::ZERO - secp256k1::Fq::ONE).to_repr()) + 1u64; } /// Helper function to convert a `CtOption` into an `Result`. Similar to diff --git a/external-tracer/src/lib.rs b/external-tracer/src/lib.rs index 678bc21e33..138f0fd2dd 100644 --- a/external-tracer/src/lib.rs +++ b/external-tracer/src/lib.rs @@ -27,18 +27,15 @@ pub struct TraceConfig { /// Configuration structure for `logger.Config` #[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "PascalCase")] pub struct LoggerConfig { /// enable memory capture - #[serde(rename = "EnableMemory")] pub enable_memory: bool, /// disable stack capture - #[serde(rename = "DisableStack")] pub disable_stack: bool, /// disable storage capture - #[serde(rename = "DisableStorage")] pub disable_storage: bool, /// enable return data capture - #[serde(rename = "EnableReturnData")] pub enable_return_data: bool, } diff --git a/gadgets/Cargo.toml b/gadgets/Cargo.toml index 1ae6a29100..be8bf9d550 100644 --- a/gadgets/Cargo.toml +++ b/gadgets/Cargo.toml @@ -6,7 +6,7 @@ authors = ["The appliedzkp team"] license = "MIT OR Apache-2.0" [dependencies] -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_02_02" } +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_04_20" } sha3 = "0.7.2" eth-types = { path = "../eth-types" } digest = "0.7.6" diff --git a/gadgets/src/batched_is_zero.rs b/gadgets/src/batched_is_zero.rs index f7db1c8413..6f4ff461a9 100644 --- a/gadgets/src/batched_is_zero.rs +++ b/gadgets/src/batched_is_zero.rs @@ -99,9 +99,9 @@ impl BatchedIsZeroChip { .iter() .find_map(|value| Option::::from(value.invert())) { - (F::zero(), inverse) + (F::ZERO, inverse) } else { - (F::one(), F::zero()) + (F::ONE, F::ZERO) } }); @@ -158,6 +158,7 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -216,7 +217,7 @@ mod test { || "expect_is_zero", config.expect_is_zero, 0, - || Value::known(F::from(*expect_is_zero)), + || Value::known(F::from(*expect_is_zero as u64)), )?; for (value_column, value) in config.values.iter().zip(values.iter()) { region.assign_advice( diff --git a/gadgets/src/binary_number.rs b/gadgets/src/binary_number.rs index 2005e09405..cb5099504b 100644 --- a/gadgets/src/binary_number.rs +++ b/gadgets/src/binary_number.rs @@ -190,7 +190,7 @@ where || format!("binary number {:?}", column), column, offset, - || Value::known(F::from(bit)), + || Value::known(F::from(bit as u64)), )?; } Ok(()) diff --git a/gadgets/src/evm_word.rs b/gadgets/src/evm_word.rs deleted file mode 100644 index e3fa344f88..0000000000 --- a/gadgets/src/evm_word.rs +++ /dev/null @@ -1,275 +0,0 @@ -//! An EVM word (256 bits) is represented as a linear combination of 32 bytes: -//! `encode(word) = b_0 + r * b_1 + ... + r^{31} * b_{31}`, -//! where `word` is a 256-bit word, b_i's are bytes, and `r` is a random factor. -//! This helper returns an expression of `encode(word)`. -//! -//! In the zkevm circuit, this `encode(word)` expression will not be directly -//! looked up. Instead, it will be folded into the bus mapping lookup. - -use crate::Variable; -use digest::{FixedOutput, Input}; -use eth_types::Field; -use halo2_proofs::{ - circuit::{Region, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector}, - poly::Rotation, -}; -use sha3::{Digest, Keccak256}; - -#[cfg(test)] -use halo2_proofs::circuit::Layouter; - -// TODO: Move into crate-level `constants` file. -/// r = hash([0, 1, ..., 255]) -pub fn r() -> F { - let mut hasher = Keccak256::new(); - for byte in 0..=u8::MAX { - hasher.process(&[byte]); - } - let mut r = [0; 64]; - r[..32].copy_from_slice(hasher.fixed_result().as_slice()); - F::from_bytes_wide(&r) -} - -/// Returns encoding of big-endian representation of a 256-bit word. -pub fn encode(vals: impl Iterator, r: F) -> F { - vals.fold(F::zero(), |acc, val| { - let byte = F::from(val as u64); - acc * r + byte - }) -} - -/// A 256-bit word represented in the circuit as 32 bytes. -pub struct Word([Variable; 32]); - -#[allow(dead_code)] -/// Configuration structure used to constraint. generate and assign an EVM Word -/// inside a circuit. -#[derive(Clone, Debug)] -pub struct WordConfig { - /// Randomness used to compress the word encoding. - r: F, - /// Selector to toggle the word encoding gate. - // TODO: This may be replaced by a synthetic selector. - q_encode: Selector, - /// Advice columns used to witness the byte representation of the word. - bytes: [Column; 32], - /// Fixed column containing all possible 8-bit values. This is used in - /// a lookup argument to range-constrain each byte. - byte_lookup: Column, - /// Expression representing `encode(word)`. - encode_word_expr: Expression, -} - -impl WordConfig { - /// Sets up the configuration of the config by creating the required columns - /// & selectors and defining the constraints that take part when using a - /// Word. - pub fn configure( - meta: &mut ConstraintSystem, - r: F, - q_encode: Selector, - bytes: [Column; 32], - byte_lookup: Column, - ) -> Self { - // Expression representing `encode(word)`. - let mut encode_word_expr = Expression::Constant(F::zero()); - - // Lookup each byte in the witnessed word representation to - // range-constrain it to 8 bits. - for byte in bytes.iter().rev() { - meta.lookup_any("Word byte for range", |meta| { - let q_encode = meta.query_selector(q_encode); - let r = Expression::Constant(r); - let byte = meta.query_advice(*byte, Rotation::cur()); - let byte_lookup = meta.query_fixed(byte_lookup, Rotation::cur()); - - // Update encode_word_expr. - encode_word_expr = encode_word_expr.clone() * r + byte.clone(); - - vec![(q_encode * byte, byte_lookup)] - }); - } - - Self { - r, - q_encode, - bytes, - byte_lookup, - encode_word_expr, - } - } - - /// Loads the 8-bit lookup table. - /// NB: This is only used in tests, since the zkevm circuit will load a - /// global 8-bit table. - #[cfg(test)] - pub fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { - layouter.assign_region( - || "8-bit table", - |mut meta| { - for byte in 0..=u8::MAX { - meta.assign_fixed( - || format!("load {}", byte), - self.byte_lookup, - byte.into(), - || Value::known(F::from(byte as u64)), - )?; - } - - Ok(()) - }, - ) - } - - /// Assigns the byte decomposition of a word to the `bytes` advice columns. - pub fn assign_word( - &self, - region: &mut Region<'_, F>, - offset: usize, - word: [Value; 32], - ) -> Result, Error> { - let mut bytes: Vec> = Vec::with_capacity(32); - - for (idx, (byte, column)) in word.iter().zip(self.bytes.iter()).enumerate() { - // TODO: We will likely enable this selector outside of the helper. - self.q_encode.enable(region, offset)?; - - let byte_field_elem = byte.map(|byte| F::from(byte as u64)); - let cell = region.assign_advice( - || format!("assign byte {}", idx), - *column, - offset, - || byte_field_elem, - )?; - - bytes.push(Variable::new(cell, *byte)); - } - - Ok(Word(bytes.try_into().unwrap())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use halo2_proofs::{ - arithmetic::Field as Halo2Field, - circuit::SimpleFloorPlanner, - dev::{FailureLocation, MockProver, VerifyFailure}, - halo2curves::{bn256::Fr as Fp, group::ff::PrimeField}, - plonk::{Circuit, Instance}, - }; - use rand::SeedableRng; - use rand_xorshift::XorShiftRng; - use std::marker::PhantomData; - - #[test] - fn evm_word() { - #[derive(Default)] - struct MyCircuit { - word: [Value; 32], - _marker: PhantomData, - } - - impl Circuit for MyCircuit { - // Introduce an additional instance column here to test lookups - // with public inputs. This is analogous to the bus mapping - // commitment which will be provided as public inputs. - type Config = (WordConfig, Column); - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let r = r(); - - let q_encode = meta.complex_selector(); - - let bytes: [Column; 32] = (0..32) - .map(|_| meta.advice_column()) - .collect::>() - .try_into() - .unwrap(); - let byte_lookup = meta.fixed_column(); - - let config = WordConfig::configure(meta, r, q_encode, bytes, byte_lookup); - - let pub_inputs = meta.instance_column(); - - // Make sure each encoded word has been committed to in the - // public inputs. - meta.lookup_any("Encoded word / Pub inputs", |meta| { - let q_encode = meta.query_selector(q_encode); - let pub_inputs = meta.query_instance(pub_inputs, Rotation::cur()); - - let encode_word = config.clone().encode_word_expr; - - vec![(q_encode * encode_word, pub_inputs)] - }); - - (config, pub_inputs) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - config.0.load(&mut layouter)?; - - layouter.assign_region( - || "assign word", - |mut region| { - let offset = 0; - config.0.assign_word(&mut region, offset, self.word) - }, - )?; - - Ok(()) - } - } - - { - let rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - let word = Fp::random(rng); - let circuit = MyCircuit:: { - word: word - .to_repr() - .iter() - .map(|b| Value::known(*b)) - .collect::>() - .try_into() - .unwrap(), - _marker: PhantomData, - }; - - // Test without public inputs - let prover = MockProver::::run(9, &circuit, vec![vec![]]).unwrap(); - assert_eq!( - prover.verify(), - Err(vec![VerifyFailure::Lookup { - name: "Encoded word / Pub inputs", - lookup_index: 32, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from(( - 1, - "assign word".to_string() - )), - offset: 0 - } - }]) - ); - - // Calculate word commitment and use it as public input. - let encoded: Fp = encode(word.to_repr().iter().rev().cloned(), r()); - let prover = MockProver::::run(9, &circuit, vec![vec![encoded]]).unwrap(); - prover.assert_satisfied_par() - } - } -} diff --git a/gadgets/src/is_zero.rs b/gadgets/src/is_zero.rs index 0f0d9fe721..be552b1f89 100644 --- a/gadgets/src/is_zero.rs +++ b/gadgets/src/is_zero.rs @@ -51,11 +51,11 @@ impl IsZeroConfig { } } +#[derive(Debug, Clone)] /// Wrapper arround [`IsZeroConfig`] for which [`Chip`] is implemented. pub struct IsZeroChip { config: IsZeroConfig, } - #[rustfmt::skip] impl IsZeroChip { /// Sets up the configuration of the chip by creating the required columns @@ -105,6 +105,11 @@ impl IsZeroChip { pub fn construct(config: IsZeroConfig) -> Self { IsZeroChip { config } } + + /// Annotates columns of this gadget embedded within a circuit region. + pub fn annotate_columns_in_region(&self, region: &mut Region, prefix: &str) { + self.config.annotate_columns_in_region(region, prefix) + } } impl IsZeroInstruction for IsZeroChip { @@ -115,7 +120,7 @@ impl IsZeroInstruction for IsZeroChip { value: Value, ) -> Result<(), Error> { let config = self.config(); - let value_invert = value.map(|value| value.invert().unwrap_or(F::zero())); + let value_invert = value.map(|value| value.invert().unwrap_or(F::ZERO)); region.assign_advice( || "witness inverse of value", config.value_inv, @@ -208,6 +213,7 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -335,6 +341,7 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/gadgets/src/less_than.rs b/gadgets/src/less_than.rs index 035e036213..49285951da 100644 --- a/gadgets/src/less_than.rs +++ b/gadgets/src/less_than.rs @@ -2,9 +2,8 @@ use eth_types::Field; use halo2_proofs::{ - arithmetic::FieldExt, - circuit::{Chip, Region, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, VirtualCells}, + circuit::{Chip, Layouter, Region, Value}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, }; @@ -14,7 +13,7 @@ use super::{ }; /// Instruction that the Lt chip needs to implement. -pub trait LtInstruction { +pub trait LtInstruction { /// Assign the lhs and rhs witnesses to the Lt chip's region. fn assign( &self, @@ -23,6 +22,9 @@ pub trait LtInstruction { lhs: F, rhs: F, ) -> Result<(), Error>; + + /// Load the u8 lookup table. + fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error>; } /// Config for the Lt chip. @@ -31,8 +33,9 @@ pub struct LtConfig { /// Denotes the lt outcome. If lhs < rhs then lt == 1, otherwise lt == 0. pub lt: Column, /// Denotes the bytes representation of the difference between lhs and rhs. - /// Note that the range of each byte is not checked by this config. pub diff: [Column; N_BYTES], + /// Denotes the range within which each byte should lie. + pub u8: Column, /// Denotes the range within which both lhs and rhs lie. pub range: F, } @@ -61,6 +64,7 @@ impl LtChip { let lt = meta.advice_column(); let diff = [(); N_BYTES].map(|_| meta.advice_column()); let range = pow_of_two(N_BYTES * 8); + let u8 = meta.fixed_column(); meta.create_gate("lt gate", |meta| { let q_enable = q_enable(meta); @@ -81,7 +85,22 @@ impl LtChip { .map(move |poly| q_enable.clone() * poly) }); - LtConfig { lt, diff, range } + meta.annotate_lookup_any_column(u8, || "LOOKUP_u8"); + + diff[0..N_BYTES].iter().for_each(|column| { + meta.lookup_any("range check for u8", |meta| { + let u8_cell = meta.query_advice(*column, Rotation::cur()); + let u8_range = meta.query_fixed(u8, Rotation::cur()); + vec![(u8_cell, u8_range)] + }); + }); + + LtConfig { + lt, + diff, + range, + u8, + } } /// Constructs a Lt chip given a config. @@ -108,7 +127,7 @@ impl LtInstruction for LtChip { || Value::known(F::from(lt as u64)), )?; - let diff = (lhs - rhs) + (if lt { config.range } else { F::zero() }); + let diff = (lhs - rhs) + (if lt { config.range } else { F::ZERO }); let diff_bytes = diff.to_repr(); let diff_bytes = diff_bytes.as_ref(); for (idx, diff_column) in config.diff.iter().enumerate() { @@ -122,6 +141,25 @@ impl LtInstruction for LtChip { Ok(()) } + + fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { + const RANGE: usize = 256; + + layouter.assign_region( + || "load u8 range check table", + |mut region| { + for i in 0..RANGE { + region.assign_fixed( + || "assign cell in fixed column", + self.config.u8, + i, + || Value::known(F::from(i as u64)), + )?; + } + Ok(()) + }, + ) + } } impl Chip for LtChip { @@ -139,10 +177,9 @@ impl Chip for LtChip { #[cfg(test)] mod test { - use super::{LtChip, LtConfig, LtInstruction}; + use super::*; use eth_types::Field; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Layouter, SimpleFloorPlanner, Value}, dev::MockProver, halo2curves::bn256::Fr as Fp, @@ -157,7 +194,7 @@ mod test { // TODO: remove zk blinding factors in halo2 to restore the // correct k (without the extra + 2). - let k = usize::BITS - $values.len().leading_zeros() + 2; + let k = 9; let circuit = TestCircuit:: { values: Some($values), checks: Some($checks), @@ -174,7 +211,7 @@ mod test { // TODO: remove zk blinding factors in halo2 to restore the // correct k (without the extra + 2). - let k = usize::BITS - $values.len().leading_zeros() + 2; + let k = 9; let circuit = TestCircuit:: { values: Some($values), checks: Some($checks), @@ -196,7 +233,7 @@ mod test { } #[derive(Default)] - struct TestCircuit { + struct TestCircuit { values: Option>, // checks[i] = lt(values[i + 1], values[i]) checks: Option>, @@ -206,6 +243,7 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -258,6 +296,8 @@ mod test { let (first_value, values) = values.split_at(1); let first_value = first_value[0]; + chip.load(&mut layouter)?; + layouter.assign_region( || "witness", |mut region| { @@ -314,7 +354,7 @@ mod test { } #[derive(Default)] - struct TestCircuit { + struct TestCircuit { values: Option>, // checks[i] = lt(values[i].0 - values[i].1) checks: Option>, @@ -324,6 +364,7 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -380,6 +421,8 @@ mod test { .ok_or(Error::Synthesis)?; let checks = self.checks.as_ref().ok_or(Error::Synthesis)?; + chip.load(&mut layouter)?; + layouter.assign_region( || "witness", |mut region| { diff --git a/gadgets/src/lib.rs b/gadgets/src/lib.rs index 3ef7fae9a1..a7150262ee 100644 --- a/gadgets/src/lib.rs +++ b/gadgets/src/lib.rs @@ -13,32 +13,13 @@ pub mod batched_is_zero; pub mod binary_number; -pub mod evm_word; pub mod is_zero; pub mod less_than; -pub mod monotone; pub mod mul_add; pub mod util; use eth_types::Field; -use halo2_proofs::{ - circuit::{AssignedCell, Value}, - plonk::Expression, -}; - -#[allow(dead_code)] -/// An assigned cell in the circuit. -#[derive(Clone, Debug)] -pub struct Variable { - assig_cell: AssignedCell, - value: Value, -} - -impl Variable { - pub(crate) fn new(assig_cell: AssignedCell, value: Value) -> Self { - Self { assig_cell, value } - } -} +use halo2_proofs::plonk::Expression; /// Restrict an expression to be a boolean. pub fn bool_check(value: Expression) -> Expression { diff --git a/gadgets/src/monotone.rs b/gadgets/src/monotone.rs deleted file mode 100644 index 798beeb995..0000000000 --- a/gadgets/src/monotone.rs +++ /dev/null @@ -1,311 +0,0 @@ -//! # Monotone mod -//! Monotone gadget helps to check if an advice column is monotonically -//! increasing within a range. With strict enabled, it disallows equality of two -//! cell. -use halo2_proofs::{ - arithmetic::FieldExt, - circuit::{Chip, Layouter, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, - poly::Rotation, -}; -use std::{marker::PhantomData, u64}; - -#[allow(dead_code)] -#[derive(Clone, Debug)] -pub(crate) struct MonotoneConfig { - range_table: Column, - value: Column, -} - -/// MonotoneChip helps to check if an advice column is monotonically increasing -/// within a range. With strict enabled, it disallows equality of two cell. -pub(crate) struct MonotoneChip { - config: MonotoneConfig, - _marker: PhantomData, -} - -#[allow(dead_code)] -impl - MonotoneChip -{ - /// configure which column should be check. q_enable here as a fn is - /// flexible for synthetic selector instead of a fixed one. - pub fn configure( - meta: &mut ConstraintSystem, - q_enable: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, - value: Column, - ) -> MonotoneConfig { - let range_table = meta.fixed_column(); - - let config = MonotoneConfig { range_table, value }; - - meta.lookup_any("Range check", |meta| { - let q_enable = q_enable(meta); - let range_table = meta.query_fixed(config.range_table, Rotation::cur()); - let value_diff = { - let value_cur = meta.query_advice(value, Rotation::cur()); - let value_prev = meta.query_advice(value, Rotation::prev()); - if INCR { - value_cur - value_prev - } else { - value_prev - value_cur - } - }; - - // If strict monotone, we subtract diff by one - // to make sure zero lookup fail - let min_diff = Expression::Constant(F::from(STRICT as u64)); - - vec![(q_enable * (value_diff - min_diff), range_table)] - }); - - config - } - - pub fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { - layouter.assign_region( - || "range_table", - |mut meta| { - let max = RANGE - STRICT as usize; - - for idx in 0..=max { - meta.assign_fixed( - || "range_table_value", - self.config.range_table, - idx, - || Value::known(F::from(idx as u64)), - )?; - } - - Ok(()) - }, - ) - } - - pub fn construct(config: MonotoneConfig) -> Self { - Self { - config, - _marker: PhantomData, - } - } -} - -impl Chip - for MonotoneChip -{ - type Config = MonotoneConfig; - type Loaded = (); - - fn config(&self) -> &Self::Config { - &self.config - } - - fn loaded(&self) -> &Self::Loaded { - &() - } -} - -#[cfg(test)] -mod test { - use super::{MonotoneChip, MonotoneConfig, Value}; - use halo2_proofs::{ - arithmetic::FieldExt, - circuit::{Layouter, SimpleFloorPlanner}, - dev::{ - FailureLocation, MockProver, - VerifyFailure::{self, Lookup}, - }, - halo2curves::bn256::Fr as Fp, - plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Selector}, - }; - use std::marker::PhantomData; - - #[derive(Clone, Debug)] - struct TestCircuitConfig { - q_enable: Selector, - value: Column, - mono_incr: MonotoneConfig, - } - - #[derive(Default)] - struct TestCircuit { - values: Option>, - _marker: PhantomData, - } - - impl Circuit - for TestCircuit - { - type Config = TestCircuitConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let q_enable = meta.complex_selector(); - let value = meta.advice_column(); - - let mono_incr = MonotoneChip::::configure( - meta, - |meta| meta.query_selector(q_enable), - value, - ); - - Self::Config { - q_enable, - value, - mono_incr, - } - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let monotone_chip = - MonotoneChip::::construct(config.mono_incr.clone()); - - monotone_chip.load(&mut layouter)?; - - let values: Vec<_> = self - .values - .as_ref() - .map(|values| values.iter().map(|value| F::from(*value)).collect()) - .ok_or(Error::Synthesis)?; - - layouter.assign_region( - || "witness", - |mut region| { - for (idx, value) in values.iter().enumerate() { - region.assign_advice( - || "value", - config.value, - idx, - || Value::known(*value), - )?; - if idx > 0 { - config.q_enable.enable(&mut region, idx)?; - } - } - - Ok(()) - }, - ) - } - } - - macro_rules! gen_try_test_circuit { - ($range:expr, $incr:expr, $strict:expr) => { - fn try_test_circuit(values: Vec, result: Result<(), Vec>) { - let circuit = TestCircuit:: { - values: Some(values), - _marker: PhantomData, - }; - let prover = MockProver::::run( - usize::BITS - ($range as usize).leading_zeros(), - &circuit, - vec![], - ) - .unwrap(); - if result.is_err() { - assert_eq!(prover.verify_par(), result) - } else { - prover.assert_satisfied_par() - } - } - }; - } - - #[test] - fn strict_monotonically_increasing() { - gen_try_test_circuit!(100, true, true); - - // strict monotone in range (ok) - try_test_circuit(vec![1, 2, 3, 4, 104], Ok(())); - try_test_circuit(vec![1001, 1002, 1003, 1004, 1104], Ok(())); - // non-strict monotone in range (error) - try_test_circuit( - vec![1, 2, 2, 4, 4], - Err(vec![ - Lookup { - name: "Range check", - lookup_index: 0, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from((1, "witness")), - offset: 2, - }, - }, - Lookup { - name: "Range check", - lookup_index: 0, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from((1, "witness")), - offset: 4, - }, - }, - ]), - ); - // out of range (error) - try_test_circuit( - vec![1, 2, 3, 4, 105], - Err(vec![Lookup { - name: "Range check", - lookup_index: 0, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from((1, "witness")), - offset: 4, - }, - }]), - ); - // not monotone (error) - try_test_circuit( - vec![1, 2, 3, 103, 4], - Err(vec![Lookup { - name: "Range check", - lookup_index: 0, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from((1, "witness")), - offset: 4, - }, - }]), - ); - } - - #[test] - fn non_strict_monotonically_increasing() { - gen_try_test_circuit!(100, true, false); - - // strict monotone in range (ok) - try_test_circuit(vec![1, 2, 3, 4, 104], Ok(())); - try_test_circuit(vec![1001, 1002, 1003, 1004, 1104], Ok(())); - // non-strict monotone in range (ok) - try_test_circuit(vec![1, 2, 2, 4, 4], Ok(())); - // out of range (error) - try_test_circuit( - vec![1, 2, 3, 4, 105], - Err(vec![Lookup { - name: "Range check", - lookup_index: 0, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from((1, "witness")), - offset: 4, - }, - }]), - ); - // not monotone (error) - try_test_circuit( - vec![1, 2, 3, 103, 4], - Err(vec![Lookup { - name: "Range check", - lookup_index: 0, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from((1, "witness")), - offset: 4, - }, - }]), - ); - } -} diff --git a/gadgets/src/mul_add.rs b/gadgets/src/mul_add.rs index 353c84fdac..3710f7b9c6 100644 --- a/gadgets/src/mul_add.rs +++ b/gadgets/src/mul_add.rs @@ -158,8 +158,8 @@ impl MulAddChip { overflow = carry_hi_expr.clone() + a_limbs[1].clone() * b_limbs[3].clone() + a_limbs[2].clone() * b_limbs[2].clone() - + a_limbs[3].clone() * b_limbs[2].clone() + a_limbs[2].clone() * b_limbs[3].clone() + + a_limbs[3].clone() * b_limbs[1].clone() + a_limbs[3].clone() * b_limbs[2].clone() + a_limbs[3].clone() * b_limbs[3].clone(); @@ -241,7 +241,7 @@ impl MulAddChip { || format!("unused col: {}", offset), self.config.col4, offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // b limbs. @@ -266,7 +266,7 @@ impl MulAddChip { || format!("unused col {}", offset + 1), self.config.col4, offset + 1, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // c_lo, c_hi, d_lo, d_hi. @@ -298,7 +298,7 @@ impl MulAddChip { || format!("unused col: {}", offset + 2), self.config.col4, offset + 2, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // carry_lo. @@ -328,7 +328,7 @@ impl MulAddChip { || format!("unused col: {}", offset + 4), self.config.col4, offset + 4, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // carry_hi. @@ -358,7 +358,7 @@ impl MulAddChip { || format!("unused col: {}", offset + 6), self.config.col4, offset + 6, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; Ok(()) @@ -443,6 +443,7 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn configure(meta: &mut halo2_proofs::plonk::ConstraintSystem) -> Self::Config { let q_enable = meta.complex_selector(); diff --git a/gadgets/src/util.rs b/gadgets/src/util.rs index 9e5836ca3f..29ed701b39 100644 --- a/gadgets/src/util.rs +++ b/gadgets/src/util.rs @@ -1,15 +1,12 @@ //! Utility traits, functions used in the crate. -use eth_types::{ - evm_types::{GasCost, OpcodeId}, - Field, U256, -}; +use eth_types::{evm_types::OpcodeId, Field, U256}; use halo2_proofs::plonk::Expression; /// Returns the sum of the passed in cells pub mod sum { use crate::util::Expr; use eth_types::Field; - use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; + use halo2_proofs::plonk::Expression; /// Returns an expression for the sum of the list of expressions. pub fn expr, I: IntoIterator>(inputs: I) -> Expression { @@ -19,10 +16,10 @@ pub mod sum { } /// Returns the sum of the given list of values within the field. - pub fn value(values: &[u8]) -> F { + pub fn value(values: &[u8]) -> F { values .iter() - .fold(F::zero(), |acc, value| acc + F::from(*value as u64)) + .fold(F::ZERO, |acc, value| acc + F::from(*value as u64)) } } @@ -31,7 +28,7 @@ pub mod sum { pub mod and { use crate::util::Expr; use eth_types::Field; - use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; + use halo2_proofs::plonk::Expression; /// Returns an expression that evaluates to 1 only if all the expressions in /// the given list are 1, else returns 0. @@ -42,8 +39,8 @@ pub mod and { } /// Returns the product of all given values. - pub fn value(inputs: Vec) -> F { - inputs.iter().fold(F::one(), |acc, input| acc * input) + pub fn value(inputs: Vec) -> F { + inputs.iter().fold(F::ONE, |acc, input| acc * input) } } @@ -53,7 +50,7 @@ pub mod or { use super::{and, not}; use crate::util::Expr; use eth_types::Field; - use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; + use halo2_proofs::plonk::Expression; /// Returns an expression that evaluates to 1 if any expression in the given /// list is 1. Returns 0 if all the expressions were 0. @@ -62,7 +59,7 @@ pub mod or { } /// Returns the value after passing all given values through the OR gate. - pub fn value(inputs: Vec) -> F { + pub fn value(inputs: Vec) -> F { not::value(and::value(inputs.into_iter().map(not::value).collect())) } } @@ -72,7 +69,7 @@ pub mod or { pub mod not { use crate::util::Expr; use eth_types::Field; - use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; + use halo2_proofs::plonk::Expression; /// Returns an expression that represents the NOT of the given expression. pub fn expr>(b: E) -> Expression { @@ -80,8 +77,8 @@ pub mod not { } /// Returns a value that represents the NOT of the given value. - pub fn value(b: F) -> F { - F::one() - b + pub fn value(b: F) -> F { + F::ONE - b } } @@ -90,7 +87,7 @@ pub mod not { pub mod xor { use crate::util::Expr; use eth_types::Field; - use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; + use halo2_proofs::plonk::Expression; /// Returns an expression that represents the XOR of the given expression. pub fn expr>(a: E, b: E) -> Expression { @@ -98,7 +95,7 @@ pub mod xor { } /// Returns a value that represents the XOR of the given value. - pub fn value(a: F, b: F) -> F { + pub fn value(a: F, b: F) -> F { a + b - F::from(2u64) * a * b } } @@ -123,7 +120,7 @@ pub mod select { /// Returns the `when_true` value when the selector is true, else returns /// the `when_false` value. pub fn value(selector: F, when_true: F, when_false: F) -> F { - selector * when_true + (F::one() - selector) * when_false + selector * when_true + (F::ONE - selector) * when_false } /// Returns the `when_true` word when selector is true, else returns the @@ -133,7 +130,7 @@ pub mod select { when_true: [u8; 32], when_false: [u8; 32], ) -> [u8; 32] { - if selector == F::one() { + if selector == F::ONE { when_true } else { when_false @@ -164,7 +161,7 @@ pub mod pow { pub fn value(value: F, exponent: usize) -> F { let mut result = 1.scalar(); for _ in 0..exponent { - result = result * value; + result *= value; } result } @@ -234,24 +231,18 @@ impl_expr!(u64); impl_expr!(usize); impl_expr!(isize); impl_expr!(OpcodeId, OpcodeId::as_u8); -impl_expr!(GasCost, GasCost::as_u64); impl Scalar for i32 { #[inline] fn scalar(&self) -> F { - F::from(self.unsigned_abs() as u64) - * if self.is_negative() { - -F::one() - } else { - F::one() - } + F::from(self.unsigned_abs() as u64) * if self.is_negative() { -F::ONE } else { F::ONE } } } impl Scalar for &F { #[inline] fn scalar(&self) -> F { - *self.clone() + *(*self) } } @@ -280,7 +271,7 @@ impl Expr for &Expression { /// single expression. pub fn expr_from_bytes>(bytes: &[E]) -> Expression { let mut value = 0.expr(); - let mut multiplier = F::one(); + let mut multiplier = F::ONE; for byte in bytes.iter() { value = value + byte.expr() * multiplier; multiplier *= F::from(256); @@ -288,9 +279,9 @@ pub fn expr_from_bytes>(bytes: &[E]) -> Expression { value } -/// Returns 2**by as FieldExt +/// Returns 2**by as Field pub fn pow_of_two(by: usize) -> F { - F::from(2).pow(&[by as u64, 0, 0, 0]) + F::from(2).pow([by as u64, 0, 0, 0]) } /// Returns tuple consists of low and high part of U256 diff --git a/geth-utils/gethutil/trace.go b/geth-utils/gethutil/trace.go index 56cf94dbbf..d8ddc32114 100644 --- a/geth-utils/gethutil/trace.go +++ b/geth-utils/gethutil/trace.go @@ -124,28 +124,33 @@ type TraceConfig struct { LoggerConfig *logger.Config `json:"logger_config"` } +func newUint64(val uint64) *uint64 { return &val } + func Trace(config TraceConfig) ([]*ExecutionResult, error) { chainConfig := params.ChainConfig{ - ChainID: toBigInt(config.ChainID), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: big.NewInt(0), - DAOForkSupport: true, - 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: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), + ChainID: toBigInt(config.ChainID), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + 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: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ShanghaiTime: newUint64(0), + TerminalTotalDifficulty: big.NewInt(0), + TerminalTotalDifficultyPassed: true, } var txsGasLimit uint64 blockGasLimit := toBigInt(config.Block.GasLimit).Uint64() - messages := make([]types.Message, len(config.Transactions)) + messages := make([]core.Message, len(config.Transactions)) for i, tx := range config.Transactions { // If gas price is specified directly, the tx is treated as legacy type. if tx.GasPrice != nil { @@ -158,19 +163,19 @@ func Trace(config TraceConfig) ([]*ExecutionResult, error) { txAccessList[i].Address = accessList.Address txAccessList[i].StorageKeys = accessList.StorageKeys } - messages[i] = types.NewMessage( - tx.From, - tx.To, - uint64(tx.Nonce), - toBigInt(tx.Value), - uint64(tx.GasLimit), - toBigInt(tx.GasPrice), - toBigInt(tx.GasFeeCap), - toBigInt(tx.GasTipCap), - tx.CallData, - txAccessList, - false, - ) + messages[i] = core.Message{ + From: tx.From, + To: tx.To, + Nonce: uint64(tx.Nonce), + Value: toBigInt(tx.Value), + GasLimit: uint64(tx.GasLimit), + GasPrice: toBigInt(tx.GasPrice), + GasFeeCap: toBigInt(tx.GasFeeCap), + GasTipCap: toBigInt(tx.GasTipCap), + Data: tx.CallData, + AccessList: txAccessList, + SkipAccountChecks: false, + } txsGasLimit += uint64(tx.GasLimit) } @@ -178,6 +183,9 @@ func Trace(config TraceConfig) ([]*ExecutionResult, error) { return nil, fmt.Errorf("txs total gas: %d Exceeds block gas limit: %d", txsGasLimit, blockGasLimit) } + // For opcode PREVRANDAO + randao := common.BigToHash(toBigInt(config.Block.Difficulty)) // TODO: fix + blockCtx := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, @@ -191,8 +199,9 @@ func Trace(config TraceConfig) ([]*ExecutionResult, error) { }, Coinbase: config.Block.Coinbase, BlockNumber: toBigInt(config.Block.Number), - Time: toBigInt(config.Block.Timestamp), + Time: toBigInt(config.Block.Timestamp).Uint64(), Difficulty: toBigInt(config.Block.Difficulty), + Random: &randao, BaseFee: toBigInt(config.Block.BaseFee), GasLimit: blockGasLimit, } @@ -215,9 +224,9 @@ func Trace(config TraceConfig) ([]*ExecutionResult, error) { executionResults := make([]*ExecutionResult, len(config.Transactions)) for i, message := range messages { tracer := logger.NewStructLogger(config.LoggerConfig) - evm := vm.NewEVM(blockCtx, core.NewEVMTxContext(message), stateDB, &chainConfig, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) + evm := vm.NewEVM(blockCtx, core.NewEVMTxContext(&message), stateDB, &chainConfig, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) - result, err := core.ApplyMessage(evm, message, new(core.GasPool).AddGas(message.Gas())) + result, err := core.ApplyMessage(evm, &message, new(core.GasPool).AddGas(message.GasLimit)) if err != nil { return nil, fmt.Errorf("Failed to apply config.Transactions[%d]: %w", i, err) } diff --git a/geth-utils/go.mod b/geth-utils/go.mod index d1c9cd7465..2892aa855f 100644 --- a/geth-utils/go.mod +++ b/geth-utils/go.mod @@ -3,7 +3,7 @@ module main go 1.16 require ( - github.com/ethereum/go-ethereum v1.10.18 + github.com/ethereum/go-ethereum v1.11.5 github.com/holiman/uint256 v1.2.0 ) diff --git a/geth-utils/go.sum b/geth-utils/go.sum index 31cc98766a..62c668a662 100644 --- a/geth-utils/go.sum +++ b/geth-utils/go.sum @@ -8,34 +8,84 @@ cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTj cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/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/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 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= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= +github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= +github.com/CloudyKit/jet/v6 v6.1.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= +github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= +github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM= 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.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= +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/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= 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/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= @@ -45,8 +95,15 @@ github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7 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/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= 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.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= 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/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= @@ -58,16 +115,54 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA 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.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= 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.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= +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/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= +github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= +github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= +github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= +github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= +github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= +github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= +github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= +github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= +github.com/consensys/gnark-crypto v0.9.1-0.20230105202408-1a7a29904a7c/go.mod h1:CkbdF9hbRidRJYMRzmfX8TMOr95I2pYXRHF18MzRrvA= +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/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +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/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 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/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-ipa v0.0.0-20220523130400-f11357ae11c7/go.mod h1:gFnFS95y8HstDP6P9pPwzrxOOC5TRDkwbM+ao15ChAI= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= @@ -76,85 +171,176 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= +github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= +github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= 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-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= +github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +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/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.10.18 h1:hLEd5M+UD0GJWPaROiYMRgZXl6bi5YwoTJSthsx5CZw= -github.com/ethereum/go-ethereum v1.10.18/go.mod h1:RD3NhcSBjZpj3k+SnQq24wBrmnmie78P5R/P62iNBD8= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= +github.com/ethereum/go-ethereum v1.11.5 h1:3M1uan+LAUvdn+7wCEFrcMM4LJTeuxDrPTg/f31a5QQ= +github.com/ethereum/go-ethereum v1.11.5/go.mod h1:it7x0DWnTDMfVFdXcU6Ti4KEFQynLHVRarcSlPr0HBo= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= +github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= 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/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= +github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.0.0-20220902153445-097bd83b7732/go.mod h1:o/XfIXWi4/GqbQirfRm5uTbXMG5NpqxkxblnbZ+QM9I= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= +github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= +github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= +github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= +github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= 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-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 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= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= 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/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= +github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= 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/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= 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/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= +github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= 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= @@ -163,26 +349,80 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/graph-gophers/graphql-go v1.3.0/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-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.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +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-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.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.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/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.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= @@ -190,7 +430,11 @@ github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= +github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= @@ -203,167 +447,450 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y 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/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/httpexpect/v2 v2.3.1/go.mod h1:ICTf89VBKSD3KB0fsyyHviKF8G8hyepP0dOXJPWz3T0= +github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= +github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= +github.com/iris-contrib/jade v1.1.4/go.mod h1:EDqR+ur9piDl6DUgs6qRrlfzmlx/D5UybogqrXvJTBE= +github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= +github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= 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/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/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/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/kataras/blocks v0.0.6/go.mod h1:UK+Iwk0Oxpc0GdoJja7sEildotAUKK1LYeYcVF0COWc= +github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I= +github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= +github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= +github.com/kataras/golog v0.1.7/go.mod h1:jOSQ+C5fUqsNSwurB/oAHq1IFSb0KI3l6GMa7xB6dZA= +github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= +github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= +github.com/kataras/iris/v12 v12.2.0-beta5/go.mod h1:q26aoWJ0Knx/00iPKg5iizDK7oQQSPjbD8np0XDh6dc= +github.com/kataras/jwt v0.1.8/go.mod h1:Q5j2IkcIHnfwy+oNY3TVWuEBJNw0ADgCcXK9CaZwV4o= +github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= +github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= +github.com/kataras/neffos v0.0.20/go.mod h1:srdvC/Uo8mgrApWW0AYtiiLgMbyNPf69qPsd2FhE6MQ= +github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= +github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= +github.com/kataras/pio v0.0.10/go.mod h1:gS3ui9xSD+lAUpbYnjOGiQyY7sUMJO+EHpiRzhtZ5no= +github.com/kataras/pio v0.0.11/go.mod h1:38hH6SWH6m4DKSYmRhlrCJ5WItwWgCVrTNU62XZyUvI= +github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= +github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= +github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= +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/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= +github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.1/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/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/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= +github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= +github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailgun/raymond/v2 v2.0.46/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= 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.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= +github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +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.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 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-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= +github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= +github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= +github.com/mediocregopher/radix/v3 v3.8.0/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50= +github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +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= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= 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/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4= +github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nats.go v1.15.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 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.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.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= 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/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= 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/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/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= 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/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= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 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= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.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/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= +github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= 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.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/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= 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/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= +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/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI= +github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= 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/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/assertions v1.13.0/go.mod h1:wDmR7qL282YbGsPy6H/yAsesrxfxaaSlJazyFLYVFx8= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= +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.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= 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.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/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.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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tdewolff/minify/v2 v2.12.1/go.mod h1:p5pwbvNs1ghbFED/ZW1towGsnnWwzvM8iz8l0eURi9g= +github.com/tdewolff/minify/v2 v2.12.4/go.mod h1:h+SRvSIX3kwgwTFOpSckvSxgax3uy8kZTSF1Ojrr3bk= +github.com/tdewolff/parse/v2 v2.6.3/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs= +github.com/tdewolff/parse/v2 v2.6.4/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs= +github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= -github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= +github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= +github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= +github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= +github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= +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.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= +github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +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/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 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/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/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-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -374,7 +901,13 @@ golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm0 golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= +golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= +golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 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= @@ -386,96 +919,229 @@ 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-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/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= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190327091125-710a502c58a2/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-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-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-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= +golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/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-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-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +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= @@ -483,21 +1149,31 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/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-20180917221912-90fa682c2a6e/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-20181221001348-537d06c36207/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= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 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= @@ -508,24 +1184,58 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-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-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= 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= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 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= gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= +gonum.org/v1/gonum v0.8.2/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/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= @@ -536,11 +1246,29 @@ google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 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= @@ -555,44 +1283,121 @@ 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-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 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/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= 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/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/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= +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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 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= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index a95848d960..67156dcecc 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -11,7 +11,7 @@ lazy_static = "1.4" ethers = { version = "0.17.0", features = ["ethers-solc"] } serde_json = "1.0.66" serde = { version = "1.0.130", features = ["derive"] } -bus-mapping = { path = "../bus-mapping" } +bus-mapping = { path = "../bus-mapping" , features = ["test"] } eth-types = { path = "../eth-types" } zkevm-circuits = { path = "../zkevm-circuits", features = ["test"] } tokio = { version = "1.13", features = ["macros", "rt-multi-thread"] } @@ -19,7 +19,7 @@ url = "2.2.2" pretty_assertions = "1.0.0" log = "0.4.14" env_logger = "0.9" -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_02_02" } +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_04_20" } rand_chacha = "0.3" paste = "1.0" rand_xorshift = "0.3.0" diff --git a/integration-tests/docker-compose.yml b/integration-tests/docker-compose.yml index a1765c6561..f3a5dc75ff 100644 --- a/integration-tests/docker-compose.yml +++ b/integration-tests/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: geth0: - image: "ethereum/client-go:v1.10.18" + image: "ethereum/client-go:v1.11.5" container_name: zkevm-geth0 ports: - 8545:8545 diff --git a/integration-tests/src/integration_test_circuits.rs b/integration-tests/src/integration_test_circuits.rs index e33bd3d4fd..4771edd86e 100644 --- a/integration-tests/src/integration_test_circuits.rs +++ b/integration-tests/src/integration_test_circuits.rs @@ -28,14 +28,15 @@ use rand_xorshift::XorShiftRng; use std::{collections::HashMap, marker::PhantomData, sync::Mutex}; use tokio::sync::Mutex as TokioMutex; use zkevm_circuits::{ - bytecode_circuit::circuit::BytecodeCircuit, - copy_circuit::CopyCircuit, - evm_circuit::EvmCircuit, - exp_circuit::ExpCircuit, - keccak_circuit::KeccakCircuit, - state_circuit::StateCircuit, + bytecode_circuit::TestBytecodeCircuit, + copy_circuit::TestCopyCircuit, + evm_circuit::TestEvmCircuit, + exp_circuit::TestExpCircuit, + keccak_circuit::TestKeccakCircuit, + pi_circuit::TestPiCircuit, + state_circuit::TestStateCircuit, super_circuit::SuperCircuit, - tx_circuit::TxCircuit, + tx_circuit::TestTxCircuit, util::SubCircuit, witness::{block_convert, Block}, }; @@ -79,6 +80,7 @@ const COPY_CIRCUIT_DEGREE: u32 = 16; const KECCAK_CIRCUIT_DEGREE: u32 = 16; const SUPER_CIRCUIT_DEGREE: u32 = 20; const EXP_CIRCUIT_DEGREE: u32 = 16; +const PI_CIRCUIT_DEGREE: u32 = 17; lazy_static! { /// Data generation. @@ -95,36 +97,40 @@ lazy_static! { lazy_static! { /// Integration test for EVM circuit - pub static ref EVM_CIRCUIT_TEST: TokioMutex>> = + pub static ref EVM_CIRCUIT_TEST: TokioMutex>> = TokioMutex::new(IntegrationTest::new("EVM", EVM_CIRCUIT_DEGREE)); /// Integration test for State circuit - pub static ref STATE_CIRCUIT_TEST: TokioMutex>> = + pub static ref STATE_CIRCUIT_TEST: TokioMutex>> = TokioMutex::new(IntegrationTest::new("State", STATE_CIRCUIT_DEGREE)); /// Integration test for State circuit - pub static ref TX_CIRCUIT_TEST: TokioMutex>> = + pub static ref TX_CIRCUIT_TEST: TokioMutex>> = TokioMutex::new(IntegrationTest::new("Tx", TX_CIRCUIT_DEGREE)); /// Integration test for Bytecode circuit - pub static ref BYTECODE_CIRCUIT_TEST: TokioMutex>> = + pub static ref BYTECODE_CIRCUIT_TEST: TokioMutex>> = TokioMutex::new(IntegrationTest::new("Bytecode", BYTECODE_CIRCUIT_DEGREE)); /// Integration test for Copy circuit - pub static ref COPY_CIRCUIT_TEST: TokioMutex>> = + pub static ref COPY_CIRCUIT_TEST: TokioMutex>> = TokioMutex::new(IntegrationTest::new("Copy", COPY_CIRCUIT_DEGREE)); /// Integration test for Keccak circuit - pub static ref KECCAK_CIRCUIT_TEST: TokioMutex>> = + pub static ref KECCAK_CIRCUIT_TEST: TokioMutex>> = TokioMutex::new(IntegrationTest::new("Keccak", KECCAK_CIRCUIT_DEGREE)); /// Integration test for Copy circuit - pub static ref SUPER_CIRCUIT_TEST: TokioMutex>> = + pub static ref SUPER_CIRCUIT_TEST: TokioMutex>> = TokioMutex::new(IntegrationTest::new("Super", SUPER_CIRCUIT_DEGREE)); /// Integration test for Exp circuit - pub static ref EXP_CIRCUIT_TEST: TokioMutex>> = + pub static ref EXP_CIRCUIT_TEST: TokioMutex>> = TokioMutex::new(IntegrationTest::new("Exp", EXP_CIRCUIT_DEGREE)); + + /// Integration test for Pi circuit + pub static ref PI_CIRCUIT_TEST: TokioMutex>> = + TokioMutex::new(IntegrationTest::new("Pi", PI_CIRCUIT_DEGREE)); } /// Generic implementation for integration tests diff --git a/integration-tests/tests/circuits.rs b/integration-tests/tests/circuits.rs index bfce518a62..1582bd196b 100644 --- a/integration-tests/tests/circuits.rs +++ b/integration-tests/tests/circuits.rs @@ -49,6 +49,11 @@ macro_rules! declare_tests { async fn []() { run_test! (EXP_CIRCUIT_TEST, $block_tag, $real_prover); } + + #[tokio::test] + async fn []() { + run_test! (PI_CIRCUIT_TEST, $block_tag, $real_prover); + } } }; } @@ -64,7 +69,8 @@ macro_rules! unroll_tests { COPY_CIRCUIT_TEST, KECCAK_CIRCUIT_TEST, SUPER_CIRCUIT_TEST, - EXP_CIRCUIT_TEST + EXP_CIRCUIT_TEST, + PI_CIRCUIT_TEST, }; use integration_tests::log_init; mod real_prover { diff --git a/keccak256/Cargo.toml b/keccak256/Cargo.toml deleted file mode 100644 index 490dc87063..0000000000 --- a/keccak256/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "keccak256" -version = "0.1.0" -edition = "2021" -license = "MIT OR Apache-2.0" - -[features] -dev-graph = ["halo2_proofs/dev-graph", "plotters"] - -[dependencies] -halo2_proofs = { version = "0.1.0-beta.1" } -itertools = "0.10.1" -num-bigint = "0.4.2" -num-traits = "0.2.14" -plotters = { version = "0.3.0", optional = true } -eth-types = { path = "../eth-types" } -lazy_static = "1.4" -log = "0.4.14" -env_logger = "0.9" - -[dev-dependencies] -pretty_assertions = "1.0" -rand = "0.8" diff --git a/keccak256/src/arith_helpers.rs b/keccak256/src/arith_helpers.rs deleted file mode 100644 index 9d223caea2..0000000000 --- a/keccak256/src/arith_helpers.rs +++ /dev/null @@ -1,228 +0,0 @@ -use crate::common::State; -use eth_types::Field; -use itertools::Itertools; -use log::debug; -use num_bigint::BigUint; -use num_traits::Zero; -use std::ops::{Index, IndexMut}; - -pub const B2: u8 = 2; -pub const B13: u8 = 13; -pub const B9: u8 = 9; - -/// Base 9 coef mapper scalers -/// f_logic(x1, x2, x3, x4) = x1 ^ (!x2 & x3) ^ x4 -/// f_arith(x1, x2, x3, x4) = 2*x1 + x2 + 3*x3 + 2*x4 -/// where x1, x2, x3, x4 are binary. -/// We have the property that `0 <= f_arith(...) < 9` and -/// the map from `f_arith(...)` to `f_logic(...)` is injective. -pub const A1: u64 = 2; -pub const A2: u64 = 1; -pub const A3: u64 = 3; -pub const A4: u64 = 2; - -pub type Lane13 = BigUint; -pub type Lane9 = BigUint; - -#[derive(Debug)] -pub struct StateBigInt { - pub(crate) xy: Vec, -} -impl Default for StateBigInt { - fn default() -> Self { - let mut xy: Vec = Vec::with_capacity(25); - for _ in 0..25 { - xy.push(Zero::zero()); - } - Self { xy } - } -} - -impl From for StateBigInt { - fn from(state: State) -> Self { - let xy = state - .iter() - .flatten() - .map(|num| BigUint::from(*num)) - .collect(); - Self { xy } - } -} - -impl StateBigInt { - pub fn from_state_big_int(a: &StateBigInt, lane_transform: F) -> Self - where - F: Fn(BigUint) -> BigUint, - { - let mut out = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - out[(x, y)] = lane_transform(a[(x, y)].clone()); - } - out - } -} - -impl Index<(usize, usize)> for StateBigInt { - type Output = BigUint; - fn index(&self, xy: (usize, usize)) -> &Self::Output { - debug_assert!(xy.0 < 5); - debug_assert!(xy.1 < 5); - - &self.xy[xy.0 * 5 + xy.1] - } -} - -impl IndexMut<(usize, usize)> for StateBigInt { - fn index_mut(&mut self, xy: (usize, usize)) -> &mut Self::Output { - debug_assert!(xy.0 < 5); - debug_assert!(xy.1 < 5); - - &mut self.xy[xy.0 * 5 + xy.1] - } -} - -impl Clone for StateBigInt { - fn clone(&self) -> StateBigInt { - let xy = self.xy.clone(); - StateBigInt { xy } - } -} - -pub fn convert_b2_to_b13(a: u64) -> Lane13 { - let mut lane13: BigUint = Zero::zero(); - for i in 0..64 { - let bit = (a >> i) & 1; - lane13 += bit * BigUint::from(B13).pow(i); - } - lane13 -} - -pub fn convert_b2_to_b9(a: u64) -> Lane9 { - let mut lane9: BigUint = Zero::zero(); - for i in 0..64 { - let bit = (a >> i) & 1; - lane9 += bit * BigUint::from(B9).pow(i); - } - lane9 -} - -/// Maps a sum of 12 bits to the XOR result of 12 bits. -/// -/// The input `x` is a chunk of a base 13 number and it represents the -/// arithmatic sum of 12 bits. Asking the result of the 12 bits XORed together -/// is equivalent of asking if `x` being an odd number. -/// -/// For example, if we have 5 bits set and 7 bits unset, then we have `x` as 5 -/// and the xor result to be 1. -pub fn convert_b13_coef(x: u8) -> u8 { - debug_assert!(x < 13); - x & 1 -} - -/// Maps the arithmatic result `2*a + b + 3*c + 2*d` to the bit operation result -/// `a ^ (~b & c) ^ d` -/// -/// The input `x` is a chunk of a base 9 number and it represents the arithmatic -/// result of `2*a + b + 3*c + 2*d`, where `a`, `b`, `c`, and `d` each is a bit. -pub fn convert_b9_coef(x: u8) -> u8 { - debug_assert!(x < 9); - let bit_table: [u8; 9] = [0, 0, 1, 1, 0, 0, 1, 1, 0]; - bit_table[x as usize] -} - -// We assume the input comes from Theta step and has 65 chunks -// expecting outputs from theta gate -pub fn convert_b13_lane_to_b9(x: Lane13, rot: u32) -> Lane9 { - // 65 chunks - let mut chunks = x.to_radix_le(B13.into()); - chunks.resize(65, 0); - // 0 and 64 was separated in Theta, we now combined them together - let special = chunks.first().unwrap() + chunks.get(64).unwrap(); - // middle 63 chunks - let middle = chunks.get(1..64).unwrap(); - // split at offset - let (left, right) = middle.split_at(63 - rot as usize); - // rotated has 64 chunks - // left is rotated right, and the right is wrapped over to left - // with the special chunk in the middle - let rotated: Vec = right - .iter() - .chain(vec![special].iter()) - .chain(left.iter()) - .map(|&x| convert_b13_coef(x)) - .collect_vec(); - BigUint::from_radix_le(&rotated, B9.into()).unwrap_or_default() -} - -pub fn convert_lane(lane: BigUint, from_base: u8, to_base: u8, coef_transform: F) -> BigUint -where - F: Fn(u8) -> u8, -{ - let chunks = lane.to_radix_be(from_base.into()); - let converted_chunks: Vec = chunks.iter().map(|&x| coef_transform(x)).collect(); - BigUint::from_radix_be(&converted_chunks, to_base.into()).unwrap_or_default() -} - -pub fn convert_b9_lane_to_b13(x: Lane9) -> Lane13 { - convert_lane(x, B9, B13, convert_b9_coef) -} - -pub fn convert_b9_lane_to_b2(x: Lane9) -> u64 { - convert_lane(x, B9, 2, convert_b9_coef) - .iter_u64_digits() - .take(1) - .next() - .unwrap_or(0) -} - -pub fn convert_b9_lane_to_b2_biguint(x: Lane9) -> BigUint { - convert_lane(x, B9, 2, convert_b9_coef) -} - -pub fn convert_b9_lane_to_b2_normal(x: Lane9) -> u64 { - convert_lane(x, B9, 2, |y| y) - .iter_u64_digits() - .take(1) - .next() - .unwrap_or(0) -} - -/// This function allows us to inpect coefficients of big-numbers in different -/// bases. -pub fn inspect(x: BigUint, name: &str, base: u8) { - let mut chunks = x.to_radix_le(base.into()); - chunks.resize(65, 0); - let info: Vec<(usize, u8)> = (0..65).zip(chunks.iter().copied()).collect_vec(); - debug!("inspect {} {} info {:?}", name, x, info); -} - -pub fn f_from_radix_be(buf: &[u8], base: u8) -> F { - let base = F::from(base as u64); - buf.iter() - .fold(F::zero(), |acc, &x| acc * base + F::from(x as u64)) -} - -#[cfg(test)] -mod tests { - use super::*; - use num_bigint::BigUint; - #[test] - fn test_convert_b13_lane_to_b9() { - // the number 1 is chosen that `convert_b13_coef` has no effect - let mut a = vec![0, 1, 1, 1]; - a.resize(65, 0); - let lane = BigUint::from_radix_le(&a, B13.into()).unwrap_or_default(); - assert_eq!( - convert_b13_lane_to_b9(lane.clone(), 0), - BigUint::from_radix_le(&a, B9.into()).unwrap_or_default() - ); - - // rotate by 4 - let mut b = vec![0, 0, 0, 0, 0, 1, 1, 1]; - b.resize(65, 0); - assert_eq!( - convert_b13_lane_to_b9(lane, 4), - BigUint::from_radix_le(&b, B9.into()).unwrap_or_default() - ); - } -} diff --git a/keccak256/src/common.rs b/keccak256/src/common.rs deleted file mode 100644 index 138e3264d0..0000000000 --- a/keccak256/src/common.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! Types and constants of Keccak hash function. The constants can be found in the appendices of or [pycryptodome](https://github.com/Legrandin/pycryptodome). - -/// The State is a 5x5 matrix of 64 bit lanes. -pub type State = [[u64; 5]; 5]; - -/// The number of next_inputs that are used inside the `absorb` circuit. -pub const NEXT_INPUTS_LANES: usize = 17; - -/// The number of rounds for the 1600 bits permutation used in Keccak-256. See [here](https://github.com/Legrandin/pycryptodome/blob/016252bde04456614b68d4e4e8798bc124d91e7a/src/keccak.c#L230) -pub const PERMUTATION: usize = 24; - -/// The Keccak [round constants](https://github.com/Legrandin/pycryptodome/blob/016252bde04456614b68d4e4e8798bc124d91e7a/src/keccak.c#L257-L282) -pub static ROUND_CONSTANTS: [u64; PERMUTATION] = [ - 0x0000000000000001, - 0x0000000000008082, - 0x800000000000808A, - 0x8000000080008000, - 0x000000000000808B, - 0x0000000080000001, - 0x8000000080008081, - 0x8000000000008009, - 0x000000000000008A, - 0x0000000000000088, - 0x0000000080008009, - 0x000000008000000A, - 0x000000008000808B, - 0x800000000000008B, - 0x8000000000008089, - 0x8000000000008003, - 0x8000000000008002, - 0x8000000000000080, - 0x000000000000800A, - 0x800000008000000A, - 0x8000000080008081, - 0x8000000000008080, - 0x0000000080000001, - 0x8000000080008008, -]; - -/// The Keccak [rotation offsets](https://github.com/Legrandin/pycryptodome/blob/016252bde04456614b68d4e4e8798bc124d91e7a/src/keccak.c#L232-L255) -pub static ROTATION_CONSTANTS: [[u32; 5]; 5] = [ - [0, 36, 3, 41, 18], - [1, 44, 10, 45, 2], - [62, 6, 43, 15, 61], - [28, 55, 25, 21, 56], - [27, 20, 39, 8, 14], -]; - -pub const LANE_SIZE: u32 = 64; diff --git a/keccak256/src/gate_helpers.rs b/keccak256/src/gate_helpers.rs deleted file mode 100644 index 1050f9c3ed..0000000000 --- a/keccak256/src/gate_helpers.rs +++ /dev/null @@ -1,28 +0,0 @@ -use eth_types::Field; -use num_bigint::BigUint; - -/// Convert a bigUint value to FieldExt -/// -/// We assume the input value is smaller than the field size -pub fn biguint_to_f(x: &BigUint) -> F { - let mut x_bytes = x.to_bytes_le(); - debug_assert!( - x_bytes.len() <= 32, - "expect len <=32 but got {}", - x_bytes.len() - ); - x_bytes.resize(32, 0); - let x_bytes: [u8; 32] = x_bytes.try_into().unwrap(); - F::from_repr_vartime(x_bytes).unwrap() -} - -pub fn f_to_biguint(x: F) -> BigUint { - BigUint::from_bytes_le(&x.to_repr()) -} - -pub fn biguint_mod(x: &BigUint, modulus: u8) -> u8 { - x.to_radix_le(modulus.into()) - .first() - .copied() - .unwrap_or_default() -} diff --git a/keccak256/src/keccak_arith.rs b/keccak256/src/keccak_arith.rs deleted file mode 100644 index e04f8b720c..0000000000 --- a/keccak256/src/keccak_arith.rs +++ /dev/null @@ -1,363 +0,0 @@ -use crate::{arith_helpers::*, common::*}; -use itertools::Itertools; - -#[derive(Default)] -pub struct KeccakFArith {} - -impl KeccakFArith { - pub fn permute_and_absorb( - a: &mut StateBigInt, - next_inputs: Option<&State>, - ) -> Option { - for rc in ROUND_CONSTANTS.iter().take(PERMUTATION - 1) { - let s1 = KeccakFArith::theta(a); - let s2 = KeccakFArith::rho(&s1); - let s3 = KeccakFArith::pi(&s2); - let s4 = KeccakFArith::xi(&s3); - let s5 = KeccakFArith::iota_b9(&s4, *rc); - *a = StateBigInt::from_state_big_int(&s5, convert_b9_lane_to_b13); - } - let s1 = KeccakFArith::theta(a); - let s2 = KeccakFArith::rho(&s1); - let s3 = KeccakFArith::pi(&s2); - let s4 = KeccakFArith::xi(&s3); - let res = KeccakFArith::mixing(&s4, next_inputs, *ROUND_CONSTANTS.last().unwrap()); - *a = res.clone(); - if next_inputs.is_some() { - Some(res) - } else { - None - } - } - - pub fn theta(a: &StateBigInt) -> StateBigInt { - let mut c: Vec = Vec::with_capacity(5); - let mut out = StateBigInt::default(); - - for x in 0..5 { - c.push(&a[(x, 0)] + &a[(x, 1)] + &a[(x, 2)] + &a[(x, 3)] + &a[(x, 4)]); - } - - for (x, y) in (0..5).cartesian_product(0..5) { - out[(x, y)] = &a[(x, y)] + &c[(x + 4) % 5] + &c[(x + 1) % 5] * B13; - } - out - } - - pub fn rho(a: &StateBigInt) -> StateBigInt { - let mut out = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - out[(x, y)] = convert_b13_lane_to_b9(a[(x, y)].clone(), ROTATION_CONSTANTS[x][y]); - } - out - } - - pub fn pi(a: &StateBigInt) -> StateBigInt { - let mut out = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - out[(y, (2 * x + 3 * y) % 5)] = a[(x, y)].clone(); - } - out - } - - pub fn xi(a: &StateBigInt) -> StateBigInt { - let mut out = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - out[(x, y)] = a[(x, y)].clone() * A1 - + a[((x + 1) % 5, y)].clone() * A2 - + a[((x + 2) % 5, y)].clone() * A3; - } - out - } - - pub fn absorb(a: &StateBigInt, next_input: &State) -> StateBigInt { - let mut out = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - out[(x, y)] = a[(x, y)].clone() + convert_b2_to_b9(next_input[x][y]) * A4 - } - out - } - - pub fn iota_b9(a: &StateBigInt, rc: u64) -> StateBigInt { - let mut out = a.clone(); - out[(0, 0)] += convert_b2_to_b9(rc) * A4; - out - } - - pub fn iota_b13(a: &StateBigInt, rc: u64) -> StateBigInt { - let mut out = a.clone(); - out[(0, 0)] += convert_b2_to_b13(rc); - out - } - - pub fn mixing(a: &StateBigInt, next_input: Option<&State>, rc: u64) -> StateBigInt { - if let Some(next_input) = next_input { - let out_1 = KeccakFArith::absorb(a, next_input); - - // Base conversion from 9 to 13 - let mut out_2 = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - out_2[(x, y)] = convert_b9_lane_to_b13(out_1[(x, y)].clone()) - } - KeccakFArith::iota_b13(&out_2, rc) - } else { - KeccakFArith::iota_b9(a, rc) - } - } -} - -pub struct Keccak { - state: State, - sponge: Sponge, -} - -impl Default for Keccak { - fn default() -> Keccak { - let security_level = (1088, 512); - - Keccak { - state: [[0; 5]; 5], - // rate & capacity in bytes - sponge: Sponge::new(security_level.0 / 8, security_level.1 / 8), - } - } -} - -impl Keccak { - pub fn update(&mut self, input: &[u8]) { - let padding_total = self.sponge.rate - (input.len() % self.sponge.rate); - let mut padding: Vec; - - if padding_total == 1 { - padding = vec![0x81]; - } else { - padding = vec![0x01]; - padding.resize(padding_total - 1, 0x00); - padding.push(0x80); - } - - let padded_input: &[u8] = &[input, &padding].concat(); - self.sponge.absorb(&mut self.state, padded_input); - } - - /// Returns keccak hash based on current state - pub fn digest(&mut self) -> Vec { - self.sponge.squeeze(&mut self.state) - } -} - -struct Sponge { - rate: usize, - capacity: usize, -} - -impl Sponge { - pub fn new(rate: usize, capacity: usize) -> Sponge { - Sponge { rate, capacity } - } - - pub fn absorb(&self, state: &mut State, message: &[u8]) { - debug_assert!( - message.len() % self.rate == 0, - "Message is not divisible entirely by bytes rate" - ); - - let chunks_total = message.len() / self.rate; - - let words: Vec = Sponge::bits_to_u64_words_le(message); - - let mut state_bit_int = StateBigInt::default(); - for chunk_i in 0..chunks_total { - let chunk_offset: usize = chunk_i * (self.rate / 8); - let mut x = 0; - let mut y = 0; - let mut next_inputs = State::default(); - for i in 0..(self.rate / 8) { - next_inputs[x][y] = words[chunk_offset + i]; - if x < 5 - 1 { - x += 1; - } else { - y += 1; - x = 0; - } - } - if chunk_i == 0 { - for (x, y) in (0..5).cartesian_product(0..5) { - state_bit_int[(x, y)] = convert_b2_to_b13(next_inputs[x][y]); - } - continue; - } - KeccakFArith::permute_and_absorb(&mut state_bit_int, Some(&next_inputs)); - } - KeccakFArith::permute_and_absorb(&mut state_bit_int, None); - for (x, y) in (0..5).cartesian_product(0..5) { - state[x][y] = convert_b9_lane_to_b2(state_bit_int[(x, y)].clone()) - } - } - - pub fn squeeze(&self, state: &mut State) -> Vec { - let mut output: Vec = vec![]; - - let output_len: usize = self.capacity / 2; - let elems_total: usize = output_len / 8; - let mut counter: usize = 0; - - 'outer: for y in 0..5 { - for sheet in state.iter().take(5) { - output.append(&mut sheet[y].to_le_bytes().to_vec()); - if counter == elems_total { - break 'outer; - } - counter += 1; - } - } - - output.resize(output_len, 0); - output - } - - fn bits_to_u64_words_le(message: &[u8]) -> Vec { - let words_total = message.len() / 8; - let mut words: Vec = vec![0; words_total]; - - for i in 0..words_total { - let mut word_bits: [u8; 8] = Default::default(); - word_bits.copy_from_slice(&message[i * 8..i * 8 + 8]); - words[i] = u64::from_le_bytes(word_bits); - } - words - } -} -#[cfg(test)] -mod tests { - use crate::{ - arith_helpers::*, - keccak_arith::{Keccak, KeccakFArith, State}, - plain::KeccakF, - }; - use itertools::Itertools; - use num_bigint::BigUint; - use num_traits::Zero; - - fn keccak256(msg: &[u8]) -> Vec { - let mut keccak = Keccak::default(); - keccak.update(msg); - keccak.digest() - } - - #[test] - fn test_helpers() { - assert_eq!(convert_b2_to_b13(0b101u64), BigUint::from(170u64)); - assert_eq!(convert_b2_to_b9(0b10u64), BigUint::from(9u64)); - assert_eq!(convert_b2_to_b9(0b101u64), BigUint::from(82u64)); - assert_eq!( - convert_b13_lane_to_b9(BigUint::from(170u64), 0), - BigUint::from(82u64) - ); - assert_eq!(convert_b9_lane_to_b13(BigUint::from(82u64)), Zero::zero()); - assert_eq!(convert_b9_lane_to_b2(BigUint::from(82u64)), 0); - assert_eq!( - convert_b9_lane_to_b2(BigUint::from(9u64).pow(63) * 2u64), - 1u64 << 63 - ); - } - - #[test] - fn test_theta_rho() { - let input1: State = [ - [1, 0, 0, 0, 0], - [0, 0, 0, 9223372036854775808, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - ]; - let input2: State = [ - [4398046511105, 8, 2, 268436480, 2305844108725321728], - [ - 17592186044416, - 52776560230400, - 544, - 68719493120, - 2199023255552, - ], - [ - 4398046543872, - 1152921504606846984, - 262144, - 1024, - 1099511627780, - ], - [0, 52776558133248, 514, 268451840, 2305845208236949504], - [17592186077184, 1152921504608944128, 262176, 68719476736, 4], - ]; - for a in [input1, input2] { - let mut in_b13 = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - in_b13[(x, y)] = convert_b2_to_b13(a[x][y]); - } - let s1 = KeccakF::theta(a); - let s1_arith = KeccakFArith::theta(&in_b13); - for (x, y) in (0..5).cartesian_product(0..5) { - assert_eq!( - s1[x][y], - convert_b9_lane_to_b2_normal(convert_b13_lane_to_b9( - s1_arith[(x, y)].clone(), - 0 - )) - ); - } - let s2 = KeccakF::rho(s1); - let s2_arith = KeccakFArith::rho(&s1_arith); - for (x, y) in (0..5).cartesian_product(0..5) { - let expected = convert_b9_lane_to_b2_normal(s2_arith[(x, y)].clone()); - assert_eq!(s2[x][y], expected); - } - } - } - - #[test] - fn test_empty_input_arith() { - let output = [ - 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, - 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112, - ]; - assert_eq!(keccak256(&[]), output); - } - - #[test] - fn test_short_input_arith() { - let output = [ - 56, 209, 138, 203, 103, 210, 92, 139, 185, 148, 39, 100, 182, 47, 24, 225, 112, 84, - 246, 106, 129, 123, 212, 41, 84, 35, 173, 249, 237, 152, 135, 62, - ]; - assert_eq!(keccak256(&[102, 111, 111, 98, 97, 114]), output); - } - - #[test] - fn test_long_input_arith() { - let input = [ - 65, 108, 105, 99, 101, 32, 119, 97, 115, 32, 98, 101, 103, 105, 110, 110, 105, 110, - 103, 32, 116, 111, 32, 103, 101, 116, 32, 118, 101, 114, 121, 32, 116, 105, 114, 101, - 100, 32, 111, 102, 32, 115, 105, 116, 116, 105, 110, 103, 32, 98, 121, 32, 104, 101, - 114, 32, 115, 105, 115, 116, 101, 114, 32, 111, 110, 32, 116, 104, 101, 32, 98, 97, - 110, 107, 44, 32, 97, 110, 100, 32, 111, 102, 32, 104, 97, 118, 105, 110, 103, 32, 110, - 111, 116, 104, 105, 110, 103, 32, 116, 111, 32, 100, 111, 58, 32, 111, 110, 99, 101, - 32, 111, 114, 32, 116, 119, 105, 99, 101, 32, 115, 104, 101, 32, 104, 97, 100, 32, 112, - 101, 101, 112, 101, 100, 32, 105, 110, 116, 111, 32, 116, 104, 101, 32, 98, 111, 111, - 107, 32, 104, 101, 114, 32, 115, 105, 115, 116, 101, 114, 32, 119, 97, 115, 32, 114, - 101, 97, 100, 105, 110, 103, 44, 32, 98, 117, 116, 32, 105, 116, 32, 104, 97, 100, 32, - 110, 111, 32, 112, 105, 99, 116, 117, 114, 101, 115, 32, 111, 114, 32, 99, 111, 110, - 118, 101, 114, 115, 97, 116, 105, 111, 110, 115, 32, 105, 110, 32, 105, 116, 44, 32, - 97, 110, 100, 32, 119, 104, 97, 116, 32, 105, 115, 32, 116, 104, 101, 32, 117, 115, - 101, 32, 111, 102, 32, 97, 32, 98, 111, 111, 107, 44, 32, 116, 104, 111, 117, 103, 104, - 116, 32, 65, 108, 105, 99, 101, 32, 119, 105, 116, 104, 111, 117, 116, 32, 112, 105, - 99, 116, 117, 114, 101, 115, 32, 111, 114, 32, 99, 111, 110, 118, 101, 114, 115, 97, - 116, 105, 111, 110, 115, 63, - ]; - let output = [ - 60, 227, 142, 8, 143, 135, 108, 85, 13, 254, 190, 58, 30, 106, 153, 194, 188, 6, 208, - 49, 16, 102, 150, 120, 100, 130, 224, 177, 64, 98, 53, 252, - ]; - assert_eq!(keccak256(&input), output); - } -} diff --git a/keccak256/src/lib.rs b/keccak256/src/lib.rs deleted file mode 100644 index 986db13d17..0000000000 --- a/keccak256/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Leave here until #105 uses all the functions that now are -// just used in tests - -pub mod arith_helpers; -pub mod common; -pub mod gate_helpers; -// We build arith module to get test cases for the circuit -pub mod keccak_arith; -// We build plain module for the purpose of reviewing the circuit -pub mod plain; diff --git a/mock/src/account.rs b/mock/src/account.rs index 0cdda75549..87abb710e8 100644 --- a/mock/src/account.rs +++ b/mock/src/account.rs @@ -11,7 +11,7 @@ pub struct MockAccount { /// Address pub address: Address, /// nonce - pub nonce: Word, + pub nonce: u64, /// Balance pub balance: Word, /// EVM Code @@ -24,7 +24,7 @@ impl From for Account { fn from(mock: MockAccount) -> Self { Account { address: mock.address, - nonce: mock.nonce, + nonce: mock.nonce.into(), balance: mock.balance, code: mock.code, storage: mock.storage, @@ -40,7 +40,7 @@ impl MockAccount { } /// Set nonce field for the MockAccount. - pub fn nonce(&mut self, nonce: Word) -> &mut Self { + pub fn nonce(&mut self, nonce: u64) -> &mut Self { self.nonce = nonce; self } @@ -69,7 +69,7 @@ impl MockAccount { /// Set all fields for the MockAccount based on their values in `account`. pub fn account(&mut self, account: &Account) -> &mut Self { self.address(account.address); - self.nonce(account.nonce); + self.nonce(account.nonce.as_u64()); self.balance(account.balance); self.code(account.code.clone()); self.storage(account.storage.iter().map(|(k, v)| (*k, *v))); diff --git a/mock/src/lib.rs b/mock/src/lib.rs index 3a3c176e8c..f190e0612c 100644 --- a/mock/src/lib.rs +++ b/mock/src/lib.rs @@ -1,6 +1,6 @@ //! Mock types and functions to generate GethData used for tests -use eth_types::{address, Address, Bytes, Word}; +use eth_types::{address, bytecode, bytecode::Bytecode, word, Address, Bytes, Word}; use ethers_signers::LocalWallet; use lazy_static::lazy_static; use rand::SeedableRng; @@ -57,6 +57,27 @@ lazy_static! { LocalWallet::new(&mut rng), ] }; + /// Mock EVM bytecode for a deployed contract. + /// PUSH1 0x20 + /// PUSH1 0 + /// PUSH1 0 + /// CALLDATACOPY + /// PUSH1 0x20 + /// PUSH1 0 + /// RETURN + /// + /// bytecode: 0x6020600060003760206000F3 + /// + /// // constructor + /// PUSH12 0x6020600060003760206000F3 + /// PUSH1 0 + /// MSTORE + /// PUSH1 0xC + /// PUSH1 0x14 + /// RETURN + /// + /// bytecode: 0x6B6020600060003760206000F3600052600C6014F3 + pub static ref MOCK_DEPLOYED_CONTRACT_BYTECODE: Word = word!("6B6020600060003760206000F3600052600C6014F3"); } /// Generate a [`Word`] which corresponds to a certain amount of ETH. @@ -68,3 +89,60 @@ pub fn eth(x: u64) -> Word { pub fn gwei(x: u64) -> Word { Word::from(x) * Word::from(10u64.pow(9)) } + +/// Holds the parameters for generating mock EVM bytecode for a contract call +pub struct MockCallBytecodeParams { + /// The address to call with the generated bytecode + pub address: Address, + /// The data to be passed as arguments to the contract function. + pub pushdata: Vec, + /// The offset in memory where the return data will be stored. + pub return_data_offset: usize, + /// The size of the return data. + pub return_data_size: usize, + /// The length of the call data. + pub call_data_length: usize, + /// The offset in memory where the call data will be stored. + pub call_data_offset: usize, + /// The amount of gas to be used for the contract call. + pub gas: u64, + /// The instructions to be executed after the contract call. + pub instructions_after_call: Bytecode, +} + +/// Set default parameters for MockCallBytecodeParams +impl Default for MockCallBytecodeParams { + fn default() -> Self { + MockCallBytecodeParams { + address: address!("0x0000000000000000000000000000000000000000"), + pushdata: Vec::new(), + return_data_offset: 0x00usize, + return_data_size: 0x00usize, + call_data_length: 0x00usize, + call_data_offset: 0x00usize, + gas: 0x1_0000u64, + instructions_after_call: Bytecode::default(), + } + } +} + +/// Generate mock EVM bytecode that performs a contract call +pub fn generate_mock_call_bytecode(params: MockCallBytecodeParams) -> Bytecode { + bytecode! { + .op_mstore( + 0u64, + Word::from_big_endian(¶ms.pushdata) + ) + .op_call( + params.gas, + params.address, + 0u64, + params.call_data_offset, + params.call_data_length, + params.return_data_size, + params.return_data_offset, + ) + .append(¶ms.instructions_after_call) + STOP + } +} diff --git a/mock/src/test_ctx.rs b/mock/src/test_ctx.rs index 5da3907901..11b8e0c5d2 100644 --- a/mock/src/test_ctx.rs +++ b/mock/src/test_ctx.rs @@ -25,7 +25,7 @@ pub use external_tracer::LoggerConfig; /// /// ## Example /// ```rust -/// use eth_types::evm_types::{stack::Stack, Gas, OpcodeId}; +/// use eth_types::evm_types::{stack::Stack, OpcodeId}; /// use eth_types::{address, bytecode, geth_types::GethData, word, Bytecode, ToWord, Word}; /// use lazy_static::lazy_static; /// use mock::test_ctx::{helpers::*, TestContext}; @@ -66,7 +66,7 @@ pub use external_tracer::LoggerConfig; /// txs[1] /// .to(accs[1].address) /// .from(accs[2].address) -/// .nonce(Word::one()); +/// .nonce(1); /// }, /// |block, _tx| block.number(0xcafeu64), /// ) @@ -76,7 +76,7 @@ pub use external_tracer::LoggerConfig; /// // Now we can start generating the traces and items we need to inspect /// // the behaviour of the generated env. /// ``` -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TestContext { /// chain id pub chain_id: Word, @@ -141,10 +141,8 @@ impl TestContext { .enumerate() .skip(1) .for_each(|(idx, tx)| { - tx.transaction_idx(u64::try_from(idx).expect("Unexpected idx conversion error")); - tx.nonce(Word::from( - u64::try_from(idx).expect("Unexpected idx conversion error"), - )); + let idx = u64::try_from(idx).expect("Unexpected idx conversion error"); + tx.transaction_idx(idx).nonce(idx); }); let tx_refs = transactions.iter_mut().collect(); diff --git a/mock/src/transaction.rs b/mock/src/transaction.rs index 9bb9b00291..1b6c6ccd1f 100644 --- a/mock/src/transaction.rs +++ b/mock/src/transaction.rs @@ -23,7 +23,7 @@ lazy_static! { vec![MockTransaction::default() .from(AddrOrWallet::random(&mut rng)) .to(MOCK_ACCOUNTS[0]) - .nonce(word!("0x103")) + .nonce(0x103u64) .value(word!("0x3e8")) .gas_price(word!("0x4d2")) .input(Bytes::from(b"hello")) @@ -31,7 +31,7 @@ lazy_static! { MockTransaction::default() .from(AddrOrWallet::random(&mut rng)) .to(MOCK_ACCOUNTS[1]) - .nonce(word!("0x104")) + .nonce(0x104u64) .value(word!("0x3e8")) .gas_price(word!("0x4d2")) .input(Bytes::from(b"hello")) @@ -39,7 +39,7 @@ lazy_static! { MockTransaction::default() .from(AddrOrWallet::random(&mut rng)) .to(MOCK_ACCOUNTS[2]) - .nonce(word!("0x105")) + .nonce(0x105u64) .value(word!("0x3e8")) .gas_price(word!("0x4d2")) .input(Bytes::from(b"hello")) @@ -47,7 +47,7 @@ lazy_static! { MockTransaction::default() .from(AddrOrWallet::random(&mut rng)) .to(MOCK_ACCOUNTS[0]) - .nonce(word!("0x106")) + .nonce(0x106u64) .value(word!("0x3e8")) .gas_price(word!("0x4d2")) .input(Bytes::from(b"hello")) @@ -119,7 +119,7 @@ impl AddrOrWallet { /// any of it's details. pub struct MockTransaction { pub hash: Option, - pub nonce: Word, + pub nonce: u64, pub block_hash: Hash, pub block_number: U64, pub transaction_index: U64, @@ -143,7 +143,7 @@ impl Default for MockTransaction { fn default() -> Self { MockTransaction { hash: None, - nonce: Word::zero(), + nonce: 0, block_hash: Hash::zero(), block_number: U64::zero(), transaction_index: U64::zero(), @@ -169,7 +169,7 @@ impl From for Transaction { fn from(mock: MockTransaction) -> Self { Transaction { hash: mock.hash.unwrap_or_default(), - nonce: mock.nonce, + nonce: mock.nonce.into(), block_hash: Some(mock.block_hash), block_number: Some(mock.block_number), transaction_index: Some(mock.transaction_index), @@ -207,7 +207,7 @@ impl MockTransaction { } /// Set nonce field for the MockTransaction. - pub fn nonce(&mut self, nonce: Word) -> &mut Self { + pub fn nonce(&mut self, nonce: u64) -> &mut Self { self.nonce = nonce; self } diff --git a/rust-toolchain b/rust-toolchain index 24b6d11f20..b6ce6a50ba 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2022-08-23 +nightly-2023-04-24 diff --git a/testool/Cargo.toml b/testool/Cargo.toml index a55df16161..8af5e49e12 100644 --- a/testool/Cargo.toml +++ b/testool/Cargo.toml @@ -16,7 +16,6 @@ external-tracer = { path="../external-tracer" } glob = "0.3" handlebars = "4.3" hex = "0.4.3" -keccak256 = { path = "../keccak256" } log = "0.4" mock = { path = "../mock" } once_cell = "1.10" @@ -33,7 +32,7 @@ yaml-rust = "0.4.5" zkevm-circuits = { path="../zkevm-circuits", features=["test"] } rand_chacha = "0.3" rand = "0.8" -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_02_02" } +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_04_20" } urlencoding = "2.1.2" diff --git a/testool/codehash.txt b/testool/codehash.txt index d52774dd98..233a3ad2d3 100644 --- a/testool/codehash.txt +++ b/testool/codehash.txt @@ -189,7 +189,7 @@ a6b511ce812a179b7a64838fbaa1d07cf7cd4b36c8060e086aa4b5fa61c971fd=600060006000600 f622260e13d7fabcc2cd9c1354d70e72614d8880f0c9682d07628c29b908e463=61600d600055630fffffff5600 d038887431975103a6ff47e75a1d183785b52fe006a23f264fc2eb621cff5b2f=600051506001600003506000515661600d60005500 3910e9d2cbc043a8ba787db1c0a21eb65944c7da49fc5485bdc90856d36e9c81=60006000600060006004356101000162010000f400 -a18f72995cc53877315840eb65f7906e0c90fddf4d49711297129d5539ce03de=600435600080600080846113885a03f45050 +a18f72995cc53877315840eb65f7906e0c90fddf4d49711297129d5539ce03de=6004356000600060006000846113885a03f45050 6648e38b61baede61a47f6b6fdd734b8dae0b2fd565c4b13caad748d3c77aa61=61600d60005560006010602002575b00 dfddcfaa0f3d8f89349db1aaa2ab93a46d95d27f80f9c11bfa55d98d52a9ef39=61600d60005560ff630fffffff5700 5f897f8795f7aaecc09acc9d1248777be536bed19a84da9fed831cfc1741b597=61600d6000556000630fffffff5700 @@ -384,838 +384,838 @@ ecc2da6d921e3a8add8be7f8e2edb8affa9362a00081d0fa10c3fcb93ad50390=73095e7baea6a6c 0f8768e84bd90b2a15fa1d47b16a20496ce88b55f6e2c461a38dcbb3aff56653=63deadbeef6000526101206000f300 529602fa473129c61090597d1c9cdc954defa61394356c24412d9b65df7591cf=600035ff00 9ee82de581ce9d8b59a47c812ff01adfff66aca971f144109aa5ec74b42d9a50=60016024351461001157600050610019565b61c0de612000525b60026024351461002b57600050610032565b6002612000525b6000604435146100445760005061004d565b61200051612040525b60016044351461005f57600050610071565b60a060020a6001026120005101612040525b60026044351461008357600050610095565b60fe60020a6001026120005101612040525b6003604435146100a7576000506100b9565b60ff60020a6001026120005101612040525b6004604435146100cb576000506100dd565b60a060020a6001026120005103612040525b67ff00ff00ff00ff006120205266ff00ff00ff00ff6120605260316004351461010857600050610139565b61200051316120205261204051316120605260016024351461012f57600061208052610138565b62010000612080525b5b603b6004351461014b5760005061017a565b612000513b61202052612040513b6120605260016024351461017257600061208052610179565b600f612080525b5b603c6004351461018c576000506101e0565b60206000612020612000513c60206000612060612040513c6001602435146101b9576000612080526101df565b7f63deadbeef6000526101206000f3000000000000000000000000000000000000612080525b5b603f600435146101f257600050610240565b612000513f61202052612040513f612060526001602435146102195760006120805261023f565b7f85ab232a015279867a1f5b5da4f9688c6c92e555c122e9147f9d13bc53c03e92612080525b5b60f160043514610252576000506102bb565b60206120206020612000600061200051611000f15060206120606020612000600061204051611000f1506001602435146102b0577f9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2612080526102ba565b63deadbeef612080525b5b60f2600435146102cd57600050610336565b60206120206020612000600061200051611000f25060206120606020612000600061204051611000f25060016024351461032b577f9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed261208052610335565b63deadbeef612080525b5b60f460043514610348576000506103ad565b6020612020602061200061200051611000f4506020612060602061200061204051611000f4506001602435146103a2577f9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2612080526103ac565b63deadbeef612080525b5b60fa600435146103bf57600050610424565b6020612020602061200061200051611000fa506020612060602061200061204051611000fa50600160243514610419577f9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed261208052610423565b63deadbeef612080525b5b60ff60043514610436576000506104b6565b61200051316120a052600060006020612000600062dead016310000000f150612000513161202052600060006020612040600062dead026310000000f150612000513161206052612020516120605103612060526120a0516120205103612020526001602435146104ad57611000612080526104b5565b611000612080525b5b61206051612020510360005561208051612020510360015560006101005500 -0e9c2dc682cf70a4bc15c56c064427939e0131445e20b4404363021ae9652610=fe -9a270b7cc69db5ebae71e25c626f1c262f9d6de5dc420a84bc7c80873af0984e=fe -2b43e66f7532c69d2c7c3bea96fd2de05c1d839322d155e3a537376d7e4c97e0=6220c0de803b80600080843c806000f3 -29e7cb59911b8f832e89bec6d412a599527b814628d898f6bdd863ff80e35f63=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f1811461015d5760f281146101715760f481146101855760fa81146101985761f1f181146101ab5761f2f181146101c15761f4f181146101d75761faf181146101ec5761f1f281146102015761f2f281146102175761f4f2811461022d5761faf281146102425761f1f481146102575761f2f4811461026d5761f4f481146102835761faf481146102985761f1fa81146102ad5761f2fa81146102c35761f4fa81146102d95761fafa81146102ee5760fd81146103035760fe81146103055760ff81146103075760f081146103095760f5811461032c5761f0f181146103525761f5f1811461037c5761f0f281146103a95761f5f281146103cc5761f0f481146103f25761f5f481146104145761f0fa81146104395761f5fa8114610462576460baccfa57811461048e5765bad0bad0bad06000526104a8565bfe5b602060008060008061ca115af195506104a8565b602060008060008061ca115af295506104a8565b6020600080600061ca115af495506104a8565b6020600080600061ca115afa95506104a8565b602060008060008063ca1100f15af195506104a8565b602060008060008063ca1100f15af295506104a8565b6020600080600063ca1100f15af495506104a8565b6020600080600063ca1100f15afa95506104a8565b602060008060008063ca1100f25af195506104a8565b602060008060008063ca1100f25af295506104a8565b6020600080600063ca1100f25af495506104a8565b6020600080600063ca1100f25afa95506104a8565b602060008060008063ca1100f45af195506104a8565b602060008060008063ca1100f45af295506104a8565b6020600080600063ca1100f45af495506104a8565b6020600080600063ca1100f45afa95506104a8565b602060008060008063ca1100fa5af195506104a8565b602060008060008063ca1100fa5af295506104a8565b6020600080600063ca1100fa5af495506104a8565b6020600080600063ca1100fa5afa95506104a8565bfe5bfe5bfe5b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6104a8565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6104a8565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506104a8565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506104a8565b8160008563c0dec0de3c81846000f094506020600080600080895af295506104a8565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506104a8565b8160008563c0dec0de3c81846000f0945060206000806000885af495506104a8565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506104a8565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506104a8565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506104a8565b6103ff6000526020600060206000806460baccfa575af195505b50846104b45760206000fd5b836104bf5760206000fd5b600051600055505050505050 -8362f328002b4d0c08e880edbc2133df809f450ed36f03185a99060b9498af41=fe -dfb3a38ca43d43a65edc3c3a0fd72cd14caac2ddc137b69acbb056f64a00aa13=602060008060008061ca115af160155760206000fd5b60206000f3 -fdcfd31543389df1a8b8bec4d90fd0ddcc4a817a631932e94529b11d53f8df17=602060008060008061ca115af260155760206000fd5b60206000f3 -65699b2d0e8558090352144d9310209295295547704451860855f4c230ce596e=6020600080600061ca115af460145760206000fd5b60206000f3 -e43e9d52cbc260ffe419a5c93a18852abe333e1bf102711765f13090f29793a2=6020600080600061ca115afa60145760206000fd5b60206000f3 -cbf22222a7bb92f681e16384d575b92e73420196573a32d298ba4475923e9e98=fe -0514a02fa4c5841a8341bd250fe42c5a340ea8830ded807baab6b37492d02139=fe +0e9c2dc682cf70a4bc15c56c064427939e0131445e20b4404363021ae9652610=fe7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 +9a270b7cc69db5ebae71e25c626f1c262f9d6de5dc420a84bc7c80873af0984e=fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 +2b43e66f7532c69d2c7c3bea96fd2de05c1d839322d155e3a537376d7e4c97e0=6220c0de803b8060006000843c806000f35050 +29e7cb59911b8f832e89bec6d412a599527b814628d898f6bdd863ff80e35f63=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101865760f2811461019c5760f481146101b25760fa81146101c65761f1f181146101da5761f2f181146101f25761f4f1811461020a5761faf181146102205761f1f281146102365761f2f2811461024e5761f4f281146102665761faf2811461027c5761f1f481146102925761f2f481146102aa5761f4f481146102c25761faf481146102d85761f1fa81146102ee5761f2fa81146103065761f4fa811461031e5761fafa81146103345760fd811461034a5760fe81146103c75760ff81146104465760f081146104c45760f581146104e75761f0f1811461050d5761f5f181146105395761f0f281146105685761f5f2811461058d5761f0f481146105b55761f5f481146105d85761f0fa81146105fe5761f5fa8114610628576460baccfa5781146106555765bad0bad0bad0600052610670565bfe7fdead60a700000000000000000000000000000000000000000000000000000000600052610670565b6020600060006000600061ca115af19550610670565b6020600060006000600061ca115af29550610670565b602060006000600061ca115af49550610670565b602060006000600061ca115afa9550610670565b6020600060006000600063ca1100f15af19550610670565b6020600060006000600063ca1100f15af29550610670565b602060006000600063ca1100f15af49550610670565b602060006000600063ca1100f15afa9550610670565b6020600060006000600063ca1100f25af19550610670565b6020600060006000600063ca1100f25af29550610670565b602060006000600063ca1100f25af49550610670565b602060006000600063ca1100f25afa9550610670565b6020600060006000600063ca1100f45af19550610670565b6020600060006000600063ca1100f45af29550610670565b602060006000600063ca1100f45af49550610670565b602060006000600063ca1100f45afa9550610670565b6020600060006000600063ca1100fa5af19550610670565b6020600060006000600063ca1100fa5af29550610670565b602060006000600063ca1100fa5af49550610670565b602060006000600063ca1100fa5afa9550610670565bfe7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150fe7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103c257640badbadbad6000525b610670565bfe7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561044157640badbadbad6000525b610670565bfe7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150fe7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156104bf57640badbadbad6000525b610670565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c610670565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c610670565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af19550610670565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af19550610670565b8160008563c0dec0de3c81846000f0945060206000600060006000895af29550610670565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af29550610670565b8160008563c0dec0de3c81846000f094506020600060006000885af49550610670565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af49550610670565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa9550610670565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa9550610670565b6103ff600052602060006020600060006460baccfa575af195505b5084151561067e5760206000fd5b83151561068b5760206000fd5b600051600055505050505050 +8362f328002b4d0c08e880edbc2133df809f450ed36f03185a99060b9498af41=fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 +dfb3a38ca43d43a65edc3c3a0fd72cd14caac2ddc137b69acbb056f64a00aa13=6020600060006000600061ca115af1151560195760206000fd5b60206000f3 +fdcfd31543389df1a8b8bec4d90fd0ddcc4a817a631932e94529b11d53f8df17=6020600060006000600061ca115af2151560195760206000fd5b60206000f3 +65699b2d0e8558090352144d9310209295295547704451860855f4c230ce596e=602060006000600061ca115af4151560175760206000fd5b60206000f3 +e43e9d52cbc260ffe419a5c93a18852abe333e1bf102711765f13090f29793a2=602060006000600061ca115afa151560175760206000fd5b60206000f3 +cbf22222a7bb92f681e16384d575b92e73420196573a32d298ba4475923e9e98=fe7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe +0514a02fa4c5841a8341bd250fe42c5a340ea8830ded807baab6b37492d02139=fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd 38f547750047a472fa72fec253a4527ebd73e74d1ef59d71ab51dff7714d7005=6000ff -20eab3c8d300ca0bc99f12bab2359d3afe554d389def84f35d804da88303ecbd=60003560008103600b57fe5b600181036000526020600060206000806460baccfa575af1602c5760206000fd5b60206000f3 -42f7c2f75c75cef03d21d28ea88ee056455a41df4fa69e84448ada32ee1e20cc=610200600080f050 -5049b9ef30dce1c4fc833042e5e868e76b8447d2a38c364ac574f3e5b8e94564=5a615a1701610200600080f550 +20eab3c8d300ca0bc99f12bab2359d3afe554d389def84f35d804da88303ecbd=6000356000811415603557fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af1151560595760206000fd5b60206000f350 +42f7c2f75c75cef03d21d28ea88ee056455a41df4fa69e84448ada32ee1e20cc=61020060006000f050 +5049b9ef30dce1c4fc833042e5e868e76b8447d2a38c364ac574f3e5b8e94564=5a615a170161020060006000f550 9a76f62b8b1305059c0392f4d8293bc5d6f13ff4a3502f35a56ffea9f3102e91=00 -c71639a1d42dc0079620a8748605a805df5009b435adcef233671749fcd6fea8=610100600061010060008061ca115af150 -cf2810c927b9404e13cfd3755b60e6da83a777019e9290ba13bc2ef72028c384=610100600061010060008061ca115af250 +c71639a1d42dc0079620a8748605a805df5009b435adcef233671749fcd6fea8=61010060006101006000600061ca115af150 +cf2810c927b9404e13cfd3755b60e6da83a777019e9290ba13bc2ef72028c384=61010060006101006000600061ca115af250 7965d59aacd6cb567c346703fe84168ca1247909772f914c9481348eecab24c3=6101006000610100600061ca115af450 caead2353c6dfe042005dcced75ef872d4485c09b0a608a0142f7e6617963e5f=6101006000610100600061ca115afa50 635416a136a2c77e605894a10cbb3034338efdc3261571136d4b5689c377b7a2=61b0005150 d5fa7be3053a269955f2681e3456d906ed87394a2a8493c729b3582b438c12b5=60ff61b00052 a340c6e10bcd05d0f73c7b697a98ed582f2244cb0c37315ec4743247e51c173b=60ff61b00053 0f86028bf04f9934739e785ea0362e83beb4eb2bf7e941deb606dcc2f571e38f=61beef60002050 -4c10255b7e0067acc2ebd06666664cbb0e06b57f5e2da8d7f9065c8b6dd44904=61ca11803b600080833c50 -430d28ac555e5377b78dcc35ef76ea915c1f1830b34cae9638b2b46836a41f9b=600061ea6060043562c0de00015b6001838303111560445760028284010460008060008060008686f1600081036033578194505b60018103603e578193505b5050600d565b81600055505050 +4c10255b7e0067acc2ebd06666664cbb0e06b57f5e2da8d7f9065c8b6dd44904=61ca11803b60006000833c50 +430d28ac555e5377b78dcc35ef76ea915c1f1830b34cae9638b2b46836a41f9b=600061ea6060043562c0de00015b60018383031115604957600282840104600060006000600060008686f160008114156036578194505b60018114156042578193505b50505b600d565b81600055505050 938ee3f4c10a7ca8880a8893537cad1a5c5be41c18cf679b56d84ff0a7c86ba9=6009600860076006600560046003600260010c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 230ad8b2bee2664f3917e0eaa4fb99565b9b2989a0f5cd7847a2fcdc576b7e69=6009600860076006600560046003600260010c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -0c178b5973abc40e8a8e1e36c759cfbbcea1c5272ee5efafef30b2f4b258caa3=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260010c7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260010c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260010c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260010c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260010c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260010c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260010c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +0c178b5973abc40e8a8e1e36c759cfbbcea1c5272ee5efafef30b2f4b258caa3=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260010c7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260010c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260010c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260010c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260010c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260010c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260010c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 44988850b8a12a167c5cd7dddcd014166d37ab13ebe839fad024b62de3e660cf=6009600860076006600560046003600260010c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 96475191b3bcb0ad37a3b6df38be56139deaec7e02f1487bad194953eca95d21=6009600860076006600560046003600260010c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe cb7adbe8f8ee805ca68468f69f10f910b3853a98df2bdf577927b75fb11285e7=6009600860076006600560046003600260010c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -e3e1455165b47b69c29d61a2bc5078d5ad4d65e92b9687cb7180a3574e37c24f=600035600081036046576009600860076006600560046003600260010c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +e3e1455165b47b69c29d61a2bc5078d5ad4d65e92b9687cb7180a3574e37c24f=60003560008114156047576009600860076006600560046003600260010c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 17685510e75e3f74b8218b94229451ec1cc228ca37654b9b4634355eae2dbf9c=6009600860076006600560046003600260010d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 23951503023e580b1117ac0898cfba8b28e874ff8ecbb10741a11fc490966c44=6009600860076006600560046003600260010d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -d5f2777b29693306828cec239d9c18c0712efd524dc6f747104d87feb3448b21=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260010d7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260010d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260010d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260010d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260010d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260010d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260010d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +d5f2777b29693306828cec239d9c18c0712efd524dc6f747104d87feb3448b21=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260010d7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260010d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260010d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260010d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260010d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260010d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260010d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 62a6f08909838c6f7fd571901020f12c41c6915b77466aa87049392d55708787=6009600860076006600560046003600260010d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 8225f662322132ba756ddf40b9d38ee77569900e50f6017356c85ecefda4f0a4=6009600860076006600560046003600260010d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe ab0505ea2db66ed1dcae4b3ca8c86eb81a6cd7263bc4dbd23019560079f9a2f9=6009600860076006600560046003600260010d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -1de16d5bf91595f686c8a3e0b6759cd058cc82e743b0ad4b43bd5aa97b767c88=600035600081036046576009600860076006600560046003600260010d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +1de16d5bf91595f686c8a3e0b6759cd058cc82e743b0ad4b43bd5aa97b767c88=60003560008114156047576009600860076006600560046003600260010d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 1068a1249d4c3b6b6f83ec022b0b09aba43bbc01520e2c6b2b42a546625fa486=6009600860076006600560046003600260010e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 d92166b2a2f6ab44b2fc9f7c8e177c7a20b78ac7d0f1f7ca688883cb378c4c64=6009600860076006600560046003600260010e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -8eda3038d723b0f5c25b04f2fce101c2316df4c50ba5bd7741e70a044348f7be=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260010e7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260010e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260010e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260010e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260010e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260010e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260010e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +8eda3038d723b0f5c25b04f2fce101c2316df4c50ba5bd7741e70a044348f7be=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260010e7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260010e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260010e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260010e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260010e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260010e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260010e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 253155f71a6ed583e6b384069ad9fb1b6702e97a8b32df4b87224ca37c84f685=6009600860076006600560046003600260010e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 b411b634893a0d8a4d864f6dc3fd63bf69d2094b74608861b86f17f3dc63a3d1=6009600860076006600560046003600260010e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 2af31abacbbeaaf462fe2936c4b2d05649426985fe42b4061019a912a0f797d8=6009600860076006600560046003600260010e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -6a0bd10d9b4b130170f5ddb04a7738b14aabe4c805af9235f51f5ec4267dd2cf=600035600081036046576009600860076006600560046003600260010e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +6a0bd10d9b4b130170f5ddb04a7738b14aabe4c805af9235f51f5ec4267dd2cf=60003560008114156047576009600860076006600560046003600260010e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 c907e58ddf59dde5cda116a73a3be28226593da0b0278cd5b7d675f646493eb9=6009600860076006600560046003600260010f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 139fa7c0b243adb5fd1ca31d3af7d2bee20772d5b2a603a763879b4c984c6084=6009600860076006600560046003600260010f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -ee2b3952f8672b7367b2b1ed5b47135309c31ee656bc48b11259d3b5828e9f2e=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260010f7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260010f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260010f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260010f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260010f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260010f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260010f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +ee2b3952f8672b7367b2b1ed5b47135309c31ee656bc48b11259d3b5828e9f2e=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260010f7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260010f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260010f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260010f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260010f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260010f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260010f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 8fa57f0ab296fa45e253f78c45e7d71aae05d2959f1585a24cab0343397dc611=6009600860076006600560046003600260010f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 41e71afd58b254195323c77a3cf0c3025936553d7ebe1db40db2a235ae09e4d7=6009600860076006600560046003600260010f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 0ef59d8bebe83d676f9e04d12156ade7dca0289de969382f170e878c7ffa2a97=6009600860076006600560046003600260010f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -400fbe7e32c200832d7596b58ff78cbceaa998d7565891132f8718e82241f39b=600035600081036046576009600860076006600560046003600260010f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +400fbe7e32c200832d7596b58ff78cbceaa998d7565891132f8718e82241f39b=60003560008114156047576009600860076006600560046003600260010f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 1cc9592eb090e413d93224c9c40e9f0ec4bdcbdcd87c909ffeb7e78469478ed1=6009600860076006600560046003600260011e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 30b094e2b26a74f73b5f9999c6334032fb40d213ac1a42e974305ee36fc3484f=6009600860076006600560046003600260011e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -b2854feb50f32aaccfa575fb4860ccde6ff989d0ba1ebf559dcb47bd9a5bc0cd=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260011e7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260011e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260011e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260011e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260011e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260011e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260011e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +b2854feb50f32aaccfa575fb4860ccde6ff989d0ba1ebf559dcb47bd9a5bc0cd=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260011e7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260011e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260011e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260011e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260011e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260011e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260011e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 568e661d7df1612a19ba92b59655ad16d37b5f892806110e9206106842850d29=6009600860076006600560046003600260011e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 343f9e016b3d1873b1eeab5bac585e76ecb5458c3e6329d9e78729b1a5e8239d=6009600860076006600560046003600260011e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe c51830e3c8437a8e0fb86ae9650a05a5ed4806ad8ce7b6426fafefbfd638e618=6009600860076006600560046003600260011e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -da1e4c58298849b0870a90846bf251ee62076a1c96e882d90550d62c2cee6af5=600035600081036046576009600860076006600560046003600260011e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +da1e4c58298849b0870a90846bf251ee62076a1c96e882d90550d62c2cee6af5=60003560008114156047576009600860076006600560046003600260011e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 58200d9da216bae346a002e950da89f7c8f583330942315f4d033697e0eaf389=6009600860076006600560046003600260011f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 1160b9999f89fdb04e0c310ab5417e25b4a50326a37ce3d6669753c72a749164=6009600860076006600560046003600260011f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -983e12fd64a4c75ea68b3dd69813ee55ffa87f5d8d9e597d2d10572f9596c334=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260011f7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260011f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260011f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260011f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260011f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260011f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260011f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +983e12fd64a4c75ea68b3dd69813ee55ffa87f5d8d9e597d2d10572f9596c334=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260011f7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260011f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260011f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260011f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260011f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260011f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260011f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 b5e02815891113e58d0a6f40c1c09cfd3e2af3ed8ac1c2e14b6cd3b0ab98a6ae=6009600860076006600560046003600260011f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 44a28f59bab3e9db86d9f1a2039c2344dd275c39a183817bbb9b342963fc8853=6009600860076006600560046003600260011f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 389b491a506152871ab588c30fc27545526d502942ebf1f76b808d2ac84fc079=6009600860076006600560046003600260011f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -fb321121abdcb461309886fd0c9afa0bb484a27abd369aa6cc6131fe7fe51677=600035600081036046576009600860076006600560046003600260011f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +fb321121abdcb461309886fd0c9afa0bb484a27abd369aa6cc6131fe7fe51677=60003560008114156047576009600860076006600560046003600260011f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 82671344199c33734491b4e2824df44f9ed75b0feb1246d4507f12e59a297f0d=600960086007600660056004600360026001217fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 b521eb06a22eb8bc3373ea257eaa896e1f7aada6b1bd5703706666d3c7270539=600960086007600660056004600360026001217fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -59a1a02e81319c8c4fdf78bc0ef07495b2160978b45e0178a72a2ac3ee4aafb3=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001217fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001217fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001217fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001217fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001217fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001217fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001217fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +59a1a02e81319c8c4fdf78bc0ef07495b2160978b45e0178a72a2ac3ee4aafb3=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001217fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001217fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001217fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001217fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001217fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001217fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001217fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 812a84a0e6d3bf51bc645998f0f1c628964001222d7370ddafcefe0a88238afa=600960086007600660056004600360026001217fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 1817666e09c78ce0915367cf498574c3ae5f6f5964a18a6c9bbc6794d32bce20=600960086007600660056004600360026001217fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 91dfdcb44d5446d5e14a976bb0b00a7e2082523eae4494a9e57261eec389b6e8=600960086007600660056004600360026001217fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -ce6a46d3faa4f776895fc2dff6080147e39e15ce4cbe4d110fd61efd22c3d663=60003560008103604657600960086007600660056004600360026001217fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +ce6a46d3faa4f776895fc2dff6080147e39e15ce4cbe4d110fd61efd22c3d663=6000356000811415604757600960086007600660056004600360026001217fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 b777769be0e6d61f5de811b1f25d1421a2cef7a5e848906ff1e5187ff6a99713=600960086007600660056004600360026001227fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 e8a7f37f57061b04b44539079f0af364057ddf19662e6d0016f44e587315f6db=600960086007600660056004600360026001227fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -760ff99e0c688e06674e4afedb6212ecf2b58480a41684749d0137376c2aaa41=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001227fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001227fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001227fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001227fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001227fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001227fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001227fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +760ff99e0c688e06674e4afedb6212ecf2b58480a41684749d0137376c2aaa41=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001227fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001227fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001227fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001227fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001227fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001227fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001227fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 ee15e8b1def627c248e0ef2b349e642c28930100a66719b4d9d3b59c32e8d2c6=600960086007600660056004600360026001227fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 c9ef430aa137465df086e05e6883183d69b4211322ebc5b3dfebc0ad5e60adc6=600960086007600660056004600360026001227fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe c12e66e38eb2f29bce1281e9ba5cd1c7b57263a77426e15091292df57bd30ba7=600960086007600660056004600360026001227fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -8418ea603b4e88c12557e883203943437c425dea592d79c7a1e4f5afb07c7399=60003560008103604657600960086007600660056004600360026001227fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +8418ea603b4e88c12557e883203943437c425dea592d79c7a1e4f5afb07c7399=6000356000811415604757600960086007600660056004600360026001227fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 3a071d7023fb441bccc3c6aee65e5ce05b2918d1eb4e7ec9b5e2f4dc2c401740=600960086007600660056004600360026001237fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 71daa9e7174bf24991b7ce85ea872819e5f50753a06bcb9dca91922054124698=600960086007600660056004600360026001237fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -89f20a0e69e5f391e03b60d27e751ee3fb3e46a210b55c1e2c6cc74d2ef3ca89=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001237fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001237fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001237fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001237fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001237fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001237fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001237fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +89f20a0e69e5f391e03b60d27e751ee3fb3e46a210b55c1e2c6cc74d2ef3ca89=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001237fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001237fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001237fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001237fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001237fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001237fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001237fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 f2331e094cf1086abd868e108d1549172e7bb4dc30d51edba9b262a9060a4761=600960086007600660056004600360026001237fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 b9b145ac0daaf21ea5e83d3f7e8d0ec148136c453b220834f2cc7002b4bcc343=600960086007600660056004600360026001237fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe b15ddb47eb4585801737e561cc5d2caaa4ad359adf6ce6cb22b4f4e2b331daa7=600960086007600660056004600360026001237fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -efbc58adac9e1d333512bcc22be5f575ca088dce0f47710671b465cd85068c7d=60003560008103604657600960086007600660056004600360026001237fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +efbc58adac9e1d333512bcc22be5f575ca088dce0f47710671b465cd85068c7d=6000356000811415604757600960086007600660056004600360026001237fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 b89d4effa036fdd9714ea8d2909ac77aacc158a9d5f733a49c90f00af95c7a40=600960086007600660056004600360026001247fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 bf620126fdaa0bbd11bf7624530fe444f68eb0ca8511e41316cb8c119e05c292=600960086007600660056004600360026001247fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -113ad2cfcf913857d0dc5bbaac973be86da8da32114708fd235aef697aff0e55=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001247fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001247fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001247fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001247fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001247fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001247fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001247fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +113ad2cfcf913857d0dc5bbaac973be86da8da32114708fd235aef697aff0e55=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001247fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001247fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001247fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001247fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001247fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001247fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001247fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 f69e49d7e6b3de110b1be4e148e271e1b9ba7d061ce06a202f6353ec4690299a=600960086007600660056004600360026001247fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 b7dff3fc909477303c54bec6ed33be91b00b66f8c6ac80272cd379c3171b9b24=600960086007600660056004600360026001247fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe d80182d7a88eb5a2daf5b0ea6cd6a707a1633bc99ff96952c953c9df56d9a81e=600960086007600660056004600360026001247fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -26b5bc19c87e4c5d225dd5e8bcec86089b8d177a4139c29050f87e7ca4eb2720=60003560008103604657600960086007600660056004600360026001247fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +26b5bc19c87e4c5d225dd5e8bcec86089b8d177a4139c29050f87e7ca4eb2720=6000356000811415604757600960086007600660056004600360026001247fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 07dba075729f7d3a9ef6119d5c6675eb56146597942582040e967c7390dde883=600960086007600660056004600360026001257fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 bd9d506b7f4f2a50d532e0e83e5609b54dfce0d54a8c54c3a1c225d9b2783eb4=600960086007600660056004600360026001257fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -3fe65acb20e830f3619ef0632503b60eca4336d71bc659ad0ad231da07e80652=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001257fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001257fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001257fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001257fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001257fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001257fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001257fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +3fe65acb20e830f3619ef0632503b60eca4336d71bc659ad0ad231da07e80652=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001257fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001257fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001257fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001257fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001257fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001257fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001257fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 060a850ac93dee7f0f676de2852fe8ffd2f198696fc61938ff14ca04c62eb50b=600960086007600660056004600360026001257fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 54c5078af4523181c6ee17a52f63a18029faae11b06f7328da3618b34ae738eb=600960086007600660056004600360026001257fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 12562f84d5161185148e125f2ab2daebcfc5de79299459bfa6f1d56e2219644f=600960086007600660056004600360026001257fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -eb53fe99ce99a4bb6770345142474aaf1a0cab1a7d647efc0d205796611c50c4=60003560008103604657600960086007600660056004600360026001257fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +eb53fe99ce99a4bb6770345142474aaf1a0cab1a7d647efc0d205796611c50c4=6000356000811415604757600960086007600660056004600360026001257fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 6306f54c0faccf8f2946b4527b8a760f5caf5734fb951557efdc7cc2b7265020=600960086007600660056004600360026001267fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 dab38748f11aa52dc375f7c80dabb1f52c66ee8fd11eb77a8b170fc11e061df7=600960086007600660056004600360026001267fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -547da97da2db8099372801d9957365e6abef034a61b062db00b448c1d72300fb=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001267fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001267fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001267fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001267fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001267fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001267fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001267fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +547da97da2db8099372801d9957365e6abef034a61b062db00b448c1d72300fb=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001267fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001267fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001267fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001267fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001267fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001267fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001267fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 05bb935133313a3a5e317736000032b833292e0569793a0976e7d08f6cc627e3=600960086007600660056004600360026001267fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 e0565c2e19f768dc678aed3b504c568bf98a98e870942f4153ddea01d9b17f9a=600960086007600660056004600360026001267fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 3ba1210d65ebd6e8c729050cd990472bae1d52e5ffc50ff724c8a474569c29cc=600960086007600660056004600360026001267fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -ac14c89491ab4f9496852b6faab382ab873000a20dd939df3c754367a66754a4=60003560008103604657600960086007600660056004600360026001267fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +ac14c89491ab4f9496852b6faab382ab873000a20dd939df3c754367a66754a4=6000356000811415604757600960086007600660056004600360026001267fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 eef1a17bec5196611cef1df091a0a6dde2c5b11d6a6188e51f617f0eb2bd4362=600960086007600660056004600360026001277fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 f7061682ca29c7948fbc253ef98d164d3fc9adf4706e186df381154aeaf040d2=600960086007600660056004600360026001277fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -e8c24645ea676418428a4efcac2ad3daedf009e6a4279013c0fc44b01c0989d7=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001277fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001277fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001277fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001277fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001277fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001277fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001277fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +e8c24645ea676418428a4efcac2ad3daedf009e6a4279013c0fc44b01c0989d7=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001277fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001277fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001277fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001277fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001277fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001277fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001277fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 317abde5a92153497b8bf486243c4656e43437380e6e3ad03ee7e170a989addc=600960086007600660056004600360026001277fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 29fa741fc890c810301dc9c0e20eb8e2e5ce9662cabffd9e8e8667e400acbc1c=600960086007600660056004600360026001277fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe f00036cdd965cfff85830d094bc0fbb3e0e9f9396cf456f5114555d6bf7a3d28=600960086007600660056004600360026001277fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -18c2b63211ef4a0dff198473fca3cb1253b164fb917ae1cc27bf7432f877719a=60003560008103604657600960086007600660056004600360026001277fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +18c2b63211ef4a0dff198473fca3cb1253b164fb917ae1cc27bf7432f877719a=6000356000811415604757600960086007600660056004600360026001277fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 578ed7aea34fb9053bc80d93af61003d2ab5c2f9683f448d0b010d666e8646e4=600960086007600660056004600360026001287fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 94b5d42885ee094de03227f6cf52c99df51f7e7108c3522167b18fe424da7f45=600960086007600660056004600360026001287fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -242013a89507c05b17c04fad0c6a1fd52ec3fa384f40b395bd3681764cc8f4b5=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001287fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001287fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001287fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001287fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001287fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001287fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001287fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +242013a89507c05b17c04fad0c6a1fd52ec3fa384f40b395bd3681764cc8f4b5=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001287fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001287fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001287fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001287fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001287fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001287fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001287fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 1d739df75b88d7a314195f4f6c2f103966591fbc40af5d85ea7b260cc4521b39=600960086007600660056004600360026001287fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 4675b56405f82e43e19afe81bd3178b59679476fa47adc0fc1c0ef0668180933=600960086007600660056004600360026001287fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe d9e877924f288d2a84a86fafd7ca9b9cb75ba9c4eb501fcc9cf92f81c21387f5=600960086007600660056004600360026001287fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -1561ca76178e8448f44830c669acf2e17d4dfb2946d78671f1ef5ef056a91236=60003560008103604657600960086007600660056004600360026001287fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +1561ca76178e8448f44830c669acf2e17d4dfb2946d78671f1ef5ef056a91236=6000356000811415604757600960086007600660056004600360026001287fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 9845c3d252553432dc173193bfa650823b5beab40b61737394fc0d501f675ce1=600960086007600660056004600360026001297fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 b5a60844fe55cc6a04b5a58c0dd0f7c96d6053a66bd6bd6b8f9c9ce3fe2c1b13=600960086007600660056004600360026001297fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -0ca96f543bb78527612ba046247c61bb79ac58caef9e0aa5cd2d9fbdebcfe084=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001297fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001297fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001297fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001297fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001297fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001297fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001297fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +0ca96f543bb78527612ba046247c61bb79ac58caef9e0aa5cd2d9fbdebcfe084=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001297fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001297fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001297fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001297fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001297fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001297fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001297fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 923910e3d6e8be73445eb3f993ff3d52d4a9eb2ccfb110d7bcff3053d5f8a18b=600960086007600660056004600360026001297fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 47459c3386e36a6463108e07ed3a84a93579094d7a6c45acf3b6c836c7fc1d93=600960086007600660056004600360026001297fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 51df9d18d6e18041e83f5814c0601a00925b8d4b1e66740d07cbcfb5cdd4e569=600960086007600660056004600360026001297fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -e29cdda491ea2a40f56672b3d4dca382dc051fa751a9a8f945365cb9beb12c5b=60003560008103604657600960086007600660056004600360026001297fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +e29cdda491ea2a40f56672b3d4dca382dc051fa751a9a8f945365cb9beb12c5b=6000356000811415604757600960086007600660056004600360026001297fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 1667790fc49892808e26dce42b9067024bdf7447c14be38fb4f0fe7affc70315=6009600860076006600560046003600260012a7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 0e272b725152fa9efc4db1e64ea68d0d18409eb12a4b5b860e43ce7d2c73c763=6009600860076006600560046003600260012a7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -f2c7922a8b3563bfe558a8f90297876c17a1aa7406ac289377f11116a5f25125=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260012a7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260012a7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260012a7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260012a7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260012a7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260012a7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260012a7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +f2c7922a8b3563bfe558a8f90297876c17a1aa7406ac289377f11116a5f25125=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260012a7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260012a7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260012a7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260012a7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260012a7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260012a7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260012a7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 2fed7310cc2695527528578e0627bcbe1b48027db89c32d9ec573296daf407e9=6009600860076006600560046003600260012a7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 4c7781b662b945645193487b17f7bf6547b9e7e68b9bee00333a79741d3bb7e5=6009600860076006600560046003600260012a7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe e611756da0056f0b984c4171711eb0f66e4b424c8da0258c9a3a45844d8ba412=6009600860076006600560046003600260012a7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -5fe2aedfa5a4f2007d352078679cf620e88aa4772dfa8039b45565b6ac331e45=600035600081036046576009600860076006600560046003600260012a7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +5fe2aedfa5a4f2007d352078679cf620e88aa4772dfa8039b45565b6ac331e45=60003560008114156047576009600860076006600560046003600260012a7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 0948b8c75705dfc2c5eb08854fdb967f3c2ba10b13cf9918586d3947e5a4dbc9=6009600860076006600560046003600260012b7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 89190d44147f4de08c240cae6e964fb264d896bfb55b3144e29ce300fdc96f99=6009600860076006600560046003600260012b7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -4600118ac1c8f2372c55101463872f47235d57258d4a4111fb5b52caf9326629=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260012b7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260012b7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260012b7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260012b7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260012b7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260012b7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260012b7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +4600118ac1c8f2372c55101463872f47235d57258d4a4111fb5b52caf9326629=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260012b7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260012b7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260012b7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260012b7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260012b7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260012b7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260012b7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 f349b9de8a276965f5e10044c8216ea5a15357f1181d486ea43cabf4ec7b35f9=6009600860076006600560046003600260012b7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 94bb4b7630e6236592eb2022c26af4348b5c4b2a6c70df6bfc4af5c9865f4b3b=6009600860076006600560046003600260012b7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe f998b8658be28a6457ea25769ba94ee4393d48201cb7227e55ff8748591a1188=6009600860076006600560046003600260012b7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -b7d856c286c7e4524c27bef823fb2862ed08e5f782a4897d930e5e51d000f0ce=600035600081036046576009600860076006600560046003600260012b7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +b7d856c286c7e4524c27bef823fb2862ed08e5f782a4897d930e5e51d000f0ce=60003560008114156047576009600860076006600560046003600260012b7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 d330bae48ebd477135f0170a55250b1ea41526c929f4656ec937096354570bfa=6009600860076006600560046003600260012c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 f03e8fe17989acfc4a6767d5d181e6740e488f351c8805316480a128c6fc8554=6009600860076006600560046003600260012c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -570068bb31bf808d763a485c572af81549bd7ebf4fed4090529f430fc2bce49a=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260012c7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260012c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260012c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260012c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260012c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260012c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260012c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +570068bb31bf808d763a485c572af81549bd7ebf4fed4090529f430fc2bce49a=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260012c7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260012c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260012c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260012c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260012c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260012c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260012c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 3cc1dd35f1bf78480d13c4c894480977f2b300ca54f67ef06b5be08f35c0f4a3=6009600860076006600560046003600260012c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 95c308319e3d077daad87a4f138752a2797f30ac8d9ea936a9591519c336e490=6009600860076006600560046003600260012c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 1987e07f43cec087446d698e5300c17ecc08a957270483ab7bf61b3fd88a32ee=6009600860076006600560046003600260012c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -075a655ef405ee084855076ead77114ce16ba75534c41c2df8a227c2abc38cbe=600035600081036046576009600860076006600560046003600260012c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +075a655ef405ee084855076ead77114ce16ba75534c41c2df8a227c2abc38cbe=60003560008114156047576009600860076006600560046003600260012c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 6fc0db48d2bfe1a44a02cb9f2d5603ecc3ab98d5ca6869ee88880913bf165595=6009600860076006600560046003600260012d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 67af5a87f59b54b6495aebb08b1e00b70beb0a985639b252805902c91cf5b0da=6009600860076006600560046003600260012d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -d0d4130baf835a9ac84c3046960fb2cbeac37445dbdda5c78dccd9c4487d4bc7=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260012d7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260012d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260012d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260012d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260012d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260012d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260012d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +d0d4130baf835a9ac84c3046960fb2cbeac37445dbdda5c78dccd9c4487d4bc7=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260012d7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260012d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260012d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260012d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260012d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260012d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260012d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 709f4839fe8d0f0884a050d814b1d626a9f8d0567811d6739aa7076e428e3ed2=6009600860076006600560046003600260012d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 60146ee47fa94b62afd1de2aa8216652b9c4fca5de013945572c1ad7b8f7ab21=6009600860076006600560046003600260012d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe d1ad091324d6cafb3a6e7b965384d37f945563186e93ffb91b25803d8accde0d=6009600860076006600560046003600260012d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -8737af588495dd726c53d35e0b7798a7ab27f475195d025ac44e2ed2e9f21788=600035600081036046576009600860076006600560046003600260012d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +8737af588495dd726c53d35e0b7798a7ab27f475195d025ac44e2ed2e9f21788=60003560008114156047576009600860076006600560046003600260012d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 17435fc237350074a11f982c4c5076ae982d57dde6cc8cbdae863fe5c73170b0=6009600860076006600560046003600260012e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 78608b6aafca30a01a4e2d58553b28a8ec3a08360756a6867ed028f05d58543e=6009600860076006600560046003600260012e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -142ca2d74e56296478c5216fe33a961ad1f909940461e04549921c6baae0d310=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260012e7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260012e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260012e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260012e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260012e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260012e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260012e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +142ca2d74e56296478c5216fe33a961ad1f909940461e04549921c6baae0d310=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260012e7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260012e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260012e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260012e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260012e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260012e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260012e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 0c7f89070797590b8d871cd2718ddced23da1f6d9e2573d262df4ff0a0a0796d=6009600860076006600560046003600260012e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 5cc6b3c72dc49a11c01ba68ab3ca4811106946c2d4d346d537c287b96d5cadcf=6009600860076006600560046003600260012e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 3a72c1e0c73c0237c7865cb94e391f7b51ffcacafc2d7fed601932b62baaf063=6009600860076006600560046003600260012e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -f86bf1c45ec93fa6ae2601ba8809f6b28166dfcbdd4ca89bfba5085553d5193d=600035600081036046576009600860076006600560046003600260012e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +f86bf1c45ec93fa6ae2601ba8809f6b28166dfcbdd4ca89bfba5085553d5193d=60003560008114156047576009600860076006600560046003600260012e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 414a2436837bfb656681f8d74b5e8cf83f54992a290da8f0b624cb5ea3caf7d3=6009600860076006600560046003600260012f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 5d1bbe989d4ae4e0799b20fa98b23ef667b3a1e32452762bf4994b1019e2dd97=6009600860076006600560046003600260012f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -6623366f6cd4c7b146068162d28f663f041bc5c8b3339ce80cce22aa2f4ae91b=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260012f7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260012f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260012f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260012f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260012f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260012f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260012f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +6623366f6cd4c7b146068162d28f663f041bc5c8b3339ce80cce22aa2f4ae91b=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260012f7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260012f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260012f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260012f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260012f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260012f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260012f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 5365137e4bd42cb95f7f94b3863c699ef13eea46451482b07141ee2f45c29998=6009600860076006600560046003600260012f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 3a265e96cc67edc9fd622e9c8f09c11fa540441babc2302c4d213941c4af6d1b=6009600860076006600560046003600260012f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 986b406a1b50d653ad5a07a8721472c38bfb52e3fe523866c11255875968e919=6009600860076006600560046003600260012f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -a88e183c05e66acfb9f4b0ab63d1fc7be8b983a7bf5b74cc42bee90d8f5438fc=600035600081036046576009600860076006600560046003600260012f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +a88e183c05e66acfb9f4b0ab63d1fc7be8b983a7bf5b74cc42bee90d8f5438fc=60003560008114156047576009600860076006600560046003600260012f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 9a4714fbdf05813e64e03dbbed4687685e7d5b266a5a3b263166d96f05aedc17=600960086007600660056004600360026001497fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 9c38f5b3aebf6c5d2ce8ceb966acb31ae10357ae88d33119516959187ed04c8e=600960086007600660056004600360026001497fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -7cddaf41ea4d16170cf19089d3bc305dca174e2b1732dee4903af75bdbeb1b4c=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001497fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001497fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001497fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001497fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001497fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001497fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001497fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +7cddaf41ea4d16170cf19089d3bc305dca174e2b1732dee4903af75bdbeb1b4c=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001497fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001497fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001497fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001497fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001497fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001497fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001497fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 9d577cf94428220da28c0b8a41f410b522020d65b5c8e17d639008b8bee37baf=600960086007600660056004600360026001497fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 a46a48acd0af205e877799d0c8d3dabf0ff925f3bbbee5d96fb194fe24b3bf82=600960086007600660056004600360026001497fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe bf15ee2119a8c776a780165974b8e644ec7fad1137b404e3ae11108add851b60=600960086007600660056004600360026001497fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -c1c3991aeac2c3773ca81ac2435e567107bb91dbfc19092841024a675db206dd=60003560008103604657600960086007600660056004600360026001497fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +c1c3991aeac2c3773ca81ac2435e567107bb91dbfc19092841024a675db206dd=6000356000811415604757600960086007600660056004600360026001497fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 5f46643c97f2b24211b5a01eb1ec97abcd8e40357a4833880ba08936bd09f86b=6009600860076006600560046003600260014a7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 d5bec6c1d856f60d273240f18376a09553c3b6c6e0b2e093ae2b86c9ede53840=6009600860076006600560046003600260014a7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -805a168fe93920941a8de3eed871020a0cc71466afbfdbfebf23014a272f5096=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260014a7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260014a7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260014a7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260014a7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260014a7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260014a7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260014a7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +805a168fe93920941a8de3eed871020a0cc71466afbfdbfebf23014a272f5096=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260014a7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260014a7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260014a7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260014a7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260014a7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260014a7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260014a7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 1c772d49b2a475db9c074ff23856a86d2cbe0401d9d3662cf754e24744c65d10=6009600860076006600560046003600260014a7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 581ed05876c14509f37fa1a0cdf5a1e086119926da143205885c5404ad708b20=6009600860076006600560046003600260014a7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 1d716efaddb4f7e0ddefb5aa8cfb6ef8e92197310aa684e6604f1deacda59608=6009600860076006600560046003600260014a7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -489a47f2e1cad1937a3d2716ae929cfce2c7427b5c5bab7721ab15c1c43bacd8=600035600081036046576009600860076006600560046003600260014a7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +489a47f2e1cad1937a3d2716ae929cfce2c7427b5c5bab7721ab15c1c43bacd8=60003560008114156047576009600860076006600560046003600260014a7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 0dced3fc01f029d892ea79b06296941552085cc39178218212ad9eced158c3bb=6009600860076006600560046003600260014b7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 a2418bc5f50747f3642827b929360935566ad83951d719eeab6f09a3405566c8=6009600860076006600560046003600260014b7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -b596be88d8632a3695fbf7d142d273f419a824262d2c33114aafa7321268cfeb=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260014b7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260014b7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260014b7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260014b7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260014b7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260014b7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260014b7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +b596be88d8632a3695fbf7d142d273f419a824262d2c33114aafa7321268cfeb=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260014b7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260014b7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260014b7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260014b7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260014b7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260014b7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260014b7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 5bb542e49eefddf0e2198fc4a16a19b4bb813878d9641e1cf8b21e629c426d21=6009600860076006600560046003600260014b7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 dc683cb5689109fc9b3a0f7d1b2ab96e45579ba9dab80fb8c1834607a92f9acf=6009600860076006600560046003600260014b7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 5dee84479d79d9de0436622b99da00b0e821a3a366bbe1d90b2fbd2578305c7c=6009600860076006600560046003600260014b7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -7194a6731850aa94e54ed64390205e64f2e9f7ec6939c2e468d1e6052fc9dd26=600035600081036046576009600860076006600560046003600260014b7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +7194a6731850aa94e54ed64390205e64f2e9f7ec6939c2e468d1e6052fc9dd26=60003560008114156047576009600860076006600560046003600260014b7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 dac03672fecbc895d39f416c3d5f9287013ac2a4ec104ac5d7916c7d113a8ac4=6009600860076006600560046003600260014c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 450c0f2b08d2613f867e46b74c279fc97f7445a71700ecf036118e8b64ac04f9=6009600860076006600560046003600260014c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -b5e2b88ac45fff42d0a6cac5e73ece523a64eeda2c535c72ddd5a3b5b5df2df2=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260014c7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260014c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260014c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260014c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260014c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260014c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260014c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +b5e2b88ac45fff42d0a6cac5e73ece523a64eeda2c535c72ddd5a3b5b5df2df2=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260014c7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260014c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260014c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260014c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260014c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260014c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260014c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 2113ad0b1d88921edae398c9debb061acc21db749476fcc7155d17fcbc766daa=6009600860076006600560046003600260014c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 2186b1dbb5db112a710d8ea1309ae6fe8421d09a11c159a1f314b270a3bb2003=6009600860076006600560046003600260014c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 449260e60e60bd8833468bd543e381951ef7246d547c3555306b8489f035080a=6009600860076006600560046003600260014c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -c9a76debc6fd52dafce119b0d8b6a0579828992afa6c65ad579ebf05d954bbef=600035600081036046576009600860076006600560046003600260014c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +c9a76debc6fd52dafce119b0d8b6a0579828992afa6c65ad579ebf05d954bbef=60003560008114156047576009600860076006600560046003600260014c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 f78a0051f2cf054be150a84c71250a1b2d94c26b2910b9ed65c488f35752529c=6009600860076006600560046003600260014d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 5545873eec9ee46ecc7c3c81b487ebdc6c82a079800571930878a60fc3d0f3ba=6009600860076006600560046003600260014d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -ab05d4d536f135f8b1563f7dbfa5eaf895fee2c78317d78c0c4b3ff3de1b9457=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260014d7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260014d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260014d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260014d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260014d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260014d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260014d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +ab05d4d536f135f8b1563f7dbfa5eaf895fee2c78317d78c0c4b3ff3de1b9457=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260014d7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260014d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260014d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260014d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260014d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260014d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260014d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 35537ac9b48fe5c3ab0a1786ab4fe1dcd4c5a486a205c4e2c2c1c7035a83aadb=6009600860076006600560046003600260014d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 bd0735179d1d2c7387f5da8032866aa13032c49ba14d4a93297d87b911169513=6009600860076006600560046003600260014d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe cc964f4714ba3ef4ed0875a122eee444ec057559bd5f82c7f7aee62f332f7e19=6009600860076006600560046003600260014d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -e6672f781e8b423031409c2541fc834d2f9c30781c3174774eee3634cbb102b4=600035600081036046576009600860076006600560046003600260014d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +e6672f781e8b423031409c2541fc834d2f9c30781c3174774eee3634cbb102b4=60003560008114156047576009600860076006600560046003600260014d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 ae779725a8c98b7e66d84c9d84d9ece6b29f197d3c73da4f8edcc3f0bb9cb2cd=6009600860076006600560046003600260014e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 a61df624f6e8f13a390caba5e48e46833da498d66300d2a7edce56538ae76d5c=6009600860076006600560046003600260014e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -c13d93fa7aeb19705ad16a112cd7adb75159db5519fe9106d2e3d3ae86098cd4=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260014e7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260014e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260014e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260014e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260014e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260014e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260014e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +c13d93fa7aeb19705ad16a112cd7adb75159db5519fe9106d2e3d3ae86098cd4=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260014e7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260014e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260014e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260014e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260014e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260014e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260014e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 9d7b3eb3bc6c20059ca181d0ca7f23625b2d6140301a4a8fdde1d7118e848de4=6009600860076006600560046003600260014e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 3b0476fdf948756ddd558634f40d1c232fb83eabc23a9cbe974c807ea212147e=6009600860076006600560046003600260014e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 247339d44499a3c740cef516958655a4d8bb7655059316275dfbef8d441b2087=6009600860076006600560046003600260014e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -e5561ec1d724fe56c53bfe7160e9fcf689df1ccf97606a35f6065cbd8f9d8f22=600035600081036046576009600860076006600560046003600260014e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +e5561ec1d724fe56c53bfe7160e9fcf689df1ccf97606a35f6065cbd8f9d8f22=60003560008114156047576009600860076006600560046003600260014e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 e8cd4486cb70670131d053f139904e3a60073d6dae6830523acee4c3f1ba5386=6009600860076006600560046003600260014f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 d55547540141b5c2474116865a25c2074b577a778273ab5da0e552ccf0cfaf02=6009600860076006600560046003600260014f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -d2b888a1e4f0215babb30d4bde4cacbf0f10d774ad6eab3897d254ecce84dc4a=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260014f7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260014f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260014f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260014f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260014f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260014f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260014f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +d2b888a1e4f0215babb30d4bde4cacbf0f10d774ad6eab3897d254ecce84dc4a=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260014f7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260014f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260014f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260014f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260014f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260014f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260014f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 86d603dda136eabedebae7b0c92e9ef908cdeae1a7c64b8ea9a1586e4322df90=6009600860076006600560046003600260014f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 f4830420556f48087180be5a547eb617b02954cac5d0f32e1a4475061bc53dda=6009600860076006600560046003600260014f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 1095b336e60f4181c7fcf952c990e25c06bdbe0172bf9437b3de45697970afc0=6009600860076006600560046003600260014f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -11d7ebcaaf4190d5b14d989a7e81d0001ebcc03063d7c39a8d0259a96aeed61d=600035600081036046576009600860076006600560046003600260014f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +11d7ebcaaf4190d5b14d989a7e81d0001ebcc03063d7c39a8d0259a96aeed61d=60003560008114156047576009600860076006600560046003600260014f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 1d37ad3ecba693e5c7bbb335635f6d523d8a62ece84d8342fa2618b95d1809aa=6009600860076006600560046003600260015c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 147dbb70dd39330bc7810fac0bdc7ebe79cbc820ecf63273a714a2479037f4ac=6009600860076006600560046003600260015c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -0b17087362c519279c47d90ad072fd1313d3cf7e2cd5f38d6479769c36a082b7=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260015c7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260015c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260015c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260015c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260015c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260015c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260015c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +0b17087362c519279c47d90ad072fd1313d3cf7e2cd5f38d6479769c36a082b7=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260015c7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260015c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260015c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260015c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260015c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260015c7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260015c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 5bd1b9546aa94ebb8530c1751b31c48c7b52ddd6b79734be7846dd857de86546=6009600860076006600560046003600260015c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 f70a86737a9ce9082b30da8f1a40ef6726d7bf11acf24fc7532a3997d972347d=6009600860076006600560046003600260015c7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 14fdbc5e86fccbf2259ce355c990a53abf6f403986ee97062032787fbf2e5a67=6009600860076006600560046003600260015c7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -bb2dbfd0fecf67b886c881eb40a3bf1b7e23e2d6dc1fb667ac03b2eede5fd249=600035600081036046576009600860076006600560046003600260015c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +bb2dbfd0fecf67b886c881eb40a3bf1b7e23e2d6dc1fb667ac03b2eede5fd249=60003560008114156047576009600860076006600560046003600260015c7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 c8624036f56a51f08b2ffd35faf49529eca29e28af035b0ae0d50ed86db706c1=6009600860076006600560046003600260015d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 a2e0374bc7857327c8ba35ffff2b5f76b4f3bd5d4c1c8eb1226e70e5a7a0471d=6009600860076006600560046003600260015d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -12c97dc5ea9b69cbc61eef6a65b2b1f5bc056aa512b6ef91b358c279ab15f60f=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260015d7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260015d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260015d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260015d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260015d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260015d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260015d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +12c97dc5ea9b69cbc61eef6a65b2b1f5bc056aa512b6ef91b358c279ab15f60f=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260015d7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260015d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260015d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260015d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260015d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260015d7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260015d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 9387c7b6187f6901ff27d62c398a4b1b5c9127990050bf58eb58eb89fb1dbe90=6009600860076006600560046003600260015d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 9e8f7a4f1617ed590aaa165a5347da8947a15c535ef51e3b8e1a07c885cb3c2c=6009600860076006600560046003600260015d7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe a8d2e6881ebbfcc1852e0c1f0765a3e056b190e1cb2136ed1573983702125a89=6009600860076006600560046003600260015d7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -644f7c153874c905f89f6bff11a70de4244ce8657a1428e6d702b596516f473d=600035600081036046576009600860076006600560046003600260015d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +644f7c153874c905f89f6bff11a70de4244ce8657a1428e6d702b596516f473d=60003560008114156047576009600860076006600560046003600260015d7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 a3e7bc89de3bc95768a31345a9b17dc068a21d0acfe2d0faa50bfee527abf1fe=6009600860076006600560046003600260015e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 522eb3957173b5fa70095ddefc445e52747ab61a57a6cb6ba69dcad3a8a61c23=6009600860076006600560046003600260015e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -ec8cd5ae508c638841ef91c02728cd418d3328a77e0a3de8490c7c43e9fe2edf=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260015e7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260015e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260015e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260015e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260015e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260015e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260015e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +ec8cd5ae508c638841ef91c02728cd418d3328a77e0a3de8490c7c43e9fe2edf=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260015e7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260015e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260015e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260015e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260015e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260015e7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260015e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 ee4037bbe88c11fb2fd47a485b421fcb12ef6b6222eaaa5185f2fcf98aafa827=6009600860076006600560046003600260015e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 791540cfc11716b0091faa24e4bfbab6224a6fa7d15b46b9e7c312087fb471c3=6009600860076006600560046003600260015e7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 8b60bdb6d569bc8c93db1524cf0c906e8b489e801f337865b4768cab3d02bfe8=6009600860076006600560046003600260015e7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -2a7a2d436d5028b5cca65e6a4ddbe43e79d41bbe8b194946b650e8a7ae4a95f1=600035600081036046576009600860076006600560046003600260015e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +2a7a2d436d5028b5cca65e6a4ddbe43e79d41bbe8b194946b650e8a7ae4a95f1=60003560008114156047576009600860076006600560046003600260015e7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 3985e89094ec6add1a5fec4e9028dc868618558120e379b9a32111d76abbb722=6009600860076006600560046003600260015f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 f406f382d6365894d2e79e047bcc8318eb362b3009e0c24450fd6dc1ebc09802=6009600860076006600560046003600260015f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -cb90f8f8d1399f09cc59afb30b2e1d19656593f908a340811c22743c8ec06033=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b6009600860076006600560046003600260015f7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b6009600860076006600560046003600260015f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af1506009600860076006600560046003600260015f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b6009600860076006600560046003600260015f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f1506009600860076006600560046003600260015f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b6009600860076006600560046003600260015f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af1506009600860076006600560046003600260015f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +cb90f8f8d1399f09cc59afb30b2e1d19656593f908a340811c22743c8ec06033=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b6009600860076006600560046003600260015f7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b6009600860076006600560046003600260015f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af1506009600860076006600560046003600260015f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b6009600860076006600560046003600260015f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f1506009600860076006600560046003600260015f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b6009600860076006600560046003600260015f7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af1506009600860076006600560046003600260015f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 18563cf49ecc84c88fb26dc80586b5505720918dda5d64886a0183843ed1b390=6009600860076006600560046003600260015f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 cee67c20632de89651377f47b8b4d63536aa1892d81cd153babfab74aef85556=6009600860076006600560046003600260015f7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 9ad1f5391eadcbcb99b3de4ec3ac3fc692025d0068434f706b35245665db16ea=6009600860076006600560046003600260015f7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -f7a8515356cea83f5aad1ef8aa7ec797a8595f81961c283df205f50388517961=600035600081036046576009600860076006600560046003600260015f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +f7a8515356cea83f5aad1ef8aa7ec797a8595f81961c283df205f50388517961=60003560008114156047576009600860076006600560046003600260015f7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 d1eab8fea44614eff5970e1e7261a183641d737cffd761337cf19e11a1b56b23=600960086007600660056004600360026001a57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 edab2de8e06c42119704196b4143f60e32a0c6896327cf03d59eed9ae643cadb=600960086007600660056004600360026001a57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -7617ac597cc32b4d5804938b024729c826a571db621b6084c3387028abc7a5da=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001a57fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001a57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001a57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001a57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001a57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001a57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001a57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +7617ac597cc32b4d5804938b024729c826a571db621b6084c3387028abc7a5da=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001a57fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001a57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001a57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001a57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001a57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001a57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001a57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 7044d816d041064224679b185ba14bbc395c4c8c1a2fb2c219e6c9a75107e8bb=600960086007600660056004600360026001a57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 45728a2ac58cfda8b05d4fa2a87108efd6f608f2febc22b54fafce5b827599cc=600960086007600660056004600360026001a57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 546756297ca7be8134054870e2fdecb2baabbbadb7802f85546ad2565bafb142=600960086007600660056004600360026001a57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -623cb76cc7f263283e14a83debf2be6d33e6387b7ca4c291adabeebc28b6cf73=60003560008103604657600960086007600660056004600360026001a57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +623cb76cc7f263283e14a83debf2be6d33e6387b7ca4c291adabeebc28b6cf73=6000356000811415604757600960086007600660056004600360026001a57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 d97882037d93f9e6a488a09c902811b351a9386d76e4d6917b3d4dde713d4307=600960086007600660056004600360026001a67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 3727a46ef1cf11f260d99eca5376cf92bb901638f2320eb0d4c1a1ad064f872a=600960086007600660056004600360026001a67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -c3dab4fbd966b4bf6be70ef971bc0cf0ea416edf2d43eb32f17205c1f06f7cba=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001a67fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001a67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001a67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001a67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001a67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001a67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001a67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +c3dab4fbd966b4bf6be70ef971bc0cf0ea416edf2d43eb32f17205c1f06f7cba=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001a67fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001a67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001a67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001a67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001a67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001a67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001a67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 905e8d8f6cb731f9c4eac2508b3fbdd103b4d1f9a220ce70b9cacd3b06de8ed9=600960086007600660056004600360026001a67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 9170a721847222a91f022445156dc1f7e22b2c508241d29b6034ce8ccadf051b=600960086007600660056004600360026001a67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 089e0113da900c9baf841ee09f9976c1d5fa817a4f66301005fcae69fd2851b5=600960086007600660056004600360026001a67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -2172a5bbf7248de069dafb926cffc6b486cd3f5e7363cd67e6d9bb7e09d6b987=60003560008103604657600960086007600660056004600360026001a67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +2172a5bbf7248de069dafb926cffc6b486cd3f5e7363cd67e6d9bb7e09d6b987=6000356000811415604757600960086007600660056004600360026001a67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 aa7d40c64d7b03188e2196062bfc3145a39d6007c5ff6241669fab753e1d1e22=600960086007600660056004600360026001a77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 68630c36bc83e50c031d5a843b9f69c87a31262fe579c318afb46b09f6fd2b80=600960086007600660056004600360026001a77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -aa4683a35fd38194581d7a313b9d03d3c09f0651550f7ecd6bee2890ceaf2842=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001a77fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001a77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001a77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001a77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001a77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001a77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001a77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +aa4683a35fd38194581d7a313b9d03d3c09f0651550f7ecd6bee2890ceaf2842=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001a77fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001a77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001a77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001a77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001a77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001a77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001a77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 4c7c09173ec8b4b0bc4f84eea4836c54b13cb5fc9a2001256801fac4b73dcd98=600960086007600660056004600360026001a77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 c57ce6f6d2a0dc44c1b5ea88555ddfeba94c7b5a90e88d099d62f24b93644a49=600960086007600660056004600360026001a77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 8d70f65be4dfe1874039f917afc337fc0f78c9d73ec0cf6f87952407479e26ba=600960086007600660056004600360026001a77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -a2deab31c731cb19a32ee81b5dd1fb8dd0470eefe87bece2fd3fa79fed1f1efa=60003560008103604657600960086007600660056004600360026001a77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +a2deab31c731cb19a32ee81b5dd1fb8dd0470eefe87bece2fd3fa79fed1f1efa=6000356000811415604757600960086007600660056004600360026001a77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 c36ee9b66f33ccab08ca07a6652201f082d2bd94eb36b04daadd893dccf6b965=600960086007600660056004600360026001a87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 f430a70af6677071abf68b854672da24115e8d63aaa840ad0e97dc1308a97b3c=600960086007600660056004600360026001a87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -622d029430a2cc1137ab7ba37a468fb2bb3605528bb5ce612fc1b1e51cd92473=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001a87fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001a87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001a87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001a87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001a87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001a87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001a87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +622d029430a2cc1137ab7ba37a468fb2bb3605528bb5ce612fc1b1e51cd92473=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001a87fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001a87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001a87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001a87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001a87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001a87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001a87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 8a6a4726a21f9b7c1a94a620ced3d9fec8a69190f8029c2ea0bb0ad0938ed425=600960086007600660056004600360026001a87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 ab14c2fcc9ded0061d67077862e0acc84a9e5482e85859418fcacad7212d4685=600960086007600660056004600360026001a87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe f1fbc47491951df62388ad0a6aa79863a6223d9e97069a12ed84896fdb947444=600960086007600660056004600360026001a87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -648d9c3d8273fcab8e7b98a3d748652514c00cd53c9c542846d8fc152b554588=60003560008103604657600960086007600660056004600360026001a87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +648d9c3d8273fcab8e7b98a3d748652514c00cd53c9c542846d8fc152b554588=6000356000811415604757600960086007600660056004600360026001a87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 f19dfbe8a65ab6d3733b1b2e67034aa0eebbd4b47b53db891098e6d1ce93db5d=600960086007600660056004600360026001a97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 a72f444d49095ff1ca7447104f75e88ac9870f742c100f62df082257c26946fd=600960086007600660056004600360026001a97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -33704028c14ae91be0d7da306465314c2bbdf085bb687c43609ef8ce7df5eab8=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001a97fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001a97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001a97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001a97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001a97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001a97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001a97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +33704028c14ae91be0d7da306465314c2bbdf085bb687c43609ef8ce7df5eab8=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001a97fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001a97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001a97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001a97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001a97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001a97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001a97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 ee898162a2c27aa90bc90d003ce02bc1d506fa8788cbe3c69f0fb36f3be2a2e8=600960086007600660056004600360026001a97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 f7a6f24172fc6615b21e4fce5d6f8d37d9250dffab35bf76320ea31a7f989b76=600960086007600660056004600360026001a97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 0259856d6aef71d5d73c6369a5b22de974d2b441cf01c6e8add0f29039a7e21d=600960086007600660056004600360026001a97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -dad74d4465d591ea226d9264970f547f9d4b7f6f9c80fd069873d7669d3f13a5=60003560008103604657600960086007600660056004600360026001a97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +dad74d4465d591ea226d9264970f547f9d4b7f6f9c80fd069873d7669d3f13a5=6000356000811415604757600960086007600660056004600360026001a97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 295601473c4153b15be75da0ea7f2cfc9f5b402319469b549b78ae66b1392e01=600960086007600660056004600360026001aa7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 6eee4cca6270f0aa318bdae5d978343e4d6cd121c1e07fbede1951e345114c3a=600960086007600660056004600360026001aa7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -1bfe22a37bcb20eb9485eee47d820daf3972aae460efc38c72b2dfccea7021e5=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001aa7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001aa7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001aa7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001aa7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001aa7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001aa7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001aa7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +1bfe22a37bcb20eb9485eee47d820daf3972aae460efc38c72b2dfccea7021e5=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001aa7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001aa7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001aa7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001aa7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001aa7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001aa7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001aa7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 04da003845cd78215fbcc2de15facdfe22cffe9724e0f31825e70706beed643c=600960086007600660056004600360026001aa7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 091fd439ce89bd0d7c76bc50722fcd05bb1a406d2ff29addd449576cba3e396e=600960086007600660056004600360026001aa7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 7eae079129300611085a8201bb2748f6e5d0086df9301207fc7539f774657776=600960086007600660056004600360026001aa7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -03cd8ab90e3393d2da8f899965a864a80b0287674c891e7a84761286f5922f99=60003560008103604657600960086007600660056004600360026001aa7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +03cd8ab90e3393d2da8f899965a864a80b0287674c891e7a84761286f5922f99=6000356000811415604757600960086007600660056004600360026001aa7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 cfaa42b334629e86f0d984c20cc5d2d72fd634f816658c493e080fad5577c92c=600960086007600660056004600360026001ab7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 0cde0e487762e59d9178c148d5669503e3e80b9e0b0a7cd4ef2aeb0b043d49df=600960086007600660056004600360026001ab7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -656b84b3aa8ec81eeca08cf8c56e595a45e32076429dc249c91e7a5638d7facc=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001ab7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001ab7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001ab7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001ab7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001ab7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001ab7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001ab7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +656b84b3aa8ec81eeca08cf8c56e595a45e32076429dc249c91e7a5638d7facc=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001ab7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001ab7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001ab7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001ab7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001ab7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001ab7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001ab7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 38ed1b839b2e14af0ecfb0beba968e9ade19702be2c7defd53f01e68c263e6d2=600960086007600660056004600360026001ab7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 e39774a762fb20a88645672dfc2c74ce0822560179cf7507d5ddc05ca1481ef3=600960086007600660056004600360026001ab7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe a3e5a64802f89fce1f32efc513b486ac0820b0b34685b8fb0737f9a6b4180a8c=600960086007600660056004600360026001ab7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -24efcd39ac3cdf3dde99e1b504caf4614b03d4f43ead5f9beacfe8be4ae816b3=60003560008103604657600960086007600660056004600360026001ab7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +24efcd39ac3cdf3dde99e1b504caf4614b03d4f43ead5f9beacfe8be4ae816b3=6000356000811415604757600960086007600660056004600360026001ab7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 465d137cae2495e4fea7763aa4da24bfec43f79e4039aeed2631000950c0e3dd=600960086007600660056004600360026001ac7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 bd0de9ef240e2ae2c3dee263eb8d295d979357922169643f7fbad4fadf3203aa=600960086007600660056004600360026001ac7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -4a91f97ac5ec001bcdaba076904646eda8d0911267ce0bd3f7816d1b0b2a381d=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001ac7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001ac7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001ac7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001ac7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001ac7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001ac7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001ac7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +4a91f97ac5ec001bcdaba076904646eda8d0911267ce0bd3f7816d1b0b2a381d=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001ac7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001ac7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001ac7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001ac7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001ac7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001ac7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001ac7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 a5512c98710041a04f287685c1eb58b7e9ef51dac1b73198c3e891cedb404317=600960086007600660056004600360026001ac7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 69fc317a70e7d65296a306efc4741f68405423cc089d4bb14aab766838b3e152=600960086007600660056004600360026001ac7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe e79010d4bf814f5120e3396e358b55653ca0a4360bda3397ac2ed928a6c2a015=600960086007600660056004600360026001ac7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -92375e3fd5eecf61074e7259bc7e2e32f83c71f22da2cea0ccddaf6022323169=60003560008103604657600960086007600660056004600360026001ac7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +92375e3fd5eecf61074e7259bc7e2e32f83c71f22da2cea0ccddaf6022323169=6000356000811415604757600960086007600660056004600360026001ac7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 c212f1f96138ae71c8933e72aa994bd3e192bcc21a09c6d767b078f150f4553a=600960086007600660056004600360026001ad7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 c1739198be2c296443151fabb12fb05b7f4ffe17443adbeab1c7fed63a56a882=600960086007600660056004600360026001ad7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -5bf1a319391892e8984f26d09759eff4e0d2e5ebfd94c92e82cc668ae379d931=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001ad7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001ad7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001ad7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001ad7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001ad7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001ad7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001ad7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +5bf1a319391892e8984f26d09759eff4e0d2e5ebfd94c92e82cc668ae379d931=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001ad7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001ad7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001ad7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001ad7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001ad7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001ad7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001ad7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 545be1cdc1c5a237b85abee063d9120ea2a05fa6925ff6b0b33934f610461832=600960086007600660056004600360026001ad7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 fedf639ff904520415f367ac44356cb354f4bf3edc09eedc5dae8fc35d770b37=600960086007600660056004600360026001ad7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe d461f93244983a64bd0b0382e66618c6a20f95e121a0d824906ab123e5ed790b=600960086007600660056004600360026001ad7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -714e679483bf1a87a19bfb1cafc948c27a6776fac302505f89675503d4d31faf=60003560008103604657600960086007600660056004600360026001ad7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +714e679483bf1a87a19bfb1cafc948c27a6776fac302505f89675503d4d31faf=6000356000811415604757600960086007600660056004600360026001ad7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 92a2f94f47279694ebe05abea98e523dcc77834cf0aa46b71dc20fffabeee61e=600960086007600660056004600360026001ae7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 e25445ec2ded00e9e890b3a5adf8a6be5ad404bc6b59af04ee9043b555030ebf=600960086007600660056004600360026001ae7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -cf3e714196c6f7cc168f3149daf08cae9eb74da0bd1096eb6dc3826318b38ad8=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001ae7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001ae7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001ae7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001ae7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001ae7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001ae7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001ae7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +cf3e714196c6f7cc168f3149daf08cae9eb74da0bd1096eb6dc3826318b38ad8=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001ae7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001ae7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001ae7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001ae7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001ae7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001ae7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001ae7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 39280436baea12152a641f69ec1915b38370be4ec75797802783c6225a57c7ad=600960086007600660056004600360026001ae7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 c9e403e13ce264fa6669e004e8e519a4b3355125bf857f5eba1e7a04df1426f9=600960086007600660056004600360026001ae7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 3598fd8bac38c5ab3fd4330ab554809e1911d8bbe08762f83a1686b68c6c6b6b=600960086007600660056004600360026001ae7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -eb8f7a432c7cda4f93de0e9b1fb9894f1a05a5a8b3a64b8d81e0807c9614d063=60003560008103604657600960086007600660056004600360026001ae7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +eb8f7a432c7cda4f93de0e9b1fb9894f1a05a5a8b3a64b8d81e0807c9614d063=6000356000811415604757600960086007600660056004600360026001ae7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 18028922015b199d55fa28bc39c483b5dac60dbd99f2647189299646209d3f4e=600960086007600660056004600360026001af7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 ff43a4b6bd4f497c81600f4b31f55457e17a0f2b8e51bddcf1cb73855a68a642=600960086007600660056004600360026001af7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -b813fe4e9ca8b8d5eb9de231f8577ca1dd344b0d804ec942b5852740b680272a=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001af7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001af7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001af7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001af7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001af7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001af7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001af7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +b813fe4e9ca8b8d5eb9de231f8577ca1dd344b0d804ec942b5852740b680272a=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001af7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001af7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001af7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001af7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001af7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001af7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001af7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 34f0a9b3798dfc90a5de90017cc9e9225c2db1ef43fdc4acdb6ac0632ba7fe8e=600960086007600660056004600360026001af7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 4bdabc9cc4cd2dca72f8be241b8ce0159385a80f03ee15e267901c1ad0137886=600960086007600660056004600360026001af7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 6580f3abd546f8acf7550390002de2cb77194e9f4a1552586fe1669e2d6152eb=600960086007600660056004600360026001af7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -3b58ce1ccb1bff45ac0377b154d262698ef5951667d644d24afc8b5228b1e368=60003560008103604657600960086007600660056004600360026001af7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +3b58ce1ccb1bff45ac0377b154d262698ef5951667d644d24afc8b5228b1e368=6000356000811415604757600960086007600660056004600360026001af7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 23ac936ed2f76cb2295e9492fb292f61eb377fd35de083dfaa32caeb883492af=600960086007600660056004600360026001b07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 595b56b882e1c748c6ce52746e5aaeacf5829509d13e566d20766708951b6de8=600960086007600660056004600360026001b07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -1c79ff878b728067cceaa9dc5013ffc81bbe10a6b35b9ec68da3b7cb597544d5=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001b07fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001b07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001b07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001b07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001b07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001b07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001b07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +1c79ff878b728067cceaa9dc5013ffc81bbe10a6b35b9ec68da3b7cb597544d5=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001b07fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001b07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001b07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001b07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001b07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001b07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001b07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 fe60968b98e5713e5717de453aa6e7f59607afbc23edad154615882624e22a89=600960086007600660056004600360026001b07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 79e65b2e2104a8925e1bfe24aa5f1836fb779bdc4779335b77d1471474c79aec=600960086007600660056004600360026001b07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 93c5046ce9a0ceda9b15fc5973038054e3fe198bb5499252667e528910761b60=600960086007600660056004600360026001b07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -8f54b1af2c7db7e47322314d4a06b9b46753be55dc629dec8adb17185bf14147=60003560008103604657600960086007600660056004600360026001b07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +8f54b1af2c7db7e47322314d4a06b9b46753be55dc629dec8adb17185bf14147=6000356000811415604757600960086007600660056004600360026001b07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 375b5d3f62a035eb2b1865ecf1e9c2ced18053d504dc4118c8f709fa04076ec8=600960086007600660056004600360026001b17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 e16ebef2c62fa5608817d23362ea324c2ecfda6f683daf18f9bd3ab4639b81b2=600960086007600660056004600360026001b17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -0a0b3e15d26674691a7e70d24e04e701aac766224f296a283a45af17275854f6=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001b17fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001b17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001b17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001b17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001b17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001b17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001b17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +0a0b3e15d26674691a7e70d24e04e701aac766224f296a283a45af17275854f6=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001b17fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001b17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001b17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001b17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001b17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001b17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001b17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 124fe5955b02e4ee014861e023171c6fc4910083f3a53cbc7f5ca0abe1b0c314=600960086007600660056004600360026001b17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 5f75a8c43a8a45708325265dee57d9304421d3ba7466dd54bcbd93551b09c6bb=600960086007600660056004600360026001b17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 75535d4885e5f908585c7adb2fe96c6acb264a6a4e08c380050a2479d7c19d85=600960086007600660056004600360026001b17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -3af4eb3da62fc565046e8f3aa54fa54ec36914e466bef0c9e4c2edaa5c216241=60003560008103604657600960086007600660056004600360026001b17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +3af4eb3da62fc565046e8f3aa54fa54ec36914e466bef0c9e4c2edaa5c216241=6000356000811415604757600960086007600660056004600360026001b17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 6d47c3cb6eff5e142423c64e46fbb8ba1933f14485da3b8e9ba8ee410ae70c8f=600960086007600660056004600360026001b27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 671b4f416fb000cd2c078ead172cf30911da23657143463dec98e974490aa0f5=600960086007600660056004600360026001b27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -36a77cb26e41113db21c7774e6dfff07da405edf2614a1a57750ac80e724af43=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001b27fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001b27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001b27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001b27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001b27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001b27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001b27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +36a77cb26e41113db21c7774e6dfff07da405edf2614a1a57750ac80e724af43=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001b27fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001b27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001b27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001b27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001b27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001b27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001b27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 f0c3c5eed4b608a5347b90251412db3927cc57fe5f52446d74b80160026247d7=600960086007600660056004600360026001b27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 dae7a64245fd8264485ae19feecd4081291ecb49861c638a4fddeb9a662a5219=600960086007600660056004600360026001b27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 928e010688153b6d9552e29afec10b51d52a3175b8aa3b0562251ab9e0b2fa6d=600960086007600660056004600360026001b27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -22170866d87ea1fcf07fcd8c36072b1b399719e0146cfe6ba92c1fc131b7c95e=60003560008103604657600960086007600660056004600360026001b27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +22170866d87ea1fcf07fcd8c36072b1b399719e0146cfe6ba92c1fc131b7c95e=6000356000811415604757600960086007600660056004600360026001b27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 f1c7bb44fcad11ceb3b9fc7a47fe2e26e00b74bdcd085173433f7b5de6e38980=600960086007600660056004600360026001b37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 469439b15a646f36c5427bd314a7f3dca16e45a4f678286f344c91c25b77a5b6=600960086007600660056004600360026001b37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -36bcf0fa2bf92207c4936243b58b43c7c5b4a00f238be2427061f4d5b44130bb=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001b37fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001b37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001b37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001b37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001b37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001b37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001b37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +36bcf0fa2bf92207c4936243b58b43c7c5b4a00f238be2427061f4d5b44130bb=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001b37fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001b37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001b37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001b37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001b37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001b37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001b37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 78a6788de72feb7e1e104f501e300fbd6894242ea6f54c7d72fb9a1ec0ff8477=600960086007600660056004600360026001b37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 781f806cb192042e5088fa9e50fcc5229e6175cb5fb5e28a66ccf48cd2e32f77=600960086007600660056004600360026001b37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 2c0430a512830f77bc21d09623504c1fde5e358d30b51dc419bf0af446aab731=600960086007600660056004600360026001b37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -c9a36209c89dfb69f00264a028ec5d4a69637f3cba0e90091a2560887bfd0574=60003560008103604657600960086007600660056004600360026001b37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +c9a36209c89dfb69f00264a028ec5d4a69637f3cba0e90091a2560887bfd0574=6000356000811415604757600960086007600660056004600360026001b37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 4d17d1349fb9f995833650a444ff16c18827e3dd1b38ea6d11d42168dd3e6251=600960086007600660056004600360026001b47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 32c1087d13a89661f0d090b01d2908b03da0f044368020440f78a0c5887475bc=600960086007600660056004600360026001b47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -982fe1590c1f12ddbb79d0995340fa62d6136498b2a8b26203cc9ac804b2c79b=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001b47fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001b47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001b47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001b47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001b47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001b47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001b47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +982fe1590c1f12ddbb79d0995340fa62d6136498b2a8b26203cc9ac804b2c79b=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001b47fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001b47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001b47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001b47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001b47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001b47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001b47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 8c73894e63fb9113d3fde4e1226c32143091f0f6d2938267960d24eeb532095c=600960086007600660056004600360026001b47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 810d283fa069e990fc35355a8e236c9455eb032ca179ca096e5e334d10b15984=600960086007600660056004600360026001b47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 6801bf09c7edba24775aae91fd05a0b5a76e29e32ba89f87ec41f9177c00730f=600960086007600660056004600360026001b47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -afe2534a3f30e8ab5742f1822626c38b933f2d7eb3eb3ff5d0ffb9c412f47f42=60003560008103604657600960086007600660056004600360026001b47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +afe2534a3f30e8ab5742f1822626c38b933f2d7eb3eb3ff5d0ffb9c412f47f42=6000356000811415604757600960086007600660056004600360026001b47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 32e499d14f918180bc38b8a9d48d47d77c2b9b9b774a5e58488fedd697597707=600960086007600660056004600360026001b57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 51ef377562e53c18f2c0c8ebca6d060f913794967a619507bbc0a4a91d31fbb4=600960086007600660056004600360026001b57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -bf14c2273eb002a265ab4fc35d3f4987e02f56a4e3434e40e719a659c1da7d81=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001b57fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001b57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001b57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001b57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001b57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001b57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001b57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +bf14c2273eb002a265ab4fc35d3f4987e02f56a4e3434e40e719a659c1da7d81=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001b57fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001b57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001b57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001b57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001b57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001b57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001b57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 7a912ed2def5530349bcac97a961836f04f79978466c1951b5c4a17626bb8540=600960086007600660056004600360026001b57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 020af5e103d2f18a385ff9969ad5a4cc1f7879edd42a44deaff1382ccd6ae262=600960086007600660056004600360026001b57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 1640682cc01269764a2ef366cf43fea58f95405bda149a2148eb0bf9dbab9aab=600960086007600660056004600360026001b57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -928a8b2a29b3ccd0643acbb28da10364a97b1493f0f036e34ad3b05447bf956e=60003560008103604657600960086007600660056004600360026001b57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +928a8b2a29b3ccd0643acbb28da10364a97b1493f0f036e34ad3b05447bf956e=6000356000811415604757600960086007600660056004600360026001b57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 f4477b5d8a992e45bb09f463322830800845b324a87edcb990b242ebcad55112=600960086007600660056004600360026001b67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 fcd8e234f92da37c67a6c7290fdc039a69fbb2cf1c8eb14be7749d7ca5225156=600960086007600660056004600360026001b67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -b08254d18d2253b2d0b26707744350cdd9f6c86cac8a61072807f8e59ba2f191=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001b67fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001b67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001b67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001b67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001b67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001b67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001b67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +b08254d18d2253b2d0b26707744350cdd9f6c86cac8a61072807f8e59ba2f191=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001b67fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001b67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001b67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001b67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001b67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001b67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001b67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 4f7396a5055abb9752060645295bfabccbe44ac99987d863c4d844dffe9776cb=600960086007600660056004600360026001b67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 47817da94d6bfc0115b8f8a4b0998700721e504f5e67f072c3b06e6e908bd57b=600960086007600660056004600360026001b67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe f88ebae2e2b0720dcee15414637fb3d3e32446e0867d6ec63de508cf0a87c5cc=600960086007600660056004600360026001b67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -a46d8c215ef747e4cf6f9a5daed023e2f50d863301065ec125eec387bc2e95d2=60003560008103604657600960086007600660056004600360026001b67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +a46d8c215ef747e4cf6f9a5daed023e2f50d863301065ec125eec387bc2e95d2=6000356000811415604757600960086007600660056004600360026001b67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 dc47640e378fcf9d691d6b599259f7f5527ac41277fd7d5bbeedb957b4d80d49=600960086007600660056004600360026001b77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 74f617c08a9bcbb808bb8f7104f7b3d29c5c55bff6992bd4af3e52fbef7df05b=600960086007600660056004600360026001b77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -32e5491651bf426b83a9498d4dcba4e2d908a491d48c98e5b863d8dc08f2b54b=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001b77fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001b77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001b77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001b77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001b77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001b77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001b77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +32e5491651bf426b83a9498d4dcba4e2d908a491d48c98e5b863d8dc08f2b54b=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001b77fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001b77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001b77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001b77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001b77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001b77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001b77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 cd2dc524aafa37042e23a0e44f11be551e807bec5d92118babee55c00220ca3c=600960086007600660056004600360026001b77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 4bb64ecefd2002ebce9297c45a1ce2c990b131db282d56afa78a70aaac859511=600960086007600660056004600360026001b77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 18b26b6ff4b692a2f45c3e1795cd7d33a384e3d5d75dba5fb5c2cb3188c1dd17=600960086007600660056004600360026001b77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -c5f510ab27978d1ed30488c56fc1431396198a2e7b02c14bdf5a2223b1a314a7=60003560008103604657600960086007600660056004600360026001b77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +c5f510ab27978d1ed30488c56fc1431396198a2e7b02c14bdf5a2223b1a314a7=6000356000811415604757600960086007600660056004600360026001b77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 060eb32a46ac0a0a127d8ff499441843b9a7fa40b1a616206dc3192ef8c76053=600960086007600660056004600360026001b87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 41f21ea7974797fac4d5489776481d7a2b62ee2bf514079761224a7b8e131d33=600960086007600660056004600360026001b87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -f58cd0f4b2167441afd6ce239d2bcb8ab537f23d13a8ef0b3438cb80310a625b=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001b87fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001b87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001b87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001b87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001b87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001b87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001b87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +f58cd0f4b2167441afd6ce239d2bcb8ab537f23d13a8ef0b3438cb80310a625b=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001b87fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001b87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001b87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001b87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001b87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001b87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001b87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 4ef5b0b7d82b26ac3d4272e69e908eb983ff9247f872a6b4f8e5701163da4ac2=600960086007600660056004600360026001b87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 56e3a592dd11a809a75deff30b01a591bd237f91271ae0d290f1e452f45e8664=600960086007600660056004600360026001b87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 19d81279025ac2acfc0cc45a874ac42706d42f2d8b311d3758027066f22679a9=600960086007600660056004600360026001b87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -ab05337b437e65da194ed9c8a563c5ea899b96e4dca691e9169b5c8978ba4f8e=60003560008103604657600960086007600660056004600360026001b87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +ab05337b437e65da194ed9c8a563c5ea899b96e4dca691e9169b5c8978ba4f8e=6000356000811415604757600960086007600660056004600360026001b87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 68353f05fc7e97c586eb3b8bea79b0360b0ecd5af70040e76a85eb8ede03fe07=600960086007600660056004600360026001b97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 4f6d5424ef81c065114c560a6b9c7b642ed1ba0f7e32729c4f9f83908934619f=600960086007600660056004600360026001b97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -c1e1ad645a498e3daec6fa098b38f2b1aba2ce869d5b2076fb236d094142fdeb=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001b97fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001b97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001b97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001b97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001b97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001b97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001b97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +c1e1ad645a498e3daec6fa098b38f2b1aba2ce869d5b2076fb236d094142fdeb=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001b97fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001b97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001b97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001b97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001b97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001b97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001b97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 714ed27fbcb26b98adc34d507e6e03645f80cc231e43386565c2afaf1f83a3e9=600960086007600660056004600360026001b97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 eb3c3c4986684fdc2068398c4684f100ce78a6e3142dbcdd4d67f463d5460dde=600960086007600660056004600360026001b97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe e450e874f29d34b4480a880a9e31db38b01e7bb5bd4693c7e3bd258d0fb701a4=600960086007600660056004600360026001b97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -63c1b930bc412b67d7d24253b1d20dd1985b214b72f368a3e6842009d2ec9d62=60003560008103604657600960086007600660056004600360026001b97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +63c1b930bc412b67d7d24253b1d20dd1985b214b72f368a3e6842009d2ec9d62=6000356000811415604757600960086007600660056004600360026001b97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 90b4aec00af9accae5e7f5207d4839b95d28df1c249e6f95b0d961513cd1e572=600960086007600660056004600360026001ba7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 ad7d9ee1beca4b66b8bc39911b853419ba26c05f2f312c869f9bc4cb21a99229=600960086007600660056004600360026001ba7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -6c050fe5c83db884e90b5aa7e7386a3ec77d1e1247351c3e2fc9997a079b22f7=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001ba7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001ba7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001ba7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001ba7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001ba7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001ba7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001ba7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +6c050fe5c83db884e90b5aa7e7386a3ec77d1e1247351c3e2fc9997a079b22f7=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001ba7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001ba7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001ba7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001ba7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001ba7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001ba7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001ba7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 885031d46e1d825960abd359a762b9bb464573c652043bd5d75eb346d26815ea=600960086007600660056004600360026001ba7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 f557552f010fc2c4caadc4a38b60361d65a83bd932bac36149ee79992531cb80=600960086007600660056004600360026001ba7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 56ffd5c13a4aad794d26796e681d74b8a3384e283301828adb4568dd38b05309=600960086007600660056004600360026001ba7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -11ef9a4a25acb1844a4b010af5eee70f1351d3edddb4348cb1413d48c2fdee50=60003560008103604657600960086007600660056004600360026001ba7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +11ef9a4a25acb1844a4b010af5eee70f1351d3edddb4348cb1413d48c2fdee50=6000356000811415604757600960086007600660056004600360026001ba7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 d9c20ffb6352c592ed47ce05fc10defad6f2b695bdb61235f4ab457e6fed13f7=600960086007600660056004600360026001bb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 f587a62ed97890df766b66bea9ec2ef86fc697ee3eff9a248a311f95bdd603d8=600960086007600660056004600360026001bb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -2ae87cdc2993ba049ebcfba248401241c35aee3134c3867974cac31c19f50102=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001bb7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001bb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001bb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001bb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001bb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001bb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001bb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +2ae87cdc2993ba049ebcfba248401241c35aee3134c3867974cac31c19f50102=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001bb7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001bb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001bb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001bb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001bb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001bb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001bb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 a7ec3e451e4df447640e4faeb5115d4717d6d39c95a4e93780d6f286ac740130=600960086007600660056004600360026001bb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 a3da684d2da4a84617d15196aece456f0c6c3410718e4f241356885418334d74=600960086007600660056004600360026001bb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 22c231e7da3c71b41c76b4c8c93e6429b308dfdb59357f4675e2da62b08d2df5=600960086007600660056004600360026001bb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -61c9a242cba0f3a0691359ce010ba50cd8401350253c695b7c0009f5ad6e746e=60003560008103604657600960086007600660056004600360026001bb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +61c9a242cba0f3a0691359ce010ba50cd8401350253c695b7c0009f5ad6e746e=6000356000811415604757600960086007600660056004600360026001bb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 bbe32aa859c49ab5fb458dfe83d006331f8cb98d1a970cc5978201eabfade8ad=600960086007600660056004600360026001bc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 a9d4c65cf0b8b6e2c9a0fc33714c44737efd898474b67c009e3266b20ec6b83e=600960086007600660056004600360026001bc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -afb0383f74c8fb962e5a94e2714ff5b425e6657c505b382442bafac93e9753f7=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001bc7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001bc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001bc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001bc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001bc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001bc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001bc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +afb0383f74c8fb962e5a94e2714ff5b425e6657c505b382442bafac93e9753f7=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001bc7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001bc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001bc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001bc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001bc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001bc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001bc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 f21d9bc01a0741c0e702b32d09bf28d6749ec517705d1eb7238fd403c2921427=600960086007600660056004600360026001bc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 3c7ddf8dd24b16f19a47ce5eb3ac90d2128a35049e4ce0ff4b99fade851ca8b0=600960086007600660056004600360026001bc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe bdef3b665cd59dfea57fc602d5017e1e8eb092c15b355dd0456d94789be71e31=600960086007600660056004600360026001bc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -4237df860359f33d365a69395d5f321d4d8b7ee1e9166acf6214c0fd4cb5ad0e=60003560008103604657600960086007600660056004600360026001bc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +4237df860359f33d365a69395d5f321d4d8b7ee1e9166acf6214c0fd4cb5ad0e=6000356000811415604757600960086007600660056004600360026001bc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 2ea5eeb8ef97f0ff1f8aa51fa11c7213de8e1d1be8f878f77fc98f808608938e=600960086007600660056004600360026001bd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 325ad31a49d67fc404441fc41fe1bdcccc34b0f17a6ab6f8238c14f0d0dc97c5=600960086007600660056004600360026001bd7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -e84edbf58da2d1e6f701242356a9ff5c710214cf065f587a6c98d432670447c2=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001bd7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001bd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001bd7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001bd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001bd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001bd7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001bd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +e84edbf58da2d1e6f701242356a9ff5c710214cf065f587a6c98d432670447c2=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001bd7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001bd7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001bd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001bd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001bd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001bd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001bd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 a516af5aea961e1fac619bc19a11abc7625e9ab59754da20190a65f6acb189aa=600960086007600660056004600360026001bd7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 c1fd32b16a5b0f534498e1d40c344c968069951277a01e791c686185bceb542d=600960086007600660056004600360026001bd7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 92cdd0607a0c0d8e28a101fe5df98d04376d85e02863f5bbf3cae30c2d473e45=600960086007600660056004600360026001bd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -4439a26104f7dbd9b240afb41bc818231979bc587a218d552de0c8373f61890e=60003560008103604657600960086007600660056004600360026001bd7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +4439a26104f7dbd9b240afb41bc818231979bc587a218d552de0c8373f61890e=6000356000811415604757600960086007600660056004600360026001bd7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 69e10cddc75cda5d4d1ff9cb935650afc2118cdf1db4a3fce6caff616ede500b=600960086007600660056004600360026001be7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 b96f19b1d24912ebb702f1c393305b4cf2fc2a1cc9d6ac69d6b33b97bdf38ed8=600960086007600660056004600360026001be7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -9781012481f678a36f86a25b86fb52d017479dc87be9a704c6a2d5cddc3e7477=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001be7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001be7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001be7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001be7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001be7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001be7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001be7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +9781012481f678a36f86a25b86fb52d017479dc87be9a704c6a2d5cddc3e7477=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001be7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001be7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001be7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001be7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001be7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001be7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001be7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 f29704b621a80db5c5ee36cdc261e2a7730f418c374b2e1634cad6dca0971aa9=600960086007600660056004600360026001be7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 969208a452514723bf359c36fbe83a921e78b9e24f906637c95c63595e1e599e=600960086007600660056004600360026001be7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe f9fd9ca72e3f03cccf514d064261ea2415ba872125a85517f195603bd132e778=600960086007600660056004600360026001be7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -7633c312f2d5e65fa6721fba58659feca6ca4bdd05d0bfdc28b1ec3b34513a64=60003560008103604657600960086007600660056004600360026001be7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +7633c312f2d5e65fa6721fba58659feca6ca4bdd05d0bfdc28b1ec3b34513a64=6000356000811415604757600960086007600660056004600360026001be7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 57b73f83400b5ef877cdd52844ad09c82b8fe390d58fe63ebb018ae6ea00c481=600960086007600660056004600360026001bf7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 5ad24ba6a729b87d21f517a92ac64f275229d61d93421b2d66982d9a059ba29e=600960086007600660056004600360026001bf7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -90c69b12e37f9f8ee8ee52cd8e9b7062cf48845e25401c64d52bea5026e906f6=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001bf7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001bf7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001bf7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001bf7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001bf7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001bf7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001bf7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +90c69b12e37f9f8ee8ee52cd8e9b7062cf48845e25401c64d52bea5026e906f6=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001bf7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001bf7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001bf7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001bf7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001bf7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001bf7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001bf7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 baf5c820aa3e58c78f163531e8e2472a5cb184c6e2e6fc32f1547bd84307e656=600960086007600660056004600360026001bf7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 486ee7d509561e73aaca3f243feb8c1ac4e30807fb2d4577fb582f25e8ac81c8=600960086007600660056004600360026001bf7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 4d2c16a28c19d6fb0386ccb9005fde6d62c8ca4a5a30a4c18424f096610c6b8a=600960086007600660056004600360026001bf7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -5f46a8529b5e1d3b165b4ee0b775d9d441dd780ff7af26154697cdb10199b760=60003560008103604657600960086007600660056004600360026001bf7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +5f46a8529b5e1d3b165b4ee0b775d9d441dd780ff7af26154697cdb10199b760=6000356000811415604757600960086007600660056004600360026001bf7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 aab5ea36b89f5907f0f139b28e0f3ee64b3b4e074b2baf8d16b70356eda8c72d=600960086007600660056004600360026001c07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 43a33476d20d5465f15b69b5ac3dcba65e253264b229885b18709f4efe6d513d=600960086007600660056004600360026001c07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -440ec0e78611df0b764686eb6810cc37c97827aa13e57ff12201bf021ef0eda0=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001c07fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001c07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001c07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001c07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001c07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001c07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001c07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +440ec0e78611df0b764686eb6810cc37c97827aa13e57ff12201bf021ef0eda0=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001c07fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001c07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001c07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001c07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001c07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001c07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001c07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 6e352e2ebd9d9bac28494cab053a3e4d67401baa7484d3cadf318cf18ea7c4e3=600960086007600660056004600360026001c07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 1ad9173ac7b3cb4a897a6a58070706acc250504ec6daae074a01ac15c4150fb2=600960086007600660056004600360026001c07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 6fb28d0f57446bdba873fb68f0972bcffda1250fe19012c87dccd30f4e472e44=600960086007600660056004600360026001c07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -49cd5faa9fb3e42f1fe31081b56a22ba654ed76197bfb0a2016bd6d6fc1f27a7=60003560008103604657600960086007600660056004600360026001c07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +49cd5faa9fb3e42f1fe31081b56a22ba654ed76197bfb0a2016bd6d6fc1f27a7=6000356000811415604757600960086007600660056004600360026001c07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 8e657bb75f00a0297400a3a50e4a6b40c23f255530ebb0530afd60b3c19d53b8=600960086007600660056004600360026001c17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 368e68be00e685410b86a60080737fe47b7fc9857b479a25372224c6763df8de=600960086007600660056004600360026001c17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -8723ed6741f8d8fb5be14f39f9cbd1b908133c3db40ff77d229f8a6e22f669b8=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001c17fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001c17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001c17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001c17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001c17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001c17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001c17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +8723ed6741f8d8fb5be14f39f9cbd1b908133c3db40ff77d229f8a6e22f669b8=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001c17fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001c17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001c17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001c17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001c17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001c17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001c17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 dd628deed7e03429ba283bc2d2898d18cdc98c23fbd11b33a0f3156e733bb776=600960086007600660056004600360026001c17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 6a87672c9ee5e435040ce2b29286e062cf1aae0f571a42a3a98d60283779e3c6=600960086007600660056004600360026001c17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 069be315142d5ae52faed421c0aff3f3a6d217a81efb09d7d75a353491e8184b=600960086007600660056004600360026001c17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -234b6b37e76484cbcee4b1dd2cce1b2f8d773d64f98df062c466099e7e6926c0=60003560008103604657600960086007600660056004600360026001c17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +234b6b37e76484cbcee4b1dd2cce1b2f8d773d64f98df062c466099e7e6926c0=6000356000811415604757600960086007600660056004600360026001c17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 1ce96ce059b153a9e8802e6632ea68c611876ad0b238204659161407388fc332=600960086007600660056004600360026001c27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 2162c46f2802de086aa7f2056f60574bb25bd74b4f005f80529202067321ca8f=600960086007600660056004600360026001c27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -d5e04f14bd81b88c48371518f9f6ce4cc862b25d7ad266ced28beb79e814a386=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001c27fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001c27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001c27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001c27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001c27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001c27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001c27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +d5e04f14bd81b88c48371518f9f6ce4cc862b25d7ad266ced28beb79e814a386=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001c27fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001c27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001c27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001c27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001c27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001c27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001c27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 7aaf6a69cba0b1b8f4da62eb864a4b2a9b340d60dc5a6a40ea0807873f58d16e=600960086007600660056004600360026001c27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 4a004cb232cd1e3a18e12aeca88a7efac99815dcf28292244b346c27e445ff3e=600960086007600660056004600360026001c27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe a2cd69df723f90bafd39ea419d93c4ef4f4572831db7b45cccb7461d902c8cf9=600960086007600660056004600360026001c27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -974f7984dd282554276542e774e708d9fff20311c449fd16921c3088553c3a72=60003560008103604657600960086007600660056004600360026001c27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +974f7984dd282554276542e774e708d9fff20311c449fd16921c3088553c3a72=6000356000811415604757600960086007600660056004600360026001c27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 9bb857c9bebb6c8d81a51785922eb45ba1f92a6ed98738f7fe0405ac49ca7d53=600960086007600660056004600360026001c37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 2b982e1a87489c81cf9a368fefd6a74a9d9f72ec9c0d0551acadf19ca7bd2273=600960086007600660056004600360026001c37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -b697e84cbc6834254d4b76d3aae988420d5ad35d4f09ddd95cd4b5c0a00fdf9e=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001c37fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001c37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001c37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001c37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001c37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001c37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001c37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +b697e84cbc6834254d4b76d3aae988420d5ad35d4f09ddd95cd4b5c0a00fdf9e=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001c37fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001c37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001c37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001c37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001c37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001c37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001c37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 4d96fb0435b0d495988e9e29144bb21494dd1fb222ef9b09fd45bd624135000a=600960086007600660056004600360026001c37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 4b22465493b76a6aebfbf2a73ebe5a22f4ef92685c2b83cb99472c4bc7c0f896=600960086007600660056004600360026001c37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 3211d35021f25e0987cba652ec37b9e39657ce55a42877fc8df701649ce0cada=600960086007600660056004600360026001c37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -f1516a4385c86866869634bf6949cb7fb3d1ce8141ccf858f3ab96ed87447251=60003560008103604657600960086007600660056004600360026001c37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +f1516a4385c86866869634bf6949cb7fb3d1ce8141ccf858f3ab96ed87447251=6000356000811415604757600960086007600660056004600360026001c37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 439530133f2228fa8e0dad386601dc9695a2d09a01a803deaaf39ea5162241e2=600960086007600660056004600360026001c47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 ac6dcf8ae0079e8d984fb01831ad21afbd824fada61ee5fc1b0603c96616ac09=600960086007600660056004600360026001c47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -ad79ebdd32f48ba8ddc1ce9a9476ec848bd139f9a2ddde614138955d4689b242=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001c47fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001c47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001c47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001c47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001c47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001c47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001c47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +ad79ebdd32f48ba8ddc1ce9a9476ec848bd139f9a2ddde614138955d4689b242=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001c47fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001c47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001c47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001c47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001c47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001c47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001c47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 a4b1246e81e1369d23fb22273dac81fc1b8598cc6cb14f71cb9aeeb00a9d04fa=600960086007600660056004600360026001c47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 c892dede084f9118b019849b9be25a068558251f5430a4c1921921845ca8f512=600960086007600660056004600360026001c47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 615252e815af8eb1de05741ea6ab2c7d03d00af4eb87e088d341fffb871fe754=600960086007600660056004600360026001c47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -5e8e0a54f184c32d46fd08bdade18d6a8d3fccd64c87cba8a7703b38f62c7336=60003560008103604657600960086007600660056004600360026001c47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +5e8e0a54f184c32d46fd08bdade18d6a8d3fccd64c87cba8a7703b38f62c7336=6000356000811415604757600960086007600660056004600360026001c47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 0375fa912edf2f7c60cf125aa617f5dd3514a1356545df920965d08c41597984=600960086007600660056004600360026001c57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 ef168ca331ba8645a99cc6cb80283d220a5ed8cd973b491d4c88bbd9265b0dd6=600960086007600660056004600360026001c57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -3f32f88b66bf79172337e14f8f52104b5d200d429dcdcfa6f93192a5d6629782=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001c57fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001c57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001c57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001c57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001c57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001c57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001c57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +3f32f88b66bf79172337e14f8f52104b5d200d429dcdcfa6f93192a5d6629782=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001c57fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001c57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001c57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001c57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001c57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001c57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001c57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 b8fbeb85cccd9c76387a9cee0ea09c4e8a943ce8c2ee47ae5b7766dc6fc690bf=600960086007600660056004600360026001c57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 2dfb655ecff1501872f5979d451dc466a49c234ddcd8ac8692a36a9d91df4f77=600960086007600660056004600360026001c57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 88b21439bf1281ae594679796592b8952dc990b55d954c2a9915abc9f2703ce9=600960086007600660056004600360026001c57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -08496e3e273367dbc053d796145e71454729616fac8c190973153de07904b6a6=60003560008103604657600960086007600660056004600360026001c57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +08496e3e273367dbc053d796145e71454729616fac8c190973153de07904b6a6=6000356000811415604757600960086007600660056004600360026001c57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 4d9486b483e2f77620501d46a7caac785c5d2bc18be0bf4d30dd2478d5c7c75a=600960086007600660056004600360026001c67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 73762e0d992f8d0ea797598c83cf6a377707170d51e16aedd139629f629c4f1c=600960086007600660056004600360026001c67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -e9a4c9f4ca05b4b4e9cf162d01dbbf480b6399185a17e8caf0ef41119e39f4b7=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001c67fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001c67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001c67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001c67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001c67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001c67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001c67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +e9a4c9f4ca05b4b4e9cf162d01dbbf480b6399185a17e8caf0ef41119e39f4b7=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001c67fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001c67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001c67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001c67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001c67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001c67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001c67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 ea271447fed62860c072b33063cd8090b64081faf695c1bde2d018e826c04d31=600960086007600660056004600360026001c67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 813bfd0b2011043a0f2cc1357b62e042d3fc33aeb1e132e58af04f847f1c6746=600960086007600660056004600360026001c67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe f591e793f6861eb3c3de8fd1bfa638d3cc470ef12dea27e0164ebd28d25665c4=600960086007600660056004600360026001c67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -5f115fc96d83aea046796b3a2506102bae1afbc207f908fc4c9a8e148afb3b6b=60003560008103604657600960086007600660056004600360026001c67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +5f115fc96d83aea046796b3a2506102bae1afbc207f908fc4c9a8e148afb3b6b=6000356000811415604757600960086007600660056004600360026001c67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 d5335691ff41d95710b99a76303028bf42de25eaf85a8dbe61433792cd6262c4=600960086007600660056004600360026001c77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 5ed0e3fbb30789c1b44fa44c061ee79566cf2963e02fa490a5ca3b08b23cb521=600960086007600660056004600360026001c77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -13e9e5ac7f93cca3980104b053f3ad103533c797703864ed22277f9ce7107569=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001c77fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001c77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001c77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001c77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001c77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001c77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001c77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +13e9e5ac7f93cca3980104b053f3ad103533c797703864ed22277f9ce7107569=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001c77fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001c77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001c77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001c77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001c77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001c77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001c77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 380d14538a25f7c64f6b81b5a5e38fe5fab66c7180d90845a8876c938478aede=600960086007600660056004600360026001c77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 1d14163cc318001db2bb78675bad098c084799c5dedad0dc12a6428dbb17c6e9=600960086007600660056004600360026001c77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 120cb7b7259f1ed21453a6d1b779c82de8cb2e65eacea355a1e27d0d4035ee9e=600960086007600660056004600360026001c77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -35ef132ecdc159990e9b6ad2ceae338a486093fc05b57a770f38b667e95e0493=60003560008103604657600960086007600660056004600360026001c77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +35ef132ecdc159990e9b6ad2ceae338a486093fc05b57a770f38b667e95e0493=6000356000811415604757600960086007600660056004600360026001c77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 ac4896171d33303ef20703033f342b17cc1650dbfd30771818344ff0dc9058d1=600960086007600660056004600360026001c87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 5f42440bcc01b7d5f9ad06ff26a9a8bdecd35acb5ca8699196d4e55edab93471=600960086007600660056004600360026001c87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -4e868c4d256b21ed972bc250ee1ac518f9c014dde98341ac2b6d9e565c90f638=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001c87fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001c87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001c87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001c87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001c87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001c87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001c87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +4e868c4d256b21ed972bc250ee1ac518f9c014dde98341ac2b6d9e565c90f638=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001c87fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001c87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001c87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001c87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001c87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001c87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001c87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 6f53a391ea3183a124183d8c150ec3266fd181cf10c312cd346b1f4614216aef=600960086007600660056004600360026001c87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 b993591e9fdcac10016723e14f857946844c0a64303a908a24100f54f9df17d8=600960086007600660056004600360026001c87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 8f3537806dadb3d4a4b60815f850f4ff5ad01e8f0feee907d69ca4cf0ea013ba=600960086007600660056004600360026001c87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -0f1b2da59c4b602977dafe4d1b79597d3c8afe89ef9f71b018eb59d076c8a4cb=60003560008103604657600960086007600660056004600360026001c87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +0f1b2da59c4b602977dafe4d1b79597d3c8afe89ef9f71b018eb59d076c8a4cb=6000356000811415604757600960086007600660056004600360026001c87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 28f4dd34447648ec9e675bb317c32c8539298c27cfb9ff498cc10e95c634accc=600960086007600660056004600360026001c97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 3b1bbf312fdde0d278d6f3f34e606cbd27d98cfc897d447f5f8037028757197b=600960086007600660056004600360026001c97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -d0cc642242daac82c715d9c71ea0112f8df6106a0745056ebc81dc01104d73e9=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001c97fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001c97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001c97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001c97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001c97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001c97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001c97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +d0cc642242daac82c715d9c71ea0112f8df6106a0745056ebc81dc01104d73e9=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001c97fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001c97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001c97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001c97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001c97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001c97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001c97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 b361154f19d72fe35b479160786b939835c066ea168d45a57042771b49d43123=600960086007600660056004600360026001c97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 3adbb8134114e2c5d77a5c47590159462b2b74eeed2b38c3379bbc3bbe9a565f=600960086007600660056004600360026001c97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 3e0bfc127758c9f9ebb12099a868a468389f4f73c69b2db4afd56bec3dd8b3dd=600960086007600660056004600360026001c97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -3f2ca8c7e6934176b71288146a8af8e3dca9c054220be767de61cc27ba176ca3=60003560008103604657600960086007600660056004600360026001c97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +3f2ca8c7e6934176b71288146a8af8e3dca9c054220be767de61cc27ba176ca3=6000356000811415604757600960086007600660056004600360026001c97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 46dfd8ebec3c8c8ff8adf43651d4cf432fb2343355b3bb1cd230f6063c13b309=600960086007600660056004600360026001ca7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 857740fd572bdfd1829434b61f42b53ec3f31ac7ef3ad788677b6de599a27a41=600960086007600660056004600360026001ca7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -c3f190c9403fea1726c7123e52f40930bda6a41c98abae21e68add8590f44f5f=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001ca7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001ca7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001ca7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001ca7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001ca7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001ca7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001ca7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +c3f190c9403fea1726c7123e52f40930bda6a41c98abae21e68add8590f44f5f=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001ca7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001ca7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001ca7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001ca7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001ca7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001ca7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001ca7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 aaf645f4b626eff209e52b63facf7c9cb088404a4d11f26782d218cf3bc0c06e=600960086007600660056004600360026001ca7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 280c654513fc9039633ec318ff2bbf0570123a7eaefe2664a3863d9fcea5d253=600960086007600660056004600360026001ca7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 5d02e6a32bc8967dba0b0ad780a853d1d19505d7b792e08c92d94f65f40c96b3=600960086007600660056004600360026001ca7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -bf1db7109b5b16330b9442b03e834442165ac90fe17931e6a79ed334af1f79de=60003560008103604657600960086007600660056004600360026001ca7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +bf1db7109b5b16330b9442b03e834442165ac90fe17931e6a79ed334af1f79de=6000356000811415604757600960086007600660056004600360026001ca7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 70421cfebcef742f9487c2db6f68d667ef753858aaaf6c301af352d1b0871e27=600960086007600660056004600360026001cb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 a413342f8f17847a45f5421ef05f891bb039b7896940c4c503721546b3a11b9e=600960086007600660056004600360026001cb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -abc9893ccc2e8bef454b27536ab005ebe0e7693ba75754709b8cec6c6c77bf01=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001cb7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001cb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001cb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001cb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001cb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001cb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001cb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +abc9893ccc2e8bef454b27536ab005ebe0e7693ba75754709b8cec6c6c77bf01=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001cb7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001cb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001cb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001cb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001cb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001cb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001cb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 7560dea88f0ac2ed905efb2f9ba2ee018caa5ab5b25148b9d6fe1c9767f68b74=600960086007600660056004600360026001cb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 7254df8898b0191c5de0a4562b34d446bcf7712907f80b06ea9315a8e8f88de2=600960086007600660056004600360026001cb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 7e8ab85fe33c0c8473895fac19e3302573291ac2c6dba89e62e5b9e4699585b6=600960086007600660056004600360026001cb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -16bfb54cbf3bbf0e6ccb475c5fd7245ae24f7bfb351e02368eedc9482de19390=60003560008103604657600960086007600660056004600360026001cb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +16bfb54cbf3bbf0e6ccb475c5fd7245ae24f7bfb351e02368eedc9482de19390=6000356000811415604757600960086007600660056004600360026001cb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 7f39f7ef0125cfa83ce32a6c4c20af7377a7df8755e37da7d7a1a6389ba57365=600960086007600660056004600360026001cc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 1ec250321b9543f1a2b63b55786cb643ca5627b875f2dfa2f3821f4f08e621f9=600960086007600660056004600360026001cc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -412ae4e449f6ee919062d19559e8ae94f7017911a509c4af228fe8b6832220f7=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001cc7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001cc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001cc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001cc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001cc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001cc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001cc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +412ae4e449f6ee919062d19559e8ae94f7017911a509c4af228fe8b6832220f7=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001cc7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001cc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001cc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001cc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001cc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001cc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001cc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 e0ccaf557395e255d198d6c34bf27e0aee8d4762711964e57fb9411b21e6eee2=600960086007600660056004600360026001cc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 a1aefc889e3adc93641419f664b8ea9f95149d97eb8cae8a5dad4ac9e34b0855=600960086007600660056004600360026001cc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 686770c818dbadcfcd15a05fbc6df2c728d670e1868fa635a9e0e9c716cb26ba=600960086007600660056004600360026001cc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -5f378f22ff9567958d9bbc63045ec893afb585a57548203da4bd3c76d680ac32=60003560008103604657600960086007600660056004600360026001cc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +5f378f22ff9567958d9bbc63045ec893afb585a57548203da4bd3c76d680ac32=6000356000811415604757600960086007600660056004600360026001cc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 9e401dddb0173c98851dd8f2181b97d7a25699de42ae789d25fa9e16feb28842=600960086007600660056004600360026001cd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 0405d3537840abcaf76d3b1c998546b198f5b6731723d276b47dafcfb1b9039a=600960086007600660056004600360026001cd7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -0617e94807baea3f5758b9863492443530f4168146a2052aec9d02b47c19dad3=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001cd7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001cd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001cd7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001cd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001cd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001cd7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001cd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +0617e94807baea3f5758b9863492443530f4168146a2052aec9d02b47c19dad3=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001cd7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001cd7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001cd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001cd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001cd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001cd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001cd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 881c8bbef1191c85c0903fedd72e09004d647749c5bb19cc3e9d8711c0f3f63e=600960086007600660056004600360026001cd7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 27509aba5dd674de97ac3bc3f02ff621cd5204e8c379ea6134382a887bfaf012=600960086007600660056004600360026001cd7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 523d1338396825bc37d7d890d5c741bc19ca16f5702710eb0c4463e7dce88a8c=600960086007600660056004600360026001cd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -c4888914c26f389aa69980067e9024f8180821fd7f2555ad685f6d63a839f4a4=60003560008103604657600960086007600660056004600360026001cd7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +c4888914c26f389aa69980067e9024f8180821fd7f2555ad685f6d63a839f4a4=6000356000811415604757600960086007600660056004600360026001cd7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 47d43ea4b70f2e35c0174a4ae1735c42eaecb0c5165f77fb49840a9ffb892c78=600960086007600660056004600360026001ce7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 ca3fa8eddd68904f7cdcf567db56125df0f53cfacdae06877cc59cc6ebf7b96d=600960086007600660056004600360026001ce7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -f7555bae98a13bc3cbc3babcbbad27008299fe802625d8891e63aa16eafa8c7b=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001ce7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001ce7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001ce7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001ce7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001ce7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001ce7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001ce7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +f7555bae98a13bc3cbc3babcbbad27008299fe802625d8891e63aa16eafa8c7b=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001ce7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001ce7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001ce7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001ce7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001ce7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001ce7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001ce7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 583e89c83bea326aed85a4a2a8134d938385a5ea79de4ad2579117bae4a51bcd=600960086007600660056004600360026001ce7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 f295d6f7b7cf81c4c473520ee2757f551ae0ce2b97b11eea2eeae988255bf125=600960086007600660056004600360026001ce7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 36398ac27c1816ccf217d9b386b8f7a5be1aa664449309177ab911ba9aad0365=600960086007600660056004600360026001ce7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -0003dfc581f74bddc1450abd12552dcaf94121bc7295e1fbed29045883cbe066=60003560008103604657600960086007600660056004600360026001ce7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +0003dfc581f74bddc1450abd12552dcaf94121bc7295e1fbed29045883cbe066=6000356000811415604757600960086007600660056004600360026001ce7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 97dd6dd62fffecc0fc6e69e9a3015126f136fff8a8b372a0228e63397e7b11b8=600960086007600660056004600360026001cf7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 a5766152b2a05afa2ca57cf3967be5e3867341faa344734fd2b694efafa49f0c=600960086007600660056004600360026001cf7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -b806609a7674f3eb7ef902f665ff511113138d949164be00b3b847f1843d00c9=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001cf7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001cf7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001cf7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001cf7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001cf7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001cf7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001cf7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +b806609a7674f3eb7ef902f665ff511113138d949164be00b3b847f1843d00c9=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001cf7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001cf7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001cf7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001cf7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001cf7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001cf7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001cf7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 8764a3fbee18814e6d7eb42c119d88ea6ec6ddf9a5af976075158c867698f833=600960086007600660056004600360026001cf7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 229ddb8f6d9b8dba7d1178c0ac7f5231f827dc28442056b4ea17fab3522cd271=600960086007600660056004600360026001cf7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 004cd42d73a275dddb68180477b7677ab62e5d6f6057658b78f9063e7c06278e=600960086007600660056004600360026001cf7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -839cf458c161c9fb3272afc4d4f935159ee82366dfe32b7aa5576b40849273b0=60003560008103604657600960086007600660056004600360026001cf7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +839cf458c161c9fb3272afc4d4f935159ee82366dfe32b7aa5576b40849273b0=6000356000811415604757600960086007600660056004600360026001cf7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 324f6a027dcca08b9b1c28a8b43984511b72b4ccd7d8d93974a0c6e2bd317115=600960086007600660056004600360026001d07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 8adb8a14d90cb3bd6040ce08ca1608ec3f03c1fc6ad2fd13b81d82c66782b704=600960086007600660056004600360026001d07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -1f01ea747ddb19f54f9dc9042d65774b8705b8ee7ad09a44f00046ff2a66e6c3=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001d07fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001d07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001d07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001d07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001d07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001d07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001d07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +1f01ea747ddb19f54f9dc9042d65774b8705b8ee7ad09a44f00046ff2a66e6c3=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001d07fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001d07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001d07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001d07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001d07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001d07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001d07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 2d5bafeb06df5023ed65dda8ffe7f2e1baaa45cdfe86c81f1d3bda2fb00dd363=600960086007600660056004600360026001d07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 8bf25357302a2ba964bec400939e5db164a9af11273b528c0ebe6109279fc22f=600960086007600660056004600360026001d07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe c0e4c6e0704bb5bf9f5368cde41925f637a4de4205a6edbf2b1cbf99c3db06dd=600960086007600660056004600360026001d07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -29b933f1d48738476cb6774bbd6fe38268e4a8ca9114ba5039bbf553c092a9de=60003560008103604657600960086007600660056004600360026001d07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +29b933f1d48738476cb6774bbd6fe38268e4a8ca9114ba5039bbf553c092a9de=6000356000811415604757600960086007600660056004600360026001d07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 7fcaaa2ecb30302187e61f37facfce1000d5e6b55c42ab7be83c0731a24d4440=600960086007600660056004600360026001d17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 e214794f819f7b5e1305eaef18f24c1a985c98f158a18d621abe8fd21bc26171=600960086007600660056004600360026001d17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -e8bbf703e8a2b15300745f8e110a465726c0c318d989625eaca882de89de4817=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001d17fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001d17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001d17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001d17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001d17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001d17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001d17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +e8bbf703e8a2b15300745f8e110a465726c0c318d989625eaca882de89de4817=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001d17fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001d17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001d17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001d17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001d17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001d17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001d17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 47fe4485c32d3a236868937fe4f5009cc531650588c5b2fc68c41b16469a0a96=600960086007600660056004600360026001d17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 f10b6f035e1a56675d8c3b158a752972d25e7a6b881ca653d2a51c07c6a64ebe=600960086007600660056004600360026001d17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 839e3adf3131ddd154706cc442affd253483b29fe17af2e5d53afb7c8e482e23=600960086007600660056004600360026001d17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -c89f6034d9a030297654740493ddc7a2a5f6e240b29684a43ad6f0fc735cb10c=60003560008103604657600960086007600660056004600360026001d17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +c89f6034d9a030297654740493ddc7a2a5f6e240b29684a43ad6f0fc735cb10c=6000356000811415604757600960086007600660056004600360026001d17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 27e04042d96f3bfd4bc7ffab12c8e6da8dd89ded025e2190a72c5ddae24b40ba=600960086007600660056004600360026001d27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 6789ae48d53a5fcfe44d3bd79fcfe6c5babe219dcb2c62f0c90d461544765644=600960086007600660056004600360026001d27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -0a87e0b425c4292daebce0ea27c5f00546272c4638017ac11b4b32b0473afa21=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001d27fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001d27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001d27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001d27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001d27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001d27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001d27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +0a87e0b425c4292daebce0ea27c5f00546272c4638017ac11b4b32b0473afa21=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001d27fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001d27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001d27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001d27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001d27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001d27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001d27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 c1faf1fa66ce438c83fdda08f99d9493edc31fc175dc063fdaec53a617021634=600960086007600660056004600360026001d27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 c39b88b457679ecd4fc2ac6b6327c1e1fc72686b55a811d51832de64d5b1946d=600960086007600660056004600360026001d27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 662889ac99a3ea140e46a9b23c1c0e93c36e424a8b0bf9c470caaa64120f9398=600960086007600660056004600360026001d27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -b6670619113d57dc4d111396d11f5d88e79fff101834b4e1bfd5a1c6a97046d2=60003560008103604657600960086007600660056004600360026001d27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +b6670619113d57dc4d111396d11f5d88e79fff101834b4e1bfd5a1c6a97046d2=6000356000811415604757600960086007600660056004600360026001d27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 7a817fd5ad0721df230e0495b386b7f86fd2b478ddb25af2dcdb69f3f970e36f=600960086007600660056004600360026001d37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 9b0334b33fc11550c7d3f46efbfaebb1d863c986293f3f33700b00f11aa2181d=600960086007600660056004600360026001d37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -93c2648c72a95c6d10f6ca25e6fbbb98bfe781fed47bd28b8afa0c2ec546d418=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001d37fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001d37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001d37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001d37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001d37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001d37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001d37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +93c2648c72a95c6d10f6ca25e6fbbb98bfe781fed47bd28b8afa0c2ec546d418=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001d37fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001d37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001d37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001d37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001d37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001d37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001d37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 b35e3f5d8e752fc1fed8671de2fb3d95d42c67b490f010162b2ecd624666a74e=600960086007600660056004600360026001d37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 064f3d1db755c16a7e8fb51d0b847d4b670641ccd8ada7cea1ea9779e5de9e45=600960086007600660056004600360026001d37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe f11bf1529932e01464f2ca2b42783b2805b74c3223cda19f8835c9664aaff6e4=600960086007600660056004600360026001d37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -e5718f59f49690be6b36f5d0581b7292b6db85bf0dc6060627b0f728cef5f8d3=60003560008103604657600960086007600660056004600360026001d37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +e5718f59f49690be6b36f5d0581b7292b6db85bf0dc6060627b0f728cef5f8d3=6000356000811415604757600960086007600660056004600360026001d37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 1217c0355376c0b2adaac7603b0c744a37dff7a4c3199a5149f1f04aab7cc5b1=600960086007600660056004600360026001d47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 075a24ccb35099b1f869b1f84153341aa0cddaaf7dbc8aa1b093bb8833498e78=600960086007600660056004600360026001d47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -15766aa9806a8cd8bcee26202bba40881e9e0e8a66db2a4273891512af1acd64=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001d47fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001d47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001d47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001d47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001d47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001d47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001d47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +15766aa9806a8cd8bcee26202bba40881e9e0e8a66db2a4273891512af1acd64=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001d47fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001d47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001d47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001d47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001d47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001d47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001d47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 72d686960fe6db035ab23e69f2adce742b03d333f2cb621d69ef03b4c22b6ffb=600960086007600660056004600360026001d47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 6d29b72e08ca8356711aa2ee00c1c158beef62ea84cc170c68bf7f7eb7175e83=600960086007600660056004600360026001d47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe e14d4d4b85f8ffd1c95fa85d76010482ebc3221c9d146fd987e0be8bc601d231=600960086007600660056004600360026001d47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -be8d399779cb2f1b616afb6174ba07ac61dd26cf04c3bea05f944e85666d81d4=60003560008103604657600960086007600660056004600360026001d47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +be8d399779cb2f1b616afb6174ba07ac61dd26cf04c3bea05f944e85666d81d4=6000356000811415604757600960086007600660056004600360026001d47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 f41c0f6c76fc66275366fe14f2b8f2af35b0594a1b685ba5e78d1af61cdf98da=600960086007600660056004600360026001d57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 9a38442cd0670da65eef3698a1898427c2ce78e75a1ab3e5c70b120b0b9191d0=600960086007600660056004600360026001d57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -8e96e35d11f9f8532c879452f5db63f0895021bf129d206548a2c791629c4450=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001d57fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001d57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001d57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001d57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001d57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001d57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001d57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +8e96e35d11f9f8532c879452f5db63f0895021bf129d206548a2c791629c4450=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001d57fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001d57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001d57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001d57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001d57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001d57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001d57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 5c8ceecb04a87ff4b1c2b53ef865c0601829c40ae5f17653f39b3d936441a6e2=600960086007600660056004600360026001d57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 dfb435ad82a9369f7453ba8f286b4b2e7499afd0b7ed7ef51eef7720c62a8ae6=600960086007600660056004600360026001d57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 4b6fde58d18bd57262e281bb7e9fb59ff191306e2fc0277d9e5f10a40c2091f8=600960086007600660056004600360026001d57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -d3b66b44788668af78eccdbf58d84642ecff673e20b7502e1663cc431ab5d342=60003560008103604657600960086007600660056004600360026001d57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +d3b66b44788668af78eccdbf58d84642ecff673e20b7502e1663cc431ab5d342=6000356000811415604757600960086007600660056004600360026001d57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 163945c5a9be31f3e018865aa765df7b14b682b3a9b9f0d021d4c29a7bb28f2a=600960086007600660056004600360026001d67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 2aee190bd85c6b513ea8f30f27da989327d6183e568c7f7ff991152eeb2c757c=600960086007600660056004600360026001d67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -f7ad6f86da09b659cfc4d9947a7cac528976d6582509b69379eda8a21b998d7a=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001d67fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001d67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001d67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001d67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001d67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001d67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001d67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +f7ad6f86da09b659cfc4d9947a7cac528976d6582509b69379eda8a21b998d7a=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001d67fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001d67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001d67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001d67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001d67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001d67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001d67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 875f38a3db0ca95ee896d4b699bbf0ba9d25467a71c3572a1df57a7e07607741=600960086007600660056004600360026001d67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 b012e1e4d422e456b576d4cbe3f81675855c26125b190073924d2f8239080d5b=600960086007600660056004600360026001d67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe ae63b4287c7620c858ac5dc37c145397a6d69612673bcd9ef45f0cdb0e7a8d17=600960086007600660056004600360026001d67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -64def6d894038ace4d29e4560133253a9dc739a4e008b598d12a6fd6e3236429=60003560008103604657600960086007600660056004600360026001d67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +64def6d894038ace4d29e4560133253a9dc739a4e008b598d12a6fd6e3236429=6000356000811415604757600960086007600660056004600360026001d67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 f4ef7077431710006a2b764b801578dcd027e2b86a6358bab4e486c92ef4685a=600960086007600660056004600360026001d77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 775f19d00e1080de5f07d6846e7b51e9929ab37f6a27b7084794ab1c0f715b3e=600960086007600660056004600360026001d77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -16c4d61fce0ada82ed2ec4656ab3865f8cdeefa98c7f5bf9d3fd06d8871a9419=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001d77fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001d77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001d77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001d77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001d77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001d77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001d77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +16c4d61fce0ada82ed2ec4656ab3865f8cdeefa98c7f5bf9d3fd06d8871a9419=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001d77fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001d77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001d77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001d77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001d77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001d77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001d77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 a824e8ac73e123c5e07e5e107d2eb7abd178559b577294885c757f2f076311a9=600960086007600660056004600360026001d77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 c1329fe93858de8b770dcc1c7e52f7349ac7bdb8610a18207bd5ae13b341a07a=600960086007600660056004600360026001d77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe cf8c4842e4bb9ece4811336ab2c422087238a45367ba1c11e193d7346ae31065=600960086007600660056004600360026001d77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -331e3b5c867cdd5aa20285b95f2398f78d0e5a68ec062acbbc89fc8286da3037=60003560008103604657600960086007600660056004600360026001d77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +331e3b5c867cdd5aa20285b95f2398f78d0e5a68ec062acbbc89fc8286da3037=6000356000811415604757600960086007600660056004600360026001d77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 94a82bf0575a00c9163b91fef857f2cfff2ecbaddcef44ea7c2ce5b6b38d9d64=600960086007600660056004600360026001d87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 5c0cec19f2d3ae92bdce5b5654fd8bee8044611589caccc23399cb60a6464144=600960086007600660056004600360026001d87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -405255f663538275a907425bdcdb797e029036741dda74a120b29d7149dbce63=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001d87fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001d87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001d87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001d87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001d87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001d87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001d87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +405255f663538275a907425bdcdb797e029036741dda74a120b29d7149dbce63=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001d87fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001d87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001d87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001d87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001d87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001d87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001d87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 8f8f08994f5f89ac8a9cde63b2ea1d677be283ca214089c5e0cf38fe13f4db53=600960086007600660056004600360026001d87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 323f0b237c36d5cea8ac5dcf452b5f85b7ae706d87f01d7acbc46109573b3192=600960086007600660056004600360026001d87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe b36eb463bfed34c5d90ddee6c1592d4e8f93c2600bb220228b2556981449d367=600960086007600660056004600360026001d87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -08978ff8d624d9ecb06071d361315d2228da3686f03af064dccec557f1db11c0=60003560008103604657600960086007600660056004600360026001d87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +08978ff8d624d9ecb06071d361315d2228da3686f03af064dccec557f1db11c0=6000356000811415604757600960086007600660056004600360026001d87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 6c36283664f46e81235d3afad69072c43635e31c46794fbb3474f28078eb5793=600960086007600660056004600360026001d97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 ac6d8178f43cd0a6d05514c5e3af50ce7ddd3333535fba9910632b863fd8bf43=600960086007600660056004600360026001d97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -c724c311074c9a2ee54976e99d4ae0ce19897de982f1b5187a9cac58c25bf716=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001d97fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001d97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001d97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001d97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001d97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001d97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001d97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +c724c311074c9a2ee54976e99d4ae0ce19897de982f1b5187a9cac58c25bf716=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001d97fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001d97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001d97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001d97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001d97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001d97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001d97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 69ec86bbdd4ade4070a50f6872983f5b59ae4333942d075b96995cbc94568e65=600960086007600660056004600360026001d97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 ec0cd2b30d45a55c363acd904e6dcde544e5cd7eea4309392e9911f45e33007a=600960086007600660056004600360026001d97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 30bd6becf598597a96536d8eed93f9ef5644fb05954af3fdd072867adebbe13b=600960086007600660056004600360026001d97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -8d6322ba141319d9776813a3655c5f299ed3fd2fe1eae15948be78c79d4a0738=60003560008103604657600960086007600660056004600360026001d97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +8d6322ba141319d9776813a3655c5f299ed3fd2fe1eae15948be78c79d4a0738=6000356000811415604757600960086007600660056004600360026001d97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 9458b6fd43c02f02f8542d869d0834b1f04e27c4fae4624584e7ff5b4fc79052=600960086007600660056004600360026001da7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 59b9f014a87bf7838def2dfcd331d91b7a0915ae0db03002c81d932ac5f15f3f=600960086007600660056004600360026001da7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -3f9c76d4bbfe55059fe39bae69dbef405332254451e1d7a1c7c9de151e2a8b01=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001da7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001da7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001da7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001da7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001da7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001da7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001da7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +3f9c76d4bbfe55059fe39bae69dbef405332254451e1d7a1c7c9de151e2a8b01=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001da7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001da7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001da7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001da7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001da7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001da7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001da7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 65d8dc667146b84ee62eb1fcf2694a76dd31a27b08bc00d70407974d47ccff50=600960086007600660056004600360026001da7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 fca911db6adcd2fa48c069a8353858eb2955e951b3b75157b20ed2e03846ee82=600960086007600660056004600360026001da7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 8df84a49644c995eb18520a8c5230a2d024963dc265b89ae2324a536d81147d3=600960086007600660056004600360026001da7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -870634eae2549eee081ca5f11660282ef4f8069ccf71f29afbc9fe6cd6ae7da5=60003560008103604657600960086007600660056004600360026001da7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +870634eae2549eee081ca5f11660282ef4f8069ccf71f29afbc9fe6cd6ae7da5=6000356000811415604757600960086007600660056004600360026001da7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 f631b07b0a7264219a1cc041fac7ceb62d22bb65ea09d6fc5700beca11e37d9f=600960086007600660056004600360026001db7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 b16bf31ae1d6b8fa9e718acf1fb557be4eb770c6775bc2ee0932d86708ce6099=600960086007600660056004600360026001db7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -e8c593b5498e8afc393365076b80fe4770005e308197bfa5b323da1a66c0e150=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001db7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001db7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001db7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001db7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001db7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001db7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001db7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +e8c593b5498e8afc393365076b80fe4770005e308197bfa5b323da1a66c0e150=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001db7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001db7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001db7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001db7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001db7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001db7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001db7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 c2a78dda9a8c7eb48f529d3e449d2d8b788e05265fd839764756e2c2076607e0=600960086007600660056004600360026001db7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 bd03b9dbe76b449ab5fe33f92b39d9ea5a8c6a16d2c6caf2a380b1e902a80415=600960086007600660056004600360026001db7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 360a00f371f04b54bbfe13be05b8788c3e59ecfaa6881bc5e910b79c52a66307=600960086007600660056004600360026001db7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -97a2c48b39d7d3a8f624dc8ce0181d677e218139d94caa6a8ae82ecc4cdab09c=60003560008103604657600960086007600660056004600360026001db7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +97a2c48b39d7d3a8f624dc8ce0181d677e218139d94caa6a8ae82ecc4cdab09c=6000356000811415604757600960086007600660056004600360026001db7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 cbb0cf3fbb3f5c80bc9096e3928144501943c2feabab7ff0a3a2bb9eac5bceb5=600960086007600660056004600360026001dc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 71c068967993c00134b8538ed23f2b6486441267f31e0a92e0e25b3810e81497=600960086007600660056004600360026001dc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -fb7689fd218372578e00ff151d993cb0ce2b792bc3acb9e37a59f55803ae4503=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001dc7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001dc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001dc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001dc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001dc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001dc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001dc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +fb7689fd218372578e00ff151d993cb0ce2b792bc3acb9e37a59f55803ae4503=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001dc7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001dc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001dc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001dc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001dc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001dc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001dc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 e8b398e970686741ad54f51190189e1a5ded585f482a2c2f4a697657033255de=600960086007600660056004600360026001dc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 7b370d0bca923d09f516af5a322472733de1589ed6ee37b1bc73c487f425b9b9=600960086007600660056004600360026001dc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe cfd246eac675a4ceecd8074c3e5583c1990f9fcf2b6d130572c1f0907259b360=600960086007600660056004600360026001dc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -b781132f17c619b2ccd714cc1622704e74ee10935fea39cb439d9415e12dfe57=60003560008103604657600960086007600660056004600360026001dc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +b781132f17c619b2ccd714cc1622704e74ee10935fea39cb439d9415e12dfe57=6000356000811415604757600960086007600660056004600360026001dc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 e6c8c72aa6f08fc6db5875486d607500fe965e04ee25e73df35785e576978995=600960086007600660056004600360026001dd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 ab28dbd529d23d84f896551e629a1915612e0745710de40eaebea1aefbd4b014=600960086007600660056004600360026001dd7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -160cf541e6977d5a09a2c467fbfc2e629830f54ad1b8a4c91489eb25c930b7db=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001dd7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001dd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001dd7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001dd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001dd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001dd7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001dd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +160cf541e6977d5a09a2c467fbfc2e629830f54ad1b8a4c91489eb25c930b7db=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001dd7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001dd7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001dd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001dd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001dd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001dd7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001dd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 2ea73f874cd7182e0b5eff43404d91593e05b89bd5db0752a0452c8586146ed0=600960086007600660056004600360026001dd7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 b85dfb52a17799294dd49d478e9e9297de19b1b14aa7420c7bb82ed53379f2ef=600960086007600660056004600360026001dd7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 8d86480db10dd96397832298544f8bdeb3007d321358b2f0aaaaab18939389a6=600960086007600660056004600360026001dd7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -9554b5e01a9a9f64a9b2f671630d836c3ce772d052b45d89b73fd098b8e4956b=60003560008103604657600960086007600660056004600360026001dd7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +9554b5e01a9a9f64a9b2f671630d836c3ce772d052b45d89b73fd098b8e4956b=6000356000811415604757600960086007600660056004600360026001dd7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 ae45994ab69564b9f752678823f17108f35b7c43d1ecc3c79e73d13955495deb=600960086007600660056004600360026001de7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 f8347065d449af9ab723ebf487760effac17d0a688f7d2d692ae4b1083d6087c=600960086007600660056004600360026001de7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -fc3b1f81b4101c234f757b02963c76e321e1a19c86313f4a37c2729f36f96917=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001de7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001de7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001de7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001de7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001de7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001de7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001de7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +fc3b1f81b4101c234f757b02963c76e321e1a19c86313f4a37c2729f36f96917=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001de7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001de7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001de7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001de7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001de7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001de7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001de7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 c98b11cb7ff2d2c263c6071e3aef3949e61fd49135c9c702b5f97cfa4868b6f9=600960086007600660056004600360026001de7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 99b152ba3596c620cfb81f360a2f1f13a3b67e8f79e407e04ec7b120278054e0=600960086007600660056004600360026001de7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 894f7d896c6f3f97aa74475659106e083c12f1c9485b47ba2a5a46b914750377=600960086007600660056004600360026001de7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -197ace0677b5717dbe9bbb38a007d4f3d9812825b88e9a5d26d3bc6c217120e6=60003560008103604657600960086007600660056004600360026001de7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +197ace0677b5717dbe9bbb38a007d4f3d9812825b88e9a5d26d3bc6c217120e6=6000356000811415604757600960086007600660056004600360026001de7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 978d1bb072c242655c64bd67200292aaea8a905e434635e1ed8f7ae9b89db82e=600960086007600660056004600360026001df7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 61488411be39d9c3e1e0f15dc89143aaa78839f44d32f7e9e003bd71e87c2402=600960086007600660056004600360026001df7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -d6466f2f27d8ca3e9b3476113929d47bff8e5f2a00a660c80f3caa990ece0cd3=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001df7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001df7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001df7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001df7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001df7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001df7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001df7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +d6466f2f27d8ca3e9b3476113929d47bff8e5f2a00a660c80f3caa990ece0cd3=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001df7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001df7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001df7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001df7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001df7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001df7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001df7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 d8c5bbd4c907b6331666081e9482c1c0141adce57f2a250e86ae4052f79f4aec=600960086007600660056004600360026001df7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 850f5e5daba158cac9a20182ac89b09b1776df7dcf2201a5ff99e176ae70885d=600960086007600660056004600360026001df7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe b585478be2d5a7a3f172740d15bdd195936eec002036b56c44fa417a42da7406=600960086007600660056004600360026001df7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -c88490b7609917a71bd76632e9201eec743796779723888aa6223d47f4404568=60003560008103604657600960086007600660056004600360026001df7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +c88490b7609917a71bd76632e9201eec743796779723888aa6223d47f4404568=6000356000811415604757600960086007600660056004600360026001df7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 50d02a31714a13bad96ed7cbde3f5a7a50f12d568b29388904605f5671ed9c50=600960086007600660056004600360026001e07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 c062216b1dc2fc8721c9c45646301694a0587b25a9fd9503f1b02a292fc86a1d=600960086007600660056004600360026001e07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -b52a33d8d4afda44273bff7efec552c32ec14abc6513da8f23aedd5bb694f58c=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001e07fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001e07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001e07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001e07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001e07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001e07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001e07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +b52a33d8d4afda44273bff7efec552c32ec14abc6513da8f23aedd5bb694f58c=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001e07fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001e07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001e07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001e07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001e07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001e07fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001e07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 0b920f1fe84b8c374a1ab93bf9aa760ae7c6806e0e2c6108f805561135365f66=600960086007600660056004600360026001e07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 a2d670b4640142e018a6bd97b67b4a224d1e2cfd9d83ffca7323f1d7427760c2=600960086007600660056004600360026001e07fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 71170857b67aa7ce3c99da25de12b434b19462d52c8a3869bac0131d8252f5dc=600960086007600660056004600360026001e07fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -838dff6d3389cc17980a9c4a11de3e5c7391e8123c802f36008101d92e2a008b=60003560008103604657600960086007600660056004600360026001e07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +838dff6d3389cc17980a9c4a11de3e5c7391e8123c802f36008101d92e2a008b=6000356000811415604757600960086007600660056004600360026001e07fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 9e712e19129b8d66e24b17d1ffc677ab6c383bdad01e9082ce2c14d52e6a7076=600960086007600660056004600360026001e17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 e70105ebd0e122c5ef776e26d866cde52530a8c006e3295527f3deab717c1e66=600960086007600660056004600360026001e17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -d8fdc37daa1e640c784d5401d6d0ca8e7de0cc2e4b29aa166df5ace2353ce82b=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001e17fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001e17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001e17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001e17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001e17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001e17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001e17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +d8fdc37daa1e640c784d5401d6d0ca8e7de0cc2e4b29aa166df5ace2353ce82b=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001e17fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001e17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001e17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001e17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001e17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001e17fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001e17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 15181965c3c5ef7cb70238bd725622c8fbc4377cf46f839fc1259b065d9250fc=600960086007600660056004600360026001e17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 358c91b529a4ecebb28364ec1f8d99613fb9efdbf0872f1958d226e4a860912d=600960086007600660056004600360026001e17fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 918ac15c8f7a5083d26993a83325ec98a4b16de9aa6a0215837275a050c6205f=600960086007600660056004600360026001e17fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -147142e2be4899fdd8ff9959236d1abac1e0033ed7c87b43e64ef73dcb86226b=60003560008103604657600960086007600660056004600360026001e17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +147142e2be4899fdd8ff9959236d1abac1e0033ed7c87b43e64ef73dcb86226b=6000356000811415604757600960086007600660056004600360026001e17fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 d8b9606ab675ee115b0e60b04773f5d4a2df97c5eb99a443419fc6b3e1b2e474=600960086007600660056004600360026001e27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 e6ce16ceb898cfa9bbc0cba76df7cc00535795959f08a5eb0c02c9fce0ea34fc=600960086007600660056004600360026001e27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -1d996539b54dc4ceb0bc68671b905a148681459bbde92ba5e9e3c1c8020a407d=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001e27fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001e27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001e27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001e27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001e27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001e27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001e27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +1d996539b54dc4ceb0bc68671b905a148681459bbde92ba5e9e3c1c8020a407d=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001e27fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001e27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001e27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001e27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001e27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001e27fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001e27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 f43f03e7ed78f6e2bfb5a70c6ade85dadf46d6c91afe7e60b2272df4e4aab3ca=600960086007600660056004600360026001e27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 748ff30f8c474d41e87de746de6037d7d9d826eca2b7ecfa80c98fc8e6466b1c=600960086007600660056004600360026001e27fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 90f9c8ed5b164c21fe987d8145eba63e508302a173df32caf5bc8a463fceda63=600960086007600660056004600360026001e27fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -8c4e34bc2e6dc2f06a18b86f33ff5fd536cd7ce3f50493b5efc56f91e63b3ad2=60003560008103604657600960086007600660056004600360026001e27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +8c4e34bc2e6dc2f06a18b86f33ff5fd536cd7ce3f50493b5efc56f91e63b3ad2=6000356000811415604757600960086007600660056004600360026001e27fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 2ef23e24840908e28f272d4c7f173528c3570840129b69c795879c61310015ac=600960086007600660056004600360026001e37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 2a5ebcc50b0e976a4ab894984c5a6dae8b8ea71ef0625047d15c4cdbb7866be6=600960086007600660056004600360026001e37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -5d948400df239177e455905eba8f51f32b0f5e5e69b68e244b8fcffa8703145c=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001e37fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001e37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001e37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001e37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001e37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001e37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001e37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +5d948400df239177e455905eba8f51f32b0f5e5e69b68e244b8fcffa8703145c=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001e37fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001e37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001e37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001e37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001e37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001e37fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001e37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 7184f958970c18d7adfb10b2ec2b601452aa1131c2b21ec877b0c5ebd7f51633=600960086007600660056004600360026001e37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 7804a58e973d93c54d03faedc926895199fbfd19e9461eb229e0da36afa3042d=600960086007600660056004600360026001e37fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 1a63c5fdb5fae8d3416f1243245bf1231abfb8c558d8509b76d915292c27f6b9=600960086007600660056004600360026001e37fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -4d248672a92e38f633896e9e5bf15c772fa582fe5ca04dbc81d771a60bed395f=60003560008103604657600960086007600660056004600360026001e37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +4d248672a92e38f633896e9e5bf15c772fa582fe5ca04dbc81d771a60bed395f=6000356000811415604757600960086007600660056004600360026001e37fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 f4b9860f730ac0ce4cdd628b4a68d77127144da2d6336f249cd0e1028b331958=600960086007600660056004600360026001e47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 004d869f75d3113eb4b1a8b5c3282f1ccf2f42d15f9708bb95ef48907fc696db=600960086007600660056004600360026001e47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -8fab11955a49591336a0bce44ce64f1aa3e2ed52e8220b54a112d7bb2314126b=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001e47fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001e47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001e47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001e47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001e47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001e47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001e47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +8fab11955a49591336a0bce44ce64f1aa3e2ed52e8220b54a112d7bb2314126b=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001e47fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001e47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001e47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001e47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001e47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001e47fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001e47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 1c11ba4de8f65fd6ebf6bbd4701233fff32babd9598a55a7c2274e6aff3b0bc2=600960086007600660056004600360026001e47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 7262317bcd650546789641e6af1fae56fee788a8e5eb01a91073b0038a8ceadb=600960086007600660056004600360026001e47fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe c940dcf747770c6769d1247f2f1c4a24b376efff10320068098674d6bc55348f=600960086007600660056004600360026001e47fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -a60757a515fed7e4001a6c80bf5eb79662a3e2f790b5d218ace612848892d24d=60003560008103604657600960086007600660056004600360026001e47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +a60757a515fed7e4001a6c80bf5eb79662a3e2f790b5d218ace612848892d24d=6000356000811415604757600960086007600660056004600360026001e47fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 bd43068cdf178c5141677a7dc456fe49fdd5542aa6015f895c0cae8dcfe05218=600960086007600660056004600360026001e57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 f949f0e895164d46b43e1fd7e6891f8487c3ba0f76e2f425586c975fee6bf23c=600960086007600660056004600360026001e57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -c204db0c59faaea7e9626de3a05dc08fd7c77bbf3f912264526c4a202f7df095=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001e57fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001e57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001e57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001e57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001e57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001e57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001e57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +c204db0c59faaea7e9626de3a05dc08fd7c77bbf3f912264526c4a202f7df095=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001e57fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001e57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001e57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001e57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001e57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001e57fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001e57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 92b4f61420d40e50c74aeea4079577f930e1fe9c93c6ff3b6917c0a63f60002a=600960086007600660056004600360026001e57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 a01cdfb37dfffb72a91d601b507b85817df2b9a0e326121e61f6b0c65cbb1ca3=600960086007600660056004600360026001e57fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe bb21e9c890ae5f2f18be121ca55d570705aa54ce039be0f02e8e95414a7ee777=600960086007600660056004600360026001e57fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -b14413ae274483b2b02dd1b409a7648069eb31f13cff3689197ecc2e7947104a=60003560008103604657600960086007600660056004600360026001e57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +b14413ae274483b2b02dd1b409a7648069eb31f13cff3689197ecc2e7947104a=6000356000811415604757600960086007600660056004600360026001e57fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 448dee1cdc38e696009657272efa1e2e1381bba0846b564600d465de9fad0723=600960086007600660056004600360026001e67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 2e738888b00adbf0f721a9ada9299e58cbc96558f847d1234d1261932abf02cf=600960086007600660056004600360026001e67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -f7dfd3c51bd9bcb2ca1bdcf7bdc99d9b3de7c6962f409b46b98ad12202db9746=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001e67fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001e67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001e67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001e67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001e67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001e67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001e67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +f7dfd3c51bd9bcb2ca1bdcf7bdc99d9b3de7c6962f409b46b98ad12202db9746=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001e67fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001e67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001e67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001e67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001e67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001e67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001e67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 6bfc8ec98f64c2587317236f1510fbb5355a1a92fa07883db7d3f536de27a638=600960086007600660056004600360026001e67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 cf403ca4444fb4d137e849c9171585d46b87d99ec538275f7e66710e6c1ffc34=600960086007600660056004600360026001e67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 770bd70777d6d9f6da72762abeccffc85c6ab6756e160e02712222e20306deac=600960086007600660056004600360026001e67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -97c18561f3dcd2620892b7275d3db5ffd49143df436c750cf87a8ecbf4bf53fa=60003560008103604657600960086007600660056004600360026001e67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +97c18561f3dcd2620892b7275d3db5ffd49143df436c750cf87a8ecbf4bf53fa=6000356000811415604757600960086007600660056004600360026001e67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 0b24158a59c86d19c78373b88e3161ebcc1c6a0e04c549b49701cd0b0e1c5737=600960086007600660056004600360026001e77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 6485b4eb21ede9bd055ea05483bdaa33254cd92105e2308e9d18fce8338d2433=600960086007600660056004600360026001e77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -ee071cdda683c7ceff8b9b83f1481c2180b9c3f8571cca56b91c95017b1a2116=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001e77fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001e77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001e77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001e77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001e77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001e77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001e77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +ee071cdda683c7ceff8b9b83f1481c2180b9c3f8571cca56b91c95017b1a2116=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001e77fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001e77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001e77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001e77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001e77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001e77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001e77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 4c7afa2c891225aa4c8c986c82073f08539b48e7a57ea81b79296f9eae780e0b=600960086007600660056004600360026001e77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 12ac3f9b1eb1e2413e67bee48e812532cc9eb0173f52da644fc378d5d8a95e0c=600960086007600660056004600360026001e77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 30071e8db62321fc32b490e741b8fc5536f612665632c320144a36b9196a3e77=600960086007600660056004600360026001e77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -02573bc0c5143d90436c8ff66ebc7f621436a6cc40278f9d9460059247811a78=60003560008103604657600960086007600660056004600360026001e77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +02573bc0c5143d90436c8ff66ebc7f621436a6cc40278f9d9460059247811a78=6000356000811415604757600960086007600660056004600360026001e77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 e2378fa0cb856ee48b09c7e8b0db9f52123bae9dabff49eeab4bf212166dbfd7=600960086007600660056004600360026001e87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 f1cd14bae66897b416e5e20ed1b50a21f7e8ee07441324ba8faa2e1bc8428d7a=600960086007600660056004600360026001e87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -4c5d6782a96a8855f216b981439480744a61cb8277cd958f7440dca653b21b33=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001e87fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001e87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001e87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001e87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001e87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001e87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001e87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +4c5d6782a96a8855f216b981439480744a61cb8277cd958f7440dca653b21b33=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001e87fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001e87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001e87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001e87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001e87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001e87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001e87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 741179d363ca301ff2c65d4f13db04d42bdc3bf59ea5dc5e4dac1218d6a98a58=600960086007600660056004600360026001e87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 3c2b6934acde64f6d8588a20304a2ae2a13a6a65316ceb0dd7fb96839707a56b=600960086007600660056004600360026001e87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe be0ebce8b105dbbbbbead7c935065267c5567badd5a816c94191e634bf341887=600960086007600660056004600360026001e87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -1666550ed444b24d7984ecd6191d558e7a52352344ea846b61ef4eb81ee7d194=60003560008103604657600960086007600660056004600360026001e87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +1666550ed444b24d7984ecd6191d558e7a52352344ea846b61ef4eb81ee7d194=6000356000811415604757600960086007600660056004600360026001e87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 fd51cabe84e080eee626fb7ff99e5e5b54254ec8a5ca28417d30e576c4f51b3a=600960086007600660056004600360026001e97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 f52593d3ce08e889d02ca234c7fe21a3222660b4d8eb0d7398963e896dffaf18=600960086007600660056004600360026001e97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -ac2a746cfe8a2238a189ca6bca3710601a315749b5c1e554ece549119658cfe3=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001e97fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001e97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001e97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001e97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001e97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001e97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001e97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +ac2a746cfe8a2238a189ca6bca3710601a315749b5c1e554ece549119658cfe3=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001e97fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001e97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001e97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001e97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001e97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001e97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001e97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 8cba75cd4b78afff97003cd8758eba1775b5b4809b42bfa0d17ccf460f458d09=600960086007600660056004600360026001e97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 83b9da2d97405bb336b9d9e452a1e4cd5b66475bc7cf554a1a0fd76a818e8173=600960086007600660056004600360026001e97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe c3eb6c19c606f2e44d7a1b6446bdb49671b49b6de924274929c866a279cd588c=600960086007600660056004600360026001e97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -508cb9e9c724f7c54026bf7a66bb3f86b6ee58f3d630f5d52949ab17edcfbcb6=60003560008103604657600960086007600660056004600360026001e97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +508cb9e9c724f7c54026bf7a66bb3f86b6ee58f3d630f5d52949ab17edcfbcb6=6000356000811415604757600960086007600660056004600360026001e97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 fc5416739dbe5bc410d5c09a131838d69f2d872a199d8b72076fcb81cb0b0385=600960086007600660056004600360026001ea7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 7c5ccb1941125ae95b92319bfc5075c48bb9ab7be68b080da30616aafa66efb7=600960086007600660056004600360026001ea7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -1f1751da9715f59708a3bc6aedca352318984206f05550f900d15f58caecd3e2=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001ea7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001ea7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001ea7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001ea7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001ea7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001ea7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001ea7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +1f1751da9715f59708a3bc6aedca352318984206f05550f900d15f58caecd3e2=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001ea7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001ea7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001ea7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001ea7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001ea7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001ea7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001ea7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 f675e7d0054ac2a9fd2d4041b18dad38a4dfee33b9548ef9ac3ea7dc213225fd=600960086007600660056004600360026001ea7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 6fc248e5fcf6149b241e47474249f34563d0615515edfdd5f009eb89a65261be=600960086007600660056004600360026001ea7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 2d2de327c12b768da69a87030c7570e2a34fa56bb9b2c992a51018a5930730b7=600960086007600660056004600360026001ea7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -790d409dd506e4483d1c95bdcceb0ffe7d377bd248c1024981e1eb580a074745=60003560008103604657600960086007600660056004600360026001ea7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +790d409dd506e4483d1c95bdcceb0ffe7d377bd248c1024981e1eb580a074745=6000356000811415604757600960086007600660056004600360026001ea7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 2991645d30d0f81333430b4feee3c094a02487997ed2e3c9ffe1aff72ca2e03c=600960086007600660056004600360026001eb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 5ad97103143eb047e9a9165cc1f11b82ecefce78db8858e3e578b9ac9999491e=600960086007600660056004600360026001eb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -749aca47d178d05e3cf31550643c9ea9ec3cfc7ebb3faaaab7999a7a03616d04=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001eb7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001eb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001eb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001eb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001eb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001eb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001eb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +749aca47d178d05e3cf31550643c9ea9ec3cfc7ebb3faaaab7999a7a03616d04=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001eb7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001eb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001eb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001eb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001eb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001eb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001eb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 73cfbd20fb20381794fba4875c1ebe2a19f8847325bdca681a2771b885f961e3=600960086007600660056004600360026001eb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 0c831cd6fac65d3e335439819f6e11e95cf63d5a55580c146ec1557ab788f918=600960086007600660056004600360026001eb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 2487f9d3459e086b2088b8fdabd90d263cf7f2321a596b53464400cacbdce974=600960086007600660056004600360026001eb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -bf43887b8ccffedf3b7ad7612936bacf07c5a3ccc9cb833a50a3762c9eb92edd=60003560008103604657600960086007600660056004600360026001eb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +bf43887b8ccffedf3b7ad7612936bacf07c5a3ccc9cb833a50a3762c9eb92edd=6000356000811415604757600960086007600660056004600360026001eb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 4422cf00319d3c6a5c5e443a8f19a50ef85e8e599ba92fd911afda95f996c533=600960086007600660056004600360026001ec7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 c99c258df1e50fbd0ad72e7b1adce397df49b9325767ed2b9981d8dff580a424=600960086007600660056004600360026001ec7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -dac939bea3b32e80426f9b4c1a549c752872c8ddd8c0cf7d2948daab6136c466=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001ec7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001ec7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001ec7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001ec7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001ec7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001ec7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001ec7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +dac939bea3b32e80426f9b4c1a549c752872c8ddd8c0cf7d2948daab6136c466=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001ec7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001ec7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001ec7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001ec7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001ec7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001ec7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001ec7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 c92b7f7cf758c418b11eee606cc18a463b0090d40792e9df7518915ef1d5e5e4=600960086007600660056004600360026001ec7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 b05952efd7dc44559e180dcd9f8258075ea0bc6b7c358e600ec13b6581919b9c=600960086007600660056004600360026001ec7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe a0f6909df33c00736528b1959c7e34244e7e3043297e09678bdcd7e644929ec9=600960086007600660056004600360026001ec7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -16af98e3251ea8b8e3602e4836e4fabcc6bf77a778c82049807d17b3fdf35a75=60003560008103604657600960086007600660056004600360026001ec7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +16af98e3251ea8b8e3602e4836e4fabcc6bf77a778c82049807d17b3fdf35a75=6000356000811415604757600960086007600660056004600360026001ec7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 9b454404772226a5db6f7f798e465a0dcd47accf73f5d9e5fab44731d6052d51=600960086007600660056004600360026001ed7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 e679d4e17ab1e726c6eb12146eb5dac927307db1e9e5083af9b12cc0fb1f5c70=600960086007600660056004600360026001ed7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -94826e1a45103e831581ac5368ffaa27627b0b466f8dc027e59eae2e2dcdeb22=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001ed7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001ed7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001ed7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001ed7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001ed7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001ed7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001ed7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +94826e1a45103e831581ac5368ffaa27627b0b466f8dc027e59eae2e2dcdeb22=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001ed7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001ed7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001ed7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001ed7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001ed7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001ed7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001ed7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 d557e90865d1307627e76f21aa7298c39cd0f612ea13feac9f7b540cf2df1fcf=600960086007600660056004600360026001ed7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 08877d272eb0c04b7bcf9fdd68f74cbcd1c9e0888aeaba02898ca2ce87bf6503=600960086007600660056004600360026001ed7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe c0259ad40b8922acb3889dac1db324d0c28379ef4d375cbd48a3628359118269=600960086007600660056004600360026001ed7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -fac9b9c7698a12627883df0e362f966cd83abb95aa505594d7faabc40a2b4c41=60003560008103604657600960086007600660056004600360026001ed7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +fac9b9c7698a12627883df0e362f966cd83abb95aa505594d7faabc40a2b4c41=6000356000811415604757600960086007600660056004600360026001ed7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 8a18d4a7a9184f5cf6a5c3e08b77080ccc314000dbe68c5e47be5c0babb8c832=600960086007600660056004600360026001ee7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 200e26eb56e6656f0092ab68376777202054401a66caa6936e805ca5863af2e2=600960086007600660056004600360026001ee7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -5c77cf8e7cd77ecfc8e53ebd4a8801e3463e9f31462faa0f30fae5487b019f54=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001ee7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001ee7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001ee7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001ee7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001ee7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001ee7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001ee7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +5c77cf8e7cd77ecfc8e53ebd4a8801e3463e9f31462faa0f30fae5487b019f54=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001ee7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001ee7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001ee7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001ee7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001ee7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001ee7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001ee7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 1b8526f4a0eb15e80297bc3266c5d47d6dbf8e446f4cc25fc1012324509d29e5=600960086007600660056004600360026001ee7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 86a08195a2cd38e59ed66b1018861c7c253db9be50f0eccd8ce98c1e28fdc9a0=600960086007600660056004600360026001ee7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 0e8d12772c6b9ea24d72b536f7e173abc5ff5690bd03db17bf8a2bc2c18b4856=600960086007600660056004600360026001ee7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -d6afcfd9d082830fc0c0e212c9c6ff9347b63203119217ace3e76f3fcd803075=60003560008103604657600960086007600660056004600360026001ee7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +d6afcfd9d082830fc0c0e212c9c6ff9347b63203119217ace3e76f3fcd803075=6000356000811415604757600960086007600660056004600360026001ee7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 4188def3c908f821e7eeeda474e26783a020985f72f51d460ce6e56a8c047382=600960086007600660056004600360026001ef7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 ea674664bb589f3b0bd3ab158f8b3190fe86ad034e9f3b74cb96db0320111057=600960086007600660056004600360026001ef7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -ab098c91a13fa3ac0d7825c39a892b40b660fc1a26c26692d016d9e556ff7b7d=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001ef7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001ef7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001ef7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001ef7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001ef7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001ef7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001ef7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +ab098c91a13fa3ac0d7825c39a892b40b660fc1a26c26692d016d9e556ff7b7d=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001ef7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001ef7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001ef7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001ef7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001ef7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001ef7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001ef7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 a130730ce2d9bb64aad176f5548267d617949d578cc6d10708f72dd0455a6884=600960086007600660056004600360026001ef7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 528551b0bfdbe14e3d782f97911a7928ec8e37396374037720cafc1818e16a05=600960086007600660056004600360026001ef7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 2c27eda63c29e0eab08260e36aacfadfc6ceb28129cf27818ae300f76a213bfc=600960086007600660056004600360026001ef7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -54823c00c84f087d687ad203696c739d49e1a2b9e1fd265b115c1292011f79fb=60003560008103604657600960086007600660056004600360026001ef7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +54823c00c84f087d687ad203696c739d49e1a2b9e1fd265b115c1292011f79fb=6000356000811415604757600960086007600660056004600360026001ef7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 130e8f17c85252e3e5b4211fe7658c031a3e7928f8f82be4a9cdcf22036d0771=600960086007600660056004600360026001f67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 149217604850d2db398de728ed7e92ac81a736ef24af6b728e3efe48c6f5580e=600960086007600660056004600360026001f67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -e075b85082356d2adac0e23604d8fa75476e55156fa1ff51057d10d809b28275=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001f67fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001f67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001f67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001f67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001f67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001f67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001f67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +e075b85082356d2adac0e23604d8fa75476e55156fa1ff51057d10d809b28275=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001f67fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001f67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001f67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001f67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001f67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001f67fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001f67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 8e06cc3f38e5075be9fa28e8515a93cc044db9ff0d413857ab63090f1c560054=600960086007600660056004600360026001f67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 551460d3811a5057532721ddbe687ae86e68e64195ba9f1a0ba6de1ffa8cb2c3=600960086007600660056004600360026001f67fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 92e1a3010fb46ff1fb92d3d596064909d890d5f07877e143d912df5fe84e3c2f=600960086007600660056004600360026001f67fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -efca004e21c714ff1882b8704340b5ca514f095967aeeb63a1c1eb9b227cc223=60003560008103604657600960086007600660056004600360026001f67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +efca004e21c714ff1882b8704340b5ca514f095967aeeb63a1c1eb9b227cc223=6000356000811415604757600960086007600660056004600360026001f67fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 66626bd408b53255151083f06eda987d818c85248fc091fa5f6f987b1437bce5=600960086007600660056004600360026001f77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 48f7d28dca8a60ecf9c7390e24c109fa9cbac35fc25903c9961df7e0f13162ca=600960086007600660056004600360026001f77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -a0886f328f015a1288ae2fa74ad076ef06b4a54ecba42e77f5b7d4ead12fee3d=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001f77fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001f77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001f77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001f77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001f77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001f77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001f77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +a0886f328f015a1288ae2fa74ad076ef06b4a54ecba42e77f5b7d4ead12fee3d=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001f77fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001f77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001f77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001f77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001f77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001f77fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001f77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 86b2e0b1d3a7abbe8892ece62b8a3b3ac04dac2a42dab2238b4c4db47ecc54a8=600960086007600660056004600360026001f77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 ac9b6e7eab17f4fb2c5ce550d16004ddc1dd585bd8f23f2efadddcb8faf62a32=600960086007600660056004600360026001f77fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe d1a5d7ad17bf02e2368bd1e326414a9d1db70f57d837232f0bc0bbb94474ea8e=600960086007600660056004600360026001f77fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -2fab2f4022d9e68b4c9bde400ec8444f2d0987f4f56e7e3829fecbf79ff680aa=60003560008103604657600960086007600660056004600360026001f77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +2fab2f4022d9e68b4c9bde400ec8444f2d0987f4f56e7e3829fecbf79ff680aa=6000356000811415604757600960086007600660056004600360026001f77fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 ba34d1576787716f1d1351cbcfaa4a3f206d86cc8ba8256eb36fee0e43443a3a=600960086007600660056004600360026001f87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 544f89ca28f35f4a7c4ba6b89918ff27f0173750045d58551fb9c756ad42e4d5=600960086007600660056004600360026001f87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -732386382ebcf1e94fa88c27bc1ca78c34fede07f54b73d84f51036ca80f03a4=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001f87fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001f87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001f87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001f87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001f87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001f87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001f87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +732386382ebcf1e94fa88c27bc1ca78c34fede07f54b73d84f51036ca80f03a4=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001f87fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001f87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001f87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001f87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001f87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001f87fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001f87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 eaac45cae1c91ddc0cea84ecc42109eef7a432ec70bfe0ab310f5b6248c47b64=600960086007600660056004600360026001f87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 7734e4759cf3f82530b97d76f06f0ec43a4ab2894ff0ee4733a6083a295b0894=600960086007600660056004600360026001f87fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 84de9c38859f423edceaaca6adfbd1bba371db979512238ed1990488c1eeaa2d=600960086007600660056004600360026001f87fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -3b66d4f3fe8ce9ff5ea2e29c8829dca24e231020e5cc843e41b6859a475b9fdf=60003560008103604657600960086007600660056004600360026001f87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +3b66d4f3fe8ce9ff5ea2e29c8829dca24e231020e5cc843e41b6859a475b9fdf=6000356000811415604757600960086007600660056004600360026001f87fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 764a4ba331bf7ee3cc40136347fc0bfcbbf53a2c3bc6b96d7db777a4fd8de3c9=600960086007600660056004600360026001f97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 380f19f02df7665b3bca3d4ea3307dd40ace2f8eb5e1e7148f6ab9581ecf2386=600960086007600660056004600360026001f97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -72dcf1e1aa56c92cf038fe889da1b7822ffe1630233703b08b6384bcbf422dac=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001f97fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001f97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001f97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001f97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001f97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001f97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001f97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +72dcf1e1aa56c92cf038fe889da1b7822ffe1630233703b08b6384bcbf422dac=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001f97fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001f97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001f97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001f97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001f97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001f97fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001f97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 e0ab635835c12d40088b1bbba2810127fe547d24828b4f410c124d52365404da=600960086007600660056004600360026001f97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 ed348aeb464344a017bb9cad3309f4d37c5bb3c6b316dfe930334f1df000ab11=600960086007600660056004600360026001f97fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe f71bd4bdb0866e72f6e47a2d83cf288b9f835d95f8f45a194e32e0efada535ca=600960086007600660056004600360026001f97fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -621388d59d36a339074b9d69c476be0f65475dce67601f723a9ba9b79b4fa608=60003560008103604657600960086007600660056004600360026001f97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +621388d59d36a339074b9d69c476be0f65475dce67601f723a9ba9b79b4fa608=6000356000811415604757600960086007600660056004600360026001f97fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 0d8265dabeb6d9eb79896c42ee04c67afff9e48187d5adab68a4763a2a28faa9=600960086007600660056004600360026001fb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 def56e3326695ace527958b3bf1a7976e8ad11b1ef353c7719b74c53bf114f4d=600960086007600660056004600360026001fb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -9c0194fa50a9b8adf02dac6daf1181f8e873e5d5f9a4bc37ec6cb173fe6f84cb=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001fb7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001fb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001fb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001fb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001fb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001fb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001fb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +9c0194fa50a9b8adf02dac6daf1181f8e873e5d5f9a4bc37ec6cb173fe6f84cb=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001fb7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001fb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001fb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001fb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001fb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001fb7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001fb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 c8b6a7bb41353a21c229fdb76b587e903155597eb5d822459d9cbd71799c126e=600960086007600660056004600360026001fb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 3ef1ec27d7ad72b7e812963d31b03d485ce148e671d4924f73b6eb268e9a01cb=600960086007600660056004600360026001fb7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 37c262381218dbef934dd5f7dc2a9f81a6cd63fdacd6e5c53586e2ba83d01746=600960086007600660056004600360026001fb7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -8b057a099f7b4d272da7a5208e6ab9450b9031dbbe3c2d18a90c2a8c59f5a613=60003560008103604657600960086007600660056004600360026001fb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +8b057a099f7b4d272da7a5208e6ab9450b9031dbbe3c2d18a90c2a8c59f5a613=6000356000811415604757600960086007600660056004600360026001fb7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 b087d8d8964016028417a134fa6aaf6f7041883d435986a67edffc817471cb59=600960086007600660056004600360026001fc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 2f2ed98d4ea262d50ad3c7e1400ed6d2462c7a4b05a71aeb0e6e780d132caa00=600960086007600660056004600360026001fc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -7298454d03ba1e1a1cbf32f062e8c19b2026d0c00b91c2c2908597112acb974e=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001fc7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001fc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001fc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001fc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001fc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001fc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001fc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +7298454d03ba1e1a1cbf32f062e8c19b2026d0c00b91c2c2908597112acb974e=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001fc7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001fc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001fc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001fc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001fc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001fc7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001fc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 16513dc959b0a2b4a245e7608771a01e6eaa750ad6fa07a9c17ef6c4b7f09968=600960086007600660056004600360026001fc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 1f0d48fd4212f0451bb29d193812636f7fee3642d8dfab2e9aeef817daeff580=600960086007600660056004600360026001fc7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe a6c29866c12943f819a2dc44a05ffcf8e1fa22a24589a38b14ce05e45f2cba69=600960086007600660056004600360026001fc7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -6e032b46774ee52a28b67cb147af38734a8fcbc4f2b0a84094eb4bededc590b9=60003560008103604657600960086007600660056004600360026001fc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 +6e032b46774ee52a28b67cb147af38734a8fcbc4f2b0a84094eb4bededc590b9=6000356000811415604757600960086007600660056004600360026001fc7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 7004cff6a85e2c30fb2f711e0986d1eddb81a650c26c31f1ed4d5bd802bf93ab=600960086007600660056004600360026001fe7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516040526021603ff3 05f1cb0012e6f176881117d451b9b045f242be5dc419ac3e8c336bcec0be97c5=600960086007600660056004600360026001fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 -564733c13227d18244a0766222bb2a3098c3ba68e51c48dca39b7eef76927911=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101975760f281146101ab5760f481146101bf5760fa81146101d25761f1f181146101e55761f2f181146101fb5761f4f181146102115761faf181146102265761f1f2811461023b5761f2f281146102515761f4f281146102675761faf2811461027c5761f1f481146102915761f2f481146102a75761f4f481146102bd5761faf481146102d25761f1fa81146102e75761f2fa81146102fd5761f4fa81146103135761fafa81146103285760fd811461033d5760fe81146103da5760ff81146104795760f081146105175760f5811461053a5761f0f181146105605761f5f1811461058a5761f0f281146105b75761f5f281146105da5761f0f481146106005761f5f481146106225761f0fa81146106475761f5fa8114610670576460baccfa57811461069c5765bad0bad0bad06000526106b6565b600960086007600660056004600360026001fe7fdead60a7000000000000000000000000000000000000000000000000000000006000526106b6565b602060008060008061ca115af195506106b6565b602060008060008061ca115af295506106b6565b6020600080600061ca115af495506106b6565b6020600080600061ca115afa95506106b6565b602060008060008063ca1100f15af195506106b6565b602060008060008063ca1100f15af295506106b6565b6020600080600063ca1100f15af495506106b6565b6020600080600063ca1100f15afa95506106b6565b602060008060008063ca1100f25af195506106b6565b602060008060008063ca1100f25af295506106b6565b6020600080600063ca1100f25af495506106b6565b6020600080600063ca1100f25afa95506106b6565b602060008060008063ca1100f45af195506106b6565b602060008060008063ca1100f45af295506106b6565b6020600080600063ca1100f45af495506106b6565b6020600080600063ca1100f45afa95506106b6565b602060008060008063ca1100fa5af195506106b6565b602060008060008063ca1100fa5af295506106b6565b6020600080600063ca1100fa5af495506106b6565b6020600080600063ca1100fa5afa95506106b6565b600960086007600660056004600360026001fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560008060008060006260bacc5af150600960086007600660056004600360026001fe7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600054146103d557640badbadbad6000525b6106b6565b600960086007600660056004600360026001fe7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000806000806000620600066161a8f150600960086007600660056004600360026001fe7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461047457640badbadbad6000525b6106b6565b600960086007600660056004600360026001fe7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600080600080600063deaddead5af150600960086007600660056004600360026001fe7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541461051257640badbadbad6000525b6106b6565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106b6565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af195506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af195506106b6565b8160008563c0dec0de3c81846000f094506020600080600080895af295506106b6565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af295506106b6565b8160008563c0dec0de3c81846000f0945060206000806000885af495506106b6565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af495506106b6565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa95506106b6565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa95506106b6565b6103ff6000526020600060206000806460baccfa575af195505b50846106c25760206000fd5b836106cd5760206000fd5b600051600055505050505050 +564733c13227d18244a0766222bb2a3098c3ba68e51c48dca39b7eef76927911=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101985760f281146101ae5760f481146101c45760fa81146101d85761f1f181146101ec5761f2f181146102045761f4f1811461021c5761faf181146102325761f1f281146102485761f2f281146102605761f4f281146102785761faf2811461028e5761f1f481146102a45761f2f481146102bc5761f4f481146102d45761faf481146102ea5761f1fa81146103005761f2fa81146103185761f4fa81146103305761fafa81146103465760fd811461035c5760fe81146103fd5760ff81146104a05760f081146105425760f581146105655761f0f1811461058b5761f5f181146105b75761f0f281146105e65761f5f2811461060b5761f0f481146106335761f5f481146106565761f0fa811461067c5761f5fa81146106a6576460baccfa5781146106d35765bad0bad0bad06000526106ee565b600960086007600660056004600360026001fe7fdead60a7000000000000000000000000000000000000000000000000000000006000526106ee565b6020600060006000600061ca115af195506106ee565b6020600060006000600061ca115af295506106ee565b602060006000600061ca115af495506106ee565b602060006000600061ca115afa95506106ee565b6020600060006000600063ca1100f15af195506106ee565b6020600060006000600063ca1100f15af295506106ee565b602060006000600063ca1100f15af495506106ee565b602060006000600063ca1100f15afa95506106ee565b6020600060006000600063ca1100f25af195506106ee565b6020600060006000600063ca1100f25af295506106ee565b602060006000600063ca1100f25af495506106ee565b602060006000600063ca1100f25afa95506106ee565b6020600060006000600063ca1100f45af195506106ee565b6020600060006000600063ca1100f45af295506106ee565b602060006000600063ca1100f45af495506106ee565b602060006000600063ca1100f45afa95506106ee565b6020600060006000600063ca1100fa5af195506106ee565b6020600060006000600063ca1100fa5af295506106ee565b602060006000600063ca1100fa5af495506106ee565b602060006000600063ca1100fa5afa95506106ee565b600960086007600660056004600360026001fe7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055600060006000600060006260bacc5af150600960086007600660056004600360026001fe7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000541415156103f857640badbadbad6000525b6106ee565b600960086007600660056004600360026001fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560006000600060006000620600066161a8f150600960086007600660056004600360026001fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561049b57640badbadbad6000525b6106ee565b600960086007600660056004600360026001fe7fdead60a7000000000000000000000000000000000000000000000000000000006000526000516000556000600060006000600063deaddead5af150600960086007600660056004600360026001fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005414151561053d57640badbadbad6000525b6106ee565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c6106ee565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c6106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af195506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af195506106ee565b8160008563c0dec0de3c81846000f0945060206000600060006000895af295506106ee565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af295506106ee565b8160008563c0dec0de3c81846000f094506020600060006000885af495506106ee565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af495506106ee565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa95506106ee565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa95506106ee565b6103ff600052602060006020600060006460baccfa575af195505b508415156106fc5760206000fd5b8315156107095760206000fd5b600051600055505050505050 346d6fad6b517c62f2b07bfbb66c2b902a2a033bbe2b4a7bb896b692420452b4=600960086007600660056004600360026001fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f3 294a7c35085c0947a909e2167712861b0fdbe14f9aff6b4e0197507ee784982c=600960086007600660056004600360026001fe7fdead60a700000000000000000000000000000000000000000000000000000000600052600051600055fe 3ff76d99e731d79141b5d3a53a252a8b009785a86e59d86e620799c55ef6b27c=600960086007600660056004600360026001fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260005160005560206000fd -972da93271a848f9a5ee60cc17d73c56ff8db8f01f50a3e0b413f87591e70f89=60003560008103604657600960086007600660056004600360026001fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b600181036000526020600060206000806460baccfa575af160675760206000fd5b60206000f3 -0fe574972cc9c19750b1f10dd6f2d9bdc5b92b04935875b2716fc6a456d18934=610200600080f0600055 -cd9646219770ab10d99b25d29215d8ef36dab77db51b67cf814e6a6dd3e2f560=615a17610200600080f5600055 +972da93271a848f9a5ee60cc17d73c56ff8db8f01f50a3e0b413f87591e70f89=6000356000811415604757600960086007600660056004600360026001fe7fdead60a70000000000000000000000000000000000000000000000000000000060005260206000f35b60018103600052602060006020600060006460baccfa575af11515606b5760206000fd5b60206000f350 +0fe574972cc9c19750b1f10dd6f2d9bdc5b92b04935875b2716fc6a456d18934=61020060006000f0600055 +cd9646219770ab10d99b25d29215d8ef36dab77db51b67cf814e6a6dd3e2f560=615a1761020060006000f5600055 a8b8864fb681a19ccd2c165422f515b7f880ebe72c9e7763e395e54ef9520d2c=63deadbeef6000526101006000f3 74abd0b6773d7e56910bffd51015a816f1be85889f54357e0b94cc8da40fccc9=61beef5150 813aeb718ccb8cccdd9ff70ff394092a1012f072db8fca7637732cf38e31888d=60ff61beef52 6f4cf8175e4d21d167dfe5839411e2a9291a3d57060ebfdc79c640898daa74df=60ff61beef53 -c768c173e76aa9afafc358f1faa7a441e93dc31af52f01496d7743b21d8d9bf0=60243560443560043562c0de000160005b60008103602d5760008060008060008689f1905082840193506010565b82840360005550505050 -850489727f49e4d0fbf69f704aa27e6ae870c65eeeecaf1ee989968fa94168e5=60005b6101008110156030578060981b600080600080600085612710f115602557600182555b506001810190506002565b50600161010055 +c768c173e76aa9afafc358f1faa7a441e93dc31af52f01496d7743b21d8d9bf0=60243560443560043562c0de000160005b6000811415603157600060006000600060008689f1905082840193505b6010565b82840360005550505050 +850489727f49e4d0fbf69f704aa27e6ae870c65eeeecaf1ee989968fa94168e5=60005b6101008110156033578060981b6000600060006000600085612710f115602757600182555b505b6001810190506002565b50600161010055 6928d085b75273e089cb60e6279c2e5258b5de474c5c739efcda1d2f9e82ea5b=600060006000600060003562011170fa60015500 5384e137990f962e727fc9208c6ddae5eebed3c50907b8580f1cb88ee887d156=6001600152600160016001f060025500 3e3ca66c03e0afded22a1454fa2e937382c62708046cbdb213e599ccd49d0c08=60406000604060006000731000000000000000000000000000000000000001611770f100 @@ -1223,35 +1223,35 @@ bb647378cad07f6e87fe40bb0fe78701a9e3661a5362fa59bda7c3957db6cfb6=604060006040600 a3ca55d78a4ac1b3d171e8d3174126d734a02985e10c202967d8ced5b26adfce=6040600060406000600173100000000000000000000000000000000000000162055730f160005500 5bd15877907e4de9273ea001e7f4a96dddbe0510b3342aafecd9bdf97edeec92=604060006040600060027310000000000000000000000000000000000000026203d090f160015500 88456889c166b83101ad56e52b749cee36c47f971234d3bb2192f65909f7d3ab=600160025533600455346007553060e6553260e8553660ec553860ee553a60f05500 -8e3b63e1835877f5c7e14ae041df5e55c3e1c58425c48ee09d567c25afd345c5=6001600255622fffff60002000 -12c5b66cab03697243c3c8394e393d9ada028aa2f939a29893dd29132cb251ab=60406000604060006000731000000000000000000000000000000000000001620249f0f160005500 8ed43c0dd7c55dd8e69ca219d4e46f648a1a8822c54a706dc6827a2df2b12d26=60406000604060006000731000000000000000000000000000000000000002614e34f16001556001600b5500 -14b4e2df05d9e705d07cbe82e401ae12f456777e33684246ba685a82b999db2f=60406000604060006014731000000000000000000000000000000000000001620c3500f160005500 +12c5b66cab03697243c3c8394e393d9ada028aa2f939a29893dd29132cb251ab=60406000604060006000731000000000000000000000000000000000000001620249f0f160005500 +8e3b63e1835877f5c7e14ae041df5e55c3e1c58425c48ee09d567c25afd345c5=6001600255622fffff60002000 b77a8cb081a384fefe0b039719a385c5e7826d5fdb3503cda50dcb1169f029ed=6040600060406000600a731000000000000000000000000000000000000002620927c0f16001556001600b5500 -0e02fd4a9d353d6f8b41c307719a12a96d40df3a8017deb600b3be18a9815da1=6040600060406000600073100000000000000000000000000000000000000261c350f1600155731000000000000000000000000000000000000000ff00 +14b4e2df05d9e705d07cbe82e401ae12f456777e33684246ba685a82b999db2f=60406000604060006014731000000000000000000000000000000000000001620c3500f160005500 f07eabab66fdff9b2221467aa50e4adb2fa9752be1569992c09b58afc9186fde=600160025500 -cb98cd24b0233d3b9df91efc03eb40eb74fa5fda19002137f3c3b2fd828e069c=60406000604060006002731000000000000000000000000000000000000002620493e0f160015500 -cd28eb7e8e982cc57daf0ebef75bb87c5a1db046d0d0807ebd05bde76a3e12da=604060006040600060037310000000000000000000000000000000000000036203d090f160025500 +0e02fd4a9d353d6f8b41c307719a12a96d40df3a8017deb600b3be18a9815da1=6040600060406000600073100000000000000000000000000000000000000261c350f1600155731000000000000000000000000000000000000000ff00 7813f04a6d80c2325ce2651c237631d8e8a4afc30418ed83cdcaf7e6579969d2=600160035533600455346007553061014a553261014c55366101505538610152553a6101545500 -8b918c7081972ad7b1410972e6a04b99afeda6e9b41f30b5e7b5e360f2196b6e=6040600060406000600073100000000000000000000000000000000000000362061a80f16002556001600b5500 +cd28eb7e8e982cc57daf0ebef75bb87c5a1db046d0d0807ebd05bde76a3e12da=604060006040600060037310000000000000000000000000000000000000036203d090f160025500 +cb98cd24b0233d3b9df91efc03eb40eb74fa5fda19002137f3c3b2fd828e069c=60406000604060006002731000000000000000000000000000000000000002620493e0f160015500 +3577c21f368308f65d37ab7fd52ca073e89f8de1acbed04abac4b9f3d6093e4f=60406000604060006000731000000000000000000000000000000000000002620927c0f160015500 f2cdbcd1dc78c4f263d22b40356f172af8a68747e742ef5412a84aa63f620353=60406000604060006000731000000000000000000000000000000000000001620c3500f160005500 +8b918c7081972ad7b1410972e6a04b99afeda6e9b41f30b5e7b5e360f2196b6e=6040600060406000600073100000000000000000000000000000000000000362061a80f16002556001600b5500 3c8f41896e16cadb4206f2d760dd083207e2735bc8e9e240aa41a2cc6163af37=6001600355622fffff60002000 -3577c21f368308f65d37ab7fd52ca073e89f8de1acbed04abac4b9f3d6093e4f=60406000604060006000731000000000000000000000000000000000000002620927c0f160015500 +f5c742a5ab1925c82083c4dee955d0c814e53920968aa92b59b0c8d1fcdeb360=60406000604060006000731000000000000000000000000000000000000002620927c0f1600155622fffff60002000 c954d7752af9bb6b359ec69b4f1b7b5d8496b1b1c7a5e5eb1bf0e5ce1b08a0ad=6040600060406000600073100000000000000000000000000000000000000362061a80f160025500 c43df18b56f764adfe86e2d2a48e33b927d48bed4522c2aabe28115c550e4838=60406000604060006000731000000000000000000000000000000000000001620c3500f16000556001600b5500 -f5c742a5ab1925c82083c4dee955d0c814e53920968aa92b59b0c8d1fcdeb360=60406000604060006000731000000000000000000000000000000000000002620927c0f1600155622fffff60002000 ce2a353f90f2d01731989db0ecffe7d87aaf7a4fef740d3450d85d46e9c2c6f5=600160035500 -79e1d0c4625fa2858058c76337db79abc512f3eee48e7350ddcc566944b59240=622fffff600020506040600060406000600073100000000000000000000000000000000000000362061a80f160025500 7bccf87f855ed3aa795c0985150e4c4f002eb570aa34a94ab98e7136a194e4c4=60406000604060006000731000000000000000000000000000000000000002620927c0f16001556001600b5500 +79e1d0c4625fa2858058c76337db79abc512f3eee48e7350ddcc566944b59240=622fffff600020506040600060406000600073100000000000000000000000000000000000000362061a80f160025500 67b61ae1d5635fbd4412ec9b2c60a1418a290151e86ab16c497ec6acd8a79a9a=6040600060406000600073100000000000000000000000000000000000000361c350f1600255731000000000000000000000000000000000000001ff00 d9062174eb6c02dc75092567cdb85a07d131f321f6233583c1aca93c7ea8b771=600160035500 c4b27aecda668677fbf62f8e3015c4456eb92e4d6cb80501135c8e8b5efafd62=60406000604060006000731000000000000000000000000000000000000002620186a0f160015500 1a243e537a1f1194ca38645e48c62d8772c1e0f3d18d80e49ef32b6704765a89=731000000000000000000000000000000000000000ff6040600060406000600073100000000000000000000000000000000000000361c350f160025500 75b5d0815b5a203505379176aa93a9b92f4991c54ec9bcb3a920c822926a4b9d=6040600060406000600073100000000000000000000000000000000000000163017d7840f160005500 -f72666efbda95799975b0e9fb2cd7271b921758f61f87fb96f8ecb79498b074c=604060006040600060007310000000000000000000000000000000000000016207a120f160025500 e31f2dd3719ab6b5665d781a415fb9676b6d8564f72b34cbe2e62a14f12fced6=60406000604060006000731000000000000000000000000000000000000002620f4240f160015500 -f8b7a672693c1238a852f75319baa0ebddcbfb7f1ed66a9be6df5eb9b4e64ab0=604060006040600060037310000000000000000000000000000000000000036203d090f260025500 +f72666efbda95799975b0e9fb2cd7271b921758f61f87fb96f8ecb79498b074c=604060006040600060007310000000000000000000000000000000000000016207a120f160025500 b51f7a5fde1b616abd9108bd69188022c9a3a8ecdb2cc1b10fbe56c2de4a547b=600160035533600455346007553061014a553261014c55366101505538610152553a6101545500 +f8b7a672693c1238a852f75319baa0ebddcbfb7f1ed66a9be6df5eb9b4e64ab0=604060006040600060037310000000000000000000000000000000000000036203d090f260025500 aab9b0b3c96ec228ad35203f938704da71856552d8ccd462c32b6763888eff32=6040600060406000600073100000000000000000000000000000000000000362061a80f26002556001600b5500 582f7d4a96ae68d119e55d4fdc233b684112f32b6fa159d3351c2956441d4505=6040600060406000600073100000000000000000000000000000000000000362061a80f260025500 f569a9205c64a5ad90f7dd19e0512767f3f6478684369fe213b0a061ed0a5195=60406000604060006000731000000000000000000000000000000000000001620c3500f16000556001600b5500 @@ -1259,13 +1259,13 @@ c2cb4e1d0e625267431eacd607318205bf668542f75b3abe0e905b890626bb58=622fffff6000205 67e0d65e01b31402a90c27c2af74793dfe4bbdfa25f98618697cdec5b204f0d0=6040600060406000600073100000000000000000000000000000000000000361c350f2600255731000000000000000000000000000000000000001ff00 074806ed0f72c17fd378df530017b5188388d55a86ccb184acad1e1e78a6445a=731000000000000000000000000000000000000000ff6040600060406000600073100000000000000000000000000000000000000361c350f260025500 afd2261368d666cf7edd2b4d148257839c388eef0ac93bae452fd120fe65bac5=604060006040600060007310000000000000000000000000000000000000016207a120f260025500 -46357373e108cf8881d0cf14866ef5217ab6247fc4c2d7c833c348bc2d4d3a1f=600160025533600455346007553060e6553260e8553660ec553860ee553a60f05500 cbc8787a9c2f2999deac8496bc4c0fce7586384b86a424e3db005b59887af28e=604060006040600060027310000000000000000000000000000000000000026203d090f260015500 +46357373e108cf8881d0cf14866ef5217ab6247fc4c2d7c833c348bc2d4d3a1f=600160025533600455346007553060e6553260e8553660ec553860ee553a60f05500 8fdee024b6e02a1555f973f0696ac1f8c3cd356288ad1a8e1610e1b882911fc6=60406000604060006000731000000000000000000000000000000000000002620927c0f26001556001600b5500 3b0b8cc8d5b61e73b2a4205b62239de5b6a4ecd1167b14931702c464f7cde9c0=6040600060406000600073100000000000000000000000000000000000000261c350f2600155731000000000000000000000000000000000000000ff00 10aa98a2c518f4fedfc8d64915d777c8f8d3a67766355befca05f91d48b37df1=60406000604060006002731000000000000000000000000000000000000002620493e0f260015500 -58836a1be02c5e300cf41ab746d3d83083d734a3e2258425897bc248a78aa49d=6040600060406000600073100000000000000000000000000000000000000362061a80f16002556001600b5500 5fa60e5a81aa2b8ad7c733a64c9c49b86c5c9b525bbf8115a44321b4e4a53051=60406000604060006000731000000000000000000000000000000000000002620927c0f260015500 +58836a1be02c5e300cf41ab746d3d83083d734a3e2258425897bc248a78aa49d=6040600060406000600073100000000000000000000000000000000000000362061a80f16002556001600b5500 0efc3763ee86b2084d202698b0e7f67965a1d9c699ca5a473e1b74eaff702d17=60406000604060006000731000000000000000000000000000000000000002620927c0f2600155622fffff60002000 dc572586bef4a8aaeddeca7bdb4a2fb22aea11e73c3a0c8257922ad35da9ba48=60406000604060006000731000000000000000000000000000000000000002620186a0f260015500 5eeff94fbf1224f83abf924923d427f2c5117e826dc41d97eb722afcb98d7861=731000000000000000000000000000000000000000ff6040600060406000600073100000000000000000000000000000000000000361c350f160025500 @@ -1275,24 +1275,24 @@ f73554dec3a16f0e74e40e1024a0ef5ec5d54e5031b36135bc5a29dd18a3fe9e=604060006040600 5c4c27068d91a4bc0a509b062587e32c76d219f8c6383de4618a5083a54b9b5a=622fffff600020506040600060406000600073100000000000000000000000000000000000000362061a80f260025500 046362e811154a462291783b0bce2d041eebd98be82f44a641ee7841b0149a06=731000000000000000000000000000000000000000ff6040600060406000600073100000000000000000000000000000000000000361c350f260025500 5c4e4211d31ab5f57365c6e4c7984034fd67bba3f8fdc2d8b619ad44f84a6785=604680602760003960006000f0600a5560406000604060006000600a54620186a0f2600b5500fe604060006040600060007313136008b64ff592819b2fa6d43f2835c452020e620186a0f2607a5560128060346000396000f300fe600160005530601455326015553360165500 -ffb08aeffbd9b520533cfc3c107a95d2a60ae17609070190257a7cc3c3a3162c=7f604060006040600060007313136008b64ff592819b2fa6d43f2835c452020e626000527f0186a0f2600b5533600c55000000000000000000000000000000000000000000602052604060006001f000 0781c07e53700dd3d72727c8b2debce4508ab04080c0f18a391d94ee9a6354ad=60006000600060006000600035620c3500f100 -e8b1ae01b050389d43a28f95b9772586231d88a54f96a951174feb63ed78d6e5=6000601f80602960003960006000f5600a5560406000604060006000600a54620186a0f2600b5500fe601280600d6000396000f300fe600160005530601455326015553360165500 -a1600b2bec3bcf1f10142fd2693965b831337028896de7a65b0ec5e3c1b05485=601f80602760003960006000f0600a5560406000604060006000600a54620186a0f2600b5500fe601280600d6000396000f300fe600160005530601455326015553360165500 -9317b5ce007832961b43b36beec35b6eb98061807bb791ba6a57599a48c50422=604680600f60003960006000f000fe601f80602760003960006000f0600a5560406000604060006000600a54620186a0f2600b5500fe601280600d6000396000f300fe600160005530601455326015553360165500 +ffb08aeffbd9b520533cfc3c107a95d2a60ae17609070190257a7cc3c3a3162c=7f604060006040600060007313136008b64ff592819b2fa6d43f2835c452020e626000527f0186a0f2600b5533600c55000000000000000000000000000000000000000000602052604060006001f000 b632e6ffcaeaba9e194657cee1302cad3b6f20080074d6b676eec1c3ce52830c=604880600f60003960006000f000fe6000601f80602960003960006000f5600a5560406000604060006000600a54620186a0f2600b5500fe601280600d6000396000f300fe600160005530601455326015553360165500 +9317b5ce007832961b43b36beec35b6eb98061807bb791ba6a57599a48c50422=604680600f60003960006000f000fe601f80602760003960006000f0600a5560406000604060006000600a54620186a0f2600b5500fe601280600d6000396000f300fe600160005530601455326015553360165500 +a1600b2bec3bcf1f10142fd2693965b831337028896de7a65b0ec5e3c1b05485=601f80602760003960006000f0600a5560406000604060006000600a54620186a0f2600b5500fe601280600d6000396000f300fe600160005530601455326015553360165500 +e8b1ae01b050389d43a28f95b9772586231d88a54f96a951174feb63ed78d6e5=6000601f80602960003960006000f5600a5560406000604060006000600a54620186a0f2600b5500fe601280600d6000396000f300fe600160005530601455326015553360165500 17a17bdfd8ed20c2d05b070e39b9860be846caecdb1a6c1593d5e4de63d001a2=60406000604060006103e873945304eb96065b2a98b57a48a06ae28d285a71b561c350f260005500 -61100d03e6bc8e21007b967394c28070e1a33b8251653d33f33bc844f795e18c=602d80600f60003960006000f000fe600060006000600060017310000000000000000000000000000000000000016207a120f2600155600160025500 -9ac432d14151579f379e6c6ef6603dba2c74f9ca0b72aa1ddd8ab6b447e588b0=60006000600060006000600035620493e0f100 b43dc61de74fe6a4953f2906b071ec1438c398373f3a9698f1fe18aa0978264a=6000602d80601160003960006000f500fe600060006000600060017310000000000000000000000000000000000000016207a120f2600155600160025500 -d4cc8a95f1e80db2ba9a385019262bab72dad70d36552b7ce4eeb2af6a255c7f=600160025500 -3e66e2480ca9e491f8eda2b3fea9454f246a77f7230c37b0f00ec43bac018405=6000602880601160003960006000f500fe600060006000600060017310000000000000000000000000000000000000016207a120f260015500 +9ac432d14151579f379e6c6ef6603dba2c74f9ca0b72aa1ddd8ab6b447e588b0=60006000600060006000600035620493e0f100 +61100d03e6bc8e21007b967394c28070e1a33b8251653d33f33bc844f795e18c=602d80600f60003960006000f000fe600060006000600060017310000000000000000000000000000000000000016207a120f2600155600160025500 9d0234cabda87e4cee52a8399e41e5d14a66ea152f45ff4cbc3f28b0ed90afc7=602880600f60003960006000f000fe600060006000600060017310000000000000000000000000000000000000016207a120f260015500 +3e66e2480ca9e491f8eda2b3fea9454f246a77f7230c37b0f00ec43bac018405=6000602880601160003960006000f500fe600060006000600060017310000000000000000000000000000000000000016207a120f260015500 +d4cc8a95f1e80db2ba9a385019262bab72dad70d36552b7ce4eeb2af6a255c7f=600160025500 c26c1be026f10f7967f44e293e60a5f0792a00930b69ea74b536e6a95f46817a=6000602780601160003960006001f500fe6000600060006000600173100000000000000000000000000000000000000161c350f260015500 3055ea3fb0023a093dc69ca6f68d42a1bf1f4aeb9e72345bc724cc1fde2f99d3=602780600f60003960006001f000fe6000600060006000600173100000000000000000000000000000000000000161c350f260015500 b574c885f9a99c244236f9e0cff1c5f8a6384b7a5a12e193dd4c2470779b7801=7f6040600060406000600573945304eb96065b2a98b57a48a06ae28d285a71b5626000527f0186a0f260005500000000000000000000000000000000000000000000000000602052604060006005f000 -c1fb2cafa472d25b2e701f98e55b581704134654e4acb7b37071cc4dee227d02=600160005500 d8afdb839931d006b01476af545bf36e997c21212cd1080ec45b76fb7a401349=60406000604060006000731000000000000000000000000000000000000001620f4240f1505860035500 +c1fb2cafa472d25b2e701f98e55b581704134654e4acb7b37071cc4dee227d02=600160005500 f5aa71ba0105c50bf66641dcc2b61db2716a975af14270bfd4c1d68caeb95878=6040600060406000600173100000000000000000000000000000000000000162055730f260005500 b49f13774260617e36de55306ef58d53dbb960c90296f87df1945e7f12290af4=60406000604060006000731000000000000000000000000000000000000001620c3500f260005500 8aae40196db54fe83f59c904358d7f2652a28e9d91956a345078f6a9dad2a812=60406000604060006000731000000000000000000000000000000000000001620249f0f260005500 @@ -1321,8 +1321,8 @@ edb1554eb88f7baca78e288b2899cf55561b38fbf961e7c307911564f1511a52=7f5e20a0453cecd ba13a823bdd4269bd02e87eeeaf8af71c03171c5e51ef3080cc3ab5818ddbfb4=7f5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6600052600a600060006000600073aaae7baea6a6c7c4c2dfeb977efac326af552d87620249f0f15060005160005500 9666a182ca2a743f281ec8466180532d04b0262e95b4d2c58d998728ee2a36bd=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa602052600260006040600068056bc75e2d6310000073945304eb96065b2a98b57a48a06ae28d285a71b56bfffffffffffffffffffffffff160005560005160015500 51450a8f66a7c8770c21f5d79b35fe3c98091e94540a41f80b3a087aecd9f20b=6000600060006000620186a173945304eb96065b2a98b57a48a06ae28d285a71b5622dc6c1f160005500 -d17813a18f145ef8682b4c40342acfb5287063595493be309cc9984189443a8b=600160025500 33251d4eb0ed54b567a8771e520308d222c42ca134974159a2ae5c45845c1dc6=6002600060406000670de0b6b3a764000173945304eb96065b2a98b57a48a06ae28d285a71b5620249f0f160005500 +d17813a18f145ef8682b4c40342acfb5287063595493be309cc9984189443a8b=600160025500 b6907b94d070d2947662ab7e7598594c4b4bb454df640bb9930390e22a653d99=60016000600060006000670de0b6b3a764000073945304eb96065b2a98b57a48a06ae28d285a71b5612710f10160005500 4bcdd059d6818774f0d404da5955423dade2461920da3e71eb4bc9e598c0fefc=7f5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b66000526000600060006000600073aaae7baea6a6c7c4c2dfeb977efac326af552d87620249f0f25060005160005500 6a37834b53b708e0e94116d0ce4e0350aa7355254971dfae93d3855825528782=7f5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b66000526000600060206000600073aaae7baea6a6c7c4c2dfeb977efac326af552d8761c350f25060005160005500 @@ -1340,15 +1340,15 @@ ec694004735c0b522f8a9f6fd5180a9c2494247b555610be80fdc4d75b2500ac=605660005360016 6e11f3e395588cc86b63e3c2b2516f87b48c6bdb06b08687133485406879f9f4=7f6103ff6000525b7f0102030405060708090a0102030405060708090a010203046000527f05060708090a0102600160005103600052600051600657000000000000000000602052604060006001f0ff00 c137a7179c74a03d14fd503f963426b55c96d8bf1cbdc9ad3d1a36cf802d0f24=6001600053600160006001f0ff00 a32a49add76b1a0489253be706b5e8dab6df23601f8b163137f9357c99706ea7=60f4600053600160006001f0ff00 +84ce02ddd7aaf18251de022973d16da57a66afb8540bc8ac823d150f61f04580=60f9600053600160006001f0ff00 aee27f8ef4805828008cb931a22fcfdc7b29f5fa6cb059fafbde54ee908bffe3=6000600060006000600073100000000000000000000000000000000000000062061a80f16000556000600060006000600073200000000000000000000000000000000000000062061a80f1600155600160025500 746af2369d7b396e4bd872828720e3ee44a32ddc257a721d6b9fa6d15674d952=60f96000536000600160006001f5ff00 -84ce02ddd7aaf18251de022973d16da57a66afb8540bc8ac823d150f61f04580=60f9600053600160006001f0ff00 f2be3b0fb39e23bdb60c9755a02f1abd00f5dbbf3e9a9ecf28b723de274ea981=600d80600f60003960006001f000fe6001600155622fffff60002000 3cc19bd67be0e03b590c3914a7888fc7bfe26f68890764c19dcc5446a5fa0e23=605a600053600160006001f0ff00 f49c3ea625a65ef58ed794f61b60b0ae0b0cb7350dcea10506dca6a7046603fd=7f6001600155601080600c6000396000f3006000355415600957005b602035600060005260356020536055602153602260006017f000 240cceee43253699aea20e3e83707e9cd84da2a18e53d9b801886906fac74b24=7c601080600c6000396000f3006000355415600957005b60203560003555600052601d6003670de0b6b3a7640001f060005500 -398cd6796959872c403ed50ad5d3dbebe0a99c5e0fc2842540e7afdc024bb2a2=600160035533600455346005553061014a553261014c55366101505538610152553a6101545500 51d10086c257a98329b0add6e29cb76177d582c784780c9bc85d46094a267d2b=60406000604060007310000000000000000000000000000000000000036203d090f460025500 +398cd6796959872c403ed50ad5d3dbebe0a99c5e0fc2842540e7afdc024bb2a2=600160035533600455346005553061014a553261014c55366101505538610152553a6101545500 1af2def18495e5fa4ad47ed64ba29d322587f9ae8ae842697c3f9eb782943812=604060006040600073100000000000000000000000000000000000000362061a80f46002556001600b5500 f29f2b9f024abf0877bf09b16d0f902378512cdb21351f2cafdb3221b8e98355=60406000604060006000731000000000000000000000000000000000000001620c3500f26000556001600b5500 02a99c1e93cd900f2936fcc2847d8104318e36af498a1b45d2552faf8be77064=604060006040600073100000000000000000000000000000000000000362061a80f460025500 @@ -1360,9 +1360,9 @@ c0f7ed0d8e02ed57c41436dab38b36ad26350dba91ca4f37c58ea8437ed57dc7=604060006040600 cf702121818e2231130dd688ad9f774dc418a4cb117b62958855ef7b71a217e3=600160025533600455346005553060e6553260e8553660ec553860ee553a60f05500 159206c712451985f130b9eca80a8383d0011c8de790db7be4ce55e5d1bda864=6040600060406000731000000000000000000000000000000000000002620927c0f46001556001600b5500 c35f50164dd0b05b29e015b62358a090ea77f334fe41aa8f97da6034f9e8c305=604060006040600073100000000000000000000000000000000000000261c350f4600155731000000000000000000000000000000000000000ff00 -88eab4bd2174b26ebbf11ef85aff5de1c77e79292004dee2ea85e6a00d566c7a=6040600060406000731000000000000000000000000000000000000002620493e0f460015500 -2ad9bcd6417058afe68fdaa03098e2b5acf03dc1fca6c59b2a737a8ed698059b=604060006040600060027310000000000000000000000000000000000000036203d090f26002553360055500 d2095106e49e36d21f7392fedf4690153d3ebda1654e56ffa1a4fc2201abc429=600160035533600455346006553061014a553261014c55366101505538610152553a6101545500 +2ad9bcd6417058afe68fdaa03098e2b5acf03dc1fca6c59b2a737a8ed698059b=604060006040600060027310000000000000000000000000000000000000036203d090f26002553360055500 +88eab4bd2174b26ebbf11ef85aff5de1c77e79292004dee2ea85e6a00d566c7a=6040600060406000731000000000000000000000000000000000000002620493e0f460015500 d7d9ebb58870cae24949ef5e9077cb1d7f4f86b93fc7e76370c2c0855c158156=6040600060406000731000000000000000000000000000000000000002620927c0f460015500 3a0c79acef60d052ce3524576eec73bb49573b7b0d96dc31811a4dbecbf8727a=6040600060406000731000000000000000000000000000000000000002620927c0f4600155622fffff60002000 47b71b42a05f88499a250e5d3d0cc183b251591d277615c440f59f24d2354d5c=6040600060406000731000000000000000000000000000000000000002620186a0f460015500 @@ -1375,9 +1375,9 @@ b512dd9e6d8c4407956a32fb9b294ffe741000025a2a30b6d95443ec4d794fba=731000000000000 f80639dcae440a5f3755817c73200108d10346d1b2a1ec462d3c64baf147be39=604060006040600073100000000000000000000000000000000000000162055730f460005500 104c137733c8008766e33f46ed1ca976a471001b1acd32fb8ae21c0d6a2b3966=6040600060406000731000000000000000000000000000000000000001620c3500f460005500 a0d6cac9b13007bde698284e9d71776d04aeb0e83af14f6471178303416e13c7=6040600060406000731000000000000000000000000000000000000001620249f0f460005500 -d20b8c7aac7af042709438df0c55c9eb4a0c76ed138feebad265a24d4dd108a9=604060006040600060027310000000000000000000000000000000000000036203d090f260025500 3a7e5951bd41fe60f89934b73f2f9feacd129886a864bebaa310f008cd6ba7c1=60406000604060006001731000000000000000000000000000000000000002620493e0f26001553360055500 57254510d94ba0bb062a4c74cfbcac305796ebb501a9e8a0eaeb0d1e8b974532=600160035533600455346006553061014a553261014c55366101505538610152553a6101545500 +d20b8c7aac7af042709438df0c55c9eb4a0c76ed138feebad265a24d4dd108a9=604060006040600060027310000000000000000000000000000000000000036203d090f260025500 0d33be7d29a18dfbea4bb5ed93a8c5ab3e109aaa0f7cbaf62a823063be027318=6001600355622fffff60002000 05d1fdcfa91cc161f56f0552e0ef4f4dfed7c9d8d4ccb4f56c91ef3858704d7a=6040600060406000731000000000000000000000000000000000000001620c3500f46000556001600b5500 cbc141ccec0aab609b060af8b39c0d2895341cf79d14d369737d5922297aa13c=604060006040600073100000000000000000000000000000000000000163017d7840f460005500 @@ -1395,10 +1395,10 @@ a0d25ad83bce8d5ccc1266a7390006c141f50d55dbfa58ccaf97c9279f5f3f95=604060006040600 a5f9917621aa3c9aa7a9960bc14036a51d86172218f77e1a67eea6c8fffb02a8=6040600060406000731000000000000000000000000000000000000002620927c0f46001556001600b5500 4d64baf66cc6d9d0dd3475fd3615b39a15308a74f19d1a2850cba310a3397aa9=604060006040600060027310000000000000000000000000000000000000036203d090f16002553360055500 7393600193654230e2045d30a614f74a499650cc202a7439cdaf0db78f67b701=6040600060406000731000000000000000000000000000000000000002620927c0f46001556001600b5500 -1edb4454c5c39aa6e23260046157dba37e2b48f7a37cd1fa2f5c3768ac16f235=600160025533600455346007553060e6553260e8553660ec553860ee553a60f05500 2a8c48db3cd48b654c4826fb06db713f48123aac882693cc5461d82fabe81ee7=604060006040600060017310000000000000000000000000000000000000026203d090f160015500 -4a30d44b30c21f2b3213ebc5549ee0253178486a874ba9c9afdc3f999c1ca120=604060006040600060027310000000000000000000000000000000000000036203d090f160025500 +1edb4454c5c39aa6e23260046157dba37e2b48f7a37cd1fa2f5c3768ac16f235=600160025533600455346007553060e6553260e8553660ec553860ee553a60f05500 e4d99bb09a1622904a65f60c72b1c9ef57ee9e92738b91977d4bfaa645ee07f8=60406000604060006001731000000000000000000000000000000000000002620493e0f16001553360055500 +4a30d44b30c21f2b3213ebc5549ee0253178486a874ba9c9afdc3f999c1ca120=604060006040600060027310000000000000000000000000000000000000036203d090f160025500 832b3f9c132356394daddb9475fa7914249bf41fa567c1e91835050db6a7f2b0=60406000604060006001731000000000000000000000000000000000000002620493e0f16001553360055500 937af60a3bbebb327cccf02e219a1c9e91a927555238869c6e41f5cb8c275fd7=622fffff60002050604060006040600073100000000000000000000000000000000000000362061a80f460025500 4ba903617e999dc3428cdb258fecfdf2b29dcc9c8b42f173da4f1957fb3df070=604060006040600060017310000000000000000000000000000000000000036203d090f16002553360065500 @@ -1406,21 +1406,21 @@ e4d99bb09a1622904a65f60c72b1c9ef57ee9e92738b91977d4bfaa645ee07f8=604060006040600 d6f6c2b3d15ea8de99213966d762fb488bd20a2c536de50bd5add1d342b25960=61123460205260406000600073eeef5374fce5edbc8e2a8697c15331677e6ebf0b3c60005160005560205160015561567860605260406000604073a94f5374fce5edbc8e2a8697c15331677e6ebf0b3c60405160025560605160035500 8a4cd2fde1ab95e5f6b7f1d175682bf9393eec6878449795d82777be9d1b7673=6002600a600173bbbf5374fce5edbc8e2a8697c15331677e6ebf0b3c6000516002556002600a600173cccf5374fce5edbc8e2a8697c15331677e6ebf0b3c6000516003556002600a600173dddf5374fce5edbc8e2a8697c15331677e6ebf0b3c6000516004556002600a600173eeef5374fce5edbc8e2a8697c15331677e6ebf0b3c60005160055560c8600a600173eeef5374fce5edbc8e2a8697c15331677e6ebf0b3c60005160065500 a26bb885494e0b85f27e61d59f8f645aeaea0a42b03b4d6f2c9e327989f55816=38600155303b60025500 -43a2044718bbc63caf58b8787e580d53758357db8f9a1dd83f6dec4fd5ebda3a=600035600052600036600080f560005560018055 +43a2044718bbc63caf58b8787e580d53758357db8f9a1dd83f6dec4fd5ebda3a=60003560005260003660006000f56000556001600155 b3ae2ca19746076634f6ce2073cefb64cdc7ae3dd306639840eebdb9ea882f00=6160006000f3 c44968378b56fe928db69677e9f50d18fdf6ae558b6afa25ecb473ab38558d23=6160016000f3 -58cb5fb86ad9d15449738047d3b636c8da84305fc0085d159588788c8ed9cb49=60003560005236600080f060005560018055 +58cb5fb86ad9d15449738047d3b636c8da84305fc0085d159588788c8ed9cb49=6000356000523660006000f06000556001600155 cfaef2ef539e024c4a5185c0fe50c2b333446e676879766168f4b431aab1e270=7f6001600155601080600c6000396000f3006000355415600957005b602035600060005260356020536055602153600063ffffffff60006001f500 08b8b52cb0c8acbc1f7d3c022b73b922d06daf308e69a669c03e22243c29f902=7f6001600155601080600c6000396000f3006000355415600957005b602035600060005260356020536055602153600067ffffffffffffffff60006001f55060006fffffffffffffffffffffffffffffffff60006001f55060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60006001f55060006000630fffffff6001f5506000600063ffffffff6001f5506000600067ffffffffffffffff6001f550600060006fffffffffffffffffffffffffffffffff6001f550600060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001f5506000630fffffff630fffffff6001f550600063ffffffff63ffffffff6001f550600067ffffffffffffffff67ffffffffffffffff6001f55060006fffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff6001f55060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001f500 56e90a040e5942ed41cb57aba0bb88d80496b2b3ee5c224561348017daf29891=7f6001600155601080600c6000396000f3006000355415600957005b6020356000600052603560205360556021536000600060006001f5506000630fffffff60006001f500 215a6b4634450b670cf51948a4ac6bbcc02b064a7effdad6d09d895a704498d8=6020600060006000600173c94f5374fce5edbc8e2a8697c15331677e6ebf0b620249f0f15060005160015500 a0bd887cab26482b2eae42e58c3b25ec2363925901e1c69ea890b43448bb929b=746d64600c6000556000526005601bf36000526001ff60005260006015600b6001f550600b600055600e6012f300 -52227e17d7647fe54dafe44b4deb62e226a5128df5abeb56b567d38dad4d665a=600035600052600036600080f5600055 -eab3f419be128f0744b8ff553eac239abd0fa8010eb15c6e908c6906a6bf9206=7f600060005360016000f30000000000000000000000000000000000000000000060005260005b610100811015604d57806001536000600a600080f5604357600181555b6001810190506026565b50600161010055 -99f533052ff998814398779b29eb17d1d9cda70c84a1c5c8de62d89afc739ba9=60003561ffff54706005600c60003960056000f360016001556000526000808303602f576011806020036000f090505b60018303604357816011806020036000f590505b8060025560008111156058576001820161ffff555b8060005260206000f3 -aa230d92a57b6ac334b54c9a345753a57bd824c1e86bdae7d5afb69ad0b40db7=60003561ffff54706005600c60003960056000f360016001556000526000808303602f576011806020036000f090505b60018303604357816011806020036000f590505b8060025560008111156058576001820161ffff555b8060005260206000f3 -242ffaf4896aa1a33fd22f9736aae7f38ec7926a44af5c83ecf4178c5466184b=60043560243560443560643561ffff545b838110156045576460016000f36000526005806020036000f06000811115603a576001820161ffff555b5061ffff5490506010565b5080600052600084036062576020600060206000856103e85a03f4505b60018403607b57602060006020600080866103e85a03f2505b60028403609457602060006020600080866103e85a03f1505b60005180600155600081111560b4576000806000806000856103e85a03f1505b5050505050 -d21bc6ddad58c0de08949ea6b6459b6a157f5d430adc1dcc3dacfc651e30e8c2=7f60016000f300000000000000000000000000000000000000000000000000000060005260006005600080f560005560018055 +52227e17d7647fe54dafe44b4deb62e226a5128df5abeb56b567d38dad4d665a=60003560005260003660006000f5600055 +eab3f419be128f0744b8ff553eac239abd0fa8010eb15c6e908c6906a6bf9206=7f600060005360016000f30000000000000000000000000000000000000000000060005260005b610100811015605157806001536000600a60006000f51515604657600181555b5b6001810190506026565b50600161010055 +99f533052ff998814398779b29eb17d1d9cda70c84a1c5c8de62d89afc739ba9=60003561ffff54706005600c60003960056000f3600160015560005260006000831415603257601160116020036000f090505b600183141560485781601160116020036000f590505b806002556000811115605d576001820161ffff555b8060005260206000f3505050 +aa230d92a57b6ac334b54c9a345753a57bd824c1e86bdae7d5afb69ad0b40db7=60003561ffff54706005600c60003960056000f3600160015560005260006000831415603257601160116020036000f090505b600183141560485781601160116020036000f590505b806002556000811115605d576001820161ffff555b8060005260206000f3505050 +242ffaf4896aa1a33fd22f9736aae7f38ec7926a44af5c83ecf4178c5466184b=60043560243560443560643561ffff545b838110156047576460016000f3600052600560056020036000f06000811115603b576001820161ffff555b505b61ffff5490506010565b508060005260008414156065576020600060206000856103e85a03f4505b600184141560805760206000602060006000866103e85a03f2505b6002841415609b5760206000602060006000866103e85a03f1505b60005180600155600081111560bd5760006000600060006000856103e85a03f1505b5050505050 +d21bc6ddad58c0de08949ea6b6459b6a157f5d430adc1dcc3dacfc651e30e8c2=7f60016000f30000000000000000000000000000000000000000000000000000006000526000600560006000f56000556001600155 0cb7ff2ff5e91cde9ae944d1e97fbd4ba7846b22dd7870a48acde18e2116d79c=626001ff60005260006003601d6000f500 fe0da0e6c1a27cd3fcd9449de9c3fc8ae987439af42de55d589e07dbe20b53a6=6b626001ff6000526003601df36000526000600c60146000f55060006000600060006000735649527a8464a86cae579719d347065f6eb27279620249f0f100 9156fae8844f012720eec7d8aa673ad9cca3ca5dd23b928b9775b0cba8e970d0=626001ff60005260006003601d6001f500 @@ -1433,35 +1433,35 @@ e904075f6bb91684919a73b6f6c2faad062ac28e0d1b4b0324e37dfaed73f2bf=6b626001ff60005 320c0bdac224010fc389c11ded0214972cede15725f62d68bb368ebaa343fe00=6b626001ff6000526003601df36000526000600c60146001f5506000600060006000735649527a8464a86cae579719d347065f6eb27279620249f0fa00 6adc11cbb07e63284761e20c84dd42c2f0357cf416a9765221b113cc8496b91b=6a6130ff6000526002601ef36000526000600b60156000f5506000600060006000736cd0e5133771823da00d4cb545ec8cdab0e38203620249f0fa00 9444e91fe9e94eb25af4daa9780ce737e1a4ffdad450b59d459feb44b5cd1de9=6a6130ff6000526002601ef36000526000600b60156001f5506000600060006000736cd0e5133771823da00d4cb545ec8cdab0e38203620249f0fa00 -340145cf7877773c55440eb898245cda7ff08bffbb771df78ba3304b528ec4c1=600435803b80600080843c600081600080f560008103601a57fe5b505050 -5e89e098fa8a48e4074af88f6823d5ce6305599a4ba824c41d6f249a2a45c363=600160005560018055600060015560016000f3 -c0553b477805522c5df52fb777795c449f563514238aa02af2bff31c12129e9f=60016000556001805560006001556113886000f3 -64f9d48dbb03e233b564cffb000d25d46d619bf7ead017c3a60da12bbda05e92=6001600055600180556000600155fe -4e8170cad07e738c37d8081a5043b7dda9218af13e3e8c2d8cc933f92056030e=60016000556000806000806000620c0dea5af15060016000f3 -aa22e6199d06e6084cda614c3a3db0be54f5f87f362de868f596d2b68148969c=60016000556000806000806000620c0dea5af1506113886000f3 -cc9309c1b6cb2faea24cca4c16c568792e9ba7bd09cef2a913672bd0c38382d6=60016000556000806000806000620c0dea5af150fe -60948fb40224c78fd3a4c9aaa7e7a04973a6f7e7e78805b49dfbf819b847ea08=600160005560018055600080600080620c0dea5af45060016000f3 -a4eafe227ee9964f46af2519ea377ae296a117b84a91a54fde4f4a321d009830=600160005560018055600080600080620c0dea5af4506113886000f3 -3490e27dac61dd1c08c41a6d5fd3d6df2d5c053660def96def65826e1d059792=600160005560018055600080600080620c0dea5af450fe -f82e343e47b2a2164551d322270159a4d6f2e32443eee96e671d40af31792568=6001600055600180556000806000806000620c0dea5af25060016000f3 -6d1cf3d28163e44c11833d12ddb720f6fa12983d9989d57560375cbccc615fcc=6001600055600180556000806000806000620c0dea5af2506113886000f3 -93a4c5e642c58f6ea90ef828723fdacca3b8254909dc7de50b54a035add60ebd=6001600055600180556000806000806000620c0dea5af250fe -13febad19db39c352c37a194221a3658ab3c1d4fc7188ff7e76b4784b62ef8ca=60016000556000806000806000620c0ded5af15060016000f3 -ca690331276298ad3f041bf742be14b44d3b308f90925e5ccc93c5a9b6c0aefd=60016000556000806000806000620c0ded5af1506113886000f3 -bdd7b053de3ae2d9398c43459cb70dd456a242b868144cdf53c4cd94f4b1d471=60016000556000806000806000620c0ded5af150fe -adff105dfff0c5647ec1ce6fc08d2083f927ca29e76e12f9f206a88a49724331=60016000556000806000806000620c0de05af15060016000f3 -68f65e37716676a50230ef8fe1b50f7ae45b9a35c28f893fe6f452ee27c1c85c=60016000556000806000806000620c0de05af1506113886000f3 -6a819c53a9ea47952ca7a953489d5625d102ea77122cf21e7376b6fb99db8872=60016000556000806000806000620c0de05af150fe -36c8e5e4d13fb10e4f3b50768b298da7ed47dd6dc82e68b555184cd0c879345b=6001600055600180556000600155620c0de1803b80600080843c80600080f0506001808201f3 -047d5aaf086bdd6f55cdcc7b5ca8f1de1f7321f7fb8f03ee6dc1b6833d9b00fa=6001600055600180556000600155620c0de1803b80600080843c80600080f05061138860018201f3 -53adb60340f14943f224f428f6d3a8eb7edc14b89fb5b459eba8e1549ea187a1=6001600055600180556000600155620c0de1803b80600080843c80600080f050fe -91bbbde20fcd7e34727b852e501b73199c4273b1b32933dfc335a18e8e8e1f8a=6001600055600180556000600155620c0de1803b80600080843c600081600080f5506001808201f3 -543016ad470d6c160504dc6c9c8ddf08b16707bde3f564c397c001a8ddeba5b6=6001600055600180556000600155620c0de1803b80600080843c600081600080f55061138860018201f3 -47916ef90b042f3a5106cff87fc7a949b0f1d40e763cfcd201ec85b4bfde6ec8=6001600055600180556000600155620c0de1803b80600080843c600081600080f550fe +340145cf7877773c55440eb898245cda7ff08bffbb771df78ba3304b528ec4c1=600435803b8060006000843c60008160006000f56000811415601d57fe5b505050 +5e89e098fa8a48e4074af88f6823d5ce6305599a4ba824c41d6f249a2a45c363=60016000556001600155600060015560016000f3 +c0553b477805522c5df52fb777795c449f563514238aa02af2bff31c12129e9f=6001600055600160015560006001556113886000f3 +64f9d48dbb03e233b564cffb000d25d46d619bf7ead017c3a60da12bbda05e92=600160005560016001556000600155fe +4e8170cad07e738c37d8081a5043b7dda9218af13e3e8c2d8cc933f92056030e=600160005560006000600060006000620c0dea5af15060016000f3 +aa22e6199d06e6084cda614c3a3db0be54f5f87f362de868f596d2b68148969c=600160005560006000600060006000620c0dea5af1506113886000f3 +cc9309c1b6cb2faea24cca4c16c568792e9ba7bd09cef2a913672bd0c38382d6=600160005560006000600060006000620c0dea5af150fe +60948fb40224c78fd3a4c9aaa7e7a04973a6f7e7e78805b49dfbf819b847ea08=600160005560016001556000600060006000620c0dea5af45060016000f3 +a4eafe227ee9964f46af2519ea377ae296a117b84a91a54fde4f4a321d009830=600160005560016001556000600060006000620c0dea5af4506113886000f3 +3490e27dac61dd1c08c41a6d5fd3d6df2d5c053660def96def65826e1d059792=600160005560016001556000600060006000620c0dea5af450fe +f82e343e47b2a2164551d322270159a4d6f2e32443eee96e671d40af31792568=6001600055600160015560006000600060006000620c0dea5af25060016000f3 +6d1cf3d28163e44c11833d12ddb720f6fa12983d9989d57560375cbccc615fcc=6001600055600160015560006000600060006000620c0dea5af2506113886000f3 +93a4c5e642c58f6ea90ef828723fdacca3b8254909dc7de50b54a035add60ebd=6001600055600160015560006000600060006000620c0dea5af250fe +13febad19db39c352c37a194221a3658ab3c1d4fc7188ff7e76b4784b62ef8ca=600160005560006000600060006000620c0ded5af15060016000f3 +ca690331276298ad3f041bf742be14b44d3b308f90925e5ccc93c5a9b6c0aefd=600160005560006000600060006000620c0ded5af1506113886000f3 +bdd7b053de3ae2d9398c43459cb70dd456a242b868144cdf53c4cd94f4b1d471=600160005560006000600060006000620c0ded5af150fe +adff105dfff0c5647ec1ce6fc08d2083f927ca29e76e12f9f206a88a49724331=600160005560006000600060006000620c0de05af15060016000f3 +68f65e37716676a50230ef8fe1b50f7ae45b9a35c28f893fe6f452ee27c1c85c=600160005560006000600060006000620c0de05af1506113886000f3 +6a819c53a9ea47952ca7a953489d5625d102ea77122cf21e7376b6fb99db8872=600160005560006000600060006000620c0de05af150fe +36c8e5e4d13fb10e4f3b50768b298da7ed47dd6dc82e68b555184cd0c879345b=600160005560016001556000600155620c0de1803b8060006000843c8060006000f050600160018201f35050 +047d5aaf086bdd6f55cdcc7b5ca8f1de1f7321f7fb8f03ee6dc1b6833d9b00fa=600160005560016001556000600155620c0de1803b8060006000843c8060006000f05061138860018201f35050 +53adb60340f14943f224f428f6d3a8eb7edc14b89fb5b459eba8e1549ea187a1=600160005560016001556000600155620c0de1803b8060006000843c8060006000f050fe5050 +91bbbde20fcd7e34727b852e501b73199c4273b1b32933dfc335a18e8e8e1f8a=600160005560016001556000600155620c0de1803b8060006000843c60008160006000f550600160018201f35050 +543016ad470d6c160504dc6c9c8ddf08b16707bde3f564c397c001a8ddeba5b6=600160005560016001556000600155620c0de1803b8060006000843c60008160006000f55061138860018201f35050 +47916ef90b042f3a5106cff87fc7a949b0f1d40e763cfcd201ec85b4bfde6ec8=600160005560016001556000600155620c0de1803b8060006000843c60008160006000f550fe5050 8648b214afb7430e364eba9298aef2f4cf789dfe3c10b7169d60aca559d4db65=6000600155 80ab328c7bf9ff6ec231b37fd3a29be9a4a52ce75a9403ac1f1d3dfa4c12e8b6=32ff d788f00379736afa7b39a7532465cb8ea82a12dd35d61daec41b81356fda4cc2=60ff60005260206000a060fa60206000a160fb60fa60206000a260fc60fb60fa60206000a360fd60fc60fb60fa60206000a4 -23a0e63111b844c94ad18cf0e41bce1a67fa8635ec21efaf18bc6014c29f084a=60016000556000805560016000f3 +23a0e63111b844c94ad18cf0e41bce1a67fa8635ec21efaf18bc6014c29f084a=6001600055600060005560016000f3 a2a11955877ae151e36f329f8bc45ab5a5565ee1d755940576841438208b830d=6d6460016001556000526005601bf36000526000600e60126000f500 7761f9e47080a9dea07ad74dcd0b4eee6bb2bc0a5d2cbe5d284f858b031765de=6d6460016001556000526005601bf36000526000600e60126000f5503d6001556000600060003e60005160025500 5dd098f4422594a7933ff26fb535c61299b4658a6cf97ac8177285a779953a47=6020600060006000600073b94f5374fce5edbc8e2a8697c15331677e6ebf0b5af2506020600060003e60005160015500 @@ -1475,15 +1475,15 @@ ea87b03a322010861c9c3f2d91113817ed5af1ad5509d1d296e953d885d261f6=600035600052600 9b80084d8b54d392190ba850971ca31ab58a26470e85844e00a53d6647973a45=6000356000526000516002016000526104006000511460435760006000602060006104006000511473c94f5374fce5edbc8e2a8697c15331677e6ebf0b5af150606d565b78686000600060006000f56000526000600960176000f56001556020526000601960276000f56001555b00 3507842b88aa1c0e1f22cf1cbc3dd8f2a37e4dfd544cc4711468651f49f7110b=7d606460006000396103e85a10601b576000606460006000f5601d565b5a5b6000526000601e60026000f500 a24c75b3028d6c82ba28bd9349aa3a6c94109a221c8cf79637c1873255454624=69600c600055600d6001556000526000600a60166000f500 -88ab1b584b23704782ea1a04695ae304adf2f3826b4297db965d2568d2a1e1d8=60086002556000600060006000f550600c60035500 9906997c04cc7ac0c88e7e25b9cf3b0b36b808b8090b395adb1fb1ea00d94870=60016000556000600060006000600073b000000000000000000000000000000000000000600035f1600155600c60045500 +88ab1b584b23704782ea1a04695ae304adf2f3826b4297db965d2568d2a1e1d8=60086002556000600060006000f550600c60035500 b9134334f82ce111680bb32468b9df3d41061303d1511e4b9361eb7d22dbbd74=30506000600e80602860003960006000f56002553d6000556020600060003e6000516001550000fe6211223360005260206000fd0000 01775ea3ee583a2e77f1f14633400a5385e341e5ae3ac38eec026cd58fcb252e=6e600160005560016000fd60116001556000526000600f60116001f5600155600c60005500 3d57290be527913c1e95ad5333a6100955c93d8856983312dc60e9e56ab52715=6000600e80601760003960006000f5503d6000550000fe6211223360005260206000fd0000 b37864a984e35fc2d0e7ceb61198503de2988d27a5510464f54dbaba442ffd48=7d111122223333444455556666777788889999aaaabbbbccccddddeeeeffff60005260206000f300 73d60d979b543a41feef88f3749f87baac94113d0540f97fe77a301649960067=60206000600060006000730aabbccdd5c57f15886f9b263e2f6d2d6c7b5ec6640900000000f1506000600e80603e60003960006000f5503d6000550000fe6211223360005260206000f30000 -8a168d3c2b531bbfb7eed870f0c15ff2d5229fe26db10fe47625a97cbcb51e88=60006000600060006000730aabbccdd5c57f15886f9b263e2f6d2d6c7b5ec6640900000000f1506000600e80603e60003960006000f5503d6000550000fe6211223360005260206000f30000 6c6d1b053e052e2aaaa460b4c0db743515eb307279b458b6a2ef9b18c846db38=7d111122223333444455556666777788889999aaaabbbbccccddddeeeeffff60005260206000f30000 +8a168d3c2b531bbfb7eed870f0c15ff2d5229fe26db10fe47625a97cbcb51e88=60006000600060006000730aabbccdd5c57f15886f9b263e2f6d2d6c7b5ec6640900000000f1506000600e80603e60003960006000f5503d6000550000fe6211223360005260206000f30000 c79e0198cf5bd2391dfab79ab4a5f727191b360097e4480f46ba6b55343c6fd1=60006000536000600160006000f560005500 a1c0af891959756f7603db930bde8c199d25c3b45e93892f65e2fc58a6e84d01=60566000536000600160006000f560005500 170d0e39252912ff91a1ecaca213518f87e5ab208f0b1de87b643d79b6e5a7dc=60016000536000600160006000f560005500 @@ -1493,19 +1493,19 @@ af92af26df14fab9cfeacf2d1ea273af589f14a70a6a9f85c81fb3b8a1a9634e=626001ff6000526 1f224ef3dee7bce1e818896ba56cea12fa5929f5322ff3cc30935666a995a355=626001ff60005260006003601d6001f560005500 343399ee1d228e1f9967c71d5018fcb3a10487d7158a3686918df63869426864=60006003601d6000f560005500 4dea22089412be26f403665dec5b440f064752f4e3f1580981b6f743f6914f10=6160a960005260006002601e6001f560005500 -20aed0d47cdf2f12c13f245182de6e26d4c3e26ebc16b02e6e09da2268d389e3=7c600060015414601157600a6000f3601c565b6001600155600a6000f35b6000526000601d60036001f56001556000601b60056001f560025500 ca6d627872c35c5437a29a3fabda1f3f19b675a53ff0c281339bb7c0c8d6409c=7a600060015414601157600a6000f3601a565b60016001556001ff5b6000526000601b60056001f56001556000601b60056001f560025500 8d71f1baf932ccfe51802cd53663486a5e45cb95151cb1a322ec39a141518cd3=600060006000600060006000355af100 +20aed0d47cdf2f12c13f245182de6e26d4c3e26ebc16b02e6e09da2268d389e3=7c600060015414601157600a6000f3601c565b6001600155600a6000f35b6000526000601d60036001f56001556000601b60056001f560025500 0ceab5d26ca0b097ec0e2b5a810d9a46a576747f5a2ee39acf1125dbefa1e379=6000602980601160003960006000f500fe61010060006000600073f2000000000000000000000000000000000000005afa50600051600a550000 -7b3b54ebb35862901dec1f208e1a51ae747dabaf9434b1c8a5272baa7eaa7207=6000600060006000600073f0000000000000000000000000000000000000005af100 -a27ff87311b40a91645d0e29e85f17bef428ee98334082d8810a81ebc72bfe80=6000602380601360003960006000f5500000fe30600055303160015532600255336003553460045536600555386006553a6007550000 02aa5a85c50eccbdd3714fe0c173fe47a7ec78e3b98e0166542c0d3d55dc1bf2=6000602480601360003960006000f5500000fe6000600060006000600073f0000000000000000000000000000000000000005af2500000 +556f9c674339d5fd4c44ca3c79b25d43b95ba0dfc0753b4e551406a92576edb9=6000602280601360003960006000f5500000fe600060006000600073f0000000000000000000000000000000000000005af4500000 +a27ff87311b40a91645d0e29e85f17bef428ee98334082d8810a81ebc72bfe80=6000602380601360003960006000f5500000fe30600055303160015532600255336003553460045536600555386006553a6007550000 c3fa0ace59c7d664250610583f9f21f2688f488bd99e107964cf0b1d69977d17=600060006000600073f0000000000000000000000000000000000000005af4500000 fb57d4384857666596aa0f1ae985061514c50d24c6536985e93c7721f26ab366=6000600060006000600073f0000000000000000000000000000000000000005af200 -a36567eb706d26cb1f601f73396df65f77eec2935c59d347a1f1525b4e7e5dbc=61010060006000600073f2000000000000000000000000000000000000005afa50600051600a5500 f2cba7b387262c5d65c0c23f4c7920ed5cb1ce9a55eb6959781cf1f39775f143=6000602980601160003960006000f500fe3060005230316020523260405233606052346080523660a0523860c0523a60e0526101006000f30000 -556f9c674339d5fd4c44ca3c79b25d43b95ba0dfc0753b4e551406a92576edb9=6000602280601360003960006000f5500000fe600060006000600073f0000000000000000000000000000000000000005af4500000 +a36567eb706d26cb1f601f73396df65f77eec2935c59d347a1f1525b4e7e5dbc=61010060006000600073f2000000000000000000000000000000000000005afa50600051600a5500 c3ce08944bad51dc73b907a561f88e768067ad7797c597f4e57126175c6be569=6000602480601360003960006000f5500000fe6000600060006000600073f0000000000000000000000000000000000000005af1500000 +7b3b54ebb35862901dec1f208e1a51ae747dabaf9434b1c8a5272baa7eaa7207=6000600060006000600073f0000000000000000000000000000000000000005af100 fd39994f2b05bfbff9216d2e0ff446e83c2504d25ac18ca1c69efbf13d5ce691=6000600060006000f500 8709980edfa2db2bca11f56a4f8ccf4f40130495b192afc3d66338be29c79fe8=64600160015560005260006005601b6000f500 edc3d79069c655807240f56f6d25b23bc70fb7f74b6c3c72ceb022842193cf96=6000600060006001f500 @@ -1536,7 +1536,7 @@ fbb431f2189ebaffc4f9c1b0a170a321958396ed8b683e1ba534787f4064beba=600060298060206 3694d6a2e270143e1b7ad42de73c57175a13c50dfd52d1be17f5b34ba872fd34=6000600d80601760003960006000f5503d6000550000fe6211223360005260206000f300 4cc61e1ed6d2e4e5227c4388744a5b9cdacad746f9620ddb6ce11a9aabd8c23b=600c60015500 63d1873266f31c5c1211c5e798508c6222574120b233cfc18cf34ca6ac238bfc=5a600055602060006000f06001555a6002556000600060006000600073e1ecf98489fa9ed60a664fc4998db699cfa39d4061ea60f16003555a60645500 -50ac9394e4391fc0702cfc7b594d452128fbda9f4060d0f949916d03701fcc4d=60003560005236600080f0600055 +50ac9394e4391fc0702cfc7b594d452128fbda9f4060d0f949916d03701fcc4d=6000356000523660006000f0600055 6e7614d58424c8409591332959588c34ee21514f7a8ea69e76a59e4c994c50cc=5a600055602060006000f06001555a6002556000600060006000600060015461ea60f16003555a60645500 c014473615e7e31367489957d58724908eeb02d7bc2200959781a5bbfaa5381e=5a600055602060006000f06001555a6002556000600060006000600160015461ea60f16003555a60645500 bc1e9da083d9ee4773f2e596ffe3d2f8d56fc65822fb05e489dab7f3d9e1d9f0=5a600055602060006000f06001555a60645500 @@ -1544,76 +1544,76 @@ bc1e9da083d9ee4773f2e596ffe3d2f8d56fc65822fb05e489dab7f3d9e1d9f0=5a6000556020600 588b8618ca23c9938da936cad23b8a754426f9101d2cd743bb76d08fb6d54491=5a6000557f600c6000556000600060006000600073c94f5374fce5edbc8e2a8697c15331676000527f7e6ebf0b61ea60f1000000000000000000000000000000000000000000000000602052604060006000f06001555a6002556000600060006000600060015461ea60f16003555a60645500 9f443d4664f4165129254b21d2aa9f402e990ec3f21c8a3557c9053f13a8bb47=5a6000557f600c6000556000600060006000600073c94f5374fce5edbc8e2a8697c15331676000527f7e6ebf0b61ea60f1000000000000000000000000000000000000000000000000602052604060006000f06001555a6002556000600060006000600160015461ea60f16003555a60645500 4fef7f1b0a5d0be8d7231aba1a5e3ea4d34f7dc7dd96071b4c04e6e43716bd91=5a6000557f600c6000556000600060006000600073c94f5374fce5edbc8e2a8697c15331676000527f7e6ebf0b61ea60f1000000000000000000000000000000000000000000000000602052604060006000f06001555a60645500 -0e64b07c5f8a4b1d805d72a663b7922bebd1fffc63fdda9f74ee833a962cd08f=7f600060005360016000f30000000000000000000000000000000000000000000060005260005b610100811015604b5780600153600a600080f0604157600181555b6001810190506026565b50600161010055 -0f80ade5cd0aebbf46caac3e6642ca49675bc577b59305821c4f28ff215d106e=7f60016000f30000000000000000000000000000000000000000000000000000006000526005600080f060005560018055 +0e64b07c5f8a4b1d805d72a663b7922bebd1fffc63fdda9f74ee833a962cd08f=7f600060005360016000f30000000000000000000000000000000000000000000060005260005b610100811015604f5780600153600a60006000f01515604457600181555b5b6001810190506026565b50600161010055 +0f80ade5cd0aebbf46caac3e6642ca49675bc577b59305821c4f28ff215d106e=7f60016000f3000000000000000000000000000000000000000000000000000000600052600560006000f06000556001600155 8f2b325bc960e0b2589c62f03544cb60df9ff0743d658cd3b2531de5b60d4036=6000356000545560016000540160005500 2ab24936454b2c8365281adf6aa1abbeab4c3834a3470510287a934d1f9fd054=60068061004c610100396102005260db8061005260003961022052600160043514603757615a17610200516101000160006000f56045565b610200516101000160006000f05b6102405200fe60ff60005500610100610100610100395861026052600060006020610260600061da7a62fffffff1503061026052600060006020610260600061da7a62fffffff1503861026052600060006020610260600061da7a62fffffff150303b61026052600060006020610260600061da7a62fffffff15060206000610100396101005161026052600060006020610260600061da7a62fffffff15060206000610100303c6101005161026052600060006020610260600061da7a62fffffff1505861026052600060006020610260600061da7a62fffffff1506101003803610100f300 8fe0d0b35be498ea58c02e2f3bfd06f7ff8c08eb81f04115affacd68520f5ee1=601d60005500 6bc0f214f1d46352212bff0bb2bf8f22e56a258863ee84aab805ba06e3044fde=60f860020a6000350461010052601580610158610300396105405260068061016d61020039610520526001610100511461004957615a17610540516103006000f561060052610058565b610540516103006000f0610600525b586020553d6010556106005160115560006000600060006000738af6a7af30d840ba137e8f3f34d54cfb8beba6e261fffff16106405258602155600161064051036012553d601355600060006000600060007340f1299359ea754ac29eb2662a1900752bf8275f61fffff16106405258602255600161064051036014553d601555738af6a7af30d840ba137e8f3f34d54cfb8beba6e23b6030556030546000610660738af6a7af30d840ba137e8f3f34d54cfb8beba6e23c610660516031557340f1299359ea754ac29eb2662a1900752bf8275f3b60325560325460006106607340f1299359ea754ac29eb2662a1900752bf8275f3c6106605160335500fe600680600f61020039610200f300fe60ff6000550060ff60005500 -3b0b68a7a28df4b5781690a493c35a0625901aa7b3dcb525b1949e33e7bdc4b4=6000600060006000600060003562013880f100 6ec4750d6b26b36a640db94d86ffea865b5e7c2c4ec374fe9bfcabc0ba2847b5=6460016001556000526005601b6000f060015500 -faa74b0b8c25e339bcbc2891614c363020f5bcabc51694e5519efe90cc6184d3=600435803b80600080843c80600080f060008103601857fe5b505050 -a27d87251798a593b75d6fc3305ed0eba86be2c4281283e6ec41a596d6ea23ed=600160005560018055600060015560016000f3 -cc0f8546303c24ff600f1084b88781cc91f432173256a6e0fec568f530130ca6=60016000556001805560006001556113886000f3 -af7b42087df475463f3f4681f43871f54737ecb874aeccc22c513978312d5c7a=6001600055600180556000600155fe -a80a8bffdd8dfcedecf9fec790b91200681b9680e658f239cf2f43ce41872497=60016000556000806000806000620c0dea5af15060016000f3 -1b3e88ede6b23b56ae8e682a5f59f5b7952cd6d0a4021feb90ba28d9c33881bf=60016000556000806000806000620c0dea5af1506113886000f3 -5e64f31cc3d60a722b54b1dd990213d114b43afb91bbdf1fbd253a0b0e09da2e=60016000556000806000806000620c0dea5af150fe -9756ccce0af3ab7f2ddcd9ead3417771c3e13e76f1982ef66757f2b9773908a7=600160005560018055600080600080620c0dea5af45060016000f3 -9859a54aafd2db530ca791b7cc9f75dc42a4750f17a2572175e5ff253401dc5c=600160005560018055600080600080620c0dea5af4506113886000f3 -6cf00b462393771a4d13d9a6a81721d3f576cfcf6d980a7ac0bf5ce9feb3c90e=600160005560018055600080600080620c0dea5af450fe -4f1407a0d00cfdd4938f41333df0db261155dcb8957dbf6e8a5aea79b4c39d64=6001600055600180556000806000806000620c0dea5af25060016000f3 -de71dee39e096d303abbe8e0d9b854de76c7abba0f5c868b618ea8f18e7adaa9=6001600055600180556000806000806000620c0dea5af2506113886000f3 -6b70aedf8a9e0995baf0ca14afadf1f1b2b8ff97b07ddc35c1654a17b3321c51=6001600055600180556000806000806000620c0dea5af250fe -53964f63ba7984d3ea9f17e77bf61770d262605e1b9afc6b5dc3b08ce76c9e07=60016000556000806000806000620c0ded5af15060016000f3 -2823105003d7f7bcb6a57448f36820843bdba58e19b23e548b99f0804b93fdb2=60016000556000806000806000620c0ded5af1506113886000f3 -16a843c37a1f02710eb64f1b0a8c09151cf80cc38bebae862f782b7d5c16c92f=60016000556000806000806000620c0ded5af150fe -62e06c8f58c452573ea2dedf8f1775060d6ceca56f811765344a61868cb5c5b6=60016000556000806000806000620c0de05af15060016000f3 -a30a1c1183bf98b15857ad1054e59dd365ecad93d3a22ec182750e2879cc0ad3=60016000556000806000806000620c0de05af1506113886000f3 -d5995b05f6a20e0100d0cfd60f01e0ad1094b297fa5311107fca3c5e96108b2e=60016000556000806000806000620c0de05af150fe -8cfc1ffde61af5b3a4c41ca48021e77291241e208e853eab1d5dce96c392b1d6=6001600055600180556000600155620c0de1803b80600080843c80600080f0506001808201f3 -0793730d42a2860ea9ee769705c5870d6cda60dfb4d781105c7f45db491026ea=6001600055600180556000600155620c0de1803b80600080843c80600080f05061138860018201f3 -9fc54038403be74936d2bf3282e4986caa6e7867abf72b992b78dac6b9ed7555=6001600055600180556000600155620c0de1803b80600080843c80600080f050fe -fa33b07cd047c51c449ba132a5bff6ba06f377530b02d88e2befe8a6663b67c4=6001600055600180556000600155620c0de1803b80600080843c600081600080f5506001808201f3 -44fcf2437a6eb80065abd34775b314990164b8cbd17c325aeac7932035089682=6001600055600180556000600155620c0de1803b80600080843c600081600080f55061138860018201f3 -b253d47579a0e1c65178b50c589e110c1efe4d2b82771cdfb1b85820bc56d951=6001600055600180556000600155620c0de1803b80600080843c600081600080f550fe +3b0b68a7a28df4b5781690a493c35a0625901aa7b3dcb525b1949e33e7bdc4b4=6000600060006000600060003562013880f100 +faa74b0b8c25e339bcbc2891614c363020f5bcabc51694e5519efe90cc6184d3=600435803b8060006000843c8060006000f06000811415601b57fe5b505050 +a27d87251798a593b75d6fc3305ed0eba86be2c4281283e6ec41a596d6ea23ed=60016000556001600155600060015560016000f3 +cc0f8546303c24ff600f1084b88781cc91f432173256a6e0fec568f530130ca6=6001600055600160015560006001556113886000f3 +af7b42087df475463f3f4681f43871f54737ecb874aeccc22c513978312d5c7a=600160005560016001556000600155fe +a80a8bffdd8dfcedecf9fec790b91200681b9680e658f239cf2f43ce41872497=600160005560006000600060006000620c0dea5af15060016000f3 +1b3e88ede6b23b56ae8e682a5f59f5b7952cd6d0a4021feb90ba28d9c33881bf=600160005560006000600060006000620c0dea5af1506113886000f3 +5e64f31cc3d60a722b54b1dd990213d114b43afb91bbdf1fbd253a0b0e09da2e=600160005560006000600060006000620c0dea5af150fe +9756ccce0af3ab7f2ddcd9ead3417771c3e13e76f1982ef66757f2b9773908a7=600160005560016001556000600060006000620c0dea5af45060016000f3 +9859a54aafd2db530ca791b7cc9f75dc42a4750f17a2572175e5ff253401dc5c=600160005560016001556000600060006000620c0dea5af4506113886000f3 +6cf00b462393771a4d13d9a6a81721d3f576cfcf6d980a7ac0bf5ce9feb3c90e=600160005560016001556000600060006000620c0dea5af450fe +4f1407a0d00cfdd4938f41333df0db261155dcb8957dbf6e8a5aea79b4c39d64=6001600055600160015560006000600060006000620c0dea5af25060016000f3 +de71dee39e096d303abbe8e0d9b854de76c7abba0f5c868b618ea8f18e7adaa9=6001600055600160015560006000600060006000620c0dea5af2506113886000f3 +6b70aedf8a9e0995baf0ca14afadf1f1b2b8ff97b07ddc35c1654a17b3321c51=6001600055600160015560006000600060006000620c0dea5af250fe +53964f63ba7984d3ea9f17e77bf61770d262605e1b9afc6b5dc3b08ce76c9e07=600160005560006000600060006000620c0ded5af15060016000f3 +2823105003d7f7bcb6a57448f36820843bdba58e19b23e548b99f0804b93fdb2=600160005560006000600060006000620c0ded5af1506113886000f3 +16a843c37a1f02710eb64f1b0a8c09151cf80cc38bebae862f782b7d5c16c92f=600160005560006000600060006000620c0ded5af150fe +62e06c8f58c452573ea2dedf8f1775060d6ceca56f811765344a61868cb5c5b6=600160005560006000600060006000620c0de05af15060016000f3 +a30a1c1183bf98b15857ad1054e59dd365ecad93d3a22ec182750e2879cc0ad3=600160005560006000600060006000620c0de05af1506113886000f3 +d5995b05f6a20e0100d0cfd60f01e0ad1094b297fa5311107fca3c5e96108b2e=600160005560006000600060006000620c0de05af150fe +8cfc1ffde61af5b3a4c41ca48021e77291241e208e853eab1d5dce96c392b1d6=600160005560016001556000600155620c0de1803b8060006000843c8060006000f050600160018201f35050 +0793730d42a2860ea9ee769705c5870d6cda60dfb4d781105c7f45db491026ea=600160005560016001556000600155620c0de1803b8060006000843c8060006000f05061138860018201f35050 +9fc54038403be74936d2bf3282e4986caa6e7867abf72b992b78dac6b9ed7555=600160005560016001556000600155620c0de1803b8060006000843c8060006000f050fe5050 +fa33b07cd047c51c449ba132a5bff6ba06f377530b02d88e2befe8a6663b67c4=600160005560016001556000600155620c0de1803b8060006000843c60008160006000f550600160018201f35050 +44fcf2437a6eb80065abd34775b314990164b8cbd17c325aeac7932035089682=600160005560016001556000600155620c0de1803b8060006000843c60008160006000f55061138860018201f35050 +b253d47579a0e1c65178b50c589e110c1efe4d2b82771cdfb1b85820bc56d951=600160005560016001556000600155620c0de1803b8060006000843c60008160006000f550fe5050 72a954af5006562f0dfb34e46464fdcd1a793f13b7356b2b6917ce16ae9fe447=6d6460016001556000526005601bf3600052600e60126000f000 8d3966a550258f89fd720a3a7b1a679fdd35354a926a6b798f38074a44c1352b=6d6460016001556000526005601bf3600052600e60126000f0503d6001556000600060003e60005160025500 cace50ebb0fb04dc5d78ecaf4ed9bdad4c5ba918c13b44605d82630d472cfaf5=6d6460016001556000526005601bf3600052600e60126000f0503d6001556020600060003e60005160025500 202db2b317af70e9ac3cda5a8d314345fe4b2188daea61087e9908fe7d45bcaa=726960016001556001600255600052600a6016f36000526013600d6000f0503d60020a00 -c66ba2e979c0d75239d1097adb2387c7918cf6ba151ad35697e0e17ad05f674b=6d6460016001556000526005601bf3600052600e60126000f05060206000fd00 4f8c74954df3634bebd2fc766b4288f4ddbf31a4780966679927bbbc5c211772=6020600060006000600073b94f5374fce5edbc8e2a8697c15331677e6ebf0b6159d8f15060005160015500 -1a282d2e56e29c13ec3fb7abb753de1f5d5b0d384059764043de57d1bb1bf213=6d6460016001556000526005601bf3600052600e60126000f0506000600060006000600073094f5374fce5edbc8e2a8697c15331677e6ebf0b612710f25060206000fd00 +c66ba2e979c0d75239d1097adb2387c7918cf6ba151ad35697e0e17ad05f674b=6d6460016001556000526005601bf3600052600e60126000f05060206000fd00 f7294423dfa4154ea5a6b3ac3cc4d02bb309bef32739d78422b4ac88dc4d838e=622fffff60002000 +1a282d2e56e29c13ec3fb7abb753de1f5d5b0d384059764043de57d1bb1bf213=6d6460016001556000526005601bf3600052600e60126000f0506000600060006000600073094f5374fce5edbc8e2a8697c15331677e6ebf0b612710f25060206000fd00 996cb8a428e971c6f11083e3145eec7d57824c1e0a3c7ab0b2c813797bf6dc85=386000556000361115600f576000ff5b -0ab50e48e5899bad047f2e35a53e0bdfa7f586424a34e03c540244e910bf42c4=620c0de0803b600080833c6160006000f3 -368981308235cb257758010274fb07e7d9f8eed95cc019c6533d9cb6e944b77b=60018055600035602035620c0de1803b80600080843c60005b8481101560375781600080f08060208302840152506001810190506018565b506000831115604257fe5b6020840281f3 -f56e738504b614f030b881a1aa1ab1fe0f8bf17565fd539ce0b000df49765dbc=600435602435604435606435836000526000602052604060208502808260406000620c0deb60025a04f460008103603557600080fd5b856000528460205260208702604001925060208602915081836040600080620c0deb60025a04f190506000810360725760008503607157600080fd5b5b866000860360805786880190505b60005b8181101560ad5760208102604001516000806000806000856103e85a03f150506001810190506083565b5060005b8581101560db5760208102604001516000806001600080856103e85a03f1505060018101905060b1565b505050505050505050 +0ab50e48e5899bad047f2e35a53e0bdfa7f586424a34e03c540244e910bf42c4=620c0de0803b60006000833c6160006000f350 +368981308235cb257758010274fb07e7d9f8eed95cc019c6533d9cb6e944b77b=6001600155600035602035620c0de1803b8060006000843c60005b84811015603b578160006000f08060208302840152505b600181019050601a565b506000831115604657fe5b6020840281f350505050 +f56e738504b614f030b881a1aa1ab1fe0f8bf17565fd539ce0b000df49765dbc=600435602435604435606435836000526000602052604060208502808260406000620c0deb60025a04f4600081141560375760006000fd5b85600052846020526020870260400192506020860291508183604060006000620c0deb60025a04f190506000811415607857600085141560775760006000fd5b5b86600086141560875786880190505b60005b8181101560b757602081026040015160006000600060006000856103e85a03f150505b600181019050608a565b5060005b8581101560e857602081026040015160006000600160006000856103e85a03f150505b60018101905060bb565b505050505050505050 b6c5a45d051cd5bc14c41fa06350d5c433107570d31b02b189232f4d40f29831=60043561010052602435610120526044356101405260046101405114600061014051141761002f5760005061003e565b60218061025061030039610540525b6001610140511461005157600050610060565b60298061027161030039610540525b6002610140511461007357600050610082565b60268061029a61030039610540525b60036101405114610095576000506100a4565b602c806102c061030039610540525b600561014051146100b7576000506100c6565b6028806102ec61030039610540525b600661014051146100d9576000506100e8565b602a8061031461030039610540525b60128061033e61020039610520526001610100511461011757615a17610540516103006000f561060052610126565b610540516103006000f0610600525b586020553d601055600461014051143d1761014357600050610153565b602060006101603e610160516011555b610600513b61056052610560516000610400610600513c61056051610520510360125561040051610200510360135560016101205114610195576000506101ac565b600060006000600060006106005161fffff1610640525b600261012051146101bf576000506101d6565b600060006000600060006106005161fffff2610640525b600361012051146101e9576000506101fe565b60006000600060006106005161fffff4610640525b6004610120511461021157600050610226565b60006000600060006106005161fffffa610640525b58602155600061012051141561023e5760005061024d565b600161064051036014553d6015555b00fe601280600f61020039610200f300fe600060006000600060006160a761fffff100622fffff60002050601280601761020039610200f300fe600060006000600060006160a761fffff10060006000fd601280601461020039610200f300fe600060006000600060006160a761fffff1006160a760005260206000fd601280601a61020039610200f300fe600060006000600060006160a761fffff1006160a760005200601280601661020039610200f300fe600060006000600060006160a761fffff1006160a76000526000ff601280601861020039610200f300fe600060006000600060006160a761fffff100600060006000600060006160a761fffff100 e6c9a9cd0576a0827c6d7e153bad1773be79b062b739d821e314d228becc90d3=6160a760005500 1269e1165214001763734f4ab9e8b4af94c6784128543571388100b0a8c2dd53=63deadbeef6000526160a760205260406000f3 7b5d67ae9317ae71fd4c0d26534e80a90ecc51e6970da1b80ff0de5896ae993a=650bad0bad0bad60005260206000fd cd4d3c09da11087ab7bf7869059e607ed149955ae16144b9b704b83d17b6552f=61600d60005260206000f3 -936fa4ee803af69ccca98fa58ec947c8200668c125be49d4673bd30b100c9f39=6040610100600080600061da7a5af16010553d6011556101005160125561012051601355600035803b80600080843c80600080f06000553d6001553d60006102003e61020051600255610220516003555050 -5365da138b0f9466cea68b6eecf601e402750cb8e13782201eaa011bd716f3d5=6040610100600080600061da7a5af16010553d6011556101005160125561012051601355600035803b80600080843c615a1781600080f56000553d6001553d60006102003e61020051600255610220516003555050 -2919a2ea3a06a002625142a11b44d8ee0180ba391c0273c49ee6e66b584127ce=6040610100600080600061da7a5af16010553d6011556101005160125561012051601355600035803b80600080843c63bad05a1781600080f56000553d6001553d60006102003e61020051600255610220516003555050 -16977ca088151212b46237b6c336d138f8bfd24129a63424ce9aec7eab7d47ad=6040610100600080600061da7a5af16010553d6011556101005160125561012051601355600035803b80600080843c80600080f06000553d6001553d60006102003e61020051600255610220516003555050 -7945813967cb96252b4d804e32df042ffd0df0cdbde6294bbdf40b02ed0f0262=6004356024358162c0de00016040610100600080600061da7a5af16010553d60115561010051601255610120516013555a600683036044576004615654026175300190505b82600052602061020060206000808686f1806000553d600155610200516002555050505050 -0f40edb53bcfa6f1d0b2756709e14a6609b10de043a398af6291a6beda197a48=60001960005260206101008039610100516000f3 -614f9f94e270fc2022d8845ecd77cc8daabca21b4b140b8f9f7c4c61753cae23=60043560243560443561c0de3b8060008061c0de3c826001820353816101005260008460f0811460345760f58114604157604e565b610120600080f09150604e565b615a17610120600080f591505b50806000555a600155803f6002555050505050 +936fa4ee803af69ccca98fa58ec947c8200668c125be49d4673bd30b100c9f39=604061010060006000600061da7a5af16010553d6011556101005160125561012051601355600035803b8060006000843c8060006000f06000553d6001553d60006102003e61020051600255610220516003555050 +5365da138b0f9466cea68b6eecf601e402750cb8e13782201eaa011bd716f3d5=604061010060006000600061da7a5af16010553d6011556101005160125561012051601355600035803b8060006000843c615a178160006000f56000553d6001553d60006102003e61020051600255610220516003555050 +2919a2ea3a06a002625142a11b44d8ee0180ba391c0273c49ee6e66b584127ce=604061010060006000600061da7a5af16010553d6011556101005160125561012051601355600035803b8060006000843c63bad05a178160006000f56000553d6001553d60006102003e61020051600255610220516003555050 +16977ca088151212b46237b6c336d138f8bfd24129a63424ce9aec7eab7d47ad=604061010060006000600061da7a5af16010553d6011556101005160125561012051601355600035803b8060006000843c8060006000f06000553d6001553d60006102003e61020051600255610220516003555050 +7945813967cb96252b4d804e32df042ffd0df0cdbde6294bbdf40b02ed0f0262=6004356024358162c0de0001604061010060006000600061da7a5af16010553d60115561010051601255610120516013555a60068314156046576004615654026175300190505b8260005260206102006020600060008686f1806000553d600155610200516002555050505050 +0f40edb53bcfa6f1d0b2756709e14a6609b10de043a398af6291a6beda197a48=600019600052602061010061010039610100516000f3 +614f9f94e270fc2022d8845ecd77cc8daabca21b4b140b8f9f7c4c61753cae23=60043560243560443561c0de3b806000600061c0de3c826001820353816101005260008460f0811460355760f581146043576051565b61012060006000f091506051565b615a1761012060006000f591505b50806000555a600155803f6002555050505050 5a7d04b359943ed94efac3837ef063438beffff6facc990229c19b49b7348e1b=600160005401600055600060006000600073bbbf5374fce5edbc8e2a8697c15331677e6ebf0b650ffffffffffff460015500 ae55b10532d3bf62548b4bff144544b45ee68ff8efeb6ef266609904dbe7a9b3=600160005401600055600060006000600073bbbf5374fce5edbc8e2a8697c15331677e6ebf0b610401600054046001036127105a0302f46001556103e86000540260010160025500 b424a9fc173523f0ddaf08491e9a131e4cf6320fa57fd3687e17db02d8391a36=6000600060006000600173aaaf5374fce5edbc8e2a8697c15331677e6ebf0b61fffff16002556000600060006000600173aaaf5374fce5edbc8e2a8697c15331677e6ebf0b61fffff1600355600160005401600055600060006000600073bbbf5374fce5edbc8e2a8697c15331677e6ebf0b650ffffffffffff460015500 512a7e819ae177c42e94558e8dbfdd801a2dc4706b3148d3b5b9350071a4f85e=600160005401600055600060006000600073bbbf5374fce5edbc8e2a8697c15331677e6ebf0b620186a060005402600101f46001556103e86000540260010160025500 -fdba06429bd93561b41c5953611bb7ffe0b3b706509a872d20c09606d4a051e2=6000600060006000601773bad304eb96065b2a98b57a48a06ae28d285a71b5620186a0f150600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b56707fffffffffffffff400 6403d40db5100426f4be765613d8db9d58f6b1b276a0d0cffeeff1264b787e8a=6001600054016000556000600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b562036b005a03f160015500 +fdba06429bd93561b41c5953611bb7ffe0b3b706509a872d20c09606d4a051e2=6000600060006000601773bad304eb96065b2a98b57a48a06ae28d285a71b5620186a0f150600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b56707fffffffffffffff400 451d06e323434c52e2a3d65400abaac0b245634eb0f33307723c2e276ac83b32=7f5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6600052600060006000600073aaae7baea6a6c7c4c2dfeb977efac326af552d8761c350f45060005160005500 a0fe427a36ac004b74ca059fffb2c45e481bd46f564281299e03c66b64dc3a26=7f5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6600052600060006020600073aaae7baea6a6c7c4c2dfeb977efac326af552d8761c350f45060005160005500 c65245e65a9bed57921028dcb30c7a2e52fde9f6c793fcb24b969f87b997ccda=7f5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6600052602060006000600073aaae7baea6a6c7c4c2dfeb977efac326af552d8761c350f45060005160005500 b7e72e1c8d9f2c941bce07dd527a3611514420e00fb57d100819a80c2e09b15e=7f5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6600052600a60006000600073aaae7baea6a6c7c4c2dfeb977efac326af552d8761c350f45060005160005500 b2957d2a9eae413e2f05c2b4e1e9105ff1c03459660ffc36c47fd654a988adc9=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa602052600260006040600073945304eb96065b2a98b57a48a06ae28d285a71b56bfffffffffffffffffffffffff460005500 -8205ccbba7ba5cda9ba18d2b743456a5a5ae28c794289b4f7f674e3de3263e9a=346000553360015560003560025500 133de2b241ec3ff88af5e8eb24416f1be967b72c6f8fddb438380ad644f05f4b=60016000526040600060406000731000000000000000000000000000000000000001620186a0f400 +8205ccbba7ba5cda9ba18d2b743456a5a5ae28c794289b4f7f674e3de3263e9a=346000553360015560003560025500 6de5f23423df62287edbcc3ba1da7c482ddf3f82fc9f4b2c02914f94c1f5ad93=600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b5622dc6c1f460005500 6e6e141f37460a061512b6f252198f61fe0504403956b067297066395f3a70fe=600260006040600073945304eb96065b2a98b57a48a06ae28d285a71b56207a120f460005500 848d6cde8e336967fe91c0806a8fa5b7d7b90c01b3aea951b342540a3b9116c3=604060006040600073945304eb96065b2a98b57a48a06ae28d285a71b561c350f460005500 @@ -1632,22 +1632,22 @@ ffcb314bb59677ab6c9d61dbdc62cf0048c65ce6f887e99d155542035f3672ef=5a6008556000600 1f2652b1c89a3813b6f38ba3dbef143dc331428de3b5ddcf96877c64b391256f=5a6008556000600060006000600073100000000000000000000000000000000000010762030d40f160095500 016d949e2d94b04a422ce01d9b6b8814ac56de8730447894c127d6b34a942b42=5a60085500 68322fd65ffc3848db62898f9a624413c70af4f57322bcfbdb900551fd2f9176=5a60085560006000600060006000731000000000000000000000000000000000000108620927c0f160095500 -cb7f48a28e3b7344d383cd49b2657a0c20bc109c28dd890ec0b313afa643460e=5a60085560006000600060006000731000000000000000000000000000000000000113620927c0f160095500 -5a12bedda1432446b7eec7329b1b305da2685f9444140a847891658049fa929e=5a600855622fffff60002000 e24d0510cfdb27d02652a2093bb0ab7c072a142e875da65ec872c8db043ea87a=5a60085560006000600060006000731000000000000000000000000000000000000114620927c0f160095500 +5a12bedda1432446b7eec7329b1b305da2685f9444140a847891658049fa929e=5a600855622fffff60002000 +cb7f48a28e3b7344d383cd49b2657a0c20bc109c28dd890ec0b313afa643460e=5a60085560006000600060006000731000000000000000000000000000000000000113620927c0f160095500 +e5ea9813b1a45461a2c6b4b4d54e1b2bc84c43492a60e67790431bc764241205=5a60085560006000600060006000731000000000000000000000000000000000000110620927c0f160095500 4a7fd2dbe174b72f892e2a64d82d493c802bb9cb7cd0f499a37bc32903b6e205=5a600855622fffff600020505a6009555a600a5500 7cc4db19afdf9c3ccd33c8f4b8003c832abbaec2de7923147b7b316652b8595f=5a60085560006000600060006000731000000000000000000000000000000000000111620493e0f16009556001600c5500 -e5ea9813b1a45461a2c6b4b4d54e1b2bc84c43492a60e67790431bc764241205=5a60085560006000600060006000731000000000000000000000000000000000000110620927c0f160095500 2fb5ca46ea750c14fff562ff06e6e2ce55b347d6f2bad23cd79f48f076d86148=5a606452635a60fd556000526004601c6000f0600b555a6064510360095500 078aa7a67e05378d2d6a01c7849209aa1e715ff722779ad7236ec0f607cb837d=5a6008526000600060006000731000000000000000000000000000000000000105620927c0f46009555a6008510360085500 15ba436321000c9c5343067ee4a6ae690f10cbdf8bd2839ccb53920beb05667a=600c60015500 e952288bfd7d359e8e6d68c34e8f374ecb9bbc6d3b31bdaceae8a92b6dcda854=60006000600060006000731000000000000000000000000000000000000001620927c0f160015500 -4a20311675b86136b91882212546df94ac5adc617567ceb81b905635606f0c80=5a6103e7527310000000000000000000000000000000000000103b6001556014600060007310000000000000000000000000000000000000103c60005160025560005460045560006000600060006001731000000000000000000000000000000000000011617530f160055560006000600060006001731000000000000000000000000000000000000011617530f26006556000600060006000731000000000000000000000000000000000000011617530f460075560006000600060006000731000000000000000000000000000000000000013617530f160085573a94f5374fce5edbc8e2a8697c15331677e6ebf0b316003555a6103e75103600a5500 eb8e9149bbe1ef10c9bb97aefb652f40040aa4b8b99e6d2bebc607cabc4a6071=601160645500 +4a20311675b86136b91882212546df94ac5adc617567ceb81b905635606f0c80=5a6103e7527310000000000000000000000000000000000000103b6001556014600060007310000000000000000000000000000000000000103c60005160025560005460045560006000600060006001731000000000000000000000000000000000000011617530f160055560006000600060006001731000000000000000000000000000000000000011617530f26006556000600060006000731000000000000000000000000000000000000011617530f460075560006000600060006000731000000000000000000000000000000000000013617530f160085573a94f5374fce5edbc8e2a8697c15331677e6ebf0b316003555a6103e75103600a5500 774633243e64cf3ebc40608cd4a5d4e63aa60c5de4c19caaccb7eea0b2ec60f6=73b94f5374fce5edbc8e2a8697c15331677e6ebf0bff00 d50f384149292a2da6a35604e0c706c197c311494ef9767a50d597f341a7171a=5a6000526000600060006000600073100000000000000000000000000000000000011861ea60f1505a6000510360015500 -fa34d1e0544d9f4d11d1839bded8f96c4357b060adab2f42dce8965629ff5198=732000000000000000000000000000000000000115ff00 6e131966e19f64076f1fb97dc8dfe75255336e26958c64ee9974266d60390329=5a6000526000600060006000600073100000000000000000000000000000000000011661ea60f1505a6000510360015500 +fa34d1e0544d9f4d11d1839bded8f96c4357b060adab2f42dce8965629ff5198=732000000000000000000000000000000000000115ff00 9c6be42daef8d1f2b41ab7e221cbaafb0e2af8d9b7d5bd9c81f98c42e505c386=5a6000526000600060006000600073100000000000000000000000000000000000011862027100f1505a6000510360025500 e3573896039e19c47ed370daf5f6d284a1825e8d8efd0c0a7a1d9c2f6825b2b3=5a60005273a94f5374fce5edbc8e2a8697c15331677e6ebf0b31505a6000510360015500 1e7aa0dd5e3e0ebb8264cc2a31098e1f40f9712dbdea4f8bbf0a274ac956a992=5a60025500 @@ -1711,20 +1711,20 @@ a564c77866beb770e7d04eb1e7b69e1f793642c45d1ceae9b048e4e8700c3554=6160a7600052616 566a2fb3788518dcdbf2b4e84b1242016e317bea37cffb8e50d7d02d940daa2b=5a6000526000600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61ea60f1505a6000510360645500 1857e39f8922c5c728afc24a2dddb22a1e865a4c101b873e6ee67f639cccac55=5a600052600c60000a6001555a600051036002555a6000526000600c0a6003555a600051036004555a60005267ffffffffffffffff60000a6005555a600051036006555a6000526fffffffffffffffffffffffffffffffff60000a6007555a600051036008555a6000527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60000a6009555a60005103600a555a600052600067ffffffffffffffff0a600b555a60005103600c555a60005260006fffffffffffffffffffffffffffffffff0a600d555a60005103600e555a60005260007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0a600f555a6000510360645500 c30b60a297bc114822c8e14df3c876c56add3a4182cf7b987477a5fbef3a6553=5a60005273c94f5374fce5edbc8e2a8697c15331677e6ebf0b3b6001555a6000510360645500 -91e8abf25e107edb430c758a636574a9856b877250a7716180702eb337fc6b0a=6000600060006000600073ee098e6c2a43d9e2c04f08f0c3a87b0ba59079d46000f1506000600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b6000f100 a19fa2b72a0cc97f6e79cad016c5e9496b86ee450244d51285ba2941cb87ce2b=5a00 +91e8abf25e107edb430c758a636574a9856b877250a7716180702eb337fc6b0a=6000600060006000600073ee098e6c2a43d9e2c04f08f0c3a87b0ba59079d46000f1506000600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b6000f100 f15d8dd53de81966c3c53f458e7c59bf8ea1a492e92c282f371ccb6b17d67336=60206000f3 -010911570642c8263ecf3e4b9962fd621db6b14ead00e43a01038c8946240fd6=600160005560008060008061271073d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d05af150 +010911570642c8263ecf3e4b9962fd621db6b14ead00e43a01038c8946240fd6=6001600055600060006000600061271073d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d05af150 6837614816f00cdf8a34c7089b92ccfb445c85d3ae39fe52254ad886233e23c6=6001600055600060206000612710f550 f2adcfff8511f649415782700392d85ad868bce9be6bf4cb8182906811455113=600160005560206000612710f050 -1069c043580db2ae345628d5f35ae22d30dd303007850a0bc1c2ef218d351591=600160005560008060008073d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d05af450 -a54dac63f58da9143c7a638921f297db39e67ce2e3ced0e395b20430d1e3d0fb=60008060008061271073d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d05af150 -38500297ffe27de5ec2ee4eaf8acff775637bb8d7f745de2a72837e045571252=60008060008073d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d05af450 -c582eff643ca49efe39fc279b4bd43c2e150e59b643c69470116f65ce1f5f7d1=6000600060006000600073dead00000000000000000000000000000000000162028488f160005573dead0000000000000000000000000000000000013f60015500 +1069c043580db2ae345628d5f35ae22d30dd303007850a0bc1c2ef218d351591=6001600055600060006000600073d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d05af450 +a54dac63f58da9143c7a638921f297db39e67ce2e3ced0e395b20430d1e3d0fb=600060006000600061271073d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d05af150 +38500297ffe27de5ec2ee4eaf8acff775637bb8d7f745de2a72837e045571252=600060006000600073d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d05af450 a335dc68decf7ac526505bee581c3669f6b925204dac27ed9a4661dce408ea2d=600060006000600073dead00000000000000000000000000000000000162028488fa60005573dead0000000000000000000000000000000000013f60015500 b0186d1f0fb83cab30598535b1d61a06b4d5b7d4f30f54ce0baa5c36b6168a5c=6025ff00 e4d13dbb224dfb3c5baa06f80b928b029ae49a326d7f4dc4931dcd2e18fe1b74=6000600060006000600073dead00000000000000000000000000000000000162028488f260005573dead0000000000000000000000000000000000013f60015500 0d3135ebc4586f22e79775adca28a939f1dad92378cc4e56b65d2748ab8ddb29=604060006040600060006000355af200 +c582eff643ca49efe39fc279b4bd43c2e150e59b643c69470116f65ce1f5f7d1=6000600060006000600073dead00000000000000000000000000000000000162028488f160005573dead0000000000000000000000000000000000013f60015500 07f57c2882d785f5e00721021ade0111a4ad7f5731d8e446856cb5f81b811b0a=600060006000600073dead00000000000000000000000000000000000162028488f460005573dead0000000000000000000000000000000000013f60015500 2fbb4625057c201a4e9d2f25d41fc8231f598df1013a01cc5a8d38747f8d9878=60206000600073a2220000000000000000000000000000000000003c60005160105573a2220000000000000000000000000000000000003b60115573a2220000000000000000000000000000000000003f6012556000600060006000600073a22200000000000000000000000000000000000061c350f260135560206000600073a2000000000000000000000000000000000000003c60005160205573a2000000000000000000000000000000000000003b60215573a2000000000000000000000000000000000000003f6022556000600060006000600073a20000000000000000000000000000000000000061c350f260235560206000600073a3000000000000000000000000000000000000003c60005160305573a3000000000000000000000000000000000000003b60315573a3000000000000000000000000000000000000003f6032556000600060006000600073a30000000000000000000000000000000000000061c350f26033556020600060006000600073a10000000000000000000000000000000000000062086470f15060005160405500 9655ae0666d5d46da6ef913c73b969ffdd36c5adb6f19bfe83c6bfbe47f1568f=6000603980601a60003960006000f560005260206000f30000fe60206000600039600051605055303b605155303f605255600060006000600060003061c350f23b605355602060006000303c60005160545500 @@ -1736,9 +1736,9 @@ e4c17df96db7198923adbb3613fb06daf08df364dd4eaefea23f979186b7924d=600b60505500 156633606cec03dd7aa5cbba790a1391e1d3cf2eaecfad59f006d7480258ee68=6020600060006000600073deadbeef00000000000000000000000000000000620249f0f1506020600060003e6000516000556020600060006000600073aeadbeef00000000000000000000000000000000620249f0f1506020600060003e60005160015500 82651f83a3f20bc5f22127f2f83e9e046389754ea52cbffeff2615a12d326cf5=73deadbeef000000000000000000000000000000013f60005260206000f300 7d479463e5816548703f295fb2cc82613af84454eb6d339fe3ed819215d2212b=73deadbeef000000000000000000000000000000013b60005260206000f300 -62d393661b8ff460bab64febc392284589fd4a4cff568e32c3c13de085552b74=73deadbeef000000000000000000000000000000013b60005260206000f300 fd3a9c1e19e63f147b68a35c11172ee7136a4310e5d938cf45faf4b24146e5e9=6020600060006000600073deadbeef00000000000000000000000000000000620249f0f2506020600060003e6000516000556020600060006000600073aeadbeef00000000000000000000000000000000620249f0f2506020600060003e60005160015500 68ff1831256501f492cf7e85526c3b0e356d95fa9ca755db1f3714349b04f050=73deadbeef000000000000000000000000000000013f60005260206000f300 +62d393661b8ff460bab64febc392284589fd4a4cff568e32c3c13de085552b74=73deadbeef000000000000000000000000000000013b60005260206000f300 b6b7e12f5a976593684d319b348150b69549ce23fb21eaaf5b71a2e7f8730bf6=6020600060006000600073deadbeef00000000000000000000000000000000620249f0f1506020600060003e6000516000556020600060006000600073aeadbeef00000000000000000000000000000000620249f0f1506020600060003e60005160015500 e483c6077f21978e25ee7e35054786b2a769babf998f275b37370e99df528c8d=73deadbeef000000000000000000000000000000013f60005573deadbeef000000000000000000000000000000013b60015560006000600060006103e873deadbeef0000000000000000000000000000000162010000f15073deadbeef000000000000000000000000000000013f60025573deadbeef000000000000000000000000000000013b6003550000 45b5782e03b97c0637e4926e532285d223e5358cdd8b2d9b1c0558f5496a5ab3=600060006000f05061123460005500 @@ -1800,12 +1800,12 @@ ee7da33709c19bd962eee68389c89314f6ec80137d3441cb78d6c111a4027308=74600c600055666 509b43ac8411dbf966c230b9f8dd35c152414b7be1c848a8bebb1da34cb58353=3060025560206000600039602060006000f000 c21ce7b52ab574f79ada30926ebda6e9f60641c8985aafee8dbaae36f687c35f=602060006000f000 9697d016645564aeb2639c2d88c07e6954b66c78b4e4319d1030e7591481b591=600980601160003960006001f0500000fe621122336000550000 -b4387f4c142f0127da220e06eacce7c746b6bbee86fcab846173ebfb1f108f9b=60156000526020602060206000600073b94f5374fce5edbc8e2a8697c15331677e6ebf0b611b58f15060005160005560205160015560406000f300 3ee127d91eea32240afe65fc76abd217b294a9645f1bb8dcbddda48bb07e9d57=60003560030260005260206000f300 +b4387f4c142f0127da220e06eacce7c746b6bbee86fcab846173ebfb1f108f9b=60156000526020602060206000600073b94f5374fce5edbc8e2a8697c15331677e6ebf0b611b58f15060005160005560205160015560406000f300 e3ec67bda76132618ccf43c96cb0cf44bcf28757f2c8595217290fd11e4a7988=60156000526001601ff300 b02b73e0457e3cdff66bcdce1be234608caa8fb50cf3cf31086b54fe03ff5eab=6001601f6001601e600073b94f5374fce5edbc8e2a8697c15331677e6ebf0b6107d0f1506000516000556002601ef300 -df423a926a6db526900013c97c1e27cec738a0c18a441b152adafb975c79e1c2=60006000a000 8902038f59677d0f2f76aec116db122a94b1b364cdadafb02af511ab035e30eb=60006000600060006017730f572e5295c57f15886f9b263e2f6d2d6c7b5ec66103e8f160005500 +df423a926a6db526900013c97c1e27cec738a0c18a441b152adafb975c79e1c2=60006000a000 f6f233f954759153a90af5be5b90fdec95b9df6818174f60f0a0497bb0291117=7faabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd60005260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa000 56e86c9da13b58772b3e014e61121aae77642ace0a6ee9bb3b7feb9496581def=7faabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd6000527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001a000 49ba9ec8d6537b838d9b4e3a3dc60731eb96782bb565e84a530856908c296d52=7faabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd60005260006001a000 @@ -1855,8 +1855,8 @@ d04d9233925cf1a3991f0848c9c7fe98a2984dd8dfdf09f76309330759eb2f0b=7faabbfffffffff 399fe86cade164b82ac638f669b21249d01d6a52bb537fe65152c0737457eddf=63dead60a76000526101006000f300 ac71ea1958b7e077fbfc3c8e16602315e0d77e77e36856e7d150c159266858da=63dead60a76000526101006000fd00 46ce285cc3af557e8194e718100c3ae4c9c82b85b48cd9c7a9b891c902e2bb86=60043561012052602435610140526360a760a760005261010060006000600060006101205173ccccccccccccccccccccccccccccccccccccccc00161014051f16101005260005160005560003d116057576000506060565b602060006101603e5b6101605160015500 -8ccf19d65aab653a0aa9489691349aec03a45fa6cfc046110f327dec627084e8=630fffffff630fffffff630fffffff630fffffff60007310000000000000000000000000000000000000016707fffffffffffffff200 7d73b53969b64e0a95ec98b7562644ccd76661044d654aa5a5790dd8428bb4c6=60005460010160005500 +8ccf19d65aab653a0aa9489691349aec03a45fa6cfc046110f327dec627084e8=630fffffff630fffffff630fffffff630fffffff60007310000000000000000000000000000000000000016707fffffffffffffff200 064c87204cf39627b92357a24043959f79dbfac6a8f962672c5790969e86aff2=63ffffffff63ffffffff63ffffffff63ffffffff60007310000000000000000000000000000000000000016707fffffffffffffff200 584c831d0ad95dc1b1a2fa111cd90fb80e843c25b95f052ad877021b62e171c7=67ffffffffffffffff600067ffffffffffffffff600060007310000000000000000000000000000000000000016707fffffffffffffff2506fffffffffffffffffffffffffffffffff60006fffffffffffffffffffffffffffffffff600060007310000000000000000000000000000000000000016707fffffffffffffff2507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600060007310000000000000000000000000000000000000016707fffffffffffffff25067ffffffffffffffff67ffffffffffffffff67ffffffffffffffff67ffffffffffffffff60007310000000000000000000000000000000000000016707fffffffffffffff25067ffffffffffffffff67ffffffffffffffff67ffffffffffffffff67ffffffffffffffff60007310000000000000000000000000000000000000016707fffffffffffffff2507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60007310000000000000000000000000000000000000016707fffffffffffffff2506fffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff60007310000000000000000000000000000000000000016707fffffffffffffff200 5eedf45cf7035da00c6c38686d1b3773cb6d615a2826065c6c608906813459d0=600060006000600060007310000000000000000000000000000000000000016707fffffffffffffff250630fffffff6000630fffffff600060007310000000000000000000000000000000000000016707fffffffffffffff25063ffffffff600063ffffffff600060007310000000000000000000000000000000000000016707fffffffffffffff2506000630fffffff6000630fffffff60007310000000000000000000000000000000000000016707fffffffffffffff250600063ffffffff600063ffffffff60007310000000000000000000000000000000000000016707fffffffffffffff250600067ffffffffffffffff600067ffffffffffffffff60007310000000000000000000000000000000000000016707fffffffffffffff25060006fffffffffffffffffffffffffffffffff60006fffffffffffffffffffffffffffffffff60007310000000000000000000000000000000000000016707fffffffffffffff25060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60007310000000000000000000000000000000000000016707fffffffffffffff200 @@ -1879,23 +1879,23 @@ fe915acef64713ed78a436609d597a9515a122a544222751638161e50026ea40=600164fffffffff f2e3f4df6f5b48738fe1a4dbd5aea8f72b4842797a0b5d207977b9654e8573fd=6001623fffff5200 0cae2e21896571f1d0482a40f66e06729ad32ae86e9440c19e452d563d593597=600167ffffffffffffffff5200 47a126462863e1fc487df67bab3df2089abfc71aa4b523f7b6782ae90825600b=60005063ffffffff5067ffffffffffffffff506fffffffffffffffffffffffffffffffff507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5000 +386fe076c477ec967131a49281dbba375b51925bb4430042079e8915798b9348=67ffffffffffffffff6000f300 +6a6e213b3e7eb57ceb0bb22c17e461767dcb28d94c1b903eb304dcf95bb41c61=6000630ffffffff300 +78905e9e7a45f8598e6b7867c3adc15240d24277e35d8c16f2db09cdc1544975=60006d0ffffffffffffffffffffffffffff300 +8408466c1816cc59b6b9fcec7e37aea7164604a0df94e1e30831c9423886c817=6d0fffffffffffffffffffffffffff6d0ffffffffffffffffffffffffffff300 +4c1bdd3575d4730e3f56181077c12ee35942e531c8bf25e5a917af08eae5539d=630fffffff6000f300 +00d5de551ee7a5a4b93cfcfd0a3e939c901ba44f1121cb499f3f906f32307c78=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000f300 +89a733741df289bcde6ceec5cf1bea1bd3c58eadc58c72fed47657d554a23ecb=60006000f300 +d679f961d83db60ac5f3be30a471dda0a7670a8b011a9e6390e8cca98bec5b62=600067fffffffffffffffff300 0b89d04832122a7a3e9bff16f2c29dcd664ae3018605a456f43030cff862c3e6=60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff300 +3eb1e695975e52e7dd4f7afffe8b02fca385f966034c7be4ed277d9c596208ee=600060006000600060007310000000000000000000000000000000000000016707fffffffffffffff1600155600060006000600060007310000000000000000000000000000000000000026707fffffffffffffff1600255600060006000600060007310000000000000000000000000000000000000036707fffffffffffffff1600355600060006000600060007310000000000000000000000000000000000000046707fffffffffffffff1600455600060006000600060007310000000000000000000000000000000000000056707fffffffffffffff1600555600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600655600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600755600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600855600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600955600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600a55600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600b55600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600c55600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600d55600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600e55600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600f55600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff160105500 eed3ff87a0d0c61b833776c14ef3fba65cc145a1bcc2a914d4226781c9ec623c=63ffffffff63fffffffff300 -d679f961d83db60ac5f3be30a471dda0a7670a8b011a9e6390e8cca98bec5b62=600067fffffffffffffffff300 -89a733741df289bcde6ceec5cf1bea1bd3c58eadc58c72fed47657d554a23ecb=60006000f300 -4c1bdd3575d4730e3f56181077c12ee35942e531c8bf25e5a917af08eae5539d=630fffffff6000f300 -b4d880e59c7c17bc74784c186bde67430d3ddb71a778a98eda8c7122d56b653c=6d0fffffffffffffffffffffffffff6000f300 -6a6e213b3e7eb57ceb0bb22c17e461767dcb28d94c1b903eb304dcf95bb41c61=6000630ffffffff300 4134e61bba4b29954deb6977e3c14a40dafd92ddb73461f86413c2e18d0e4904=67ffffffffffffffff67fffffffffffffffff300 -00d5de551ee7a5a4b93cfcfd0a3e939c901ba44f1121cb499f3f906f32307c78=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000f300 -78905e9e7a45f8598e6b7867c3adc15240d24277e35d8c16f2db09cdc1544975=60006d0ffffffffffffffffffffffffffff300 +f1cc4f74ba159815e4fd2c1e19cc1dbac964dda4b45bf47ccfe0e029852358ef=63ffffffff6000f300 46a54a35478ad25df50ebc2b6f7550b25b7451b343fb8c1e08b141b4ce751547=600063fffffffff300 -386fe076c477ec967131a49281dbba375b51925bb4430042079e8915798b9348=67ffffffffffffffff6000f300 +b4d880e59c7c17bc74784c186bde67430d3ddb71a778a98eda8c7122d56b653c=6d0fffffffffffffffffffffffffff6000f300 317476e600a0d257f84d0f46262ccfbc9acd728b996b9e560551ac22c4900860=630fffffff630ffffffff300 -3eb1e695975e52e7dd4f7afffe8b02fca385f966034c7be4ed277d9c596208ee=600060006000600060007310000000000000000000000000000000000000016707fffffffffffffff1600155600060006000600060007310000000000000000000000000000000000000026707fffffffffffffff1600255600060006000600060007310000000000000000000000000000000000000036707fffffffffffffff1600355600060006000600060007310000000000000000000000000000000000000046707fffffffffffffff1600455600060006000600060007310000000000000000000000000000000000000056707fffffffffffffff1600555600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600655600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600755600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600855600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600955600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600a55600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600b55600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600c55600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600d55600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600e55600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff1600f55600060006000600060007310000000000000000000000000000000000000066707fffffffffffffff160105500 ff821a70b5e78b13b36cf3e507408b602fdefa2af105e07b7f8ab49f1dd2ad3a=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff300 -8408466c1816cc59b6b9fcec7e37aea7164604a0df94e1e30831c9423886c817=6d0fffffffffffffffffffffffffff6d0ffffffffffffffffffffffffffff300 -f1cc4f74ba159815e4fd2c1e19cc1dbac964dda4b45bf47ccfe0e029852358ef=63ffffffff6000f300 9af366a03b359821a8029b03699c4ab4e3bdc8ed02cab79a9cdf4a36fe915bac=6000545063ffffffff545067ffffffffffffffff54506fffffffffffffffffffffffffffffffff54507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5400 883a7f1d89643b1d3f3d73e32f11083eec85093215e3195184dce5cd758a89d1=600163ffffffff55600167ffffffffffffffff5560016fffffffffffffffffffffffffffffffff5560017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5563ffffffff60205567ffffffffffffffff6040556fffffffffffffffffffffffffffffffff6080557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101005500 ff631084ef91e3ef02af7701b65c7849f05e96278f62e502bce19f156459080b=6401773594005160015500 @@ -1905,8 +1905,8 @@ e2b739e3e9aee02a6540cdfc27b5b6cc26ab2c574857b23db6235f224f676592=600160005263fff 9e915990c251fb9a282d74e6fbaea9bc9d69565ddb7da2592dd0ba2b8326b408=63ffffffff6000f300 e82976f19147f4b453ca8b58f9be648e9c40de237fa944ce6cce004794db8a40=630fffffff630fffffff630fffffff630fffffff7310000000000000000000000000000000000000016707fffffffffffffffa00 c4685fdab5c0cb3684ee14a4f16fb05bdb7706f8122aa0fd95ecbd197894ca0e=63ffffffff63ffffffff63ffffffff63ffffffff7310000000000000000000000000000000000000016707fffffffffffffffa00 -266d50413b3526c6d6db3488bbe86c35500a9fca848732b9704a5a6573083583=67ffffffffffffffff600067ffffffffffffffff60007310000000000000000000000000000000000000016707fffffffffffffffa506fffffffffffffffffffffffffffffffff60006fffffffffffffffffffffffffffffffff60007310000000000000000000000000000000000000016707fffffffffffffffa507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60007310000000000000000000000000000000000000016707fffffffffffffffa5063ffffffff63ffffffff63ffffffff63ffffffff7310000000000000000000000000000000000000016707fffffffffffffffa5067ffffffffffffffff67ffffffffffffffff67ffffffffffffffff67ffffffffffffffff7310000000000000000000000000000000000000016707fffffffffffffffa506fffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff7310000000000000000000000000000000000000016707fffffffffffffffa507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7310000000000000000000000000000000000000016707fffffffffffffffa00 f1e2e5150310bd12b688199b146ce34bf0e03758d5c911f0069a8cdcfbcb4292=60005460010160005200 +266d50413b3526c6d6db3488bbe86c35500a9fca848732b9704a5a6573083583=67ffffffffffffffff600067ffffffffffffffff60007310000000000000000000000000000000000000016707fffffffffffffffa506fffffffffffffffffffffffffffffffff60006fffffffffffffffffffffffffffffffff60007310000000000000000000000000000000000000016707fffffffffffffffa507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60007310000000000000000000000000000000000000016707fffffffffffffffa5063ffffffff63ffffffff63ffffffff63ffffffff7310000000000000000000000000000000000000016707fffffffffffffffa5067ffffffffffffffff67ffffffffffffffff67ffffffffffffffff67ffffffffffffffff7310000000000000000000000000000000000000016707fffffffffffffffa506fffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff7310000000000000000000000000000000000000016707fffffffffffffffa507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7310000000000000000000000000000000000000016707fffffffffffffffa00 c69e6a67ecbf69e8d1d465b762944371857db02d1485df69f2ec8ea24d98fccc=60006000600060007310000000000000000000000000000000000000016707fffffffffffffffa50630fffffff6000630fffffff60007310000000000000000000000000000000000000016707fffffffffffffffa5063ffffffff600063ffffffff60007310000000000000000000000000000000000000016707fffffffffffffffa506000630fffffff6000630fffffff7310000000000000000000000000000000000000016707fffffffffffffffa50600063ffffffff600063ffffffff7310000000000000000000000000000000000000016707fffffffffffffffa50600067ffffffffffffffff600067ffffffffffffffff7310000000000000000000000000000000000000016707fffffffffffffffa5060006fffffffffffffffffffffffffffffffff60006fffffffffffffffffffffffffffffffff7310000000000000000000000000000000000000016707fffffffffffffffa5060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7310000000000000000000000000000000000000016707fffffffffffffffa00 02b70170ddbf89061c6a8c7bb91a023ca494b643c0d63ec0efa75ea1c07421ac=6101206000f300 3c4e35c17317d029fa326f619b7d5fa854bfc5c5d207e3b040d4e284c6c47133=6000600055600035602035f300 @@ -1955,15 +1955,15 @@ b2f34f8fd7351af42946f10cbcbaceabb764e90e0f230d4bde11fa12195ad688=602a61f9e053596 3128fa92bfdd224510dfdf841a34b464559eb1ab297d8f5d4c68c16514e1aae6=602a61f9df535960005500 936f52989634c9dc92d33ba901a5ac808bc9acd93db45d9b7eba773c496490fb=602a61f9de535960005500 d4f7e542342692e64b86707702652e79e04b253edfa91cdd1a6f0bbd3efbb0d8=602a61f9ff535960005500 -2008b8b4aea344d1383a53ece06a0db6166b4133f10877fdf922b9998cce5731=600460005b600f811015601f576011600182010281536001810190506004565b50600051600055600a6002600a600080855af150600051600155600a600060203e60205160025550 +2008b8b4aea344d1383a53ece06a0db6166b4133f10877fdf922b9998cce5731=600460005b600f8110156020576011600182010281535b6001810190506004565b50600051600055600a6002600a60006000855af150600051600155600a600060203e60205160025550 c367c64d90ffa50458cf755dc5d10f83bc2d9d8250058d3d05b943468aefa07e=366000600037596000f300 7012bc42bb8026cf166f7326ed246b0f95e4f751c2e5cf9c08dad4f0d7047b2e=620100005160015500 0898f33abc8db9ff3da67ce0a69db61613ce641395635c11fa1517790e7d6dc7=6101005160015500 a9e5d35726cf7d3f99d83c391100c936092c87831b690d033d14da5db51bf170=61100060002050 -9ef622c9b42196fe0519fda0941f998d4d99a6a175c9005b7097c2903217b31d=61100060008037 -28913a4f566a2dc5e3ac457a4eaeccbf06bbefa53f18a652b15e10dfb271bfb9=61100060008039 -5e2b9eeb0294a6acad85d6c1e76bdb6d37df823ec31f36660d850cd903ae67ff=611000600080303c -55c5e2cef947d4612f1729e73b33e00c5ff964879895947e26937d916a5e939b=6020600060206000806201113e5af150601060006110003e +9ef622c9b42196fe0519fda0941f998d4d99a6a175c9005b7097c2903217b31d=6110006000600037 +28913a4f566a2dc5e3ac457a4eaeccbf06bbefa53f18a652b15e10dfb271bfb9=6110006000600039 +5e2b9eeb0294a6acad85d6c1e76bdb6d37df823ec31f36660d850cd903ae67ff=61100060006000303c +55c5e2cef947d4612f1729e73b33e00c5ff964879895947e26937d916a5e939b=602060006020600060006201113e5af150601060006110003e e499501e5bafbd1c361c20dc5a05e40c7d486d5ab46cf9e2cfd59346786b9b38=7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2060005260206000f3 13d0233a2bd697f7398d45c35017fc346a4b6f6b9accef673effad9d91a5c92f=6110005150 10cfa4256c7bf2c8801020effb7b4d90f16681520073bf48f4b0aaab4c3b70b8=60ff61100052 @@ -1976,11 +1976,11 @@ c15fc1157326db732bdd5e728ae4f8c71f30a379e40db53a60cd1a63e6b1a89b=600460036002600 b439c2fe865f899a9b489a5f79a04101534669e9d6b7051df6f16083dd5c160e=6020620100006000f050 b4ba69d5a16b5e51f0e17fbfd2225ea8c2eb959a74162c79e3b85783dadd9fde=615a176020620100006000f550 1b2290979cf403b8281ad39f910906c10e8953e8048d40dc38623dff8007c8b9=602062010000f3 -2ecc7534ab3ed062c9e731421cad45f819bb031262794a81addffc46def2fa03=6000806000620100006000620111f15af150 -26210b12ec0faac7a4d6c0d0b55aee7569965df3ff695be9895e27f6e3d7f533=6000806000620100006000620111f15af250 -d93eddd86ce9004dfd9651ee2531a49a39dd41e2e4335266970443a8c3fd841a=600080600062010000620111f15af450 -187f3a42d83cff10533684fecfc325133b872195c7a3b8d777b2a70c670e2ad6=600080600062010000620111f15afa50 -74137ced833d5efd3e225bc476c503830692634aa1932401210e2f8fb533e2fb=600435602435600080600080600086620100000186f16000555050 +2ecc7534ab3ed062c9e731421cad45f819bb031262794a81addffc46def2fa03=600060006000620100006000620111f15af150 +26210b12ec0faac7a4d6c0d0b55aee7569965df3ff695be9895e27f6e3d7f533=600060006000620100006000620111f15af250 +d93eddd86ce9004dfd9651ee2531a49a39dd41e2e4335266970443a8c3fd841a=60006000600062010000620111f15af450 +187f3a42d83cff10533684fecfc325133b872195c7a3b8d777b2a70c670e2ad6=60006000600062010000620111f15afa50 +74137ced833d5efd3e225bc476c503830692634aa1932401210e2f8fb533e2fb=6004356024356000600060006000600086620100000186f16000555050 caf9facbed862fe613b3320022ed06b17a8dca8eacc2c73de4713f009ae469dd=5a6000526000600060006000600173c94f5374fce5edbc8e2a8697c15331677e6ebf0b61ea60f26001555a6000510360645500 c7a6d05c0caf2c88e24e84509b2b882162e24a3c66f8e17d5f0a9b93a7ee5e4b=5a6000526000600060006000600173c94f5374fce5edbc8e2a8697c15331677e6ebf0b61ea60f16001555a6000510360645500 4366d37cfc4ffc877c93c4b6b4c5c7334203a7532f95d17ed0b69f11c4817573=5a600052600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61ea60f46001555a6000510360645500 @@ -1993,9 +1993,9 @@ f01c97341686169e115128bc4a55d9714e85d3f05bb3c2c5764afeb983e04c6f=600154506002545 044661348c230fea4bf2ff442a4422738b9b9ebbef430f09273aec57fec16952=600154506002545061ffff60020a600a553031600b5560006001556000600255600060035560006004556000600555600060065500 b1ec84eb90eb693cdbff9a5ac86f8f729e3ef784bf36384c730ba39d1b10dda0=73ddddddddddddddddddddddddddddddddddddddddff cd8bcc7a2b45eedecfdb09569a86e3148578de2e210b26bb4cf3f862bbd3a062=6000806000558060015580600255806003558060045580600555806006558060075550 -2e8d8a457543cf940ef631ca98fcfd11c5cb1119d2e6d6eda3efb2a8a1c5d674=60008055 -4ded1eb002cb054874f2ea1996edd56bd3da577953433c9aaa702976cd608321=5a6016526001600a556000600060006000600073aaae7baea6a6c7c4c2dfeb977efac326af552aaa600035f1600b55600060015560006002556000600355600060045560006005556000600655600060075560006008555a6016510360175500 +2e8d8a457543cf940ef631ca98fcfd11c5cb1119d2e6d6eda3efb2a8a1c5d674=6000600055 f1e3bd42875da1d50d5f6ee47b7c3a81cdfa546ce482edf6cd3e276d661e27f4=73095e7baea6a6c7c4c2dfeb977efac326af552d87ff00 +4ded1eb002cb054874f2ea1996edd56bd3da577953433c9aaa702976cd608321=5a6016526001600a556000600060006000600073aaae7baea6a6c7c4c2dfeb977efac326af552aaa600035f1600b55600060015560006002556000600355600060045560006005556000600655600060075560006008555a6016510360175500 a6524cc4b8de915b5b279f9b2f6591f15af39ee9f677579cdbbebd9eeba36913=600060015500 6c0bee772d21bb9572e13fba061d867af488ce4c4799703b5c1953648cc1cbd7=6000600060006000600073aaae7baea6a6c7c4c2dfeb977efac326af552aaa61157cf160005500 b6a870e429c801eacf3617f4a0c5705161fc4b18909b049f784cc616cd3d6001=6000600060006000600073aaae7baea6a6c7c4c2dfeb977efac326af552aaa611770f160005500 @@ -2007,23 +2007,23 @@ f138dcfed5bbdbd7e4b21866ccb59e0ee6f66d493dbc9f4445c64b313aeed11e=60aa60006000600 d5b367e8600704385458fb4001e9f55a8fa2f57d7f861f0f4bfc53162c24de37=60206000600060006000730aabbccdd5c57f15886f9b263e2f6d2d6c7b5ec6640900000000f150600e80603c60003960006000f0503d6000550000fe6211223360005260206000f30000 c3aaa46b1bcad7e65b2f93c8d62b8ddf023c2217dae18d3542b31fa445897e9b=60206000600060006000730aabbccdd5c57f15886f9b263e2f6d2d6c7b5ec6640900000000f150602060006000600065ffffffffffff730aabbccdd5c57f15886f9b263e2f6d2d6c7b5ec6640900000000f1503d60005500 ecd1d96f20e09e6de52af48896e1e955214fdfc84299aa74e6f22bc31818e538=60006000600060006000730aabbccdd5c57f15886f9b263e2f6d2d6c7b5ec6640900000000f150600e80603c60003960006000f0503d6000550000fe6211223360005260206000f30000 -161997d4cd71eeac3a1229ed1334818012302b0e4fde1db26818bd6a499c7acb=6000356160a7600052806000f3 -708e124a9dd48d3f9fed7de053464e4a73b23a3b11270d7dd3ecd3078915663e=6000356160a7600052806000fd -8fc69ebe56dd2a6b658881e1287c49a7966486390dc49bea82ed4228f571c71b=60003560203560403581600052602060006020600080875af1503d6000553060015500 -ca4a5d2e59e72586a296c5928c1ad18abd1d5ee9dcee43c3bd4c6c6472a23025=60003560203580600052602060006020600080865af150307846455050 -293709612af3fad0c2119caac70779499e03a1e504e1a316b6e0161a77b9e34e=60043560243560443560006100cc565b6000607f60005382600152606060215360006022536052602353606060245360ff60255360606026536020602753606060285360206029536060602a536000602b536060602c536000602d53607f602e5381602f52605a604f5360f1605053606060515360206052536051605353606060545360006055536055605653603d605753606060585360016059536055605a536000605b53605c905092915050565b60006100bb838361000f565b60fe60018203538091505092915050565b8360f08114610138576111f0811461014f5760f58114610166576111f581146101805760f1811461019a576111f181146101b85760f281146101d7576111f281146101f55760f48114610214576111f481146102315760fa811461024f576111fa811461026f57600080fd5b610142838561000f565b915081600080f050610289565b61015983856100af565b915081600080f050610289565b610170838561000f565b9150615a1782600080f550610289565b61018a83856100af565b9150615a1782600080f550610289565b83600052826020526020610100604060008062ca11ed5af150610289565b83600052826020526020610100604060008063bad0ca115af150610289565b83600052826020526020610100604060008062ca11ed5af250610289565b83600052826020526020610100604060008063bad0ca115af250610289565b836000528260205260206101006040600062ca11ed5af450610289565b836000528260205260206101006040600063bad0ca115af450610289565b83600052826020526020610100604060006557a700ca11ed5afa50610289565b836000528260205260206101006040600063bad0ca115afa505b503d60005560005160015550505050 +161997d4cd71eeac3a1229ed1334818012302b0e4fde1db26818bd6a499c7acb=6000356160a7600052806000f350 +708e124a9dd48d3f9fed7de053464e4a73b23a3b11270d7dd3ecd3078915663e=6000356160a7600052806000fd50 +8fc69ebe56dd2a6b658881e1287c49a7966486390dc49bea82ed4228f571c71b=6000356020356040358160005260206000602060006000875af1503d6000553060015500505050 +ca4a5d2e59e72586a296c5928c1ad18abd1d5ee9dcee43c3bd4c6c6472a23025=6000356020358060005260206000602060006000865af150307846455050 +293709612af3fad0c2119caac70779499e03a1e504e1a316b6e0161a77b9e34e=60043560243560443560006100ce565b6000607f60005382600152606060215360006022536052602353606060245360ff60255360606026536020602753606060285360206029536060602a536000602b536060602c536000602d53607f602e5381602f52605a604f5360f1605053606060515360206052536051605353606060545360006055536055605653603d605753606060585360016059536055605a536000605b53605c90505b92915050565b60006100bc838361000f565b60fe6001820353809150505b92915050565b8360f0811461013f576111f081146101575760f5811461016f576111f5811461018a5760f181146101a5576111f181146101c45760f281146101e4576111f281146102035760f48114610223576111f481146102405760fa811461025e576111fa811461027e5760006000fd610298565b610149838561000f565b91508160006000f050610298565b61016183856100b0565b91508160006000f050610298565b610179838561000f565b9150615a178260006000f550610298565b61019483856100b0565b9150615a178260006000f550610298565b8360005282602052602061010060406000600062ca11ed5af150610298565b8360005282602052602061010060406000600063bad0ca115af150610298565b8360005282602052602061010060406000600062ca11ed5af250610298565b8360005282602052602061010060406000600063bad0ca115af250610298565b836000528260205260206101006040600062ca11ed5af450610298565b836000528260205260206101006040600063bad0ca115af450610298565b83600052826020526020610100604060006557a700ca11ed5afa50610298565b836000528260205260206101006040600063bad0ca115afa505b503d60005560005160015550505050 bdf500c86f231800385106c4eab9f4a9e0ec938f514e501fc7d35bb7683fac51=602380601560003960006000f0503d6000550000fe62112233600052602060006020600060006004619000f1503d60005560206000f30000 b051a2695de61dc1f8fe6c50384ffabd135e0c645c1fccdee63eb37c18ad7741=36600060003760206103e8366000600060055af26001556103e8516002553d60035500 f8f5b19526c9d84bc95117b16831037cb97126b1444a88bb23ec93ceb035aba7=600780601d60003960006000f0506000600060003e60006000550000fe60016000550000 87717fca55e893cef3f3914ec90aaaba3bfc968d16f510da898ed603d52d8a1c=69600260005260206000fd600052600a60166000f0503d6000556020600060003e60005160015500 28ad3e121d9589a67147605913b360a2e0d50fccfab6b2ee0465931f34c941d8=600060006000600060007310000000000000000000000000000000000000026000f2506020600060003e60005160005500 758e92cde37c427e1b07b24d2ef29eedf1b4d7d751fdc6a810f72b7a0778c419=6000600060006000731000000000000000000000000000000000000002612710f4506020600060003e60005160005500 -dd3bbb7ecc8bfd78bf710b1255cf3b301b0a756ab260ec6d876cc0307cd36e7e=60006000600060007310000000000000000000000000000000000000026000fa506020600060003e60005160005500 00c441f2b4e2a23fda7eef407948ba235b74a414dff979b1561064945069f5b8=3360005260206000f300 +dd3bbb7ecc8bfd78bf710b1255cf3b301b0a756ab260ec6d876cc0307cd36e7e=60006000600060007310000000000000000000000000000000000000026000fa506020600060003e60005160005500 cd446ca1ba0c4d0e1eb1ec088cc54e93e9612e3d4c9c338fe91af58937f70b8f=600060006000600073100000000000000000000000000000000000000261ea60fa506020600060003e60005160005500 8adbfbd72997442672335696caa60cf9b9b8e01e77de1376075c8d4e1c9a5717=3360005260206000fd00 -2ced4da970e939336a948595b4f5cce901ea7d5ee9d5d7ff7b7e36681b548386=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60005260206000f300 105560c2675c8add352fd3988fb3b2102447185de01fda631f740ca38f5cbc6b=6000600060006000600073100000000000000000000000000000000000000261ea60f2506020600060003e60005160005500 +2ced4da970e939336a948595b4f5cce901ea7d5ee9d5d7ff7b7e36681b548386=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60005260206000f300 c9da6b2267bf1df2805231660fe98266cfe9d714998e507390e60330f3263f0d=600060006000600073100000000000000000000000000000000000000261ea60f4506020600060003e60005160005500 5060f04aaabe7f06a6359e3a1f732141d3dee099d8604a7d96bd47f04d142e2d=60006000600060006000730aabbccdd5c57f15886f9b263e2f6d2d6c7b5ec6640900000000f1506020600060003e60005160005500 bc41a1d08d89f9c286a585d00ab125c327a6af22694673d7fe8f7d8b40d7e990=6000600052596000526002806028600051396000516000f0506020600060003e60005160005500fe0000 @@ -2046,8 +2046,8 @@ fe0af8e75cf2e035b8b23bfbed37332c3925a20a60bca0e51fb94cc4ccfc0e24=602060006000600 f35e49c73c0a888b6c0e443c097739497fafe4035143a02e35447272c4653ac6=6000600060006000600073100000000000000000000000000000000000000261ea60f2503d60005500 1b5310e67a245b79d0ab8131e41c822ee4e1ce46448d8b473dbcf074e339bff6=600060006000600073100000000000000000000000000000000000000261ea60f4503d60005500 185f20ff61caf8d7018cb183bbedf3679658fb8c5ed5af5adbeefbade8144956=3360005260146000f300 -61ad6db59417ebd7b09a5c7831d035277b13ed9ccaf902dc554911285e0f7683=600060006000600061c350731f572e5295c57f15886f9b263e2f6d2d6c7b5ec66001f1503d60005500 9ce395ea9d8983ea96b855c7295a9fb0eb0765cf768bc7c8e6bfaf6570e82896=600060006000600061c3506001600af150600160015500 +61ad6db59417ebd7b09a5c7831d035277b13ed9ccaf902dc554911285e0f7683=600060006000600061c350731f572e5295c57f15886f9b263e2f6d2d6c7b5ec66001f1503d60005500 4d421b7cf93e7f40f00c1550a5a230f35f398e061f5bfd4d5866a8b807da62c0=600e80601560003960006000f0503d6000550000fe6211223360005260206000f30000 3e8e081bbde4c7bceffb768abbaef57dffec021049d0b152ed1aa89a645184eb=3d60005500 840e4db4d3b170831b6679c863c2a54133deaa2ece07122ae5ef07f1634252f9=6000600060003e60005160005500 @@ -2056,11 +2056,11 @@ e100f08670c1c78b59351c33526f8d7eb4e4abeaace4bde8f8f9a65e826d905f=600260016000030 518779d7b59baf1ff9c3c45cff398e12ae2d88511da81ec16f9a0dd9e8b32c6d=600c600060006000600073194f5374fce5edbc8e2a8697c15331677e6ebf0b62030d40f1506000516000556000600052600c60006000600073194f5374fce5edbc8e2a8697c15331677e6ebf0b62030d40f4506000516001556000600052600c60006000600073194f5374fce5edbc8e2a8697c15331677e6ebf0b62030d40fa506000516002556000600052600c600060006000600073194f5374fce5edbc8e2a8697c15331677e6ebf0b62030d40f2506000516003556000600052600c600060006000600073294f5374fce5edbc8e2a8697c15331677e6ebf0b62030d40f1506000516004556000600052600c60006000600073294f5374fce5edbc8e2a8697c15331677e6ebf0b62030d40f4506000516005556000600052600c60006000600073294f5374fce5edbc8e2a8697c15331677e6ebf0b62030d40fa506000516006556000600052600c600060006000600073294f5374fce5edbc8e2a8697c15331677e6ebf0b62030d40f25060005160075500 ff06284e0407bdc49c5eef8b55970f90a62e18cdf210a830c4f834f91a71f994=7f11223344556677889910111213141516171819202122232425262728293031326000527f333435363738394041424344454647484950515253545556575859606162636460205260406000f300 a4cb6609f02e609855d0ae4469bc0c2dc008e0b6b77906b9622eb50e147b4a22=7f11223344556677889910111213141516171819202122232425262728293031326000527f333435363738394041424344454647484950515253545556575859606162636460205260406000fd00 -a803bfe42bdb99fe3090d66eefe6c6992f5677953c520ec7ece5b5a4b8b928db=6000357f1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201ff60005280600055806000f3 -9fb65906349b75182a0aea5e5020ca92c4bff64eba948b986bb4ba18caf322de=6000357f1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201ff60005280600055806000fd -0f73f356d63d54c487e160fe69035f674a815e71913d266052f8b5a35b4e2b64=6004356024356044356064358360005261010060006020600080855af182846101003e61c0de3b60200160008061c0de3c3660200160008037386020016000803961dead6000555050505050 -47513d8ba80f62468bedeb52e4a41afafa57013079eb37939861f95d5c98f578=6001600054016000556000600060006000600073a0000000000000000000000000000000000000005af100 +a803bfe42bdb99fe3090d66eefe6c6992f5677953c520ec7ece5b5a4b8b928db=6000357f1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201ff60005280600055806000f350 +9fb65906349b75182a0aea5e5020ca92c4bff64eba948b986bb4ba18caf322de=6000357f1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201ff60005280600055806000fd50 +0f73f356d63d54c487e160fe69035f674a815e71913d266052f8b5a35b4e2b64=600435602435604435606435836000526101006000602060006000855af182846101003e61c0de3b6020016000600061c0de3c36602001600060003738602001600060003961dead6000555050505050 4eef5052b492b54594df3d7ba37b5b619fc81a63981dbe3e5ded7115c51644f8=6001600054016000556000600060006000600073b0000000000000000000000000000000000000005af100 +47513d8ba80f62468bedeb52e4a41afafa57013079eb37939861f95d5c98f578=6001600054016000556000600060006000600073a0000000000000000000000000000000000000005af100 995aee3e8c5b5615400ead8106a3309fb4e38b06f44753ca2b209aefe698aab5=60005460010160005500 7f4605a1aad677a8fb1dd57110051118add90d619b6dc53bd301e1849d40eeee=600160005401600055600060006000600073b0000000000000000000000000000000000000005af400 2cae8875430fe46523b61dc18a2fc87da9abeab97011c1ec7577fa7356ae0756=600160005401600055600060006000600073a0000000000000000000000000000000000000005af400 @@ -2068,67 +2068,67 @@ b0ad0eef9078a814b41980e9cd2dfeea6f102c1cdaa6aadb5fe563528cdd7254=600054600101600 1ec6f47a4fe34ebded91833b6aa3eface5f7c6f5bd6dbe8a4c413288ee2d3ac7=6000546001016000556000600060006000600073c00000000000000000000000000000000000000061c350f16001555a60025500 a35f8f16a1d016bdf12a2b6acce812fe1e5e8a77c0f4f22a387c645583422111=6000546001016000556000600060006000600073b000000000000000000000000000000000000000620249f0f16001556000600060006000600073d000000000000000000000000000000000000000620249f0f160025500 b729186ceaa5a67fd8098e83f30236febf44a9f5d91de570fe34bb3af857e73a=6008600255600060006000f050600c60035500 -b97fb21bf3656ee0f7c642b99c3650acda2cfcb26474a23e1fbaf07bab794939=60406000604060006103e873945304eb96065b2a98b57a48a06ae28d285a71b561c350f26000553d6001556020600060403e60405160025500 9dce4945c71d01792533bd9e5329578fc79e8445aeaec2f2ccf34b63a487e5c8=61223260005260206000fd00 +b97fb21bf3656ee0f7c642b99c3650acda2cfcb26474a23e1fbaf07bab794939=60406000604060006103e873945304eb96065b2a98b57a48a06ae28d285a71b561c350f26000553d6001556020600060403e60405160025500 53a1824fc1a4451a418c68bbf4a08b9e1814e872d34c49af967d79e2454f988f=3050600d80602460003960006000f0503d6000556020600060003e6000516001550000fe6211223360005260206000fd00 -70598f4acaa69f4a950ab8fd0b0d08717e7c220f057cd2507389ee6c24965617=604060006040600073945304eb96065b2a98b57a48a06ae28d285a71b561c350f46000553d60015560206000603f3e603f5160025500 df2ae27db7df40d08a468158826304dacf484d12bca37d68e1e214ff8e526e12=600a60205260206020fd00 +70598f4acaa69f4a950ab8fd0b0d08717e7c220f057cd2507389ee6c24965617=604060006040600073945304eb96065b2a98b57a48a06ae28d285a71b561c350f46000553d60015560206000603f3e603f5160025500 cc05c22789c7358145e0a5f77b240bf8e4ec982f93c5b6ae50984477a5b337dd=604060006040600073945304eb96065b2a98b57a48a06ae28d285a71b561c350fa60005500 af5d00491b061867822572fb3545c4061853a8d8e9c8a3ced43a51f5c34d0adf=60006000fd00 -319cd5ed40e8ff86432b4cf3316d74e2cbc0f6977b097135d447c3f4d064e448=6000600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350f1600455600e60055500 +3464e9bde5c71c833cc6831cd82721320f00e40234f90372a592d80882be119a=6000600060006000600073b3305374fce5edbc8e2a8697c15331677e6ebf0b620186a0f1600055600e60025500 +33d45503472dd1a4e113cc68e971b6caf3937fa02d770be80668055e68a5b527=600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350f4600055600e60025500 895860136351eed3de541eb3d09df83e8daa38b5fcc40c45269a6b4cab883ff8=600c60015560016000fd600d60035500 89e9a89b21328e3aea83e007869a6a4c3ff1b98fe94c60b1b07977d6c2f8b147=6000600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350f1600055600e60025500 -3464e9bde5c71c833cc6831cd82721320f00e40234f90372a592d80882be119a=6000600060006000600073b3305374fce5edbc8e2a8697c15331677e6ebf0b620186a0f1600055600e60025500 +319cd5ed40e8ff86432b4cf3316d74e2cbc0f6977b097135d447c3f4d064e448=6000600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350f1600455600e60055500 3b357612db25ba26854d2f37558c7a6a47f0a1dca4f46e16e5eeda523ff4e2e9=6000600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350f2600055600e60025500 7d1fdfa405f46c1e83d1ab1cd7b53f0a3cc0146c6c365c908d62690200d3cf54=600060006000600060006000356203f7a0f1600a5500 -33d45503472dd1a4e113cc68e971b6caf3937fa02d770be80668055e68a5b527=600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350f4600055600e60025500 14fbe20c970af8fc8c386203907c949991b93320df402bb9ed5185b88dc38a02=6e600160005560016000fd6011600155600052600f60116001f0600155600c60005500 8b1ce28462104780a7aa93982e97da1b6f6948d48b21e5bd48899717b0247f24=6000600060006000600060003561ea60f1600a5500 -20f7607c5fdd32c0f2095adc899402f5d758147244b93039be132d2d605f71cc=6000600060006000600073ffff5374fce5edbc8e2a8697c15331677e6ebf0b6000f1506000600060006000600073b3305374fce5edbc8e2a8697c15331677e6ebf0b620186a0f16000553d60025500 +d1b9a99ff301b3984b9a681af8a9abf44430b7837f840fb2b81e1e30381c4c2d=6000600060006000600073ffff5374fce5edbc8e2a8697c15331677e6ebf0b6000f150600060006000600060006000356203f7a0f1600a5500 237fb00cc56bddf56cfe500979f1ced0efab82c327f8cd902800a197e0873324=600c60015260406000f300 +20f7607c5fdd32c0f2095adc899402f5d758147244b93039be132d2d605f71cc=6000600060006000600073ffff5374fce5edbc8e2a8697c15331677e6ebf0b6000f1506000600060006000600073b3305374fce5edbc8e2a8697c15331677e6ebf0b620186a0f16000553d60025500 2fc56ae367a9085b4961e02cb1a78061d320ea2601b907afdf2875fd8f902174=6000600060006000600073ffff5374fce5edbc8e2a8697c15331677e6ebf0b6000f1506000600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350f16004553d60055500 -dc66b56cd9184d970249516b0e5524c708b2e30be4563d17a5dc9a55e86800be=6000600060006000600073ffff5374fce5edbc8e2a8697c15331677e6ebf0b6000f1506000600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350f26000553d60025500 -d1b9a99ff301b3984b9a681af8a9abf44430b7837f840fb2b81e1e30381c4c2d=6000600060006000600073ffff5374fce5edbc8e2a8697c15331677e6ebf0b6000f150600060006000600060006000356203f7a0f1600a5500 -eea250b53d6627f5ac656c0f6a5fcdc007795364ba913641335a4fba72d16423=6000600060006000600073ffff5374fce5edbc8e2a8697c15331677e6ebf0b6000f1506000600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350f16000553d60025500 6d3eeb967abb524f7487139cbd322889a1497f39d61b3631b9ac2415e7300b9f=6000600060006000600073ffff5374fce5edbc8e2a8697c15331677e6ebf0b6000f150600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350f46000553d60025500 +eea250b53d6627f5ac656c0f6a5fcdc007795364ba913641335a4fba72d16423=6000600060006000600073ffff5374fce5edbc8e2a8697c15331677e6ebf0b6000f1506000600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350f16000553d60025500 +dc66b56cd9184d970249516b0e5524c708b2e30be4563d17a5dc9a55e86800be=6000600060006000600073ffff5374fce5edbc8e2a8697c15331677e6ebf0b6000f1506000600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350f26000553d60025500 c80af6033aafda8dab0312529df7ac0e6ce3ee9e68664aa1f3d8714814f4ec3a=600d80601560003960006000f0503d6000550000fe6211223360005260206000fd00 -76e98a46d06ac4c527b7911bf149cc00716d8f28c9020bd2558a0cec9db9171f=6000600060006000600073b00000000000000000000000000000000000000061c350f2600a556000600060006000600073c00000000000000000000000000000000000000061c350f2600b556000600060006000600073d00000000000000000000000000000000000000061c350f2600c55600c600455600c60055500 -8cd7a6423ab8ec8845afd56fbcdcc2719343ae93b044567b2874c9e6918bbf37=6000600060006000600073b00000000000000000000000000000000000000061c350f1600a556000600060006000600073c00000000000000000000000000000000000000061c350f1600b556000600060006000600073d00000000000000000000000000000000000000061c350f1600c55600c600455600c60055500 ee8b9e96deaca789fef2e186e903ab4eca54f3779f9fcaa96dd9dd4a20eb34f0=6000600060006000600073b00000000000000000000000000000000000000061c350f1600a55600060006000600073c00000000000000000000000000000000000000061c350f4600b556000600060006000600073d00000000000000000000000000000000000000061c350f2600c55600c600455600c60055500 -ae83baa314f8d08ef26978bb651f062053846078ae2bea5838418e5a1842b23c=600c60025560016000fd00 -bb34e89b4f88309b086ec2581cd2eecc885fb51307e8543b002ce411c11d8594=600060006000600073b00000000000000000000000000000000000000061c350f4600a55600060006000600073c00000000000000000000000000000000000000061c350f4600b55600060006000600073d00000000000000000000000000000000000000061c350f4600c55600c600455600c60055500 +47a14c35cc677428f8a0ecd275553c312b4a296ce0f29475f5e0797a7d8fe2bf=600c60015560016000fd00 +8cd7a6423ab8ec8845afd56fbcdcc2719343ae93b044567b2874c9e6918bbf37=6000600060006000600073b00000000000000000000000000000000000000061c350f1600a556000600060006000600073c00000000000000000000000000000000000000061c350f1600b556000600060006000600073d00000000000000000000000000000000000000061c350f1600c55600c600455600c60055500 2698e2391cb7be2d6df86ba0062ad67d31b592d61362d8a5ac6107823316a283=6000600060006000346000356203f7a0f100 +bb34e89b4f88309b086ec2581cd2eecc885fb51307e8543b002ce411c11d8594=600060006000600073b00000000000000000000000000000000000000061c350f4600a55600060006000600073c00000000000000000000000000000000000000061c350f4600b55600060006000600073d00000000000000000000000000000000000000061c350f4600c55600c600455600c60055500 +ae83baa314f8d08ef26978bb651f062053846078ae2bea5838418e5a1842b23c=600c60025560016000fd00 04e48245db9a3c3476fed2e5d68e0db8f8492e5e5890e0624d72ea2aa2197762=600c60035560016000fd00 -47a14c35cc677428f8a0ecd275553c312b4a296ce0f29475f5e0797a7d8fe2bf=600c60015560016000fd00 -dd3283c605d58dbe21d0a0f2ab884dda1c6e149eaff1840a77e1e12d06103b53=6c726576657274656420646174616000556d726576657274206d65737361676560005260006001fd00 -d7257879609c29a12ae74b59a3fde006207a36328ff7f4fd2717eeba469dee1a=6c726576657274656420646174616000556d726576657274206d65737361676560005260206000fd00 -e2e3bbd0bda8f67f70369cb7206b24e0496d947130a64ca55e9130999e762309=6c726576657274656420646174616000556d726576657274206d6573736167656000526000610100fd00 +76e98a46d06ac4c527b7911bf149cc00716d8f28c9020bd2558a0cec9db9171f=6000600060006000600073b00000000000000000000000000000000000000061c350f2600a556000600060006000600073c00000000000000000000000000000000000000061c350f2600b556000600060006000600073d00000000000000000000000000000000000000061c350f2600c55600c600455600c60055500 +05b81dc0e5b0bd4187605683d6395f4d82d71703093e70dbdd3bc0fe0533710f=6c726576657274656420646174616000556d726576657274206d6573736167656000526e0fffffffffffffffffffffffffffff6000fd00 7181d3ffeaa870b0caf01a52a66ed7cd21ba2ee2b2045c33b011986fdd31ea5c=60206000600060006000600035620249f0f160015560005160025500 -459bee294ee0354c254d9b1732de0e598a4c560582d2773a239cd0f3fc66cacc=6c726576657274656420646174616000556d726576657274206d65737361676560005260006000fd00 +e2e3bbd0bda8f67f70369cb7206b24e0496d947130a64ca55e9130999e762309=6c726576657274656420646174616000556d726576657274206d6573736167656000526000610100fd00 +d7257879609c29a12ae74b59a3fde006207a36328ff7f4fd2717eeba469dee1a=6c726576657274656420646174616000556d726576657274206d65737361676560005260206000fd00 +dd3283c605d58dbe21d0a0f2ab884dda1c6e149eaff1840a77e1e12d06103b53=6c726576657274656420646174616000556d726576657274206d65737361676560005260006001fd00 558e0faf375d92de9928c588cbcc3df3292a09b6dd0378a8112952bbe580bbe2=6c726576657274656420646174616000556d726576657274206d65737361676560005260006e0ffffffffffffffffffffffffffffffd00 -05b81dc0e5b0bd4187605683d6395f4d82d71703093e70dbdd3bc0fe0533710f=6c726576657274656420646174616000556d726576657274206d6573736167656000526e0fffffffffffffffffffffffffffff6000fd00 +459bee294ee0354c254d9b1732de0e598a4c560582d2773a239cd0f3fc66cacc=6c726576657274656420646174616000556d726576657274206d65737361676560005260006000fd00 e5dd5859cf6cdb87486d7e024f31392ce9af7d4f24e935880b6a135151acac9a=60006000600060006000365af200 e4590715e0237590775cab8ca93ac7d8497f5441f19a05e3e9e38a3a5af9fcba=6000600060006000365af400 6b19b36fcd8315974e4f487b2fe71f3e75cfc690d5721b280c6f483d5fc07d78=60006000602035600060006000355af200 be94973c2c562d44b647c3bf84e2475f1ef7d0d19cd7f88973637552e788cb2b=60006000600060006000365af100 29d48283cedda1215773a86d298557f0f069d6265fd64e4171610c655a6614cb=6000600060006000365afa00 +e9a282e75cfff1a6ac9e90ceedd6af19f59d0424430b266601a5a9fda6673736=6000600060006000600161c350fa506000600060006000600261c350fa506000600060006000600361c350fa506000600060006000600461c350fa506000600060006000600561c350fa506000600060006000600661c350fa506000600060006000600761c350fa506000600060006000600861c350fa505a6001555a6002555a60035500 02fe24861294db45cf790d2433acdf09424d0063d527734767f7ececdcce2095=600060006000600060006000355af200 -2d6381d4b64e7fb761a5749f70108e4a6bd5fdfedbc665a2b63dcb5a1e419d8a=60006000600060006000600161c350f25060006000600060006000600261c350f25060006000600060006000600361c350f25060006000600060006000600461c350f25060006000600060006000600561c350f25060006000600060006000600661c350f25060006000600060006000600761c350f25060006000600060006000600861c350f2505a6001555a6002555a60035500 df05ce605786cf1f172027621ec602aa57b0b59011a8e363d786553b38845555=6000600060006000600161c350f4506000600060006000600261c350f4506000600060006000600361c350f4506000600060006000600461c350f4506000600060006000600561c350f4506000600060006000600661c350f4506000600060006000600761c350f4506000600060006000600861c350f4505a6001555a6002555a60035500 -e9a282e75cfff1a6ac9e90ceedd6af19f59d0424430b266601a5a9fda6673736=6000600060006000600161c350fa506000600060006000600261c350fa506000600060006000600361c350fa506000600060006000600461c350fa506000600060006000600561c350fa506000600060006000600661c350fa506000600060006000600761c350fa506000600060006000600861c350fa505a6001555a6002555a60035500 +2d6381d4b64e7fb761a5749f70108e4a6bd5fdfedbc665a2b63dcb5a1e419d8a=60006000600060006000600161c350f25060006000600060006000600261c350f25060006000600060006000600361c350f25060006000600060006000600461c350f25060006000600060006000600561c350f25060006000600060006000600661c350f25060006000600060006000600761c350f25060006000600060006000600861c350f2505a6001555a6002555a60035500 de7f48a9698a7a949a5cba3dbcc46058a43402095d34a5fa5db5d6a2cd6c5459=60006000600060006000600161c350f15060006000600060006000600261c350f15060006000600060006000600361c350f15060006000600060006000600461c350f15060006000600060006000600561c350f15060006000600060006000600661c350f15060006000600060006000600761c350f15060006000600060006000600861c350f1505a6001555a6002555a60035500 ee12ccf48321392c3161569794e59e70f7be0a94df08c8ad4745a1c22e177e45=60206000602060006000737db299e0885c85039f56fa504a13dd8ce8a56aa761c350f1600055600c60015500 e10e9441043a020bebab4dad741bbff857fd56514ba85587ced83a1cbbd8881f=60206000602060006000737db299e0885c85039f56fa504a13dd8ce8a56aa761c350f1600055600c600155600c60025500 c5c83e7953c7f26673d31c14507cc529cfeeb8cdc96d2cc7e770a7c22c6b1ea5=602060006000f0600055600c60015500 335c9e5c64580590e20b011cadb3ec940b6480733d2dbcafb664d956e9d6f41b=602060006000f0600055622fffff60002000 a918da4c499f558eae39cf8cb1054c391a9b036b7dcad9cf8a5bbcff148b7866=602060006000f0600055600c60015500 -5d8a411ccd418a3581405e3635df63eaa615b4018ed5e240a967ae5864ee9cbb=600060006000600060007310000000000000000000000000000000000000006201fbd0f1600255622fffff60002000 b2fd212493f89d3b09afd9c45eeabdae28600767536d39cc971048a8f451a456=600060006000600060007310000000000000000000000000000000000000006201fbd0f16000556000600060006000600073b94f5374fce5edbc8e2a8697c15331677e6ebf0b6201fbd0f160015500 -36b0a23503550cb1130badb6429992339e51df072f544b616c691ec951df4a49=6000600060006000600073d94f5374fce5edbc8e2a8697c15331677e6ebf0b6201fbd0f16000556000600060006000600073b94f5374fce5edbc8e2a8697c15331677e6ebf0b6201fbd0f160015500 +5d8a411ccd418a3581405e3635df63eaa615b4018ed5e240a967ae5864ee9cbb=600060006000600060007310000000000000000000000000000000000000006201fbd0f1600255622fffff60002000 64820d3e7cc7fc1bea5d63ae7615070a7646ae1a0daf9a88650ff0a8635a1912=731000000000000000000000000000000000000000ff00 +36b0a23503550cb1130badb6429992339e51df072f544b616c691ec951df4a49=6000600060006000600073d94f5374fce5edbc8e2a8697c15331677e6ebf0b6201fbd0f16000556000600060006000600073b94f5374fce5edbc8e2a8697c15331677e6ebf0b6201fbd0f160015500 739204052436f7bd0bd121ebc5bcf6270ffd6b7c5e533f1688fd7f2d59ab3351=6000600060006000600073e94f5374fce5edbc8e2a8697c15331677e6ebf0b620186a0f1600255622fffff60002000 -03cf95fee543c360abf34345f9e03afb9142224d593f7b2fbe8c2987a2514100=60006000600060006000731000000000000000000000000000000000000000617530f160015500 076fe75197c119cb43e28a5d7a04d7125e0213a96268b62cd1f857c6f493c69e=6000600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b617530f1600055600160025500 +03cf95fee543c360abf34345f9e03afb9142224d593f7b2fbe8c2987a2514100=60006000600060006000731000000000000000000000000000000000000000617530f160015500 62516506325d7cd9a9c5ed8d0c534e9dde66627892fcb141a0a58814482c4d9e=60106000fd00 52aadc0ad8118342fb956b517e52d1e9af1705ebde4526e70a7e7803d994b5f7=5b6001156013576301000000600020506000565b00 7ff408e57e6ca4efed1ae0920cf2e2d4d01b9b11790d80fbb03e6a706145260e=600160000360002000 @@ -2143,7 +2143,7 @@ b4c5e7abf2c1e7d3ab977f12b44b93ae99ea6b4ce5c670a962d342aa6028203c=600060015560016 438fe345117cd7ad596753bcf57091f81c32dfca164e1b45269b97cf9c09e879=6000600b80601360003960006000f5500000fe6000600155600160015500 3bf29bf3eb0c4135fa025457b168546cb4980523dd9f0aa748f7d95c092e034b=6000600b80601860003960006000f55060006000fd0000fe6000600155600160015500 8fecce8e335fb1fb2a6807744153cbeee93c36edfeb191e66e4c005beda348aa=6000600b80603860003960006000f5506000600060006000600073dea000000000000000000000000000000000000062030d40f1500000fe6000600155600160015500 -3f0007b88f06463a813b03c55b07ee91057e2bc54fc36d4dac83a7a9e1a7afd7=611000600d6000805a915061beef6000555a9050828183030384556001840193505a915063deadbeef6000555a9050828183030384556001840193505a9150600080555a9050828183030384556001840193505a9150600080555a9050828183030384556001840193505a91506112346000555a9050828183030384556001840193505a915060006001555a9050828183030384556001840193505a91506160a76002555a9050828183030384556001840193505a915060006003555a9050828183030384556001840193505a91506160a76003555a9050828183030384556001840193506000805560006001556000600255600060035550505050 +3f0007b88f06463a813b03c55b07ee91057e2bc54fc36d4dac83a7a9e1a7afd7=611000600d600060005a915061beef6000555a9050828183030384556001840193505a915063deadbeef6000555a9050828183030384556001840193505a915060006000555a9050828183030384556001840193505a915060006000555a9050828183030384556001840193505a91506112346000555a9050828183030384556001840193505a915060006001555a9050828183030384556001840193505a91506160a76002555a9050828183030384556001840193505a915060006003555a9050828183030384556001840193505a91506160a76003555a905082818303038455600184019350600060005560006001556000600255600060035550505050 36f9db2473727c60d9e532538f556fe6bededa1527ed06644f1b093c8d37557c=600060015500 71891782a36768b68f09d49991e9e9b72f8555e2be3ce293dd936cb9bc1b33da=6000600060006000600073b000000000000000000000000000000000000000620493e0f1506000600060006000600073dea0000000000000000000000000000000000000620927c0f100 e099d68aa4f2daf6478c3bc0b7548c9734e931aadbb11b30f05e237e99d75350=6000600060006000600073b000000000000000000000000000000000000000620493e0f2506000600060006000600073dea0000000000000000000000000000000000000620927c0f100 @@ -2230,11 +2230,11 @@ e798273f90ee37816ec2050556e4ccb681c0a17dbe4ff09abf7ba18e33f8459b=600160015560006 2deb9469ef0902c49112f3d39d76c543221f25edf34221b99bf612720ce063ef=6001600155600060006000600073b000000000000000000000000000000000000000610903f415604e576000600060006000600073c000000000000000000000000000000000000000617530f1505b00 8bbed1d29403ad31bc446e9eca24e6c2fedb931f1bd387377e80b179e966ffa6=476000526000516040526021603ff3 eb957dab28ae072ae7dcc6ed70eae55227971d7f99851ccc1be18704448a5361=4760005260206000f3 -8d95f8713c7ef660d149c2330a4ea05923319e1fad7a6acbc1804cb953f0dfbe=600435600180602061c0de3b63c0dec0de3b6160a7600052856000811461015b5760f181146101645760f281146101785760f4811461018c5760fa811461019f5761f1f181146101b25761f2f181146101c85761f4f181146101de5761faf181146101f35761f1f281146102085761f2f2811461021e5761f4f281146102345761faf281146102495761f1f4811461025e5761f2f481146102745761f4f4811461028a5761faf4811461029f5761f1fa81146102b45761f2fa81146102ca5761f4fa81146102e05761fafa81146102f55760fd811461030a5760fe81146103415760ff811461037a5760f081146103b25760f581146103d55761f0f181146103fb5761f5f181146104255761f0f281146104525761f5f281146104755761f0f4811461049b5761f5f481146104bd5761f0fa81146104e25761f5fa811461050b576460baccfa5781146105375765bad0bad0bad0600052610551565b47600052610551565b602060008060008061ca115af19550610551565b602060008060008061ca115af29550610551565b6020600080600061ca115af49550610551565b6020600080600061ca115afa9550610551565b602060008060008063ca1100f15af19550610551565b602060008060008063ca1100f15af29550610551565b6020600080600063ca1100f15af49550610551565b6020600080600063ca1100f15afa9550610551565b602060008060008063ca1100f25af19550610551565b602060008060008063ca1100f25af29550610551565b6020600080600063ca1100f25af49550610551565b6020600080600063ca1100f25afa9550610551565b602060008060008063ca1100f45af19550610551565b602060008060008063ca1100f45af29550610551565b6020600080600063ca1100f45af49550610551565b6020600080600063ca1100f45afa9550610551565b602060008060008063ca1100fa5af19550610551565b602060008060008063ca1100fa5af29550610551565b6020600080600063ca1100fa5af49550610551565b6020600080600063ca1100fa5afa9550610551565b4760005260005160005560008060008060006260bacc5af150476000526000516000541461033c57640badbadbad6000525b610551565b476000526000516000556000806000806000620600066161a8f150476000526000516000541461037557640badbadbad6000525b610551565b47600052600051600055600080600080600063deaddead5af15047600052600051600054146103ad57640badbadbad6000525b610551565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c610551565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c610551565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600080600080895af19550610551565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600080600080895af19550610551565b8160008563c0dec0de3c81846000f094506020600080600080895af29550610551565b8160008563c0dec0de3c615a1782856000f594506020600080600080895af29550610551565b8160008563c0dec0de3c81846000f0945060206000806000885af49550610551565b8160008563c0dec0de3c615a1782856000f5945060206000806000885af49550610551565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000806000885afa9550610551565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000806000885afa9550610551565b6103ff6000526020600060206000806460baccfa575af195505b508461055d5760206000fd5b836105685760206000fd5b600051600055505050505050 +8d95f8713c7ef660d149c2330a4ea05923319e1fad7a6acbc1804cb953f0dfbe=60043560016001602061c0de3b63c0dec0de3b6160a7600052856000811461015c5760f181146101655760f2811461017b5760f481146101915760fa81146101a55761f1f181146101b95761f2f181146101d15761f4f181146101e95761faf181146101ff5761f1f281146102155761f2f2811461022d5761f4f281146102455761faf2811461025b5761f1f481146102715761f2f481146102895761f4f481146102a15761faf481146102b75761f1fa81146102cd5761f2fa81146102e55761f4fa81146102fd5761fafa81146103135760fd81146103295760fe81146103645760ff81146103a15760f081146103dd5760f581146104005761f0f181146104265761f5f181146104525761f0f281146104815761f5f281146104a65761f0f481146104ce5761f5f481146104f15761f0fa81146105175761f5fa8114610541576460baccfa57811461056e5765bad0bad0bad0600052610589565b47600052610589565b6020600060006000600061ca115af19550610589565b6020600060006000600061ca115af29550610589565b602060006000600061ca115af49550610589565b602060006000600061ca115afa9550610589565b6020600060006000600063ca1100f15af19550610589565b6020600060006000600063ca1100f15af29550610589565b602060006000600063ca1100f15af49550610589565b602060006000600063ca1100f15afa9550610589565b6020600060006000600063ca1100f25af19550610589565b6020600060006000600063ca1100f25af29550610589565b602060006000600063ca1100f25af49550610589565b602060006000600063ca1100f25afa9550610589565b6020600060006000600063ca1100f45af19550610589565b6020600060006000600063ca1100f45af29550610589565b602060006000600063ca1100f45af49550610589565b602060006000600063ca1100f45afa9550610589565b6020600060006000600063ca1100fa5af19550610589565b6020600060006000600063ca1100fa5af29550610589565b602060006000600063ca1100fa5af49550610589565b602060006000600063ca1100fa5afa9550610589565b47600052600051600055600060006000600060006260bacc5af1504760005260005160005414151561035f57640badbadbad6000525b610589565b4760005260005160005560006000600060006000620600066161a8f1504760005260005160005414151561039c57640badbadbad6000525b610589565b476000526000516000556000600060006000600063deaddead5af150476000526000516000541415156103d857640badbadbad6000525b610589565b8260008561c0de3c8284670de0b6b3a7640000f09450602060016000873c610589565b8260008561c0de3c615a178385670de0b6b3a7640000f59450602060016000873c610589565b8160008563c0dec0de3c8184670de0b6b3a7640000f0945060206000600060006000895af19550610589565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f5945060206000600060006000895af19550610589565b8160008563c0dec0de3c81846000f0945060206000600060006000895af29550610589565b8160008563c0dec0de3c615a1782856000f5945060206000600060006000895af29550610589565b8160008563c0dec0de3c81846000f094506020600060006000885af49550610589565b8160008563c0dec0de3c615a1782856000f594506020600060006000885af49550610589565b8160008563c0dec0de3c8184670de0b6b3a7640000f094506020600060006000885afa9550610589565b8160008563c0dec0de3c615a178285670de0b6b3a7640000f594506020600060006000885afa9550610589565b6103ff600052602060006020600060006460baccfa575af195505b508415156105975760206000fd5b8315156105a45760206000fd5b600051600055505050505050 e072ca4e44f39e2861bf2dd3848ae1d57d606c88ab8fb7c557287f611fce8b15=4760005260206000f3 d230b310957a07eb653cf0f74a2f0b12b2ce777c388d2e7429a2f6f76863ee4c=47600052600051600055fe b0bbc0608478259d03d5b37781443defa85f60926523f33ac8621c634a3f8d8a=4760005260005160005560206000fd -dddc4cdd0ce51386ddc68be5f97ae1c4f9b0be4cf052c6024934cfae9fb271fc=600035600081036013574760005260206000f35b600181036000526020600060206000806460baccfa575af160345760206000fd5b60206000f3 +dddc4cdd0ce51386ddc68be5f97ae1c4f9b0be4cf052c6024934cfae9fb271fc=60003560008114156014574760005260206000f35b60018103600052602060006020600060006460baccfa575af1151560385760206000fd5b60206000f350 84f7e621ac63ff99a09b602712f898bc3267717a9a0cf5d3e0814ef96e5dad21=3031471460015500 ec8a200e05896c520c791fd009255daec79d60e47a4bece6d62c3881d8424812=4760015500 86ee8015bb2d147fad87d3214308454eb4ce9a5fec625543e74bfe62d82a3289=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60001d60005500 @@ -2248,39 +2248,39 @@ ec181eee16d86db526290ad3b6fed53fc942d8879b0209cbc0239c9af57a3838=7b601080600c600 d5f3cccc5baa4f1489a77f9802821d3443fcb059dad76e3d3f1f29b3caa001a0=7f18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c600052601c6020527f73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f6040527feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c454960605260206103e86080600060015afa60005560a060020a6103e851066001556001543214600255600060005260006020526000604052600060605260006103e8527c0ccccccccccccccccccccccccccccccccccccccccccccccccccc00000060005260206103e86020600060025afa6003556000516004556103e851600555600060005260006103e8527c0ccccccccccccccccccccccccccccccccccccccccccccccccccc00000060005260206103e86020600060035afa6006556000516007556103e851600855600060005260006103e8527c0ccccccccccccccccccccccccccccccccccccccccccccccccccc00000060005260206103e86020600060045afa6009556103e851601055600060005260006103e8526001600052602060205260206040527f03fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc6060527f2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc6080527f2f0000000000000000000000000000000000000000000000000000000000000060a05260206103e860a1600060055afa6011556103e85160125560006000526000602052600060405260006060526000608052600060a05260006103e8527f0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd26000527f16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba6020527f1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2866040527f0217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d460605260406103e86080600060065afa6013556103e85160145561040851601555600060005260006020526000604052600060605260006103e8526000610408527f0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd26000527f16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba602052600360405260406103e86060600060075afa6016556103e8516017556104085160185560006000526000602052600060405260006103e8526000610408527f1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f596000527f3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef416020527f209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf76040527f04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416786060527f2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d6080527f120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de87755060a0527f111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c60c0527f2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf41160e0527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2610100527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed610120527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b610140527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa6101605260206103e8610180600060085afa6019556103e85160205500 9587684c7d5328f4f12a5b051bd5d226074263879c6c11ae0bda0b52db7e4ef2=366000600037655a175a175a173660006000f560005500 5598916aa5224e161a88881cdcdf6d07e2660a4863e6a00ea4ab8bc257f3dcb0=7f18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c600052601c6020527f73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f6040527feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c454960605260206103e860806000600162061a80fa60005560a060020a6103e851066001556001543214600255600060005260006020526000604052600060605260006103e8527c0ccccccccccccccccccccccccccccccccccccccccccccccccccc00000060005260206103e86020600060025afa6003556000516004556103e851600555600060005260006103e8527c0ccccccccccccccccccccccccccccccccccccccccccccccccccc00000060005260206103e86020600060035afa6006556000516007556103e851600855600060005260006103e8527c0ccccccccccccccccccccccccccccccccccccccccccccccccccc00000060005260206103e86020600060045afa6009556103e851601055600060005260006103e8526001600052602060205260206040527f03fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc6060527f2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc6080527f2f0000000000000000000000000000000000000000000000000000000000000060a05260206103e860a1600060055afa6011556103e85160125560006000526000602052600060405260006060526000608052600060a05260006103e8527f0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd26000527f16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba6020527f1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2866040527f0217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d460605260406103e86080600060065afa6013556103e85160145561040851601555600060005260006020526000604052600060605260006103e8526000610408527f0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd26000527f16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba602052600360405260406103e86060600060075afa6016556103e8516017556104085160185560006000526000602052600060405260006103e8526000610408527f1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f596000527f3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef416020527f209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf76040527f04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416786060527f2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d6080527f120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de87755060a0527f111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c60c0527f2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf41160e0527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2610100527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed610120527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b610140527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa6101605260206103e8610180600060085afa6019556103e85160205500 +cc45a5d135a8f9e57c309064b573fd2096be276abcca0a786f138b91173c9806=58600152600060006000600073195e7baea6a6c7c4c2dfeb977efac326af552d8761c350fa00 8d4ade0d727cfb64bfa4822027a066ce0356301bebb5a84a378d2d09d9592dd9=6000600060006000346000355af1600055600160015500 75b623adef17d8ee945396a0e457c02de9327d43bd6530247b01dbab9e6bccda=600060006000600073095e7baea6a6c7c4c2dfeb977efac326af552d8761c350fa600101585500 -5d48702bb1b7ab50547f4f7cb78d2dfbed39a5048e66ef3eeb7efab11b6a7616=600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b5620186a0fa585500 847517a05e83765f78c7a7f5f5c0ffd0f9aff3a2cdb0942a4c8e257546facffc=58600152600060006000600073245304eb96065b2a98b57a48a06ae28d285a71b5620186a0fa00 -cc45a5d135a8f9e57c309064b573fd2096be276abcca0a786f138b91173c9806=58600152600060006000600073195e7baea6a6c7c4c2dfeb977efac326af552d8761c350fa00 -7b0711df01faf8aa85459833a5f02dbfb532a3752b3effd24a93e9a9ca544a8f=600060006000600073195e7baea6a6c7c4c2dfeb977efac326af552d87620186a05a03fa600101585200 +5d48702bb1b7ab50547f4f7cb78d2dfbed39a5048e66ef3eeb7efab11b6a7616=600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b5620186a0fa585500 6c08e575b788dedaa76e338d12c265b1e21bf330070c6ff3b2de4238e6216afc=600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b5620186a05a03fa585500 -de8e6310b88f5e61bb3e1cf61e5018deb5ce849331b8d66508b4d2e68aa6a874=58600152600060006000600073245304eb96065b2a98b57a48a06ae28d285a71b5620186a05a03fa00 e4affd2099ee1c5609d3fbae1291089a05099c54362e1641ce853fbe480ee806=600060006000600060006000355af1600055600160015500 +de8e6310b88f5e61bb3e1cf61e5018deb5ce849331b8d66508b4d2e68aa6a874=58600152600060006000600073245304eb96065b2a98b57a48a06ae28d285a71b5620186a05a03fa00 +7b0711df01faf8aa85459833a5f02dbfb532a3752b3effd24a93e9a9ca544a8f=600060006000600073195e7baea6a6c7c4c2dfeb977efac326af552d87620186a05a03fa600101585200 3ae7d66fdc5bf050aae1b7a6aebd7b73f28a6f3a311c0d2c25ec3050c26f31c4=600060006000600073095e7baea6a6c7c4c2dfeb977efac326af552d87620186a05a03fa600101585500 c574607dad90aee6b83c3fc1665a2aaf8128ef7b9e5b3e7961b9789b47aaccb4=6000600060006000346000355af1600055600160015500 -f29a742f9be9e76aa07187a36a8e607e1eff7939468a0c57c05e5611b58e5ad4=600160005401600055600060006000600073095e7baea6a6c7c4c2dfeb977efac326af552d87620186a05a03fa00 -ffa82f8e8aca8c1b0edc26428ed39999b441647e0a65c7a518e874498b50d99c=600160005101600052600060006000600073245304eb96065b2a98b57a48a06ae28d285a71b5620186a05a03fa00 848ed1445089ed972a26f2115d5e8cff4e7f9c898926ee75e017f6da386f43b9=600160005101600052600060006000600073195e7baea6a6c7c4c2dfeb977efac326af552d87620186a05a03fa00 +f29a742f9be9e76aa07187a36a8e607e1eff7939468a0c57c05e5611b58e5ad4=600160005401600055600060006000600073095e7baea6a6c7c4c2dfeb977efac326af552d87620186a05a03fa00 03e0f6da45fc1ce6e52182732e8d12e56722585f37958a4ecd3510d452943ffd=600160005401600055600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b5620186a05a03fa00 +ffa82f8e8aca8c1b0edc26428ed39999b441647e0a65c7a518e874498b50d99c=600160005101600052600060006000600073245304eb96065b2a98b57a48a06ae28d285a71b5620186a05a03fa00 69a88067624e01b6239912c9925d34b5f210adb5c8877b3a8aac27f36a23297b=600160005401600052600060006000600073245304eb96065b2a98b57a48a06ae28d285a71b5620186a05a03fa00 49ab705dc9bb9e656e931cf56281cab0fdc9aeb783237adc2d82105f0a1c1512=600160005401600052600060006000600073195e7baea6a6c7c4c2dfeb977efac326af552d87620186a05a03fa00 -04c2ed31c0bae2704c6803bbc19baca5c9573d7d3c060e8ad5aba9e90011a916=600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b5620186a0fa585573945304eb96065b2a98b57a48a06ae28d285a71b5ff00 -ca714aeb6844ef9d58ffac2ee18ec087e8355e609476156b59aea4c14a19d112=600060006000600073245304eb96065b2a98b57a48a06ae28d285a71b5620186a0fa585273945304eb96065b2a98b57a48a06ae28d285a71b5ff00 a504bf49be3b2eb805df90688d764167d18e692ccac80bcb0740634b3321d26c=600060006000600073195e7baea6a6c7c4c2dfeb977efac326af552d8761c350fa600101585200 +ca714aeb6844ef9d58ffac2ee18ec087e8355e609476156b59aea4c14a19d112=600060006000600073245304eb96065b2a98b57a48a06ae28d285a71b5620186a0fa585273945304eb96065b2a98b57a48a06ae28d285a71b5ff00 +04c2ed31c0bae2704c6803bbc19baca5c9573d7d3c060e8ad5aba9e90011a916=600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b5620186a0fa585573945304eb96065b2a98b57a48a06ae28d285a71b5ff00 c82f5d265659d347880908fc2d570ab0b17bb8c30b9e9fde1f6ef665288704ec=600035600052600060006020600073095e7baea6a6c7c4c2dfeb977efac326af552d8761c35060003503fa50730f572e5295c57f15886f9b263e2f6d2d6c7b5ec6ff00 2de09a348bd19a028da60773913756f69431b621da433750cbb05afcb037b82b=600035600052600060006020600073945304eb96065b2a98b57a48a06ae28d285a71b5600035fa00 b9df2e03df2ca660f9df81aec2385169eb7445c609863aca34808497205337d6=600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61ea60fa600155600160645500 1e35ef69787f36587d5fb72e3def7ead221ea4af0bb297a02b427daee8f3184b=600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61ea60fa00 -0fa099c2413e5a9df168535bd5ce03c5e8cc96c6cda338fdc7ae58c4368967bd=600160015500 -9c1da38f1cea43836fcc7425513e0b01040c196afadfb4ff8da714f6dd5e1c89=600160015200 1559f73bf51a6bd64b56536e049892ddeaf090ff84d487076b145c18bd3450fe=5b61c3506080511015601c5760013b506001608051016080526000565b00 +0fa099c2413e5a9df168535bd5ce03c5e8cc96c6cda338fdc7ae58c4368967bd=600160015500 5b10e9929e41f007f2630f6e529ebc43513a2ec0779c11dc7cd2ebf790fe2717=6000600060006000600173c94f5374fce5edbc8e2a8697c15331677e6ebf0b6064f100 +9c1da38f1cea43836fcc7425513e0b01040c196afadfb4ff8da714f6dd5e1c89=600160015200 8a9ea564d7839b7dfe036c25f7d6d498ef1b9efb6f370df0be73af42efc42a04=600160015200 fe1479307d0ae7d9c9a5a1f82259d21daad6a5de6f06319930318b8afd1f5736=5a600055602060006000f06001555a600255600060006000600060015461ea60fa6003555a60645500 9a25f942d2f6c17ef3b5de806a0557b10f8efc55a37fff018d937aa50cc20e05=5a6000557f600c6000556000600060006000600073c94f5374fce5edbc8e2a8697c15331676000527f7e6ebf0b61ea60f1000000000000000000000000000000000000000000000000602052604060006000f06001555a600255600060006000600060015461ea60fa6003555a60645500 -cedba9ece1b60951ead1283952052936b8b1d66a24bc5f802d2735729916a308=60016000540160005560006000600060005473bbbf5374fce5edbc8e2a8697c15331677e6ebf0b650ffffffffffffa60015500 dd805dedda7c77cd5dc10c607c92bc756a9f0a638ed5a54244154f379d6e86fe=60016000510160005260006000600060005173cbbf5374fce5edbc8e2a8697c15331677e6ebf0b650ffffffffffffa60205200 +cedba9ece1b60951ead1283952052936b8b1d66a24bc5f802d2735729916a308=60016000540160005560006000600060005473bbbf5374fce5edbc8e2a8697c15331677e6ebf0b650ffffffffffffa60015500 451dc2391994684f6b8b47a9cfc44c55a2eb0dd2f422239ece0a3ab09e22f733=600160005401600055600060006000600073bbbf5374fce5edbc8e2a8697c15331677e6ebf0b610401600054046001036127105a0302fa6001556103e86000540260010160025500 150da3077e45f07d5988f29883b09b41dce5e7ca9b1abe39b2b8d1478769ddef=600160005101600052600060006000600073cbbf5374fce5edbc8e2a8697c15331677e6ebf0b610401600051046001036127105a0302fa506103e86000510260010160205200 efee795e927f84fb0adb945f5fbc1ebac254360dcb44df54e75ee34087563cb6=600060006000600073aaaf5374fce5edbc8e2a8697c15331677e6ebf0b61fffffa600255600060006000600073aaaf5374fce5edbc8e2a8697c15331677e6ebf0b61fffffa600355600160005401600055600060006000600073bbbf5374fce5edbc8e2a8697c15331677e6ebf0b650ffffffffffff460015500 @@ -2296,12 +2296,12 @@ a4e0406c1baf1015f08c5ebb9eb902ecb905de40083d4fa9c04a675fac554b23=600160005101600 b65a639d937e5d2bf958c15440a67e4119682763bb358d0591d9da53a4716dd7=6000600060006000731000000000000000000000000000000000000103620927c0fa60095560006000600060006000731000000000000000000000000000000000000103620927c0f2600a5500 6a087fb788ade9003cb55a55d3910c758d4ab11e2c5d62a3de2f42618484f988=6000600060006000732000000000000000000000000000000000000103620927c0fa60095560006000600060006000731000000000000000000000000000000000000103620927c0f2600a5500 6ebaf83c33c2cee3af4f96f11416c73995ddfe353b7cfe9312ab7804eb3a7ed5=601260005200 -40feb2b16aa5295c8d81010e6dc7712ec8bcc1f3618a0d2bd4022dc5f17ecb3a=5a60085200 +d154d3443627ec7b9b4714482ec0c4912d141f67e1f21261a3eb342d2e115132=5a6008526000600060006000731000000000000000000000000000000000000108620927c0fa60095200 86e34fc9f92a233255712ea4dd5596532a0b00148b10f0c867dcc2c5e91450a3=6001600855600060006000600073200000000000000000000000000000000000010762030d40fa60095500 +75f0c2ebd23355bc89911c75994a8f86f2d9b49ad6e9bb73d4b538c03b1e4d5f=5a6008526000600060006000732000000000000000000000000000000000000108620927c0fa60095200 5068499c922fdc5490952a26514b9653928a238e05efa21438bd82bdc3115f45=600160085500 +40feb2b16aa5295c8d81010e6dc7712ec8bcc1f3618a0d2bd4022dc5f17ecb3a=5a60085200 b7c05ea76a53e47443a315cdebc8d471eb1d2a8a6b5a3457b81ffb67ec948b0b=6001600855600060006000600073100000000000000000000000000000000000010762030d40fa60095500 -75f0c2ebd23355bc89911c75994a8f86f2d9b49ad6e9bb73d4b538c03b1e4d5f=5a6008526000600060006000732000000000000000000000000000000000000108620927c0fa60095200 -d154d3443627ec7b9b4714482ec0c4912d141f67e1f21261a3eb342d2e115132=5a6008526000600060006000731000000000000000000000000000000000000108620927c0fa60095200 3df6ceea3eb0ac5fd7b62ad34dc5a845fbb94db7d6778225956b191fad1f0c07=74600c60005566602060406000f060205260076039f36000526015600b6001f060005560006000600060006000546000fa60015500 054829905933d02b982db54f2071b13c66774b1e1e01c7d9880333b9d8453873=74600c60005566602060406000f060205260076039f36000526015600b6001f060005560006000600060006000546000fa00 8cf4961ea0d0e182af73c824d25be9acfe545eb446befad5c4c92c18c1f24175=74600c60005566602060406000f060205260076039f36000526015600b6001f06000556000600060006000600054620249f0fa60015500 @@ -2322,9 +2322,9 @@ b608e2bd975152e4ef474640765aeb8509e0e67131bdd4d97b1f63c1813e10a0=7f1122334455667 c26fa0477659989e854da9505974ab47978b16cde9fa9e4d24bf9c0b46ea7f61=7f18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c600052601c6020527eb1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f6040527feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c454960605260206080608060006001620493e0fa60025560a060020a60805106600055600054321460015500 15479cd180bb76cade7a0e123e5c9abf66feb718b97c77a879a49ab8615baf9c=7f18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c600052601c6020527f73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f6040527eb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c454960605260206080608060006001620493e0fa60025560a060020a60805106600055600054321460015500 ca5ef10e7d080d96e21e5d67bbb21cb2b3cdc5fde37a738066db40eb433cdaf4=7f18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c600052601c6020527f73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f6040527feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c454960605260206080608060006001620493e0fa60025560a060020a60805106600055600054321460015500 +dd44572feb385d8fe9932d59c83716c9eeb9ca62fee1056eec4a07a3b75da4b6=5b61c3506080511015601c5760013b506001608051016080526000565b00 2c480595fa5a2e2c59360c5b3822ce08adaf3651539472b08663d8546ad1d1a5=5a6008526000600060006000600035620927c0fa60095200 6d80de9a558f53c7cfcf2752f199c7ea1e8299b4896baac95e50bb86d570a996=6000356000526000600060206000731000000000000000000000000000000000000113620927c0fa600055600160015500 -dd44572feb385d8fe9932d59c83716c9eeb9ca62fee1056eec4a07a3b75da4b6=5b61c3506080511015601c5760013b506001608051016080526000565b00 76789aacaf2dfca81d7141317b87b177e44d376e98c5404fe25723223a064cca=622fffff60002000 bc825613a7f1964902f53f3b26ffeb59ce6fe49afe42d1926ef34898ef1b8e40=6000600060006000731000000000000000000000000000000000000110620927c0fa6009555a600a5500 a121df53d4b2823b2745d4a3ace3494ec0bb7309aa7a4780ed6ce94151d66300=5a6008526000600060006000731000000000000000000000000000000000000111620927c0fa60095200 @@ -2336,23 +2336,23 @@ e6216f38afec9b8417189b8b8a1ba657bfa12c83e0b508ec599cec9879fea3ae=7ffffffffffffff 062c0d6a7b8390dbb68e02dd2961e7b50eb2b2cf78eb4829c7f04cf213f1b8be=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600052602060006020600060046012fa60025560005160005500 a4032162c4796e1f1035c3324bb69c6ea0add9185870fcbccb38cf7ef7304226=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60005260206000620f424060006004610258fa60025560005160005500 0dad64f8e747061352994cc46b4fdfb0531ce0c2b48b3e4e290be4ed86defbb2=600160005401600055600060006000600073bbbf5374fce5edbc8e2a8697c15331677e6ebf0b620186a060005402600101fa6001556103e86000540260010160025500 -c40420a1ee6f77e82cb5c34fe4abdc52f2cc080aeef8498dff83a8ba71ead65c=600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b56305f5e100fa600055600160015500 fc5bcaa60198a96b3424b97c04893bc61345fafbc102838cee9e8b8c5872c116=600160005101600052600060006000600030612af85a03fa00 +c40420a1ee6f77e82cb5c34fe4abdc52f2cc080aeef8498dff83a8ba71ead65c=600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b56305f5e100fa600055600160015500 9542ab676d0c6ce7d0c6e9416e7c2a4dd96147323a2d681b22708bf78c506ab4=6000600060006000600073095e7baea6a6c7c4c2dfeb977efac326af552d875af250600160015500 100cba8d8715246e03dc7d526243a7ad966c48a10f2c7d7ec7658c6915aa83cf=600160005401600052690fffffffffffffffffff610402600051040260025260006000690fffffffffffffffffff61040260005104026000306104005a03fa00 -58949b3211da91b07c9ad06d44d6495a4580cb346b34666d10082209e401e5fc=6000600060006000600073095e7baea6a6c7c4c2dfeb977efac326af552d875af250600160015500 2058053e9a9efeae0b48503c903bc03234d323547b8b93ce24bf1f345711bb62=600160005101600052600060006000600030613a985a03fa00 +58949b3211da91b07c9ad06d44d6495a4580cb346b34666d10082209e401e5fc=6000600060006000600073095e7baea6a6c7c4c2dfeb977efac326af552d875af250600160015500 b468b4b9f144d1073ef24255286e8cee8bde42ee4fe9c6f48b85085b75b73f90=6000600060006000600073095e7baea6a6c7c4c2dfeb977efac326af552d875af2600055600160015500 2150529b7493f71d56e4819d9fb79f8db3cf440287e12eb6bc9fc6582f5e94c2=60016000540160005260006000600060003060e05a03fa00 43dcd231f392efde20b464e095660a55b5f11a9c7571ef3af56b508ea7228ba7=6000600060006000600073095e7baea6a6c7c4c2dfeb977efac326af552d87620186a05a03f2600055600160015500 -fa765eadc8a248a3d70cd0b9fdba76e1c2bd5295c50d3221ccc59ecf5a74ed63=600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b5620186a05a03fa600055600160015500 44cf7ed8d3657fc221e460d1b9887a932a77355a332f1258cde6343163272f37=5a60005260206000a06000600060006000306161a85a03fa00 -8fbc02cd413bdc3aa02d26b618a1dcec05a8653178ce330fa7052c937700ed5b=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60005260206000a06000600060006000306161a85a03fa00 +fa765eadc8a248a3d70cd0b9fdba76e1c2bd5295c50d3221ccc59ecf5a74ed63=600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b5620186a05a03fa600055600160015500 2c96f792bb792015a367fe3dea4d767cda7e299c30c5213f8895f0621406b391=600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b5620186a05a03fa600055600160015500 +8fbc02cd413bdc3aa02d26b618a1dcec05a8653178ce330fa7052c937700ed5b=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60005260206000a06000600060006000306161a85a03fa00 1a48f8ed328af49b6b4316cf8db1d7ebc81e40feae9043c165eb5a6f08f0652e=600060006000600073bad304eb96065b2a98b57a48a06ae28d285a71b5620186a0fa50600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b56707fffffffffffffffa600055600160015500 798b7d5a2d0d014cae0bc7b241134c6210d3b9feca3b87b2974c23d8f62d998e=60016000510160005260006000600060003062036b005a03fa00 -6bf82608cb5dea39e3ce20548dbc0573605056106a3b0bdc1167a4bf0b08fa6a=600160005101600052600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b562036b005a03fa00 9b9396aa9e585e07978689dc8b62ec7c725eafd0e428171285819af84e45fd97=600060006000600073bad304eb96065b2a98b57a48a06ae28d285a71b5620186a0fa50600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b56707fffffffffffffff4600055600160015500 +6bf82608cb5dea39e3ce20548dbc0573605056106a3b0bdc1167a4bf0b08fa6a=600160005101600052600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b562036b005a03fa00 487086a2f5d1e95f82d466c76f251420a060201051b91829529ff9c305d6bdff=60206000600060006003610258fa60025560005160005500 e87c24e32b6c2d97958b2783226a013846b06eca75fd9f4993b1a491dd1e1390=64f34578907f60055260206000602560006003611770fa60025560005160005500 1bf524364daa2d53448259bb5cec67741ac010f3cf578672ef9e562b03fa9187=64f34578907f60005260206000602560006003611770fa60025560005160005500 @@ -2373,45 +2373,45 @@ b5262b9f1c246c973d4f2c29992121fb22bd990c653f7c13181d67986be7a981=64f34578907f600 19494f63b9c3dfbefc0579c0f1fc986978c6368ee0a5260b4df29796b3463985=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa602052600060406040600073945304eb96065b2a98b57a48a06ae28d285a71b5620186a0fa60005500 e0b4e92497c879e419bfb2b86d55db1049dcc392419f555d1913a19b8a77c763=6001601f6000600073945304eb96065b2a98b57a48a06ae28d285a71b56103e8fa60005560005160015500 3a33b2afaae785160d59cca8bdcf315370a57d68fe7d3b483a3f09569a3d71a1=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa602052600260006040600073945304eb96065b2a98b57a48a06ae28d285a71b5611388fa60005560005160015500 -b81fbebd2baa32e081159c48938fa8cc62696329a332407a144f8f71b70ed53c=6001600152622fffff60002000 b2d091a3977fac39677c8ec072add00e29a503669ffd9782d00ea6dc92612069=60006000600060007320000000000000000000000000000000000000006064fa00 +b81fbebd2baa32e081159c48938fa8cc62696329a332407a144f8f71b70ed53c=6001600152622fffff60002000 +1d099e1a209c69de5aa5df2481c89515b6cc1b69231af9f2a8d3bae95c6d7f08=60006000526000600060006000600173a200000000000000000000000000000000000001620186a0f16000526001600152600160025200 cd4f4fb73990b39df91ad313fa43a22d3c76c4b2b9a2a255dac85ba115bcdd13=600060006000600073a500000000000000000000000000000000000001620186a0f46000526000516001146036576002600155603c565b60016001525b00 -6bc69fe9150c2f3cc4109f9748d5e806e632b281714fda813d393a3b49dfc841=6000600060006000600073a300000000000000000000000000000000000001620186a0f26000526000516001146038576002600155603e565b60016001525b00 -758d97983f44fb944608801da50b9831182861e1db77739e7cb93b38073b373b=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373100000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073a100000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 +82852f92116f941bace53a80171ff4aef2b70e7508bf0217ba729be9fb8c1dca=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373400000000000000000000000000000000000000114604b5760026001556051565b60016001525b30734000000000000000000000000000000000000001146074576002600155607a565b60016001525b34600114608a5760026001556090565b60016001525b00 +f66e92c07dfed6754d35c1970a83714e4bc0afd4b2c3c6d2be2273793dd6bfa2=6000600060006000600173a400000000000000000000000000000000000001620186a0f26000526000516001146038576002600155603e565b60016001525b00 404f9eb6bf83d63eae9918edc8e3fa7b7d91d1c8c281162e3ebc4ff7fd7940fa=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373300000000000000000000000000000000000000114604b5760026001556051565b60016001525b30733000000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 -6598fac36b2144a976e4e961f38735b42d9cbf37304e678a061e8428a6355e5f=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373100000000000000000000000000000000000000014604b5760026001556051565b60016001525b30735000000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 7ee389ec41c907f420e15834c14231da97e433d1c2d9cf63edf55c68c65849d3=6000600060006000600035620186a0fa60015500 -1d099e1a209c69de5aa5df2481c89515b6cc1b69231af9f2a8d3bae95c6d7f08=60006000526000600060006000600173a200000000000000000000000000000000000001620186a0f16000526001600152600160025200 -333b6ab0b37d67d33f0d7f98cd5be36363440995b6c2867733ae35f8325bc711=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373200000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073a200000000000000000000000000000000000001146074576002600155607a565b60016001525b34600114608a5760026001556090565b60016001525b00 +758d97983f44fb944608801da50b9831182861e1db77739e7cb93b38073b373b=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373100000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073a100000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 +6598fac36b2144a976e4e961f38735b42d9cbf37304e678a061e8428a6355e5f=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373100000000000000000000000000000000000000014604b5760026001556051565b60016001525b30735000000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 +6bc69fe9150c2f3cc4109f9748d5e806e632b281714fda813d393a3b49dfc841=6000600060006000600073a300000000000000000000000000000000000001620186a0f26000526000516001146038576002600155603e565b60016001525b00 b4bae1f29814deb5b8ae318af163cef95fc3eb9406e5cf9cb5c05b2404092824=6000600060006000600073a100000000000000000000000000000000000001620186a0f16000526000516001146038576002600155603e565b60016001525b00 -82852f92116f941bace53a80171ff4aef2b70e7508bf0217ba729be9fb8c1dca=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373400000000000000000000000000000000000000114604b5760026001556051565b60016001525b30734000000000000000000000000000000000000001146074576002600155607a565b60016001525b34600114608a5760026001556090565b60016001525b00 -f66e92c07dfed6754d35c1970a83714e4bc0afd4b2c3c6d2be2273793dd6bfa2=6000600060006000600173a400000000000000000000000000000000000001620186a0f26000526000516001146038576002600155603e565b60016001525b00 -3b84f25377a4292b9e4f7b1fbc695c55a0c90a25ef625a01d76461d936a4a1e2=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373b00000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073a100000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 -d6d980714c607a88cb51370f544420aefaf837fd5066c1260a06898ec3714989=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373500000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073a500000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 -fede4d962d1269c9642938e02ddb5cfec17e2699590faae4d4e71aee7d0009ce=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373300000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073a300000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 -893fdb92897484a363c0727c19c79f18ebbded0ae77bdf253e86049a9ff18c37=6000600060006000600035620186a0fa6000526000516001146024576002600155602a565b60016001525b00 -ea4a267b25f60eb4da367c26f8ccd520958d24e4094f4cbd1a2611b1abd625e4=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373400000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073a400000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 -3e76ea67b634ad55e3c5a485cf4ec19b8c31d252da94477e142b56e10bac0550=73a3000000000000000000000000000000000000016020526000600060406020600073b000000000000000000000000000000000000001620186a0f260005260005160011460505760026001556056565b60016001525b00 -dd9bbe198fa3c3f386b5fa54577aa2df29323f665ee5701c4fff0df49662225f=73a4000000000000000000000000000000000000016000526000600060206000600173b000000000000000000000000000000000000001620186a0f260005260005160011460505760026001556056565b60016001525b00 +333b6ab0b37d67d33f0d7f98cd5be36363440995b6c2867733ae35f8325bc711=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373200000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073a200000000000000000000000000000000000001146074576002600155607a565b60016001525b34600114608a5760026001556090565b60016001525b00 d9099ee8b971d5cfff89f8f538c03a7dc0db58ac2c15eb282b3a490fb6ebab54=73a500000000000000000000000000000000000001600052600060006020600073b000000000000000000000000000000000000001620186a0f4600052600051600114604e5760026001556054565b60016001525b00 57b4bb4a6cdf644ff79a4f0616ec39a559001128c1f801d817854a3ad163a859=73a1000000000000000000000000000000000000016000526000600060206000600073b000000000000000000000000000000000000001620186a0f160005260005160011460505760026001556056565b60016001525b00 +dd9bbe198fa3c3f386b5fa54577aa2df29323f665ee5701c4fff0df49662225f=73a4000000000000000000000000000000000000016000526000600060206000600173b000000000000000000000000000000000000001620186a0f260005260005160011460505760026001556056565b60016001525b00 +d6d980714c607a88cb51370f544420aefaf837fd5066c1260a06898ec3714989=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373500000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073a500000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 +ea4a267b25f60eb4da367c26f8ccd520958d24e4094f4cbd1a2611b1abd625e4=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373400000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073a400000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 +893fdb92897484a363c0727c19c79f18ebbded0ae77bdf253e86049a9ff18c37=6000600060006000600035620186a0fa6000526000516001146024576002600155602a565b60016001525b00 fec8703d9ae566b4ed571b0841440a97b05c473c43f1f67cf70c6b8b8645e646=73a1000000000000000000000000000000000000016000526000600060206000600173b000000000000000000000000000000000000001620186a0f16000526001600152600160025200 +fede4d962d1269c9642938e02ddb5cfec17e2699590faae4d4e71aee7d0009ce=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373300000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073a300000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 +3e76ea67b634ad55e3c5a485cf4ec19b8c31d252da94477e142b56e10bac0550=73a3000000000000000000000000000000000000016020526000600060406020600073b000000000000000000000000000000000000001620186a0f260005260005160011460505760026001556056565b60016001525b00 +3b84f25377a4292b9e4f7b1fbc695c55a0c90a25ef625a01d76461d936a4a1e2=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373b00000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073a100000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 +a0a9ad09b174a0e4b69fd8e66e3538164ef7f66206e3e61ea165eb9404f75b5c=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373100000000000000000000000000000000000000014604b5760026001556051565b60016001525b30731000000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 adf9f5e900872addcd20976752fa8c397bd63527cd62e41ad2884f10e2e22de9=6000600060006000731000000000000000000000000000000000000001620186a0fa6001556000600060006000731000000000000000000000000000000000000002620186a0fa6002553360035534600455326005553060065500 98a8f573ecd230d28465b6ae3ec5958c28c7fd153e67a05c7277cd4ea862f628=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373100000000000000000000000000000000000000014604b5760026001556051565b60016001525b30731000000000000000000000000000000000000002146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 -a0a9ad09b174a0e4b69fd8e66e3538164ef7f66206e3e61ea165eb9404f75b5c=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373100000000000000000000000000000000000000014604b5760026001556051565b60016001525b30731000000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 -4541ff509f4b7f945bfbb98bfc5c9a5d24b0ea229a33f66a2b8ff30a55ba1d36=73b0000000000000000000000000000000000000026000526000600060206000600a73a000000000000000000000000000000000000002620186a0f100 -87bc076915cabd26d946ac8ff1d7ef9f1490d8c3d912cf9baa9c74fe74992b45=73c500000000000000000000000000000000000002600052600060006020600073a000000000000000000000000000000000000002620186a0f400 e6833afb60bec0da2d810a52522e631d53e19fa2b77f83f5a8b06a2133185be3=600060006000600060006000356203d090f160015500 -c70e6cd29563926bb175ad7acebc55c469d5b5df8a99b82ad3df330ae8580133=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373400000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073c400000000000000000000000000000000000002146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 +87bc076915cabd26d946ac8ff1d7ef9f1490d8c3d912cf9baa9c74fe74992b45=73c500000000000000000000000000000000000002600052600060006020600073a000000000000000000000000000000000000002620186a0f400 +2e7f9ae3b2d4a46fc16e0a417b8562802fc3d41504ef2ac83c5fe3b88ae06175=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373300000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073c300000000000000000000000000000000000002146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 fdc379e7a00f997a69a26b3e5111261b02b9a43dde5f67400e1244f288a45f63=73b0000000000000000000000000000000000000026000526000600060206000600073a000000000000000000000000000000000000002620186a0f100 5e9e34dec2ea7ebe341e51daa89b9da17cab841d1caa960179a0c6c282ffd6c8=73c3000000000000000000000000000000000000026000526000600060206000600073a000000000000000000000000000000000000002620186a0f200 -2e7f9ae3b2d4a46fc16e0a417b8562802fc3d41504ef2ac83c5fe3b88ae06175=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373300000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073c300000000000000000000000000000000000002146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 -5cb5e5a7280bc21b84bd72fd523c26b90bad35b2e8de1077a2d48eae4c13940e=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373500000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073c500000000000000000000000000000000000002146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 -e4de4eb0dac078a046039bbea1e5b917af7de4f852be503d9cd871d4b8ceea93=600060006000600060003561c350fa60005500 ca56f36ac47069406dbd2855d9d6f06d0cf047655c766ce2d20ab1fa3cfc7856=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373a00000000000000000000000000000000000000214604b5760026001556051565b60016001525b3073b000000000000000000000000000000000000002146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 +c70e6cd29563926bb175ad7acebc55c469d5b5df8a99b82ad3df330ae8580133=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373400000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073c400000000000000000000000000000000000002146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 +e4de4eb0dac078a046039bbea1e5b917af7de4f852be503d9cd871d4b8ceea93=600060006000600060003561c350fa60005500 +5cb5e5a7280bc21b84bd72fd523c26b90bad35b2e8de1077a2d48eae4c13940e=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373500000000000000000000000000000000000000114604b5760026001556051565b60016001525b3073c500000000000000000000000000000000000002146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 +4541ff509f4b7f945bfbb98bfc5c9a5d24b0ea229a33f66a2b8ff30a55ba1d36=73b0000000000000000000000000000000000000026000526000600060206000600a73a000000000000000000000000000000000000002620186a0f100 62ed67099b2a0a74b3c9a5c69708fd2e705ce40103205bdf48c02cc44ff795f4=73c4000000000000000000000000000000000000026000526000600060206000600173a000000000000000000000000000000000000002620186a0f200 -1f49a79208f808b39c987e3b9a37e06ea7e0037376dc3fe45b453f2f1b19f5f3=6000600060006000731000000000000000000000000000000000000003620186a0fa00 46cfad55e6cbc241c831a87943487a7f8d3ff08287d0728812eca0b8ad5e9622=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373100000000000000000000000000000000000000214604b5760026001556051565b60016001525b30731000000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 +1f49a79208f808b39c987e3b9a37e06ea7e0037376dc3fe45b453f2f1b19f5f3=6000600060006000731000000000000000000000000000000000000003620186a0fa00 def5a1a160fb678f39f631f5ea5031be5d2f6a89f4e7acc87fdf8f647813076f=6000600060006000600035620927c0fa60015500 ef66cde67e17bb3af5f101543f93df462bfa5ec7d2f5b7d459d4b4f1e63d50c1=600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b6161a8fa60015500 6d779126382ca62b73d591ad0230bda4701f11eb9b4e9c15aef8a9dc1e9fdcad=600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b611388fa00 @@ -2420,102 +2420,102 @@ ef66cde67e17bb3af5f101543f93df462bfa5ec7d2f5b7d459d4b4f1e63d50c1=600060006000600 f0a42cef1d3367adecb417fddfe30066452b568dd64eed7a67a74cd80a2c451a=60006000600060006000731000000000000000000000000000000000000000620186a05a03f1600055600160015500 a0f6b36645b102ba27d6bb08d20028bc9e1a027b2889cbf272845f825aa0ac88=600060006000600073a0000000000000000000000000000000000000005afa00 55c56c0457696b45ea272ebaa736e6aa2d5a09f24adc2b3e731ee01896371c8a=600060006000600073b0000000000000000000000000000000000000005afa00 -318f3bc4238b8114f152600cf9fdc7bd6771f8f066a72d613c06071fa23cd7c1=60005160010160005200 c84ec6b401222601c68ccddf2f4cb0dc5daf3815e2cc85de439948e7f6ff979a=6103526000526000600060206000600073a0000000000000000000000000000000000000006127105a03f1600055600160015500 +318f3bc4238b8114f152600cf9fdc7bd6771f8f066a72d613c06071fa23cd7c1=60005160010160005200 6083b5f500257ccd1475297c47c0142c27992657c96be39c8518e78943d3f54b=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa602052600060006040600073945304eb96065b2a98b57a48a06ae28d285a71b5617530fa60015560005160025500 639df5cf6341d6b4c631c0237f9bf4779e4fc65ce032bd73f2494793ea32708b=60006000600060007310000000000000000000000000000000000000016707fffffffffffffffa60015560006000600060007310000000000000000000000000000000000000026707fffffffffffffffa60025560006000600060007310000000000000000000000000000000000000036707fffffffffffffffa60035560006000600060007310000000000000000000000000000000000000046707fffffffffffffffa60045560006000600060007310000000000000000000000000000000000000056707fffffffffffffffa60055560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa60065560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa60075560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa60085560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa60095560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa600a5560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa600b5560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa600c5560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa600d5560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa600e5560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa600f5560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa60105500 54e49fc3421fb9c8baa336e22373997544c2384a364621f58cb3f6293d0464ef=60006000600060007310000000000000000000000000000000000000016707fffffffffffffffa60015560006000600060007310000000000000000000000000000000000000026707fffffffffffffffa60025560006000600060007310000000000000000000000000000000000000036707fffffffffffffffa60035560006000600060007310000000000000000000000000000000000000046707fffffffffffffffa60045560006000600060007310000000000000000000000000000000000000056707fffffffffffffffa60055560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa60065560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa60075560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa60085560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa60095560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa600a5560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa600b5560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa600c5560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa600d5560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa600e5560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa600f5560006000600060007310000000000000000000000000000000000000066707fffffffffffffffa6010556000600035146102af575a6102b7565b622fffff6000205b00 -7d62497b16e28a08693cef3aea82332aa0b1561781d889c69cf955622c557dde=600060006000600073094f5374fce5edbc8e2a8697c15331677e6ebf0b622dc6c0fa505a60015500 bda26e862bfee374bea68999f70d8e5842cc79afc545ddba0af90cb2a383258b=600060006000600073094f5374fce5edbc8e2a8697c15331677e6ebf0b6201fbd0fa505a60015500 +7d62497b16e28a08693cef3aea82332aa0b1561781d889c69cf955622c557dde=600060006000600073094f5374fce5edbc8e2a8697c15331677e6ebf0b622dc6c0fa505a60015500 +097b9519ac8275905c177e3b6f7cc42b63bbaa224382603f1baa61db996b6d53=611f406000611f40600073094f5374fce5edbc8e2a8697c15331677e6ebf0b622dc6c0fa505a60015500 34676a20c227fbeb2467c2781ea554fb488bbe4c1b820cba1e9a12b99e6b6406=611f406000611f40600073094f5374fce5edbc8e2a8697c15331677e6ebf0b6201fbd0fa505a60015500 037054064d2afc539fe9ed7ccb779971403d394ce2bcfbcbe999c5fbb09b5f96=5a60005200 -097b9519ac8275905c177e3b6f7cc42b63bbaa224382603f1baa61db996b6d53=611f406000611f40600073094f5374fce5edbc8e2a8697c15331677e6ebf0b622dc6c0fa505a60015500 2f32bfbb2e5247f725e01a4151857de7cabd85f8da8a135394402ac23f913774=6015600052602060206020600073b94f5374fce5edbc8e2a8697c15331677e6ebf0b611b58fa5060005160005560205160015560406000f300 358f48d33170ae527eb2db1e7a59eaf9d5cca8797c10de562e117136502cf231=6001601f6001601e73b94f5374fce5edbc8e2a8697c15331677e6ebf0b6107d0fa506000516000556002601ef300 -f893caffc84199ad12a96155f4955dbc2c9fe5eed0fa06f9b42e3364eb38cf35=600054600101600055600060006000600073b000000000000000000000000000000000000000620249f0fa600155600060006000600073d000000000000000000000000000000000000000620249f0fa60025500 7a5df1bde40bb02b724eafff4243f6134e6e7d3526333fe9f8c1c85ea370818d=600060006000600073c00000000000000000000000000000000000000061c350fa50622fffff60002000 +f893caffc84199ad12a96155f4955dbc2c9fe5eed0fa06f9b42e3364eb38cf35=600054600101600055600060006000600073b000000000000000000000000000000000000000620249f0fa600155600060006000600073d000000000000000000000000000000000000000620249f0fa60025500 13c00864a3879ed5f8d801b044d0edb2db81ecb48eb35ea08961c8e18dd466ba=600060006000600073c00000000000000000000000000000000000000061c350fa50600160015200 50b35dca6e8923fb30f02c47e37dd62e6bdaeefc968557c43a54f742f72bddc3=60016000fd00 ce064fd2d509a2a0db6f14d867a25cdccf0d953da247155bb71531503b66a2e3=600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350fa6000553d60015500 f3e5f529719929897d5d04c7b0cdbe69ec76bfe6aebba78cd50ccf82343e7308=5a600055600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61ea60fa600155622fffff600020505a60645500 -6c9893edc37020e5544719f667e6d96a047f452a16abf2012732cc80e3b14692=73c94f5374fce5edbc8e2a8697c15331677e6ebf0bff00 fe3cc22b069757e210195ba7849dce069c1bc14d0f9ac7bc1536ae26d0d92ba0=600060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b620186a0fa50622fffff60002000 -f947f4791642e13dacac38aaf6f22eef10019f8b3d1f4181a4b77cae1d1ac35f=60006000600060006000732000000000000000000000000000000000000003619c40f150600160015200 +6c9893edc37020e5544719f667e6d96a047f452a16abf2012732cc80e3b14692=73c94f5374fce5edbc8e2a8697c15331677e6ebf0bff00 5474525e8a4ea3b05ca1910e3be360becdef41f627bb6f49e1843f55c899b3cf=600a6001a0600160015200 -6034354c9b6e85bc2acc17ebb92030ce865861ae2a27ac1d07844358c8f3966c=60006000600060006001733000000000000000000000000000000000000003619c40f250600160015200 8fede10b0db14fe3e0d42fecb434cd7a7d3ae283dbbb63e5d821961500107361=6000600060006000600035620186a0fa600055600160015500 7115c22971934f96b6570f7e3aa596bb52d623ffb9c43aff0907bfc83ae6fb20=600160015500 -1c0c7da972d6d49d681e09bbbcbbb1fc7dcab9c32316d8016a0337311aef6cf9=60006000600060006001731000000000000000000000000000000000000001620186a0f16000556000600060006000731000000000000000000000000000000000000001620186a0fa60015560006000600060006001731000000000000000000000000000000000000001620186a0f16002555b61c3506080511015608f5760013b506001608051016080526073565b00 -c10efa0cbca70244752be297ca37f60917710b83d4b1be7cd5d34735be272749=60006000600060006001731000000000000000000000000000000000000002620186a0f16000556000600060006000731000000000000000000000000000000000000002620186a0fa60015560006000600060006001731000000000000000000000000000000000000002620186a0f160025500 -f5219dd69682938778c67a55be416d2a8cb7ce6606c81f9f84b825e12596d207=600160015200 +6034354c9b6e85bc2acc17ebb92030ce865861ae2a27ac1d07844358c8f3966c=60006000600060006001733000000000000000000000000000000000000003619c40f250600160015200 +f947f4791642e13dacac38aaf6f22eef10019f8b3d1f4181a4b77cae1d1ac35f=60006000600060006000732000000000000000000000000000000000000003619c40f150600160015200 efcbe7cad146b3569f6f9ed022c67902de9346ebf2b9dbbf170169d1c8441535=6000600060006000600060003562055730f100 -81da54faf4b0963c7813553b36a4c6c47cae9d75126b211993e84f83f9156485=600160015260015460015500 20779731aefa455ec0d2a73e6dd65d49097e17935e96551f94116d28e20ffc25=60006000600060006001731000000000000000000000000000000000000001620186a0f16000556000600060006000731000000000000000000000000000000000000001620186a0fa60015560006000600060006001731000000000000000000000000000000000000001620186a0f160025500 -ad6840ba504c8dca4fd3c27940133e7f437ac30748b8ecc256f4817a43954267=600060006001f06000556000600060006000600054620493e0fa60015500 -2a3b69feb75aad4db6f3008ae9c18771ce6d868e5649242fd14db009847ce1d8=6d6460016001556000526005601bf3600052600e60126001f06000556000600060006000600054620493e0fa6001555b61c3506080511015604b5760013b50600160805101608052602f565b00 +f5219dd69682938778c67a55be416d2a8cb7ce6606c81f9f84b825e12596d207=600160015200 +c10efa0cbca70244752be297ca37f60917710b83d4b1be7cd5d34735be272749=60006000600060006001731000000000000000000000000000000000000002620186a0f16000556000600060006000731000000000000000000000000000000000000002620186a0fa60015560006000600060006001731000000000000000000000000000000000000002620186a0f160025500 +81da54faf4b0963c7813553b36a4c6c47cae9d75126b211993e84f83f9156485=600160015260015460015500 +1c0c7da972d6d49d681e09bbbcbbb1fc7dcab9c32316d8016a0337311aef6cf9=60006000600060006001731000000000000000000000000000000000000001620186a0f16000556000600060006000731000000000000000000000000000000000000001620186a0fa60015560006000600060006001731000000000000000000000000000000000000001620186a0f16002555b61c3506080511015608f5760013b506001608051016080526073565b00 f567658186b9fcfcc93374e8b6b6c3888747b8a676569d572b0f833ef1f79fd5=60006000600060006000600035620927c0f100 +ad6840ba504c8dca4fd3c27940133e7f437ac30748b8ecc256f4817a43954267=600060006001f06000556000600060006000600054620493e0fa60015500 184c22c4d3b69c05d98a7888a713a908bc8fa4a3c0524ed972798a1df246524d=6d6460016001556000526005601bf3600052600e60126001f06000556000600060006000600054620493e0fa60015500 +2a3b69feb75aad4db6f3008ae9c18771ce6d868e5649242fd14db009847ce1d8=6d6460016001556000526005601bf3600052600e60126001f06000556000600060006000600054620493e0fa6001555b61c3506080511015604b5760013b50600160805101608052602f565b00 c162de6a05cb28543af03d0fdcdbd4afaa8dc91627e8d951d30890f03c08b28f=766d600060006000600030620186a0fa600052600e6012f3600052601760096001f06000556000600060006000600054617530fa600155600160025500 -9c5629db4116e8b9ee4529087d3132274afd004cb5d6a9c975e696785820c364=6000600060006000731000000000000000000000000000000000000002620249f0fa00 bab3a4ca0f0d3f77feea2d4793233f045bf315fe47f579fee2d2dd241ce192a6=600160016000f000 024a0cb9c27cdf47846d83d334c827a21d20974fe1c1cbbcc62f739bb0a07b60=6000600060006000600035620493e0fa60005500 dece64523169799927485282e2fb02181ec6935ad838ddc51b11be52bd5101de=60006000600060006000731000000000000000000000000000000000000002620249f0f100 373127720fce833627b38d2114f193a8adeabb9d3228a1458399c665c67b8a82=6000600060006000731000000000000000000000000000000000000002620249f0f400 +9c5629db4116e8b9ee4529087d3132274afd004cb5d6a9c975e696785820c364=6000600060006000731000000000000000000000000000000000000002620249f0fa00 2f7920b36306229d530687f7eca3244cb65799eb14fcbafe7a6923e44212308e=7f5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6600052600060006000600073aaae7baea6a6c7c4c2dfeb977efac326af552d8761c350fa5060005160005500 f56bfbe6b2842f1010a7139b0bacab690f400ac718429a8d12cde6c54e3b396b=7f5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6600052600060006020600073aaae7baea6a6c7c4c2dfeb977efac326af552d8761c350fa5060005160005500 efc9308c2a34f83b2767bf47bdbd05a6867d7cae92e0ffc09862b842eec8e0a0=7f5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6600052602060006000600073aaae7baea6a6c7c4c2dfeb977efac326af552d8761c350fa5060005160005500 0aa22eb4acc24219c0c94948b65c062352a4a77862c669dcadfd56efe48c54d7=7f5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6600052600a60006000600073aaae7baea6a6c7c4c2dfeb977efac326af552d8761c350fa5060005160005500 +b06fe602262b042548fa44e76b9295acc2952d28206385c9e67347d1a89dd95f=6000600060006000600035620186a0fa60005500 4b0ed9836107b1329850e71c8c4996b671b6f519a521ba8e05457e55ad4fa6c3=60006000600060006000731000000000000000000000000000000000000002620186a0f26000526000516001146038576001600155603e565b60016001525b00 4fb0bde6147b7281a0ed0205b519c2c569f4280507f6e678747805ea9ac929bd=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373100000000000000000000000000000000000000114604b5760026001556051565b60016001525b30731000000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 -b06fe602262b042548fa44e76b9295acc2952d28206385c9e67347d1a89dd95f=6000600060006000600035620186a0fa60005500 -802c7ddf3b32e2f65e55845250538d5e7502ede4769c36c0d55cc2f48eaf317a=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373100000000000000000000000000000000000000114604b5760026001556051565b60016001525b30731000000000000000000000000000000000000002146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 e0a4ebf1aad3532ab57071d51cbac3950a28db80dfacef64d3f18b29e320e723=60006000600060006000731000000000000000000000000000000000000002620186a0f16000526000516001146038576001600155603e565b60016001525b00 +802c7ddf3b32e2f65e55845250538d5e7502ede4769c36c0d55cc2f48eaf317a=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373100000000000000000000000000000000000000114604b5760026001556051565b60016001525b30731000000000000000000000000000000000000002146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 48f7bb89bf24e39f4e6a8fdde75e16afd9e340770e6ad10a7f50dc4e5653600e=6000600060006000731000000000000000000000000000000000000002620186a0f46000526000516001146036576001600155603c565b60016001525b00 4b09828843cc97f3ebe8366dd6d58b458d4db4ebbdf08a36a51fd91e3d8d626a=3273a94f5374fce5edbc8e2a8697c15331677e6ebf0b1460225760026001556028565b60016001525b3373100000000000000000000000000000000000000014604b5760026001556051565b60016001525b30731000000000000000000000000000000000000001146074576002600155607a565b60016001525b34600014608a5760026001556090565b60016001525b00 bc8fbd8026656526c8a4c4740804bcbf30242133f1017f5a11c4adb16c196101=6000600060006000731000000000000000000000000000000000000002620186a0fa6000526000516001146036576001600155603c565b60016001525b00 +273b9c83b9e6837aacaaf9d5ab1a3435f2cd460d8d8b62e24294906cd77c64f8=600060006000600060006000356207a120f100 770a7da5d03a877e5569478a32fe9faa47dcff9cfea041326718e8beb8f2ae1a=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa602052600260006040600073b45304eb96065b2a98b57a48a06ae28d285a71b56bfffffffffffffffffffffffffa60005560005160015500 3ef2444fa2b3daeb1a7d270d4ce04d5b1d80ce37437d04ff69fa0032b34d7f24=7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa602052600260006040600073945304eb96065b2a98b57a48a06ae28d285a71b56bfffffffffffffffffffffffffa60005560005160015500 -273b9c83b9e6837aacaaf9d5ab1a3435f2cd460d8d8b62e24294906cd77c64f8=600060006000600060006000356207a120f100 9645880325e712facf18c761a16f9a38c5ce47466da9e94ce97750c6637a057c=600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b5622dc6c1fa60005560005160015500 221710d943fb41ef7392a65dd3437e16e9eab8b9653406e4a4f46358c86b98ad=600260006040600073945304eb96065b2a98b57a48a06ae28d285a71b561c350fa600055600160015500 c62e60fc7991dacd07ddfd072a372ca4b4ae04834fcccf52407067f16496170d=6001600060006000600073945304eb96065b2a98b57a48a06ae28d285a71b5600afa0160005560005160015500 6b87c77e280a7e4b563480328921528b7f504aa7afa01b2aa44713a744b4fe7e=6040600060406000731000000000000000000000000000000000000001611770fa6000555a60015500 3e7813be56cd8272e377454000b6f36f93cf00b7143e47742bd22081ba0b0e3d=6040600060406000731000000000000000000000000000000000000001611770fa6000555a60015500 -c3d241b2d7d6df2e8a3bb5dafb4d8aef3b57ca7da39a22626598159f9a7cfc0e=3460005260206000f300 68db2808557a587eb39bf7032a6daeb8f16cffa68127a01739657a6e083463da=602060006000600073c94f5374fce5edbc8e2a8697c15331677e6ebf0b61c350fa60005560005160015500 +c3d241b2d7d6df2e8a3bb5dafb4d8aef3b57ca7da39a22626598159f9a7cfc0e=3460005260206000f300 95676c7e03b016f7dc9709c8ab4c1a36f1d8f6aac6370a232dfd90965b932a72=6000600060006000600a73094f5374fce5edbc8e2a8697c15331677e6ebf0b620186a0f100 c825eb8fb8c7d5e7333ed863327b648155c9ba1604e96cd2331e542043147066=6001600052336020523460405230606052326080523660a0523860c0523a60e05200 -efd0f30d72ee28f69abbbede2fea8c18ee14287e120bf79298326b0755248753=60406000604060007320000000000000000000000000000000000000026203d090fa00 -b7ad6bdf4604f9b8b757bfe05fc4ba38e10467d7d42755f7dcecce6e9530d574=60406000604060007310000000000000000000000000000000000000026203d090fa00 0540bc699d4dd5b0fa3fdb6efd5b7ceefa1b6d3cce742aedb30ce5396e218694=604060006040600073200000000000000000000000000000000000000162055730fa60005500 c4b8deae984aa5efa988bad6491930ff406a7685f87e5a4480b49b10eda1d492=604060006040600073100000000000000000000000000000000000000162055730fa60005500 +b7ad6bdf4604f9b8b757bfe05fc4ba38e10467d7d42755f7dcecce6e9530d574=60406000604060007310000000000000000000000000000000000000026203d090fa00 +efd0f30d72ee28f69abbbede2fea8c18ee14287e120bf79298326b0755248753=60406000604060007320000000000000000000000000000000000000026203d090fa00 475445062a8cf19a64a93251f1496ddc035ddf1d3501085b598c29f711ddd65e=60016002526040600060406000732000000000000000000000000000000000000002614e34fa50600160205200 -bf76068be5cbdd2c3bc4457cdf2a51627b1611a16f0db15efab83799fec63dbd=5b61c3506080511015601c5760013b506001608051016080526000565b00 -0e24e02427a8e83c8536e59980af01ea90a99ee69a0e6e8e9c78595bf5069796=6040600060406000732000000000000000000000000000000000000001620249f0fa60005500 0d4950aaa1ad9b0da6182d02ccdd623c45ffeaf725f3f02c035ce9b07ad8ff3c=600160025260406000604060007310000000000000000000000000000000000000026201d4d4fa50600160205200 43027427ed708586ce8369748f012aee319639dbc13862ef35e6758474f4dfcc=6001600255600160025200 +bf76068be5cbdd2c3bc4457cdf2a51627b1611a16f0db15efab83799fec63dbd=5b61c3506080511015601c5760013b506001608051016080526000565b00 +0e24e02427a8e83c8536e59980af01ea90a99ee69a0e6e8e9c78595bf5069796=6040600060406000732000000000000000000000000000000000000001620249f0fa60005500 9b0917aa70e74b7a3fdee1ab3685a8f2bb3d67a2e22ef4e1ff7e6fb46cd1b9f1=60016002526040600060406000732000000000000000000000000000000000000002620186a0fa50600160205200 -89dd62dafb714bb10931b9cb0574f03473f13c6754485aa5dcee061fc35e4188=6040600060406000731000000000000000000000000000000000000001620249f0fa60005500 f9d01335d8b800beefecb45a651309be0032a55978e09d8885a3f8a73c05dcf3=60016002526040600060406000731000000000000000000000000000000000000002620186a0fa50600160205200 -0350d4c406bbfc1aad8dd7e25e859c0464b5310206cc0ad8ed407a1a4b1a4ebc=5b61c3506080511015601c5760013b506001608051016080526000565b00 55dec7cefedba450204c1fa7b3b8cd7f714986da078e4a7cfb258b75cdff51c6=60016002553460055500 +89dd62dafb714bb10931b9cb0574f03473f13c6754485aa5dcee061fc35e4188=6040600060406000731000000000000000000000000000000000000001620249f0fa60005500 +0350d4c406bbfc1aad8dd7e25e859c0464b5310206cc0ad8ed407a1a4b1a4ebc=5b61c3506080511015601c5760013b506001608051016080526000565b00 a3a22b67827c10d53c308beb9aa880b926a6e7b2be5dabffc019492549a2033a=60016002526040600060406000731000000000000000000000000000000000000002620186a0fa50600160205200 97f5b7335c421b410bfd81628a8aa34a518662cd4a9557511718c845bcb913a1=60016002526040600060406000732000000000000000000000000000000000000002620186a0fa50600160205200 -d417f5e802616309071f6240ecfb572fbac573a4a1302cb560a13ab9db54313f=6001602052604060006040600073100000000000000000000000000000000000000261c350fa50731000000000000000000000000000000000000000ff00 e1c54b74c6d6e9606beae5fa041264d84e877eaa3aacd60cf2248c2ebc635c4b=600160025200 -c4a016d328743cacfb9b5b56a32359bb310ca5eeba7ac2cf55a4c56e1d61f4ec=60016003526040600060406000732000000000000000000000000000000000000002620493e0fa50600160205200 -aa4ee1aba16e5ec7311ebafdb9a904ef0f85d6e03a988740aa8b6154a042e4b4=600160035200 +d417f5e802616309071f6240ecfb572fbac573a4a1302cb560a13ab9db54313f=6001602052604060006040600073100000000000000000000000000000000000000261c350fa50731000000000000000000000000000000000000000ff00 1c123276d0c78004ecc4b697fdca7a2f8471bba878a800d994589440648d6b02=6001600352604060006040600073100000000000000000000000000000000000000262061a80fa50600160205200 +c4a016d328743cacfb9b5b56a32359bb310ca5eeba7ac2cf55a4c56e1d61f4ec=60016003526040600060406000732000000000000000000000000000000000000002620493e0fa50600160205200 608b30da060f19e5fc9e5d61f053f9328419e69eb55e1e047ef5c794a46870ed=60406000604060007310000000000000000000000000000000000000016209eb10fa60005500 -d97da057751123f2962112c654c0ef960c9bb07824891565d269b23fca4b50d6=600160035260406000604060007320000000000000000000000000000000000000036203d090fa50600160205200 +aa4ee1aba16e5ec7311ebafdb9a904ef0f85d6e03a988740aa8b6154a042e4b4=600160035200 ebab6cfa1bd31444a856b975c84cecd01192b10d0ae549de5c5157421ced65f3=600160035260406000604060007310000000000000000000000000000000000000036203d090fa50600160205200 +d97da057751123f2962112c654c0ef960c9bb07824891565d269b23fca4b50d6=600160035260406000604060007320000000000000000000000000000000000000036203d090fa50600160205200 +666da63f5fd3ab6bd27a0b52c3ef64ca80ca09a7b19156ed8a98ccc39da63dc1=6040600060406000731000000000000000000000000000000000000001620927c0fa60005500 +c664866980e1fe34d66555a12a11c700443137875c513dae8560a35d7ce52451=60016003526040600060406000731000000000000000000000000000000000000002620493e0fa50600160205200 0da1e7c490f7ab109d2b4d4403422f7b69e872c06663fcc367152053a71bd546=60406000604060007310000000000000000000000000000000000000016207a120fa60005500 -32f981e49e2bf55449a299597dd2dd99a2f0f84b15b1a2ba150851330292f499=60016003526040600060406000732000000000000000000000000000000000000002620493e0fa50600160205200 +b849c540c6aba10657a3a41940664b59713131e1f77b94296cd06898d5a61082=6001600355600160035200 9939830cf46abeb56072966cb23bd36fa74c759cdb3d4e9927696fcf58d7c342=5b61c3506080511015601c5760013b506001608051016080526000565b00 -c664866980e1fe34d66555a12a11c700443137875c513dae8560a35d7ce52451=60016003526040600060406000731000000000000000000000000000000000000002620493e0fa50600160205200 +32f981e49e2bf55449a299597dd2dd99a2f0f84b15b1a2ba150851330292f499=60016003526040600060406000732000000000000000000000000000000000000002620493e0fa50600160205200 3adbf55988966011734f99fd354b5f5060d6ef2e26eaa536f7073f28f896e919=600160035260406000604060007320000000000000000000000000000000000000036201d4d4fa50600160205200 -b849c540c6aba10657a3a41940664b59713131e1f77b94296cd06898d5a61082=6001600355600160035200 -666da63f5fd3ab6bd27a0b52c3ef64ca80ca09a7b19156ed8a98ccc39da63dc1=6040600060406000731000000000000000000000000000000000000001620927c0fa60005500 897e2856814c86522ec62ec68aa3854bbb296e9f4344509ee40ef880d035a5d8=600160035260406000604060007310000000000000000000000000000000000000036201d4d4fa50600160205200 cf13fb5722b4a8985f42be5ed89f861c4a3a7062f99ea272cc8b9082cd69901d=6040600060406000731000000000000000000000000000000000000001620aae60fa6000556001606f5500 1b4f611b885a40174a01499c4b21aa8c1007f4f54a7d31225cc1a5b972507efa=600160035260406000604060007310000000000000000000000000000000000000026206ddd0fa505b61c350608051101560445760013b506001608051016080526028565b00 @@ -2524,156 +2524,156 @@ ad1ee4098d7c69488a4fd01896a5f80b174952b5441237a8530b8adb63683f70=600160035260406 ed71de6e2f3390d6c2d109755f0e145366ac1c9eadd322ec8175da3dd631a077=600160035260406000604060007310000000000000000000000000000000000000036201d4d4fa50600160205200 655c5f203b56cf8a6ceeeecd79aeeaf34123b11bad8304a394ae71f5435459b3=604060006040600073100000000000000000000000000000000000000162092856fa6000556001606f5500 186822b494b604d7c7c1057eb6b150e7c9eeada500f72204647ff8fd3bc941d6=604060006040600073100000000000000000000000000000000000000262061ad0fa00 -b83f615d1696590a7ec4f1eac553cf448d43aacb673bd8157b785fd7d96745e1=5b61c3506080511015601c5760013b506001608051016080526000565b60406000604060007310000000000000000000000000000000000000036201d4d4fa00 74a61aa710c4800f07a12eca87ae6bc0bf81c55f7dcfd2b74550728fdca64693=6040600060406000731000000000000000000000000000000000000001620927c0fa600055600160015500 -c7a09115226858fd260a49d16e4d014933a2dbb76991bc073444ae738ae2804c=600160035200 +b83f615d1696590a7ec4f1eac553cf448d43aacb673bd8157b785fd7d96745e1=5b61c3506080511015601c5760013b506001608051016080526000565b60406000604060007310000000000000000000000000000000000000036201d4d4fa00 e025a502fa40535c61169eb1f08b3af3178c5425ad2869fdfb1afbb6aa12d7d8=6040600060406000731000000000000000000000000000000000000001620249f0fa600055600160015500 6888874442bc917f71d01f5f084db2c729e88e53b009416512b04b602a7964b8=60016003526040600060406000731000000000000000000000000000000000000002620186a0fa50600160205200 +c7a09115226858fd260a49d16e4d014933a2dbb76991bc073444ae738ae2804c=600160035200 ab014f21cbf2b3399565c003383739c0d065011cf33c294534c6760d98d40374=6001600352604060006040600073100000000000000000000000000000000000000361c350fa50731000000000000000000000000000000000000001ff00 -dc31386900fa73974feded44a0f16b4f7aa29e6dd2291eed72a741ccbcf968c1=731000000000000000000000000000000000000000ff604060006040600073100000000000000000000000000000000000000361c350fa00 4a6e960d19852283c7b72f20fa7b04a88b051d7a3a613063709e1140d3c0be3d=6040600060406000731000000000000000000000000000000000000001620249f0fa600055600160015500 -915a92c2026d93de0dffa46b7f37c7003694e3daa65fa556f4c4547a51cd1d76=6001600152604060006040600073100000000000000000000000000000000000000163017d7840fa50600160025200 +dc31386900fa73974feded44a0f16b4f7aa29e6dd2291eed72a741ccbcf968c1=731000000000000000000000000000000000000000ff604060006040600073100000000000000000000000000000000000000361c350fa00 afd1db53c3f5b489497a200ff589aeb007d9979d0bfd33eead6ee0732c95e02b=60016003526040600060406000731000000000000000000000000000000000000002620f4240fa50600160045200 +915a92c2026d93de0dffa46b7f37c7003694e3daa65fa556f4c4547a51cd1d76=6001600152604060006040600073100000000000000000000000000000000000000163017d7840fa50600160025200 690ca0710fb92000f668d54aaf7784300930602b36dc94246ebacba4779bfd5b=600160055260406000604060007310000000000000000000000000000000000000016207a120fa50600160065200 +3974d5c71fb125a5353bd7c8c9b5ff93aa74a5a403969d8de6fef553ff4e3621=60016003526040600060406000731000000000000000000000000000000000000002620493e0fa50600160035200 34d8761a05f3b29aeed5ec35a59210bbd9e9f0c2682576ae4f8b6f17a48306b4=600160035260406000604060007320000000000000000000000000000000000000036203d090f450600160035200 +6938c921e3d5b48ac019c8d0ed785c8f64885bfa7717c8c7fca15a949ab56c55=60016003526040600060406000732000000000000000000000000000000000000002620493e0fa50600160035200 afc9088819610f696ff1c705d58e85123fa8c72d603a1dc1d4bd6e13a7a1f5ec=604060006040600073100000000000000000000000000000000000000162055730fa600055600160035200 f332cc951eee86dba59a8b9d2c93d96d40801c25c6068c3bb59fa3ee23d73126=600160035260406000604060007310000000000000000000000000000000000000036203d090f450600160035200 -6938c921e3d5b48ac019c8d0ed785c8f64885bfa7717c8c7fca15a949ab56c55=60016003526040600060406000732000000000000000000000000000000000000002620493e0fa50600160035200 -3974d5c71fb125a5353bd7c8c9b5ff93aa74a5a403969d8de6fef553ff4e3621=60016003526040600060406000731000000000000000000000000000000000000002620493e0fa50600160035200 -c44f8a8322b00c2fe45a25d8bc8e7f33d4634728f787647dd49d75d89a2ea020=6000600060006000346000355af1600055600160015500 e0d4cd5d2b6fc5d75301ed128b521e335217fcca93bb3e84dbe30e00911122e2=6040600060406000731000000000000000000000000000000000000002620493e0fa50600160035200 b1f105737da31e39a435ccaca14d45bf7e966acbc89eeb4764ae931a356d241f=631122334460015200 +c44f8a8322b00c2fe45a25d8bc8e7f33d4634728f787647dd49d75d89a2ea020=6000600060006000346000355af1600055600160015500 a31685be6bc50872842b673c12b8f715652d15ab4538b870ddf4115f18a3108a=6040600060406000732000000000000000000000000000000000000002620493e0fa50600160035200 e5c21c6a73892cae374036c87ccb93503567dbcf8c17767cd3824b7a43c50a71=6001600452604060006040600060007320000000000000000000000000000000000000036203d090f250600160065200 -28259b787a771ce6a0442343dd8508b2f94ae391ba33b12365bbc17035c31e86=600160035260406000604060007310000000000000000000000000000000000000036201d4d4f450600160035200 44eeb1e90b4ba9f1cac8c855b777bcb4509d5d53263e3169665463b112fd41d8=6040600060406000731000000000000000000000000000000000000001620927c0fa600055600160035200 +28259b787a771ce6a0442343dd8508b2f94ae391ba33b12365bbc17035c31e86=600160035260406000604060007310000000000000000000000000000000000000036201d4d4f450600160035200 a05c66c87a3743060902db9fba58f6c002ba1225a11fd85c2910fa251231a54f=60016003526040600060406000731000000000000000000000000000000000000002620493e0fa50600160035200 +f2b73c66f8a6365168230f9d8247a482c901ecbbdbe08004170c0f3db9e37930=6040600060406000731000000000000000000000000000000000000002620493e0fa00 +be303a6b604153d86b8c041aee03c5165c4bab158dabd63ee408e525b9f7075b=60406000604060007320000000000000000000000000000000000000016207a120fa60005500 cc253b8464df96b5902144db47ffe52e42c74685b5e822b763190c301567b369=6040600060406000732000000000000000000000000000000000000002620493e0fa00 1fc33a3065a274a1c4731b2fff9fc4675309e557131d61b3b7700559fef1e467=604060006040600060007320000000000000000000000000000000000000036201d4d4f200 afb1b47797335e53cdc9467108080e6922ad005f3893e38d751df01e2d0998ae=604060006040600060007310000000000000000000000000000000000000036201d4d4f200 -be303a6b604153d86b8c041aee03c5165c4bab158dabd63ee408e525b9f7075b=60406000604060007320000000000000000000000000000000000000016207a120fa60005500 -f2b73c66f8a6365168230f9d8247a482c901ecbbdbe08004170c0f3db9e37930=6040600060406000731000000000000000000000000000000000000002620493e0fa00 -4fe53d29306665adcdc638aac6d0830318fcb19dd679c4978ddc158a39fe62ce=604060006040600060003562092856fa600055600160015500 722d286ead0dd8fb456e399a368af1ff1c3d3fda619eeee098f598331525ec43=604060006040600073100000000000000000000000000000000000000262061ad0fa505b61c3506080511015603f5760013b506001608051016080526023565b00 -e1e34b26e94d0b8a399a402d540ce9f6e2db126c89fe0cde67f9b2f4a6f2268f=604060006040600060007310000000000000000000000000000000000000036201d4d4f200 +4fe53d29306665adcdc638aac6d0830318fcb19dd679c4978ddc158a39fe62ce=604060006040600060003562092856fa600055600160015500 b7e895641c3007eb44a637b315aaab9539bd1209e7ad346ef2f9e80fd3b28d24=604060006040600073100000000000000000000000000000000000000262061ad0fa50600160035500 -4624e38fce300e82ef08e25d4899c2466721ac24f7c5ea4a9fe205a62f59ae1d=6001600352604060006040600073100000000000000000000000000000000000000262061ad5fa50600160035500 -81a3559f95be565bc43fdfbd30daa82a89fc86a2e92f2b9e23981bfd8f76c8c9=600160035260406000604060007310000000000000000000000000000000000000036201d4d4f450600160205200 +e1e34b26e94d0b8a399a402d540ce9f6e2db126c89fe0cde67f9b2f4a6f2268f=604060006040600060007310000000000000000000000000000000000000036201d4d4f200 e9b485a251e321f74d917400db4546485a0fb9f5fecd094bae4a389c2ef43eef=6001600352604060006040600073100000000000000000000000000000000000000262061ad5fa505b61c350608051101560445760013b506001608051016080526028565b00 +81a3559f95be565bc43fdfbd30daa82a89fc86a2e92f2b9e23981bfd8f76c8c9=600160035260406000604060007310000000000000000000000000000000000000036201d4d4f450600160205200 +4624e38fce300e82ef08e25d4899c2466721ac24f7c5ea4a9fe205a62f59ae1d=6001600352604060006040600073100000000000000000000000000000000000000262061ad5fa50600160035500 f31c205d634922344829da12de3086733e58f603fc31710c158ae456b674e668=604060006040600073100000000000000000000000000000000000000262061ad5fa505b61c3506080511015603f5760013b506001608051016080526023565b00 37c4eee477383998a832fdf28353f590e48b27123b300ed399f77700b5978111=604060006040600073100000000000000000000000000000000000000262061ad5fa50600160035500 74962fff62c0a4ce03d6cd31c1a58b749a196d66f630dbeb9629ac96720e0e53=604060006040600060003561eaf6fa600055600160015500 8978a0facc7ef4be4cbc5d2efd03de84172f3a1d8ac0bf9d139cf395e97dc94b=60406000604060007310000000000000000000000000000000000000036201d4d4f400 7230fdd3981b30ed76ae2d327857920cc937ed84661768eb1accfa1e54af4027=604060006040600073100000000000000000000000000000000000000262061ad0fa505b61c3506080511015603f5760013b506001608051016080526023565b00 +7d0f9af9265451fd3a0b5f7ee1b9aedd7995514c8a1e0fee49bba65b1b0dac43=6000356000526040600060206000731000000000000000000000000000000000000001620927c0fa600055600160015500 3174b7ef817680780a83ec454c7bcc5f4e9ea56d126b34a8b0eb3a08d0da64bc=5b61c3506080511015601c5760013b506001608051016080526000565b604060006040600060007310000000000000000000000000000000000000036201d4d4f200 -dfe22b1d3adeb26a4aae00560b744af4e53df3590769e33b40ae24a161a2d7ca=6001600155604060006040600060007310000000000000000000000000000000000000036201d4d4f200 17e38df7ed7bc33d107911adb4149ad591f78f654ab9f3108e7f4eb129ae545d=600035600052604060006040600060003562061ad0fa50600160205200 -7d0f9af9265451fd3a0b5f7ee1b9aedd7995514c8a1e0fee49bba65b1b0dac43=6000356000526040600060206000731000000000000000000000000000000000000001620927c0fa600055600160015500 +dfe22b1d3adeb26a4aae00560b744af4e53df3590769e33b40ae24a161a2d7ca=6001600155604060006040600060007310000000000000000000000000000000000000036201d4d4f200 +86b171a421431c31a2203461b78bcb590c97261d0ac766f1f54935b182345557=6000356000526040600060206000731000000000000000000000000000000000000001620927c0fa600055600160015500 e3b86e973eca920d41f0bd75d8b31bf41d234e6b076fafbab5e5569c82173211=6001600352604060006040600060003562061ad0fa50600160205200 ddb2857153071a4a369737061fd85a778889ed4352e2bad25d238af15f27da16=600160015560406000604060007310000000000000000000000000000000000000036201d4d4f450600160035200 85b2d690a5cc162c8288585b95a381f31e83a3a57b5d76b331bbb5287a199f20=5b61c3506080511015601c5760013b506001608051016080526000565b60406000604060007310000000000000000000000000000000000000036201d4d4f450600160035200 -86b171a421431c31a2203461b78bcb590c97261d0ac766f1f54935b182345557=6000356000526040600060206000731000000000000000000000000000000000000001620927c0fa600055600160015500 -911368bbead45abe1065427b032be1a7be045bb631176c25902e6ae4b1431a4f=6040600060406000731000000000000000000000000000000000000002620186a0fa00 af026713c0f79ed0f9422542314b76c2d987cd460191945ade6291ddb324edcf=6040600060406000600073100000000000000000000000000000000000000361c350f250731000000000000000000000000000000000000001ff00 +911368bbead45abe1065427b032be1a7be045bb631176c25902e6ae4b1431a4f=6040600060406000731000000000000000000000000000000000000002620186a0fa00 0d5110e79e7e7f1610af81d144f95ebf1490c262b2061ac5b1abb732d64ba8e2=604060006040600073100000000000000000000000000000000000000361c350f450731000000000000000000000000000000000000001ff00 4c7bd946f52be4d844bd55901ae229f48a66a1e42d9e8a9d930426186b6b4b98=731000000000000000000000000000000000000000ff6040600060406000600073100000000000000000000000000000000000000361c350f200 dbfcfc8a4c91780172921929c09e091ce397db603c05f886d3f79e89d6ad46ab=731000000000000000000000000000000000000000ff604060006040600073100000000000000000000000000000000000000361c350f400 -55bfae0c08eb128e982de648f2205254221e7f93b84741f952ce7faf60c6659a=6001600152604060006040600060007310000000000000000000000000000000000000016207a120f250600160025200 -1b1811f756816d2ddee4caeb1cae16252329ca9b8df4246f662aadbc43f989c2=60016001526040600060406000731000000000000000000000000000000000000002620f4240fa50600160025200 394e474a7818369aa6ef906be0c6644a95a9e29478f9cfbaa2a03daea59ec68c=6001600152604060006040600073100000000000000000000000000000000000000163017d7840fa50600160035200 -031cbc6008876a2d3377154417571f4bd0a820a4b3ebc05da64fe4fb644a91bf=60016001526040600060406000731000000000000000000000000000000000000002620f4240fa506001601f5200 +1b1811f756816d2ddee4caeb1cae16252329ca9b8df4246f662aadbc43f989c2=60016001526040600060406000731000000000000000000000000000000000000002620f4240fa50600160025200 +55bfae0c08eb128e982de648f2205254221e7f93b84741f952ce7faf60c6659a=6001600152604060006040600060007310000000000000000000000000000000000000016207a120f250600160025200 913cd9fcc69b0cf2717ce54e52ff7877c6e8ec8f533d876543d5c3e139b02ae8=6001600152604060006040600073100000000000000000000000000000000000000163017d7840fa506001601f5200 +031cbc6008876a2d3377154417571f4bd0a820a4b3ebc05da64fe4fb644a91bf=60016001526040600060406000731000000000000000000000000000000000000002620f4240fa506001601f5200 86543023e3a738c6eed5c0789fced34f74e0df473c78279763720c442e2449ac=600160015260406000604060007310000000000000000000000000000000000000016207a120f450600160155200 1eead44955d81c4eb2d0d7701f660a59433e9b3772dac184e3fab15e1df3aa47=604060006020600060027310000000000000000000000000000000000000026203d090f200 3911b30490a024a2f85a5a6c7e83be797ba997cf4865c1ee1a12000990c863fc=600035600052604060006020600060007320000000000000000000000000000000000000026203d090f200 -b4240ea416e844fc377d42e2baf78f2786934b0b4a0297accacecb853e036fe4=604060006020600073100000000000000000000000000000000000000162055730fa00 91ca3f0a794a613cb00a5af2f8b9b51f435bb74f6aedd6a0fdc67d32c548d487=631122334460005200 fa604d7cc74787b4e22cc5feeca594bd39651e1edaa4fd04713fa589e3111a36=600060006000600060006000355af2600055600160015500 bc9d5eb5e687e81d4337eebe298cf5ac56ba9af338be51564798fdf4629f9586=604060006020600073200000000000000000000000000000000000000162055730fa00 -c32a492feef390c6e2ebdcf786710b75f0249803127aa9cc425195423625c4eb=60406000604060006000600035614e34f200 +b4240ea416e844fc377d42e2baf78f2786934b0b4a0297accacecb853e036fe4=604060006020600073100000000000000000000000000000000000000162055730fa00 e3fb774d92546b939d025710375a6f30e2878f68981bdfd0d7ad941ee5b8077a=6000356000526040600060206000731000000000000000000000000000000000000001620249f0fa600055600160015500 +c32a492feef390c6e2ebdcf786710b75f0249803127aa9cc425195423625c4eb=60406000604060006000600035614e34f200 d90ca2ba7240aa1702a9340556d3b024e2ca470fec0a3e5c001f1bcdfafda78c=6040600060406000731000000000000000000000000000000000000001620249f0fa600055600160015500 3a891e03c400cb4fb705e70bebbb1467bf91e052c1e435b21d9bd7488851fdfd=6040600060406000600073100000000000000000000000000000000000000261c350f250731000000000000000000000000000000000000000ff00 941d4ad354c546f28461fe3422f954a5d8c347d075d192ca40fe1a0e4b136b37=604060006040600073100000000000000000000000000000000000000261c350f450731000000000000000000000000000000000000000ff00 +28a8dbcbc1d87f9fde6d57eca021a131effe99fb866ec78a845c85a37c1bb212=600160035260406000604060007310000000000000000000000000000000000000036203d090fa50600160035200 28debc2190ba10fe2437f4826b554e8c2983a2613eea29bd17a61b38b202cee6=60016003526040600060406000731000000000000000000000000000000000000002620493e0f450600160035200 7fd91a367a1410a3d1a44cfcdda30bf0bffc24c9fb7d8f28d4cda4aebf7f0151=600160015200 ffd7cbda91ff075896082246f9efe599b8ce72bf6dbcfa5463fab4834e385b37=604060006040600073100000000000000000000000000000000000000162055730fa600055600160015500 -28a8dbcbc1d87f9fde6d57eca021a131effe99fb866ec78a845c85a37c1bb212=600160035260406000604060007310000000000000000000000000000000000000036203d090fa50600160035200 755f1aa3078eab6342ea35bb1c5ebd1f6dba37cf197a46931cb3502703e50523=604060006040600060003562055730fa600055600160015500 8cd5f1da0a54544f60ab0932eb3a546866e047ad1b287cebc999bfad87b20908=60406000604060006000731000000000000000000000000000000000000002620493e0f200 f3315867b1ff03330818d4979ec692d60632b26a926cb8f04060bd2297d4a592=60406000604060006002731000000000000000000000000000000000000002620493e0f200 01110d9d56ce4b24560dfdc4cd4079811e32e4e2845bf7b7f1fa6ff7bb98d189=60406000604060007310000000000000000000000000000000000000036203d090fa00 +efd3791362262abf08729a7d7eed41b0b9e211ee0d7e2d63e26dfafa8dd3605b=60406000604060007310000000000000000000000000000000000000036201d4d4fa50600160015500 7106b6684160e262c6124439eed57fb5417859e6fe917de72eb194bde1f294f2=60406000604060007310000000000000000000000000000000000000036201d4d4fa505b61c3506080511015603f5760013b506001608051016080526023565b00 e6cec50e7f4fd14feca1d0152300545600b9184f9af222b92ee1ea39705a6b5f=60003560005260406000604060007310000000000000000000000000000000000000016207a120fa600055600160015500 -efd3791362262abf08729a7d7eed41b0b9e211ee0d7e2d63e26dfafa8dd3605b=60406000604060007310000000000000000000000000000000000000036201d4d4fa50600160015500 9d493cf39585e380242a5e78d876bb72bc7a6e1932d14388f8707f4edc2eb4b5=6040600060406000600035620493e0f450600160015200 959780bdbe849e75c2983634a537636aa539fc57bac17f51354eff7a8442415a=60406000604060006000356201d4d4fa00 +3eeda39518c12c58d883cee18c09bf06998f0e8cc586a87c6ae2f1e8dee4cff7=5b61c3506080511015601c5760013b506001608051016080526000565b00 f61c7e415147d3786daec93e883995af444dc4b87d9f5e2fc90f247d7a31c4cd=6000356000526040600060206000600073100000000000000000000000000000000000000262030d40f200 c3c01fcf216aa9d8a37eaddf379db38ae881981f63616b2d2e460bc0a5cd5bcb=600035600052604060006020600073100000000000000000000000000000000000000162055730fa600055600160015500 -3eeda39518c12c58d883cee18c09bf06998f0e8cc586a87c6ae2f1e8dee4cff7=5b61c3506080511015601c5760013b506001608051016080526000565b00 -6612d3591ca5cff6424dfc7ecf672bd0476b338f0c45349467e83ebdf8180dc9=6040600060406000600073100000000000000000000000000000000000000262061ad0f250600160015500 -36d372f01e14a26d3739f0a66ab218dce8adf2fe7d8f7663672aa209bfc501ef=60406000604060007310000000000000000000000000000000000000036201d4d4fa00 2626635253531004e0083e5ab107a55b0da18c00d0e63d11dd231f922b3a64a0=604060006040600060003561eaf6fa600055600160015500 +36d372f01e14a26d3739f0a66ab218dce8adf2fe7d8f7663672aa209bfc501ef=60406000604060007310000000000000000000000000000000000000036201d4d4fa00 e351411758140bbda208c47f21a717c14592bb69887f75acb72450640718fafe=6040600060406000600073100000000000000000000000000000000000000262061ad0f2505b61c350608051101560415760013b506001608051016080526025565b00 +6612d3591ca5cff6424dfc7ecf672bd0476b338f0c45349467e83ebdf8180dc9=6040600060406000600073100000000000000000000000000000000000000262061ad0f250600160015500 +2a9cd3f540828b9d7f10518a4986822deaef29110d4a4e9b214167d3dd422135=60406000604060007310000000000000000000000000000000000000036201d4e8fa00 27711706af4e7c525eb52c15796c712d0c11e595ec37fe8e85a91c401f6b4191=604060006040600073100000000000000000000000000000000000000262061ad0f450600160035500 7016b75b4fe8b619967eb8c1368c2465918b0165d4c5704c54e0be85982c1e69=604060006040600073100000000000000000000000000000000000000262061ad0f4505b61c3506080511015603f5760013b506001608051016080526023565b00 -2a9cd3f540828b9d7f10518a4986822deaef29110d4a4e9b214167d3dd422135=60406000604060007310000000000000000000000000000000000000036201d4e8fa00 4d1c50f95e2e5a62d0d80afa50d74bf47549c34141e979880ba45ed74c09380a=6040600060406000600073100000000000000000000000000000000000000262061ad0f250600160035500 -f0165c8201e02783fc98137d70eaf711d12c99d88ddfc41ddedf99443e54d612=6000356000526040600060406000731000000000000000000000000000000000000001620c3500fa600055600160015500 -b77cbcf2dc17a2d2e64fd93462fae09f85f9441231f9e1011807b7f92ae2dc80=6040600060406000600060003562061ad0f200 a3fc2a1912421434d65d63c44b9276655cfdcd7b63f2ad5aebde3a6eb1871f91=5b61c3506080511015601c5760013b506001608051016080526000565b60406000604060007310000000000000000000000000000000000000036201d4d4fa00 79d11cbc7e13370c339c4b6cf861c79ea723d013dc56ce97450cd3123d80b801=60016003556040600060406000731000000000000000000000000000000000000003614e34fa00 -9c05c56c35679d36fbefc4cfedf1eb9eec0ec59f91f8314511afa4ddd53a4d79=600160035560406000604060007310000000000000000000000000000000000000036201d4d4fa00 +b77cbcf2dc17a2d2e64fd93462fae09f85f9441231f9e1011807b7f92ae2dc80=6040600060406000600060003562061ad0f200 +f0165c8201e02783fc98137d70eaf711d12c99d88ddfc41ddedf99443e54d612=6000356000526040600060406000731000000000000000000000000000000000000001620c3500fa600055600160015500 7ef175fe7268e1628cbe09e3bffb1760c2d2642000ff5a2fadd85aa2b4e2fab4=604060006040600060003562061ad0f400 a71d4709c30594846e170a0199776c922f7a2f3053bd4c4623195f33d8354880=5b61c3506080511015601c5760013b506001608051016080526000565b6040600060406000731000000000000000000000000000000000000003614e34fa00 -330bcfcd9070b4879165e359c0952305d4b5de189f5b283b73556d46c8aa89fe=60406000604060006000731000000000000000000000000000000000000002620186a0f200 +9c05c56c35679d36fbefc4cfedf1eb9eec0ec59f91f8314511afa4ddd53a4d79=600160035560406000604060007310000000000000000000000000000000000000036201d4d4fa00 898daa0dde418d103f87750e94ad28510cf1d2c3eaa2019adb161607c3f345cc=604060006040600073100000000000000000000000000000000000000361c350fa50731000000000000000000000000000000000000001ff00 +330bcfcd9070b4879165e359c0952305d4b5de189f5b283b73556d46c8aa89fe=60406000604060006000731000000000000000000000000000000000000002620186a0f200 0ca686a3596a517253fb3eed546559ea48bafe95f1255e56636e57ce9b4f206d=6040600060406000731000000000000000000000000000000000000002620186a0f400 81ce17c01b81feaac753e31bbaa6c5962927200ac16e21614ed904a5aa58ba59=731000000000000000000000000000000000000000ff604060006040600073100000000000000000000000000000000000000361c350fa00 -aa752032f8023793e2ce90a566d8261ce682b60cdd57dea16d2faf17ba0e0530=604060006040600073100000000000000000000000000000000000000163017d7840fa600055600160015500 -2345251fc06998d6100c332efdce916fd61069881ca2746b9e7fccb71889ae97=60406000604060006000731000000000000000000000000000000000000002620f4240f200 ad8b9a8ada9cbea6549060796d360654180d999b61ce823716bc6dccb3eb130b=60406000604060007310000000000000000000000000000000000000016207a120fa00 +2345251fc06998d6100c332efdce916fd61069881ca2746b9e7fccb71889ae97=60406000604060006000731000000000000000000000000000000000000002620f4240f200 +aa752032f8023793e2ce90a566d8261ce682b60cdd57dea16d2faf17ba0e0530=604060006040600073100000000000000000000000000000000000000163017d7840fa600055600160015500 b91f8f6f60072a26c6702cc3940f64a191ee88199bde844e7320eba342270219=60406000604060007310000000000000000000000000000000000000016207a120fa00 a8b4e47486764e10c92491f389d0e9f039c49387f0bfc4f7b0cddc13ed91d258=6040600060406000731000000000000000000000000000000000000002620f4240f400 d737cfe4f473b358e4bbf25b675defdfbad5d0feb5e579fa2a6bd8b8e33cb928=604060006040600073100000000000000000000000000000000000000162055730fa600055600160015500 15427cf6c2f1cda400a5e22ae9352fbbcde451223c2ba8874824fc20ee3831e2=60016001526040600060406000731000000000000000000000000000000000000002620493e0f450600160015200 f2d6dba9b905bd2faa31322899c65419ba6fec6a6e2ced2f1dd968952b410dcd=6001600b5260406000604060007310000000000000000000000000000000000000036203d090f4506001600b5200 +a83f4b1e886937bf2e59f33acd445d05b75c06e7fca6a40cf211fca04cc46035=600035600052604060006040600073100000000000000000000000000000000000000162055730fa600055600160015500 f0bf62e3b1074f38f203640db7bbdb0cd29f2d7da3cda308f15cde20e882bac9=60406000604060006000600035620493e0f200 7e7a1fbba5a24ab8b99a1c587c7f936f2d6c9173affaa3fdec27dde16301ee71=604060006040600060007310000000000000000000000000000000000000036203d090f200 de74252bf18ef8d8ef2a0c4426609537173ab8e6b8a8267f4aa024512551a308=604060006040600060017310000000000000000000000000000000000000036203d090f200 -a83f4b1e886937bf2e59f33acd445d05b75c06e7fca6a40cf211fca04cc46035=600035600052604060006040600073100000000000000000000000000000000000000162055730fa600055600160015500 +9e5fa0550d8e6448c9d7ed44098020a5d8259fb54c0df41c4e49b80b87f0fff7=6000356000526040600060406000731000000000000000000000000000000000000001620249f0fa600055600160015500 6a0b6c3956ea8d7f09ceb59ffd67d87ab7c83ed2ea24c18dd36fa368927b68d9=6000356000526040600060406000731000000000000000000000000000000000000002620186a0f400 9e493073ffb1d3f2fafa1ab6c4e12c8d50e90b7f6c5f8265bcabe708a1d3f49a=6040600060406000600035614e34f400 -9e5fa0550d8e6448c9d7ed44098020a5d8259fb54c0df41c4e49b80b87f0fff7=6000356000526040600060406000731000000000000000000000000000000000000001620249f0fa600055600160015500 -d785046261ef2b2c902e001004a68d692bd4ef92f80a1ad2d038c947bcf79b30=600b60035260406000604060006001731000000000000000000000000000000000000003614e34f2506001600d5200 be3c1b374105a1ad263bf482a34d194340e256d9e9caffae33a1d8eac752de9b=600b60035260406000604060006000731000000000000000000000000000000000000003614e34f2506001600d5200 +d785046261ef2b2c902e001004a68d692bd4ef92f80a1ad2d038c947bcf79b30=600b60035260406000604060006001731000000000000000000000000000000000000003614e34f2506001600d5200 773de79017408de6196b8092ec68a270308777e85196fb7f8d7a3adf15eaf984=600160035260406000604060006000600035620186a0f250600160035200 +716e7b90937b670d0f8861902e63aa3ca135e3bb31db95139ed4dcff4516d0ad=60406000604060006000731000000000000000000000000000000000000002619c90f2505b61c350608051101560405760013b506001608051016080526024565b00 2ad769b31a685c24a9813ff218ab0c162e73904096cee987a83cbc91f7910ac6=60406000604060006000731000000000000000000000000000000000000002619c90f250600160035500 3ee90542e7dac7319fdc339bca415439887d15ba46aef52574fb4fd444e1736a=60406000604060006000731000000000000000000000000000000000000003614e34f200 -716e7b90937b670d0f8861902e63aa3ca135e3bb31db95139ed4dcff4516d0ad=60406000604060006000731000000000000000000000000000000000000002619c90f2505b61c350608051101560405760013b506001608051016080526024565b00 +63ddc6c7cde8118fe54c30fd9fc1cb826c13964e4e018ed18a95115d1f667420=60016001526040600060406000731000000000000000000000000000000000000002619c90f450600160035500 9bd2b9edcce1e8c22477108d177cf7bb0a8c937fd056509b0e3813f861660733=604060006040600060003561eaecfa600055600160015500 6a377ab9663a733e2d19a51cc67035ed2cf988848e1958613920c61678d8981b=60016001526040600060406000731000000000000000000000000000000000000003614e34f450600160015200 02a39c0ea5cfcd25758f8478f2eed90a6cf897e5f78fa2ac736d5a14e76ddbe9=60016001526040600060406000731000000000000000000000000000000000000002619c90f4505b61c350608051101560435760013b506001608051016080526027565b00 -63ddc6c7cde8118fe54c30fd9fc1cb826c13964e4e018ed18a95115d1f667420=60016001526040600060406000731000000000000000000000000000000000000002619c90f450600160035500 -e10487259f293e94299500b7b3ae77caa62ff12bd1558573151cca375536d2c9=60016003526040600060406000731000000000000000000000000000000000000003614e34f450600160035200 19344224a8deb0fa8e82e94a09aeb02e7e820970a78c254a0cbc8a70621232f9=60016003526040600060406000731000000000000000000000000000000000000002619c90f450600160035500 +e10487259f293e94299500b7b3ae77caa62ff12bd1558573151cca375536d2c9=60016003526040600060406000731000000000000000000000000000000000000003614e34f450600160035200 0a2c796d5cbdaf129fb6d80cf80513a1dd595b0c5f675d754ff0e658d3c09154=60016003526040600060406000731000000000000000000000000000000000000002619c90f4505b61c350608051101560435760013b506001608051016080526027565b00 +c30f2919b4c2b7d30b6872d74fea80089c40b9fd88392074e369dd88a6e8ac78=600160035260406000604060006000731000000000000000000000000000000000000002619c90f250600160035500 2e49a6a0d257b972358b3b47343bf3fca517c29627ae610a084eba25df4b3665=600160035260406000604060006000731000000000000000000000000000000000000003614e34f250600160205200 f927fe9f4febe484192375e0cb1a95db93295e98d2d2ff5114dae3a562cf76fc=600160035260406000604060006000731000000000000000000000000000000000000002619c90f2505b61c350608051101560455760013b506001608051016080526029565b00 -c30f2919b4c2b7d30b6872d74fea80089c40b9fd88392074e369dd88a6e8ac78=600160035260406000604060006000731000000000000000000000000000000000000002619c90f250600160035500 -cf91a54add743885c763456b67470db07e8637c921d01d36d02dc8147b41aa45=600160205260406000604060006000600035619c90f250600160205200 -1035e4a30d74279c5322335b9a804376a8791f7704fee5e5e2abe2c8a69a4a25=600160035560406000604060006000731000000000000000000000000000000000000003614e34f250600160205200 -2bc0daa50656548f8bb288c4162fe1ff516e80f60357203625403a33391f58d6=5b61c3506080511015601c5760013b506001608051016080526000565b60406000604060006000731000000000000000000000000000000000000003614e34f250600160205200 4996d25cdcb6aee5f3542bb02aef18224e2ce46faf8d21484b6cf4fddbaef153=60406000604060006001731000000000000000000000000000000000000003614e34f250600160205200 +1035e4a30d74279c5322335b9a804376a8791f7704fee5e5e2abe2c8a69a4a25=600160035560406000604060006000731000000000000000000000000000000000000003614e34f250600160205200 36ef1ee3541797068fa8fb591658e5247ff437d1039a9ea43e8e6a92f0e67158=600160205200 +2bc0daa50656548f8bb288c4162fe1ff516e80f60357203625403a33391f58d6=5b61c3506080511015601c5760013b506001608051016080526000565b60406000604060006000731000000000000000000000000000000000000003614e34f250600160205200 +cf91a54add743885c763456b67470db07e8637c921d01d36d02dc8147b41aa45=600160205260406000604060006000600035619c90f250600160205200 596076fbb6f5a523e367c612a3f0a760389aa32b2e2c3c493e08f293709284a6=5b61c3506080511015601c5760013b506001608051016080526000565b6040600060406000731000000000000000000000000000000000000003614e34f450600160035200 -0d282f2ca4d6fa58b776906b97ec17f4320c928bef222d3bc31c47c3ebb5cfdb=60016003526040600060406000600035619c90f450600160035200 -c80166c3731db7b3358f05e480910feacd1b883cd4bd69168a23aceec1707a9f=6000356000526040600060406000731000000000000000000000000000000000000001620249f0fa600055600160015500 aa174be1ab553fb83733662264927bf27e9a130167b31b2b2b65d2d7ac664604=60016003556040600060406000731000000000000000000000000000000000000003614e34f450600160035200 -9302404ae67eacca7bccb8ce9db8ad39bb93f78aa516219470c0ee3cbb4846e9=60016003526040600060406000600073100000000000000000000000000000000000000361c350f250731000000000000000000000000000000000000001ff00 +c80166c3731db7b3358f05e480910feacd1b883cd4bd69168a23aceec1707a9f=6000356000526040600060406000731000000000000000000000000000000000000001620249f0fa600055600160015500 +0d282f2ca4d6fa58b776906b97ec17f4320c928bef222d3bc31c47c3ebb5cfdb=60016003526040600060406000600035619c90f450600160035200 c3b941b3fe17595e21ebc4a42723044fe3e8a9b8b31da85497b7d2b6e2d4987a=600160035260406000604060006000731000000000000000000000000000000000000002620186a0f200 -c0cc692ef46a185404ff734f2c5076bfda4a55fa1ea5cf1dbea81af30b8ae7f6=600160035260406000604060006000731000000000000000000000000000000000000002620186a0f250600160035200 +9302404ae67eacca7bccb8ce9db8ad39bb93f78aa516219470c0ee3cbb4846e9=60016003526040600060406000600073100000000000000000000000000000000000000361c350f250731000000000000000000000000000000000000001ff00 bc19b7fd8dc1fa147fecd948973aceb2960745fbc40991f8d09f3dda415c4269=731000000000000000000000000000000000000000ff6040600060406000600073100000000000000000000000000000000000000361c350f250600160035200 +c0cc692ef46a185404ff734f2c5076bfda4a55fa1ea5cf1dbea81af30b8ae7f6=600160035260406000604060006000731000000000000000000000000000000000000002620186a0f250600160035200 485db9468861ff56fb336a5a134619fe32ee06fe7753ce1dae5c6e7cd893a531=731000000000000000000000000000000000000000ff604060006040600073100000000000000000000000000000000000000361c350f400 540ce432b3faf56301cbb2a9c10c084662a44e8bfb4ed0f8fd05c63c3d4a61ee=604060006040600060007310000000000000000000000000000000000000016207a120f200 f0da9c01ce06dfb829b58182a0fb24e3568c3553534c74abb063a3e8832be49b=60406000604060007310000000000000000000000000000000000000016207a120f400 @@ -2682,102 +2682,102 @@ b48d38f93eaa084033fc5970bf96e559c33c4cdc07d889ab00b4d63f9590739d=00 e466181d4de18c6565740a948ed375a7fe9891a0a75399242ff737c26a856b26=604060006040600073100000000000000000000000000000000000000162055730f4600055600160015500 3d8f4bb3dad33ace4a8f7c6913646815b69d3c1f09dafd0d559582cec2d9d128=600160015260406000604060007310000000000000000000000000000000000000026203d090fa50600160015200 70e7b30188c10f3b74489ef379faa66340e65da8a80808b6c818c6822a8d1896=6040600060406000600173100000000000000000000000000000000000000162055730f2600055600160015500 -af8497642fe2691e45cea7806fb7b32ca3118f9f0b9ea95d1efea3333ec1dde4=6040600060406000600035614e34fa00 c9e0efef6051a60fe41b11d60c4251dc7550f8130b97eab201a4722069a4fa0e=600160025500 6f2c9e67d2547208275b4ab871e237a4fa918163569ac8080ee96f1ca5caa8a3=6000356000526040600060406000731000000000000000000000000000000000000001620249f0f4600055600160015500 -36c09a0105dba101675477518b5de0f40ee543c20ebd216334a2136c0dd96bda=60016002526040600060406000600035614e34fa50600160205200 +af8497642fe2691e45cea7806fb7b32ca3118f9f0b9ea95d1efea3333ec1dde4=6040600060406000600035614e34fa00 f5e209b303eade212f36f01b21e7b5a8ced3d863f2dca92d731ef404e65fb2d5=60003560005260406000604060006000731000000000000000000000000000000000000001620249f0f2600055600160015500 +36c09a0105dba101675477518b5de0f40ee543c20ebd216334a2136c0dd96bda=60016002526040600060406000600035614e34fa50600160205200 49a03e4a082ceed2f4d0ee0743159847cf4e90384f831ff9522dc5e54263f47a=604060006040600073100000000000000000000000000000000000000261c350fa50731000000000000000000000000000000000000000ff00 73b9beefffb6fc59d657c5cf1f65ad598588720d2293468e99ba84c0c199059a=604060006040600034731000000000000000000000000000000000000001620249f0f26000555a60015500 a4d8ac63bba5d1407cc90474aa760b25476191f9bfdb3ea3f205e30b83be56f7=6000356000526040600060406000731000000000000000000000000000000000000001620249f0f46000555a60015500 7cff0f681f55e309bc5b0b5558d7a0ea013598dae7dfe9d850a4aa2cdd29c9ba=604060006040600060003561c350fa50731000000000000000000000000000000000000000ff00 -7b74af78ab90454c7ba643be2a669f5126394b0bbd9aeafbd9c5d62a02968823=604060006040600073100000000000000000000000000000000000000162055730f4600055600160015500 0f7018e4aefce00bec400639dbdeb88233c6c34d4173b8d0361ffc2848a818fe=600160015200 +7b74af78ab90454c7ba643be2a669f5126394b0bbd9aeafbd9c5d62a02968823=604060006040600073100000000000000000000000000000000000000162055730f4600055600160015500 56b70cdab35a44498f8c30f51b124841bcc612585f42bf683ed7139a989367a6=6040600060406000731000000000000000000000000000000000000002620493e0fa503360055200 9369ca7656de8f96762b8b91431e9369c02656fd26053c627b454af8ceb9a4c1=60406000604060003473100000000000000000000000000000000000000162055730f2600055600160015500 -7ba813acda194fe4b62fee350fd3020f5c5b45160abcfb6e51889fcc8b37f9c1=600035600052604060006040600034731000000000000000000000000000000000000001620249f0f2600055600160015500 9fa0d03017a2bcf1443caec9cf50043d73e5fd1740909380d1500bdae39b9aef=6000356000526040600060406000731000000000000000000000000000000000000002620186a0fa00 -510e52c221c015efc381ac34678af05fff9fbba15fecf7c3210fe7de502df981=6040600060406000602035614e34fa50600160035200 +7ba813acda194fe4b62fee350fd3020f5c5b45160abcfb6e51889fcc8b37f9c1=600035600052604060006040600034731000000000000000000000000000000000000001620249f0f2600055600160015500 6c613adfea0dcd0824ed27114194787fcc267c40244035702c8f4715cea14784=6000356020526040600060406000731000000000000000000000000000000000000002620186a0fa50600160035200 -9f20298822a4958cf1dc738403369d631e04e8934953235e2344f9bdff9deec9=60406000604060003460003562092856f2600055600160015500 +510e52c221c015efc381ac34678af05fff9fbba15fecf7c3210fe7de502df981=6040600060406000602035614e34fa50600160035200 36820aea6f9402e8239f60a98c9450a4cf2b442a09539cb500a85e1506706b8b=60406000604060007310000000000000000000000000000000000000036201d4d4fa00 +9f20298822a4958cf1dc738403369d631e04e8934953235e2344f9bdff9deec9=60406000604060003460003562092856f2600055600160015500 d268180b298019481d7be10ba1cd08c80afc5214321ad815d093e9d5dad384ad=604060006040600073100000000000000000000000000000000000000262061ad0fa505b61c3506080511015603f5760013b506001608051016080526023565b00 +8b8fdfc6b0a8d4adef57eff09c997bcf3b5fb8cd1a189decbcd53fb9f7e3687c=60016003526040600060406000731000000000000000000000000000000000000003614e34fa50600160205200 f128aff3de450169fb369b813d485683dd3542f6beef48d4f8c12d7b0fb18212=604060006040600073100000000000000000000000000000000000000161eaf6f4600055600160015500 32e4d943aca0578baa32c4b55935067c1774d4b9796d92976c027dd5b1bd78ae=60016003526040600060406000731000000000000000000000000000000000000002619c90fa505b61c350608051101560435760013b506001608051016080526027565b00 -8b8fdfc6b0a8d4adef57eff09c997bcf3b5fb8cd1a189decbcd53fb9f7e3687c=60016003526040600060406000731000000000000000000000000000000000000003614e34fa50600160205200 -614bcbeebf85626b016baa47ccb56a6ac8e749a7a3bc1e69503c60ea642d082a=6001600352604060006040600073100000000000000000000000000000000000000262061ad0fa505b61c350608051101560445760013b506001608051016080526028565b00 0f46598e2e1e8f98d1241e8eada22e112d94cfb3121a4c6ff54a674af11703ab=604060006040600073100000000000000000000000000000000000000162092d9cf4600055600160015500 de1bb63e65cd0796c21f393b4cc896526bb1fb32f87fe77995c7646757ee59f2=600160035260406000604060007310000000000000000000000000000000000000036201d4d4fa50600160035200 +614bcbeebf85626b016baa47ccb56a6ac8e749a7a3bc1e69503c60ea642d082a=6001600352604060006040600073100000000000000000000000000000000000000262061ad0fa505b61c350608051101560445760013b506001608051016080526028565b00 0a907ef25f7ec3a7649fde0fc3bde1d5c600f6c6d3e0166d840549736ec07cd3=60406000604060003473100000000000000000000000000000000000000161eaf6f2600055600160015500 -fbec71cba80e73df867097de0d4ee932071a8150cab693f5a7a93c089aaad60e=60016003556040600060406000731000000000000000000000000000000000000003614e34fa00 cd24eeb6ba19cd96b7c1843d903213377f400d5cd3a37704e5497c71a2ca035d=600035600052604060006040600034731000000000000000000000000000000000000001620249f0f2600055600160015500 +fbec71cba80e73df867097de0d4ee932071a8150cab693f5a7a93c089aaad60e=60016003556040600060406000731000000000000000000000000000000000000003614e34fa00 6ecb6187005c1b52d5b1b9c27a968488e5af836a2d06d8dec268f03faf4c064e=60016003526040600060406000600035619c90fa00 94249ff02157f717f6ea8ca94d3a8d7de38cbad39ec8293b7276d5d891f93b40=5b61c3506080511015601c5760013b506001608051016080526000565b6040600060406000731000000000000000000000000000000000000003614e34fa00 -a4c0ee0e96648cc50ca53228a42f432658166d070a9d4c149b902f04cf644363=60016003556040600060406000731000000000000000000000000000000000000003614e34fa50600160035200 +260f4e68033d4e00ac9ef749d006bf7415e8ff70b8d7c91edb81c19effae4f86=6000356000526040600060406000731000000000000000000000000000000000000001620249f0f4600055600160015500 350b386a63491778e8c5deba52e39331d598d4b288cf4c08991fa7b766d1e348=6040600060406000600035619c90fa50600160035200 1ca2139ddaff3045190c85c3f1a62f93cc328a8867c22e2d16974d051bc4dab4=5b61c3506080511015601c5760013b506001608051016080526000565b6040600060406000731000000000000000000000000000000000000003614e34fa00 -260f4e68033d4e00ac9ef749d006bf7415e8ff70b8d7c91edb81c19effae4f86=6000356000526040600060406000731000000000000000000000000000000000000001620249f0f4600055600160015500 +a4c0ee0e96648cc50ca53228a42f432658166d070a9d4c149b902f04cf644363=60016003556040600060406000731000000000000000000000000000000000000003614e34fa50600160035200 9e9ef6d05630957275c98645f1874d04a8386698f4b2e1c22ad2add17a3351da=60406000604060006000731000000000000000000000000000000000000001620249f0f2600055600160015500 5ea8f924946ab1da886ea3d6fb342ad2f5ad281ba4daf64e0d54ea0756dfc5c5=6001600352604060006040600073100000000000000000000000000000000000000361c350fa50731000000000000000000000000000000000000001ff00 1e8f62ea8d62323b4facebd9a33309e6be110ab045c9068a124e0743ec7b8641=6040600060406000731000000000000000000000000000000000000001620249f0f4600055600160015500 23de4e424220ffefefb2130e496ec18b778b07e4554db256516c7bef0f969acc=604060006040600034731000000000000000000000000000000000000001620249f0f2600055600160015500 48133ce8c05d2a6ec8fbc4e23892bd5faf8bde675dd75064a8a3c04399e2506f=60016003526040600060406000731000000000000000000000000000000000000002620186a0fa50600160205200 -60ae939946e955ed64a55af1334be7353b16cf67154e6e993ec591dbce884498=60406000604060003473100000000000000000000000000000000000000163017d7840f2600055600160015500 3bc5208a1d4e38e24a6767941196cab880161d0f905b797d99e6280ff098613a=6040600060406000731000000000000000000000000000000000000002620f4240fa00 +60ae939946e955ed64a55af1334be7353b16cf67154e6e993ec591dbce884498=60406000604060003473100000000000000000000000000000000000000163017d7840f2600055600160015500 91a0e84c1e91aae18f142ce0292dfeb6a10163f366259318b8a919f9d414d737=604060006040600073100000000000000000000000000000000000000163017d7840f4600055600160015500 -5cb3d491a0a70a0af216a87e20fbe23ba17785d85118fdd5814c750419546197=60016001526040600060406000731000000000000000000000000000000000000002620493e0fa506001601f5200 58602048e23c946475e99fcb483028b9f4b93d658d602acfc604a7b360d609ff=600160015260406000604060007310000000000000000000000000000000000000036203d090f4506001601f5200 -4bd54fc1246bc6cdda8054b1736ad1647804b383d908d028d6018dc61eea02aa=6040600060406000600035620493e0fa00 +5cb3d491a0a70a0af216a87e20fbe23ba17785d85118fdd5814c750419546197=60016001526040600060406000731000000000000000000000000000000000000002620493e0fa506001601f5200 84d1af0c5c6c3883b3c16560eac3756318e4115fb14bdda65e718ef213c984b5=60003560005260406000604060003473100000000000000000000000000000000000000162055730f2600055600160015500 +4bd54fc1246bc6cdda8054b1736ad1647804b383d908d028d6018dc61eea02aa=6040600060406000600035620493e0fa00 1011b3270837b0bc4fbf6531f8ce57306e94efd1d9e5d3c841d40e613e60ef14=60406000604060007310000000000000000000000000000000000000036201d4d4f400 4371bec7904154375251cdb8ad720246cda44afd4265f54a97fdb17f8593f08d=604060006040600073100000000000000000000000000000000000000262030d40fa00 756d7e76b15eda302bd0da29fe9670240105b4d434b6785ad9830bc2a7c9794c=600160035260406000604060006000731000000000000000000000000000000000000003614e34f200 -da8d5432a874207132bcf3abde03605d022c018ebe7400d7875aaec2c4c00407=60406000604060006000731000000000000000000000000000000000000001620aaef6f250600160035200 -71dbc3fca2af095c1853e6d45c001c84a647976dab0596259de5ab597a35118b=60406000604060006000732000000000000000000000000000000000000001620aaef6f250600160035200 -28e88bc8516f4edf414d22533479b4b26a559189c25a285e2f2bd546c93d0045=604060006040600060007320000000000000000000000000000000000000036201d4d4f250600160035200 06fb81d18433dc0ded42d33d4464632d084b7c904e5aec9ce159f8e303bf7637=604060006040600073200000000000000000000000000000000000000262061ad0fa50600160035200 6ac0a7431d0eb3e895d3c0fadeab35226f85930c46eb9bba91d8220f2d2401ad=604060006040600060007310000000000000000000000000000000000000036201d4d4f250600160035200 -cb76cadcc46d52fe0db8a30112e78c679fa3ca3ea5ae25a1f53a5211c45302d8=6040600060406000731000000000000000000000000000000000000003614e34f450600160035200 +28e88bc8516f4edf414d22533479b4b26a559189c25a285e2f2bd546c93d0045=604060006040600060007320000000000000000000000000000000000000036201d4d4f250600160035200 +da8d5432a874207132bcf3abde03605d022c018ebe7400d7875aaec2c4c00407=60406000604060006000731000000000000000000000000000000000000001620aaef6f250600160035200 +71dbc3fca2af095c1853e6d45c001c84a647976dab0596259de5ab597a35118b=60406000604060006000732000000000000000000000000000000000000001620aaef6f250600160035200 +52c50940cc34283081c291b8240f2efe9a017c174c698ad7a1751209c8b58a4f=6040600060406000731000000000000000000000000000000000000002619c90fa505b61c3506080511015603e5760013b506001608051016080526022565b00 7ed1a2b2d590a12d61621ac6a7349a10a5571af1ec032ee3726de3887f6ed2e3=6040600060406000732000000000000000000000000000000000000002619c90fa00 -b29eebdd30e390867117f875b426663efcc56c2e1ca53e008100448daf179444=604060006040600073200000000000000000000000000000000000000161eaf6f4600055600160015500 0b3c9a8f6810a7d891a1b98f1e626517238a509f4e58a4a6ab122d81f700671b=604060006040600073100000000000000000000000000000000000000161eaf6f46000555a60015500 +cb76cadcc46d52fe0db8a30112e78c679fa3ca3ea5ae25a1f53a5211c45302d8=6040600060406000731000000000000000000000000000000000000003614e34f450600160035200 53ea926755491ecc957290dc5b2761ae0556232b03dc5471fbc718d9c844bd23=6040600060406000732000000000000000000000000000000000000003614e34f450600160035200 -52c50940cc34283081c291b8240f2efe9a017c174c698ad7a1751209c8b58a4f=6040600060406000731000000000000000000000000000000000000002619c90fa505b61c3506080511015603e5760013b506001608051016080526022565b00 -a2a239813865d7d5692bb2207007a923a27c7aad842251e6e6528820df5d2be4=60016003526040600060406000731000000000000000000000000000000000000002619c90fa50600160035200 -b7163a865729b62df082e1b4f8e8bfd1f19244f9a5341116011ddf5abb4dfa28=604060006040600034731000000000000000000000000000000000000001620249f0f2600055600160015500 +b29eebdd30e390867117f875b426663efcc56c2e1ca53e008100448daf179444=604060006040600073200000000000000000000000000000000000000161eaf6f4600055600160015500 8a2731d929b54f34dc0b82265e6bb2dc91bd33c186789d5e93bc8110fad0b0ea=5b61c3506080511015601c5760013b506001608051016080526000565b60406000604060006000731000000000000000000000000000000000000003614e34f200 +b7163a865729b62df082e1b4f8e8bfd1f19244f9a5341116011ddf5abb4dfa28=604060006040600034731000000000000000000000000000000000000001620249f0f2600055600160015500 +a2a239813865d7d5692bb2207007a923a27c7aad842251e6e6528820df5d2be4=60016003526040600060406000731000000000000000000000000000000000000002619c90fa50600160035200 707f41cf7885b5ead8e3e1ba796b727b8e466875ffba6fae98a52ed932a0c1ca=5b61c3506080511015601c5760013b506001608051016080526000565b6040600060406000731000000000000000000000000000000000000003614e34f400 2091a5db3247f6d174ad7a3becf4ffcefa1171720288653cd7e80c980fe255c4=731000000000000000000000000000000000000000ff6040600060406000600073100000000000000000000000000000000000000361c350f200 fef2ad1bd1d0042a6efdd15783014b1af38d0e62db2d9b3f4dcb95a4102aa1f9=60406000604060003460003563017d7840f2600055600160015500 -18e71aba1fabecef860a76d397205a6226dd73c289caaa59e237037cdf3f33e4=604060006040600060017320000000000000000000000000000000000000016207a120f200 8e91a8556ca4ea7ca6e52b6207def03bc8dec91c4122be6b66bc215c78aa265c=6040600060406000732000000000000000000000000000000000000002620f4240fa00 -cafe4006fa0a33e7beb1bb618c22ee3c36be51c9b437d2eb77e10ca1db33ed20=60406000604060003473100000000000000000000000000000000000000162055730f2600055600160015500 +18e71aba1fabecef860a76d397205a6226dd73c289caaa59e237037cdf3f33e4=604060006040600060017320000000000000000000000000000000000000016207a120f200 b98a2cd25451cc59f3e145e18b860744e8a92d1c6afbc521919947b15a198ba6=604060006040600060013403731000000000000000000000000000000000000002620493e0f200 +cafe4006fa0a33e7beb1bb618c22ee3c36be51c9b437d2eb77e10ca1db33ed20=60406000604060003473100000000000000000000000000000000000000162055730f2600055600160015500 07f6a9472c712464919f59e9e8785a2c8972273b04f2370fc1e4931e65430371=600160015260406000604060007310000000000000000000000000000000000000036203d090fa50600160015200 1918a8f94735cfee2a5bd6f836f14eb12f7f43faa317b3d84aea67c96f5c2774=6001600152604060006040600060013403731000000000000000000000000000000000000002620493e0f2506001601f5200 c68361add39b3ac30cfe63fdc345f2e0712b8194b1653a6df786532ee0dfefb2=600160015260406000604060007310000000000000000000000000000000000000036203d090fa506001601f5200 -0bf40b092409d33068ede1528ed0c7589021e8e292d7dfdd15f144755b49cff9=6040600060406000731000000000000000000000000000000000000003614e34fa00 b6e6ebdd00d58b886b5d2c7c7318961b0defdd3a3c94852c1a62d6560ed9bfaa=604060006040600060013403731000000000000000000000000000000000000002620186a0f200 +0bf40b092409d33068ede1528ed0c7589021e8e292d7dfdd15f144755b49cff9=6040600060406000731000000000000000000000000000000000000003614e34fa00 a06aee939887e976a9ed735d040644f23e196c0b66bae5072e0251081b4bd3e4=604060006040600060013403731000000000000000000000000000000000000002619c90f2505b61c350608051101560425760013b506001608051016080526026565b00 -377901720af802d93c9a67ccfa9732ce496dc57a5bc06dcace058ef7bb0dc21e=6040600060406000731000000000000000000000000000000000000002619c90f4505b61c3506080511015603e5760013b506001608051016080526022565b00 9997b6fb70eb91d61dbd8d5e51e1fb06152afe9d045bd279766b8e557dadcfd9=6040600060406000731000000000000000000000000000000000000003614e34fa00 -22109675dc763fc2ff376e0489170c411a5be253e37c288fccffea224388d22e=604060006040600060013403731000000000000000000000000000000000000002619c90f2505b61c350608051101560425760013b506001608051016080526026565b00 +377901720af802d93c9a67ccfa9732ce496dc57a5bc06dcace058ef7bb0dc21e=6040600060406000731000000000000000000000000000000000000002619c90f4505b61c3506080511015603e5760013b506001608051016080526022565b00 66fd6efff22789e0022a148c485bcc4dd430c75c87a5ac0ddc84a4978d334d10=60406000604060003473100000000000000000000000000000000000000161eaf6f2600055600160015500 -6bf73fe3b9e1007d402594ae2d1ea36fb201eb90c7f68d788d81d2159cfaf582=604060006040600060013403731000000000000000000000000000000000000002619c90f200 +22109675dc763fc2ff376e0489170c411a5be253e37c288fccffea224388d22e=604060006040600060013403731000000000000000000000000000000000000002619c90f2505b61c350608051101560425760013b506001608051016080526026565b00 fc5309d4154da6030e05a30dd2ce4585118873591176f79ee402988d43297b24=604060006040600034731000000000000000000000000000000000000001620249f0f2600055600160015500 +6bf73fe3b9e1007d402594ae2d1ea36fb201eb90c7f68d788d81d2159cfaf582=604060006040600060013403731000000000000000000000000000000000000002619c90f200 4a20daf1922ebcc25cf82baf42ee2c532c2da74926e44894a7c7e8770abc7d81=6040600060406000731000000000000000000000000000000000000002619c90f400 92c32bd57eefaf63706894534570b803fdc3ff23c3379867f688d289566eae96=604060006040600034731000000000000000000000000000000000000002620186a0f200 21906a140b3d2329b1b977c9bdb906d61e092ce21f8f3644ea709b839e462ec4=6040600060406000731000000000000000000000000000000000000001620249f0f46000555a60015500 af9837019dc7e71ad40ad889121d58d8840f03364cfa98c910fea95cebe565e0=6040600060406000731000000000000000000000000000000000000002620186a0fa00 f6f3d117f582e9aeed2b021271206d10bb0d1122bcd53be6f33273d5d05b2852=60406000604060007310000000000000000000000000000000000000026203d090f400 -f49691185c98bc4d38cbd6f72fee8d284ae0347ac3dab980fe98fc9855053d08=60006000600060006000734000000000000000000000000000000000000004620f4240f200 ca395fbb13fec43e41a6f1dd5f292ebd9f3ba198b79e4caf0ea84aaac1116d27=604060006040600073100000000000000000000000000000000000000161c350fa00 +f49691185c98bc4d38cbd6f72fee8d284ae0347ac3dab980fe98fc9855053d08=60006000600060006000734000000000000000000000000000000000000004620f4240f200 6aaee305608773195015611678fc4b42bd9996507fffbd7646555c815b32c8ae=600160015200 d5680d1a57d65009464d64daf8a18d829b4321d52250ba8252efb41dfe013548=600060006000600060007340000000000000000000000000000000000000046103e8f200 b6854de517529726bb59d586b298c37c964c0bea333f2ae471b44beb4a230ed8=6000600060006000730f572e5295c57f15886f9b263e2f6d2d6c7b5ec66103e8fa600055600160015500 6c0e2b5b332daee1e973fdc64c85c26e3ba3989a858fa1c32cc57423045be2e6=6000600060006000730f572e5295c57f15886f9b263e2f6d2d6c7b5ec66103e8fa60005500 -318aa00ff7c3c999aec760237f43b8a15e0c221498a327b4eaf57003b2afc156=60ff6000533360206000a100 62ba2266bc159fa76122f6b060114940d17c4dcce9cdf5e4fb75cd475ceb7df3=60ff6000533360006000600060206000a400 +318aa00ff7c3c999aec760237f43b8a15e0c221498a327b4eaf57003b2afc156=60ff6000533360206000a100 cfc2b962815c543844b7b2a412adbe303fa0efa9a2e0ec93c1fbf9e231558199=60ff60005360206000a000 66ea953d8c72d0637943560c649fb4f6d540ad3cdacebd34ba592c612cdefc72=7b601080600c6000396000f20060003554156009570060203560003555600052600060006000600073aaaaaaaaace5edbc8e2a8697c15331677e6ebf0b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecfa00 5eff31ebea62b04f764a877c84c2cc92a00a13feff33ed56c575a25635dab23d=600060006000600073aaae7baea6a6c7c4c2dfeb977efac326af552aaa61157cfa600055600160015500 @@ -2814,14 +2814,14 @@ d9ee1ed86455c4d7ca5c8734f327a0cd55d4b62cf74dc98cc5bda96e1bd7d4fe=7ffeedfeedfeedf f26b0805bac34e43cbe429c9f772f7113748e7bfeca8e39df096ed3d0552c258=6000600060006000601773095e7baea6a6c7c4c2dfeb977efac326af552d8761c350f1600101585500 ecdbcebf816bd6ffd6e9da9d9307cdd607cc57071f27d857549d810d733fd2bf=6000600060006000601773095e7baea6a6c7c4c2dfeb977efac326af552d87620186a05a03f1600101585500 b94850406c5cc6921a6e6d8bde93b0f1e88973724fdc618631232ec443ff5691=6000600060006000601873945304eb96065b2a98b57a48a06ae28d285a71b5620186a05a03f1585500 -1e6a612423b1c661aecfd0d91d2339e425f91dcff078cd9d07d28c405e8bc1a6=6001600054016000556000600060006000600073095e7baea6a6c7c4c2dfeb977efac326af552d87620186a05a03f100 cf5fd0ebd61fc2dbf8dac98eabdc6b44980e07c42b6814dec77a941628bae32a=6001600054016000556000600060006000600173945304eb96065b2a98b57a48a06ae28d285a71b5620186a05a03f100 +1e6a612423b1c661aecfd0d91d2339e425f91dcff078cd9d07d28c405e8bc1a6=6001600054016000556000600060006000600073095e7baea6a6c7c4c2dfeb977efac326af552d87620186a05a03f100 b254f222c66920f0da84e3e5327cdb7a4a78e48aed4cf079f517be94be4c13bb=6000600060006000601873945304eb96065b2a98b57a48a06ae28d285a71b5620186a0f1585573945304eb96065b2a98b57a48a06ae28d285a71b5ff00 86e091a7ccdb683bac5489b99bd742053ecee5874f04a78561b6197ee5082b85=6000356000526000600060206000601773095e7baea6a6c7c4c2dfeb977efac326af552d8761c35060003503f16001015855730f572e5295c57f15886f9b263e2f6d2d6c7b5ec6ff00 f2a018a1727b04c0eed1e47c656aa734465df019c4e6cc6761d1fc43c4272558=6000356000526000600060206000601873945304eb96065b2a98b57a48a06ae28d285a71b5600035f1585500 78aa31217c88bb237260ad76d5e7b1e9466a0b5bd65e1a4e254e06f58d86991d=5b600a60805110156042576000600061c3506000600173aaaf5374fce5edbc8e2a8697c15331677e6ebf0b650ffffffffffff16000556001608051016080526000565b60805160015500 -7e3b8f4e80ca8679786483f27f64ac780f82e72385fbc89b420027cc437eea9f=6001600054016000556000600060006000600030612af85a03f160015500 fa24c7c55f747597fc149cce83a02d9843f9e1dbf982d5f75e1111da358547da=6000600060006000601773945304eb96065b2a98b57a48a06ae28d285a71b56305f5e100f100 +7e3b8f4e80ca8679786483f27f64ac780f82e72385fbc89b420027cc437eea9f=6001600054016000556000600060006000600030612af85a03f160015500 97acd9b2ba21ee05bf3ac6c3a1f535f821b539c64a02322f2d71e2ade8a1fe1c=600160005401600055690fffffffffffffffffff610402600054040260025560006000690fffffffffffffffffff610402600054040260006000306104005a03f160015500 4e009047ee5311748b376c8542e67a3a2fc308addd07cc27c90724ba32b5e38d=6001600054016000556000600060006000600030613a985a03f160015500 faf42407d80f6f38660d94fbb71f781655060e1300aa15a3c783a67c0a646b7a=600160005401600055600060006000600060003060e05a03f160015500 @@ -2875,12 +2875,12 @@ b5449ad280d4b54db0bd0d5b109c28f7ddab926a8fb168387c423352e8733907=603760015500 2c1a699913984bf25854461c0a2fcf852759e7fdeb5bfbc959305b64f8245eb6=6000600060006000600173c94f5374fce5edbc8e2a8697c15331677e6ebf0b611388f100 39dc286827f4e11b0c27330b0399837d618c349d58bc7d032fc8beddb9b24eae=600060006000600060016000619c40f100 0819cb021f81370dc3d7d14a3fa84fce82d9db2e5264e0fce4f5bc28b89dd744=600060006000600060016000620186a0f100 -2a203f69a317afd0cdf2dd1ca84a097da8a0cdda385e87a53540eee36596e3b3=7f6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9600052601b6020527f79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817986040527f6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a96060526020600060806000806001620f4240f1600055600051600155 +2a203f69a317afd0cdf2dd1ca84a097da8a0cdda385e87a53540eee36596e3b3=7f6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9600052601b6020527f79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817986040527f6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9606052602060006080600060006001620f4240f1600055600051600155 9c3d4d32bdfbe6aba7805fac561682e706c6fe15490dd10a81f815ab1c72ddab=6000600055600060015560006002556000600355600060006000600060016000614e20f100 f3377e9d4f09b635d56a8f2920cb572f927692b5c7d275e0011a9f054139d298=600060005560006001556000600255600060035560006000600060006001600061c350f100 e3669f5ce010826e278e252dec1f74ceb2e71ed12270a062ed4de187ea78e719=635a60fd556000526004601c6000f000 -94dbd8dbb7085e6bcb88b8be8b99cc70470085eb68d1b80dfcb78b8fa9a713a8=6001ff00 21bf113995efc70852a2c4e35595a6f277fa46118386eecd47fa077730ca3cd1=6000600060006000600060006000f1506000ff00 +94dbd8dbb7085e6bcb88b8be8b99cc70470085eb68d1b80dfcb78b8fa9a713a8=6001ff00 9154fb82afafc124f4eb1ba75102f69e51a2957e3b3a6bd4ed421173a1a57a11=6000600060006000600160006000f1506000ff00 82f8a4223d0dc45259ee5caeed7281a789472a29852447a1e2c71cf502603732=6000600060006000600160006155f0f1506000ff00 038c2a3bbbd6ae23c6b7c6aa9f7ce434bd6eb3c54d9d1066c8af7d4d3470ae5c=600060006000600060016000600035f1506000ff00 diff --git a/testool/src/compiler.rs b/testool/src/compiler.rs index 6f88c443a1..b40f8ecb0d 100644 --- a/testool/src/compiler.rs +++ b/testool/src/compiler.rs @@ -1,8 +1,8 @@ #![allow(clippy::map_entry)] use anyhow::{bail, Context, Result}; -use eth_types::{bytecode, Bytecode, Bytes, H256}; -use keccak256::plain::Keccak; +use eth_types::{bytecode, keccak256, Bytecode, Bytes, H256}; +use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, io::{Read, Write}, @@ -59,12 +59,143 @@ impl Cache { } fn hash(src: &str) -> H256 { - let mut hash = Keccak::default(); - hash.update(src.as_bytes()); - H256::from_slice(&hash.digest()) + H256::from_slice(&keccak256(src.as_bytes())) } } +#[derive(Debug, Serialize, Deserialize)] +struct CompilerInput { + language: Language, + settings: CompilerSettings, + sources: HashMap, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +enum Language { + Solidity, + Yul, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct CompilerSettings { + optimizer: Optimizer, + output_selection: HashMap>>, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Optimizer { + enabled: bool, + details: HashMap, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Source { + content: String, +} + +impl CompilerInput { + pub fn new_default(language: Language, src: &str) -> Self { + let mut sources = HashMap::new(); + sources.insert( + "stdin".to_string(), + Source { + content: src.to_string(), + }, + ); + CompilerInput { + language, + settings: Default::default(), + sources, + } + } +} + +impl Default for CompilerSettings { + fn default() -> Self { + let mut output_selection = HashMap::new(); + let mut selection = HashMap::new(); + selection.insert("*".to_string(), vec!["evm.bytecode".to_string()]); + output_selection.insert("*".to_string(), selection); + CompilerSettings { + optimizer: Default::default(), + output_selection, + } + } +} + +impl Default for Optimizer { + fn default() -> Self { + let mut details = HashMap::new(); + details.insert("peephole".to_string(), false); + details.insert("inliner".to_string(), false); + details.insert("jumpdestRemover".to_string(), false); + Optimizer { + enabled: false, + details, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct CompilationResult { + contracts: HashMap>, + errors: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Contract { + evm: Evm, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Evm { + bytecode: BytecodeResult, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct BytecodeResult { + object: String, + opcodes: String, + source_map: String, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct CompilationError { + component: String, + error_code: String, + formatted_message: String, + message: String, + severity: Severity, + #[serde(rename = "type")] + error_type: String, + source_location: Option, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] +enum Severity { + Error, + Warning, + Info, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct SourceLocation { + end: i32, + file: String, + start: i32, +} + #[derive(Default)] pub struct Compiler { cache: Option, @@ -142,64 +273,42 @@ impl Compiler { /// compiles YUL code pub fn yul(&mut self, src: &str) -> Result { - if let Some(bytecode) = self.cache.as_mut().and_then(|c| c.get(src)) { - return Ok(bytecode.clone()); - } - if !self.compile { - bail!("No way to compile Yul for '{}'", src) - } - - let stdout = Self::exec( - &["run", "-i", "--rm", "solc", "--strict-assembly", "-"], - src, - )?; - let placeholder = "Binary representation:\n"; - let from_pos = stdout.find(placeholder); - let len = from_pos.and_then(|pos| stdout[pos + placeholder.len()..].find('\n')); - let bytecode = if let (Some(from_pos), Some(len)) = (from_pos, len) { - let hex = &stdout[from_pos + placeholder.len()..from_pos + placeholder.len() + len]; - Bytes::from(hex::decode(&hex)?) - } else { - bail!("Unable to compile: {}", src); - }; - if let Some(cache) = &mut self.cache { - cache.insert(src, bytecode.clone())?; - } - - Ok(bytecode) + self.solc(Language::Yul, src) } + /// compiles Solidity code pub fn solidity(&mut self, src: &str) -> Result { + self.solc(Language::Solidity, src) + } + + fn solc(&mut self, language: Language, src: &str) -> Result { if let Some(bytecode) = self.cache.as_mut().and_then(|c| c.get(src)) { return Ok(bytecode.clone()); } if !self.compile { - bail!("No way to compile Solidity for '{}'", src) + bail!("No way to compile {:?} for '{}'", language, src) } + let compiler_input = CompilerInput::new_default(language, src); let stdout = Self::exec( - &[ - "run", - "-i", - "--rm", - "solc", - "--bin", - "--optimize", - "--metadata-hash", - "none", - "-", - ], - src, + &["run", "-i", "--rm", "solc", "--standard-json", "-"], + serde_json::to_string(&compiler_input).unwrap().as_str(), )?; - let placeholder = "Binary:\n"; - let from_pos = stdout.find(placeholder); - let len = from_pos.and_then(|pos| stdout[pos + placeholder.len()..].find('\n')); - let bytecode = if let (Some(from_pos), Some(len)) = (from_pos, len) { - let hex = &stdout[from_pos + placeholder.len()..from_pos + placeholder.len() + len]; - Bytes::from(hex::decode(&hex)?) - } else { - bail!("Unable to compile: {}", src); - }; + let mut compilation_result: CompilationResult = serde_json::from_str(&stdout)?; + let bytecode = compilation_result + .contracts + .remove("stdin") + .expect("should have stdin") + .into_iter() + .next() + .expect("should have one contract") + .1 + .evm + .bytecode + .object; + + let bytecode = Bytes::from(hex::decode(bytecode)?); + if let Some(cache) = &mut self.cache { cache.insert(src, bytecode.clone())?; } diff --git a/testool/src/statetest/executor.rs b/testool/src/statetest/executor.rs index 1b110f6062..4f1fd3e6c6 100644 --- a/testool/src/statetest/executor.rs +++ b/testool/src/statetest/executor.rs @@ -16,9 +16,6 @@ use std::{collections::HashMap, str::FromStr}; use thiserror::Error; use zkevm_circuits::{super_circuit::SuperCircuit, test_util::CircuitTestBuilder, witness::Block}; -const MAX_TXS: usize = 1; -const MAX_CALLDATA: usize = 32; - #[derive(PartialEq, Eq, Error, Debug)] pub enum StateTestError { #[error("CannotGenerateCircuitInput({0})")] @@ -26,7 +23,7 @@ pub enum StateTestError { #[error("BalanceMismatch(expected:{expected:?}, found:{found:?})")] BalanceMismatch { expected: U256, found: U256 }, #[error("NonceMismatch(expected:{expected:?}, found:{found:?})")] - NonceMismatch { expected: U256, found: U256 }, + NonceMismatch { expected: u64, found: u64 }, #[error("CodeMismatch(expected: {expected:?}, found:{found:?})")] CodeMismatch { expected: Bytes, found: Bytes }, #[error("StorgeMismatch(slot:{slot:?} expected:{expected:?}, found: {found:?})")] @@ -142,9 +139,9 @@ fn into_traceconfig(st: StateTest) -> (String, TraceConfig, StateTestResult) { transactions: vec![geth_types::Transaction { from: st.from, to: st.to, - nonce: st.nonce, + nonce: U64::from(st.nonce), value: st.value, - gas_limit: U256::from(st.gas_limit), + gas_limit: U64::from(st.gas_limit), gas_price: st.gas_price, gas_fee_cap: U256::zero(), gas_tip_cap: U256::zero(), @@ -204,30 +201,20 @@ pub fn run_test( )); } - if suite.max_gas > 0 && geth_traces[0].gas.0 > suite.max_gas { - return Err(StateTestError::SkipTestMaxGasLimit(geth_traces[0].gas.0)); + if suite.max_gas > 0 && geth_traces[0].gas > suite.max_gas { + return Err(StateTestError::SkipTestMaxGasLimit(geth_traces[0].gas)); } let transactions = trace_config .transactions .into_iter() .enumerate() - .map(|(index, tx)| eth_types::Transaction { - from: tx.from, - to: tx.to, - value: tx.value, - input: tx.call_data, - gas_price: Some(tx.gas_price), - access_list: tx.access_list, - nonce: tx.nonce, - gas: tx.gas_limit, - transaction_index: Some(U64::from(index)), - r: tx.r, - s: tx.s, - v: U64::from(tx.v), - block_number: Some(U64::from(trace_config.block_constants.number.as_u64())), - chain_id: Some(trace_config.chain_id), - ..eth_types::Transaction::default() + .map(|(index, tx)| { + tx.to_response( + U64::from(index), + trace_config.chain_id, + trace_config.block_constants.number, + ) }) .collect(); @@ -287,8 +274,8 @@ pub fn run_test( geth_data.sign(&wallets); let circuits_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, + max_txs: 1, + max_calldata: 32, max_rws: 256, max_copy_rows: 256, max_exp_steps: 256, @@ -297,8 +284,7 @@ pub fn run_test( max_keccak_rows: 0, }; let (k, circuit, instance, _builder) = - SuperCircuit::::build(geth_data, circuits_params) - .unwrap(); + SuperCircuit::::build(geth_data, circuits_params, Fr::from(0x100)).unwrap(); builder = _builder; let prover = MockProver::run(k, &circuit, instance).unwrap(); diff --git a/testool/src/statetest/json.rs b/testool/src/statetest/json.rs index a083077a8f..7bd35e26b7 100644 --- a/testool/src/statetest/json.rs +++ b/testool/src/statetest/json.rs @@ -1,16 +1,13 @@ -#![allow(dead_code, unused_imports)] - use super::{ parse, spec::{AccountMatch, Env, StateTest}, }; -use crate::{abi, compiler::Compiler, utils::MainnetFork}; -use anyhow::{bail, Context, Result}; -use eth_types::{evm_types::OpcodeId, geth_types::Account, Address, Bytes, H256, U256}; +use crate::{compiler::Compiler, utils::MainnetFork}; +use anyhow::{bail, Result}; +use eth_types::{geth_types::Account, Address, U256}; use ethers_core::{k256::ecdsa::SigningKey, utils::secret_key_to_address}; use serde::Deserialize; -use std::{collections::HashMap, convert::TryInto, ops::RangeBounds, str::FromStr}; -use yaml_rust::Yaml; +use std::collections::HashMap; #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] @@ -36,7 +33,6 @@ struct AccountPost { code: Option, nonce: Option, storage: Option>, - shouldnotexist: Option, } #[derive(Debug, Clone, Deserialize)] @@ -112,7 +108,7 @@ impl<'a> JsonStateTestBuilder<'a> { let to = parse::parse_to_address(&test.transaction.to)?; let secret_key = parse::parse_bytes(&test.transaction.secret_key)?; let from = secret_key_to_address(&SigningKey::from_bytes(&secret_key.to_vec())?); - let nonce = parse::parse_u256(&test.transaction.nonce)?; + let nonce = parse::parse_u64(&test.transaction.nonce)?; let gas_price = parse::parse_u256(&test.transaction.gas_price)?; let data_s: Vec<_> = test @@ -219,7 +215,7 @@ impl<'a> JsonStateTestBuilder<'a> { let account = Account { address, balance: parse::parse_u256(&acc.balance)?, - nonce: parse::parse_u256(&acc.nonce)?, + nonce: parse::parse_u64(&acc.nonce)?.into(), code: parse::parse_code(self.compiler, &acc.code)?, storage, }; @@ -257,7 +253,7 @@ impl<'a> JsonStateTestBuilder<'a> { nonce: acc .nonce .as_ref() - .map(|v| parse::parse_u256(v)) + .map(|v| parse::parse_u64(v)) .transpose()?, storage, }; @@ -298,6 +294,8 @@ impl<'a> JsonStateTestBuilder<'a> { #[cfg(test)] mod test { use super::*; + use eth_types::{Bytes, H256}; + use std::{collections::HashMap, str::FromStr}; const JSON: &str = r#" { @@ -390,24 +388,23 @@ mod test { )?), gas_limit: 400000, gas_price: U256::from(10u64), - nonce: U256::from(0u64), + nonce: 0, value: U256::from(100000u64), data: Bytes::from(hex::decode("6001")?), pre: HashMap::from([( acc095e, Account { address: acc095e, - nonce: U256::from(0u64), balance: U256::from(1000000000000000000u64), code: Bytes::from(hex::decode("600160010160005500")?), - storage: HashMap::new(), + ..Default::default() }, )]), result: HashMap::from([( acc095e, AccountMatch { address: acc095e, - nonce: Some(U256::from(1u64)), + nonce: Some(1u64), balance: None, code: Some(Bytes::from(hex::decode("600160010160005500")?)), storage: HashMap::from([(U256::zero(), U256::from(2u64))]), diff --git a/testool/src/statetest/parse.rs b/testool/src/statetest/parse.rs index 488e631b0d..2cd2d32a4e 100644 --- a/testool/src/statetest/parse.rs +++ b/testool/src/statetest/parse.rs @@ -41,7 +41,7 @@ fn decompose_tags(expr: &str) -> HashMap { } else { while !it.is_empty() { if it.starts_with(':') { - let tag = &it[..it.find(&[' ', '\n']).expect("unable to find end tag")]; + let tag = &it[..it.find([' ', '\n']).expect("unable to find end tag")]; it = &it[tag.len() + 1..]; let value_len = if tag == ":yul" || tag == ":solidity" || tag == ":asm" { it.len() diff --git a/testool/src/statetest/spec.rs b/testool/src/statetest/spec.rs index b2d224a73f..234867ccda 100644 --- a/testool/src/statetest/spec.rs +++ b/testool/src/statetest/spec.rs @@ -1,5 +1,5 @@ use anyhow::{anyhow, bail, Context}; -use eth_types::{geth_types::Account, Address, Bytes, Word, H256, U256}; +use eth_types::{geth_types::Account, Address, Bytes, Word, H256, U256, U64}; use ethers_core::{k256::ecdsa::SigningKey, utils::secret_key_to_address}; use std::{collections::HashMap, str::FromStr}; @@ -18,7 +18,7 @@ pub struct AccountMatch { pub address: Address, pub balance: Option, pub code: Option, - pub nonce: Option, + pub nonce: Option, pub storage: HashMap, } @@ -29,7 +29,7 @@ impl TryInto for AccountMatch { address: self.address, balance: self.balance.context("balance")?, code: self.code.context("code")?, - nonce: self.nonce.context("nonce")?, + nonce: self.nonce.context("nonce")?.into(), storage: self.storage, }) } @@ -47,7 +47,7 @@ pub struct StateTest { pub to: Option
, pub gas_limit: u64, pub gas_price: U256, - pub nonce: U256, + pub nonce: u64, pub value: U256, pub data: Bytes, pub pre: HashMap, @@ -205,10 +205,8 @@ impl StateTest { from, Account { address: from, - nonce: U256::zero(), balance: U256::from(10).pow(18.into()), - code: Bytes::default(), - storage: HashMap::new(), + ..Default::default() }, ); @@ -242,7 +240,7 @@ impl StateTest { address, Account { address, - nonce: U256::one(), + nonce: U64::one(), code: Bytes::from(code.code()), balance, storage, @@ -250,11 +248,20 @@ impl StateTest { ); } + pre.insert( + *mock::MOCK_COINBASE, + Account { + address: *mock::MOCK_COINBASE, + balance: U256::from(1), + ..Default::default() + }, + ); + let state_test = StateTest { path: String::default(), id: String::default(), env: Env { - current_coinbase: Address::default(), + current_coinbase: *mock::MOCK_COINBASE, current_difficulty: U256::default(), current_gas_limit: 16000000, current_number: 1, @@ -266,7 +273,7 @@ impl StateTest { to, gas_limit, gas_price: U256::one(), - nonce: U256::zero(), + nonce: 0, value, data: data.into(), pre, diff --git a/testool/src/statetest/yaml.rs b/testool/src/statetest/yaml.rs index a6ae70ef68..28bee90c17 100644 --- a/testool/src/statetest/yaml.rs +++ b/testool/src/statetest/yaml.rs @@ -104,7 +104,7 @@ impl<'a> YamlStateTestBuilder<'a> { Self::parse_u256(&yaml_transaction["gasPrice"]).unwrap_or_else(|_| U256::one()); // TODO handle maxPriorityFeePerGas & maxFeePerGas - let nonce = Self::parse_u256(&yaml_transaction["nonce"])?; + let nonce = Self::parse_u64(&yaml_transaction["nonce"])?; let to = Self::parse_to_address(&yaml_transaction["to"])?; let secret_key = Self::parse_bytes(&yaml_transaction["secretKey"])?; let from = secret_key_to_address(&SigningKey::from_bytes(&secret_key.to_vec())?); @@ -244,7 +244,7 @@ impl<'a> YamlStateTestBuilder<'a> { nonce: if acc_nonce.is_badvalue() { None } else { - Some(Self::parse_u256(acc_nonce)?) + Some(Self::parse_u64(acc_nonce)?) }, storage, }; @@ -264,7 +264,7 @@ impl<'a> YamlStateTestBuilder<'a> { } else { while !it.is_empty() { if it.starts_with(':') { - let tag = &it[..it.find(&[' ', '\n']).expect("unable to find end tag")]; + let tag = &it[..it.find([' ', '\n']).expect("unable to find end tag")]; it = &it[tag.len() + 1..]; let value_len = if tag == ":yul" || tag == ":solidity" { it.len() @@ -615,7 +615,7 @@ arith: to: Some(ccccc), gas_limit: 80000000, gas_price: U256::from(10u64), - nonce: U256::zero(), + nonce: 0, value: U256::one(), data: Bytes::from(&[0]), pre: HashMap::from([ @@ -625,9 +625,8 @@ arith: address: ccccc, balance: U256::from(1000000000000u64), code: Bytes::from(&[0x60, 0x01, 0x00]), - nonce: U256::zero(), - storage: HashMap::from([(U256::zero(), U256::one())]), + ..Default::default() }, ), ( @@ -635,10 +634,7 @@ arith: Account { address: a94f5, balance: U256::from(1000000000000u64), - code: Bytes::default(), - nonce: U256::zero(), - - storage: HashMap::new(), + ..Default::default() }, ), ]), @@ -647,7 +643,7 @@ arith: AccountMatch { address: ccccc, balance: Some(U256::from(1000000000001u64)), - nonce: Some(U256::from(0)), + nonce: Some(0), code: Some(Bytes::from(&[0x60, 0x01, 0x00])), storage: HashMap::from([(U256::zero(), U256::one())]), }, @@ -762,8 +758,8 @@ arith: CircuitsConfig::default() ), Err(StateTestError::NonceMismatch { - expected: U256::from(2), - found: U256::from(0) + expected: 2, + found: 0 }) ); diff --git a/testool/src/utils.rs b/testool/src/utils.rs index c835987e50..a147684a46 100644 --- a/testool/src/utils.rs +++ b/testool/src/utils.rs @@ -125,12 +125,12 @@ pub fn print_trace(trace: GethExecTrace) -> Result<()> { ]); for step in trace.struct_logs { table.add_row(row![ - format!("{}", step.pc.0), + format!("{}", step.pc), format!("{:?}", step.op), - format!("{}", step.gas.0), - format!("{}", step.gas_cost.0), + format!("{}", step.gas), + format!("{}", step.gas_cost), format!("{}", step.depth), - step.error.unwrap_or_else(|| "".to_string()), + step.error.unwrap_or_default(), split(step.stack.0.iter().map(u256_to_str).collect(), 30), split(step.memory.0.iter().map(ToString::to_string).collect(), 30), split(kv(step.storage.0), 30) @@ -146,7 +146,7 @@ pub fn print_trace(trace: GethExecTrace) -> Result<()> { pub fn current_git_commit() -> Result { let output = Command::new("git") - .args(&["rev-parse", "HEAD"]) + .args(["rev-parse", "HEAD"]) .output() .unwrap(); let git_hash = String::from_utf8(output.stdout).unwrap(); @@ -156,7 +156,7 @@ pub fn current_git_commit() -> Result { pub fn current_submodule_git_commit() -> Result { let git_cmd = Command::new("git") - .args(&["ls-tree", "HEAD"]) + .args(["ls-tree", "HEAD"]) .stdout(Stdio::piped()) .output()?; diff --git a/zkevm-circuits/Cargo.toml b/zkevm-circuits/Cargo.toml index c84f32ad7d..5db21ca298 100644 --- a/zkevm-circuits/Cargo.toml +++ b/zkevm-circuits/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_02_02" } +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", features = ["circuit-params"], tag = "v2023_04_20" } num = "0.4" sha3 = "0.10" array-init = "2.0.0" @@ -24,35 +24,37 @@ rand_xorshift = "0.3" rand = "0.8" itertools = "0.10.3" lazy_static = "1.4" -keccak256 = { path = "../keccak256"} log = "0.4" env_logger = "0.9" -ecdsa = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_02_02" } -ecc = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_02_02" } -maingate = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_02_02" } -integer = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_02_02" } +ecdsa = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20" } +ecc = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20" } +maingate = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20" } +integer = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20" } libsecp256k1 = "0.7" num-bigint = { version = "0.4" } -subtle = "2.4" rand_chacha = "0.3" -snark-verifier = { git = "https://github.com/privacy-scaling-explorations/snark-verifier", tag = "v2023_02_02", default-features = false, features = ["loader_halo2", "system_halo2"] } +snark-verifier = { git = "https://github.com/privacy-scaling-explorations/snark-verifier", tag = "v2023_04_20", default-features = false, features = ["loader_halo2", "system_halo2"] } +cli-table = { version = "0.4", optional = true } num_enum = "0.5.7" [dev-dependencies] bus-mapping = { path = "../bus-mapping", features = ["test"] } -criterion = "0.3" ctor = "0.1.22" ethers-signers = "0.17.0" hex = "0.4.3" itertools = "0.10.1" mock = { path = "../mock" } pretty_assertions = "1.0.0" -cli-table = "0.4" serde = { version = "1.0.130", features = ["derive"] } serde_json = "1.0.78" [features] default = [] -test = ["ethers-signers", "mock"] +test = ["ethers-signers", "mock", "bus-mapping/test"] test-circuits = [] warn-unimplemented = ["eth-types/warn-unimplemented"] +stats = ["warn-unimplemented", "dep:cli-table"] + +[[bin]] +name = "stats" +required-features = ["stats"] diff --git a/zkevm-circuits/src/stats.rs b/zkevm-circuits/src/bin/stats/helpers.rs similarity index 78% rename from zkevm-circuits/src/stats.rs rename to zkevm-circuits/src/bin/stats/helpers.rs index 272bda9ed9..29b760ae3e 100644 --- a/zkevm-circuits/src/stats.rs +++ b/zkevm-circuits/src/bin/stats/helpers.rs @@ -1,61 +1,17 @@ use std::cmp::Ordering; -use crate::evm_circuit::step::ExecutionState; use bus_mapping::{ circuit_input_builder::{self, CircuitsParams, ExecState}, mock::BlockData, }; +use cli_table::{ + format::{Justify, Separator}, + print_stdout, Table, WithTitle, +}; use eth_types::{bytecode, evm_types::OpcodeId, geth_types::GethData, Address, Bytecode, ToWord}; use mock::{eth, test_ctx::TestContext, MOCK_ACCOUNTS}; use strum::IntoEnumIterator; - -/// Helper type to print formatted tables in MarkDown -pub(crate) struct DisplayTable { - header: [String; N], - rows: Vec<[String; N]>, -} - -impl DisplayTable { - pub(crate) fn new(header: [String; N]) -> Self { - Self { - header, - rows: Vec::new(), - } - } - fn push_row(&mut self, row: [String; N]) { - self.rows.push(row) - } - fn print_row(row: &[String; N], rows_width: &[usize; N]) { - for (i, h) in row.iter().enumerate() { - if i == 0 { - print!("|"); - } - print!(" {:width$} |", h, width = rows_width[i]); - } - println!(); - } - pub(crate) fn print(&self) { - let mut rows_width = [0; N]; - for row in std::iter::once(&self.header).chain(self.rows.iter()) { - for (i, s) in row.iter().enumerate() { - if s.len() > rows_width[i] { - rows_width[i] = s.len(); - } - } - } - Self::print_row(&self.header, &rows_width); - for (i, width) in rows_width.iter().enumerate() { - if i == 0 { - print!("|"); - } - print!(" {:- Bytecode { @@ -106,12 +62,34 @@ pub(crate) fn bytecode_prefix_op_big_rws(opcode: OpcodeId) -> Bytecode { } } +/// Wrap f64 for both sorting and pretty formatting +#[derive(PartialEq, PartialOrd)] +struct PrettyF64(f64); + +impl std::fmt::Display for PrettyF64 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:1.3}", self.0) + } +} + +impl From for PrettyF64 { + fn from(value: f64) -> Self { + Self(value) + } +} + +#[derive(Table)] struct Row { + #[table(title = "Execution State")] state: ExecutionState, + #[table(title = "Opcode")] opcode: OpcodeId, + #[table(title = "Height", justify = "Justify::Right")] height: usize, + #[table(title = "Gas Cost", justify = "Justify::Right")] gas_cost: u64, - height_per_gas: f64, + #[table(title = "Height per Gas", justify = "Justify::Right")] + height_per_gas: PrettyF64, } /// This function prints to stdout a table with all the implemented states @@ -161,7 +139,6 @@ pub(crate) fn print_circuit_stats_by_states( STOP }; - let mut table = DisplayTable::new(["state", "opcode", "h", "g", "h/g"].map(|s| s.into())); let mut rows = vec![]; for state in implemented_states { if !fn_filter(state) { @@ -192,7 +169,7 @@ pub(crate) fn print_circuit_stats_by_states( code.write_op(opcode); let opcode_pc = code.code.len() - 1; // let opcode_step_index = (proxy_code.num_opcodes - 1 + code.num_opcodes) - 1; - code.write_op(OpcodeId::STOP); + code.op_stop(); let block: GethData = TestContext::<10, 1>::new( None, |accs| { @@ -236,7 +213,7 @@ pub(crate) fn print_circuit_stats_by_states( .steps() .iter() .enumerate() - .find(|(_, s)| s.call_index == 1 && s.pc.0 == opcode_pc) + .find(|(_, s)| s.call_index == 1 && s.pc == (opcode_pc as u64)) .unwrap(); assert_eq!(ExecState::Op(opcode), step.exec_state); let height = fn_height(&builder.block, state, step_index); @@ -245,13 +222,13 @@ pub(crate) fn print_circuit_stats_by_states( // in the geth trace. let geth_step = &block.geth_traces[0].struct_logs[step_index - 1]; assert_eq!(opcode, geth_step.op); - let gas_cost = geth_step.gas_cost.0; + let gas_cost = geth_step.gas_cost; rows.push(Row { state, opcode, height, gas_cost, - height_per_gas: height as f64 / gas_cost as f64, + height_per_gas: (height as f64 / gas_cost as f64).into(), }); } } @@ -261,16 +238,6 @@ pub(crate) fn print_circuit_stats_by_states( .unwrap_or(Ordering::Greater) }); - for row in rows.iter() { - let row = [ - format!("{:?}", row.state), - format!("{:?}", row.opcode), - format!("{}", row.height), - format!("{}", row.gas_cost), - format!("{:1.3}", row.height_per_gas), - ]; - table.push_row(row); - } - - table.print(); + print_stdout(rows.with_title().separator(Separator::builder().build())) + .expect("the table renders"); } diff --git a/zkevm-circuits/src/bin/stats/main.rs b/zkevm-circuits/src/bin/stats/main.rs new file mode 100644 index 0000000000..30c8fc9a27 --- /dev/null +++ b/zkevm-circuits/src/bin/stats/main.rs @@ -0,0 +1,185 @@ +use cli_table::{print_stdout, Cell, Style, Table}; +use eth_types::{bytecode, evm_types::OpcodeId, ToWord}; +use halo2_proofs::{ + halo2curves::bn256::Fr, + plonk::{Circuit, ConstraintSystem}, +}; +mod helpers; +use helpers::{bytecode_prefix_op_big_rws, print_circuit_stats_by_states}; +use itertools::Itertools; +use mock::MOCK_ACCOUNTS; +use std::env; +use zkevm_circuits::evm_circuit::{ + param::{LOOKUP_CONFIG, N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, N_PHASE2_COLUMNS}, + step::ExecutionState, + EvmCircuit, +}; +fn main() { + let args: Vec = env::args().collect(); + + match &args[1][..] { + "evm" => evm_states_stats(), + "state" => state_states_stats(), + "copy" => copy_states_stats(), + "exec" => get_exec_steps_occupancy(), + &_ => unreachable!("Unsupported arg"), + } +} + +/// Prints the stats of EVM circuit per execution state. +fn evm_states_stats() { + print_circuit_stats_by_states( + |state| { + // TODO: Enable CREATE/CREATE2 once they are supported + !matches!( + state, + ExecutionState::ErrorInvalidOpcode + | ExecutionState::CREATE + | ExecutionState::CREATE2 + | ExecutionState::SELFDESTRUCT + ) + }, + |opcode| match opcode { + OpcodeId::RETURNDATACOPY => { + bytecode! { + PUSH1(0x00) // retLength + PUSH1(0x00) // retOffset + PUSH1(0x00) // argsLength + PUSH1(0x00) // argsOffset + PUSH1(0x00) // value + PUSH32(MOCK_ACCOUNTS[3].to_word()) + PUSH32(0x1_0000) // gas + CALL + PUSH2(0x01) // size + PUSH2(0x00) // offset + PUSH2(0x00) // destOffset + } + } + _ => bytecode! { + PUSH2(0x40) + PUSH2(0x50) + }, + }, + |_, state, _| state.get_step_height_option().unwrap(), + ); +} + +/// Prints the stats of State circuit per execution state. +fn state_states_stats() { + print_circuit_stats_by_states( + |state| { + // TODO: Enable CREATE/CREATE2 once they are supported + !matches!( + state, + ExecutionState::ErrorInvalidOpcode + | ExecutionState::CREATE + | ExecutionState::CREATE2 + | ExecutionState::SELFDESTRUCT + ) + }, + bytecode_prefix_op_big_rws, + |block, _, step_index| { + let step = &block.txs[0].steps()[step_index]; + let step_next = &block.txs[0].steps()[step_index + 1]; + step_next.rwc.0 - step.rwc.0 + }, + ); +} + +/// Prints the stats of Copy circuit per execution state. +fn copy_states_stats() { + print_circuit_stats_by_states( + |state| { + // TODO: Enable CREATE/CREATE2 once they are supported + matches!( + state, + ExecutionState::RETURNDATACOPY + | ExecutionState::CODECOPY + | ExecutionState::LOG + | ExecutionState::CALLDATACOPY + | ExecutionState::EXTCODECOPY + | ExecutionState::RETURN_REVERT + ) + }, + bytecode_prefix_op_big_rws, + |block, _, _| { + assert!(block.copy_events.len() <= 1); + block + .copy_events + .iter() + .map(|c| c.bytes.len() * 2) + .sum::() + }, + ); +} + +/// This function prints to stdout a table with the top X ExecutionState +/// cell consumers of each EVM Cell type. +fn get_exec_steps_occupancy() { + let mut meta = ConstraintSystem::::default(); + let circuit = EvmCircuit::configure(&mut meta); + + let report = circuit.0.execution.instrument().clone().analyze(); + macro_rules! gen_report { + ($report:expr, $($id:ident, $cols:expr), +) => { + $( + let row_report = report + .iter() + .sorted_by(|a, b| a.$id.utilization.partial_cmp(&b.$id.utilization).unwrap()) + .rev() + .take(10) + .map(|exec| { + vec![ + format!("{:?}", exec.state), + format!("{:?}", exec.$id.available_cells), + format!("{:?}", exec.$id.unused_cells), + format!("{:?}", exec.$id.used_cells), + format!("{:?}", exec.$id.top_height), + format!("{:?}", exec.$id.used_columns), + format!("{:?}", exec.$id.utilization), + ] + }) + .collect::>>(); + + let table = row_report.table().title(vec![ + format!("{:?}", stringify!($id)).cell().bold(true), + format!("total_available_cells").cell().bold(true), + format!("unused_cells").cell().bold(true), + format!("cells").cell().bold(true), + format!("top_height").cell().bold(true), + format!("used columns (Max: {:?})", $cols).cell().bold(true), + format!("Utilization").cell().bold(true), + ]); + print_stdout(table).unwrap(); + )* + }; + } + + gen_report!( + report, + storage_1, + N_PHASE1_COLUMNS, + storage_2, + N_PHASE2_COLUMNS, + storage_perm, + N_COPY_COLUMNS, + byte_lookup, + N_BYTE_LOOKUPS, + fixed_table, + LOOKUP_CONFIG[0].1, + tx_table, + LOOKUP_CONFIG[1].1, + rw_table, + LOOKUP_CONFIG[2].1, + bytecode_table, + LOOKUP_CONFIG[3].1, + block_table, + LOOKUP_CONFIG[4].1, + copy_table, + LOOKUP_CONFIG[5].1, + keccak_table, + LOOKUP_CONFIG[6].1, + exp_table, + LOOKUP_CONFIG[7].1 + ); +} diff --git a/zkevm-circuits/src/bytecode_circuit.rs b/zkevm-circuits/src/bytecode_circuit.rs index a9db23040f..297e3a1622 100644 --- a/zkevm-circuits/src/bytecode_circuit.rs +++ b/zkevm-circuits/src/bytecode_circuit.rs @@ -4,7 +4,12 @@ pub mod bytecode_unroller; /// Bytecode circuit pub mod circuit; +pub(crate) mod param; + +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +mod dev; /// Bytecode circuit tester #[cfg(any(feature = "test", test))] -pub mod dev; -pub(crate) mod param; +mod test; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +pub use dev::BytecodeCircuit as TestBytecodeCircuit; diff --git a/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs b/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs index e032461a1c..6c4fb174fc 100644 --- a/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs +++ b/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs @@ -16,7 +16,7 @@ pub(crate) struct BytecodeRow { } /// Unrolled bytecode -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Default)] pub struct UnrolledBytecode { pub(crate) bytes: Vec, pub(crate) rows: Vec>, @@ -28,8 +28,8 @@ pub fn unroll(bytes: Vec) -> UnrolledBytecode { let mut rows = vec![BytecodeRow:: { code_hash, tag: F::from(BytecodeFieldTag::Header as u64), - index: F::zero(), - is_code: F::zero(), + index: F::ZERO, + is_code: F::ZERO, value: F::from(bytes.len() as u64), }]; // Run over all the bytes diff --git a/zkevm-circuits/src/bytecode_circuit/circuit.rs b/zkevm-circuits/src/bytecode_circuit/circuit.rs index 10ef41e0fe..d0540d3126 100644 --- a/zkevm-circuits/src/bytecode_circuit/circuit.rs +++ b/zkevm-circuits/src/bytecode_circuit/circuit.rs @@ -1,12 +1,16 @@ use crate::{ - evm_circuit::util::{and, constraint_builder::BaseConstraintBuilder, not, or, rlc, select}, + evm_circuit::util::{ + and, + constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, + not, or, rlc, select, + }, table::{BytecodeFieldTag, BytecodeTable, KeccakTable, LookupTable}, util::{get_push_size, Challenges, Expr, SubCircuit, SubCircuitConfig}, witness, }; use bus_mapping::state_db::EMPTY_CODE_HASH_LE; use eth_types::{Field, ToLittleEndian}; -use gadgets::is_zero::{IsZeroChip, IsZeroConfig, IsZeroInstruction}; +use gadgets::is_zero::{IsZeroChip, IsZeroInstruction}; use halo2_proofs::{ circuit::{Layouter, Region, Value}, plonk::{ @@ -22,8 +26,42 @@ use super::{ param::PUSH_TABLE_WIDTH, }; -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -use halo2_proofs::{circuit::SimpleFloorPlanner, plonk::Circuit}; +#[derive(Debug, Clone, Default)] +/// Row for assignment +pub struct BytecodeCircuitRow { + offset: usize, + last_row_offset: usize, + code_hash: Value, + tag: F, + index: F, + is_code: F, + value: F, + push_data_left: u64, + value_rlc: Value, + length: F, + push_data_size: F, +} +impl BytecodeCircuitRow { + /// enable selector if we are within the range of table size. + pub fn enable(&self) -> bool { + self.offset <= self.last_row_offset + } + + /// Determine if we are at last row of the bytecode table. + pub fn last(&self) -> bool { + self.offset == self.last_row_offset + } + + /// Get offset + pub fn offset(&self) -> usize { + self.offset + } + + /// Witness to IsZero chip to determine if we are at the last row of a bytecode instance + pub fn diff(&self) -> F { + self.index + F::ONE - self.length + } +} #[derive(Clone, Debug)] /// Bytecode circuit configuration @@ -37,8 +75,8 @@ pub struct BytecodeCircuitConfig { value_rlc: Column, length: Column, push_data_size: Column, - push_data_left_inv: Column, - push_data_left_is_zero: IsZeroConfig, + push_data_left_is_zero: IsZeroChip, + index_length_diff_is_zero: IsZeroChip, push_table: [Column; PUSH_TABLE_WIDTH], // External tables pub(crate) keccak_table: KeccakTable, @@ -75,6 +113,7 @@ impl SubCircuitConfig for BytecodeCircuitConfig { let length = meta.advice_column(); let push_data_size = meta.advice_column(); let push_data_left_inv = meta.advice_column(); + let index_length_diff_inv = meta.advice_column(); let push_table = array_init::array_init(|_| meta.fixed_column()); // annotate columns @@ -128,6 +167,16 @@ impl SubCircuitConfig for BytecodeCircuitConfig { push_data_left_inv, ); + let index_length_diff_is_zero = IsZeroChip::configure( + meta, + |meta| meta.query_fixed(q_enable, Rotation::cur()), + |meta| { + meta.query_advice(bytecode_table.index, Rotation::cur()) + 1.expr() + - meta.query_advice(length, Rotation::cur()) + }, + index_length_diff_inv, + ); + // When q_first || q_last -> // assert cur.tag == Header meta.create_gate("first and last row", |meta| { @@ -342,6 +391,23 @@ impl SubCircuitConfig for BytecodeCircuitConfig { ])) }); + // When cur.tag == Byte and cur.index + 1 == cur.length -> + // assert next.tag == Header + meta.create_gate("cur.tag == Byte and cur.index + 1 == cur.length", |meta| { + let mut cb = BaseConstraintBuilder::default(); + + cb.require_zero( + "next.tag == Header", + meta.query_advice(bytecode_table.tag, Rotation::next()), + ); + + cb.gate(and::expr(vec![ + meta.query_fixed(q_enable, Rotation::cur()), + meta.query_advice(bytecode_table.tag, Rotation::cur()), + index_length_diff_is_zero.clone().is_zero_expression, + ])) + }); + // When is_byte_to_header -> // assert cur.index + 1 == cur.length // assert keccak256_table_lookup(cur.hash, cur.length, cur.value_rlc) @@ -387,6 +453,9 @@ impl SubCircuitConfig for BytecodeCircuitConfig { }, ); + let push_data_left_is_zero = IsZeroChip::construct(push_data_left_is_zero); + let index_length_diff_is_zero = IsZeroChip::construct(index_length_diff_is_zero); + BytecodeCircuitConfig { minimum_rows: meta.minimum_rows(), q_enable, @@ -397,8 +466,8 @@ impl SubCircuitConfig for BytecodeCircuitConfig { value_rlc, length, push_data_size, - push_data_left_inv, push_data_left_is_zero, + index_length_diff_is_zero, push_table, keccak_table, } @@ -406,27 +475,15 @@ impl SubCircuitConfig for BytecodeCircuitConfig { } impl BytecodeCircuitConfig { - pub(crate) fn assign( - &self, - layouter: &mut impl Layouter, - size: usize, - witness: &[UnrolledBytecode], - challenges: &Challenges>, - ) -> Result<(), Error> { - self.assign_internal(layouter, size, witness, challenges, true) - } - pub(crate) fn assign_internal( &self, layouter: &mut impl Layouter, size: usize, witness: &[UnrolledBytecode], + overwrite: &UnrolledBytecode, challenges: &Challenges>, fail_fast: bool, ) -> Result<(), Error> { - let push_data_left_is_zero_chip = - IsZeroChip::construct(self.push_data_left_is_zero.clone()); - // Subtract the unusable rows from the size assert!(size > self.minimum_rows); let last_row_offset = size - self.minimum_rows + 1; @@ -438,10 +495,6 @@ impl BytecodeCircuitConfig { last_row_offset ); - let empty_hash = challenges - .evm_word() - .map(|challenge| rlc::value(EMPTY_CODE_HASH_LE.as_ref(), challenge)); - layouter.assign_region( || "assign bytecode", |mut region| { @@ -454,8 +507,6 @@ impl BytecodeCircuitConfig { &mut region, bytecode, challenges, - &push_data_left_is_zero_chip, - empty_hash, &mut offset, last_row_offset, fail_fast, @@ -464,27 +515,64 @@ impl BytecodeCircuitConfig { // Padding for idx in offset..=last_row_offset { - self.set_padding_row( - &mut region, - &push_data_left_is_zero_chip, - empty_hash, - idx, - last_row_offset, - )?; + self.set_padding_row(&mut region, challenges, idx, last_row_offset)?; + } + + // Overwrite the witness assignment by using the values in the `overwrite` + // parameter. This is used to explicitly set intermediate witness values for + // negative tests. + let mut value_rlc = challenges.keccak_input().map(|_| F::ZERO); + for (offset, row) in overwrite.rows.iter().enumerate() { + for (name, column, value) in [ + ("tag", self.bytecode_table.tag, row.tag), + ("index", self.bytecode_table.index, row.index), + ("is_code", self.bytecode_table.is_code, row.is_code), + ("value", self.bytecode_table.value, row.value), + ("length", self.length, F::from(overwrite.bytes.len() as u64)), + ] { + region.assign_advice( + || format!("assign {} {}", name, offset), + column, + offset, + || Value::known(value), + )?; + } + + if row.tag == F::ONE { + value_rlc.as_mut().zip(challenges.keccak_input()).map( + |(value_rlc, challenge)| { + *value_rlc = *value_rlc * challenge + row.value + }, + ); + } else { + value_rlc = challenges.keccak_input().map(|_| F::ZERO); + } + + let code_hash = challenges + .evm_word() + .map(|challenge| rlc::value(&row.code_hash.to_le_bytes(), challenge)); + for (name, column, value) in [ + ("code_hash", self.bytecode_table.code_hash, code_hash), + ("value_rlc", self.value_rlc, value_rlc), + ] { + region.assign_advice( + || format!("assign {} {}", name, offset), + column, + offset, + || value, + )?; + } } Ok(()) }, ) } - #[allow(clippy::too_many_arguments)] fn assign_bytecode( &self, region: &mut Region<'_, F>, bytecode: &UnrolledBytecode, challenges: &Challenges>, - push_data_left_is_zero_chip: &IsZeroChip, - empty_hash: Value, offset: &mut usize, last_row_offset: usize, fail_fast: bool, @@ -493,7 +581,7 @@ impl BytecodeCircuitConfig { let mut push_data_left = 0; let mut next_push_data_left = 0; let mut push_data_size = 0; - let mut value_rlc = challenges.keccak_input().map(|_| F::zero()); + let mut value_rlc = challenges.keccak_input().map(|_| F::ZERO); let length = F::from(bytecode.bytes.len() as u64); // Code hash with challenge is calculated only using the first row of the @@ -533,49 +621,28 @@ impl BytecodeCircuitConfig { // Set the data for this row if *offset < last_row_offset { - self.set_row( - region, - push_data_left_is_zero_chip, - *offset, - true, - *offset == last_row_offset, + let row = BytecodeCircuitRow { + offset: *offset, + last_row_offset, code_hash, - row.tag, - row.index, - row.is_code, - row.value, + tag: row.tag, + index: row.index, + is_code: row.is_code, + value: row.value, push_data_left, value_rlc, length, - F::from(push_data_size as u64), - )?; + push_data_size: F::from(push_data_size), + }; + self.set_row(region, row.clone())?; - trace!( - "bytecode.set_row({}): last:{} h:{:?} t:{:?} i:{:?} c:{:?} v:{:?} pdl:{} rlc:{:?} l:{:?} pds:{:?}", - offset, - *offset == last_row_offset, - code_hash, - row.tag.get_lower_32(), - row.index.get_lower_32(), - row.is_code.get_lower_32(), - row.value.get_lower_32(), - push_data_left, - value_rlc, - length.get_lower_32(), - push_data_size - ); + trace!("bytecode.set_row({:?})", row); *offset += 1; push_data_left = next_push_data_left } if *offset == last_row_offset { - self.set_padding_row( - region, - push_data_left_is_zero_chip, - empty_hash, - *offset, - last_row_offset, - )?; + self.set_padding_row(region, challenges, *offset, last_row_offset)?; } } @@ -585,53 +652,35 @@ impl BytecodeCircuitConfig { fn set_padding_row( &self, region: &mut Region<'_, F>, - push_data_left_is_zero_chip: &IsZeroChip, - empty_hash: Value, + challenges: &Challenges>, offset: usize, last_row_offset: usize, ) -> Result<(), Error> { + let empty_hash = challenges + .evm_word() + .map(|challenge| rlc::value(EMPTY_CODE_HASH_LE.as_ref(), challenge)); + self.set_row( region, - push_data_left_is_zero_chip, - offset, - offset < last_row_offset, - offset == last_row_offset, - empty_hash, - F::from(BytecodeFieldTag::Header as u64), - F::zero(), - F::zero(), - F::zero(), - 0, - Value::known(F::zero()), - F::zero(), - F::zero(), + BytecodeCircuitRow { + offset, + last_row_offset, + code_hash: empty_hash, + tag: F::from(BytecodeFieldTag::Header as u64), + value_rlc: Value::known(F::ZERO), + ..Default::default() + }, ) } - #[allow(clippy::too_many_arguments)] - fn set_row( - &self, - region: &mut Region<'_, F>, - push_data_left_is_zero_chip: &IsZeroChip, - offset: usize, - enable: bool, - last: bool, - code_hash: Value, - tag: F, - index: F, - is_code: F, - value: F, - push_data_left: u64, - value_rlc: Value, - length: F, - push_data_size: F, - ) -> Result<(), Error> { + fn set_row(&self, region: &mut Region<'_, F>, row: BytecodeCircuitRow) -> Result<(), Error> { + let offset = row.offset; // q_enable region.assign_fixed( || format!("assign q_enable {}", offset), self.q_enable, offset, - || Value::known(F::from(enable as u64)), + || Value::known(F::from(row.enable().into())), )?; // q_first @@ -639,31 +688,30 @@ impl BytecodeCircuitConfig { || format!("assign q_first {}", offset), self.q_first, offset, - || Value::known(F::from((offset == 0) as u64)), + || Value::known(F::from((offset == 0).into())), )?; // q_last - let q_last_value = if last { F::one() } else { F::zero() }; region.assign_fixed( || format!("assign q_last {}", offset), self.q_last, offset, - || Value::known(q_last_value), + || Value::known(F::from(row.last().into())), )?; // Advices for (name, column, value) in [ - ("tag", self.bytecode_table.tag, tag), - ("index", self.bytecode_table.index, index), - ("is_code", self.bytecode_table.is_code, is_code), - ("value", self.bytecode_table.value, value), + ("tag", self.bytecode_table.tag, row.tag), + ("index", self.bytecode_table.index, row.index), + ("is_code", self.bytecode_table.is_code, row.is_code), + ("value", self.bytecode_table.value, row.value), ( "push_data_left", self.push_data_left, - F::from(push_data_left), + F::from(row.push_data_left), ), - ("length", self.length, length), - ("push_data_size", self.push_data_size, push_data_size), + ("length", self.length, row.length), + ("push_data_size", self.push_data_size, row.push_data_size), ] { region.assign_advice( || format!("assign {} {}", name, offset), @@ -673,8 +721,8 @@ impl BytecodeCircuitConfig { )?; } for (name, column, value) in [ - ("code_hash", self.bytecode_table.code_hash, code_hash), - ("value_rlc", self.value_rlc, value_rlc), + ("code_hash", self.bytecode_table.code_hash, row.code_hash), + ("value_rlc", self.value_rlc, row.value_rlc), ] { region.assign_advice( || format!("assign {} {}", name, offset), @@ -684,12 +732,15 @@ impl BytecodeCircuitConfig { )?; } - push_data_left_is_zero_chip.assign( + self.push_data_left_is_zero.assign( region, offset, - Value::known(F::from(push_data_left)), + Value::known(F::from(row.push_data_left)), )?; + self.index_length_diff_is_zero + .assign(region, offset, Value::known(row.diff()))?; + Ok(()) } @@ -699,6 +750,8 @@ impl BytecodeCircuitConfig { self.push_data_left_is_zero .annotate_columns_in_region(region, "BYTECODE"); + self.index_length_diff_is_zero + .annotate_columns_in_region(region, "BYTECODE"); region.name_column(|| "BYTECODE_q_enable", self.q_enable); region.name_column(|| "BYTECODE_q_first", self.q_first); region.name_column(|| "BYTECODE_q_last", self.q_last); @@ -706,7 +759,6 @@ impl BytecodeCircuitConfig { region.name_column(|| "BYTECODE_push_data_left", self.push_data_left); region.name_column(|| "BYTECODE_push_data_size", self.push_data_size); region.name_column(|| "BYTECODE_value_rlc", self.value_rlc); - region.name_column(|| "BYTECODE_push_data_left_inv", self.push_data_left_inv); } /// load fixed tables @@ -747,20 +799,26 @@ pub struct BytecodeCircuit { pub bytecodes: Vec>, /// Circuit size pub size: usize, + /// Overwrite + pub overwrite: UnrolledBytecode, } impl BytecodeCircuit { /// new BytecodeCircuitTester pub fn new(bytecodes: Vec>, size: usize) -> Self { - BytecodeCircuit { bytecodes, size } + BytecodeCircuit { + bytecodes, + size, + overwrite: Default::default(), + } } /// Creates bytecode circuit from block and bytecode_size. pub fn new_from_block_sized(block: &witness::Block, bytecode_size: usize) -> Self { let bytecodes: Vec> = block .bytecodes - .iter() - .map(|(_, b)| unroll(b.bytes.clone())) + .values() + .map(|b| unroll(b.bytes.clone())) .collect(); Self::new(bytecodes, bytecode_size) } @@ -769,15 +827,14 @@ impl BytecodeCircuit { impl SubCircuit for BytecodeCircuit { type Config = BytecodeCircuitConfig; + fn unusable_rows() -> usize { + // No column queried at more than 3 distinct rotations, so returns 6 as + // minimum unusable rows. + 6 + } + fn new_from_block(block: &witness::Block) -> Self { - // TODO: Find a nicer way to add the extra `128`. Is this to account for - // unusable rows? Then it could be calculated like this: - // fn unusable_rows>() -> usize { - // let mut cs = ConstraintSystem::default(); - // C::configure(&mut cs); - // cs.blinding_factors() - // } - let bytecode_size = block.circuits_params.max_bytecode + 128; + let bytecode_size = block.circuits_params.max_bytecode; Self::new_from_block_sized(block, bytecode_size) } @@ -801,298 +858,13 @@ impl SubCircuit for BytecodeCircuit { layouter: &mut impl Layouter, ) -> Result<(), Error> { config.load_aux_tables(layouter)?; - config.assign_internal(layouter, self.size, &self.bytecodes, challenges, false) - } -} - -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -impl Circuit for BytecodeCircuit { - type Config = (BytecodeCircuitConfig, Challenges); - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let bytecode_table = BytecodeTable::construct(meta); - let keccak_table = KeccakTable::construct(meta); - let challenges = Challenges::construct(meta); - - let config = { - let challenges = challenges.exprs(meta); - BytecodeCircuitConfig::new( - meta, - BytecodeCircuitConfigArgs { - bytecode_table, - keccak_table, - challenges, - }, - ) - }; - - (config, challenges) - } - - fn synthesize( - &self, - (config, challenges): Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let challenges = challenges.values(&mut layouter); - - config.keccak_table.dev_load( - &mut layouter, - self.bytecodes.iter().map(|b| &b.bytes), - &challenges, - true, - )?; - self.synthesize_sub(&config, &challenges, &mut layouter)?; - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - bytecode_circuit::{bytecode_unroller::BytecodeRow, dev::test_bytecode_circuit_unrolled}, - util::{is_push, keccak}, - }; - use bus_mapping::evm::OpcodeId; - use eth_types::{Bytecode, Word}; - use halo2_proofs::halo2curves::bn256::Fr; - - /// Verify unrolling code - #[test] - fn bytecode_unrolling() { - let k = 10; - let mut rows = vec![]; - let mut bytecode = Bytecode::default(); - // First add all non-push bytes, which should all be seen as code - for byte in 0u8..=255u8 { - if !is_push(byte) { - bytecode.write(byte, true); - rows.push(BytecodeRow { - code_hash: Word::zero(), - tag: Fr::from(BytecodeFieldTag::Byte as u64), - index: Fr::from(rows.len() as u64), - is_code: Fr::from(true as u64), - value: Fr::from(byte as u64), - }); - } - } - // Now add the different push ops - for n in 1..=32 { - let data_byte = OpcodeId::PUSH32.as_u8(); - bytecode.push( - n, - Word::from_little_endian(&vec![data_byte; n as usize][..]), - ); - rows.push(BytecodeRow { - code_hash: Word::zero(), - tag: Fr::from(BytecodeFieldTag::Byte as u64), - index: Fr::from(rows.len() as u64), - is_code: Fr::from(true as u64), - value: Fr::from(OpcodeId::PUSH1.as_u64() + ((n - 1) as u64)), - }); - for _ in 0..n { - rows.push(BytecodeRow { - code_hash: Word::zero(), - tag: Fr::from(BytecodeFieldTag::Byte as u64), - index: Fr::from(rows.len() as u64), - is_code: Fr::from(false as u64), - value: Fr::from(data_byte as u64), - }); - } - } - // Set the code_hash of the complete bytecode in the rows - let code_hash = keccak(&bytecode.to_vec()[..]); - for row in rows.iter_mut() { - row.code_hash = code_hash; - } - rows.insert( - 0, - BytecodeRow { - code_hash, - tag: Fr::from(BytecodeFieldTag::Header as u64), - index: Fr::zero(), - is_code: Fr::zero(), - value: Fr::from(bytecode.to_vec().len() as u64), - }, - ); - // Unroll the bytecode - let unrolled = unroll(bytecode.to_vec()); - // Check if the bytecode was unrolled correctly - assert_eq!( - UnrolledBytecode { - bytes: bytecode.to_vec(), - rows, - }, - unrolled, - ); - // Verify the unrolling in the circuit - test_bytecode_circuit_unrolled::(k, vec![unrolled], true); - } - - /// Tests a fully empty circuit - #[test] - fn bytecode_empty() { - let k = 9; - test_bytecode_circuit_unrolled::(k, vec![unroll(vec![])], true); - } - - #[test] - fn bytecode_simple() { - let k = 9; - let bytecodes = vec![unroll(vec![7u8]), unroll(vec![6u8]), unroll(vec![5u8])]; - test_bytecode_circuit_unrolled::(k, bytecodes, true); - } - - /// Tests a fully full circuit - #[test] - fn bytecode_full() { - let k = 9; - test_bytecode_circuit_unrolled::(k, vec![unroll(vec![7u8; 2usize.pow(k) - 8])], true); - } - - #[test] - fn bytecode_last_row_with_byte() { - let k = 9; - // Last row must be a padding row, so we have one row less for actual bytecode - test_bytecode_circuit_unrolled::(k, vec![unroll(vec![7u8; 2usize.pow(k) - 7])], false); - } - - /// Tests a circuit with incomplete bytecode - #[test] - fn bytecode_incomplete() { - let k = 9; - test_bytecode_circuit_unrolled::(k, vec![unroll(vec![7u8; 2usize.pow(k) + 1])], false); - } - - /// Tests multiple bytecodes in a single circuit - #[test] - fn bytecode_push() { - let k = 9; - test_bytecode_circuit_unrolled::( - k, - vec![ - unroll(vec![]), - unroll(vec![OpcodeId::PUSH32.as_u8()]), - unroll(vec![OpcodeId::PUSH32.as_u8(), OpcodeId::ADD.as_u8()]), - unroll(vec![OpcodeId::ADD.as_u8(), OpcodeId::PUSH32.as_u8()]), - unroll(vec![ - OpcodeId::ADD.as_u8(), - OpcodeId::PUSH32.as_u8(), - OpcodeId::ADD.as_u8(), - ]), - ], - true, - ); - } - - /// Test invalid code_hash data - #[test] - fn bytecode_invalid_hash_data() { - let k = 9; - let bytecode = vec![8u8, 2, 3, 8, 9, 7, 128]; - let unrolled = unroll(bytecode); - test_bytecode_circuit_unrolled::(k, vec![unrolled.clone()], true); - // Change the code_hash on the first position (header row) - { - let mut invalid = unrolled; - invalid.rows[0].code_hash += Word::one(); - trace!("bytecode_invalid_hash_data: Change the code_hash on the first position"); - test_bytecode_circuit_unrolled::(k, vec![invalid], false); - } - // TODO: other rows code_hash are ignored by the witness generation, to - // test other rows invalid code_hash, we would need to inject an evil - // witness. - } - - /// Test invalid index - #[test] - #[ignore] - fn bytecode_invalid_index() { - let k = 9; - let bytecode = vec![8u8, 2, 3, 8, 9, 7, 128]; - let unrolled = unroll(bytecode); - test_bytecode_circuit_unrolled::(k, vec![unrolled.clone()], true); - // Start the index at 1 - { - let mut invalid = unrolled.clone(); - for row in invalid.rows.iter_mut() { - row.index += Fr::one(); - } - test_bytecode_circuit_unrolled::(k, vec![invalid], false); - } - // Don't increment an index once - { - let mut invalid = unrolled; - invalid.rows.last_mut().unwrap().index -= Fr::one(); - test_bytecode_circuit_unrolled::(k, vec![invalid], false); - } - } - - /// Test invalid byte data - #[test] - fn bytecode_invalid_byte_data() { - let k = 9; - let bytecode = vec![8u8, 2, 3, 8, 9, 7, 128]; - let unrolled = unroll(bytecode); - test_bytecode_circuit_unrolled::(k, vec![unrolled.clone()], true); - // Change the first byte - { - let mut invalid = unrolled.clone(); - invalid.rows[1].value = Fr::from(9u64); - test_bytecode_circuit_unrolled::(k, vec![invalid], false); - } - // Change a byte on another position - { - let mut invalid = unrolled.clone(); - invalid.rows[5].value = Fr::from(6u64); - test_bytecode_circuit_unrolled::(k, vec![invalid], false); - } - // Set a byte value out of range - { - let mut invalid = unrolled; - invalid.rows[3].value = Fr::from(256u64); - test_bytecode_circuit_unrolled::(k, vec![invalid], false); - } - } - - /// Test invalid is_code data - #[test] - fn bytecode_invalid_is_code() { - let k = 9; - let bytecode = vec![ - OpcodeId::ADD.as_u8(), - OpcodeId::PUSH1.as_u8(), - OpcodeId::PUSH1.as_u8(), - OpcodeId::SUB.as_u8(), - OpcodeId::PUSH7.as_u8(), - OpcodeId::ADD.as_u8(), - OpcodeId::PUSH6.as_u8(), - ]; - let unrolled = unroll(bytecode); - test_bytecode_circuit_unrolled::(k, vec![unrolled.clone()], true); - // Mark the 3rd byte as code (is push data from the first PUSH1) - { - let mut invalid = unrolled.clone(); - invalid.rows[3].is_code = Fr::one(); - test_bytecode_circuit_unrolled::(k, vec![invalid], false); - } - // Mark the 4rd byte as data (is code) - { - let mut invalid = unrolled.clone(); - invalid.rows[4].is_code = Fr::zero(); - test_bytecode_circuit_unrolled::(k, vec![invalid], false); - } - // Mark the 7th byte as code (is data for the PUSH7) - { - let mut invalid = unrolled; - invalid.rows[7].is_code = Fr::one(); - test_bytecode_circuit_unrolled::(k, vec![invalid], false); - } + config.assign_internal( + layouter, + self.size, + &self.bytecodes, + &self.overwrite, + challenges, + false, + ) } } diff --git a/zkevm-circuits/src/bytecode_circuit/dev.rs b/zkevm-circuits/src/bytecode_circuit/dev.rs index d4610c32d0..5d5c4b14d4 100644 --- a/zkevm-circuits/src/bytecode_circuit/dev.rs +++ b/zkevm-circuits/src/bytecode_circuit/dev.rs @@ -1,49 +1,59 @@ -use super::{ - bytecode_unroller::{unroll, UnrolledBytecode}, - circuit::BytecodeCircuit, -}; +pub use super::circuit::BytecodeCircuit; +use crate::{ + bytecode_circuit::circuit::{BytecodeCircuitConfig, BytecodeCircuitConfigArgs}, + table::{BytecodeTable, KeccakTable}, + util::{Challenges, SubCircuit, SubCircuitConfig}, +}; use eth_types::Field; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner}, + plonk::{Circuit, ConstraintSystem, Error}, +}; -use halo2_proofs::dev::MockProver; -use log::error; +impl Circuit for BytecodeCircuit { + type Config = (BytecodeCircuitConfig, Challenges); + type FloorPlanner = SimpleFloorPlanner; + type Params = (); -impl BytecodeCircuit { - /// Verify that the selected bytecode fulfills the circuit - pub fn verify_raw(k: u32, bytecodes: Vec>) { - let unrolled: Vec<_> = bytecodes.iter().map(|b| unroll(b.clone())).collect(); - Self::verify(k, unrolled, true); + fn without_witnesses(&self) -> Self { + Self::default() } - pub(crate) fn verify(k: u32, bytecodes: Vec>, success: bool) { - let circuit = BytecodeCircuit::::new(bytecodes, 2usize.pow(k)); - - let prover = MockProver::::run(k, &circuit, Vec::new()).unwrap(); - let result = prover.verify(); - if let Err(failures) = &result { - for failure in failures.iter() { - error!("{}", failure); - } - } - assert_eq!(result.is_ok(), success); + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let bytecode_table = BytecodeTable::construct(meta); + let keccak_table = KeccakTable::construct(meta); + let challenges = Challenges::construct(meta); + + let config = { + let challenges = challenges.exprs(meta); + BytecodeCircuitConfig::new( + meta, + BytecodeCircuitConfigArgs { + bytecode_table, + keccak_table, + challenges, + }, + ) + }; + + (config, challenges) } -} -/// Test bytecode circuit with unrolled bytecode -pub fn test_bytecode_circuit_unrolled( - k: u32, - bytecodes: Vec>, - success: bool, -) { - let circuit = BytecodeCircuit::::new(bytecodes, 2usize.pow(k)); - - let prover = MockProver::::run(k, &circuit, Vec::new()).unwrap(); - let result = prover.verify_par(); - if let Err(failures) = &result { - for failure in failures.iter() { - error!("{}", failure); - } + fn synthesize( + &self, + (config, challenges): Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenges = challenges.values(&mut layouter); + + config.keccak_table.dev_load( + &mut layouter, + self.bytecodes.iter().map(|b| &b.bytes), + &challenges, + true, + )?; + self.synthesize_sub(&config, &challenges, &mut layouter)?; + Ok(()) } - let error_msg = if success { "valid" } else { "invalid" }; - assert_eq!(result.is_ok(), success, "proof must be {}", error_msg); } diff --git a/zkevm-circuits/src/bytecode_circuit/test.rs b/zkevm-circuits/src/bytecode_circuit/test.rs new file mode 100644 index 0000000000..c765aec905 --- /dev/null +++ b/zkevm-circuits/src/bytecode_circuit/test.rs @@ -0,0 +1,325 @@ +#![allow(unused_imports)] +use crate::{ + bytecode_circuit::{bytecode_unroller::*, circuit::BytecodeCircuit}, + table::BytecodeFieldTag, + util::{is_push, keccak, unusable_rows, Challenges, SubCircuit}, +}; +use bus_mapping::evm::OpcodeId; +use eth_types::{Bytecode, Field, Word}; +use halo2_proofs::{arithmetic::Field as Halo2Field, dev::MockProver, halo2curves::bn256::Fr}; +use log::error; + +#[test] +fn bytecode_circuit_unusable_rows() { + assert_eq!( + BytecodeCircuit::::unusable_rows(), + unusable_rows::>(()), + ) +} + +impl BytecodeCircuit { + /// Verify that the selected bytecode fulfills the circuit + pub fn verify_raw(k: u32, bytecodes: Vec>) { + let unrolled: Vec<_> = bytecodes.iter().map(|b| unroll(b.clone())).collect(); + Self::verify(k, unrolled, true); + } + + pub(crate) fn verify(k: u32, bytecodes: Vec>, success: bool) { + let circuit = BytecodeCircuit::::new(bytecodes, 2usize.pow(k)); + + let prover = MockProver::::run(k, &circuit, Vec::new()).unwrap(); + let result = prover.verify(); + if let Err(failures) = &result { + for failure in failures.iter() { + error!("{}", failure); + } + } + assert_eq!(result.is_ok(), success); + } +} + +/// Test bytecode circuit with unrolled bytecode +pub fn test_bytecode_circuit_unrolled( + k: u32, + bytecodes: Vec>, + success: bool, +) { + let circuit = BytecodeCircuit::::new(bytecodes, 2usize.pow(k)); + + let prover = MockProver::::run(k, &circuit, Vec::new()).unwrap(); + let result = prover.verify_par(); + if let Err(failures) = &result { + for failure in failures.iter() { + error!("{}", failure); + } + } + let error_msg = if success { "valid" } else { "invalid" }; + assert_eq!(result.is_ok(), success, "proof must be {}", error_msg); +} + +/// Verify unrolling code +#[test] +fn bytecode_unrolling() { + let k = 10; + let mut rows = vec![]; + let mut bytecode = Bytecode::default(); + // First add all non-push bytes, which should all be seen as code + for byte in 0u8..=255u8 { + if !is_push(byte) { + bytecode.write(byte, true); + rows.push(BytecodeRow { + code_hash: Word::zero(), + tag: Fr::from(BytecodeFieldTag::Byte as u64), + index: Fr::from(rows.len() as u64), + is_code: Fr::from(true as u64), + value: Fr::from(byte as u64), + }); + } + } + // Now add the different push ops + for n in 1..=32 { + let data_byte = OpcodeId::PUSH32.as_u8(); + bytecode.push( + n, + Word::from_little_endian(&vec![data_byte; n as usize][..]), + ); + rows.push(BytecodeRow { + code_hash: Word::zero(), + tag: Fr::from(BytecodeFieldTag::Byte as u64), + index: Fr::from(rows.len() as u64), + is_code: Fr::from(true as u64), + value: Fr::from(OpcodeId::PUSH1.as_u64() + ((n - 1) as u64)), + }); + for _ in 0..n { + rows.push(BytecodeRow { + code_hash: Word::zero(), + tag: Fr::from(BytecodeFieldTag::Byte as u64), + index: Fr::from(rows.len() as u64), + is_code: Fr::from(false as u64), + value: Fr::from(data_byte as u64), + }); + } + } + // Set the code_hash of the complete bytecode in the rows + let code_hash = keccak(&bytecode.to_vec()[..]); + for row in rows.iter_mut() { + row.code_hash = code_hash; + } + rows.insert( + 0, + BytecodeRow { + code_hash, + tag: Fr::from(BytecodeFieldTag::Header as u64), + index: Fr::ZERO, + is_code: Fr::ZERO, + value: Fr::from(bytecode.to_vec().len() as u64), + }, + ); + // Unroll the bytecode + let unrolled = unroll(bytecode.to_vec()); + // Check if the bytecode was unrolled correctly + assert_eq!( + UnrolledBytecode { + bytes: bytecode.to_vec(), + rows, + }, + unrolled, + ); + // Verify the unrolling in the circuit + test_bytecode_circuit_unrolled::(k, vec![unrolled], true); +} + +/// Tests a fully empty circuit +#[test] +fn bytecode_empty() { + let k = 9; + test_bytecode_circuit_unrolled::(k, vec![unroll(vec![])], true); +} + +#[test] +fn bytecode_simple() { + let k = 9; + let bytecodes = vec![unroll(vec![7u8]), unroll(vec![6u8]), unroll(vec![5u8])]; + test_bytecode_circuit_unrolled::(k, bytecodes, true); +} + +/// Tests a fully full circuit +#[test] +fn bytecode_full() { + let k = 9; + test_bytecode_circuit_unrolled::(k, vec![unroll(vec![7u8; 2usize.pow(k) - 8])], true); +} + +#[test] +fn bytecode_last_row_with_byte() { + let k = 9; + // Last row must be a padding row, so we have one row less for actual bytecode + test_bytecode_circuit_unrolled::(k, vec![unroll(vec![7u8; 2usize.pow(k) - 7])], false); +} + +/// Tests a circuit with incomplete bytecode +#[test] +fn bytecode_incomplete() { + let k = 9; + test_bytecode_circuit_unrolled::(k, vec![unroll(vec![7u8; 2usize.pow(k) + 1])], false); +} + +/// Tests multiple bytecodes in a single circuit +#[test] +fn bytecode_push() { + let k = 9; + test_bytecode_circuit_unrolled::( + k, + vec![ + unroll(vec![]), + unroll(vec![OpcodeId::PUSH32.as_u8()]), + unroll(vec![OpcodeId::PUSH32.as_u8(), OpcodeId::ADD.as_u8()]), + unroll(vec![OpcodeId::ADD.as_u8(), OpcodeId::PUSH32.as_u8()]), + unroll(vec![ + OpcodeId::ADD.as_u8(), + OpcodeId::PUSH32.as_u8(), + OpcodeId::ADD.as_u8(), + ]), + ], + true, + ); +} + +/// Test invalid code_hash data +#[test] +fn bytecode_invalid_hash_data() { + let k = 9; + let bytecode = vec![8u8, 2, 3, 8, 9, 7, 128]; + let unrolled = unroll(bytecode); + test_bytecode_circuit_unrolled::(k, vec![unrolled.clone()], true); + // Change the code_hash on the first position (header row) + { + let mut invalid = unrolled; + invalid.rows[0].code_hash += Word::one(); + log::trace!("bytecode_invalid_hash_data: Change the code_hash on the first position"); + test_bytecode_circuit_unrolled::(k, vec![invalid], false); + } + // TODO: other rows code_hash are ignored by the witness generation, to + // test other rows invalid code_hash, we would need to inject an evil + // witness. +} + +/// Test invalid index +#[test] +#[ignore] +fn bytecode_invalid_index() { + let k = 9; + let bytecode = vec![8u8, 2, 3, 8, 9, 7, 128]; + let unrolled = unroll(bytecode); + test_bytecode_circuit_unrolled::(k, vec![unrolled.clone()], true); + // Start the index at 1 + { + let mut invalid = unrolled.clone(); + for row in invalid.rows.iter_mut() { + row.index += Fr::ONE; + } + test_bytecode_circuit_unrolled::(k, vec![invalid], false); + } + // Don't increment an index once + { + let mut invalid = unrolled; + invalid.rows.last_mut().unwrap().index -= Fr::ONE; + test_bytecode_circuit_unrolled::(k, vec![invalid], false); + } +} + +/// Test invalid byte data +#[test] +fn bytecode_invalid_byte_data() { + let k = 9; + let bytecode = vec![8u8, 2, 3, 8, 9, 7, 128]; + let unrolled = unroll(bytecode); + test_bytecode_circuit_unrolled::(k, vec![unrolled.clone()], true); + // Change the first byte + { + let mut invalid = unrolled.clone(); + invalid.rows[1].value = Fr::from(9u64); + test_bytecode_circuit_unrolled::(k, vec![invalid], false); + } + // Change a byte on another position + { + let mut invalid = unrolled.clone(); + invalid.rows[5].value = Fr::from(6u64); + test_bytecode_circuit_unrolled::(k, vec![invalid], false); + } + // Set a byte value out of range + { + let mut invalid = unrolled; + invalid.rows[3].value = Fr::from(256u64); + test_bytecode_circuit_unrolled::(k, vec![invalid], false); + } +} + +/// Test invalid is_code data +#[test] +fn bytecode_invalid_is_code() { + let k = 9; + let bytecode = vec![ + OpcodeId::ADD.as_u8(), + OpcodeId::PUSH1.as_u8(), + OpcodeId::PUSH1.as_u8(), + OpcodeId::SUB.as_u8(), + OpcodeId::PUSH7.as_u8(), + OpcodeId::ADD.as_u8(), + OpcodeId::PUSH6.as_u8(), + ]; + let unrolled = unroll(bytecode); + test_bytecode_circuit_unrolled::(k, vec![unrolled.clone()], true); + // Mark the 3rd byte as code (is push data from the first PUSH1) + { + let mut invalid = unrolled.clone(); + invalid.rows[3].is_code = Fr::ONE; + test_bytecode_circuit_unrolled::(k, vec![invalid], false); + } + // Mark the 4rd byte as data (is code) + { + let mut invalid = unrolled.clone(); + invalid.rows[4].is_code = Fr::ZERO; + test_bytecode_circuit_unrolled::(k, vec![invalid], false); + } + // Mark the 7th byte as code (is data for the PUSH7) + { + let mut invalid = unrolled; + invalid.rows[7].is_code = Fr::ONE; + test_bytecode_circuit_unrolled::(k, vec![invalid], false); + } +} + +#[test] +#[should_panic] +#[allow(clippy::clone_on_copy)] +fn bytecode_soundness_bug_1() { + let k = 9; + let bytecode = vec![1, 2, 3, 4]; + let bytecode_len = bytecode.len(); + let unrolled = unroll(bytecode); + let unrolled_len = unrolled.rows.len(); + let code_hash = unrolled.rows[0].code_hash.clone(); + let mut index = bytecode_len as u64; + let size = 100; + let minimum_rows = 8; + + let mut overwrite = unrolled.clone(); + for i in 0..size - minimum_rows + 3 { + if i >= unrolled_len { + overwrite.rows.push(BytecodeRow { + code_hash: code_hash.clone(), + tag: Fr::ONE, + index: Fr::from(index), + is_code: Fr::ONE, + value: Fr::from((i % 10 + 1) as u64), + }); + index += 1; + } + } + let mut circuit = BytecodeCircuit::::new(vec![unrolled], size); + circuit.overwrite = overwrite; + + let prover = MockProver::::run(k, &circuit, Vec::new()).unwrap(); + prover.assert_satisfied_par(); +} diff --git a/zkevm-circuits/src/circuit_tools.rs b/zkevm-circuits/src/circuit_tools.rs index 25519a2ba0..2c8535d22e 100644 --- a/zkevm-circuits/src/circuit_tools.rs +++ b/zkevm-circuits/src/circuit_tools.rs @@ -1,6 +1,8 @@ //! Circuit utilities +#![allow(missing_docs)] #[macro_use] pub mod constraint_builder; +pub mod cached_region; pub mod cell_manager; pub mod gadgets; pub mod memory; diff --git a/zkevm-circuits/src/circuit_tools/cached_region.rs b/zkevm-circuits/src/circuit_tools/cached_region.rs new file mode 100644 index 0000000000..510e67d152 --- /dev/null +++ b/zkevm-circuits/src/circuit_tools/cached_region.rs @@ -0,0 +1,228 @@ +use crate::circuit_tools::cell_manager::Cell; +use eth_types::Field; +use halo2_proofs::{ + circuit::{AssignedCell, Region, Value}, + plonk::{Advice, Any, Assigned, Column, Error, Expression, Fixed}, + poly::Rotation, +}; +use std::{ + collections::HashMap, + hash::{Hash, Hasher}, +}; + +use super::{cell_manager::CellType, constraint_builder::ConstraintBuilder}; + +pub trait ChallengeSet { + fn indexed(&self) -> Vec<&Value>; +} + +impl]>> ChallengeSet for V { + fn indexed(&self) -> Vec<&Value> { + self.as_ref().iter().collect() + } +} + +pub struct CachedRegion<'r, 'b, F: Field, S: ChallengeSet> { + region: &'r mut Region<'b, F>, + pub advice: HashMap<(usize, usize), F>, + pub fixed: HashMap<(usize, usize), F>, + challenges: &'r S, + disable_description: bool, + regions: Vec<(usize, usize)>, + pub le_r: F, + pub be_r: F, +} + +impl<'r, 'b, F: Field, S: ChallengeSet> CachedRegion<'r, 'b, F, S> { + pub(crate) fn new(region: &'r mut Region<'b, F>, challenges: &'r S, le_r: F, be_r: F) -> Self { + Self { + region, + advice: HashMap::new(), + fixed: HashMap::new(), + challenges, + disable_description: false, + regions: Vec::new(), + le_r, + be_r, + } + } + + pub(crate) fn set_disable_description(&mut self, disable_description: bool) { + self.disable_description = disable_description; + } + + pub(crate) fn push_region(&mut self, offset: usize, region_id: usize) { + self.regions.push((offset, region_id)); + } + + pub(crate) fn pop_region(&mut self) { + // Nothing to do + } + + pub(crate) fn assign_stored_expressions( + &mut self, + cb: &ConstraintBuilder, + ) -> Result<(), Error> { + for (offset, region_id) in self.regions.clone() { + for stored_expression in cb.get_stored_expressions(region_id).iter() { + // println!("stored expression: {}", stored_expression.name); + stored_expression.assign(self, offset)?; + } + } + Ok(()) + } + + /// Assign an advice column value (witness). + pub fn assign_advice<'v, V, VR, A, AR>( + &'v mut self, + annotation: A, + column: Column, + offset: usize, + to: V, + ) -> Result, Error> + where + V: Fn() -> Value + 'v, + for<'vr> Assigned: From<&'vr VR>, + A: Fn() -> AR, + AR: Into, + { + // Actually set the value + let res = self.region.assign_advice(annotation, column, offset, &to); + // Cache the value + // Note that the `value_field` in `AssignedCell` might be `Value::unkonwn` if + // the column has different phase than current one, so we call to `to` + // again here to cache the value. + if res.is_ok() { + to().map(|f: VR| { + let existing = self + .advice + .insert((column.index(), offset), Assigned::from(&f).evaluate()); + assert!(existing.is_none()); + existing + }); + } + res + } + + pub fn name_column(&mut self, annotation: A, column: T) + where + A: Fn() -> AR, + AR: Into, + T: Into>, + { + self.region + .name_column(|| annotation().into(), column.into()); + } + + pub fn assign_fixed<'v, V, VR, A, AR>( + &'v mut self, + annotation: A, + column: Column, + offset: usize, + to: V, + ) -> Result, Error> + where + V: Fn() -> Value + 'v, + for<'vr> Assigned: From<&'vr VR>, + A: Fn() -> AR, + AR: Into, + { + let res = self.region.assign_fixed(annotation, column, offset, &to); + if res.is_ok() { + to().map(|f: VR| { + let existing = self + .fixed + .insert((column.index(), offset), Assigned::from(&f).evaluate()); + assert!(existing.is_none()); + existing + }); + } + res + } + + pub fn get_fixed(&self, row_index: usize, column_index: usize, rotation: Rotation) -> F { + let zero = F::ZERO; + *self + .fixed + .get(&(column_index, row_index + rotation.0 as usize)) + .unwrap_or(&zero) + } + + pub fn get_advice(&self, row_index: usize, column_index: usize, rotation: Rotation) -> F { + let zero = F::ZERO; + *self + .advice + .get(&(column_index, row_index + rotation.0 as usize)) + .unwrap_or(&zero) + } + + pub fn challenges(&self) -> &S { + self.challenges + } + + /// Constrains a cell to have a constant value. + /// + /// Returns an error if the cell is in a column where equality has not been + /// enabled. + pub fn constrain_constant( + &mut self, + cell: AssignedCell, + constant: VR, + ) -> Result<(), Error> + where + VR: Into>, + { + self.region.constrain_constant(cell.cell(), constant.into()) + } +} + +#[derive(Debug, Clone)] +pub struct StoredExpression { + pub(crate) name: String, + pub(crate) cell: Cell, + pub(crate) cell_type: C, + pub(crate) expr: Expression, + pub(crate) expr_id: String, +} + +impl Hash for StoredExpression { + fn hash(&self, state: &mut H) { + self.expr_id.hash(state); + self.cell_type.hash(state); + } +} + +impl StoredExpression { + pub fn assign>( + &self, + region: &mut CachedRegion<'_, '_, F, S>, + offset: usize, + ) -> Result, Error> { + let value = self.expr.evaluate( + &|scalar| Value::known(scalar), + &|_| unimplemented!("selector column"), + &|fixed_query| { + Value::known(region.get_fixed( + offset, + fixed_query.column_index(), + fixed_query.rotation(), + )) + }, + &|advice_query| { + Value::known(region.get_advice( + offset, + advice_query.column_index(), + advice_query.rotation(), + )) + }, + &|_| unimplemented!("instance column"), + &|challenge| *region.challenges().indexed()[challenge.index()], + &|a| -a, + &|a, b| a + b, + &|a, b| a * b, + &|a, scalar| a * Value::known(scalar), + ); + self.cell.assign_value(region, offset, value)?; + Ok(value) + } +} diff --git a/zkevm-circuits/src/circuit_tools/cell_manager.rs b/zkevm-circuits/src/circuit_tools/cell_manager.rs index 9153579a75..9593f34cd9 100644 --- a/zkevm-circuits/src/circuit_tools/cell_manager.rs +++ b/zkevm-circuits/src/circuit_tools/cell_manager.rs @@ -1,20 +1,33 @@ //! Cell manager -use crate::util::Expr; +use crate::{ + circuit_tools::cached_region::{CachedRegion, ChallengeSet}, + util::{query_expression, Expr}, +}; + use eth_types::Field; use halo2_proofs::{ - circuit::{AssignedCell, Region, Value}, - plonk::{Advice, Column, Error, Expression, VirtualCells}, + circuit::{AssignedCell, Value}, + plonk::{ + Advice, Column, ConstraintSystem, Error, Expression, FirstPhase, SecondPhase, ThirdPhase, + VirtualCells, + }, poly::Rotation, }; -use std::collections::BTreeMap; +use lazy_static::__Deref; +use std::{ + cmp::{max, Ordering}, + collections::{BTreeMap, HashMap}, + fmt::Debug, + hash::Hash, +}; #[derive(Clone, Debug, Default)] pub(crate) struct Cell { // expression for constraint expression: Option>, - column: Option>, + pub column: Option>, // relative position to selector for synthesis - rotation: usize, + pub rotation: usize, } impl Cell { @@ -26,9 +39,9 @@ impl Cell { } } - pub(crate) fn assign( + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, value: F, ) -> Result, Error> { @@ -45,6 +58,26 @@ impl Cell { ) } + pub(crate) fn assign_value>( + &self, + region: &mut CachedRegion<'_, '_, F, S>, + offset: usize, + value: Value, + ) -> Result, Error> { + region.assign_advice( + || { + format!( + "Cell column: {:?} and rotation: {}", + self.column.unwrap(), + self.rotation + ) + }, + self.column.unwrap(), + offset + self.rotation, + || value, + ) + } + pub(crate) fn column(&self) -> Column { self.column.unwrap() } @@ -56,6 +89,10 @@ impl Cell { pub(crate) fn rot(&self, meta: &mut VirtualCells, rot: usize) -> Expression { meta.query_advice(self.column.unwrap(), Rotation((self.rotation + rot) as i32)) } + + pub(crate) fn identifier(&self) -> String { + self.expr().identifier() + } } impl Expr for Cell { @@ -70,78 +107,283 @@ impl Expr for &Cell { } } -/// CellType -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum CellType { - /// General - Storage, +#[derive(Clone, Debug)] +pub struct CellConfig { + pub cell_type: C, + pub num_columns: usize, + pub phase: u8, + pub permutable: bool, +} + +impl From<(C, usize, u8, bool)> for CellConfig { + fn from((cell_type, num_columns, phase, permutable): (C, usize, u8, bool)) -> Self { + Self { + cell_type, + num_columns, + phase, + permutable, + } + } +} + +impl CellConfig { + pub fn init_columns(&self, meta: &mut ConstraintSystem) -> Vec> { + let mut columns = Vec::with_capacity(self.num_columns); + for _ in 0..self.num_columns { + let tmp = match self.phase { + 0 => meta.advice_column_in(FirstPhase), + 1 => meta.advice_column_in(SecondPhase), + 2 => meta.advice_column_in(ThirdPhase), + _ => unreachable!(), + }; + columns.push(tmp); + } + if self.permutable { + let _ = columns + .iter() + .map(|c| meta.enable_equality(*c)) + .collect::>(); + } + columns + } +} + +pub trait CellType: + Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Hash + Default +{ + fn byte_type() -> Option; + + // The phase that given `Expression` becomes evaluateable. + fn expr_phase(expr: &Expression) -> u8 { + use Expression::*; + match expr { + Challenge(challenge) => challenge.phase() + 1, + Advice(query) => query.phase(), + Constant(_) | Selector(_) | Fixed(_) | Instance(_) => 0, + Negated(a) | Expression::Scaled(a, _) => Self::expr_phase(a), + Sum(a, b) | Product(a, b) => std::cmp::max(Self::expr_phase(a), Self::expr_phase(b)), + } + } + + /// Return the storage phase of phase + fn storage_for_phase(phase: u8) -> Self; + + /// Return the storage cell of the expression + fn storage_for_expr(expr: &Expression) -> Self { + Self::storage_for_phase(Self::expr_phase::(expr)) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum DefaultCellType { + StoragePhase1, + StoragePhase2, + StoragePhase3, +} + +impl Default for DefaultCellType { + fn default() -> Self { + Self::StoragePhase1 + } +} + +impl CellType for DefaultCellType { + fn byte_type() -> Option { + Some(DefaultCellType::StoragePhase1) + } + + fn storage_for_phase(phase: u8) -> Self { + // println!("phase: {}", phase); + match phase { + 0 => DefaultCellType::StoragePhase1, + 1 => DefaultCellType::StoragePhase2, + 2 => DefaultCellType::StoragePhase3, + _ => unreachable!(), + } + } } -/// CellColumn #[derive(Clone, Debug)] -pub struct CellColumn { - pub(crate) index: usize, - pub(crate) cell_type: CellType, - pub(crate) height: usize, +pub(crate) struct CellColumn { + index: usize, + pub(crate) cell_type: C, + height: usize, + cells: Vec>, pub(crate) expr: Expression, } -impl Expr for CellColumn { +impl PartialEq for CellColumn { + fn eq(&self, other: &Self) -> bool { + self.index == other.index + && self.cell_type == other.cell_type + && self.height == other.height + } +} + +impl Eq for CellColumn {} + +impl PartialOrd for CellColumn { + fn partial_cmp(&self, other: &Self) -> Option { + self.height.partial_cmp(&other.height) + } +} + +impl Ord for CellColumn { + fn cmp(&self, other: &Self) -> Ordering { + self.height.cmp(&other.height) + } +} + +impl Expr for CellColumn { fn expr(&self) -> Expression { self.expr.clone() } } -/// CellManager #[derive(Clone, Debug)] -pub struct CellManager { - width: usize, +pub struct CellManager { + configs: Vec>, + columns: Vec>, height: usize, - cells: Vec>, - columns: Vec>, + width: usize, height_limit: usize, + + // branch ctxs + branch_ctxs: HashMap>, + parent_ctx: Option>, } -impl CellManager { - pub(crate) fn new(meta: &mut VirtualCells, advice_columns: &[Column]) -> Self { - // Setup the columns and query the cells - let width = advice_columns.len(); - let height = 32; - let mut cells = Vec::with_capacity(height * width); - let mut columns = Vec::with_capacity(width); - for c in 0..width { - for r in 0..height { - cells.push(Cell::new(meta, advice_columns[c], r)); +#[derive(Default, Clone, Debug)] +struct CmContext { + parent: Box>>, + columns: Vec>, +} + +impl CellManager { + pub(crate) fn new( + meta: &mut ConstraintSystem, + configs: Vec<(C, usize, u8, bool)>, + offset: usize, + max_height: usize, + ) -> Self { + let configs = configs + .into_iter() + .map(|c| c.into()) + .collect::>>(); + + let mut width = 0; + let mut columns = Vec::new(); + for config in configs.iter() { + let cols = config.init_columns(meta); + for col in cols.iter() { + let mut cells = Vec::new(); + for r in 0..max_height { + query_expression(meta, |meta| { + cells.push(Cell::new(meta, *col, offset + r)); + }); + } + columns.push(CellColumn { + index: columns.len(), + cell_type: config.cell_type, + height: 0, + expr: cells[0].expr(), + cells, + }); + width += 1; } - columns.push(CellColumn { - index: c, - cell_type: CellType::Storage, - height: 0, - expr: cells[c * height].expr(), - }); } - Self { - width, - height, - cells, + configs, columns, - height_limit: height, + height: max_height, + width, + height_limit: max_height, + branch_ctxs: HashMap::new(), + parent_ctx: None, } } - pub(crate) fn query_cells(&mut self, cell_type: CellType, count: usize) -> Vec> { + pub(crate) fn cur_to_parent(&mut self) { + let new_parent = match self.parent_ctx.clone() { + // if parent context exists, meaning we are deep in a callstack + // we set it as the parent of new parent + Some(ctx) => CmContext { + parent: Box::new(Some(ctx.clone())), + columns: self.columns.clone(), + }, + // otherwise, this is the fist level of callstack + // the parent of new parent is None + None => CmContext { + parent: Box::new(None), + columns: self.columns.clone(), + }, + }; + self.parent_ctx = Some(new_parent); + self.reset(self.height_limit); + } + + pub(crate) fn cur_to_branch(&mut self, name: &str) { + let new_branch = match self.parent_ctx.clone() { + // if parent context exists, meaning we are deep in a callstack + // we set it as the parent of new branch + Some(ctx) => CmContext { + parent: Box::new(Some(ctx.clone())), + columns: self.columns.clone(), + }, + // otherwise, this is the fist level of callstack + // the parent of new branch is None + None => CmContext { + parent: Box::new(None), + columns: self.columns.clone(), + }, + }; + self.branch_ctxs.insert(name.to_string(), new_branch); + self.reset(self.height_limit); + } + + pub(crate) fn recover_max_branch(&mut self) { + let mut new_cols = self.columns.clone(); + let parent = self + .parent_ctx + .clone() + .expect("Retruning context needs parent"); + self.branch_ctxs.iter().for_each(|(_name, ctx)| { + for c in 0..self.width { + new_cols[c] = max(&new_cols[c], &ctx.columns[c]).clone(); + new_cols[c] = max(&new_cols[c], &parent.columns[c]).clone(); + } + }); + self.columns = new_cols; + self.branch_ctxs.clear(); + self.parent_ctx = self + .parent_ctx + .clone() + .map(|ctx| ctx.parent.deref().clone()) + .unwrap(); + } + + pub(crate) fn recover_parent(&mut self) { + assert!(self.parent_ctx.is_some(), "No parent context to recover"); + self.columns = self.parent_ctx.clone().unwrap().columns.clone(); + self.parent_ctx + .clone() + .map(|ctx| self.parent_ctx = ctx.parent.deref().clone()) + .unwrap(); + self.branch_ctxs.clear(); + } + + pub(crate) fn query_cells(&mut self, cell_type: C, count: usize) -> Vec> { let mut cells = Vec::with_capacity(count); while cells.len() < count { let column_idx = self.next_column(cell_type); let column = &mut self.columns[column_idx]; - cells.push(self.cells[column_idx * self.height + column.height].clone()); + cells.push(column.cells[column.height].clone()); column.height += 1; } cells } - pub(crate) fn query_cell(&mut self, cell_type: CellType) -> Cell { + pub(crate) fn query_cell(&mut self, cell_type: C) -> Cell { self.query_cells(cell_type, 1)[0].clone() } @@ -153,7 +395,7 @@ impl CellManager { } } - fn next_column(&self, cell_type: CellType) -> usize { + fn next_column(&self, cell_type: C) -> usize { let mut best_index: Option = None; let mut best_height = self.height; for column in self.columns.iter() { @@ -180,7 +422,7 @@ impl CellManager { } /// Returns a map of CellType -> (width, height, num_cells) - pub(crate) fn get_stats(&self) -> BTreeMap { + pub(crate) fn get_stats(&self) -> BTreeMap { let mut data = BTreeMap::new(); for column in self.columns.iter() { let (mut count, mut height, mut num_cells) = @@ -193,7 +435,17 @@ impl CellManager { data } - pub(crate) fn columns(&self) -> &[CellColumn] { + pub(crate) fn columns(&self) -> &[CellColumn] { &self.columns } + + pub(crate) fn get_typed_columns(&self, cell_type: C) -> Vec> { + let mut columns = Vec::new(); + for column in self.columns.iter() { + if column.cell_type == cell_type { + columns.push(column.clone()); + } + } + columns + } } diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index 5594f78214..c093d5708a 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -1,57 +1,148 @@ //! Circuit utilities -use std::ops::{Add, Mul}; +use std::{ + collections::HashMap, + ops::{Add, Mul}, + vec, +}; -use crate::{evm_circuit::util::rlc, util::Expr}; +use crate::{evm_circuit::util::rlc, table::LookupTable, util::Expr}; use eth_types::Field; use gadgets::util::{and, sum, Scalar}; -use halo2_proofs::plonk::{ConstraintSystem, Expression}; +use halo2_proofs::{ + plonk::{ConstraintSystem, Expression}, + poly::Rotation, +}; use itertools::Itertools; -use super::cell_manager::{Cell, CellManager, CellType}; +use super::{ + cached_region::StoredExpression, + cell_manager::{Cell, CellManager, CellType}, +}; -/// Lookup data -#[derive(Clone)] -pub struct LookupData { +fn get_condition_expr(conditions: &Vec>) -> Expression { + if conditions.is_empty() { + 1.expr() + } else { + and::expr(conditions) + } +} + +/// Data for dynamic lookup +#[derive(Clone, Debug)] +pub struct DynamicData { /// Desciption pub description: &'static str, - /// Lookup tag - pub tag: String, /// Condition under which the lookup needs to be done pub condition: Expression, /// The values to lookup pub values: Vec>, + /// region + pub region_id: usize, + /// If is fixed, use static table for lookup + pub is_fixed: bool, + /// Use rlc + pub is_combine: bool, + /// Lower the degree by spliting expression + pub is_split: bool, } /// Constraint builder #[derive(Clone)] -pub struct ConstraintBuilder { +pub struct ConstraintBuilder { + /// Constraints to be returned to meta constraints: Vec<(&'static str, Expression)>, + /// Max degree of constraints max_degree: usize, + /// conditions for constraints conditions: Vec>, - /// The lookups - pub lookups: Vec>, - /// The lookup tables - pub lookup_tables: Vec>, + /// The lookups generated during synthesis + /// assembles runtime access to RAM + pub dynamic_lookups: HashMap>>, + /// The tables written during synthesis + /// write to RAM + pub dynamic_tables: HashMap>>, + /// All stored expressions + pub stored_expressions: HashMap>>, /// CellManager - pub cell_manager: Option>, + pub cell_manager: Option>, + /// Disable macro-generated description for constraints & lookups + /// for graph display + pub disable_description: bool, + /// region id + pub region_id: usize, + /// lookup input challenge + pub lookup_challenge: Option>, + /// state contect + pub state_context: Vec>, + /// state constraints start + pub state_constraints_start: usize, } -impl ConstraintBuilder { - pub(crate) fn new(max_degree: usize, cell_manager: Option>) -> Self { +impl ConstraintBuilder { + pub(crate) fn new( + max_degree: usize, + cell_manager: Option>, + lookup_challenge: Option>, + ) -> Self { ConstraintBuilder { constraints: Vec::new(), max_degree, conditions: Vec::new(), - lookups: Vec::new(), - lookup_tables: Vec::new(), + dynamic_lookups: HashMap::new(), + dynamic_tables: HashMap::new(), cell_manager, + disable_description: false, + stored_expressions: HashMap::new(), + region_id: 0, + lookup_challenge, + state_context: Vec::new(), + state_constraints_start: 0, } } - pub(crate) fn set_cell_manager(&mut self, cell_manager: CellManager) { + pub(crate) fn set_cell_manager(&mut self, cell_manager: CellManager) { self.cell_manager = Some(cell_manager); } + pub(crate) fn set_max_degree(&mut self, max_degree: usize) { + self.max_degree = max_degree; + } + + pub(crate) fn push_region(&mut self, region_id: usize) { + assert!(region_id != 0); + self.region_id = region_id; + self.state_context = self.conditions.clone(); + self.conditions.clear(); + self.state_constraints_start = self.constraints.len(); + } + + pub(crate) fn pop_region(&mut self) { + let condition = get_condition_expr(&self.state_context); + for idx in self.state_constraints_start..self.constraints.len() { + self.constraints[idx].1 = condition.expr() * self.constraints[idx].1.clone(); + } + for (_, values) in self.dynamic_lookups.iter_mut() { + for value in values { + if value.region_id == self.region_id { + value.condition = condition.expr() * value.condition.expr(); + } + } + } + for (_key, values) in self.dynamic_tables.iter_mut() { + for value in values { + if value.region_id == self.region_id { + value.condition = condition.expr() * value.condition.expr(); + } + } + } + self.conditions = self.state_context.clone(); + self.region_id = 0; + } + + pub(crate) fn set_disable_description(&mut self, disable_description: bool) { + self.disable_description = disable_description; + } + pub(crate) fn require_zero(&mut self, name: &'static str, constraint: Expression) { self.add_constraint(name, constraint); } @@ -108,10 +199,14 @@ impl ConstraintBuilder { } pub(crate) fn add_constraint(&mut self, name: &'static str, constraint: Expression) { + if self.max_degree == 0 { + return; + } let constraint = match self.get_condition() { Some(condition) => condition * constraint, None => constraint, }; + let constraint = self.split_expression(name, constraint); self.validate_degree(constraint.degree(), name); self.constraints.push((name, constraint)); } @@ -119,47 +214,44 @@ impl ConstraintBuilder { // Query pub(crate) fn query_bool(&mut self) -> Cell { - let cell = self.query_cell(); + let cell = self.query_default(); self.require_boolean("Constrain cell to be a bool", cell.expr()); cell } - pub(crate) fn query_byte(&mut self) -> Cell { - // TODO(Brecht): fix - self.query_cell_with_type(CellType::Storage) - } - - pub(crate) fn query_bytes(&mut self) -> [Cell; N] { - self.query_bytes_dyn(N).try_into().unwrap() - } - - pub(crate) fn query_bytes_dyn(&mut self, count: usize) -> Vec> { - self.query_cells_dyn(CellType::Storage, count) - } - - pub(crate) fn query_cell(&mut self) -> Cell { - self.query_cell_with_type(CellType::Storage) + pub(crate) fn query_default(&mut self) -> Cell { + self.query_cells_dyn(C::default(), 1) + .get(0) + .expect("No cell found") + .clone() } - pub(crate) fn query_cells(&mut self) -> [Cell; N] { - self.query_cells_dyn(CellType::Storage, N) - .try_into() - .unwrap() + pub(crate) fn query_one(&mut self, cell_type: C) -> Cell { + self.query_cells_dyn(cell_type, 1).first().unwrap().clone() } - pub(crate) fn query_cell_with_type(&mut self, cell_type: CellType) -> Cell { - self.query_cells_dyn(cell_type, 1).first().unwrap().clone() + pub(crate) fn query_bytes(&mut self) -> [Cell; N] { + self.query_cells_dyn( + C::byte_type().expect("No byte type for this CellManager"), + N, + ) + .try_into() + .unwrap() } - fn query_cells_dyn(&mut self, cell_type: CellType, count: usize) -> Vec> { + pub(crate) fn query_cells_dyn(&mut self, cell_type: C, count: usize) -> Vec> { self.cell_manager .as_mut() - .unwrap() + .expect("Cell manager not set") .query_cells(cell_type, count) } + pub(crate) fn query_cell_with_type(&mut self, cell_type: C) -> Cell { + self.query_cells_dyn(cell_type, 1).first().unwrap().clone() + } + pub(crate) fn validate_degree(&self, degree: usize, name: &'static str) { - if self.max_degree > 0 { + if self.max_degree > 0 && self.region_id != 0 { debug_assert!( degree <= self.max_degree, "Expression {} degree too high: {} > {}", @@ -170,40 +262,101 @@ impl ConstraintBuilder { } } - pub(crate) fn generate_constraints(&self) -> Vec<(&'static str, Expression)> { + pub(crate) fn build_constraints(&self) -> Vec<(&'static str, Expression)> { self.constraints.clone() } - pub(crate) fn generate_lookups>( + pub(crate) fn build_celltype_lookups( &self, meta: &mut ConstraintSystem, - lookup_names: &[S], + cell_managers: Vec>, + tables: Vec<(C, &dyn LookupTable)>, ) { + for cm in cell_managers { + for (cell_type, table) in &tables { + for col in cm.get_typed_columns(*cell_type) { + let name = format!("{:?}", cell_type); + meta.lookup_any(Box::leak(name.into_boxed_str()), |meta| { + vec![( + col.expr, + rlc::expr( + &table.table_exprs(meta), + self.lookup_challenge.clone().unwrap(), + ), + )] + }); + } + } + } + } + + pub(crate) fn build_dynamic_lookups( + &mut self, + meta: &mut ConstraintSystem, + lookup_names: &[C], + fixed_table: Vec<(C, &dyn LookupTable)>, + ) { + let lookups = self.dynamic_lookups.clone(); for lookup_name in lookup_names.iter() { - let lookups = self - .lookups - .iter() - .cloned() - .filter(|lookup| lookup.tag == lookup_name.as_ref()) - .collect::>(); - for lookup in lookups.iter() { - meta.lookup_any(lookup.description, |_meta| { - let table = self.get_lookup_table_values(lookup_name); - let mut values: Vec<_> = lookup - .values - .iter() - .map(|value| lookup.condition.expr() * value.expr()) - .collect(); - assert!(table.len() >= values.len()); - while values.len() < table.len() { - values.push(0.expr()); - } - table - .iter() - .zip(values.iter()) - .map(|(table, value)| (value.expr(), table.expr())) - .collect() - }); + if let Some(lookups) = lookups.get(lookup_name) { + for lookup in lookups.iter() { + meta.lookup_any(lookup.description, |meta| { + // Fixed lookup is a direct lookup into the pre-difined fixed tables + // i.e. cond * (v1, v2, v3) => (t1, t2, t3) + // equivalent to the vanilla lookup operation of Halo2. + // Dynamic lookup applies condition to the advice values stored at + // configuration time i.e. cond * (v1, v2, v3) => + // cond * (t1, t2, t3) the dynamic lookup in a ifx! + // branch would become trivial 0 => 0 + // when the elsex! branch evaluates to true + + let table = if lookup.is_fixed { + let table_cols = fixed_table + .iter() + .find(|(name, _)| name == lookup_name) + .unwrap() + .1 + .columns(); + table_cols + .iter() + .map(|col| meta.query_any(*col, Rotation(0))) + .collect() + } else { + let dyn_table = self.get_dynamic_table_values(*lookup_name); + assert_eq!(dyn_table.len(), lookup.values.len()); + dyn_table + }; + + let mut values: Vec<_> = lookup + .values + .iter() + .map(|value| value.expr() * lookup.condition.clone()) + .collect(); + // allign the length of values and table + assert!(table.len() >= values.len()); + while values.len() < table.len() { + values.push(0.expr()); + } + + // Perform rlc if specified + // i.e. (v1*r + v2*r^2 + v3*r^3) => (t1*r + t2*r^2 + t3*r^3) + // lastly is_split had been fulfilled at insertion time + + let ret = if lookup.is_combine { + vec![( + rlc::expr(&values, self.lookup_challenge.clone().unwrap()), + rlc::expr(&table, self.lookup_challenge.clone().unwrap()), + )] + } else { + values + .iter() + .zip(table.iter()) + .map(|(v, t)| (v.expr(), t.expr())) + .collect() + }; + ret + }); + } } } } @@ -220,95 +373,151 @@ impl ConstraintBuilder { self.get_condition().unwrap_or_else(|| 1.expr()) } - pub(crate) fn lookup_table>( + pub(crate) fn store_dynamic_table( &mut self, description: &'static str, - tag: S, + tag: C, values: Vec>, + is_combine: bool, + is_split: bool, ) { let condition = self.get_condition_expr(); - self.lookup_tables.push(LookupData { + let mut values = if is_combine { + vec![rlc::expr(&values, self.lookup_challenge.clone().unwrap())] + } else { + values.clone() + }; + if is_split { + values.iter_mut().for_each(|v| { + *v = self.split_expression( + Box::leak(format!("compression value - {:?}", tag).into_boxed_str()), + v.clone(), + ) + }); + } + let lookup = DynamicData { description, - tag: tag.as_ref().to_owned(), condition, values, - }); + region_id: self.region_id, + // cannot be is_fixed + is_fixed: false, + is_combine: false, + is_split: false, + }; + if let Some(table_data) = self.dynamic_tables.get_mut(&tag) { + table_data.push(lookup); + } else { + self.dynamic_tables.insert(tag, vec![lookup]); + } } - pub(crate) fn lookup>( + pub(crate) fn add_dynamic_lookup( &mut self, description: &'static str, - tag: S, + tag: C, values: Vec>, + is_fixed: bool, + is_combine: bool, + is_split: bool, ) { let condition = self.get_condition_expr(); - self.lookups.push(LookupData { + let mut values = if is_combine { + vec![rlc::expr(&values, self.lookup_challenge.clone().unwrap())] + } else { + values.clone() + }; + if is_split { + values.iter_mut().for_each(|v| { + *v = self.split_expression( + Box::leak(format!("compression value - {:?}", tag).into_boxed_str()), + v.clone(), + ) + }); + } + let lookup = DynamicData { description, - tag: tag.as_ref().to_owned(), condition, values, - }); + region_id: self.region_id, + is_fixed, + is_combine, + is_split, + }; + if let Some(lookup_data) = self.dynamic_lookups.get_mut(&tag) { + lookup_data.push(lookup); + } else { + self.dynamic_lookups.insert(tag, vec![lookup]); + } } - pub(crate) fn get_lookups>(&self, tags: &[S]) -> Vec> { - self.lookups + pub(crate) fn add_celltype_lookup( + &mut self, + description: &str, + cell_type: C, + values: Vec>, + ) { + let condition = self.get_condition_expr(); + let values = values .iter() - .cloned() - .filter(|lookup| tags.iter().any(|tag| lookup.tag == tag.as_ref())) - .collect::>() + .map(|value| condition.expr() * value.expr()) + .collect_vec(); + let compressed_expr = self.split_expression( + "compression", + rlc::expr(&values, self.lookup_challenge.clone().unwrap().expr()), + ); + self.store_expression(description, compressed_expr, cell_type); } - pub(crate) fn consume_lookups>(&mut self, tags: &[S]) -> Vec> { - let lookups = self.get_lookups(tags); - self.lookups - .retain(|lookup| tags.iter().any(|tag| lookup.tag != tag.as_ref())); - lookups + pub(crate) fn get_stored_expressions(&self, region_id: usize) -> Vec> { + self.stored_expressions + .get(®ion_id) + .cloned() + .unwrap_or_default() } - pub(crate) fn get_lookup_table>( - &self, - tag: S, - ) -> (Expression, Vec>) { - let lookups = self - .lookup_tables - .iter() - .filter(|lookup| lookup.tag == tag.as_ref()) - .collect::>(); - + pub(crate) fn get_dynamic_table(&self, tag: C) -> (Expression, Vec>) { + let table_values = self + .dynamic_tables + .get(&tag) + .unwrap_or_else(|| panic!("Dynamic table {:?} not found", tag)); merge_values_unsafe( - lookups + table_values .iter() - .map(|lookup| (lookup.condition.clone(), lookup.values.clone())) + .map(|table| { + ( + table.condition.clone(), + table.values.clone(), + table.is_split, + ) + }) .collect::>(), ) } - pub(crate) fn get_lookup_table_values>(&self, tag: S) -> Vec> { - let lookup_table = self.get_lookup_table(tag); - // Combine with the merged selector as well - lookup_table + pub(crate) fn get_dynamic_table_values(&self, tag: C) -> Vec> { + let condition_and_values = self.get_dynamic_table(tag); + condition_and_values .1 .iter() - .map(|v| v.expr() * lookup_table.0.expr()) + .map(|value| value.expr() * condition_and_values.0.expr()) .collect::>() } - pub(crate) fn generate_lookup_table_checks>(&mut self, tag: S) { - let lookups = self - .lookup_tables - .iter() - .filter(|lookup| lookup.tag == tag.as_ref()) + pub(crate) fn generate_lookup_table_checks(&mut self, tag: C) { + let table_values = self + .dynamic_tables + .get(&tag) + .unwrap_or_else(|| panic!("Dynamic table {:?} not found", tag)) + .clone(); + let selectors = table_values + .into_iter() + .map(|value| { + let sel = value.condition.expr(); + self.require_boolean("lookup table condition needs to be boolean", sel.clone()); + sel + }) .collect::>(); - let selectors = lookups - .iter() - .map(|lookup| lookup.condition.expr()) - .collect::>(); - for selector in selectors.iter() { - self.require_boolean( - "lookup table condition needs to be boolean", - selector.expr(), - ); - } let selector = sum::expr(&selectors); self.require_boolean( "lookup table conditions sum needs to be boolean", @@ -316,6 +525,94 @@ impl ConstraintBuilder { ); } + pub(crate) fn store_expression( + &mut self, + name: &str, + expr: Expression, + cell_type: C, + ) -> Expression { + // Check if we already stored the expression somewhere + let stored_expression = self.find_stored_expression(&expr, cell_type); + match stored_expression { + Some(stored_expression) => stored_expression.cell.expr(), + None => { + // Require the stored value to equal the value of the expression + let cell = self.query_one(cell_type); + let name = format!("{} (stored expression)", name); + self.constraints.push(( + Box::leak(name.clone().into_boxed_str()), + cell.expr() - expr.clone(), + )); + self.stored_expressions + .entry(self.region_id) + .or_insert_with(Vec::new) + .push(StoredExpression { + name, + cell: cell.clone(), + cell_type, + expr_id: expr.identifier(), + expr, + }); + cell.expr() + } + } + } + + pub(crate) fn find_stored_expression( + &self, + expr: &Expression, + cell_type: C, + ) -> Option<&StoredExpression> { + let expr_id = expr.identifier(); + if let Some(stored_expressions) = self.stored_expressions.get(&self.region_id) { + stored_expressions + .iter() + .find(|&e| e.cell_type == cell_type && e.expr_id == expr_id) + } else { + None + } + } + + fn split_expression(&mut self, name: &'static str, expr: Expression) -> Expression { + if expr.degree() > self.max_degree && self.region_id != 0 { + match expr { + Expression::Negated(poly) => { + Expression::Negated(Box::new(self.split_expression(name, *poly))) + } + Expression::Scaled(poly, v) => { + Expression::Scaled(Box::new(self.split_expression(name, *poly)), v) + } + Expression::Sum(a, b) => { + let a = self.split_expression(name, *a); + let b = self.split_expression(name, *b); + a + b + } + Expression::Product(a, b) => { + let (mut a, mut b) = (*a, *b); + while a.degree() + b.degree() > self.max_degree { + let mut split = |expr: Expression| { + if expr.degree() > self.max_degree { + self.split_expression(name, expr) + } else { + let cell_type = C::storage_for_expr(&expr); + self.store_expression(name, expr, cell_type) + } + }; + if a.degree() >= b.degree() { + a = split(a); + } else { + b = split(b); + } + } + a * b + } + _ => expr.clone(), + } + } else { + expr.clone() + } + } + pub(crate) fn print_stats(&self) { let mut expressions = self.constraints.clone(); expressions.sort_by(|a, b| a.1.degree().cmp(&b.1.degree())); @@ -325,24 +622,21 @@ impl ConstraintBuilder { } } -pub(crate) fn merge_lookups( - cb: &mut ConstraintBuilder, - lookups: Vec>, -) -> (Expression, Vec>) { - merge_values( - cb, - lookups - .iter() - .map(|lookup| (lookup.condition.clone(), lookup.values.clone())) - .collect::>(), - ) -} - -pub(crate) fn merge_values( - cb: &mut ConstraintBuilder, - values: Vec<(Expression, Vec>)>, +pub(crate) fn merge_lookups( + cb: &mut ConstraintBuilder, + lookups: Vec>, ) -> (Expression, Vec>) { - let selector = sum::expr(values.iter().map(|(condition, _)| condition.expr())); + let values = lookups + .iter() + .map(|lookup| { + ( + lookup.condition.clone(), + lookup.values.clone(), + lookup.is_split, + ) + }) + .collect::>(); + let selector = sum::expr(values.iter().map(|(condition, _, _)| condition.expr())); crate::circuit!([meta, cb], { require!(selector => bool); }); @@ -350,19 +644,27 @@ pub(crate) fn merge_values( } pub(crate) fn merge_values_unsafe( - values: Vec<(Expression, Vec>)>, + values: Vec<(Expression, Vec>, bool)>, ) -> (Expression, Vec>) { if values.is_empty() { return (0.expr(), Vec::new()); } - let selector = sum::expr(values.iter().map(|(condition, _)| condition.expr())); + let selector = sum::expr(values.iter().map(|(condition, _, _)| condition.expr())); // Merge - let max_length = values.iter().map(|(_, values)| values.len()).max().unwrap(); + let max_length = values + .iter() + .map(|(_, values, _)| values.len()) + .max() + .unwrap(); let mut merged_values = vec![0.expr(); max_length]; let default_value = 0.expr(); for (idx, value) in merged_values.iter_mut().enumerate() { - *value = sum::expr(values.iter().map(|(condition, values)| { - condition.expr() * values.get(idx).unwrap_or_else(|| &default_value).expr() + *value = sum::expr(values.iter().map(|(condition, values, _is_split)| { + // Condition had been included if is_split + values.get(idx).unwrap_or(&default_value).expr() * { + condition.expr() + // if *is_split {1.expr() } else {condition.expr()} + } })); } (selector, merged_values) @@ -388,7 +690,7 @@ impl ToVec for [T] { impl ToVec for &[T] { fn to_vec(&self) -> Vec { - self.clone().to_owned() + <&[T]>::clone(self).to_owned() } } @@ -505,12 +807,8 @@ pub trait ExprResult { } impl ExprResult for () { - fn add(&self, _other: &Self) -> Self { - () - } - fn mul(&self, _other: &Expression) -> Self { - () - } + fn add(&self, _other: &Self) -> Self {} + fn mul(&self, _other: &Expression) -> Self {} } impl ExprResult for Vec> { @@ -588,12 +886,21 @@ impl_expr_result!( pub trait RLCable { /// Returns the RLC of itself fn rlc(&self, r: &Expression) -> Expression; + /// Returns the RLC of the reverse of itself + fn rlc_rev(&self, r: &Expression) -> Expression; } impl + ?Sized> RLCable for E { fn rlc(&self, r: &Expression) -> Expression { rlc::expr(&self.to_expr_vec(), r.expr()) } + + fn rlc_rev(&self, r: &Expression) -> Expression { + rlc::expr( + &self.to_expr_vec().iter().rev().cloned().collect_vec(), + r.expr(), + ) + } } /// Trait around RLC @@ -608,6 +915,18 @@ impl RLCChainable for (Expression, Expression) { } } +/// Trait around RLC +pub trait RLCChainable2 { + /// Returns the RLC of itself with a starting rlc/multiplier + fn rlc_chain2(&self, other: (Expression, Expression)) -> Expression; +} + +impl RLCChainable2 for Expression { + fn rlc_chain2(&self, other: (Expression, Expression)) -> Expression { + self.expr() * other.1.expr() + other.0.expr() + } +} + /// Trait around RLC pub trait RLCableValue { /// Returns the RLC of itself @@ -637,13 +956,12 @@ impl, I: IntoIterator> RLCChainableValue {{ - ConstraintBuilder::::new(0, None) + use $crate::circuit_tools::cell_manager::DefaultCellType; + ConstraintBuilder::::new(0, None, None) }}; } @@ -728,14 +1047,12 @@ macro_rules! _unreachablex { #[macro_export] macro_rules! _require { ($cb:expr, $lhs:expr => bool) => {{ - $cb.require_boolean( - concat_with_preamble!( - stringify!($lhs), - " => ", - "bool", - ), - $lhs.expr(), + let description = concat_with_preamble!( + stringify!($lhs), + " => ", + "bool", ); + $cb.require_boolean(description, $lhs.expr()); }}; ($cb:expr, $lhs:expr => $rhs:expr) => {{ @@ -766,77 +1083,182 @@ macro_rules! _require { // Lookup using a tuple ($cb:expr, ($($v:expr),+) => @$tag:expr) => {{ - $cb.lookup( - concat_with_preamble!( - "(", - $( - stringify!($v), - ", ", - )* - ") => @", - stringify!($tag), - ), - $tag.to_string(), + let description = concat_with_preamble!( + "(", + $( + stringify!($v), + ", ", + )* + ") => @", + stringify!($tag), + ); + $cb.add_celltype_lookup( + description, + $tag, vec![$($v.expr(),)*], ); }}; ($cb:expr, $descr:expr, ($($v:expr),+) => @$tag:expr) => {{ - $cb.lookup( + $cb.add_celltype_lookup( Box::leak($descr.into_boxed_str()), - $tag.to_string(), + $tag, vec![$($v.expr(),)*], ); }}; // Lookup using an array ($cb:expr, $values:expr => @$tag:expr) => {{ - $cb.lookup( - concat_with_preamble!( - stringify!($values), - " => @", - stringify!($tag), - ), - $tag.to_string(), + let description = concat_with_preamble!( + stringify!($values), + " => @", + stringify!($tag), + ); + $cb.add_celltype_lookup( + description, + $tag, $values.clone(), ); }}; ($cb:expr, $descr:expr, $values:expr => @$tag:expr) => {{ - $cb.lookup( + $cb.add_celltype_lookup( Box::leak($descr.to_string().into_boxed_str()), - $tag.to_string(), + $tag, $values.clone(), ); }}; - // Put values in a lookup table using a tuple - ($cb:expr, @$tag:expr => ($($v:expr),+)) => {{ - $cb.lookup_table( - concat_with_preamble!( - "@", - stringify!($tag), - " => (", + // Lookup using a tuple + ($cb:expr, ($($v:expr),+) => @$tag:expr, $is_fixed:expr, $is_combine:expr, $is_split:expr) => {{ + let description = concat_with_preamble!( + "(", $( stringify!($v), ", ", )* - ")", - ), - $tag.to_string(), + ") => @", + stringify!($tag), + ); + $cb.add_dynamic_lookup( + description, + $tag, + vec![$($v.expr(),)*], + $is_fixed, + $is_combine, + $is_split, + ); + }}; + ($cb:expr, $descr:expr, ($($v:expr),+) => @$tag:expr, $is_fixed:expr, $is_combine:expr, $is_split:expr) => {{ + $cb.add_dynamic_lookup( + Box::leak($descr.into_boxed_str()), + $tag, + vec![$($v.expr(),)*], + $is_fixed, + $is_combine, + $is_split, + ); + }}; + + + // Lookup using an array + ($cb:expr, $values:expr => @$tag:expr, $is_fixed:expr, $is_combine:expr, $is_split:expr) => {{ + let description = concat_with_preamble!( + stringify!($values), + " => @", + stringify!($tag), + ); + $cb.add_dynamic_lookup( + description, + $tag, + $values.clone(), + $is_fixed, + $is_combine, + $is_split, + ); + }}; + ($cb:expr, $descr:expr, $values:expr => @$tag:expr, $is_fixed:expr, $is_combine:expr, $is_split:expr) => {{ + $cb.add_dynamic_lookup( + Box::leak($descr.into_boxed_str()), + $tag, + $values.clone(), + $is_fixed, + $is_combine, + $is_split, + ); + }}; + + // Put values in a lookup table using a tuple + ($cb:expr, @$tag:expr => ($($v:expr),+)) => {{ + let description = concat_with_preamble!( + "@", + stringify!($tag), + " => (", + $( + stringify!($v), + ", ", + )* + ")", + ); + $cb.store_dynamic_table( + description, + $tag, vec![$($v.expr(),)*], + false, + false, ); }}; // Put values in a lookup table using an array ($cb:expr, @$tag:expr => $values:expr) => {{ - $cb.lookup_table( - concat_with_preamble!( - "@", - stringify!($tag), - " => (", - stringify!($values), - ")", - ), - $tag.to_string(), + let description = concat_with_preamble!( + "@", + stringify!($tag), + " => (", + stringify!($values), + ")", + ); + $cb.store_dynamic_table( + description, + $tag, $values, + false, + false, + ); + }}; + + // Put values in a lookup table using a tuple + ($cb:expr, @$tag:expr => ($($v:expr),+), $is_combine:expr, $is_split:expr) => {{ + let description = concat_with_preamble!( + "@", + stringify!($tag), + " => (", + $( + stringify!($v), + ", ", + )* + ")", + ); + $cb.store_dynamic_table( + description, + $tag, + vec![$($v.expr(),)*], + $is_combine, + $is_split, + ); + }}; + // Put values in a lookup table using an array + ($cb:expr, @$tag:expr => $values:expr, $is_combine:expr, $is_split:expr) => {{ + let description = concat_with_preamble!( + "@", + stringify!($tag), + " => (", + stringify!($values), + ")", + ); + $cb.store_dynamic_table( + description, + $tag, + $values, + $is_combine, + $is_split, ); }}; } @@ -942,6 +1364,12 @@ macro_rules! assign { let value: F = $value; $region.assign_advice(|| description, $column, $offset, || Value::known(value)) }}; + ($region:expr, ($column:expr, $offset:expr) => $annotation:expr, $value:expr) => {{ + use halo2_proofs::circuit::Value; + let value: F = $value; + $region.name_column(|| $annotation, $column); + $region.assign_advice(|| "", $column, $offset, || Value::known(value)) + }}; // Cell ($region:expr, $cell:expr, $offset:expr => $value:expr) => {{ use halo2_proofs::circuit::Value; @@ -955,6 +1383,16 @@ macro_rules! assign { || Value::known(value), ) }}; + ($region:expr, $cell:expr, $offset:expr => $annotation:expr, $value:expr) => {{ + use halo2_proofs::circuit::Value; + let value: F = $value; + $region.assign_advice( + || $annotation, + $cell.column(), + $offset + $cell.rotation(), + || Value::known(value), + ) + }}; } /// assign fixed @@ -969,7 +1407,7 @@ macro_rules! assignf { } /// Circuit builder macros -/// Nested macro's can't do repetition (https://github.com/rust-lang/rust/issues/35853) +/// Nested macro's can't do repetition /// so we expose a couple of permutations here manually. #[macro_export] macro_rules! circuit { @@ -1063,6 +1501,30 @@ macro_rules! circuit { _require!($cb, $descr, $values => @$tag); }}; + (($a:expr) => @$tag:expr, $is_fixed:expr, $is_combine:expr, $is_split:expr) => {{ + _require!($cb, ($a) => @$tag, $is_fixed, $is_combine, $is_split); + }}; + + (($a:expr, $b:expr) => @$tag:expr, $is_fixed:expr, $is_combine:expr, $is_split:expr) => {{ + _require!($cb, ($a, $b) => @$tag, $is_fixed, $is_combine, $is_split); + }}; + + (($a:expr, $b:expr, $c:expr) => @$tag:expr, $is_fixed:expr, $is_combine:expr, $is_split:expr) => {{ + _require!($cb, ($a, $b, $c) => @$tag, $is_fixed, $is_combine, $is_split); + }}; + + (($a:expr, $b:expr, $c:expr, $d:expr) => @$tag:expr, $is_fixed:expr, $is_combine:expr, $is_split:expr) => {{ + _require!($cb, ($a, $b, $c, $d) => @$tag, $is_fixed, $is_combine, $is_split); + }}; + + ($values:expr => @$tag:expr, $is_fixed:expr, $is_combine:expr, $is_split:expr) => {{ + _require!($cb, $values => @$tag, $is_fixed, $is_combine, $is_split); + }}; + + ($descr:expr, $values:expr => @$tag:expr, $is_fixed:expr, $is_combine:expr, $is_split:expr) => {{ + _require!($cb, $descr, $values => @$tag, $is_fixed, $is_combine, $is_split); + }}; + (@$tag:expr => ($a:expr, $b:expr, $c:expr)) => {{ _require!($cb, @$tag => ($a, $b, $c)); }}; diff --git a/zkevm-circuits/src/circuit_tools/gadgets.rs b/zkevm-circuits/src/circuit_tools/gadgets.rs index b1e21c5c9c..df1c1a703c 100644 --- a/zkevm-circuits/src/circuit_tools/gadgets.rs +++ b/zkevm-circuits/src/circuit_tools/gadgets.rs @@ -1,26 +1,30 @@ //! Circuit gadgets use eth_types::Field; use gadgets::util::Expr; -use halo2_proofs::{ - circuit::Region, - plonk::{Error, Expression}, -}; +use halo2_proofs::plonk::{Error, Expression}; use crate::evm_circuit::util::{from_bytes, pow_of_two}; -use super::{cell_manager::Cell, constraint_builder::ConstraintBuilder}; +use super::{ + cached_region::{CachedRegion, ChallengeSet}, + cell_manager::{Cell, CellType}, + constraint_builder::ConstraintBuilder, +}; /// Returns `1` when `value == 0`, and returns `0` otherwise. #[derive(Clone, Debug, Default)] pub struct IsZeroGadget { - inverse: Cell, + inverse: Option>, is_zero: Option>, } impl IsZeroGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder, value: Expression) -> Self { + pub(crate) fn construct( + cb: &mut ConstraintBuilder, + value: Expression, + ) -> Self { circuit!([meta, cb], { - let inverse = cb.query_cell(); + let inverse = cb.query_default(); let is_zero = 1.expr() - (value.expr() * inverse.expr()); // `value != 0` => check `inverse = a.invert()`: value * (1 - value * inverse) @@ -29,7 +33,7 @@ impl IsZeroGadget { require!(inverse.expr() * is_zero.expr() => 0); Self { - inverse, + inverse: Some(inverse), is_zero: Some(is_zero), } }) @@ -39,18 +43,21 @@ impl IsZeroGadget { self.is_zero.as_ref().unwrap().clone() } - pub(crate) fn assign( + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, value: F, ) -> Result { - let inverse = value.invert().unwrap_or(F::zero()); - self.inverse.assign(region, offset, inverse)?; + let inverse = value.invert().unwrap_or(F::ZERO); + self.inverse + .as_ref() + .unwrap() + .assign(region, offset, inverse)?; Ok(if value.is_zero().into() { - F::one() + F::ONE } else { - F::zero() + F::ZERO }) } } @@ -62,8 +69,8 @@ pub struct IsEqualGadget { } impl IsEqualGadget { - pub(crate) fn construct( - cb: &mut ConstraintBuilder, + pub(crate) fn construct( + cb: &mut ConstraintBuilder, lhs: Expression, rhs: Expression, ) -> Self { @@ -76,9 +83,9 @@ impl IsEqualGadget { self.is_zero.expr() } - pub(crate) fn assign( + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, lhs: F, rhs: F, @@ -97,16 +104,16 @@ impl IsEqualGadget { /// be `1` when `lhs < rhs`. #[derive(Clone, Debug, Default)] pub struct LtGadget { - lt: Cell, // `1` when `lhs < rhs`, `0` otherwise. + lt: Option>, // `1` when `lhs < rhs`, `0` otherwise. diff: Option<[Cell; N_BYTES]>, /* The byte values of `diff`. - * `diff` equals `lhs - rhs` if `lhs >= rhs`, - * `lhs - rhs + range` otherwise. */ + * `diff` equals `lhs - rhs` if `lhs >= rhs`, + * `lhs - rhs + range` otherwise. */ range: F, // The range of the inputs, `256**N_BYTES` } impl LtGadget { - pub(crate) fn construct( - cb: &mut ConstraintBuilder, + pub(crate) fn construct( + cb: &mut ConstraintBuilder, lhs: Expression, rhs: Expression, ) -> Self { @@ -122,19 +129,19 @@ impl LtGadget { ); Self { - lt, + lt: Some(lt), diff: Some(diff), range, } } pub(crate) fn expr(&self) -> Expression { - self.lt.expr() + self.lt.as_ref().unwrap().expr() } - pub(crate) fn assign( + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, lhs: F, rhs: F, @@ -142,16 +149,18 @@ impl LtGadget { // Set `lt` let lt = lhs < rhs; self.lt - .assign(region, offset, if lt { F::one() } else { F::zero() })?; + .as_ref() + .unwrap() + .assign(region, offset, if lt { F::ONE } else { F::ZERO })?; // Set the bytes of diff - let diff = (lhs - rhs) + (if lt { self.range } else { F::zero() }); + let diff = (lhs - rhs) + (if lt { self.range } else { F::ZERO }); let diff_bytes = diff.to_repr(); for (idx, diff) in self.diff.as_ref().unwrap().iter().enumerate() { diff.assign(region, offset, F::from(diff_bytes[idx] as u64))?; } - Ok((if lt { F::one() } else { F::zero() }, diff_bytes.to_vec())) + Ok((if lt { F::ONE } else { F::ZERO }, diff_bytes.to_vec())) } pub(crate) fn diff_bytes(&self) -> Vec> { diff --git a/zkevm-circuits/src/circuit_tools/memory.rs b/zkevm-circuits/src/circuit_tools/memory.rs index 9eeeca1d51..0740e3ded2 100644 --- a/zkevm-circuits/src/circuit_tools/memory.rs +++ b/zkevm-circuits/src/circuit_tools/memory.rs @@ -6,20 +6,17 @@ use halo2_proofs::{ plonk::{Advice, Column, ConstraintSystem, Error, Expression}, poly::Rotation, }; -use std::{ - marker::PhantomData, - ops::{Index, IndexMut}, -}; +use std::ops::{Index, IndexMut}; -use super::constraint_builder::ConstraintBuilder; +use super::{cell_manager::CellType, constraint_builder::ConstraintBuilder}; #[derive(Clone, Debug, Default)] -pub(crate) struct Memory { +pub(crate) struct Memory { columns: Vec>, - banks: Vec>, + banks: Vec>, } -impl Memory { +impl Memory { pub(crate) fn new(columns: Vec>) -> Self { Self { columns, @@ -27,41 +24,41 @@ impl Memory { } } - pub(crate) fn allocate>( - &mut self, - meta: &mut ConstraintSystem, - tag: S, - ) -> &MemoryBank { + pub(crate) fn allocate(&mut self, meta: &mut ConstraintSystem, tag: C) -> &MemoryBank { self.banks .push(MemoryBank::new(meta, self.columns[self.banks.len()], tag)); self.banks.last().unwrap() } - pub(crate) fn get>(&self, tag: S) -> &MemoryBank { + pub(crate) fn get(&self, tag: C) -> &MemoryBank { for bank in self.banks.iter() { - if bank.tag() == tag.as_ref() { + if bank.tag() == tag { return bank; } } unreachable!() } - pub(crate) fn get_mut>(&mut self, tag: S) -> &mut MemoryBank { + pub(crate) fn get_mut(&mut self, tag: C) -> &mut MemoryBank { for bank in self.banks.iter_mut() { - if bank.tag() == tag.as_ref() { + if bank.tag() == tag { return bank; } } unreachable!() } - pub(crate) fn generate_constraints( + pub(crate) fn get_columns(&self) -> Vec> { + self.columns.clone() + } + + pub(crate) fn build_constraints( &self, - cb: &mut ConstraintBuilder, + cb: &mut ConstraintBuilder, is_first_row: Expression, ) { for bank in self.banks.iter() { - bank.generate_constraints(cb, is_first_row.expr()); + bank.build_constraints(cb, is_first_row.expr()); cb.generate_lookup_table_checks(bank.tag()); } } @@ -83,17 +80,17 @@ impl Memory { Ok(()) } - pub(crate) fn tags(&self) -> Vec { + pub(crate) fn tags(&self) -> Vec { self.banks.iter().map(|bank| bank.tag()).collect() } } -impl> Index for Memory { - type Output = MemoryBank; +impl Index for Memory { + type Output = MemoryBank; - fn index(&self, tag: S) -> &Self::Output { + fn index(&self, tag: C) -> &Self::Output { for bank in self.banks.iter() { - if bank.tag() == tag.as_ref() { + if bank.tag() == tag { return bank; } } @@ -101,10 +98,10 @@ impl> Index for Memory { } } -impl> IndexMut for Memory { - fn index_mut(&mut self, tag: S) -> &mut Self::Output { +impl IndexMut for Memory { + fn index_mut(&mut self, tag: C) -> &mut Self::Output { for bank in self.banks.iter_mut() { - if bank.tag() == tag.as_ref() { + if bank.tag() == tag { return bank; } } @@ -113,22 +110,17 @@ impl> IndexMut for Memory { } #[derive(Clone, Debug)] -pub(crate) struct MemoryBank { +pub(crate) struct MemoryBank { column: Column, - tag: String, + tag: C, cur: Expression, next: Expression, store_offsets: Vec, stored_values: Vec>, - _marker: PhantomData, } -impl MemoryBank { - pub(crate) fn new>( - meta: &mut ConstraintSystem, - column: Column, - tag: S, - ) -> Self { +impl MemoryBank { + pub(crate) fn new(meta: &mut ConstraintSystem, column: Column, tag: C) -> Self { let mut cur = 0.expr(); let mut next = 0.expr(); query_expression(meta, |meta| { @@ -137,12 +129,11 @@ impl MemoryBank { }); Self { column, - tag: tag.as_ref().to_owned(), + tag, cur, next, store_offsets: Vec::new(), stored_values: Vec::new(), - _marker: PhantomData, } } @@ -153,7 +144,7 @@ impl MemoryBank { pub(crate) fn load( &self, description: &'static str, - cb: &mut ConstraintBuilder, + cb: &mut ConstraintBuilder, offset: Expression, values: &[Expression], ) { @@ -163,16 +154,24 @@ impl MemoryBank { pub(crate) fn load_with_key( &self, description: &'static str, - cb: &mut ConstraintBuilder, + cb: &mut ConstraintBuilder, key: Expression, values: &[Expression], ) { - cb.lookup(description, self.tag(), self.insert_key(key, values)); + // TODO:(Cecilia) make split expression work + cb.add_dynamic_lookup( + description, + self.tag(), + self.insert_key(key, values), + false, + true, + true, + ); } pub(crate) fn store( &self, - cb: &mut ConstraintBuilder, + cb: &mut ConstraintBuilder, values: &[Expression], ) -> Expression { let key = self.key() + 1.expr(); @@ -182,11 +181,17 @@ impl MemoryBank { pub(crate) fn store_with_key( &self, - cb: &mut ConstraintBuilder, + cb: &mut ConstraintBuilder, key: Expression, values: &[Expression], ) { - cb.lookup_table("memory store", self.tag(), self.insert_key(key, values)); + cb.store_dynamic_table( + "memory store", + self.tag(), + self.insert_key(key, values), + true, + true, + ); } pub(crate) fn witness_store(&mut self, offset: usize, values: &[F]) { @@ -202,17 +207,18 @@ impl MemoryBank { self.store_offsets.clear(); } - pub(crate) fn generate_constraints( + pub(crate) fn build_constraints( &self, - cb: &mut ConstraintBuilder, + cb: &mut ConstraintBuilder, is_first_row: Expression, ) { - let lookup_table = cb.get_lookup_table(self.tag()); + let lookup_table = cb.get_dynamic_table(self.tag()); crate::circuit!([meta, cb], { ifx! {is_first_row => { require!(self.cur.expr() => 0); }} - require!(self.tag(), self.next => self.cur.expr() + lookup_table.0); + let description = format!("{:?}", self.tag()); + require!(description, self.next => self.cur.expr() + lookup_table.0); }); } @@ -228,9 +234,8 @@ impl MemoryBank { let mut store_offsets = self.store_offsets.clone(); store_offsets.push(height); - let mut store_index = 0; let mut offset = 0; - for &stored_offset in store_offsets.iter() { + for (store_index, &stored_offset) in store_offsets.iter().enumerate() { while offset <= stored_offset { region.assign_advice( || "assign memory index".to_string(), @@ -240,18 +245,17 @@ impl MemoryBank { )?; offset += 1; } - store_index += 1; } Ok(()) }, ) } - pub(crate) fn tag(&self) -> String { - self.tag.clone() + pub(crate) fn tag(&self) -> C { + self.tag } - pub(crate) fn insert_key(&self, key: T, values: &[T]) -> Vec { + pub(crate) fn insert_key(&self, key: V, values: &[V]) -> Vec { [vec![key], values.to_owned()].concat().to_vec() } } diff --git a/zkevm-circuits/src/copy_circuit.rs b/zkevm-circuits/src/copy_circuit.rs index 03e556cd28..cb9de0dc47 100644 --- a/zkevm-circuits/src/copy_circuit.rs +++ b/zkevm-circuits/src/copy_circuit.rs @@ -1,9 +1,21 @@ //! The Copy circuit implements constraints and lookups for read-write steps for //! copied bytes while execution opcodes such as CALLDATACOPY, CODECOPY, LOGS, //! etc. +pub(crate) mod util; -use bus_mapping::circuit_input_builder::{CopyDataType, CopyEvent, NumberOrHash}; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +mod dev; +#[cfg(test)] +mod test; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +pub use dev::CopyCircuit as TestCopyCircuit; + +use bus_mapping::{ + circuit_input_builder::{CopyDataType, CopyEvent}, + operation::Target, +}; use eth_types::{Field, Word}; + use gadgets::{ binary_number::BinaryNumberChip, less_than::{LtChip, LtConfig, LtInstruction}, @@ -18,40 +30,16 @@ use itertools::Itertools; use std::{collections::HashMap, marker::PhantomData}; use crate::{ - evm_circuit::util::{constraint_builder::BaseConstraintBuilder, rlc}, + evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, table::{ - BytecodeFieldTag, BytecodeTable, CopyTable, LookupTable, RwTable, RwTableTag, - TxContextFieldTag, TxTable, + BytecodeFieldTag, BytecodeTable, CopyTable, LookupTable, RwTable, TxContextFieldTag, + TxTable, }, util::{Challenges, SubCircuit, SubCircuitConfig}, witness, witness::{Bytecode, RwMap, Transaction}, }; -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -use halo2_proofs::{ - circuit::SimpleFloorPlanner, - plonk::{Challenge, Circuit}, -}; - -/// Encode the type `NumberOrHash` into a field element -pub fn number_or_hash_to_field(v: &NumberOrHash, challenge: Value) -> Value { - match v { - NumberOrHash::Number(n) => Value::known(F::from(*n as u64)), - NumberOrHash::Hash(h) => { - // since code hash in the bytecode table is represented in - // the little-endian form, we reverse the big-endian bytes - // of H256. - let le_bytes = { - let mut b = h.to_fixed_bytes(); - b.reverse(); - b - }; - challenge.map(|challenge| rlc::value(&le_bytes, challenge)) - } - } -} - /// The rw table shared between evm circuit and state circuit #[derive(Clone, Debug)] pub struct CopyCircuitConfig { @@ -62,6 +50,8 @@ pub struct CopyCircuitConfig { pub is_last: Column, /// The value copied in this copy step. pub value: Column, + /// Random linear combination accumulator value. + pub value_acc_rlc: Column, /// Whether the row is padding. pub is_pad: Column, /// In case of a bytecode tag, this denotes whether or not the copied byte @@ -119,7 +109,8 @@ impl SubCircuitConfig for CopyCircuitConfig { ) -> Self { let q_step = meta.complex_selector(); let is_last = meta.advice_column(); - let value = meta.advice_column_in(SecondPhase); + let value = meta.advice_column(); + let value_acc_rlc = meta.advice_column_in(SecondPhase); let is_code = meta.advice_column(); let is_pad = meta.advice_column(); let is_first = copy_table.is_first; @@ -237,23 +228,36 @@ impl SubCircuitConfig for CopyCircuitConfig { rw_diff, ); }); - cb.condition( - and::expr([ - meta.query_advice(is_last, Rotation::cur()), - tag.value_equals(CopyDataType::RlcAcc, Rotation::cur())(meta), - ]), - |cb| { - cb.require_equal( - "value == rlc_acc at the last row for RlcAcc", - meta.query_advice(value, Rotation::cur()), - meta.query_advice(rlc_acc, Rotation::cur()), - ); - }, - ); cb.gate(meta.query_fixed(q_enable, Rotation::cur())) }); + meta.create_gate( + "Last Step (check value accumulator) Memory => Bytecode or RlcAcc", + |meta: &mut halo2_proofs::plonk::VirtualCells| { + let mut cb = BaseConstraintBuilder::default(); + + cb.require_equal( + "value_acc_rlc == rlc_acc on the last row", + meta.query_advice(value_acc_rlc, Rotation::next()), + meta.query_advice(rlc_acc, Rotation::next()), + ); + + cb.gate(and::expr([ + meta.query_fixed(q_enable, Rotation::cur()), + meta.query_advice(is_last, Rotation::next()), + // To build a selector expression just having 0 when false and != 0 when true + // is enough, so we could replace the `or` by a `+`. This + // would give 2 when both expressions are true + // but it's fine. + and::expr([ + tag.value_equals(CopyDataType::Memory, Rotation::cur())(meta), + tag.value_equals(CopyDataType::Bytecode, Rotation::next())(meta), + ]) + tag.value_equals(CopyDataType::RlcAcc, Rotation::next())(meta), + ])) + }, + ); + meta.create_gate("verify step (q_step == 1)", |meta| { let mut cb = BaseConstraintBuilder::default(); @@ -265,10 +269,10 @@ impl SubCircuitConfig for CopyCircuitConfig { ]), ); cb.condition( - not::expr(meta.query_advice(is_last, Rotation::next())) - * (not::expr(tag.value_equals(CopyDataType::Padding, Rotation::cur())( - meta, - ))), + not::expr(or::expr([ + meta.query_advice(is_last, Rotation::next()), + tag.value_equals(CopyDataType::Padding, Rotation::cur())(meta), + ])), |cb| { cb.require_equal( "bytes_left == bytes_left_next + 1 for non-last step", @@ -277,25 +281,38 @@ impl SubCircuitConfig for CopyCircuitConfig { ); }, ); + cb.condition(meta.query_advice(is_first, Rotation::cur()), |cb| { + cb.require_equal( + "value == value_acc_rlc at every first copy event", + meta.query_advice(value, Rotation::cur()), + meta.query_advice(value_acc_rlc, Rotation::cur()), + ); + }); + cb.require_equal( + "write value == read value", + meta.query_advice(value, Rotation::cur()), + meta.query_advice(value, Rotation::next()), + ); + cb.require_equal( + "value_acc_rlc is same for read-write rows", + meta.query_advice(value_acc_rlc, Rotation::cur()), + meta.query_advice(value_acc_rlc, Rotation::next()), + ); cb.condition( - not::expr(tag.value_equals(CopyDataType::RlcAcc, Rotation::next())( - meta, - )), + and::expr([ + not::expr(meta.query_advice(is_last, Rotation::next())), + not::expr(meta.query_advice(is_pad, Rotation::cur())), + ]), |cb| { cb.require_equal( - "write value == read value (if not rlc acc)", - meta.query_advice(value, Rotation::cur()), - meta.query_advice(value, Rotation::next()), + "value_acc_rlc(2) == value_acc_rlc(0) * r + value(2)", + meta.query_advice(value_acc_rlc, Rotation(2)), + meta.query_advice(value_acc_rlc, Rotation::cur()) + * challenges.keccak_input() + + meta.query_advice(value, Rotation(2)), ); }, ); - cb.condition(meta.query_advice(is_first, Rotation::cur()), |cb| { - cb.require_equal( - "write value == read value (is_first == 1)", - meta.query_advice(value, Rotation::cur()), - meta.query_advice(value, Rotation::next()), - ); - }); cb.require_zero( "value == 0 when is_pad == 1 for read", and::expr([ @@ -313,26 +330,7 @@ impl SubCircuitConfig for CopyCircuitConfig { meta.query_advice(is_pad, Rotation::next()), ); - cb.gate(meta.query_selector(q_step)) - }); - - meta.create_gate("verify_step (q_step == 0)", |meta| { - let mut cb = BaseConstraintBuilder::default(); - - cb.require_equal( - "rows[2].value == rows[0].value * r + rows[1].value", - meta.query_advice(value, Rotation(2)), - meta.query_advice(value, Rotation::cur()) * challenges.keccak_input() - + meta.query_advice(value, Rotation::next()), - ); - - cb.gate(and::expr([ - meta.query_fixed(q_enable, Rotation::cur()), - not::expr(meta.query_selector(q_step)), - not::expr(meta.query_advice(is_last, Rotation::cur())), - tag.value_equals(CopyDataType::RlcAcc, Rotation::cur())(meta), - not::expr(meta.query_advice(is_pad, Rotation::cur())), - ])) + cb.gate(and::expr([meta.query_selector(q_step)])) }); meta.lookup_any("Memory lookup", |meta| { @@ -342,7 +340,7 @@ impl SubCircuitConfig for CopyCircuitConfig { vec![ meta.query_advice(rw_counter, Rotation::cur()), not::expr(meta.query_selector(q_step)), - RwTableTag::Memory.expr(), + Target::Memory.expr(), meta.query_advice(id, Rotation::cur()), // call_id meta.query_advice(addr, Rotation::cur()), // memory address 0.expr(), @@ -364,7 +362,7 @@ impl SubCircuitConfig for CopyCircuitConfig { vec![ meta.query_advice(rw_counter, Rotation::cur()), 1.expr(), - RwTableTag::TxLog.expr(), + Target::TxLog.expr(), meta.query_advice(id, Rotation::cur()), // tx_id meta.query_advice(addr, Rotation::cur()), // byte_index || field_tag || log_id 0.expr(), @@ -417,6 +415,7 @@ impl SubCircuitConfig for CopyCircuitConfig { q_step, is_last, value, + value_acc_rlc, is_pad, is_code, q_enable, @@ -474,13 +473,19 @@ impl CopyCircuitConfig { || "q_enable", self.q_enable, *offset, - || Value::known(F::one()), + || Value::known(F::ONE), )?; // is_last, value, is_pad, is_code - for (column, &(value, label)) in [self.is_last, self.value, self.is_pad, self.is_code] - .iter() - .zip_eq(circuit_row) + for (column, &(value, label)) in [ + self.is_last, + self.value, + self.value_acc_rlc, + self.is_pad, + self.is_code, + ] + .iter() + .zip_eq(circuit_row) { region.assign_advice( || format!("{} at row: {}", label, *offset), @@ -528,6 +533,8 @@ impl CopyCircuitConfig { let tag_chip = BinaryNumberChip::construct(self.copy_table.tag); let lt_chip = LtChip::construct(self.addr_lt_addr_end); + lt_chip.load(layouter)?; + layouter.assign_region( || "assign copy table", |mut region| { @@ -574,7 +581,7 @@ impl CopyCircuitConfig { || "q_enable", self.q_enable, *offset, - || Value::known(F::one()), + || Value::known(F::ONE), )?; // q_step if *offset % 2 == 0 { @@ -587,89 +594,96 @@ impl CopyCircuitConfig { || format!("assign is_first {}", *offset), self.copy_table.is_first, *offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // is_last region.assign_advice( || format!("assign is_last {}", *offset), self.is_last, *offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // id region.assign_advice( || format!("assign id {}", *offset), self.copy_table.id, *offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // addr region.assign_advice( || format!("assign addr {}", *offset), self.copy_table.addr, *offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // src_addr_end region.assign_advice( || format!("assign src_addr_end {}", *offset), self.copy_table.src_addr_end, *offset, - || Value::known(F::one()), + || Value::known(F::ONE), )?; // bytes_left region.assign_advice( || format!("assign bytes_left {}", *offset), self.copy_table.bytes_left, *offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // value region.assign_advice( || format!("assign value {}", *offset), self.value, *offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), + )?; + // value_acc_rlc + region.assign_advice( + || format!("assign value_acc_rlc {}", *offset), + self.value_acc_rlc, + *offset, + || Value::known(F::ZERO), )?; // rlc_acc region.assign_advice( || format!("assign rlc_acc {}", *offset), self.copy_table.rlc_acc, *offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // is_code region.assign_advice( || format!("assign is_code {}", *offset), self.is_code, *offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // is_pad region.assign_advice( || format!("assign is_pad {}", *offset), self.is_pad, *offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // rw_counter region.assign_advice( || format!("assign rw_counter {}", *offset), self.copy_table.rw_counter, *offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // rwc_inc_left region.assign_advice( || format!("assign rwc_inc_left {}", *offset), self.copy_table.rwc_inc_left, *offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; // tag tag_chip.assign(region, *offset, &CopyDataType::Padding)?; // Assign LT gadget - lt_chip.assign(region, *offset, F::zero(), F::one())?; + lt_chip.assign(region, *offset, F::ZERO, F::ONE)?; *offset += 1; @@ -746,6 +760,12 @@ impl CopyCircuit { impl SubCircuit for CopyCircuit { type Config = CopyCircuitConfig; + fn unusable_rows() -> usize { + // No column queried at more than 3 distinct rotations, so returns 6 as + // minimum unusable rows. + 6 + } + fn new_from_block(block: &witness::Block) -> Self { Self::new_with_external_data( block.copy_events.clone(), @@ -784,466 +804,3 @@ impl SubCircuit for CopyCircuit { config.assign_copy_events(layouter, &self.copy_events, self.max_copy_rows, *challenges) } } - -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -impl Circuit for CopyCircuit { - type Config = (CopyCircuitConfig, Challenges); - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let tx_table = TxTable::construct(meta); - let rw_table = RwTable::construct(meta); - let bytecode_table = BytecodeTable::construct(meta); - let q_enable = meta.fixed_column(); - let copy_table = CopyTable::construct(meta, q_enable); - let challenges = Challenges::construct(meta); - let challenge_exprs = challenges.exprs(meta); - - ( - CopyCircuitConfig::new( - meta, - CopyCircuitConfigArgs { - tx_table, - rw_table, - bytecode_table, - copy_table, - q_enable, - challenges: challenge_exprs, - }, - ), - challenges, - ) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), halo2_proofs::plonk::Error> { - let challenge_values = config.1.values(&mut layouter); - - config.0.tx_table.load( - &mut layouter, - &self.external_data.txs, - self.external_data.max_txs, - self.external_data.max_calldata, - &challenge_values, - )?; - - config.0.rw_table.load( - &mut layouter, - &self.external_data.rws.table_assignments(), - self.external_data.max_rws, - challenge_values.evm_word(), - )?; - - config.0.bytecode_table.load( - &mut layouter, - self.external_data.bytecodes.values(), - &challenge_values, - )?; - self.synthesize_sub(&config.0, &challenge_values, &mut layouter) - } -} - -/// Dev helpers -#[cfg(any(feature = "test", test))] -pub mod dev { - use crate::{copy_circuit::*, witness::Block}; - use halo2_proofs::dev::{MockProver, VerifyFailure}; - - /// Test copy circuit from copy events and test data - pub fn test_copy_circuit( - k: u32, - copy_events: Vec, - max_copy_rows: usize, - external_data: ExternalData, - ) -> Result<(), Vec> { - let circuit = - CopyCircuit::::new_with_external_data(copy_events, max_copy_rows, external_data); - - let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); - prover.verify_par() - } - - /// Test copy circuit with the provided block witness - pub fn test_copy_circuit_from_block( - k: u32, - block: Block, - ) -> Result<(), Vec> { - test_copy_circuit::( - k, - block.copy_events, - block.circuits_params.max_copy_rows, - ExternalData { - max_txs: block.circuits_params.max_txs, - max_calldata: block.circuits_params.max_calldata, - txs: block.txs, - max_rws: block.circuits_params.max_rws, - rws: block.rws, - bytecodes: block.bytecodes, - }, - ) - } -} - -#[cfg(test)] -mod tests { - use super::dev::test_copy_circuit_from_block; - use crate::{ - copy_circuit::CopyCircuit, - evm_circuit::{test::rand_bytes, witness::block_convert}, - }; - use bus_mapping::{ - circuit_input_builder::{CircuitInputBuilder, CircuitsParams}, - evm::{gen_sha3_code, MemoryKind}, - mock::BlockData, - }; - use eth_types::{bytecode, geth_types::GethData, ToWord, Word}; - use halo2_proofs::{ - dev::{MockProver, VerifyFailure}, - halo2curves::bn256::Fr, - }; - use mock::{test_ctx::helpers::account_0_code_account_1_no_code, TestContext, MOCK_ACCOUNTS}; - use pretty_assertions::assert_eq; - - fn gen_calldatacopy_data() -> CircuitInputBuilder { - let length = 0x0fffusize; - let code = bytecode! { - PUSH32(Word::from(length)) - PUSH32(Word::from(0x00)) - PUSH32(Word::from(0x00)) - CALLDATACOPY - STOP - }; - let calldata = rand_bytes(length); - let test_ctx = TestContext::<2, 1>::new( - None, - account_0_code_account_1_no_code(code), - |mut txs, accs| { - txs[0] - .from(accs[1].address) - .to(accs[0].address) - .input(calldata.into()); - }, - |block, _txs| block.number(0xcafeu64), - ) - .unwrap(); - let block: GethData = test_ctx.into(); - let mut builder = BlockData::new_from_geth_data_with_params( - block.clone(), - CircuitsParams { - max_rws: 8192, - max_copy_rows: 8192 + 2, - max_calldata: 5000, - ..Default::default() - }, - ) - .new_circuit_input_builder(); - builder - .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); - builder - } - - fn gen_codecopy_data() -> CircuitInputBuilder { - let code = bytecode! { - PUSH32(Word::from(0x20)) - PUSH32(Word::from(0x00)) - PUSH32(Word::from(0x00)) - CODECOPY - STOP - }; - let test_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(); - let block: GethData = test_ctx.into(); - let mut builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); - builder - .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); - builder - } - - fn gen_extcodecopy_data() -> CircuitInputBuilder { - let external_address = MOCK_ACCOUNTS[0]; - let code = bytecode! { - PUSH1(0x30usize) - PUSH1(0x0usize) - PUSH1(0x0usize) - PUSH20(external_address.to_word()) - EXTCODECOPY - STOP - }; - let code_ext = rand_bytes(0x0fffusize); - let test_ctx = TestContext::<3, 1>::new( - None, - |accs| { - accs[0].address(MOCK_ACCOUNTS[1]).code(code.clone()); - - accs[1].address(external_address).code(code_ext.clone()); - - accs[2] - .address(MOCK_ACCOUNTS[2]) - .balance(Word::from(1u64 << 20)); - }, - |mut txs, accs| { - txs[0].to(accs[0].address).from(accs[2].address); - }, - |block, _tx| block.number(0xcafeu64), - ) - .unwrap(); - let block: GethData = test_ctx.into(); - let mut builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); - builder - .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); - builder - } - - fn gen_sha3_data() -> CircuitInputBuilder { - let (code, _) = gen_sha3_code(0x20, 0x200, MemoryKind::EqualToSize); - let test_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(); - let block: GethData = test_ctx.into(); - let mut builder = BlockData::new_from_geth_data_with_params( - block.clone(), - CircuitsParams { - max_rws: 2000, - max_copy_rows: 0x200 * 2 + 2, - ..Default::default() - }, - ) - .new_circuit_input_builder(); - builder - .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); - builder - } - - fn gen_tx_log_data() -> CircuitInputBuilder { - let code = bytecode! { - PUSH32(200) // value - PUSH32(0) // offset - MSTORE - PUSH32(Word::MAX) // topic - PUSH1(32) // length - PUSH1(0) // offset - LOG1 - STOP - }; - let test_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(); - let block: GethData = test_ctx.into(); - let mut builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); - builder - .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); - builder - } - - #[test] - fn copy_circuit_valid_calldatacopy() { - let builder = gen_calldatacopy_data(); - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - assert_eq!(test_copy_circuit_from_block(14, block), Ok(())); - } - - #[test] - fn copy_circuit_valid_codecopy() { - let builder = gen_codecopy_data(); - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - assert_eq!(test_copy_circuit_from_block(10, block), Ok(())); - } - - #[test] - fn copy_circuit_valid_extcodecopy() { - let builder = gen_extcodecopy_data(); - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - assert_eq!(test_copy_circuit_from_block(14, block), Ok(())); - } - - #[test] - fn copy_circuit_valid_sha3() { - let builder = gen_sha3_data(); - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - assert_eq!(test_copy_circuit_from_block(14, block), Ok(())); - } - - #[test] - fn copy_circuit_valid_tx_log() { - let builder = gen_tx_log_data(); - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - assert_eq!(test_copy_circuit_from_block(10, block), Ok(())); - } - - #[test] - fn copy_circuit_invalid_calldatacopy() { - let mut builder = gen_calldatacopy_data(); - - // modify first byte of first copy event - builder.block.copy_events[0].bytes[0].0 = - builder.block.copy_events[0].bytes[0].0.wrapping_add(1); - - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - - assert_error_matches( - test_copy_circuit_from_block(14, block), - vec!["Memory lookup", "Tx calldata lookup"], - ); - } - - #[test] - fn copy_circuit_invalid_codecopy() { - let mut builder = gen_codecopy_data(); - - // modify first byte of first copy event - builder.block.copy_events[0].bytes[0].0 = - builder.block.copy_events[0].bytes[0].0.wrapping_add(1); - - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - - assert_error_matches( - test_copy_circuit_from_block(10, block), - vec!["Memory lookup", "Bytecode lookup"], - ); - } - - #[test] - fn copy_circuit_invalid_extcodecopy() { - let mut builder = gen_extcodecopy_data(); - - // modify first byte of first copy event - builder.block.copy_events[0].bytes[0].0 = - builder.block.copy_events[0].bytes[0].0.wrapping_add(1); - - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - - assert_error_matches( - test_copy_circuit_from_block(14, block), - vec!["Memory lookup", "Bytecode lookup"], - ); - } - - #[test] - fn copy_circuit_invalid_sha3() { - let mut builder = gen_sha3_data(); - - // modify first byte of first copy event - builder.block.copy_events[0].bytes[0].0 = - builder.block.copy_events[0].bytes[0].0.wrapping_add(1); - - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - - assert_error_matches( - test_copy_circuit_from_block(14, block), - vec!["Memory lookup"], - ); - } - - #[test] - fn copy_circuit_invalid_tx_log() { - let mut builder = gen_tx_log_data(); - - // modify first byte of first copy event - builder.block.copy_events[0].bytes[0].0 = - builder.block.copy_events[0].bytes[0].0.wrapping_add(1); - - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - - assert_error_matches( - test_copy_circuit_from_block(10, block), - vec!["Memory lookup", "TxLog lookup"], - ); - } - - #[test] - fn variadic_size_check() { - let builder = gen_tx_log_data(); - let block1 = block_convert::(&builder.block, &builder.code_db).unwrap(); - - let block: GethData = TestContext::<0, 0>::new(None, |_| {}, |_, _| {}, |b, _| b) - .unwrap() - .into(); - let mut builder = - BlockData::new_from_geth_data_with_params(block.clone(), CircuitsParams::default()) - .new_circuit_input_builder(); - builder - .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); - let block2 = block_convert::(&builder.block, &builder.code_db).unwrap(); - - let circuit = - CopyCircuit::::new(block1.copy_events, block1.circuits_params.max_copy_rows); - let prover1 = MockProver::::run(14, &circuit, vec![]).unwrap(); - - let circuit = CopyCircuit::::new( - block2.copy_events.clone(), - block2.circuits_params.max_copy_rows, - ); - let prover2 = MockProver::::run(14, &circuit, vec![]).unwrap(); - - assert_eq!(prover1.fixed(), prover2.fixed()); - assert_eq!(prover1.permutation(), prover2.permutation()); - } - - fn assert_error_matches(result: Result<(), Vec>, names: Vec<&str>) { - let errors = result.expect_err("result is not an error"); - assert_eq!(errors.len(), names.len(), "{:?}", errors); - for i in 0..names.len() { - match &errors[i] { - VerifyFailure::Lookup { - name: lookup_name, .. - } => { - assert_eq!(lookup_name, &names[i]) - } - VerifyFailure::ConstraintNotSatisfied { .. } => panic!(), - VerifyFailure::CellNotAssigned { .. } => panic!(), - VerifyFailure::ConstraintPoisoned { .. } => panic!(), - VerifyFailure::Permutation { .. } => panic!(), - } - } - } -} - -#[cfg(test)] -mod copy_circuit_stats { - use crate::{ - evm_circuit::step::ExecutionState, - stats::{bytecode_prefix_op_big_rws, print_circuit_stats_by_states}, - }; - - /// Prints the stats of Copy circuit per execution state. See - /// `print_circuit_stats_by_states` for more details. - /// - /// Run with: - /// `cargo test -p zkevm-circuits --release --all-features - /// get_evm_states_stats -- --nocapture --ignored` - #[ignore] - #[test] - fn get_copy_states_stats() { - print_circuit_stats_by_states( - |state| { - // TODO: Enable CREATE/CREATE2 once they are supported - matches!( - state, - ExecutionState::RETURNDATACOPY - | ExecutionState::CODECOPY - | ExecutionState::LOG - | ExecutionState::CALLDATACOPY - | ExecutionState::EXTCODECOPY - | ExecutionState::RETURN_REVERT - ) - }, - bytecode_prefix_op_big_rws, - |block, _, _| { - assert!(block.copy_events.len() <= 1); - block - .copy_events - .iter() - .map(|c| c.bytes.len() * 2) - .sum::() - }, - ); - } -} diff --git a/zkevm-circuits/src/copy_circuit/dev.rs b/zkevm-circuits/src/copy_circuit/dev.rs new file mode 100644 index 0000000000..996b12b07a --- /dev/null +++ b/zkevm-circuits/src/copy_circuit/dev.rs @@ -0,0 +1,77 @@ +pub use super::CopyCircuit; + +use crate::{ + copy_circuit::{CopyCircuitConfig, CopyCircuitConfigArgs}, + table::{BytecodeTable, CopyTable, RwTable, TxTable}, + util::{Challenges, SubCircuit, SubCircuitConfig}, +}; +use eth_types::Field; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner}, + plonk::{Challenge, Circuit, ConstraintSystem, Error}, +}; + +impl Circuit for CopyCircuit { + type Config = (CopyCircuitConfig, Challenges); + type FloorPlanner = SimpleFloorPlanner; + type Params = (); + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let tx_table = TxTable::construct(meta); + let rw_table = RwTable::construct(meta); + let bytecode_table = BytecodeTable::construct(meta); + let q_enable = meta.fixed_column(); + let copy_table = CopyTable::construct(meta, q_enable); + let challenges = Challenges::construct(meta); + let challenge_exprs = challenges.exprs(meta); + + ( + CopyCircuitConfig::new( + meta, + CopyCircuitConfigArgs { + tx_table, + rw_table, + bytecode_table, + copy_table, + q_enable, + challenges: challenge_exprs, + }, + ), + challenges, + ) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenge_values = config.1.values(&mut layouter); + + config.0.tx_table.load( + &mut layouter, + &self.external_data.txs, + self.external_data.max_txs, + self.external_data.max_calldata, + &challenge_values, + )?; + + config.0.rw_table.load( + &mut layouter, + &self.external_data.rws.table_assignments(), + self.external_data.max_rws, + challenge_values.evm_word(), + )?; + + config.0.bytecode_table.load( + &mut layouter, + self.external_data.bytecodes.values(), + &challenge_values, + )?; + self.synthesize_sub(&config.0, &challenge_values, &mut layouter) + } +} diff --git a/zkevm-circuits/src/copy_circuit/test.rs b/zkevm-circuits/src/copy_circuit/test.rs new file mode 100644 index 0000000000..b60b965c9a --- /dev/null +++ b/zkevm-circuits/src/copy_circuit/test.rs @@ -0,0 +1,352 @@ +use crate::{ + copy_circuit::*, + evm_circuit::{test::rand_bytes, witness::block_convert}, + util::unusable_rows, + witness::Block, +}; +use bus_mapping::{ + circuit_input_builder::{CircuitInputBuilder, CircuitsParams}, + evm::Sha3CodeGen, + mock::BlockData, +}; +use eth_types::{bytecode, geth_types::GethData, ToWord, Word}; +use halo2_proofs::{ + dev::{MockProver, VerifyFailure}, + halo2curves::bn256::Fr, +}; +use mock::{test_ctx::helpers::account_0_code_account_1_no_code, TestContext, MOCK_ACCOUNTS}; + +#[test] +fn copy_circuit_unusable_rows() { + assert_eq!( + CopyCircuit::::unusable_rows(), + unusable_rows::>(()), + ) +} + +/// Test copy circuit from copy events and test data +pub fn test_copy_circuit( + k: u32, + copy_events: Vec, + max_copy_rows: usize, + external_data: ExternalData, +) -> Result<(), Vec> { + let circuit = + CopyCircuit::::new_with_external_data(copy_events, max_copy_rows, external_data); + + let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); + prover.verify_par() +} + +/// Test copy circuit with the provided block witness +pub fn test_copy_circuit_from_block( + k: u32, + block: Block, +) -> Result<(), Vec> { + test_copy_circuit::( + k, + block.copy_events, + block.circuits_params.max_copy_rows, + ExternalData { + max_txs: block.circuits_params.max_txs, + max_calldata: block.circuits_params.max_calldata, + txs: block.txs, + max_rws: block.circuits_params.max_rws, + rws: block.rws, + bytecodes: block.bytecodes, + }, + ) +} + +fn gen_calldatacopy_data() -> CircuitInputBuilder { + let length = 0x0fffusize; + let code = bytecode! { + PUSH32(Word::from(length)) + PUSH32(Word::from(0x00)) + PUSH32(Word::from(0x00)) + CALLDATACOPY + STOP + }; + let calldata = rand_bytes(length); + let test_ctx = TestContext::<2, 1>::new( + None, + account_0_code_account_1_no_code(code), + |mut txs, accs| { + txs[0] + .from(accs[1].address) + .to(accs[0].address) + .input(calldata.into()); + }, + |block, _txs| block.number(0xcafeu64), + ) + .unwrap(); + let block: GethData = test_ctx.into(); + let mut builder = BlockData::new_from_geth_data_with_params( + block.clone(), + CircuitsParams { + max_rws: 8192, + max_copy_rows: 8192 + 2, + max_calldata: 5000, + ..Default::default() + }, + ) + .new_circuit_input_builder(); + builder + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); + builder +} + +fn gen_codecopy_data() -> CircuitInputBuilder { + let code = bytecode! { + PUSH32(Word::from(0x20)) + PUSH32(Word::from(0x00)) + PUSH32(Word::from(0x00)) + CODECOPY + STOP + }; + let test_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(); + let block: GethData = test_ctx.into(); + let mut builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); + builder + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); + builder +} + +fn gen_extcodecopy_data() -> CircuitInputBuilder { + let external_address = MOCK_ACCOUNTS[0]; + let code = bytecode! { + PUSH1(0x30usize) + PUSH1(0x0usize) + PUSH1(0x0usize) + PUSH20(external_address.to_word()) + EXTCODECOPY + STOP + }; + let code_ext = rand_bytes(0x0fffusize); + let test_ctx = TestContext::<3, 1>::new( + None, + |accs| { + accs[0].address(MOCK_ACCOUNTS[1]).code(code.clone()); + + accs[1].address(external_address).code(code_ext.clone()); + + accs[2] + .address(MOCK_ACCOUNTS[2]) + .balance(Word::from(1u64 << 20)); + }, + |mut txs, accs| { + txs[0].to(accs[0].address).from(accs[2].address); + }, + |block, _tx| block.number(0xcafeu64), + ) + .unwrap(); + let block: GethData = test_ctx.into(); + let mut builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); + builder + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); + builder +} + +fn gen_sha3_data() -> CircuitInputBuilder { + let (code, _) = Sha3CodeGen::mem_eq_size(0x20, 0x200).gen_sha3_code(); + let test_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(); + let block: GethData = test_ctx.into(); + let mut builder = BlockData::new_from_geth_data_with_params( + block.clone(), + CircuitsParams { + max_rws: 2000, + max_copy_rows: 0x200 * 2 + 2, + ..Default::default() + }, + ) + .new_circuit_input_builder(); + builder + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); + builder +} + +fn gen_tx_log_data() -> CircuitInputBuilder { + let code = bytecode! { + PUSH32(200) // value + PUSH32(0) // offset + MSTORE + PUSH32(Word::MAX) // topic + PUSH1(32) // length + PUSH1(0) // offset + LOG1 + STOP + }; + let test_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(); + let block: GethData = test_ctx.into(); + let mut builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); + builder + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); + builder +} + +#[test] +fn copy_circuit_valid_calldatacopy() { + let builder = gen_calldatacopy_data(); + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + assert_eq!(test_copy_circuit_from_block(14, block), Ok(())); +} + +#[test] +fn copy_circuit_valid_codecopy() { + let builder = gen_codecopy_data(); + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + assert_eq!(test_copy_circuit_from_block(10, block), Ok(())); +} + +#[test] +fn copy_circuit_valid_extcodecopy() { + let builder = gen_extcodecopy_data(); + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + assert_eq!(test_copy_circuit_from_block(14, block), Ok(())); +} + +#[test] +fn copy_circuit_valid_sha3() { + let builder = gen_sha3_data(); + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + assert_eq!(test_copy_circuit_from_block(14, block), Ok(())); +} + +#[test] +fn copy_circuit_valid_tx_log() { + let builder = gen_tx_log_data(); + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + assert_eq!(test_copy_circuit_from_block(10, block), Ok(())); +} + +#[test] +fn copy_circuit_invalid_calldatacopy() { + let mut builder = gen_calldatacopy_data(); + + // modify first byte of first copy event + builder.block.copy_events[0].bytes[0].0 = + builder.block.copy_events[0].bytes[0].0.wrapping_add(1); + + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + + assert_error_matches( + test_copy_circuit_from_block(14, block), + vec!["Memory lookup", "Tx calldata lookup"], + ); +} + +#[test] +fn copy_circuit_invalid_codecopy() { + let mut builder = gen_codecopy_data(); + + // modify first byte of first copy event + builder.block.copy_events[0].bytes[0].0 = + builder.block.copy_events[0].bytes[0].0.wrapping_add(1); + + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + + assert_error_matches( + test_copy_circuit_from_block(10, block), + vec!["Memory lookup", "Bytecode lookup"], + ); +} + +#[test] +fn copy_circuit_invalid_extcodecopy() { + let mut builder = gen_extcodecopy_data(); + + // modify first byte of first copy event + builder.block.copy_events[0].bytes[0].0 = + builder.block.copy_events[0].bytes[0].0.wrapping_add(1); + + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + + assert_error_matches( + test_copy_circuit_from_block(14, block), + vec!["Memory lookup", "Bytecode lookup"], + ); +} + +#[test] +fn copy_circuit_invalid_sha3() { + let mut builder = gen_sha3_data(); + + // modify first byte of first copy event + builder.block.copy_events[0].bytes[0].0 = + builder.block.copy_events[0].bytes[0].0.wrapping_add(1); + + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + + assert_error_matches( + test_copy_circuit_from_block(14, block), + vec!["Memory lookup"], + ); +} + +#[test] +fn copy_circuit_invalid_tx_log() { + let mut builder = gen_tx_log_data(); + + // modify first byte of first copy event + builder.block.copy_events[0].bytes[0].0 = + builder.block.copy_events[0].bytes[0].0.wrapping_add(1); + + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + + assert_error_matches( + test_copy_circuit_from_block(10, block), + vec!["Memory lookup", "TxLog lookup"], + ); +} + +#[test] +fn variadic_size_check() { + let builder = gen_tx_log_data(); + let block1 = block_convert::(&builder.block, &builder.code_db).unwrap(); + + let block: GethData = TestContext::<0, 0>::new(None, |_| {}, |_, _| {}, |b, _| b) + .unwrap() + .into(); + let mut builder = + BlockData::new_from_geth_data_with_params(block.clone(), CircuitsParams::default()) + .new_circuit_input_builder(); + builder + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); + let block2 = block_convert::(&builder.block, &builder.code_db).unwrap(); + + let circuit = CopyCircuit::::new(block1.copy_events, block1.circuits_params.max_copy_rows); + let prover1 = MockProver::::run(14, &circuit, vec![]).unwrap(); + + let circuit = CopyCircuit::::new( + block2.copy_events.clone(), + block2.circuits_params.max_copy_rows, + ); + let prover2 = MockProver::::run(14, &circuit, vec![]).unwrap(); + + assert_eq!(prover1.fixed(), prover2.fixed()); + assert_eq!(prover1.permutation(), prover2.permutation()); +} + +fn assert_error_matches(result: Result<(), Vec>, names: Vec<&str>) { + let errors = result.expect_err("result is not an error"); + assert_eq!(errors.len(), names.len(), "{:?}", errors); + for i in 0..names.len() { + match &errors[i] { + VerifyFailure::Lookup { + name: lookup_name, .. + } => { + assert_eq!(lookup_name, &names[i]) + } + VerifyFailure::ConstraintNotSatisfied { .. } => panic!(), + VerifyFailure::CellNotAssigned { .. } => panic!(), + VerifyFailure::ConstraintPoisoned { .. } => panic!(), + VerifyFailure::Permutation { .. } => panic!(), + } + } +} diff --git a/zkevm-circuits/src/copy_circuit/util.rs b/zkevm-circuits/src/copy_circuit/util.rs new file mode 100644 index 0000000000..87d5056949 --- /dev/null +++ b/zkevm-circuits/src/copy_circuit/util.rs @@ -0,0 +1,22 @@ +use crate::evm_circuit::util::rlc; +use bus_mapping::circuit_input_builder::NumberOrHash; +use eth_types::Field; +use halo2_proofs::circuit::Value; + +/// Encode the type `NumberOrHash` into a field element +pub fn number_or_hash_to_field(v: &NumberOrHash, challenge: Value) -> Value { + match v { + NumberOrHash::Number(n) => Value::known(F::from(*n as u64)), + NumberOrHash::Hash(h) => { + // since code hash in the bytecode table is represented in + // the little-endian form, we reverse the big-endian bytes + // of H256. + let le_bytes = { + let mut b = h.to_fixed_bytes(); + b.reverse(); + b + }; + challenge.map(|challenge| rlc::value(&le_bytes, challenge)) + } + } +} diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index ec02be80cd..85ac549a03 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -8,13 +8,19 @@ use halo2_proofs::{ mod execution; pub mod param; -pub(crate) mod step; +pub mod step; +pub mod table; pub(crate) mod util; -pub mod table; +#[cfg(any(feature = "test", test))] +pub(crate) mod test; +use self::step::HasExecutionState; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +pub use self::EvmCircuit as TestEvmCircuit; pub use crate::witness; use crate::{ + evm_circuit::param::{MAX_STEP_HEIGHT, STEP_STATE_HEIGHT}, table::{ BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, LookupTable, RwTable, TxTable, }, @@ -33,7 +39,7 @@ use witness::Block; pub struct EvmCircuitConfig { fixed_table: [Column; 4], byte_table: [Column; 1], - pub(crate) execution: Box>, + pub execution: Box>, // External tables tx_table: TxTable, rw_table: RwTable, @@ -68,7 +74,6 @@ impl SubCircuitConfig for EvmCircuitConfig { type ConfigArgs = EvmCircuitConfigArgs; /// Configure EvmCircuitConfig - #[allow(clippy::too_many_arguments)] fn new( meta: &mut ConstraintSystem, Self::ConfigArgs { @@ -135,7 +140,7 @@ impl EvmCircuitConfig { layouter.assign_region( || "fixed table", |mut region| { - for (offset, row) in std::iter::once([F::zero(); 4]) + for (offset, row) in std::iter::once([F::ZERO; 4]) .chain(fixed_table_tags.iter().flat_map(|tag| tag.build())) .enumerate() { @@ -217,7 +222,7 @@ impl EvmCircuit { let mut num_rows = 0; for transaction in &block.txs { for step in &transaction.steps { - num_rows += step.execution_state.get_step_height(); + num_rows += step.execution_state().get_step_height(); } } @@ -229,6 +234,12 @@ impl EvmCircuit { impl SubCircuit for EvmCircuit { type Config = EvmCircuitConfig; + fn unusable_rows() -> usize { + // Most columns are queried at MAX_STEP_HEIGHT + STEP_STATE_HEIGHT distinct rotations, so + // returns (MAX_STEP_HEIGHT + STEP_STATE_HEIGHT + 3) unusable rows. + MAX_STEP_HEIGHT + STEP_STATE_HEIGHT + 3 + } + fn new_from_block(block: &witness::Block) -> Self { Self::new(block.clone()) } @@ -269,7 +280,7 @@ pub(crate) fn detect_fixed_table_tags(block: &Block) -> Vec for EvmCircuitCached { type Config = (EvmCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self(self.0.without_witnesses()) @@ -348,6 +360,7 @@ pub(crate) mod cached { impl Circuit for EvmCircuit { type Config = (EvmCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -423,76 +436,32 @@ impl Circuit for EvmCircuit { } } -#[cfg(any(feature = "test", test))] -pub mod test { - use super::*; - use crate::evm_circuit::witness::Block; - - use eth_types::{Field, Word}; - use rand::{ - distributions::uniform::{SampleRange, SampleUniform}, - random, thread_rng, Rng, - }; - - pub(crate) fn rand_range(range: R) -> T - where - T: SampleUniform, - R: SampleRange, - { - thread_rng().gen_range(range) - } - - pub(crate) fn rand_bytes(n: usize) -> Vec { - (0..n).map(|_| random()).collect() - } - - pub(crate) fn rand_bytes_array() -> [u8; N] { - [(); N].map(|_| random()) - } - - pub(crate) fn rand_word() -> Word { - Word::from_big_endian(&rand_bytes_array::<32>()) - } - - impl EvmCircuit { - pub fn get_test_cicuit_from_block(block: Block) -> Self { - let fixed_table_tags = detect_fixed_table_tags(&block); - EvmCircuit::::new_dev(block, fixed_table_tags) - } - } -} - #[cfg(test)] mod evm_circuit_stats { use crate::{ - evm_circuit::{ - param::{ - LOOKUP_CONFIG, N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, N_PHASE2_COLUMNS, - }, - step::ExecutionState, - EvmCircuit, - }, - stats::print_circuit_stats_by_states, + evm_circuit::EvmCircuit, test_util::CircuitTestBuilder, + util::{unusable_rows, SubCircuit}, witness::block_convert, }; use bus_mapping::{circuit_input_builder::CircuitsParams, mock::BlockData}; - use cli_table::{print_stdout, Cell, Style, Table}; - use eth_types::{bytecode, evm_types::OpcodeId, geth_types::GethData, ToWord}; - use halo2_proofs::{ - dev::MockProver, - halo2curves::bn256::Fr, - plonk::{Circuit, ConstraintSystem}, - }; - use itertools::Itertools; - use mock::{ - test_ctx::{ - helpers::{account_0_code_account_1_no_code, tx_from_1_to_0}, - TestContext, - }, - MOCK_ACCOUNTS, + + use eth_types::{bytecode, geth_types::GethData}; + use halo2_proofs::{self, dev::MockProver, halo2curves::bn256::Fr}; + + use mock::test_ctx::{ + helpers::{account_0_code_account_1_no_code, tx_from_1_to_0}, + TestContext, }; + #[test] + fn evm_circuit_unusable_rows() { + assert_eq!( + EvmCircuit::::unusable_rows(), + unusable_rows::>(()), + ) + } + #[test] pub fn empty_evm_circuit_no_padding() { CircuitTestBuilder::new_from_test_ctx( @@ -512,127 +481,6 @@ mod evm_circuit_stats { .run(); } - /// Prints the stats of EVM circuit per execution state. See - /// `print_circuit_stats_by_states` for more details. - /// - /// Run with: - /// `cargo test -p zkevm-circuits --release --all-features - /// get_evm_states_stats -- --nocapture --ignored` - #[ignore] - #[test] - fn get_evm_states_stats() { - print_circuit_stats_by_states( - |state| { - // TODO: Enable CREATE/CREATE2 once they are supported - !matches!( - state, - ExecutionState::ErrorInvalidOpcode - | ExecutionState::CREATE - | ExecutionState::CREATE2 - | ExecutionState::SELFDESTRUCT - ) - }, - |opcode| match opcode { - OpcodeId::RETURNDATACOPY => { - bytecode! { - PUSH1(0x00) // retLength - PUSH1(0x00) // retOffset - PUSH1(0x00) // argsLength - PUSH1(0x00) // argsOffset - PUSH1(0x00) // value - PUSH32(MOCK_ACCOUNTS[3].to_word()) - PUSH32(0x1_0000) // gas - CALL - PUSH2(0x01) // size - PUSH2(0x00) // offset - PUSH2(0x00) // destOffset - } - } - _ => bytecode! { - PUSH2(0x40) - PUSH2(0x50) - }, - }, - |_, state, _| state.get_step_height_option().unwrap(), - ); - } - - /// This function prints to stdout a table with the top X ExecutionState - /// cell consumers of each EVM Cell type. - /// - /// Run with: - /// `cargo test -p zkevm-circuits --release get_exec_steps_occupancy - /// --features test -- --nocapture --ignored` - #[ignore] - #[test] - fn get_exec_steps_occupancy() { - let mut meta = ConstraintSystem::::default(); - let circuit = EvmCircuit::configure(&mut meta); - - let report = circuit.0.execution.instrument().clone().analyze(); - macro_rules! gen_report { - ($report:expr, $($id:ident, $cols:expr), +) => { - $( - let row_report = report - .iter() - .sorted_by(|a, b| a.$id.utilization.partial_cmp(&b.$id.utilization).unwrap()) - .rev() - .take(10) - .map(|exec| { - vec![ - format!("{:?}", exec.state), - format!("{:?}", exec.$id.available_cells), - format!("{:?}", exec.$id.unused_cells), - format!("{:?}", exec.$id.used_cells), - format!("{:?}", exec.$id.top_height), - format!("{:?}", exec.$id.used_columns), - format!("{:?}", exec.$id.utilization), - ] - }) - .collect::>>(); - - let table = row_report.table().title(vec![ - format!("{:?}", stringify!($id)).cell().bold(true), - format!("total_available_cells").cell().bold(true), - format!("unused_cells").cell().bold(true), - format!("cells").cell().bold(true), - format!("top_height").cell().bold(true), - format!("used columns (Max: {:?})", $cols).cell().bold(true), - format!("Utilization").cell().bold(true), - ]); - print_stdout(table).unwrap(); - )* - }; - } - - gen_report!( - report, - storage_1, - N_PHASE1_COLUMNS, - storage_2, - N_PHASE2_COLUMNS, - storage_perm, - N_COPY_COLUMNS, - byte_lookup, - N_BYTE_LOOKUPS, - fixed_table, - LOOKUP_CONFIG[0].1, - tx_table, - LOOKUP_CONFIG[1].1, - rw_table, - LOOKUP_CONFIG[2].1, - bytecode_table, - LOOKUP_CONFIG[3].1, - block_table, - LOOKUP_CONFIG[4].1, - copy_table, - LOOKUP_CONFIG[5].1, - keccak_table, - LOOKUP_CONFIG[6].1, - exp_table, - LOOKUP_CONFIG[7].1 - ); - } #[test] fn variadic_size_check() { let params = CircuitsParams { diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 58af4bfe3d..ec26da51cd 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -4,6 +4,7 @@ use super::{ FIXED_TABLE_LOOKUPS, KECCAK_TABLE_LOOKUPS, N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, RW_TABLE_LOOKUPS, TX_TABLE_LOOKUPS, }, + step::HasExecutionState, util::{instrumentation::Instrument, CachedRegion, CellManager, StoredExpression}, }; use crate::{ @@ -12,7 +13,9 @@ use crate::{ step::{ExecutionState, Step}, table::Table, util::{ - constraint_builder::{BaseConstraintBuilder, ConstraintBuilder}, + constraint_builder::{ + BaseConstraintBuilder, ConstrainBuilderCommon, EVMConstraintBuilder, + }, rlc, CellType, }, witness::{Block, Call, ExecStep, Transaction}, @@ -65,6 +68,7 @@ mod error_oog_call; mod error_oog_constant; mod error_oog_exp; mod error_oog_log; +mod error_oog_memory_copy; mod error_oog_sload_sstore; mod error_oog_static_memory; mod error_return_data_oo_bound; @@ -136,6 +140,7 @@ use error_oog_call::ErrorOOGCallGadget; use error_oog_constant::ErrorOOGConstantGadget; use error_oog_exp::ErrorOOGExpGadget; use error_oog_log::ErrorOOGLogGadget; +use error_oog_memory_copy::ErrorOOGMemoryCopyGadget; use error_oog_sload_sstore::ErrorOOGSloadSstoreGadget; use error_return_data_oo_bound::ErrorReturnDataOutOfBoundGadget; use error_stack::ErrorStackGadget; @@ -179,7 +184,7 @@ pub(crate) trait ExecutionGadget { const EXECUTION_STATE: ExecutionState; - fn configure(cb: &mut ConstraintBuilder) -> Self; + fn configure(cb: &mut EVMConstraintBuilder) -> Self; fn assign_exec_step( &self, @@ -193,7 +198,7 @@ pub(crate) trait ExecutionGadget { } #[derive(Clone, Debug)] -pub(crate) struct ExecutionConfig { +pub struct ExecutionConfig { // EVM Circuit selector, which enables all usable rows. The rows where this selector is // disabled won't verify any constraint (they can be unused rows or rows with blinding // factors). @@ -281,6 +286,7 @@ pub(crate) struct ExecutionConfig { error_oog_call: Box>, error_oog_constant: Box>, error_oog_exp: Box>, + error_oog_memory_copy: Box>, error_oog_sload_sstore: Box>, error_oog_static_memory_gadget: Box>, @@ -289,7 +295,6 @@ pub(crate) struct ExecutionConfig { error_oog_dynamic_memory_gadget: Box>, error_oog_log: Box>, - error_oog_memory_copy: Box>, error_oog_account_access: Box>, error_oog_sha3: Box>, @@ -298,8 +303,6 @@ pub(crate) struct ExecutionConfig { error_oog_self_destruct: Box>, error_oog_code_store: Box>, - error_insufficient_balance: - Box>, error_invalid_jump: Box>, error_invalid_opcode: Box>, error_depth: Box>, @@ -562,7 +565,6 @@ impl ExecutionConfig { error_oog_create2: configure_gadget!(), error_oog_self_destruct: configure_gadget!(), error_oog_code_store: configure_gadget!(), - error_insufficient_balance: configure_gadget!(), error_invalid_jump: configure_gadget!(), error_invalid_opcode: configure_gadget!(), error_write_protection: configure_gadget!(), @@ -594,7 +596,7 @@ impl ExecutionConfig { config } - pub(crate) fn instrument(&self) -> &Instrument { + pub fn instrument(&self) -> &Instrument { &self.instrument } @@ -617,7 +619,7 @@ impl ExecutionConfig { // height let height = { let dummy_step_next = Step::new(meta, advices, MAX_STEP_HEIGHT, true); - let mut cb = ConstraintBuilder::new( + let mut cb = EVMConstraintBuilder::new( step_curr.clone(), dummy_step_next, challenges, @@ -630,7 +632,7 @@ impl ExecutionConfig { // Now actually configure the gadget with the correct minimal height let step_next = &Step::new(meta, advices, height, true); - let mut cb = ConstraintBuilder::new( + let mut cb = EVMConstraintBuilder::new( step_curr.clone(), step_next.clone(), challenges, @@ -676,7 +678,7 @@ impl ExecutionConfig { name: &'static str, execution_state: ExecutionState, height: usize, - mut cb: ConstraintBuilder, + mut cb: EVMConstraintBuilder, ) { // Enforce the step height for this opcode let num_rows_until_next_step_next = query_expression(meta, |meta| { @@ -854,10 +856,10 @@ impl ExecutionConfig { || "step selector", self.q_step, offset, - || Value::known(if idx == 0 { F::one() } else { F::zero() }), + || Value::known(if idx == 0 { F::ONE } else { F::ZERO }), )?; let value = if idx == 0 { - F::zero() + F::ZERO } else { F::from((height - idx) as u64) }; @@ -871,7 +873,7 @@ impl ExecutionConfig { || "step height inv", self.num_rows_inv, offset, - || Value::known(value.invert().unwrap_or(F::zero())), + || Value::known(value.invert().unwrap_or(F::ZERO)), )?; } Ok(()) @@ -926,7 +928,7 @@ impl ExecutionConfig { if next.is_none() { break; } - let height = step.execution_state.get_step_height(); + let height = step.execution_state().get_step_height(); // Assign the step witness self.assign_exec_step( @@ -1009,13 +1011,13 @@ impl ExecutionConfig { || "step height", self.num_rows_until_next_step, offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; region.assign_advice( || "step height inv", self.q_step, offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; Ok(()) @@ -1073,8 +1075,8 @@ impl ExecutionConfig { return Ok(()); } assert_eq!(height, 1); - assert!(step.rw_indices.is_empty()); - assert!(matches!(step.execution_state, ExecutionState::EndBlock)); + assert!(step.rw_indices_len() == 0); + assert!(matches!(step.execution_state(), ExecutionState::EndBlock)); // Disable access to next step deliberately for "repeatable" step let region = &mut CachedRegion::<'_, '_, F>::new( @@ -1087,7 +1089,7 @@ impl ExecutionConfig { self.assign_exec_step_int(region, offset_begin, block, transaction, call, step)?; region.replicate_assignment_for_range( - || format!("repeat {:?} rows", step.execution_state), + || format!("repeat {:?} rows", step.execution_state()), offset_begin + 1, offset_end, )?; @@ -1108,11 +1110,11 @@ impl ExecutionConfig { next: Option<(&Transaction, &Call, &ExecStep)>, challenges: &Challenges>, ) -> Result<(), Error> { - if !matches!(step.execution_state, ExecutionState::EndBlock) { + if !matches!(step.execution_state(), ExecutionState::EndBlock) { log::trace!( "assign_exec_step offset: {} state {:?} step: {:?} call: {:?}", offset, - step.execution_state, + step.execution_state(), step, call ); @@ -1164,7 +1166,7 @@ impl ExecutionConfig { }; } - match step.execution_state { + match step.execution_state() { // internal states ExecutionState::BeginTx => assign_exec_step!(self.begin_tx_gadget), ExecutionState::EndTx => assign_exec_step!(self.end_tx_gadget), @@ -1278,9 +1280,6 @@ impl ExecutionConfig { assign_exec_step!(self.error_stack) } - ExecutionState::ErrorInsufficientBalance => { - assign_exec_step!(self.error_insufficient_balance) - } ExecutionState::ErrorInvalidJump => { assign_exec_step!(self.error_invalid_jump) } @@ -1290,12 +1289,6 @@ impl ExecutionConfig { ExecutionState::ErrorWriteProtection => { assign_exec_step!(self.error_write_protection) } - ExecutionState::ErrorDepth => { - assign_exec_step!(self.error_depth) - } - ExecutionState::ErrorContractAddressCollision => { - assign_exec_step!(self.error_contract_address_collision) - } ExecutionState::ErrorInvalidCreationCode => { assign_exec_step!(self.error_invalid_creation_code) } @@ -1303,7 +1296,7 @@ impl ExecutionConfig { assign_exec_step!(self.error_return_data_out_of_bound) } - _ => evm_unimplemented!("unimplemented ExecutionState: {:?}", step.execution_state), + unimpl_state => evm_unimplemented!("unimplemented ExecutionState: {:?}", unimpl_state), } // Fill in the witness values for stored expressions @@ -1311,8 +1304,8 @@ impl ExecutionConfig { // enable with `RUST_LOG=debug` if log::log_enabled!(log::Level::Debug) { - let is_padding_step = matches!(step.execution_state, ExecutionState::EndBlock) - && step.rw_indices.is_empty(); + let is_padding_step = matches!(step.execution_state(), ExecutionState::EndBlock) + && step.rw_indices_len() == 0; if !is_padding_step { // expensive function call Self::check_rw_lookup( @@ -1336,8 +1329,8 @@ impl ExecutionConfig { let mut assigned_stored_expressions = Vec::new(); for stored_expression in self .stored_expressions_map - .get(&step.execution_state) - .unwrap_or_else(|| panic!("Execution state unknown: {:?}", step.execution_state)) + .get(&step.execution_state()) + .unwrap_or_else(|| panic!("Execution state unknown: {:?}", step.execution_state())) { let assigned = stored_expression.assign(region, offset)?; assigned.map(|v| { @@ -1354,9 +1347,9 @@ impl ExecutionConfig { block: &Block, challenges: &Challenges>, ) { - let mut evm_randomness = F::zero(); + let mut evm_randomness = F::ZERO; challenges.evm_word().map(|v| evm_randomness = v); - let mut lookup_randomness = F::zero(); + let mut lookup_randomness = F::ZERO; challenges.lookup_input().map(|v| lookup_randomness = v); if evm_randomness.is_zero_vartime() || lookup_randomness.is_zero_vartime() { // challenges not ready @@ -1372,10 +1365,8 @@ impl ExecutionConfig { } } - let rlc_assignments: BTreeSet<_> = step - .rw_indices - .iter() - .map(|rw_idx| block.rws[*rw_idx]) + let rlc_assignments: BTreeSet<_> = (0..step.rw_indices_len()) + .map(|index| block.get_rws(step, index)) .map(|rw| { rw.table_assignment_aux(evm_randomness) .rlc(lookup_randomness) @@ -1395,10 +1386,10 @@ impl ExecutionConfig { // Check that the number of rw operations generated from the bus-mapping // correspond to the number of assigned rw lookups by the EVM Circuit // plus the number of rw lookups done by the copy circuit. - if step.rw_indices.len() != assigned_rw_values.len() + step.copy_rw_counter_delta as usize { + if step.rw_indices_len() != assigned_rw_values.len() + step.copy_rw_counter_delta as usize { log::error!( "step.rw_indices.len: {} != assigned_rw_values.len: {} + step.copy_rw_counter_delta: {} in step: {:?}", - step.rw_indices.len(), + step.rw_indices_len(), assigned_rw_values.len(), step.copy_rw_counter_delta, step @@ -1422,23 +1413,21 @@ impl ExecutionConfig { // corresponding rw lookup, but in the bus-mapping they are // generated at the end of the step. let idx = if is_rev { - step.rw_indices.len() - rev_count + step.rw_indices_len() - rev_count } else { idx - rev_count }; - let rw_idx = step.rw_indices[idx]; - let rw = block.rws[rw_idx]; + let rw = block.get_rws(step, idx); let table_assignments = rw.table_assignment_aux(evm_randomness); let rlc = table_assignments.rlc(lookup_randomness); if rlc != assigned_rw_value.1 { log::error!( - "incorrect rw witness. lookup input name: \"{}\"\nassigned={:?}\nrlc ={:?}\nrw index: {:?}, {}th rw of step {:?}, rw: {:?}", + "incorrect rw witness. lookup input name: \"{}\"\nassigned={:?}\nrlc ={:?}\n{}th rw of step {:?}, rw: {:?}", assigned_rw_value.0, assigned_rw_value.1, rlc, - rw_idx, idx, - step.execution_state, + step.execution_state(), rw); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/add_sub.rs b/zkevm-circuits/src/evm_circuit/execution/add_sub.rs index d604d636b0..94065d66d8 100644 --- a/zkevm-circuits/src/evm_circuit/execution/add_sub.rs +++ b/zkevm-circuits/src/evm_circuit/execution/add_sub.rs @@ -4,7 +4,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, math_gadget::{AddWordsGadget, PairSelectGadget}, select, CachedRegion, }, @@ -32,7 +32,7 @@ impl ExecutionGadget for AddSubGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ADD_SUB; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let a = cb.query_word_rlc(); @@ -82,13 +82,13 @@ impl ExecutionGadget for AddSubGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let indices = if opcode == OpcodeId::SUB { - [step.rw_indices[2], step.rw_indices[1], step.rw_indices[0]] + [2, 1, 0] } else { - [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] + [0, 1, 2] }; - let [a, b, c] = indices.map(|idx| block.rws[idx].stack_value()); + let [a, b, c] = indices.map(|index| block.get_rws(step, index).stack_value()); self.add_words.assign(region, offset, [a, b], c)?; self.is_sub.assign( region, diff --git a/zkevm-circuits/src/evm_circuit/execution/addmod.rs b/zkevm-circuits/src/evm_circuit/execution/addmod.rs index 45bc6c6200..5ae1f1b5f5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/addmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/addmod.rs @@ -4,7 +4,10 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, + Transition::Delta, + }, math_gadget::{ AddWordsGadget, CmpWordsGadget, IsZeroGadget, MulAddWords512Gadget, MulAddWordsGadget, @@ -49,7 +52,7 @@ impl ExecutionGadget for AddModGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ADDMOD; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); // values got from stack (original r is modified if n==0) @@ -147,9 +150,7 @@ impl ExecutionGadget for AddModGadget { self.same_context.assign_exec_step(region, offset, step)?; // get stack values - let [mut r, n, b, a] = [3, 2, 1, 0] - .map(|idx| step.rw_indices[idx]) - .map(|idx| block.rws[idx].stack_value()); + let [mut r, n, b, a] = [3, 2, 1, 0].map(|index| block.get_rws(step, index).stack_value()); // assing a,b & n stack values self.a.assign(region, offset, Some(a.to_le_bytes()))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/address.rs b/zkevm-circuits/src/evm_circuit/execution/address.rs index 494740d68c..13ef41aa42 100644 --- a/zkevm-circuits/src/evm_circuit/execution/address.rs +++ b/zkevm-circuits/src/evm_circuit/execution/address.rs @@ -5,7 +5,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, from_bytes, CachedRegion, RandomLinearCombination, }, witness::{Block, Call, ExecStep, Transaction}, @@ -16,7 +16,6 @@ use crate::{ use bus_mapping::evm::OpcodeId; use eth_types::{Field, ToAddress, ToLittleEndian}; use halo2_proofs::plonk::Error; -use std::convert::TryInto; #[derive(Clone, Debug)] pub(crate) struct AddressGadget { @@ -29,7 +28,7 @@ impl ExecutionGadget for AddressGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ADDRESS; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let address = cb.query_word_rlc(); // Lookup callee address in call context. @@ -70,8 +69,8 @@ impl ExecutionGadget for AddressGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let address = block.rws[step.rw_indices[1]].stack_value(); - debug_assert_eq!(call.callee_address, address.to_address()); + let address = block.get_rws(step, 1).stack_value(); + debug_assert_eq!(call.address, address.to_address()); self.address.assign( region, @@ -90,8 +89,8 @@ impl ExecutionGadget for AddressGadget { #[cfg(test)] mod test { use crate::{evm_circuit::test::rand_bytes, test_util::CircuitTestBuilder}; - use eth_types::{bytecode, ToWord, Word}; - use mock::test_ctx::TestContext; + use eth_types::{bytecode, Word}; + use mock::{generate_mock_call_bytecode, test_ctx::TestContext, MockCallBytecodeParams}; fn test_root_ok() { let bytecode = bytecode! { @@ -116,22 +115,13 @@ mod test { // code A calls code B. let pushdata = rand_bytes(8); - let code_a = bytecode! { - // populate memory in A's context. - PUSH8(Word::from_big_endian(&pushdata)) - PUSH1(0x00) // offset - MSTORE - // call ADDR_B. - PUSH1(0x00) // retLength - PUSH1(0x00) // retOffset - PUSH32(call_data_length) // argsLength - PUSH32(call_data_offset) // argsOffset - PUSH1(0x00) // value - PUSH32(addr_b.to_word()) // addr - PUSH32(0x1_0000) // gas - CALL - STOP - }; + let code_a = generate_mock_call_bytecode(MockCallBytecodeParams { + address: addr_b, + pushdata, + call_data_length, + call_data_offset, + ..MockCallBytecodeParams::default() + }); let ctx = TestContext::<3, 1>::new( None, diff --git a/zkevm-circuits/src/evm_circuit/execution/balance.rs b/zkevm-circuits/src/evm_circuit/execution/balance.rs index a806dd3833..f392ad2508 100644 --- a/zkevm-circuits/src/evm_circuit/execution/balance.rs +++ b/zkevm-circuits/src/evm_circuit/execution/balance.rs @@ -6,7 +6,8 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{ - ConstraintBuilder, ReversionInfo, StepStateTransition, Transition::Delta, + ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, + Transition::Delta, }, from_bytes, math_gadget::IsZeroGadget, @@ -37,7 +38,7 @@ impl ExecutionGadget for BalanceGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::BALANCE; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let address_word = cb.query_word_rlc(); let address = from_bytes::expr(&address_word.cells[..N_BYTES_ACCOUNT_ADDRESS]); cb.stack_pop(address_word.expr()); @@ -108,7 +109,7 @@ impl ExecutionGadget for BalanceGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let address = block.rws[step.rw_indices[0]].stack_value(); + let address = block.get_rws(step, 0).stack_value(); self.address_word .assign(region, offset, Some(address.to_le_bytes()))?; @@ -122,11 +123,11 @@ impl ExecutionGadget for BalanceGadget { call.is_persistent, )?; - let (_, is_warm) = block.rws[step.rw_indices[4]].tx_access_list_value_pair(); + let (_, is_warm) = block.get_rws(step, 4).tx_access_list_value_pair(); self.is_warm - .assign(region, offset, Value::known(F::from(is_warm)))?; + .assign(region, offset, Value::known(F::from(is_warm as u64)))?; - let code_hash = block.rws[step.rw_indices[5]].account_value_pair().0; + let code_hash = block.get_rws(step, 5).account_value_pair().0; self.code_hash .assign(region, offset, region.word_rlc(code_hash))?; self.not_exists @@ -134,7 +135,7 @@ impl ExecutionGadget for BalanceGadget { let balance = if code_hash.is_zero() { eth_types::Word::zero() } else { - block.rws[step.rw_indices[6]].account_value_pair().0 + block.get_rws(step, 6).account_value_pair().0 }; self.balance .assign(region, offset, region.word_rlc(balance))?; @@ -146,11 +147,9 @@ impl ExecutionGadget for BalanceGadget { #[cfg(test)] mod test { use crate::{evm_circuit::test::rand_bytes, test_util::CircuitTestBuilder}; - use eth_types::{ - address, bytecode, geth_types::Account, Address, Bytecode, ToWord, Word, U256, - }; + use eth_types::{address, bytecode, geth_types::Account, Address, Bytecode, Word, U256}; use lazy_static::lazy_static; - use mock::TestContext; + use mock::{generate_mock_call_bytecode, test_ctx::TestContext, MockCallBytecodeParams}; lazy_static! { static ref TEST_ADDRESS: Address = address!("0xaabbccddee000000000000000000000000000000"); @@ -204,14 +203,12 @@ mod test { let mut code = Bytecode::default(); if is_warm { code.append(&bytecode! { - PUSH20(address.to_word()) - BALANCE + .op_balance(address) POP }); } code.append(&bytecode! { - PUSH20(address.to_word()) - BALANCE + .op_balance(address) STOP }); @@ -257,35 +254,24 @@ mod test { let mut code_b = Bytecode::default(); if is_warm { code_b.append(&bytecode! { - PUSH20(address.to_word()) - BALANCE + .op_balance(address) POP }); } code_b.append(&bytecode! { - PUSH20(address.to_word()) - BALANCE + .op_balance(address) STOP }); // code A calls code B. let pushdata = rand_bytes(8); - let code_a = bytecode! { - // populate memory in A's context. - PUSH8(Word::from_big_endian(&pushdata)) - PUSH1(0x00) // offset - MSTORE - // call ADDR_B. - PUSH1(0x00) // retLength - PUSH1(0x00) // retOffset - PUSH32(call_data_length) // argsLength - PUSH32(call_data_offset) // argsOffset - PUSH1(0x00) // value - PUSH32(addr_b.to_word()) // addr - PUSH32(0x1_0000) // gas - CALL - STOP - }; + let code_a = generate_mock_call_bytecode(MockCallBytecodeParams { + address: addr_b, + pushdata, + call_data_length, + call_data_offset, + ..MockCallBytecodeParams::default() + }); let ctx = TestContext::<4, 1>::new( None, diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs index aa93f2b55e..90db035125 100644 --- a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs @@ -1,25 +1,27 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_WORD}, + param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_U64, N_BYTES_WORD}, step::ExecutionState, util::{ and, common_gadget::TransferWithGasFeeGadget, constraint_builder::{ - ConstraintBuilder, ReversionInfo, StepStateTransition, + ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition::{Delta, To}, }, is_precompiled, math_gadget::{ - ContractCreateGadget, IsEqualGadget, IsZeroGadget, MulWordByU64Gadget, - RangeCheckGadget, + ConstantDivisionGadget, ContractCreateGadget, IsEqualGadget, IsZeroGadget, + MulWordByU64Gadget, RangeCheckGadget, }, not, or, select, CachedRegion, Cell, StepRws, Word, }, witness::{Block, Call, ExecStep, Transaction}, }, - table::{AccountFieldTag, CallContextFieldTag, TxFieldTag as TxContextFieldTag}, + table::{ + AccountFieldTag, BlockContextFieldTag, CallContextFieldTag, TxFieldTag as TxContextFieldTag, + }, util::Expr, }; use eth_types::{evm_types::GasCost, Field, ToLittleEndian, ToScalar}; @@ -42,6 +44,7 @@ pub(crate) struct BeginTxGadget { tx_value: Word, tx_call_data_length: Cell, tx_call_data_gas_cost: Cell, + tx_call_data_word_length: ConstantDivisionGadget, reversion_info: ReversionInfo, sufficient_gas_left: RangeCheckGadget, transfer_with_gas_fee: TransferWithGasFeeGadget, @@ -51,6 +54,12 @@ pub(crate) struct BeginTxGadget { create: ContractCreateGadget, callee_not_exists: IsZeroGadget, is_caller_callee_equal: Cell, + // EIP-3651 (Warm COINBASE) + coinbase: Cell, + // Caller, callee and a list addresses are added to the access list before + // coinbase, and may be duplicate. + // + is_coinbase_warm: Cell, } impl ExecutionGadget for BeginTxGadget { @@ -58,7 +67,7 @@ impl ExecutionGadget for BeginTxGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::BeginTx; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { // Use rw_counter of the step which triggers next call as its call_id. let call_id = cb.curr.state.rw_counter.clone(); @@ -127,13 +136,25 @@ impl ExecutionGadget for BeginTxGadget { let mul_gas_fee_by_gas = MulWordByU64Gadget::construct(cb, tx_gas_price.clone(), tx_gas.expr()); + let tx_call_data_word_length = + ConstantDivisionGadget::construct(cb, tx_call_data_length.expr() + 31.expr(), 32); + + // Calculate gas cost of init code for EIP-3860. + let init_code_gas_cost = select::expr( + tx_is_create.expr(), + tx_call_data_word_length.quotient().expr() + * eth_types::evm_types::INIT_CODE_WORD_GAS.expr(), + 0.expr(), + ); + // TODO: Take gas cost of access list (EIP 2930) into consideration. // Use intrinsic gas let intrinsic_gas_cost = select::expr( tx_is_create.expr(), GasCost::CREATION_TX.expr(), GasCost::TX.expr(), - ) + tx_call_data_gas_cost.expr(); + ) + tx_call_data_gas_cost.expr() + + init_code_gas_cost; // Check gas_left is sufficient let gas_left = tx_gas.expr() - intrinsic_gas_cost; @@ -158,6 +179,18 @@ impl ExecutionGadget for BeginTxGadget { None, ); // rwc_delta += 1 + // Query coinbase address. + let coinbase = cb.query_cell(); + let is_coinbase_warm = cb.query_bool(); + cb.block_lookup(BlockContextFieldTag::Coinbase.expr(), None, coinbase.expr()); + cb.account_access_list_write( + tx_id.expr(), + coinbase.expr(), + 1.expr(), + is_coinbase_warm.expr(), + None, + ); // rwc_delta += 1 + // Read code_hash of callee let phase2_code_hash = cb.query_cell_phase2(); let is_empty_code_hash = @@ -262,8 +295,9 @@ impl ExecutionGadget for BeginTxGadget { // - Write CallContext IsPersistent // - Write CallContext IsSuccess // - Write Account (Caller) Nonce - // - Write TxAccessListAccount - // - Write TxAccessListAccount + // - Write TxAccessListAccount (Caller) + // - Write TxAccessListAccount (Callee) + // - Write TxAccessListAccount (Coinbase) for EIP-3651 // - a TransferWithGasFeeGadget // - Write Account (Callee) Nonce (Reversible) // - Write CallContext Depth @@ -279,7 +313,7 @@ impl ExecutionGadget for BeginTxGadget { // - Write CallContext IsRoot // - Write CallContext IsCreate // - Write CallContext CodeHash - rw_counter: Delta(21.expr() + transfer_with_gas_fee.rw_delta()), + rw_counter: Delta(22.expr() + transfer_with_gas_fee.rw_delta()), call_id: To(call_id.expr()), is_root: To(true.expr()), is_create: To(tx_is_create.expr()), @@ -318,11 +352,12 @@ impl ExecutionGadget for BeginTxGadget { // - Write CallContext IsPersistent // - Write CallContext IsSuccess // - Write Account Nonce - // - Write TxAccessListAccount - // - Write TxAccessListAccount + // - Write TxAccessListAccount (Caller) + // - Write TxAccessListAccount (Callee) + // - Write TxAccessListAccount (Coinbase) for EIP-3651 // - Read Account CodeHash // - a TransferWithGasFeeGadget - rw_counter: Delta(8.expr() + transfer_with_gas_fee.rw_delta()), + rw_counter: Delta(9.expr() + transfer_with_gas_fee.rw_delta()), call_id: To(call_id.expr()), ..StepStateTransition::any() }); @@ -362,8 +397,9 @@ impl ExecutionGadget for BeginTxGadget { // - Write CallContext IsPersistent // - Write CallContext IsSuccess // - Write Account Nonce - // - Write TxAccessListAccount - // - Write TxAccessListAccount + // - Write TxAccessListAccount (Caller) + // - Write TxAccessListAccount (Callee) + // - Write TxAccessListAccount (Coinbase) for EIP-3651 // - Read Account CodeHash // - a TransferWithGasFeeGadget // - Write CallContext Depth @@ -379,7 +415,7 @@ impl ExecutionGadget for BeginTxGadget { // - Write CallContext IsRoot // - Write CallContext IsCreate // - Write CallContext CodeHash - rw_counter: Delta(21.expr() + transfer_with_gas_fee.rw_delta()), + rw_counter: Delta(22.expr() + transfer_with_gas_fee.rw_delta()), call_id: To(call_id.expr()), is_root: To(true.expr()), is_create: To(tx_is_create.expr()), @@ -406,6 +442,7 @@ impl ExecutionGadget for BeginTxGadget { tx_value, tx_call_data_length, tx_call_data_gas_cost, + tx_call_data_word_length, reversion_info, sufficient_gas_left, transfer_with_gas_fee, @@ -415,6 +452,8 @@ impl ExecutionGadget for BeginTxGadget { create, callee_not_exists, is_caller_callee_equal, + coinbase, + is_coinbase_warm, } } @@ -432,6 +471,8 @@ impl ExecutionGadget for BeginTxGadget { let mut rws = StepRws::new(block, step); rws.offset_add(7); + + let is_coinbase_warm = rws.next().tx_access_list_value_pair().1; let mut callee_code_hash = zero; if !is_precompiled(&tx.callee_address) && !tx.is_create { callee_code_hash = rws.next().account_value_pair().1; @@ -490,7 +531,7 @@ impl ExecutionGadget for BeginTxGadget { self.is_caller_callee_equal.assign( region, offset, - Value::known(F::from(caller_address == callee_address)), + Value::known(F::from((caller_address == callee_address) as u64)), )?; self.tx_is_create .assign(region, offset, Value::known(F::from(tx.is_create as u64)))?; @@ -504,6 +545,8 @@ impl ExecutionGadget for BeginTxGadget { offset, Value::known(F::from(tx.call_data_gas_cost)), )?; + self.tx_call_data_word_length + .assign(region, offset, tx.call_data_length as u128 + 31)?; self.reversion_info.assign( region, offset, @@ -557,22 +600,41 @@ impl ExecutionGadget for BeginTxGadget { None, )?; + self.coinbase.assign( + region, + offset, + Value::known( + block + .context + .coinbase + .to_scalar() + .expect("unexpected Address -> Scalar conversion failure"), + ), + )?; + self.is_coinbase_warm.assign( + region, + offset, + Value::known(F::from(is_coinbase_warm as u64)), + )?; + Ok(()) } } #[cfg(test)] mod test { + use std::vec; + use crate::{evm_circuit::test::rand_bytes, test_util::CircuitTestBuilder}; use bus_mapping::evm::OpcodeId; use eth_types::{self, bytecode, evm_types::GasCost, word, Bytecode, Word}; - use mock::{eth, gwei, TestContext, MOCK_ACCOUNTS}; + use mock::{eth, gwei, MockTransaction, TestContext, MOCK_ACCOUNTS}; fn gas(call_data: &[u8]) -> Word { Word::from( - GasCost::TX.as_u64() - + 2 * OpcodeId::PUSH32.constant_gas_cost().as_u64() + GasCost::TX + + 2 * OpcodeId::PUSH32.constant_gas_cost() + call_data .iter() .map(|&x| if x == 0 { 4 } else { 16 }) @@ -623,19 +685,20 @@ mod test { CircuitTestBuilder::new_from_test_ctx(ctx).run(); } - // TODO: Use `mock` crate. fn mock_tx(value: Word, gas_price: Word, calldata: Vec) -> eth_types::Transaction { let from = MOCK_ACCOUNTS[1]; let to = MOCK_ACCOUNTS[0]; - eth_types::Transaction { - from, - to: Some(to), - value, - gas: gas(&calldata), - gas_price: Some(gas_price), - input: calldata.into(), - ..Default::default() - } + + let mock_transaction = MockTransaction::default() + .from(from) + .to(to) + .value(value) + .gas(gas(&calldata)) + .gas_price(gas_price) + .input(calldata.into()) + .build(); + + eth_types::Transaction::from(mock_transaction) } #[test] @@ -661,7 +724,7 @@ mod test { // This test checks that the rw table assignment and evm circuit are consistent // in not applying an RLC to account and tx nonces. // https://github.com/privacy-scaling-explorations/zkevm-circuits/issues/592 - let multibyte_nonce = Word::from(700); + let multibyte_nonce = 700; let to = MOCK_ACCOUNTS[0]; let from = MOCK_ACCOUNTS[1]; @@ -773,12 +836,12 @@ mod test { accs[0] .address(MOCK_ACCOUNTS[0]) .balance(eth(20)) - .nonce(nonce.into()); + .nonce(nonce); }, |mut txs, _accs| { txs[0] .from(MOCK_ACCOUNTS[0]) - .nonce(nonce.into()) + .nonce(nonce) .gas_price(gwei(2)) .gas(Word::from(0x10000)) .value(eth(2)) diff --git a/zkevm-circuits/src/evm_circuit/execution/bitwise.rs b/zkevm-circuits/src/evm_circuit/execution/bitwise.rs index e6f5a3e7f6..eef97570fb 100644 --- a/zkevm-circuits/src/evm_circuit/execution/bitwise.rs +++ b/zkevm-circuits/src/evm_circuit/execution/bitwise.rs @@ -5,7 +5,7 @@ use crate::{ table::{FixedTableTag, Lookup}, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, Word, }, witness::{Block, Call, ExecStep, Transaction}, @@ -28,7 +28,7 @@ impl ExecutionGadget for BitwiseGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::BITWISE; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let a = cb.query_word_rlc(); @@ -87,8 +87,7 @@ impl ExecutionGadget for BitwiseGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [a, b, c] = [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] - .map(|idx| block.rws[idx].stack_value()); + let [a, b, c] = [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); self.a.assign(region, offset, Some(a.to_le_bytes()))?; self.b.assign(region, offset, Some(b.to_le_bytes()))?; self.c.assign(region, offset, Some(c.to_le_bytes()))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs b/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs index b594928b12..f53b0c96b1 100644 --- a/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs @@ -5,7 +5,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, from_bytes, CachedRegion, RandomLinearCombination, }, witness::{Block, Call, ExecStep, Transaction}, @@ -24,7 +24,7 @@ pub(crate) struct BlockCtxGadget { } impl BlockCtxGadget { - fn construct(cb: &mut ConstraintBuilder) -> Self { + fn construct(cb: &mut EVMConstraintBuilder) -> Self { let value = cb.query_word_rlc(); // Push the const generic parameter N_BYTES value to the stack @@ -71,7 +71,7 @@ impl ExecutionGadget for BlockCtxU64Gadget { const EXECUTION_STATE: ExecutionState = ExecutionState::BLOCKCTXU64; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let value_u64 = BlockCtxGadget::construct(cb); Self { value_u64 } @@ -90,7 +90,7 @@ impl ExecutionGadget for BlockCtxU64Gadget { .same_context .assign_exec_step(region, offset, step)?; - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); self.value_u64.value.assign( region, @@ -112,7 +112,7 @@ impl ExecutionGadget for BlockCtxU160Gadget { const EXECUTION_STATE: ExecutionState = ExecutionState::BLOCKCTXU160; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let value_u160 = BlockCtxGadget::construct(cb); Self { value_u160 } @@ -131,7 +131,7 @@ impl ExecutionGadget for BlockCtxU160Gadget { .same_context .assign_exec_step(region, offset, step)?; - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); self.value_u160.value.assign( region, @@ -157,7 +157,7 @@ impl ExecutionGadget for BlockCtxU256Gadget { const EXECUTION_STATE: ExecutionState = ExecutionState::BLOCKCTXU256; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let value_u256 = BlockCtxGadget::construct(cb); Self { value_u256 } @@ -176,7 +176,7 @@ impl ExecutionGadget for BlockCtxU256Gadget { .same_context .assign_exec_step(region, offset, step)?; - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); self.value_u256 .value diff --git a/zkevm-circuits/src/evm_circuit/execution/blockhash.rs b/zkevm-circuits/src/evm_circuit/execution/blockhash.rs index 4ddcf450a6..78d140494f 100644 --- a/zkevm-circuits/src/evm_circuit/execution/blockhash.rs +++ b/zkevm-circuits/src/evm_circuit/execution/blockhash.rs @@ -4,11 +4,14 @@ use crate::{ param::N_BYTES_U64, step::ExecutionState, util::{ - common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, - from_bytes, + and, + common_gadget::{SameContextGadget, WordByteCapGadget}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, + Transition::Delta, + }, math_gadget::LtGadget, - CachedRegion, Cell, RandomLinearCombination, Word, + CachedRegion, Cell, Word, }, witness::{Block, Call, ExecStep, Transaction}, }, @@ -23,10 +26,9 @@ use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct BlockHashGadget { same_context: SameContextGadget, - block_number: RandomLinearCombination, + block_number: WordByteCapGadget, current_block_number: Cell, block_hash: Word, - block_lt: LtGadget, diff_lt: LtGadget, } @@ -35,10 +37,7 @@ impl ExecutionGadget for BlockHashGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::BLOCKHASH; - fn configure(cb: &mut ConstraintBuilder) -> Self { - let block_number = cb.query_word_rlc(); - cb.stack_pop(block_number.expr()); - + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let current_block_number = cb.query_cell(); cb.block_lookup( BlockContextFieldTag::Number.expr(), @@ -46,28 +45,32 @@ impl ExecutionGadget for BlockHashGadget { current_block_number.expr(), ); - let block_lt = LtGadget::construct( - cb, - from_bytes::expr(&block_number.cells), - current_block_number.expr(), - ); + let block_number = WordByteCapGadget::construct(cb, current_block_number.expr()); + cb.stack_pop(block_number.original_word()); + + let block_hash = cb.query_word_rlc(); let diff_lt = LtGadget::construct( cb, current_block_number.expr(), - 257.expr() + from_bytes::expr(&block_number.cells), + 257.expr() + block_number.valid_value(), ); - let block_hash = cb.query_word_rlc(); - cb.condition(block_lt.expr() * diff_lt.expr(), |cb| { + let is_valid = and::expr([block_number.lt_cap(), diff_lt.expr()]); + + cb.condition(is_valid.expr(), |cb| { cb.block_lookup( BlockContextFieldTag::BlockHash.expr(), - Some(from_bytes::expr(&block_number.cells)), + Some(block_number.valid_value()), block_hash.expr(), ); }); - cb.condition(not::expr(block_lt.expr() * diff_lt.expr()), |cb| { - cb.require_zero("invalid range", block_hash.expr()); + + cb.condition(not::expr(is_valid), |cb| { + cb.require_zero( + "Invalid block number for block hash lookup", + block_hash.expr(), + ); }); cb.stack_push(block_hash.expr()); @@ -86,7 +89,6 @@ impl ExecutionGadget for BlockHashGadget { block_number, current_block_number, block_hash, - block_lt, diff_lt, } } @@ -102,44 +104,29 @@ impl ExecutionGadget for BlockHashGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let block_number = block.rws[step.rw_indices[0]].stack_value(); - self.block_number.assign( - region, - offset, - Some( - block_number.to_le_bytes()[..N_BYTES_U64] - .try_into() - .unwrap(), - ), - )?; - let block_number: F = block_number.to_scalar().unwrap(); - let current_block_number = block.context.number; - self.current_block_number.assign( - region, - offset, - Value::known( - current_block_number - .to_scalar() - .expect("unexpected U256 -> Scalar conversion failure"), - ), - )?; - let current_block_number: F = current_block_number.to_scalar().unwrap(); + let current_block_number = current_block_number + .to_scalar() + .expect("unexpected U256 -> Scalar conversion failure"); + + let block_number = block.get_rws(step, 0).stack_value(); + self.block_number + .assign(region, offset, block_number, current_block_number)?; + + self.current_block_number + .assign(region, offset, Value::known(current_block_number))?; self.block_hash.assign( region, offset, - Some(block.rws[step.rw_indices[1]].stack_value().to_le_bytes()), + Some(block.get_rws(step, 1).stack_value().to_le_bytes()), )?; - self.block_lt - .assign(region, offset, block_number, current_block_number)?; - self.diff_lt.assign( region, offset, current_block_number, - block_number + F::from(257), + F::from(u64::try_from(block_number).unwrap_or(u64::MAX)) + F::from(257), )?; Ok(()) @@ -152,7 +139,7 @@ mod test { use eth_types::{bytecode, U256}; use mock::test_ctx::{helpers::*, TestContext}; - fn test_ok(block_number: usize, current_block_number: u64) { + fn test_ok(block_number: U256, current_block_number: u64) { let code = bytecode! { PUSH32(block_number) BLOCKHASH @@ -182,21 +169,26 @@ mod test { #[test] fn blockhash_gadget_simple() { - test_ok(0, 5); - test_ok(1, 5); - test_ok(2, 5); - test_ok(3, 5); - test_ok(4, 5); - test_ok(5, 5); - test_ok(6, 5); + test_ok(0.into(), 5); + test_ok(1.into(), 5); + test_ok(2.into(), 5); + test_ok(3.into(), 5); + test_ok(4.into(), 5); + test_ok(5.into(), 5); + test_ok(6.into(), 5); } #[test] fn blockhash_gadget_large() { - test_ok(0xcafe - 257, 0xcafeu64); - test_ok(0xcafe - 256, 0xcafeu64); - test_ok(0xcafe - 1, 0xcafeu64); - test_ok(0xcafe, 0xcafeu64); - test_ok(0xcafe + 1, 0xcafeu64); + test_ok((0xcafe - 257).into(), 0xcafeu64); + test_ok((0xcafe - 256).into(), 0xcafeu64); + test_ok((0xcafe - 1).into(), 0xcafeu64); + test_ok(0xcafe.into(), 0xcafeu64); + test_ok((0xcafe + 1).into(), 0xcafeu64); + } + + #[test] + fn blockhash_gadget_block_number_overflow() { + test_ok(U256::MAX, 0xcafeu64); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/byte.rs b/zkevm-circuits/src/evm_circuit/execution/byte.rs index 2d44757959..a21c0570e8 100644 --- a/zkevm-circuits/src/evm_circuit/execution/byte.rs +++ b/zkevm-circuits/src/evm_circuit/execution/byte.rs @@ -4,7 +4,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, math_gadget::{IsEqualGadget, IsZeroGadget}, sum, CachedRegion, Word, }, @@ -31,7 +31,7 @@ impl ExecutionGadget for ByteGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::BYTE; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let index = cb.query_word_rlc(); let value = cb.query_word_rlc(); @@ -101,8 +101,8 @@ impl ExecutionGadget for ByteGadget { self.same_context.assign_exec_step(region, offset, step)?; // Inputs/Outputs - let index = block.rws[step.rw_indices[0]].stack_value().to_le_bytes(); - let value = block.rws[step.rw_indices[1]].stack_value().to_le_bytes(); + let index = block.get_rws(step, 0).stack_value().to_le_bytes(); + let value = block.get_rws(step, 1).stack_value().to_le_bytes(); self.index.assign(region, offset, Some(index))?; self.value.assign(region, offset, Some(value))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs b/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs index 3a2892a686..6d1fb475cc 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs @@ -1,17 +1,16 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::{N_BYTES_MEMORY_ADDRESS, N_BYTES_MEMORY_WORD_SIZE}, + param::{N_BYTES_MEMORY_WORD_SIZE, N_BYTES_U64}, step::ExecutionState, util::{ - common_gadget::SameContextGadget, + common_gadget::{SameContextGadget, WordByteCapGadget}, constraint_builder::{ - ConstraintBuilder, StepStateTransition, + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::{Delta, To}, }, - from_bytes, memory_gadget::{MemoryAddressGadget, MemoryCopierGasGadget, MemoryExpansionGadget}, - not, select, CachedRegion, Cell, MemoryAddress, + not, select, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, @@ -19,7 +18,7 @@ use crate::{ util::Expr, }; use bus_mapping::{circuit_input_builder::CopyDataType, evm::OpcodeId}; -use eth_types::{evm_types::GasCost, Field, ToLittleEndian, ToScalar}; +use eth_types::{evm_types::GasCost, Field, ToScalar}; use halo2_proofs::{circuit::Value, plonk::Error}; use std::cmp::min; @@ -28,7 +27,7 @@ use std::cmp::min; pub(crate) struct CallDataCopyGadget { same_context: SameContextGadget, memory_address: MemoryAddressGadget, - data_offset: MemoryAddress, + data_offset: WordByteCapGadget, src_id: Cell, call_data_length: Cell, call_data_offset: Cell, // Only used in the internal call @@ -42,23 +41,22 @@ impl ExecutionGadget for CallDataCopyGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::CALLDATACOPY; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let memory_offset = cb.query_cell_phase2(); - let data_offset = cb.query_word_rlc(); + let src_id = cb.query_cell(); + let call_data_length = cb.query_cell(); + let call_data_offset = cb.query_cell(); + let length = cb.query_word_rlc(); + let memory_offset = cb.query_cell_phase2(); + let data_offset = WordByteCapGadget::construct(cb, call_data_length.expr()); // Pop memory_offset, data_offset, length from stack cb.stack_pop(memory_offset.expr()); - cb.stack_pop(data_offset.expr()); + cb.stack_pop(data_offset.original_word()); cb.stack_pop(length.expr()); - let memory_address = MemoryAddressGadget::construct(cb, memory_offset, length); - let src_id = cb.query_cell(); - let call_data_length = cb.query_cell(); - let call_data_offset = cb.query_cell(); - // Lookup the calldata_length and caller_address in Tx context table or // Call context table cb.condition(cb.curr.state.is_root.expr(), |cb| { @@ -97,6 +95,7 @@ impl ExecutionGadget for CallDataCopyGadget { // Calculate the next memory size and the gas cost for this memory // access + let memory_address = MemoryAddressGadget::construct(cb, memory_offset, length); let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); let memory_copier_gas = MemoryCopierGasGadget::construct( cb, @@ -111,13 +110,23 @@ impl ExecutionGadget for CallDataCopyGadget { CopyDataType::Memory.expr(), ); cb.condition(memory_address.has_length(), |cb| { + // Set source start to the minimun value of data offset and call data length. + let src_addr = call_data_offset.expr() + + select::expr( + data_offset.lt_cap(), + data_offset.valid_value(), + call_data_length.expr(), + ); + + let src_addr_end = call_data_offset.expr() + call_data_length.expr(); + cb.copy_table_lookup( src_id.expr(), src_tag, cb.curr.state.call_id.expr(), CopyDataType::Memory.expr(), - call_data_offset.expr() + from_bytes::expr(&data_offset.cells), - call_data_offset.expr() + call_data_length.expr(), + src_addr, + src_addr_end, memory_address.offset(), memory_address.length(), 0.expr(), // for CALLDATACOPY rlc_acc is 0 @@ -170,20 +179,10 @@ impl ExecutionGadget for CallDataCopyGadget { self.same_context.assign_exec_step(region, offset, step)?; let [memory_offset, data_offset, length] = - [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] - .map(|idx| block.rws[idx].stack_value()); + [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); let memory_address = self .memory_address .assign(region, offset, memory_offset, length)?; - self.data_offset.assign( - region, - offset, - Some( - data_offset.to_le_bytes()[..N_BYTES_MEMORY_ADDRESS] - .try_into() - .unwrap(), - ), - )?; let src_id = if call.is_root { tx.id } else { call.caller_id }; self.src_id.assign( region, @@ -202,6 +201,9 @@ impl ExecutionGadget for CallDataCopyGadget { self.call_data_offset .assign(region, offset, Value::known(F::from(call_data_offset)))?; + self.data_offset + .assign(region, offset, data_offset, F::from(call_data_length))?; + // rw_counter increase from copy lookup is `length` memory writes + a variable // number of memory reads. let copy_rwc_inc = length @@ -212,9 +214,10 @@ impl ExecutionGadget for CallDataCopyGadget { // memory reads when reading from memory of caller is capped by call_data_length // - data_offset. min( - length.low_u64(), - call_data_length - .checked_sub(data_offset.low_u64()) + length.as_u64(), + u64::try_from(data_offset) + .ok() + .and_then(|offset| call_data_length.checked_sub(offset)) .unwrap_or_default(), ) }; @@ -240,7 +243,7 @@ impl ExecutionGadget for CallDataCopyGadget { region, offset, length.as_u64(), - memory_expansion_gas_cost as u64, + memory_expansion_gas_cost, )?; Ok(()) @@ -251,14 +254,18 @@ impl ExecutionGadget for CallDataCopyGadget { mod test { use crate::{evm_circuit::test::rand_bytes, test_util::CircuitTestBuilder}; use bus_mapping::circuit_input_builder::CircuitsParams; - use eth_types::{bytecode, ToWord, Word}; - use mock::test_ctx::{helpers::*, TestContext}; - - fn test_ok_root( + use eth_types::{bytecode, Word}; + use mock::{ + generate_mock_call_bytecode, + test_ctx::{helpers::*, TestContext}, + MockCallBytecodeParams, + }; + + fn test_root_ok( call_data_length: usize, - memory_offset: usize, - data_offset: usize, length: usize, + data_offset: Word, + memory_offset: Word, ) { let bytecode = bytecode! { PUSH32(length) @@ -292,42 +299,28 @@ mod test { .run(); } - fn test_ok_internal( + fn test_internal_ok( call_data_offset: usize, call_data_length: usize, - dst_offset: usize, - offset: usize, length: usize, + data_offset: Word, + dst_offset: Word, ) { let (addr_a, addr_b) = (mock::MOCK_ACCOUNTS[0], mock::MOCK_ACCOUNTS[1]); // code B gets called by code A, so the call is an internal call. let code_b = bytecode! { - PUSH32(length) // size - PUSH32(offset) // offset - PUSH32(dst_offset) // dst_offset - CALLDATACOPY + .op_calldatacopy(dst_offset, data_offset, length) STOP }; - // code A calls code B. - let pushdata = rand_bytes(8); - let code_a = bytecode! { - // populate memory in A's context. - PUSH8(Word::from_big_endian(&pushdata)) - PUSH1(0x00) // offset - MSTORE - // call ADDR_B. - PUSH1(0x00) // retLength - PUSH1(0x00) // retOffset - PUSH32(call_data_length) // argsLength - PUSH32(call_data_offset) // argsOffset - PUSH1(0x00) // value - PUSH32(addr_b.to_word()) // addr - PUSH32(0x1_0000) // gas - CALL - STOP - }; + let code_a = generate_mock_call_bytecode(MockCallBytecodeParams { + address: addr_b, + pushdata: rand_bytes(32), + call_data_length, + call_data_offset, + ..MockCallBytecodeParams::default() + }); let ctx = TestContext::<3, 1>::new( None, @@ -350,25 +343,37 @@ mod test { #[test] fn calldatacopy_gadget_simple() { - test_ok_root(0x40, 0x40, 0x00, 10); - test_ok_internal(0x40, 0x40, 0xA0, 0x10, 10); + test_root_ok(0x40, 10, 0x00.into(), 0x40.into()); + test_internal_ok(0x40, 0x40, 10, 0x10.into(), 0xA0.into()); } #[test] fn calldatacopy_gadget_large() { - test_ok_root(0x204, 0x103, 0x102, 0x101); - test_ok_internal(0x30, 0x204, 0x103, 0x102, 0x101); + test_root_ok(0x204, 0x101, 0x102.into(), 0x103.into()); + test_internal_ok(0x30, 0x204, 0x101, 0x102.into(), 0x103.into()); } #[test] fn calldatacopy_gadget_out_of_bound() { - test_ok_root(0x40, 0x40, 0x20, 40); - test_ok_internal(0x40, 0x20, 0xA0, 0x28, 10); + test_root_ok(0x40, 40, 0x20.into(), 0x40.into()); + test_internal_ok(0x40, 0x20, 10, 0x28.into(), 0xA0.into()); } #[test] fn calldatacopy_gadget_zero_length() { - test_ok_root(0x40, 0x40, 0x00, 0); - test_ok_internal(0x40, 0x40, 0xA0, 0x10, 0); + test_root_ok(0x40, 0, 0x00.into(), 0x40.into()); + test_internal_ok(0x40, 0x40, 0, 0x10.into(), 0xA0.into()); + } + + #[test] + fn calldatacopy_gadget_data_offset_overflow() { + test_root_ok(0x40, 10, Word::MAX, 0x40.into()); + test_internal_ok(0x40, 0x40, 10, Word::MAX, 0xA0.into()); + } + + #[test] + fn calldatacopy_gadget_overflow_memory_offset_and_zero_length() { + test_root_ok(0x40, 0, 0x40.into(), Word::MAX); + test_internal_ok(0x40, 0x40, 0, 0x10.into(), Word::MAX); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/calldataload.rs b/zkevm-circuits/src/evm_circuit/execution/calldataload.rs index a18f69cc6b..555a105667 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldataload.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldataload.rs @@ -1,5 +1,5 @@ use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToLittleEndian}; +use eth_types::Field; use halo2_proofs::{ circuit::Value, plonk::{Error, Expression}, @@ -7,14 +7,17 @@ use halo2_proofs::{ use crate::{ evm_circuit::{ - param::{N_BYTES_MEMORY_ADDRESS, N_BYTES_WORD}, + param::{N_BYTES_MEMORY_ADDRESS, N_BYTES_U64, N_BYTES_WORD}, step::ExecutionState, util::{ - common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, - from_bytes, + and, + common_gadget::{SameContextGadget, WordByteCapGadget}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, + Transition::Delta, + }, memory_gadget::BufferReaderGadget, - not, CachedRegion, Cell, MemoryAddress, + not, select, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, @@ -34,8 +37,6 @@ pub(crate) struct CallDataLoadGadget { /// Source of data, this is transaction ID for a root call and caller ID for /// an internal call. src_id: Cell, - /// The bytes offset in calldata, from which we load a 32-bytes word. - offset: MemoryAddress, /// The size of the call's data (tx input for a root call or calldata length /// of an internal call). call_data_length: Cell, @@ -43,6 +44,9 @@ pub(crate) struct CallDataLoadGadget { /// tx data starts at the first byte, but can be non-zero offset for an /// internal call. call_data_offset: Cell, + /// The bytes offset in calldata, from which we load a 32-bytes word. It + /// is valid if within range of Uint64 and less than call_data_length. + data_offset: WordByteCapGadget, /// Gadget to read from tx calldata, which we validate against the word /// pushed to stack. buffer_reader: BufferReaderGadget, @@ -53,64 +57,87 @@ impl ExecutionGadget for CallDataLoadGadget { const NAME: &'static str = "CALLDATALOAD"; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let offset = cb.query_word_rlc(); - - // Pop the offset value from stack. - cb.stack_pop(offset.expr()); - - // Add a lookup constrain for TxId in the RW table. let src_id = cb.query_cell(); let call_data_length = cb.query_cell(); let call_data_offset = cb.query_cell(); - let src_addr = from_bytes::expr(&offset.cells) + call_data_offset.expr(); - let src_addr_end = call_data_length.expr() + call_data_offset.expr(); + let data_offset = WordByteCapGadget::construct(cb, call_data_length.expr()); + cb.stack_pop(data_offset.original_word()); + + cb.condition( + and::expr([data_offset.not_overflow(), cb.curr.state.is_root.expr()]), + |cb| { + cb.call_context_lookup( + false.expr(), + None, + CallContextFieldTag::TxId, + src_id.expr(), + ); + cb.call_context_lookup( + false.expr(), + None, + CallContextFieldTag::CallDataLength, + call_data_length.expr(), + ); + cb.require_equal( + "if is_root then call_data_offset == 0", + call_data_offset.expr(), + 0.expr(), + ); + }, + ); - cb.condition(cb.curr.state.is_root.expr(), |cb| { - cb.call_context_lookup(false.expr(), None, CallContextFieldTag::TxId, src_id.expr()); - cb.call_context_lookup( - false.expr(), - None, - CallContextFieldTag::CallDataLength, - call_data_length.expr(), - ); - cb.require_equal( - "if is_root then call_data_offset == 0", - call_data_offset.expr(), - 0.expr(), - ); - }); - cb.condition(not::expr(cb.curr.state.is_root.expr()), |cb| { - cb.call_context_lookup( - false.expr(), - None, - CallContextFieldTag::CallerId, - src_id.expr(), - ); - cb.call_context_lookup( - false.expr(), - None, - CallContextFieldTag::CallDataLength, + cb.condition( + and::expr([ + data_offset.not_overflow(), + not::expr(cb.curr.state.is_root.expr()), + ]), + |cb| { + cb.call_context_lookup( + false.expr(), + None, + CallContextFieldTag::CallerId, + src_id.expr(), + ); + cb.call_context_lookup( + false.expr(), + None, + CallContextFieldTag::CallDataLength, + call_data_length.expr(), + ); + cb.call_context_lookup( + false.expr(), + None, + CallContextFieldTag::CallDataOffset, + call_data_offset.expr(), + ); + }, + ); + + // Set source start to the minimun value of data offset and call data length. + let src_addr = call_data_offset.expr() + + select::expr( + data_offset.lt_cap(), + data_offset.valid_value(), call_data_length.expr(), ); - cb.call_context_lookup( - false.expr(), - None, - CallContextFieldTag::CallDataOffset, - call_data_offset.expr(), - ); - }); - let buffer_reader = BufferReaderGadget::construct(cb, src_addr.clone(), src_addr_end); + let src_addr_end = call_data_offset.expr() + call_data_length.expr(); - let mut calldata_word = (0..N_BYTES_WORD) + let buffer_reader = BufferReaderGadget::construct(cb, src_addr.expr(), src_addr_end); + + let mut calldata_word: Vec<_> = (0..N_BYTES_WORD) .map(|idx| { - // for a root call, the call data comes from tx's data field. + // For a root call, the call data comes from tx's data field. cb.condition( - cb.curr.state.is_root.expr() * buffer_reader.read_flag(idx), + and::expr([ + data_offset.not_overflow(), + buffer_reader.read_flag(idx), + cb.curr.state.is_root.expr(), + ]), |cb| { cb.tx_context_lookup( src_id.expr(), @@ -120,9 +147,13 @@ impl ExecutionGadget for CallDataLoadGadget { ); }, ); - // for an internal call, the call data comes from memory. + // For an internal call, the call data comes from memory. cb.condition( - (1.expr() - cb.curr.state.is_root.expr()) * buffer_reader.read_flag(idx), + and::expr([ + data_offset.not_overflow(), + buffer_reader.read_flag(idx), + not::expr(cb.curr.state.is_root.expr()), + ]), |cb| { cb.memory_lookup( 0.expr(), @@ -134,7 +165,7 @@ impl ExecutionGadget for CallDataLoadGadget { ); buffer_reader.byte(idx) }) - .collect::>>(); + .collect(); // Since the stack items are in little endian form, we reverse the bytes // here. @@ -143,7 +174,13 @@ impl ExecutionGadget for CallDataLoadGadget { // Add a lookup constraint for the 32-bytes that should have been pushed // to the stack. let calldata_word: [Expression; N_BYTES_WORD] = calldata_word.try_into().unwrap(); - cb.stack_push(cb.word_rlc(calldata_word)); + let calldata_word = cb.word_rlc(calldata_word); + cb.require_zero( + "Stack push result must be 0 if stack pop offset is Uint64 overflow", + data_offset.overflow() * calldata_word.expr(), + ); + + cb.stack_push(calldata_word); let step_state_transition = StepStateTransition { rw_counter: Delta(cb.rw_counter_offset()), @@ -157,10 +194,10 @@ impl ExecutionGadget for CallDataLoadGadget { Self { same_context, - offset, src_id, call_data_length, call_data_offset, + data_offset, buffer_reader, } } @@ -176,62 +213,59 @@ impl ExecutionGadget for CallDataLoadGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - // set the value for bytes offset in calldata. This is where we start - // reading bytes from. - let data_offset = block.rws[step.rw_indices[0]].stack_value(); - - // assign the calldata start and end cells. - self.offset.assign( - region, - offset, - Some( - data_offset.to_le_bytes()[..N_BYTES_MEMORY_ADDRESS] - .try_into() - .unwrap(), - ), - )?; - - // assign to the buffer reader gadget. - let (calldata_length, calldata_offset, src_id) = if call.is_root { - (tx.call_data_length as u64, 0u64, tx.id as u64) + // Assign to the buffer reader gadget. + let (src_id, call_data_offset, call_data_length) = if call.is_root { + (tx.id, 0, tx.call_data_length as u64) } else { - ( - call.call_data_length, - call.call_data_offset, - call.caller_id as u64, - ) + (call.caller_id, call.call_data_offset, call.call_data_length) }; self.src_id - .assign(region, offset, Value::known(F::from(src_id)))?; + .assign(region, offset, Value::known(F::from(src_id as u64)))?; self.call_data_length - .assign(region, offset, Value::known(F::from(calldata_length)))?; + .assign(region, offset, Value::known(F::from(call_data_length)))?; self.call_data_offset - .assign(region, offset, Value::known(F::from(calldata_offset)))?; + .assign(region, offset, Value::known(F::from(call_data_offset)))?; - let mut calldata_bytes = vec![0u8; N_BYTES_WORD]; - let (src_addr, src_addr_end) = ( - data_offset.as_usize() + calldata_offset as usize, - calldata_length as usize + calldata_offset as usize, - ); + let data_offset = block.get_rws(step, 0).stack_value(); + let offset_not_overflow = + self.data_offset + .assign(region, offset, data_offset, F::from(call_data_length))?; - for (i, byte) in calldata_bytes.iter_mut().enumerate() { - if call.is_root { - // fetch from tx call data - if src_addr + i < tx.call_data_length { - *byte = tx.call_data[src_addr + i]; - } - } else { - // fetch from memory - if src_addr + i < (call.call_data_offset + call.call_data_length) as usize { - *byte = block.rws[step.rw_indices[OFFSET_RW_MEMORY_INDICES + i]].memory_value(); + let data_offset = if offset_not_overflow { + data_offset.as_u64() + } else { + call_data_length + }; + let src_addr_end = call_data_offset + call_data_length; + let src_addr = call_data_offset + .checked_add(data_offset) + .unwrap_or(src_addr_end) + .min(src_addr_end); + + let mut calldata_bytes = vec![0u8; N_BYTES_WORD]; + if offset_not_overflow { + for (i, byte) in calldata_bytes.iter_mut().enumerate() { + if call.is_root { + // Fetch from tx call data. + if src_addr + (i as u64) < tx.call_data_length as u64 { + *byte = tx.call_data[src_addr as usize + i]; + } + } else { + // Fetch from memory. + if src_addr + (i as u64) < call.call_data_offset + call.call_data_length { + *byte = block + .get_rws(step, OFFSET_RW_MEMORY_INDICES + i) + .memory_value(); + } } } } + self.buffer_reader.assign( region, offset, - src_addr as u64, - src_addr_end as u64, + src_addr, + src_addr_end, &calldata_bytes, &[true; N_BYTES_WORD], )?; @@ -243,15 +277,19 @@ impl ExecutionGadget for CallDataLoadGadget { #[cfg(test)] mod test { use crate::{evm_circuit::test::rand_bytes, test_util::CircuitTestBuilder}; - use eth_types::{bytecode, ToWord, Word}; - use mock::TestContext; + use eth_types::{bytecode, Word}; + use mock::{generate_mock_call_bytecode, MockCallBytecodeParams, TestContext}; - fn test_root_ok(offset: usize) { - let bytecode = bytecode! { - PUSH32(Word::from(offset)) + fn test_bytecode(offset: Word) -> eth_types::Bytecode { + bytecode! { + PUSH32(offset) CALLDATALOAD STOP - }; + } + } + + fn test_root_ok(offset: Word) { + let bytecode = test_bytecode(offset); CircuitTestBuilder::new_from_test_ctx( TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), @@ -259,33 +297,18 @@ mod test { .run(); } - fn test_internal_ok(call_data_length: usize, call_data_offset: usize, offset: usize) { + fn test_internal_ok(call_data_length: usize, call_data_offset: usize, offset: Word) { let (addr_a, addr_b) = (mock::MOCK_ACCOUNTS[0], mock::MOCK_ACCOUNTS[1]); // code B gets called by code A, so the call is an internal call. - let code_b = bytecode! { - PUSH32(Word::from(offset)) - CALLDATALOAD - STOP - }; - - let pushdata = rand_bytes(32); - let code_a = bytecode! { - // populate memory in A's context. - PUSH32(Word::from_big_endian(&pushdata)) - PUSH1(0x00) // offset - MSTORE - // call addr_b - PUSH1(0x00) // retLength - PUSH1(0x00) // retOffset - PUSH32(call_data_length) // argsLength - PUSH32(call_data_offset) // argsOffset - PUSH1(0x00) // value - PUSH32(addr_b.to_word()) // addr - PUSH32(0x1_0000) // gas - CALL - STOP - }; + let code_b = test_bytecode(offset); + let code_a = generate_mock_call_bytecode(MockCallBytecodeParams { + address: addr_b, + pushdata: rand_bytes(32), + call_data_length, + call_data_offset, + ..MockCallBytecodeParams::default() + }); let ctx = TestContext::<3, 1>::new( None, @@ -308,17 +331,23 @@ mod test { #[test] fn calldataload_gadget_root() { - test_root_ok(0x00); - test_root_ok(0x08); - test_root_ok(0x10); - test_root_ok(0x2010); + test_root_ok(0x00.into()); + test_root_ok(0x08.into()); + test_root_ok(0x10.into()); + test_root_ok(0x2010.into()); } #[test] fn calldataload_gadget_internal() { - test_internal_ok(0x20, 0x00, 0x00); - test_internal_ok(0x20, 0x10, 0x10); - test_internal_ok(0x40, 0x20, 0x08); - test_internal_ok(0x1010, 0xff, 0x10); + test_internal_ok(0x20, 0x00, 0x00.into()); + test_internal_ok(0x20, 0x10, 0x10.into()); + test_internal_ok(0x40, 0x20, 0x08.into()); + test_internal_ok(0x1010, 0xff, 0x10.into()); + } + + #[test] + fn calldataload_gadget_offset_overflow() { + test_root_ok(Word::MAX); + test_internal_ok(0x1010, 0xff, Word::MAX); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs b/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs index c9d36227a5..dc6028e53b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs @@ -5,7 +5,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, from_bytes, CachedRegion, RandomLinearCombination, }, witness::{Block, Call, ExecStep, Transaction}, @@ -28,7 +28,7 @@ impl ExecutionGadget for CallDataSizeGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::CALLDATASIZE; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); // Add lookup constraint in the call context for the calldatasize field. @@ -70,7 +70,7 @@ impl ExecutionGadget for CallDataSizeGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let call_data_size = block.rws[step.rw_indices[1]].stack_value(); + let call_data_size = block.get_rws(step, 1).stack_value(); self.call_data_size.assign( region, diff --git a/zkevm-circuits/src/evm_circuit/execution/caller.rs b/zkevm-circuits/src/evm_circuit/execution/caller.rs index 6ea37242db..a57b97dc8e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/caller.rs +++ b/zkevm-circuits/src/evm_circuit/execution/caller.rs @@ -5,7 +5,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, from_bytes, CachedRegion, RandomLinearCombination, }, witness::{Block, Call, ExecStep, Transaction}, @@ -29,7 +29,7 @@ impl ExecutionGadget for CallerGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::CALLER; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let caller_address = cb.query_word_rlc(); // Lookup rw_table -> call_context with caller address @@ -71,7 +71,7 @@ impl ExecutionGadget for CallerGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let caller = block.rws[step.rw_indices[1]].stack_value(); + let caller = block.get_rws(step, 1).stack_value(); self.caller_address.assign( region, diff --git a/zkevm-circuits/src/evm_circuit/execution/callop.rs b/zkevm-circuits/src/evm_circuit/execution/callop.rs index d8058324e2..9ca15f7859 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callop.rs @@ -1,14 +1,15 @@ use crate::evm_circuit::{ execution::ExecutionGadget, - param::N_BYTES_GAS, + param::{N_BYTES_GAS, N_BYTES_U64}, step::ExecutionState, util::{ + and, common_gadget::{CommonCallGadget, TransferGadget}, constraint_builder::{ - ConstraintBuilder, ReversionInfo, StepStateTransition, + ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition::{Delta, To}, }, - math_gadget::{ConstantDivisionGadget, IsZeroGadget, LtWordGadget, MinMaxGadget}, + math_gadget::{ConstantDivisionGadget, IsZeroGadget, LtGadget, LtWordGadget, MinMaxGadget}, not, or, select, CachedRegion, Cell, Word, }, }; @@ -49,6 +50,7 @@ pub(crate) struct CallOpGadget { caller_balance_word: Word, // check if insufficient balance case is_insufficient_balance: LtWordGadget, + is_depth_ok: LtGadget, one_64th_gas: ConstantDivisionGadget, capped_callee_gas_left: MinMaxGadget, } @@ -58,7 +60,7 @@ impl ExecutionGadget for CallOpGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::CALL_OP; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); cb.opcode_lookup(opcode.expr(), 1.expr()); let is_call = IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::CALL.expr()); @@ -87,8 +89,6 @@ impl ExecutionGadget for CallOpGadget { ) }); - cb.range_lookup(depth.expr(), 1024); - let call_gadget = CommonCallGadget::construct( cb, is_call.expr(), @@ -155,9 +155,16 @@ impl ExecutionGadget for CallOpGadget { ); let is_insufficient_balance = LtWordGadget::construct(cb, &caller_balance_word, &call_gadget.value); + // depth < 1025 + let is_depth_ok = LtGadget::construct(cb, depth.expr(), 1025.expr()); + + let is_precheck_ok = and::expr([ + is_depth_ok.expr(), + not::expr(is_insufficient_balance.expr()), + ]); // stack write is zero when is_insufficient_balance is true - cb.condition(is_insufficient_balance.expr(), |cb| { + cb.condition(not::expr(is_precheck_ok.expr()), |cb| { cb.require_zero( "stack write result is zero when is_insufficient_balance is true", call_gadget.is_success.expr(), @@ -168,19 +175,16 @@ impl ExecutionGadget for CallOpGadget { // skip the transfer (this is necessary for non-existing accounts, which // will not be crated when value is 0 and so the callee balance lookup // would be invalid). - let transfer = cb.condition( - is_call.expr() * not::expr(is_insufficient_balance.expr()), - |cb| { - TransferGadget::construct( - cb, - caller_address.expr(), - callee_address.expr(), - not::expr(call_gadget.callee_not_exists.expr()), - call_gadget.value.clone(), - &mut callee_reversion_info, - ) - }, - ); + let transfer = cb.condition(and::expr(&[is_call.expr(), is_precheck_ok.expr()]), |cb| { + TransferGadget::construct( + cb, + caller_address.expr(), + callee_address.expr(), + not::expr(call_gadget.callee_not_exists.expr()), + call_gadget.value.clone(), + &mut callee_reversion_info, + ) + }); // For CALLCODE opcode, verify caller balance is greater than or equal to stack // `value` in successful case. that is `is_insufficient_balance` is false. @@ -219,7 +223,7 @@ impl ExecutionGadget for CallOpGadget { select::expr(is_call.expr() + is_callcode.expr(), 6.expr(), 5.expr()); let memory_expansion = call_gadget.memory_expansion.clone(); cb.condition( - no_callee_code.clone() * not::expr(is_insufficient_balance.expr()), + and::expr(&[no_callee_code.expr(), is_precheck_ok.expr()]), |cb| { // Save caller's call state for field_tag in [ @@ -265,7 +269,7 @@ impl ExecutionGadget for CallOpGadget { ); // handle is_insufficient_balance step transition - cb.condition(is_insufficient_balance.expr(), |cb| { + cb.condition(not::expr(is_precheck_ok.expr()), |cb| { // Save caller's call state for field_tag in [ CallContextFieldTag::LastCalleeId, @@ -290,7 +294,7 @@ impl ExecutionGadget for CallOpGadget { }); cb.condition( - not::expr(no_callee_code) * not::expr(is_insufficient_balance.expr()), + and::expr(&[not::expr(no_callee_code), is_precheck_ok.expr()]), |cb| { // Save caller's call state for (field_tag, value) in [ @@ -421,6 +425,7 @@ impl ExecutionGadget for CallOpGadget { transfer, caller_balance_word, is_insufficient_balance, + is_depth_ok, one_64th_gas, capped_callee_gas_left, } @@ -435,19 +440,15 @@ impl ExecutionGadget for CallOpGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let is_call = opcode == OpcodeId::CALL; let is_callcode = opcode == OpcodeId::CALLCODE; let is_delegatecall = opcode == OpcodeId::DELEGATECALL; - let [tx_id, is_static, depth, current_callee_address] = [ - step.rw_indices[0], - step.rw_indices[3], - step.rw_indices[4], - step.rw_indices[5], - ] - .map(|idx| block.rws[idx].call_context_value()); - let stack_index = 6; - + let [tx_id, is_static, depth, current_callee_address] = + [0, 3, 4, 5].map(|index| block.get_rws(step, index).call_context_value()); + let is_error_depth = depth.low_u64() > 1024; + self.is_depth_ok + .assign(region, offset, F::from(depth.low_u64()), F::from(1025))?; // This offset is used to change the index offset of `step.rw_indices`. // Since both CALL and CALLCODE have an extra stack pop `value`, and // opcode DELEGATECALL has two extra call context lookups - current @@ -455,46 +456,34 @@ impl ExecutionGadget for CallOpGadget { let mut rw_offset = 0; let [current_caller_address, current_value] = if is_delegatecall { rw_offset += 2; - [step.rw_indices[6], step.rw_indices[7]].map(|idx| block.rws[idx].call_context_value()) + [6, 7].map(|index| block.get_rws(step, index).call_context_value()) } else { [U256::zero(), U256::zero()] }; - let [gas, callee_address] = [ - step.rw_indices[stack_index + rw_offset], - step.rw_indices[stack_index + 1 + rw_offset], - ] - .map(|idx| block.rws[idx].stack_value()); + let [gas, callee_address] = + [6, 7].map(|i| block.get_rws(step, i + rw_offset).stack_value()); let value = if is_call || is_callcode { + let value = block.get_rws(step, 8 + rw_offset).stack_value(); rw_offset += 1; - block.rws[step.rw_indices[7 + rw_offset]].stack_value() + value } else { U256::zero() }; - let [cd_offset, cd_length, rd_offset, rd_length, is_success] = [ - step.rw_indices[stack_index + 2 + rw_offset], - step.rw_indices[stack_index + 3 + rw_offset], - step.rw_indices[stack_index + 4 + rw_offset], - step.rw_indices[stack_index + 5 + rw_offset], - step.rw_indices[stack_index + 6 + rw_offset], - ] - .map(|idx| block.rws[idx].stack_value()); - let callee_code_hash = block.rws[step.rw_indices[13 + rw_offset]] - .account_value_pair() - .0; + let [cd_offset, cd_length, rd_offset, rd_length, is_success] = + [8, 9, 10, 11, 12].map(|i| block.get_rws(step, i + rw_offset).stack_value()); + let callee_code_hash = block.get_rws(step, 13 + rw_offset).account_value_pair().0; let callee_exists = !callee_code_hash.is_zero(); - let (is_warm, is_warm_prev) = - block.rws[step.rw_indices[14 + rw_offset]].tx_access_list_value_pair(); + let (is_warm, is_warm_prev) = block + .get_rws(step, 14 + rw_offset) + .tx_access_list_value_pair(); - let [callee_rw_counter_end_of_reversion, callee_is_persistent] = [ - step.rw_indices[15 + rw_offset], - step.rw_indices[16 + rw_offset], - ] - .map(|idx| block.rws[idx].call_context_value()); + let [callee_rw_counter_end_of_reversion, callee_is_persistent] = + [15, 16].map(|index| block.get_rws(step, index + rw_offset).call_context_value()); // check if it is insufficient balance case. // get caller balance - let (caller_balance, _) = block.rws[step.rw_indices[17 + rw_offset]].account_value_pair(); + let (caller_balance, _) = block.get_rws(step, 17 + rw_offset).account_value_pair(); self.caller_balance_word .assign(region, offset, Some(caller_balance.to_le_bytes()))?; self.is_insufficient_balance @@ -502,15 +491,11 @@ impl ExecutionGadget for CallOpGadget { let is_insufficient = (value > caller_balance) && (is_call || is_callcode); // only call opcode do transfer in sucessful case. - let (caller_balance_pair, callee_balance_pair) = - if is_call && !is_insufficient && !value.is_zero() { - rw_offset += 2; - ( - block.rws[step.rw_indices[16 + rw_offset]].account_value_pair(), - block.rws[step.rw_indices[17 + rw_offset]].account_value_pair(), - ) + let [caller_balance_pair, callee_balance_pair] = + if is_call && !is_insufficient && !is_error_depth && !value.is_zero() { + [18, 19].map(|index| block.get_rws(step, index + rw_offset).account_value_pair()) } else { - ((U256::zero(), U256::zero()), (U256::zero(), U256::zero())) + [(U256::zero(), U256::zero()), (U256::zero(), U256::zero())] }; self.opcode @@ -593,7 +578,7 @@ impl ExecutionGadget for CallOpGadget { callee_is_persistent.low_u64() != 0, )?; // conditionally assign - if !is_insufficient && !value.is_zero() { + if !is_insufficient && !is_error_depth && !value.is_zero() { self.transfer.assign( region, offset, @@ -611,7 +596,7 @@ impl ExecutionGadget for CallOpGadget { has_value, !callee_exists, )?; - let gas_available = step.gas_left - gas_cost; + let gas_available: u64 = step.gas_left - gas_cost; self.one_64th_gas .assign(region, offset, gas_available.into())?; @@ -632,11 +617,12 @@ mod test { use crate::test_util::CircuitTestBuilder; use bus_mapping::circuit_input_builder::CircuitsParams; use eth_types::{ - address, bytecode, evm_types::OpcodeId, geth_types::Account, Address, ToWord, Word, + address, bytecode, evm_types::OpcodeId, geth_types::Account, word, Address, ToWord, Word, + U64, }; use itertools::Itertools; - use mock::TestContext; + use mock::{test_ctx::helpers::account_0_code_account_1_no_code, TestContext}; use std::default::Default; @@ -705,31 +691,31 @@ mod test { }, // With memory expansion Stack { - cd_offset: 64, + cd_offset: 64.into(), cd_length: 320, - rd_offset: 0, + rd_offset: Word::zero(), rd_length: 32, ..Default::default() }, Stack { - cd_offset: 0, + cd_offset: Word::zero(), cd_length: 32, - rd_offset: 64, + rd_offset: 64.into(), rd_length: 320, ..Default::default() }, Stack { - cd_offset: 0xFFFFFF, + cd_offset: 0xFFFFFF.into(), cd_length: 0, - rd_offset: 0xFFFFFF, + rd_offset: 0xFFFFFF.into(), rd_length: 0, ..Default::default() }, // With memory expansion and value Stack { - cd_offset: 64, + cd_offset: 64.into(), cd_length: 320, - rd_offset: 0, + rd_offset: 0.into(), rd_length: 32, value: Word::from(10).pow(18.into()), ..Default::default() @@ -746,13 +732,28 @@ mod test { } } + #[test] + fn callop_overflow_offset_and_zero_length() { + let stack = Stack { + cd_offset: Word::MAX, + cd_length: 0, + rd_offset: Word::MAX, + rd_length: 0, + ..Default::default() + }; + + TEST_CALL_OPCODES + .iter() + .for_each(|opcode| test_ok(caller(opcode, stack, true), callee(bytecode! {}))); + } + #[derive(Clone, Copy, Debug, Default)] struct Stack { gas: u64, value: Word, - cd_offset: u64, + cd_offset: Word, cd_length: u64, - rd_offset: u64, + rd_offset: Word, rd_length: u64, } @@ -762,7 +763,7 @@ mod test { Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } @@ -779,9 +780,9 @@ mod test { // Call twice for testing both cold and warm access let mut bytecode = bytecode! { PUSH32(Word::from(stack.rd_length)) - PUSH32(Word::from(stack.rd_offset)) + PUSH32(stack.rd_offset) PUSH32(Word::from(stack.cd_length)) - PUSH32(Word::from(stack.cd_offset)) + PUSH32(stack.cd_offset) }; if is_call_or_callcode { bytecode.push(32, stack.value); @@ -791,9 +792,9 @@ mod test { PUSH32(Word::from(stack.gas)) .write_op(*opcode) PUSH32(Word::from(stack.rd_length)) - PUSH32(Word::from(stack.rd_offset)) + PUSH32(stack.rd_offset) PUSH32(Word::from(stack.cd_length)) - PUSH32(Word::from(stack.cd_offset)) + PUSH32(stack.cd_offset) }); if is_call_or_callcode { bytecode.push(32, stack.value); @@ -821,9 +822,9 @@ mod test { let mut bytecode = bytecode! { PUSH32(Word::from(stack.rd_length)) - PUSH32(Word::from(stack.rd_offset)) + PUSH32(stack.rd_offset) PUSH32(Word::from(stack.cd_length)) - PUSH32(Word::from(stack.cd_offset)) + PUSH32(stack.cd_offset) }; if is_call_or_callcode { bytecode.push(32, stack.value); @@ -889,16 +890,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(&caller); + accs[2].account(&callee); }, |mut txs, accs| { txs[0] @@ -989,4 +982,42 @@ mod test { callee(callee_bytecode), ); } + + #[test] + fn test_depth() { + let callee_code = bytecode! { + PUSH1(0x00) + PUSH1(0x00) + PUSH1(0x00) + PUSH1(0x00) + PUSH1(0x00) + ADDRESS + PUSH2(0xffff) + GAS + SUB + CALL + PUSH1(0x01) + SUB + }; + + let ctx = TestContext::<2, 1>::new( + None, + account_0_code_account_1_no_code(callee_code), + |mut txs, accs| { + txs[0] + .to(accs[0].address) + .from(accs[1].address) + .gas(word!("0x2386F26FC10000")); + }, + |block, _tx| block.number(0xcafeu64), + ) + .unwrap(); + + CircuitTestBuilder::new_from_test_ctx(ctx) + .params(CircuitsParams { + max_rws: 300000, + ..Default::default() + }) + .run(); + } } diff --git a/zkevm-circuits/src/evm_circuit/execution/callvalue.rs b/zkevm-circuits/src/evm_circuit/execution/callvalue.rs index cbb8d9345d..237c954096 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callvalue.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callvalue.rs @@ -4,7 +4,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, @@ -29,7 +29,7 @@ impl ExecutionGadget for CallValueGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::CALLVALUE; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let call_value = cb.query_cell_phase2(); // Lookup rw_table -> call_context with call value @@ -71,7 +71,7 @@ impl ExecutionGadget for CallValueGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let call_value = block.rws[step.rw_indices[1]].stack_value(); + let call_value = block.get_rws(step, 1).stack_value(); self.call_value .assign(region, offset, region.word_rlc(call_value))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/chainid.rs b/zkevm-circuits/src/evm_circuit/execution/chainid.rs index d331aa78ec..e635b1e391 100644 --- a/zkevm-circuits/src/evm_circuit/execution/chainid.rs +++ b/zkevm-circuits/src/evm_circuit/execution/chainid.rs @@ -4,7 +4,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, @@ -27,7 +27,7 @@ impl ExecutionGadget for ChainIdGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::CHAINID; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let chain_id = cb.query_cell_phase2(); // Push the value to the stack @@ -63,7 +63,7 @@ impl ExecutionGadget for ChainIdGadget { step: &ExecStep, ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let chain_id = block.rws[step.rw_indices[0]].stack_value(); + let chain_id = block.get_rws(step, 0).stack_value(); self.chain_id .assign(region, offset, region.word_rlc(chain_id))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/codecopy.rs b/zkevm-circuits/src/evm_circuit/execution/codecopy.rs index 878d9f05b8..f47fef2936 100644 --- a/zkevm-circuits/src/evm_circuit/execution/codecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/codecopy.rs @@ -1,17 +1,18 @@ use bus_mapping::{circuit_input_builder::CopyDataType, evm::OpcodeId}; -use eth_types::{evm_types::GasCost, Field, ToLittleEndian, ToScalar}; +use eth_types::{evm_types::GasCost, Field, ToScalar, ToWord}; use halo2_proofs::{circuit::Value, plonk::Error}; use crate::{ evm_circuit::{ - param::{N_BYTES_MEMORY_ADDRESS, N_BYTES_MEMORY_WORD_SIZE}, + param::{N_BYTES_MEMORY_WORD_SIZE, N_BYTES_U64}, step::ExecutionState, util::{ - common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition}, - from_bytes, + common_gadget::{SameContextGadget, WordByteCapGadget}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition, + }, memory_gadget::{MemoryAddressGadget, MemoryCopierGasGadget, MemoryExpansionGadget}, - not, CachedRegion, Cell, MemoryAddress, + not, select, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, @@ -23,8 +24,9 @@ use super::ExecutionGadget; #[derive(Clone, Debug)] pub(crate) struct CodeCopyGadget { same_context: SameContextGadget, - /// Holds the memory address for the offset in code from where we read. - code_offset: MemoryAddress, + /// Holds the memory address for the offset in code from where we + /// read. It is valid if within range of Uint64 and less than code_size. + code_offset: WordByteCapGadget, /// Holds the size of the current environment's bytecode. code_size: Cell, /// The code from current environment is copied to memory. To verify this @@ -46,17 +48,18 @@ impl ExecutionGadget for CodeCopyGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::CODECOPY; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - // Query elements to be popped from the stack. - let dst_memory_offset = cb.query_cell_phase2(); - let code_offset = cb.query_word_rlc(); + let code_size = cb.query_cell(); + let size = cb.query_word_rlc(); + let dst_memory_offset = cb.query_cell_phase2(); + let code_offset = WordByteCapGadget::construct(cb, code_size.expr()); // Pop items from stack. cb.stack_pop(dst_memory_offset.expr()); - cb.stack_pop(code_offset.expr()); + cb.stack_pop(code_offset.original_word()); cb.stack_pop(size.expr()); // Construct memory address in the destionation (memory) to which we copy code. @@ -66,7 +69,6 @@ impl ExecutionGadget for CodeCopyGadget { let code_hash = cb.curr.state.code_hash.clone(); // Fetch the bytecode length from the bytecode table. - let code_size = cb.query_cell(); cb.bytecode_length(code_hash.expr(), code_size.expr()); // Calculate the next memory size and the gas cost for this memory @@ -81,12 +83,19 @@ impl ExecutionGadget for CodeCopyGadget { let copy_rwc_inc = cb.query_cell(); cb.condition(dst_memory_addr.has_length(), |cb| { + // Set source start to the minimun value of code offset and code size. + let src_addr = select::expr( + code_offset.lt_cap(), + code_offset.valid_value(), + code_size.expr(), + ); + cb.copy_table_lookup( code_hash.expr(), CopyDataType::Bytecode.expr(), cb.curr.state.call_id.expr(), CopyDataType::Memory.expr(), - from_bytes::expr(&code_offset.cells), + src_addr, code_size.expr(), dst_memory_addr.offset(), dst_memory_addr.length(), @@ -143,28 +152,19 @@ impl ExecutionGadget for CodeCopyGadget { // 3. `size` is the number of // bytes to be read and written (0s to be copied for out of bounds). let [dest_offset, code_offset, size] = - [0, 1, 2].map(|i| block.rws[step.rw_indices[i]].stack_value()); - - // assign the code offset memory address. - self.code_offset.assign( - region, - offset, - Some( - code_offset.to_le_bytes()[..N_BYTES_MEMORY_ADDRESS] - .try_into() - .unwrap(), - ), - )?; + [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); - let code = block + let bytecode = block .bytecodes - .get(&call.code_hash) + .get(&call.code_hash.to_word()) .expect("could not find current environment's bytecode"); - self.code_size.assign( - region, - offset, - Value::known(F::from(code.bytes.len() as u64)), - )?; + + let code_size = bytecode.bytes.len() as u64; + self.code_size + .assign(region, offset, Value::known(F::from(code_size)))?; + + self.code_offset + .assign(region, offset, code_offset, F::from(code_size))?; // assign the destination memory offset. let memory_address = self @@ -200,17 +200,17 @@ mod tests { use eth_types::{bytecode, Word}; use mock::TestContext; - fn test_ok(memory_offset: usize, code_offset: usize, size: usize, large: bool) { + fn test_ok(code_offset: Word, memory_offset: Word, size: usize, large: bool) { let mut code = bytecode! {}; if large { - for _ in 0..0x101 { + for _ in 0..size { code.push(1, Word::from(123)); } } let tail = bytecode! { PUSH32(Word::from(size)) - PUSH32(Word::from(code_offset)) - PUSH32(Word::from(memory_offset)) + PUSH32(code_offset) + PUSH32(memory_offset) CODECOPY STOP }; @@ -224,13 +224,23 @@ mod tests { #[test] fn codecopy_gadget_simple() { - test_ok(0x00, 0x00, 0x20, false); - test_ok(0x20, 0x30, 0x30, false); - test_ok(0x10, 0x20, 0x42, false); + test_ok(0x00.into(), 0x00.into(), 0x20, false); + test_ok(0x30.into(), 0x20.into(), 0x30, false); + test_ok(0x20.into(), 0x10.into(), 0x42, false); } #[test] fn codecopy_gadget_large() { - test_ok(0x103, 0x102, 0x101, true); + test_ok(0x102.into(), 0x103.into(), 0x101, true); + } + + #[test] + fn codecopy_gadget_code_offset_overflow() { + test_ok(Word::MAX, 0x103.into(), 0x101, true); + } + + #[test] + fn codecopy_gadget_overflow_memory_offset_and_zero_size() { + test_ok(0x102.into(), Word::MAX, 0, false); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/codesize.rs b/zkevm-circuits/src/evm_circuit/execution/codesize.rs index 33d8ffe818..86df028c24 100644 --- a/zkevm-circuits/src/evm_circuit/execution/codesize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/codesize.rs @@ -8,7 +8,9 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition, + }, from_bytes, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, @@ -30,7 +32,7 @@ impl ExecutionGadget for CodesizeGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::CODESIZE; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let codesize_bytes = array_init(|_| cb.query_byte()); @@ -74,7 +76,7 @@ impl ExecutionGadget for CodesizeGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let codesize = block.rws[step.rw_indices[0]].stack_value().as_u64(); + let codesize = block.get_rws(step, 0).stack_value().as_u64(); for (c, b) in self .codesize_bytes diff --git a/zkevm-circuits/src/evm_circuit/execution/comparator.rs b/zkevm-circuits/src/evm_circuit/execution/comparator.rs index 3e1dccd893..9d593ef747 100644 --- a/zkevm-circuits/src/evm_circuit/execution/comparator.rs +++ b/zkevm-circuits/src/evm_circuit/execution/comparator.rs @@ -4,7 +4,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, from_bytes, math_gadget::{ComparisonGadget, IsEqualGadget}, select, CachedRegion, Cell, Word, @@ -33,7 +33,7 @@ impl ExecutionGadget for ComparatorGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::CMP; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let a = cb.query_word_rlc(); @@ -115,7 +115,7 @@ impl ExecutionGadget for ComparatorGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); // EQ op check self.is_eq.assign( @@ -133,13 +133,9 @@ impl ExecutionGadget for ComparatorGadget { F::from(OpcodeId::GT.as_u8() as u64), )?; - let indices = if is_gt == F::one() { - [step.rw_indices[1], step.rw_indices[0]] - } else { - [step.rw_indices[0], step.rw_indices[1]] - }; - let [a, b] = indices.map(|idx| block.rws[idx].stack_value().to_le_bytes()); - let result = block.rws[step.rw_indices[2]].stack_value(); + let indices = if is_gt == F::ONE { [1, 0] } else { [0, 1] }; + let [a, b] = indices.map(|index| block.get_rws(step, index).stack_value().to_le_bytes()); + let result = block.get_rws(step, 2).stack_value(); // `a[0..16] <= b[0..16]` self.comparison_lo.assign( diff --git a/zkevm-circuits/src/evm_circuit/execution/dummy.rs b/zkevm-circuits/src/evm_circuit/execution/dummy.rs index 906c0009a9..eda17c468d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/dummy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/dummy.rs @@ -4,7 +4,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, step::ExecutionState, - util::{constraint_builder::ConstraintBuilder, CachedRegion, Word}, + util::{constraint_builder::EVMConstraintBuilder, CachedRegion, Word}, witness::{Block, Call, ExecStep, Transaction}, }, util::Expr, @@ -26,7 +26,7 @@ impl const EXECUTION_STATE: ExecutionState = S; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let pops: [Word; N_POP] = [(); N_POP].map(|_| cb.query_word_rlc()); let pushes: [Word; N_PUSH] = [(); N_PUSH].map(|_| cb.query_word_rlc()); for pop in pops.iter() { @@ -55,16 +55,16 @@ impl // process of being implemented but are still // using DummyGadget. // See `bus-mapping/src/evm/opcodes.rs` - if step.rw_indices.len() != N_POP + N_PUSH { + if step.rw_indices_len() != N_POP + N_PUSH { log::warn!("DummyGadget: wrong number of rw indices for {:?}", step); } for i in 0..N_POP { - let value = block.rws[step.rw_indices[i]].stack_value(); + let value = block.get_rws(step, i).stack_value(); self.pops[i].assign(region, offset, Some(value.to_le_bytes()))?; } for i in 0..N_PUSH { - let value = block.rws[step.rw_indices[N_POP + i]].stack_value(); + let value = block.get_rws(step, N_POP + i).stack_value(); self.pushes[i].assign(region, offset, Some(value.to_le_bytes()))?; } Ok(()) diff --git a/zkevm-circuits/src/evm_circuit/execution/dup.rs b/zkevm-circuits/src/evm_circuit/execution/dup.rs index f5fc855f07..9dc0816dd7 100644 --- a/zkevm-circuits/src/evm_circuit/execution/dup.rs +++ b/zkevm-circuits/src/evm_circuit/execution/dup.rs @@ -4,7 +4,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, @@ -25,7 +25,7 @@ impl ExecutionGadget for DupGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::DUP; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let value = cb.query_cell_phase2(); @@ -65,7 +65,7 @@ impl ExecutionGadget for DupGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); self.value.assign(region, offset, region.word_rlc(value))?; Ok(()) @@ -85,7 +85,7 @@ mod test { PUSH32(value) }; for _ in 0..n - 1 { - bytecode.write_op(OpcodeId::DUP1); + bytecode.op_dup1(); } bytecode.append(&bytecode! { .write_op(opcode) diff --git a/zkevm-circuits/src/evm_circuit/execution/end_block.rs b/zkevm-circuits/src/evm_circuit/execution/end_block.rs index 2a39bdbbf0..0b3eaee2a4 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_block.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_block.rs @@ -3,7 +3,9 @@ use crate::{ execution::ExecutionGadget, step::ExecutionState, util::{ - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Same}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::Same, + }, math_gadget::{IsEqualGadget, IsZeroGadget}, not, CachedRegion, Cell, }, @@ -31,7 +33,7 @@ impl ExecutionGadget for EndBlockGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::EndBlock; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let max_txs = cb.query_copy_cell(); let max_rws = cb.query_copy_cell(); let total_txs = cb.query_cell(); @@ -118,7 +120,7 @@ impl ExecutionGadget for EndBlockGadget { step: &ExecStep, ) -> Result<(), Error> { self.is_empty_block - .assign(region, offset, F::from(step.rw_counter as u64 - 1))?; + .assign(region, offset, F::from(u64::from(step.rwc) - 1))?; let max_rws = F::from(block.circuits_params.max_rws as u64); let max_rws_assigned = self.max_rws.assign(region, offset, Value::known(max_rws))?; @@ -131,7 +133,7 @@ impl ExecutionGadget for EndBlockGadget { let max_txs_assigned = self.max_txs.assign(region, offset, Value::known(max_txs))?; // When rw_indices is not empty, we're at the last row (at a fixed offset), // where we need to access the max_rws and max_txs constant. - if !step.rw_indices.is_empty() { + if !step.rw_indices_len() == 0 { region.constrain_constant(max_rws_assigned, max_rws)?; region.constrain_constant(max_txs_assigned, max_txs)?; } diff --git a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs index 207cf31c87..64eec3fafc 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs @@ -6,7 +6,7 @@ use crate::{ util::{ common_gadget::UpdateBalanceGadget, constraint_builder::{ - ConstraintBuilder, StepStateTransition, + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::{Delta, Same}, }, math_gadget::{ @@ -17,11 +17,10 @@ use crate::{ }, witness::{Block, Call, ExecStep, Transaction}, }, - table::{ - BlockContextFieldTag, CallContextFieldTag, RwTableTag, TxContextFieldTag, TxReceiptFieldTag, - }, + table::{BlockContextFieldTag, CallContextFieldTag, TxContextFieldTag, TxReceiptFieldTag}, util::Expr, }; +use bus_mapping::operation::Target; use eth_types::{evm_types::MAX_REFUND_QUOTIENT_OF_GAS_USED, Field, ToScalar}; use halo2_proofs::{circuit::Value, plonk::Error}; use strum::EnumCount; @@ -50,7 +49,7 @@ impl ExecutionGadget for EndTxGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::EndTx; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let tx_id = cb.call_context(None, CallContextFieldTag::TxId); let is_persistent = cb.call_context(None, CallContextFieldTag::IsPersistent); @@ -203,9 +202,9 @@ impl ExecutionGadget for EndTxGadget { step: &ExecStep, ) -> Result<(), Error> { let gas_used = tx.gas - step.gas_left; - let (refund, _) = block.rws[step.rw_indices[2]].tx_refund_value_pair(); + let (refund, _) = block.get_rws(step, 2).tx_refund_value_pair(); let [(caller_balance, caller_balance_prev), (coinbase_balance, coinbase_balance_prev)] = - [step.rw_indices[3], step.rw_indices[4]].map(|idx| block.rws[idx].account_value_pair()); + [3, 4].map(|index| block.get_rws(step, index).account_value_pair()); self.tx_id .assign(region, offset, Value::known(F::from(tx.id as u64)))?; @@ -284,7 +283,7 @@ impl ExecutionGadget for EndTxGadget { // first transaction needs TxReceiptFieldTag::COUNT(3) lookups to tx receipt, // while later transactions need 4 (with one extra cumulative gas read) lookups let rw = &block.rws[( - RwTableTag::TxReceipt, + Target::TxReceipt, (tx.id - 2) * (TxReceiptFieldTag::COUNT + 1) + 2, )]; rw.receipt_value() @@ -296,7 +295,7 @@ impl ExecutionGadget for EndTxGadget { Value::known(F::from(current_cumulative_gas_used)), )?; self.is_first_tx - .assign(region, offset, F::from(tx.id as u64), F::one())?; + .assign(region, offset, F::from(tx.id as u64), F::ONE)?; self.is_persistent.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs b/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs index 7d16b76eef..888c340aba 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs @@ -4,30 +4,26 @@ use crate::{ param::N_BYTES_PROGRAM_COUNTER, step::ExecutionState, util::{ - and, - common_gadget::CommonErrorGadget, - constraint_builder::ConstraintBuilder, - from_bytes, - math_gadget::{IsEqualGadget, IsZeroGadget, LtGadget}, - select, sum, CachedRegion, Cell, Word, + common_gadget::{CommonErrorGadget, WordByteCapGadget}, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + math_gadget::{IsEqualGadget, IsZeroGadget}, + CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, util::Expr, }; -use eth_types::{evm_types::OpcodeId, Field, ToLittleEndian, U256}; +use eth_types::{evm_types::OpcodeId, Field, ToWord, U256}; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct ErrorInvalidJumpGadget { opcode: Cell, - dest_word: Word, + dest: WordByteCapGadget, code_len: Cell, value: Cell, is_code: Cell, - dest_not_overflow: IsZeroGadget, - dest_lt_code_len: LtGadget, is_jump_dest: IsEqualGadget, is_jumpi: IsEqualGadget, phase2_condition: Cell, @@ -40,15 +36,9 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ErrorInvalidJump; - fn configure(cb: &mut ConstraintBuilder) -> Self { - let dest_word = cb.query_word_rlc(); - let dest_not_overflow = - IsZeroGadget::construct(cb, sum::expr(&dest_word.cells[N_BYTES_PROGRAM_COUNTER..])); - let dest = select::expr( - dest_not_overflow.expr(), - from_bytes::expr(&dest_word.cells[..N_BYTES_PROGRAM_COUNTER]), - u64::MAX.expr(), - ); + fn configure(cb: &mut EVMConstraintBuilder) -> Self { + let code_len = cb.query_cell(); + let dest = WordByteCapGadget::construct(cb, code_len.expr()); let opcode = cb.query_cell(); let value = cb.query_cell(); @@ -71,7 +61,7 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { let is_condition_zero = IsZeroGadget::construct(cb, phase2_condition.expr()); // Pop the value from the stack - cb.stack_pop(dest_word.expr()); + cb.stack_pop(dest.original_word()); cb.condition(is_jumpi.expr(), |cb| { cb.stack_pop(phase2_condition.expr()); @@ -80,39 +70,31 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { }); // Look up bytecode length - let code_len = cb.query_cell(); cb.bytecode_length(cb.curr.state.code_hash.expr(), code_len.expr()); - let dest_lt_code_len = LtGadget::construct(cb, dest.expr(), code_len.expr()); - // If destination is in valid range, lookup for the value. - cb.condition( - and::expr([dest_not_overflow.expr(), dest_lt_code_len.expr()]), - |cb| { - cb.bytecode_lookup( - cb.curr.state.code_hash.expr(), - dest.expr(), - is_code.expr(), - value.expr(), - ); - cb.require_zero( - "is_code is false or not JUMPDEST", - is_code.expr() * is_jump_dest.expr(), - ); - }, - ); + cb.condition(dest.lt_cap(), |cb| { + cb.bytecode_lookup( + cb.curr.state.code_hash.expr(), + dest.valid_value(), + is_code.expr(), + value.expr(), + ); + cb.require_zero( + "is_code is false or not JUMPDEST", + is_code.expr() * is_jump_dest.expr(), + ); + }); let common_error_gadget = CommonErrorGadget::construct(cb, opcode.expr(), 3.expr() + is_jumpi.expr()); Self { opcode, - dest_word, + dest, code_len, value, is_code, - dest_not_overflow, - dest_lt_code_len, is_jump_dest, is_jumpi, phase2_condition, @@ -130,17 +112,13 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let is_jumpi = opcode == OpcodeId::JUMPI; self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; - let dest = block.rws[step.rw_indices[0]].stack_value(); - self.dest_word - .assign(region, offset, Some(dest.to_le_bytes()))?; - let condition = if is_jumpi { - block.rws[step.rw_indices[1]].stack_value() + block.get_rws(step, 1).stack_value() } else { U256::zero() }; @@ -148,25 +126,17 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { let code = block .bytecodes - .get(&call.code_hash) + .get(&call.code_hash.to_word()) .expect("could not find current environment's bytecode"); let code_len = code.bytes.len() as u64; self.code_len .assign(region, offset, Value::known(F::from(code_len)))?; - let dest_overflow_hi = dest.to_le_bytes()[N_BYTES_PROGRAM_COUNTER..] - .iter() - .fold(0, |acc, val| acc + u64::from(*val)); - self.dest_not_overflow - .assign(region, offset, F::from(dest_overflow_hi))?; - - let dest = if dest_overflow_hi == 0 { - dest.low_u64() - } else { - u64::MAX - }; + let dest = block.get_rws(step, 0).stack_value(); + self.dest.assign(region, offset, dest, F::from(code_len))?; // set default value in case can not find value, is_code from bytecode table + let dest = u64::try_from(dest).unwrap_or(code_len); let mut code_pair = [0u8, 0u8]; if dest < code_len { // get real value from bytecode table @@ -184,9 +154,6 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { F::from(OpcodeId::JUMPDEST.as_u64()), )?; - self.dest_lt_code_len - .assign(region, offset, F::from(dest), F::from(code_len))?; - self.is_jumpi.assign( region, offset, @@ -218,7 +185,7 @@ mod test { use crate::test_util::CircuitTestBuilder; use eth_types::{ address, bytecode, bytecode::Bytecode, evm_types::OpcodeId, geth_types::Account, Address, - ToWord, Word, + ToWord, Word, U64, }; use mock::TestContext; @@ -291,7 +258,7 @@ mod test { Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } @@ -415,16 +382,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(&caller); + accs[2].account(&callee); }, |mut txs, accs| { txs[0] diff --git a/zkevm-circuits/src/evm_circuit/execution/error_invalid_opcode.rs b/zkevm-circuits/src/evm_circuit/execution/error_invalid_opcode.rs index fbc1709096..868a830306 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_opcode.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_opcode.rs @@ -3,7 +3,8 @@ use crate::evm_circuit::{ step::ExecutionState, table::{FixedTableTag, Lookup}, util::{ - common_gadget::CommonErrorGadget, constraint_builder::ConstraintBuilder, CachedRegion, Cell, + common_gadget::CommonErrorGadget, constraint_builder::EVMConstraintBuilder, CachedRegion, + Cell, }, witness::{Block, Call, ExecStep, Transaction}, }; @@ -24,7 +25,7 @@ impl ExecutionGadget for ErrorInvalidOpcodeGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ErrorInvalidOpcode; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); cb.add_lookup( "Responsible opcode lookup", @@ -55,7 +56,7 @@ impl ExecutionGadget for ErrorInvalidOpcodeGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = F::from(step.opcode.unwrap().as_u64()); + let opcode = F::from(step.opcode().unwrap().as_u64()); self.opcode.assign(region, offset, Value::known(opcode))?; self.common_error_gadget @@ -67,9 +68,9 @@ impl ExecutionGadget for ErrorInvalidOpcodeGadget { #[cfg(test)] mod test { use crate::{evm_circuit::test::rand_bytes, test_util::CircuitTestBuilder}; - use eth_types::{bytecode, bytecode::Bytecode, ToWord, Word}; + use eth_types::{bytecode::Bytecode, Word}; use lazy_static::lazy_static; - use mock::TestContext; + use mock::{generate_mock_call_bytecode, MockCallBytecodeParams, TestContext}; lazy_static! { static ref TESTING_INVALID_CODES: [Vec; 6] = [ @@ -120,23 +121,13 @@ mod test { }); // code A calls code B. - let pushdata = rand_bytes(8); - let code_a = bytecode! { - // populate memory in A's context. - PUSH8(Word::from_big_endian(&pushdata)) - PUSH1(0x00) // offset - MSTORE - // call ADDR_B. - PUSH1(0x00) // retLength - PUSH1(0x00) // retOffset - PUSH32(call_data_length) // argsLength - PUSH32(call_data_offset) // argsOffset - PUSH1(0x00) // value - PUSH32(addr_b.to_word()) // addr - PUSH32(0x1_0000) // gas - CALL - STOP - }; + let code_a = generate_mock_call_bytecode(MockCallBytecodeParams { + address: addr_b, + pushdata: rand_bytes(32), + call_data_length, + call_data_offset, + ..MockCallBytecodeParams::default() + }); let ctx = TestContext::<3, 1>::new( None, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs index f0a423df67..e85783efe0 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs @@ -5,7 +5,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::{CommonCallGadget, CommonErrorGadget}, - constraint_builder::ConstraintBuilder, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, math_gadget::{IsZeroGadget, LtGadget}, CachedRegion, Cell, }, @@ -41,7 +41,7 @@ impl ExecutionGadget for ErrorOOGCallGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ErrorOutOfGasCall; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let is_call = IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::CALL.expr()); @@ -120,37 +120,29 @@ impl ExecutionGadget for ErrorOOGCallGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let is_call_or_callcode = usize::from([OpcodeId::CALL, OpcodeId::CALLCODE].contains(&opcode)); let [tx_id, is_static] = - [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].call_context_value()); - let stack_index = 2; - let [gas, callee_address] = [ - step.rw_indices[stack_index], - step.rw_indices[stack_index + 1], - ] - .map(|idx| block.rws[idx].stack_value()); + [0, 1].map(|index| block.get_rws(step, index).call_context_value()); + let [gas, callee_address] = [2, 3].map(|index| block.get_rws(step, index).stack_value()); let value = if is_call_or_callcode == 1 { - block.rws[step.rw_indices[stack_index + 2]].stack_value() + block.get_rws(step, 4).stack_value() } else { U256::zero() }; - let [cd_offset, cd_length, rd_offset, rd_length] = [ - step.rw_indices[stack_index + is_call_or_callcode + 2], - step.rw_indices[stack_index + is_call_or_callcode + 3], - step.rw_indices[stack_index + is_call_or_callcode + 4], - step.rw_indices[stack_index + is_call_or_callcode + 5], - ] - .map(|idx| block.rws[idx].stack_value()); - - let callee_code_hash = block.rws[step.rw_indices[9 + is_call_or_callcode]] + let [cd_offset, cd_length, rd_offset, rd_length] = + [4, 5, 6, 7].map(|i| block.get_rws(step, is_call_or_callcode + i).stack_value()); + + let callee_code_hash = block + .get_rws(step, 9 + is_call_or_callcode) .account_value_pair() .0; let callee_exists = !callee_code_hash.is_zero(); - let (is_warm, is_warm_prev) = - block.rws[step.rw_indices[10 + is_call_or_callcode]].tx_access_list_value_pair(); + let (is_warm, is_warm_prev) = block + .get_rws(step, 10 + is_call_or_callcode) + .tx_access_list_value_pair(); let memory_expansion_gas_cost = self.call.assign( region, @@ -235,7 +227,7 @@ mod test { use crate::test_util::CircuitTestBuilder; use eth_types::{ address, bytecode, bytecode::Bytecode, evm_types::OpcodeId, geth_types::Account, Address, - ToWord, Word, + ToWord, Word, U64, }; use mock::TestContext; use std::default::Default; @@ -273,7 +265,7 @@ mod test { .write_op(opcode) PUSH1(0) PUSH1(0) - .write_op(OpcodeId::REVERT) + REVERT }); bytecode @@ -296,7 +288,7 @@ mod test { Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } @@ -310,16 +302,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code.clone()) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code.clone()) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(caller); + accs[2].account(callee); }, |mut txs, accs| { txs[0] diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs index bc389a3114..90bfd06163 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs @@ -4,8 +4,10 @@ use crate::{ param::N_BYTES_GAS, step::ExecutionState, util::{ - common_gadget::CommonErrorGadget, constraint_builder::ConstraintBuilder, - math_gadget::LtGadget, CachedRegion, Cell, + common_gadget::CommonErrorGadget, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + math_gadget::LtGadget, + CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, @@ -28,7 +30,7 @@ impl ExecutionGadget for ErrorOOGConstantGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ErrorOutOfGasConstant; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let gas_required = cb.query_cell(); @@ -63,7 +65,7 @@ impl ExecutionGadget for ErrorOOGConstantGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; @@ -92,17 +94,18 @@ mod test { use bus_mapping::evm::OpcodeId; use eth_types::{ self, address, bytecode, bytecode::Bytecode, evm_types::GasCost, geth_types::Account, - Address, ToWord, Word, + Address, ToWord, Word, U64, }; use mock::{ - eth, gwei, test_ctx::helpers::account_0_code_account_1_no_code, TestContext, MOCK_ACCOUNTS, + eth, gwei, test_ctx::helpers::account_0_code_account_1_no_code, MockTransaction, + TestContext, MOCK_ACCOUNTS, }; fn gas(call_data: &[u8]) -> Word { Word::from( - GasCost::TX.as_u64() - + 2 * OpcodeId::PUSH32.constant_gas_cost().as_u64() + GasCost::TX + + 2 * OpcodeId::PUSH32.constant_gas_cost() + call_data .iter() .map(|&x| if x == 0 { 4 } else { 16 }) @@ -144,19 +147,19 @@ mod test { CircuitTestBuilder::new_from_test_ctx(ctx).run(); } - // TODO: Use `mock` crate fn mock_tx(value: Word, gas_price: Word, calldata: Vec) -> eth_types::Transaction { let from = MOCK_ACCOUNTS[1]; let to = MOCK_ACCOUNTS[0]; - eth_types::Transaction { - from, - to: Some(to), - value, - gas: gas(&calldata), - gas_price: Some(gas_price), - input: calldata.into(), - ..Default::default() - } + + let mock_transaction = MockTransaction::default() + .from(from) + .to(to) + .value(value) + .gas(gas(&calldata)) + .gas_price(gas_price) + .input(calldata.into()) + .build(); + eth_types::Transaction::from(mock_transaction) } #[test] @@ -223,16 +226,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(&caller); + accs[2].account(&callee); }, |mut txs, accs| { txs[0] @@ -253,7 +248,7 @@ mod test { Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs index 65e894008a..98c262f7b5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs @@ -5,7 +5,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::CommonErrorGadget, - constraint_builder::ConstraintBuilder, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, math_gadget::{ByteSizeGadget, LtGadget}, CachedRegion, Cell, Word, }, @@ -36,7 +36,7 @@ impl ExecutionGadget for ErrorOOGExpGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ErrorOutOfGasEXP; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); cb.require_equal( @@ -67,7 +67,7 @@ impl ExecutionGadget for ErrorOOGExpGadget { // static_gas = 10 // dynamic_gas = exponent_byte_size * 50 // gas_cost = dynamic_gas + static_gas - exponent_byte_size.byte_size() * GasCost::EXP_BYTE_TIMES.0.expr() + exponent_byte_size.byte_size() * GasCost::EXP_BYTE_TIMES.expr() + OpcodeId::EXP.constant_gas_cost().expr(), ); @@ -97,8 +97,8 @@ impl ExecutionGadget for ErrorOOGExpGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); - let [base, exponent] = [0, 1].map(|idx| block.rws[step.rw_indices[idx]].stack_value()); + let opcode = step.opcode().unwrap(); + let [base, exponent] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); log::debug!( "ErrorOutOfGasEXP: gas_left = {}, gas_cost = {}", @@ -134,10 +134,11 @@ mod tests { use eth_types::{ bytecode, evm_types::{GasCost, OpcodeId}, - Bytecode, ToWord, U256, + Bytecode, U256, }; use mock::{ - eth, test_ctx::helpers::account_0_code_account_1_no_code, TestContext, MOCK_ACCOUNTS, + eth, generate_mock_call_bytecode, test_ctx::helpers::account_0_code_account_1_no_code, + MockCallBytecodeParams, TestContext, MOCK_ACCOUNTS, }; #[test] @@ -171,9 +172,9 @@ mod tests { EXP }; - let gas_cost = OpcodeId::PUSH32.constant_gas_cost().0 * 2 - + OpcodeId::EXP.constant_gas_cost().0 - + ((exponent.bits() as u64 + 7) / 8) * GasCost::EXP_BYTE_TIMES.0; + let gas_cost = OpcodeId::PUSH32.constant_gas_cost() * 2 + + OpcodeId::EXP.constant_gas_cost() + + ((exponent.bits() as u64 + 7) / 8) * GasCost::EXP_BYTE_TIMES; Self { bytecode, gas_cost } } @@ -188,7 +189,7 @@ mod tests { txs[0] .from(accs[1].address) .to(accs[0].address) - .gas((GasCost::TX.0 + testing_data.gas_cost - 1).into()); + .gas((GasCost::TX + testing_data.gas_cost - 1).into()); }, |block, _tx| block.number(0xcafe_u64), ) @@ -202,26 +203,17 @@ mod tests { // code B gets called by code A, so the call is an internal call. let code_b = testing_data.bytecode.clone(); - let gas_cost_b = testing_data.gas_cost; - - // Code A calls code B. - let code_a = bytecode! { - // populate memory in A's context. - PUSH8(U256::from_big_endian(&rand_bytes(8))) - PUSH1(0x00) // offset - MSTORE - // call ADDR_B. - PUSH1(0x00) // retLength - PUSH1(0x00) // retOffset - PUSH32(0x00) // argsLength - PUSH32(0x20) // argsOffset - PUSH1(0x00) // value - PUSH32(addr_b.to_word()) // addr - // Decrease expected gas cost (by 1) to trigger out of gas error. - PUSH32(gas_cost_b - 1) // gas - CALL - STOP - }; + + // code A calls code B. + // Decrease expected gas cost (by 1) to trigger out of gas error. + let code_a = generate_mock_call_bytecode(MockCallBytecodeParams { + address: addr_b, + pushdata: rand_bytes(32), + call_data_length: 0x00usize, + call_data_offset: 0x20usize, + gas: testing_data.gas_cost - 1, + ..MockCallBytecodeParams::default() + }); let ctx = TestContext::<3, 1>::new( None, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs index b647d95a3b..0892ce5958 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs @@ -5,7 +5,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::CommonErrorGadget, - constraint_builder::ConstraintBuilder, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, math_gadget::LtGadget, memory_gadget::{MemoryAddressGadget, MemoryExpansionGadget}, CachedRegion, Cell, @@ -39,7 +39,7 @@ impl ExecutionGadget for ErrorOOGLogGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ErrorOutOfGasLOG; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let mstart = cb.query_cell_phase2(); let msize = cb.query_word_rlc(); @@ -67,8 +67,8 @@ impl ExecutionGadget for ErrorOOGLogGadget { // access let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); - let gas_cost = GasCost::LOG.as_u64().expr() - + GasCost::LOG.as_u64().expr() * topic_count + let gas_cost = GasCost::LOG.expr() + + GasCost::LOG.expr() * topic_count + 8.expr() * memory_address.length() + memory_expansion.gas_cost(); @@ -103,12 +103,11 @@ impl ExecutionGadget for ErrorOOGLogGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; - let [memory_start, msize] = - [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].stack_value()); + let [memory_start, msize] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); let memory_address = self .memory_address @@ -146,7 +145,7 @@ mod test { use bus_mapping::evm::OpcodeId; use eth_types::{ self, address, bytecode, bytecode::Bytecode, evm_types::GasCost, geth_types::Account, - Address, ToWord, Word, + Address, ToWord, Word, U64, }; use mock::{ @@ -155,8 +154,8 @@ mod test { fn gas(call_data: &[u8]) -> Word { Word::from( - GasCost::TX.as_u64() - + 2 * OpcodeId::PUSH32.constant_gas_cost().as_u64() + GasCost::TX + + 2 * OpcodeId::PUSH32.constant_gas_cost() + call_data .iter() .map(|&x| if x == 0 { 4 } else { 16 }) @@ -268,16 +267,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(&caller); + accs[2].account(&callee); }, |mut txs, accs| { txs[0] @@ -298,7 +289,7 @@ mod test { Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_memory_copy.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_memory_copy.rs new file mode 100644 index 0000000000..63499da840 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_memory_copy.rs @@ -0,0 +1,406 @@ +use crate::{ + evm_circuit::{ + execution::ExecutionGadget, + param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_MEMORY_WORD_SIZE}, + step::ExecutionState, + util::{ + common_gadget::CommonErrorGadget, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + from_bytes, + math_gadget::{IsZeroGadget, LtGadget}, + memory_gadget::{MemoryAddressGadget, MemoryCopierGasGadget, MemoryExpansionGadget}, + select, CachedRegion, Cell, Word, + }, + witness::{Block, Call, ExecStep, Transaction}, + }, + table::CallContextFieldTag, + util::Expr, +}; +use eth_types::{ + evm_types::{GasCost, OpcodeId}, + Field, ToLittleEndian, U256, +}; +use halo2_proofs::{circuit::Value, plonk::Error}; + +/// Gadget to implement the corresponding out of gas errors for +/// [`OpcodeId::CALLDATACOPY`], [`OpcodeId::CODECOPY`], +/// [`OpcodeId::EXTCODECOPY`] and [`OpcodeId::RETURNDATACOPY`]. +#[derive(Clone, Debug)] +pub(crate) struct ErrorOOGMemoryCopyGadget { + opcode: Cell, + /// Check if `EXTCODECOPY` external address is warm + is_warm: Cell, + tx_id: Cell, + /// Extra stack pop for `EXTCODECOPY` + external_address: Word, + /// Source offset + src_offset: Word, + /// Destination offset and size to copy + dst_memory_addr: MemoryAddressGadget, + memory_expansion: MemoryExpansionGadget, + memory_copier_gas: MemoryCopierGasGadget, + insufficient_gas: LtGadget, + is_extcodecopy: IsZeroGadget, + common_error_gadget: CommonErrorGadget, +} + +impl ExecutionGadget for ErrorOOGMemoryCopyGadget { + const NAME: &'static str = "ErrorOutOfGasMemoryCopy"; + + const EXECUTION_STATE: ExecutionState = ExecutionState::ErrorOutOfGasMemoryCopy; + + fn configure(cb: &mut EVMConstraintBuilder) -> Self { + let opcode = cb.query_cell(); + cb.require_in_set( + "ErrorOutOfGasMemoryCopy opcode must be CALLDATACOPY, CODECOPY, EXTCODECOPY or RETURNDATACOPY", + opcode.expr(), + vec![ + OpcodeId::CALLDATACOPY.expr(), + OpcodeId::CODECOPY.expr(), + OpcodeId::EXTCODECOPY.expr(), + OpcodeId::RETURNDATACOPY.expr(), + ], + ); + + let dst_offset = cb.query_cell_phase2(); + let src_offset = cb.query_word_rlc(); + let copy_size = cb.query_word_rlc(); + let external_address = cb.query_word_rlc(); + let is_warm = cb.query_bool(); + let tx_id = cb.query_cell(); + + let is_extcodecopy = + IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::EXTCODECOPY.expr()); + + cb.condition(is_extcodecopy.expr(), |cb| { + cb.call_context_lookup(false.expr(), None, CallContextFieldTag::TxId, tx_id.expr()); + + // Check if EXTCODECOPY external address is warm. + cb.account_access_list_read( + tx_id.expr(), + from_bytes::expr(&external_address.cells[..N_BYTES_ACCOUNT_ADDRESS]), + is_warm.expr(), + ); + + // EXTCODECOPY has an extra stack pop for external address. + cb.stack_pop(external_address.expr()); + }); + + cb.stack_pop(dst_offset.expr()); + cb.stack_pop(src_offset.expr()); + cb.stack_pop(copy_size.expr()); + + let dst_memory_addr = MemoryAddressGadget::construct(cb, dst_offset, copy_size); + let memory_expansion = MemoryExpansionGadget::construct(cb, [dst_memory_addr.address()]); + let memory_copier_gas = MemoryCopierGasGadget::construct( + cb, + dst_memory_addr.length(), + memory_expansion.gas_cost(), + ); + + let constant_gas_cost = select::expr( + is_extcodecopy.expr(), + // According to EIP-2929, EXTCODECOPY constant gas cost is different for cold and warm + // accounts. + select::expr( + is_warm.expr(), + GasCost::WARM_ACCESS.expr(), + GasCost::COLD_ACCOUNT_ACCESS.expr(), + ), + // Constant gas cost is same for CALLDATACOPY, CODECOPY and RETURNDATACOPY. + OpcodeId::CALLDATACOPY.constant_gas_cost().expr(), + ); + + let insufficient_gas = LtGadget::construct( + cb, + cb.curr.state.gas_left.expr(), + constant_gas_cost + memory_copier_gas.gas_cost(), + ); + + cb.require_equal( + "Gas left is less than gas cost", + insufficient_gas.expr(), + 1.expr(), + ); + + let common_error_gadget = CommonErrorGadget::construct( + cb, + opcode.expr(), + // EXTCODECOPY has extra 1 call context lookup (tx_id), 1 account access list + // read (is_warm), and 1 stack pop (external_address). + 5.expr() + 3.expr() * is_extcodecopy.expr(), + ); + + Self { + opcode, + is_warm, + tx_id, + external_address, + src_offset, + dst_memory_addr, + memory_expansion, + memory_copier_gas, + insufficient_gas, + is_extcodecopy, + common_error_gadget, + } + } + + fn assign_exec_step( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + block: &Block, + transaction: &Transaction, + call: &Call, + step: &ExecStep, + ) -> Result<(), Error> { + let opcode = step.opcode().unwrap(); + let is_extcodecopy = opcode == OpcodeId::EXTCODECOPY; + + log::debug!( + "ErrorOutOfGasMemoryCopy: opcode = {}, gas_left = {}, gas_cost = {}", + opcode, + step.gas_left, + step.gas_cost, + ); + + let (is_warm, external_address) = if is_extcodecopy { + ( + block.get_rws(step, 1).tx_access_list_value_pair().0, + block.get_rws(step, 2).stack_value(), + ) + } else { + (false, U256::zero()) + }; + + let rw_offset = if is_extcodecopy { 3 } else { 0 }; + let [dst_offset, src_offset, copy_size] = [rw_offset, rw_offset + 1, rw_offset + 2] + .map(|index| block.get_rws(step, index).stack_value()); + + self.opcode + .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; + self.is_warm + .assign(region, offset, Value::known(F::from(u64::from(is_warm))))?; + self.tx_id + .assign(region, offset, Value::known(F::from(transaction.id as u64)))?; + self.external_address + .assign(region, offset, Some(external_address.to_le_bytes()))?; + self.src_offset + .assign(region, offset, Some(src_offset.to_le_bytes()))?; + let memory_addr = self + .dst_memory_addr + .assign(region, offset, dst_offset, copy_size)?; + let (_, memory_expansion_cost) = + self.memory_expansion + .assign(region, offset, step.memory_word_size(), [memory_addr])?; + let memory_copier_gas = self.memory_copier_gas.assign( + region, + offset, + copy_size.as_u64(), + memory_expansion_cost, + )?; + let constant_gas_cost = if is_extcodecopy { + if is_warm { + GasCost::WARM_ACCESS + } else { + GasCost::COLD_ACCOUNT_ACCESS + } + } else { + GasCost::FASTEST + }; + self.insufficient_gas.assign_value( + region, + offset, + Value::known(F::from(step.gas_left)), + Value::known(F::from(constant_gas_cost + memory_copier_gas)), + )?; + self.is_extcodecopy.assign( + region, + offset, + F::from(opcode.as_u64()) - F::from(OpcodeId::EXTCODECOPY.as_u64()), + )?; + self.common_error_gadget.assign( + region, + offset, + block, + call, + step, + // EXTCODECOPY has extra 1 call context lookup (tx_id), 1 account access list + // read (is_warm), and 1 stack pop (external_address). + 5 + if is_extcodecopy { 3 } else { 0 }, + )?; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + evm_circuit::test::{rand_bytes, rand_word}, + test_util::CircuitTestBuilder, + }; + use eth_types::{ + bytecode, evm_types::gas_utils::memory_copier_gas_cost, Bytecode, ToWord, U256, + }; + use itertools::Itertools; + use mock::{ + eth, test_ctx::helpers::account_0_code_account_1_no_code, TestContext, MOCK_ACCOUNTS, + }; + + const TESTING_COMMON_OPCODES: &[OpcodeId] = &[ + OpcodeId::CALLDATACOPY, + OpcodeId::CODECOPY, + OpcodeId::RETURNDATACOPY, + ]; + + const TESTING_DST_OFFSET_COPY_SIZE_PAIRS: &[(u64, u64)] = + &[(0x20, 0), (0x40, 20), (0x2000, 0x200)]; + + #[test] + fn test_oog_memory_copy_for_common_opcodes() { + for (opcode, (dst_offset, copy_size)) in TESTING_COMMON_OPCODES + .iter() + .cartesian_product(TESTING_DST_OFFSET_COPY_SIZE_PAIRS.iter()) + { + let testing_data = TestingData::new_for_common_opcode(*opcode, *dst_offset, *copy_size); + + test_root(&testing_data); + test_internal(&testing_data); + } + } + + #[test] + fn test_oog_memory_copy_for_extcodecopy() { + for (is_warm, (dst_offset, copy_size)) in [false, true] + .iter() + .cartesian_product(TESTING_DST_OFFSET_COPY_SIZE_PAIRS.iter()) + { + let testing_data = TestingData::new_for_extcodecopy(*is_warm, *dst_offset, *copy_size); + + test_root(&testing_data); + test_internal(&testing_data); + } + } + + struct TestingData { + bytecode: Bytecode, + gas_cost: u64, + } + + impl TestingData { + pub fn new_for_common_opcode(opcode: OpcodeId, dst_offset: u64, copy_size: u64) -> Self { + let bytecode = bytecode! { + PUSH32(copy_size) + PUSH32(rand_word()) + PUSH32(dst_offset) + .write_op(opcode) + }; + + let memory_word_size = (dst_offset + copy_size + 31) / 32; + + let gas_cost = OpcodeId::PUSH32.constant_gas_cost() * 3 + + opcode.constant_gas_cost() + + memory_copier_gas_cost(0, memory_word_size, copy_size); + + Self { bytecode, gas_cost } + } + + pub fn new_for_extcodecopy(is_warm: bool, dst_offset: u64, copy_size: u64) -> Self { + let external_address = MOCK_ACCOUNTS[4]; + + let mut bytecode = bytecode! { + PUSH32(copy_size) + PUSH32(U256::zero()) + PUSH32(dst_offset) + PUSH32(external_address.to_word()) + EXTCODECOPY + }; + + let memory_word_size = (dst_offset + copy_size + 31) / 32; + + let mut gas_cost = OpcodeId::PUSH32.constant_gas_cost() * 4 + + GasCost::COLD_ACCOUNT_ACCESS + + memory_copier_gas_cost(0, memory_word_size, copy_size); + + if is_warm { + bytecode.append(&bytecode! { + PUSH32(copy_size) + PUSH32(rand_word()) + PUSH32(dst_offset) + PUSH32(external_address.to_word()) + EXTCODECOPY + }); + + gas_cost += OpcodeId::PUSH32.constant_gas_cost() * 4 + + GasCost::WARM_ACCESS + + memory_copier_gas_cost(memory_word_size, memory_word_size, copy_size); + } + + Self { bytecode, gas_cost } + } + } + + fn test_root(testing_data: &TestingData) { + let ctx = TestContext::<2, 1>::new( + None, + account_0_code_account_1_no_code(testing_data.bytecode.clone()), + |mut txs, accs| { + // Decrease expected gas cost (by 1) to trigger out of gas error. + txs[0] + .from(accs[1].address) + .to(accs[0].address) + .gas((GasCost::TX + testing_data.gas_cost - 1).into()); + }, + |block, _tx| block.number(0xcafe_u64), + ) + .unwrap(); + + CircuitTestBuilder::new_from_test_ctx(ctx).run(); + } + + fn test_internal(testing_data: &TestingData) { + let (addr_a, addr_b) = (MOCK_ACCOUNTS[0], MOCK_ACCOUNTS[1]); + + // code B gets called by code A, so the call is an internal call. + let code_b = testing_data.bytecode.clone(); + let gas_cost_b = testing_data.gas_cost; + + // Code A calls code B. + let code_a = bytecode! { + // populate memory in A's context. + PUSH8(U256::from_big_endian(&rand_bytes(8))) + PUSH1(0x00) // offset + MSTORE + // call ADDR_B. + PUSH1(0x00) // retLength + PUSH1(0x00) // retOffset + PUSH32(0x00) // argsLength + PUSH32(0x20) // argsOffset + PUSH1(0x00) // value + PUSH32(addr_b.to_word()) // addr + // Decrease expected gas cost (by 1) to trigger out of gas error. + PUSH32(gas_cost_b - 1) // gas + CALL + STOP + }; + + let ctx = TestContext::<3, 1>::new( + None, + |accs| { + accs[0].address(addr_b).code(code_b); + accs[1].address(addr_a).code(code_a); + accs[2].address(MOCK_ACCOUNTS[2]).balance(eth(10)); + }, + |mut txs, accs| { + txs[0].from(accs[2].address).to(accs[1].address); + }, + |block, _tx| block, + ) + .unwrap(); + + CircuitTestBuilder::new_from_test_ctx(ctx).run(); + } +} diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_sload_sstore.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_sload_sstore.rs index 53481febb6..a17e9487f2 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_sload_sstore.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_sload_sstore.rs @@ -9,7 +9,7 @@ use crate::{ cal_sload_gas_cost_for_assignment, cal_sstore_gas_cost_for_assignment, CommonErrorGadget, SloadGasGadget, SstoreGasGadget, }, - constraint_builder::ConstraintBuilder, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, math_gadget::{LtGadget, PairSelectGadget}, or, select, CachedRegion, Cell, }, @@ -50,7 +50,7 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ErrorOutOfGasSloadSstore; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let is_sstore = PairSelectGadget::construct( @@ -115,7 +115,7 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { let insufficient_gas_sentry = LtGadget::construct( cb, cb.curr.state.gas_left.expr(), - (GasCost::SSTORE_SENTRY.0 + 1).expr(), + (GasCost::SSTORE_SENTRY + 1).expr(), ); cb.require_equal( "Gas left is less than gas cost or gas sentry (only for SSTORE)", @@ -159,15 +159,14 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let is_sstore = opcode == OpcodeId::SSTORE; - let key = block.rws[step.rw_indices[3]].stack_value(); - let (is_warm, _) = block.rws[step.rw_indices[4]].tx_access_list_value_pair(); + let key = block.get_rws(step, 3).stack_value(); + let (is_warm, _) = block.get_rws(step, 4).tx_access_list_value_pair(); let (value, value_prev, original_value, gas_cost) = if is_sstore { - let value = block.rws[step.rw_indices[5]].stack_value(); - let (_, value_prev, _, original_value) = - block.rws[step.rw_indices[6]].storage_value_aux(); + let value = block.get_rws(step, 5).stack_value(); + let (_, value_prev, _, original_value) = block.get_rws(step, 6).storage_value_aux(); let gas_cost = cal_sstore_gas_cost_for_assignment(value, value_prev, original_value, is_warm); (value, value_prev, original_value, gas_cost) @@ -181,7 +180,7 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { is_sstore, step.gas_left, gas_cost, - if is_sstore { GasCost::SSTORE_SENTRY.0 } else { 0 }, + if is_sstore { GasCost::SSTORE_SENTRY } else { 0 }, ); self.opcode @@ -194,7 +193,7 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { region, offset, Value::known( - call.callee_address + call.address .to_scalar() .expect("unexpected Address -> Scalar conversion failure"), ), @@ -229,7 +228,7 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { region, offset, Value::known(F::from(step.gas_left)), - Value::known(F::from(GasCost::SSTORE_SENTRY.0.checked_add(1).unwrap())), + Value::known(F::from(GasCost::SSTORE_SENTRY.checked_add(1).unwrap())), )?; // Additional one stack pop and one account storage read for SSTORE. @@ -256,9 +255,11 @@ mod test { use eth_types::{ bytecode, evm_types::{GasCost, OpcodeId}, - Bytecode, ToWord, U256, + Bytecode, U256, + }; + use mock::{ + eth, generate_mock_call_bytecode, MockCallBytecodeParams, TestContext, MOCK_ACCOUNTS, }; - use mock::{eth, TestContext, MOCK_ACCOUNTS}; use std::cmp::max; const TESTING_STORAGE_KEY: U256 = U256([0, 0, 0, 0x030201]); @@ -388,14 +389,14 @@ mod test { SLOAD }; let mut gas_cost = - OpcodeId::PUSH32.constant_gas_cost().0 + cal_sload_gas_cost_for_assignment(false); + OpcodeId::PUSH32.constant_gas_cost() + cal_sload_gas_cost_for_assignment(false); if is_warm { bytecode.append(&bytecode! { PUSH32(key) SLOAD }); - gas_cost += OpcodeId::PUSH32.constant_gas_cost().0 - + cal_sload_gas_cost_for_assignment(true); + gas_cost += + OpcodeId::PUSH32.constant_gas_cost() + cal_sload_gas_cost_for_assignment(true); } Self { @@ -423,10 +424,10 @@ mod test { original_value, false, ); - let mut gas_cost = 2 * OpcodeId::PUSH32.constant_gas_cost().0 + let mut gas_cost = 2 * OpcodeId::PUSH32.constant_gas_cost() + max( sstore_gas_cost, - GasCost::SSTORE_SENTRY.0.checked_add(1).unwrap(), + GasCost::SSTORE_SENTRY.checked_add(1).unwrap(), ); if is_warm { bytecode.append(&bytecode! { @@ -440,10 +441,10 @@ mod test { original_value, true, ); - gas_cost += 2 * OpcodeId::PUSH32.constant_gas_cost().0 + gas_cost += 2 * OpcodeId::PUSH32.constant_gas_cost() + max( sstore_gas_cost, - GasCost::SSTORE_SENTRY.0.checked_add(1).unwrap(), + GasCost::SSTORE_SENTRY.checked_add(1).unwrap(), ); } @@ -475,7 +476,7 @@ mod test { txs[0] .from(accs[1].address) .to(accs[0].address) - .gas((GasCost::TX.0 + testing_data.gas_cost - 1).into()); + .gas((GasCost::TX + testing_data.gas_cost - 1).into()); }, |block, _tx| block.number(0xcafe_u64), ) @@ -491,24 +492,18 @@ mod test { let code_b = testing_data.bytecode.clone(); let gas_cost_b = testing_data.gas_cost; - // Code A calls code B. - let code_a = bytecode! { - // populate memory in A's context. - PUSH8(U256::from_big_endian(&rand_bytes(8))) - PUSH1(0x00) // offset - MSTORE - // call ADDR_B. - PUSH1(0x00) // retLength - PUSH1(0x00) // retOffset - PUSH32(0x00) // argsLength - PUSH32(0x20) // argsOffset - PUSH1(0x00) // value - PUSH32(addr_b.to_word()) // addr - // Decrease expected gas cost (by 1) to trigger out of gas error. - PUSH32(gas_cost_b - 1) // gas - CALL - STOP - }; + // code A calls code B. + // Decrease expected gas cost (by 1) to trigger out of gas error. + let code_a = generate_mock_call_bytecode(MockCallBytecodeParams { + address: addr_b, + pushdata: rand_bytes(32), + return_data_offset: 0x00usize, + return_data_size: 0x00usize, + call_data_length: 0x20usize, + call_data_offset: 0x10usize, + gas: gas_cost_b - 1, + ..MockCallBytecodeParams::default() + }); let ctx = TestContext::<3, 1>::new( None, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_static_memory.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_static_memory.rs index 596885b855..995913dbfd 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_static_memory.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_static_memory.rs @@ -4,7 +4,7 @@ use crate::{ param::{N_BYTES_GAS, N_BYTES_MEMORY_WORD_SIZE}, step::ExecutionState, util::{ - constraint_builder::ConstraintBuilder, + constraint_builder::EVMConstraintBuilder, math_gadget::{IsEqualGadget, IsZeroGadget, RangeCheckGadget}, memory_gadget::{address_high, address_low, MemoryExpansionGadget}, CachedRegion, Cell, Word, @@ -41,7 +41,7 @@ impl ExecutionGadget for ErrorOOGStaticMemoryGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ErrorOutOfGasStaticMemoryExpansion; // Support other OOG due to pure memory including CREATE, RETURN and REVERT - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); // Query address by a full word @@ -95,10 +95,10 @@ impl ExecutionGadget for ErrorOOGStaticMemoryGadget { _: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); // Inputs/Outputs - let address = block.rws[step.rw_indices[0]].stack_value(); + let address = block.get_rws(step, 0).stack_value(); self.address .assign(region, offset, Some(address.to_le_bytes()))?; @@ -122,8 +122,10 @@ impl ExecutionGadget for ErrorOOGStaticMemoryGadget { region, offset, step.memory_word_size(), - [address_low::value(address.to_le_bytes()) - + if is_mstore8 == F::one() { 1 } else { 32 }], + [ + address_low::value(address.to_le_bytes()) + + if is_mstore8 == F::ONE { 1 } else { 32 }, + ], )?; // Gas insufficient check diff --git a/zkevm-circuits/src/evm_circuit/execution/error_return_data_oo_bound.rs b/zkevm-circuits/src/evm_circuit/execution/error_return_data_oo_bound.rs index e173b844a8..5363dd2125 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_return_data_oo_bound.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_return_data_oo_bound.rs @@ -5,7 +5,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::CommonErrorGadget, - constraint_builder::ConstraintBuilder, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, from_bytes, math_gadget::{AddWordsGadget, IsZeroGadget, LtGadget}, not, or, sum, CachedRegion, Cell, @@ -38,7 +38,7 @@ impl ExecutionGadget for ErrorReturnDataOutOfBoundGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ErrorReturnDataOutOfBound; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let memory_offset = cb.query_cell(); let data_offset = cb.query_word_rlc(); @@ -123,13 +123,13 @@ impl ExecutionGadget for ErrorReturnDataOutOfBoundGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; let [dest_offset, data_offset, size] = - [0, 1, 2].map(|i| block.rws[step.rw_indices[i as usize]].stack_value()); + [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); self.memory_offset .assign(region, offset, Value::known(F::from(dest_offset.as_u64())))?; @@ -138,7 +138,7 @@ impl ExecutionGadget for ErrorReturnDataOutOfBoundGadget { self.sum .assign(region, offset, [data_offset, size], remainder_end)?; - let return_data_length = block.rws[step.rw_indices[3]].call_context_value(); + let return_data_length = block.get_rws(step, 3).call_context_value(); self.return_data_length.assign( region, offset, @@ -204,9 +204,7 @@ mod test { if is_root { code_b.append(&bytecode! { - PUSH32(return_data_size) - PUSH32(return_data_offset) - RETURN + .op_return(return_data_offset, return_data_size) STOP }); } else { @@ -216,9 +214,7 @@ mod test { PUSH32(dest_offset) // memory offset RETURNDATACOPY // end for internal - PUSH32(return_data_size) - PUSH32(return_data_offset) - RETURN + .op_return(return_data_offset, return_data_size) STOP }); } @@ -246,9 +242,7 @@ mod test { }); } else { code_a.append(&bytecode! { - PUSH32(return_data_size) - PUSH32(return_data_offset) - RETURN + .op_return(return_data_offset, return_data_size) }); } diff --git a/zkevm-circuits/src/evm_circuit/execution/error_stack.rs b/zkevm-circuits/src/evm_circuit/execution/error_stack.rs index a8550c6e68..61d746d83e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_stack.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_stack.rs @@ -4,8 +4,8 @@ use crate::{ step::ExecutionState, table::{FixedTableTag, Lookup}, util::{ - common_gadget::CommonErrorGadget, constraint_builder::ConstraintBuilder, CachedRegion, - Cell, + common_gadget::CommonErrorGadget, constraint_builder::EVMConstraintBuilder, + CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, @@ -25,7 +25,7 @@ impl ExecutionGadget for ErrorStackGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ErrorStack; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); cb.add_lookup( @@ -57,7 +57,7 @@ impl ExecutionGadget for ErrorStackGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; @@ -75,6 +75,7 @@ mod test { use bus_mapping::{circuit_input_builder::CircuitsParams, evm::OpcodeId}; use eth_types::{ self, address, bytecode, bytecode::Bytecode, geth_types::Account, Address, ToWord, Word, + U64, }; use mock::TestContext; @@ -131,7 +132,7 @@ mod test { } } // append final stop op code - bytecode.write_op(OpcodeId::STOP); + bytecode.op_stop(); CircuitTestBuilder::new_from_test_ctx( TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), @@ -199,16 +200,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(&caller); + accs[2].account(&callee); }, |mut txs, accs| { txs[0] @@ -229,7 +222,7 @@ mod test { Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } diff --git a/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs b/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs index e891798b27..0f1fbe7a0c 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs @@ -3,8 +3,10 @@ use crate::{ execution::ExecutionGadget, step::ExecutionState, util::{ - common_gadget::CommonErrorGadget, constraint_builder::ConstraintBuilder, - math_gadget::IsZeroGadget, sum, CachedRegion, Cell, Word as RLCWord, + common_gadget::CommonErrorGadget, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + math_gadget::IsZeroGadget, + sum, CachedRegion, Cell, Word as RLCWord, }, witness::{Block, Call, ExecStep, Transaction}, }, @@ -30,7 +32,7 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ErrorWriteProtection; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let is_call = IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::CALL.expr()); let gas_word = cb.query_word_rlc(); @@ -98,7 +100,7 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let is_call = opcode == OpcodeId::CALL; self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; @@ -106,8 +108,7 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { if is_call { [gas, code_address, value] = - [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] - .map(|idx| block.rws[idx].stack_value()); + [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); } self.gas.assign(region, offset, Some(gas.to_le_bytes()))?; @@ -142,7 +143,7 @@ mod test { use crate::test_util::CircuitTestBuilder; use eth_types::{ address, bytecode, bytecode::Bytecode, evm_types::OpcodeId, geth_types::Account, Address, - ToWord, Word, + ToWord, Word, U64, }; use mock::TestContext; @@ -159,10 +160,11 @@ mod test { fn callee(code: Bytecode) -> Account { let code = code.to_vec(); let is_empty = code.is_empty(); + Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } @@ -283,16 +285,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(&caller); + accs[2].account(&callee); }, |mut txs, accs| { txs[0] diff --git a/zkevm-circuits/src/evm_circuit/execution/exp.rs b/zkevm-circuits/src/evm_circuit/execution/exp.rs index d83d50aa47..5a1f8dd4e3 100644 --- a/zkevm-circuits/src/evm_circuit/execution/exp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/exp.rs @@ -7,7 +7,9 @@ use crate::evm_circuit::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition, + }, from_bytes, math_gadget::{ByteSizeGadget, IsEqualGadget, IsZeroGadget}, CachedRegion, Cell, Word, @@ -48,7 +50,7 @@ impl ExecutionGadget for ExponentiationGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::EXP; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); // Query RLC-encoded values for base, exponent and exponentiation, where: @@ -185,7 +187,7 @@ impl ExecutionGadget for ExponentiationGadget { // Finally we build an expression for the dynamic gas cost as: // dynamic_gas = 50 * exponent_byte_size - let dynamic_gas_cost = GasCost::EXP_BYTE_TIMES.0.expr() * exponent_byte_size.byte_size(); + let dynamic_gas_cost = GasCost::EXP_BYTE_TIMES.expr() * exponent_byte_size.byte_size(); let step_state_transition = StepStateTransition { rw_counter: Transition::Delta(3.expr()), // 2 stack pops, 1 stack push program_counter: Transition::Delta(1.expr()), @@ -225,8 +227,7 @@ impl ExecutionGadget for ExponentiationGadget { self.same_context.assign_exec_step(region, offset, step)?; let [base, exponent, exponentiation] = - [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] - .map(|idx| block.rws[idx].stack_value()); + [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); self.base.assign(region, offset, Some(base.to_le_bytes()))?; self.exponent @@ -246,7 +247,7 @@ impl ExecutionGadget for ExponentiationGadget { self.exponent_hi_is_zero .assign(region, offset, exponent_hi_scalar)?; self.exponent_lo_is_one - .assign(region, offset, exponent_lo_scalar, F::one())?; + .assign(region, offset, exponent_lo_scalar, F::ONE)?; let (base_sq, _) = base.overflowing_mul(base); self.zero_rlc diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs index 7949522b2b..49a24fbb44 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs @@ -1,15 +1,17 @@ use crate::{ evm_circuit::{ - param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_MEMORY_ADDRESS, N_BYTES_MEMORY_WORD_SIZE}, + param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_MEMORY_WORD_SIZE, N_BYTES_U64}, step::ExecutionState, util::{ - common_gadget::SameContextGadget, + common_gadget::{SameContextGadget, WordByteCapGadget}, constraint_builder::{ - ConstraintBuilder, ReversionInfo, StepStateTransition, Transition, + ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, + Transition, }, from_bytes, + math_gadget::IsZeroGadget, memory_gadget::{MemoryAddressGadget, MemoryCopierGasGadget, MemoryExpansionGadget}, - not, select, CachedRegion, Cell, MemoryAddress, Word, + not, select, CachedRegion, Cell, Word, }, witness::{Block, Call, ExecStep, Transaction}, }, @@ -27,11 +29,12 @@ pub(crate) struct ExtcodecopyGadget { same_context: SameContextGadget, external_address_word: Word, memory_address: MemoryAddressGadget, - data_offset: MemoryAddress, + code_offset: WordByteCapGadget, tx_id: Cell, reversion_info: ReversionInfo, is_warm: Cell, code_hash: Cell, + not_exists: IsZeroGadget, code_size: Cell, copy_rwc_inc: Cell, memory_expansion: MemoryExpansionGadget, @@ -43,24 +46,24 @@ impl ExecutionGadget for ExtcodecopyGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::EXTCODECOPY; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let external_address_word = cb.query_word_rlc(); let external_address = from_bytes::expr(&external_address_word.cells[..N_BYTES_ACCOUNT_ADDRESS]); - let memory_offset = cb.query_cell_phase2(); - let data_offset = cb.query_word_rlc(); + let code_size = cb.query_cell(); + let memory_length = cb.query_word_rlc(); + let memory_offset = cb.query_cell_phase2(); + let code_offset = WordByteCapGadget::construct(cb, code_size.expr()); cb.stack_pop(external_address_word.expr()); cb.stack_pop(memory_offset.expr()); - cb.stack_pop(data_offset.expr()); + cb.stack_pop(code_offset.original_word()); cb.stack_pop(memory_length.expr()); - let memory_address = MemoryAddressGadget::construct(cb, memory_offset, memory_length); - let tx_id = cb.call_context(None, CallContextFieldTag::TxId); let mut reversion_info = cb.reversion_info_read(None); let is_warm = cb.query_bool(); @@ -78,13 +81,16 @@ impl ExecutionGadget for ExtcodecopyGadget { AccountFieldTag::CodeHash, code_hash.expr(), ); - let code_size = cb.query_cell(); - // TODO: If external_address doesn't exist, we will get code_hash = 0. With - // this value, the bytecode_length lookup will not work, and the copy - // from code_hash = 0 will not work. We should use EMPTY_HASH when - // code_hash = 0. - cb.bytecode_length(code_hash.expr(), code_size.expr()); + let not_exists = IsZeroGadget::construct(cb, code_hash.expr()); + let exists = not::expr(not_exists.expr()); + cb.condition(exists.expr(), |cb| { + cb.bytecode_length(code_hash.expr(), code_size.expr()); + }); + cb.condition(not_exists.expr(), |cb| { + cb.require_zero("code_size is zero when non_exists", code_size.expr()); + }); + let memory_address = MemoryAddressGadget::construct(cb, memory_offset, memory_length); let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); let memory_copier_gas = MemoryCopierGasGadget::construct( cb, @@ -100,12 +106,19 @@ impl ExecutionGadget for ExtcodecopyGadget { let copy_rwc_inc = cb.query_cell(); cb.condition(memory_address.has_length(), |cb| { + // Set source start to the minimun value of code offset and code size. + let src_addr = select::expr( + code_offset.lt_cap(), + code_offset.valid_value(), + code_size.expr(), + ); + cb.copy_table_lookup( code_hash.expr(), CopyDataType::Bytecode.expr(), cb.curr.state.call_id.expr(), CopyDataType::Memory.expr(), - from_bytes::expr(&data_offset.cells), + src_addr, code_size.expr(), memory_address.offset(), memory_address.length(), @@ -135,11 +148,12 @@ impl ExecutionGadget for ExtcodecopyGadget { same_context, external_address_word, memory_address, - data_offset, + code_offset, tx_id, is_warm, reversion_info, code_hash, + not_exists, code_size, copy_rwc_inc, memory_expansion, @@ -158,22 +172,13 @@ impl ExecutionGadget for ExtcodecopyGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [external_address, memory_offset, data_offset, memory_length] = - [0, 1, 2, 3].map(|idx| block.rws[step.rw_indices[idx]].stack_value()); + let [external_address, memory_offset, code_offset, memory_length] = + [0, 1, 2, 3].map(|idx| block.get_rws(step, idx).stack_value()); self.external_address_word .assign(region, offset, Some(external_address.to_le_bytes()))?; let memory_address = self.memory_address .assign(region, offset, memory_offset, memory_length)?; - self.data_offset.assign( - region, - offset, - Some( - data_offset.to_le_bytes()[..N_BYTES_MEMORY_ADDRESS] - .try_into() - .unwrap(), - ), - )?; self.tx_id .assign(region, offset, Value::known(F::from(transaction.id as u64)))?; @@ -184,15 +189,17 @@ impl ExecutionGadget for ExtcodecopyGadget { call.is_persistent, )?; - let (_, is_warm) = block.rws[step.rw_indices[7]].tx_access_list_value_pair(); + let (_, is_warm) = block.get_rws(step, 7).tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; - let code_hash = block.rws[step.rw_indices[8]].account_value_pair().0; + let code_hash = block.get_rws(step, 8).account_value_pair().0; self.code_hash .assign(region, offset, region.word_rlc(code_hash))?; + self.not_exists + .assign_value(region, offset, region.word_rlc(code_hash))?; - let bytecode_len = if code_hash.is_zero() { + let code_size = if code_hash.is_zero() { 0 } else { block @@ -200,10 +207,13 @@ impl ExecutionGadget for ExtcodecopyGadget { .get(&code_hash) .expect("could not find external bytecode") .bytes - .len() + .len() as u64 }; self.code_size - .assign(region, offset, Value::known(F::from(bytecode_len as u64)))?; + .assign(region, offset, Value::known(F::from(code_size)))?; + + self.code_offset + .assign(region, offset, code_offset, F::from(code_size))?; self.copy_rwc_inc.assign( region, @@ -226,7 +236,7 @@ impl ExecutionGadget for ExtcodecopyGadget { region, offset, memory_length.as_u64(), - memory_expansion_gas_cost as u64, + memory_expansion_gas_cost, )?; Ok(()) @@ -249,8 +259,8 @@ mod test { fn test_ok( external_account: Option, - memory_offset: usize, - data_offset: usize, + code_offset: Word, + memory_offset: Word, length: usize, is_warm: bool, ) { @@ -270,7 +280,7 @@ mod test { } code.append(&bytecode! { PUSH32(length) - PUSH32(data_offset) + PUSH32(code_offset) PUSH32(memory_offset) PUSH20(external_address.to_word()) #[start] @@ -289,10 +299,7 @@ mod test { .balance(Word::from(1u64 << 20)); accs[2].address(external_address); if let Some(external_account) = external_account { - accs[2] - .balance(external_account.balance) - .nonce(external_account.nonce) - .code(external_account.code); + accs[2].account(&external_account); } }, |mut txs, accs| { @@ -310,8 +317,8 @@ mod test { #[test] fn extcodecopy_empty_account() { - test_ok(None, 0x00, 0x00, 0x36, true); // warm account - test_ok(None, 0x00, 0x00, 0x36, false); // cold account + test_ok(None, Word::zero(), Word::zero(), 0x36, true); // warm account + test_ok(None, Word::zero(), Word::zero(), 0x36, false); // cold account } #[test] @@ -322,8 +329,8 @@ mod test { code: Bytes::from([10, 40]), ..Default::default() }), - 0x00, - 0x00, + Word::zero(), + Word::zero(), 0x36, true, ); // warm account @@ -334,8 +341,8 @@ mod test { code: Bytes::from([10, 40]), ..Default::default() }), - 0x00, - 0x00, + Word::zero(), + Word::zero(), 0x36, false, ); // cold account @@ -349,8 +356,8 @@ mod test { code: Bytes::from(rand_bytes_array::<256>()), ..Default::default() }), - 0x00, - 0x00, + Word::zero(), + Word::zero(), 0x36, true, ); @@ -360,8 +367,8 @@ mod test { code: Bytes::from(rand_bytes_array::<256>()), ..Default::default() }), - 0x00, - 0x00, + Word::zero(), + Word::zero(), 0x36, false, ); @@ -375,8 +382,8 @@ mod test { code: Bytes::from(rand_bytes_array::<64>()), ..Default::default() }), - 0x00, - 0x20, + 0x20.into(), + Word::zero(), 0x104, true, ); @@ -386,10 +393,62 @@ mod test { code: Bytes::from(rand_bytes_array::<64>()), ..Default::default() }), - 0x00, - 0x20, + 0x20.into(), + Word::zero(), 0x104, false, ); } + + #[test] + fn extcodecopy_code_offset_overflow() { + test_ok( + Some(Account { + address: *EXTERNAL_ADDRESS, + code: Bytes::from(rand_bytes_array::<256>()), + ..Default::default() + }), + Word::MAX, + Word::zero(), + 0x36, + true, + ); + test_ok( + Some(Account { + address: *EXTERNAL_ADDRESS, + code: Bytes::from(rand_bytes_array::<256>()), + ..Default::default() + }), + Word::MAX, + Word::zero(), + 0x36, + false, + ); + } + + #[test] + fn extcodecopy_overflow_memory_offset_and_zero_length() { + test_ok( + Some(Account { + address: *EXTERNAL_ADDRESS, + code: Bytes::from(rand_bytes_array::<256>()), + ..Default::default() + }), + 0x20.into(), + Word::MAX, + 0, + true, + ); + test_ok( + Some(Account { + address: *EXTERNAL_ADDRESS, + code: Bytes::from(rand_bytes_array::<256>()), + ..Default::default() + }), + 0x20.into(), + Word::MAX, + 0, + true, + ); + } } diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs b/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs index b15d727873..1d14cffa34 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs @@ -6,7 +6,7 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{ - ConstraintBuilder, ReversionInfo, StepStateTransition, Transition::Delta, + EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition::Delta, }, from_bytes, select, CachedRegion, Cell, Word, }, @@ -33,7 +33,7 @@ impl ExecutionGadget for ExtcodehashGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::EXTCODEHASH; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let address_word = cb.query_word_rlc(); let address = from_bytes::expr(&address_word.cells[..N_BYTES_ACCOUNT_ADDRESS]); cb.stack_pop(address_word.expr()); @@ -93,7 +93,7 @@ impl ExecutionGadget for ExtcodehashGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let address = block.rws[step.rw_indices[0]].stack_value(); + let address = block.get_rws(step, 0).stack_value(); self.address_word .assign(region, offset, Some(address.to_le_bytes()))?; @@ -106,11 +106,11 @@ impl ExecutionGadget for ExtcodehashGadget { call.is_persistent, )?; - let (_, is_warm) = block.rws[step.rw_indices[4]].tx_access_list_value_pair(); + let (_, is_warm) = block.get_rws(step, 4).tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; - let code_hash = block.rws[step.rw_indices[5]].account_value_pair().0; + let code_hash = block.get_rws(step, 5).account_value_pair().0; self.code_hash .assign(region, offset, region.word_rlc(code_hash))?; @@ -122,7 +122,7 @@ impl ExecutionGadget for ExtcodehashGadget { mod test { use crate::test_util::CircuitTestBuilder; use eth_types::{ - address, bytecode, geth_types::Account, Address, Bytecode, Bytes, ToWord, Word, U256, + address, bytecode, geth_types::Account, Address, Bytecode, Bytes, ToWord, Word, U256, U64, }; use lazy_static::lazy_static; use mock::TestContext; @@ -134,7 +134,7 @@ mod test { fn test_ok(external_account: Option, is_warm: bool) { let external_address = external_account - .as_ref() + .clone() .map(|a| a.address) .unwrap_or(*EXTERNAL_ADDRESS); @@ -166,10 +166,7 @@ mod test { accs[1].address(external_address); if let Some(external_account) = external_account { - accs[1] - .balance(external_account.balance) - .nonce(external_account.nonce) - .code(external_account.code); + accs[1].account(&external_account); } accs[2] .address(address!("0x0000000000000000000000000000000000000010")) @@ -200,7 +197,7 @@ mod test { test_ok( Some(Account { address: *EXTERNAL_ADDRESS, - nonce: U256::from(259), + nonce: U64::from(259), code: Bytes::from([3]), ..Default::default() }), @@ -227,7 +224,7 @@ mod test { // code = []. let nonce_only_account = Account { address: *EXTERNAL_ADDRESS, - nonce: U256::from(200), + nonce: U64::from(200), ..Default::default() }; // This account state is possible if another account sends ETH to a previously diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs b/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs index 2dbace9da6..8c00bd63c7 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs @@ -6,7 +6,8 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{ - ConstraintBuilder, ReversionInfo, StepStateTransition, Transition::Delta, + ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, + Transition::Delta, }, from_bytes, math_gadget::IsZeroGadget, @@ -37,7 +38,7 @@ impl ExecutionGadget for ExtcodesizeGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::EXTCODESIZE; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let address_word = cb.query_word_rlc(); let address = from_bytes::expr(&address_word.cells[..N_BYTES_ACCOUNT_ADDRESS]); cb.stack_pop(address_word.expr()); @@ -110,7 +111,7 @@ impl ExecutionGadget for ExtcodesizeGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let address = block.rws[step.rw_indices[0]].stack_value(); + let address = block.get_rws(step, 0).stack_value(); self.address_word .assign(region, offset, Some(address.to_le_bytes()))?; @@ -124,17 +125,17 @@ impl ExecutionGadget for ExtcodesizeGadget { call.is_persistent, )?; - let (_, is_warm) = block.rws[step.rw_indices[4]].tx_access_list_value_pair(); + let (_, is_warm) = block.get_rws(step, 4).tx_access_list_value_pair(); self.is_warm - .assign(region, offset, Value::known(F::from(is_warm)))?; + .assign(region, offset, Value::known(F::from(is_warm as u64)))?; - let code_hash = block.rws[step.rw_indices[5]].account_value_pair().0; + let code_hash = block.get_rws(step, 5).account_value_pair().0; self.code_hash .assign(region, offset, region.word_rlc(code_hash))?; self.not_exists .assign_value(region, offset, region.word_rlc(code_hash))?; - let code_size = block.rws[step.rw_indices[6]].stack_value().as_u64(); + let code_size = block.get_rws(step, 6).stack_value().as_u64(); self.code_size .assign(region, offset, Some(code_size.to_le_bytes()))?; @@ -145,8 +146,11 @@ impl ExecutionGadget for ExtcodesizeGadget { #[cfg(test)] mod test { use crate::{evm_circuit::test::rand_bytes, test_util::CircuitTestBuilder}; - use eth_types::{bytecode, geth_types::Account, Bytecode, ToWord, Word}; - use mock::{TestContext, MOCK_1_ETH, MOCK_ACCOUNTS, MOCK_CODES}; + use eth_types::{bytecode, geth_types::Account, Bytecode, ToWord}; + use mock::{ + generate_mock_call_bytecode, MockCallBytecodeParams, TestContext, MOCK_1_ETH, + MOCK_ACCOUNTS, MOCK_CODES, + }; #[test] fn test_extcodesize_gadget_simple() { @@ -199,29 +203,19 @@ mod test { }); // code A calls code B. - let pushdata = rand_bytes(8); - let bytecode_a = bytecode! { - // populate memory in A's context. - PUSH8(Word::from_big_endian(&pushdata)) - PUSH1(0x00) // offset - MSTORE - // call ADDR_B. - PUSH1(0x00) // retLength - PUSH1(0x00) // retOffset - PUSH32(0xff) // argsLength - PUSH32(0x1010) // argsOffset - PUSH1(0x00) // value - PUSH32(addr_b.to_word()) // addr - PUSH32(0x1_0000) // gas - CALL - STOP - }; + let code_a = generate_mock_call_bytecode(MockCallBytecodeParams { + address: addr_b, + pushdata: rand_bytes(32), + call_data_length: 0xffusize, + call_data_offset: 0x1010usize, + ..MockCallBytecodeParams::default() + }); let ctx = TestContext::<4, 1>::new( None, |accs| { accs[0].address(addr_b).code(bytecode_b); - accs[1].address(addr_a).code(bytecode_a); + accs[1].address(addr_a).code(code_a); // Set code if account exists. if account_exists { accs[2].address(account.address).code(account.code.clone()); diff --git a/zkevm-circuits/src/evm_circuit/execution/gas.rs b/zkevm-circuits/src/evm_circuit/execution/gas.rs index 7d8f8c37c2..f8151dc7ca 100644 --- a/zkevm-circuits/src/evm_circuit/execution/gas.rs +++ b/zkevm-circuits/src/evm_circuit/execution/gas.rs @@ -5,7 +5,10 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, + Transition::Delta, + }, from_bytes, CachedRegion, RandomLinearCombination, }, witness::{Block, Call, ExecStep, Transaction}, @@ -26,7 +29,7 @@ impl ExecutionGadget for GasGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::GAS; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { // The gas passed to a transaction is a 64-bit number. let gas_left = cb.query_word_rlc(); @@ -75,7 +78,7 @@ impl ExecutionGadget for GasGadget { offset, Some( step.gas_left - .saturating_sub(OpcodeId::GAS.constant_gas_cost().as_u64()) + .saturating_sub(OpcodeId::GAS.constant_gas_cost()) .to_le_bytes(), ), )?; diff --git a/zkevm-circuits/src/evm_circuit/execution/gasprice.rs b/zkevm-circuits/src/evm_circuit/execution/gasprice.rs index 4d816bca78..2e6c5561cb 100644 --- a/zkevm-circuits/src/evm_circuit/execution/gasprice.rs +++ b/zkevm-circuits/src/evm_circuit/execution/gasprice.rs @@ -4,7 +4,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, @@ -28,7 +28,7 @@ impl ExecutionGadget for GasPriceGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::GASPRICE; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { // Query gasprice value let gas_price = cb.query_cell_phase2(); @@ -72,7 +72,7 @@ impl ExecutionGadget for GasPriceGadget { _: &Call, step: &ExecStep, ) -> Result<(), Error> { - let gas_price = block.rws[step.rw_indices[1]].stack_value(); + let gas_price = block.get_rws(step, 1).stack_value(); self.tx_id .assign(region, offset, Value::known(F::from(tx.id as u64)))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/is_zero.rs b/zkevm-circuits/src/evm_circuit/execution/is_zero.rs index 33130cac30..36671e5f5a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/is_zero.rs +++ b/zkevm-circuits/src/evm_circuit/execution/is_zero.rs @@ -4,7 +4,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, math_gadget, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, @@ -27,7 +27,7 @@ impl ExecutionGadget for IsZeroGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ISZERO; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let value = cb.query_cell_phase2(); @@ -64,7 +64,7 @@ impl ExecutionGadget for IsZeroGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); let value = region.word_rlc(value); self.value.assign(region, offset, value)?; self.is_zero.assign_value(region, offset, value)?; diff --git a/zkevm-circuits/src/evm_circuit/execution/jump.rs b/zkevm-circuits/src/evm_circuit/execution/jump.rs index b4d858bb5a..fc03df544b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/jump.rs +++ b/zkevm-circuits/src/evm_circuit/execution/jump.rs @@ -6,7 +6,7 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{ - ConstraintBuilder, StepStateTransition, + EVMConstraintBuilder, StepStateTransition, Transition::{Delta, To}, }, from_bytes, CachedRegion, RandomLinearCombination, @@ -29,7 +29,7 @@ impl ExecutionGadget for JumpGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::JUMP; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let destination = cb.query_word_rlc(); // Pop the value from the stack @@ -70,7 +70,7 @@ impl ExecutionGadget for JumpGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let destination = block.rws[step.rw_indices[0]].stack_value(); + let destination = block.get_rws(step, 0).stack_value(); self.destination.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/execution/jumpdest.rs b/zkevm-circuits/src/evm_circuit/execution/jumpdest.rs index 5cc57ead8a..57cf0edd67 100644 --- a/zkevm-circuits/src/evm_circuit/execution/jumpdest.rs +++ b/zkevm-circuits/src/evm_circuit/execution/jumpdest.rs @@ -4,7 +4,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, @@ -25,7 +25,7 @@ impl ExecutionGadget for JumpdestGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::JUMPDEST; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { // State transition let step_state_transition = StepStateTransition { program_counter: Delta(1.expr()), diff --git a/zkevm-circuits/src/evm_circuit/execution/jumpi.rs b/zkevm-circuits/src/evm_circuit/execution/jumpi.rs index e8df2b822f..8c131e21e5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/jumpi.rs +++ b/zkevm-circuits/src/evm_circuit/execution/jumpi.rs @@ -4,26 +4,25 @@ use crate::{ param::N_BYTES_PROGRAM_COUNTER, step::ExecutionState, util::{ - common_gadget::SameContextGadget, + common_gadget::{SameContextGadget, WordByteRangeGadget}, constraint_builder::{ - ConstraintBuilder, StepStateTransition, + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::{Delta, To}, }, - from_bytes, math_gadget::IsZeroGadget, - select, CachedRegion, Cell, RandomLinearCombination, + select, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, util::Expr, }; -use eth_types::{evm_types::OpcodeId, Field, ToLittleEndian}; +use eth_types::{evm_types::OpcodeId, Field}; use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct JumpiGadget { same_context: SameContextGadget, - destination: RandomLinearCombination, + dest: WordByteRangeGadget, phase2_condition: Cell, is_condition_zero: IsZeroGadget, } @@ -33,12 +32,12 @@ impl ExecutionGadget for JumpiGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::JUMPI; - fn configure(cb: &mut ConstraintBuilder) -> Self { - let destination = cb.query_word_rlc(); + fn configure(cb: &mut EVMConstraintBuilder) -> Self { + let dest = WordByteRangeGadget::construct(cb); let phase2_condition = cb.query_cell_phase2(); // Pop the value from the stack - cb.stack_pop(destination.expr()); + cb.stack_pop(dest.original_word()); cb.stack_pop(phase2_condition.expr()); // Determine if the jump condition is met @@ -47,18 +46,20 @@ impl ExecutionGadget for JumpiGadget { // Lookup opcode at destination when should_jump cb.condition(should_jump.clone(), |cb| { - cb.opcode_lookup_at( - from_bytes::expr(&destination.cells), - OpcodeId::JUMPDEST.expr(), + cb.require_equal( + "JUMPI destination must be within range if condition is non-zero", + dest.not_overflow(), 1.expr(), ); + + cb.opcode_lookup_at(dest.valid_value(), OpcodeId::JUMPDEST.expr(), 1.expr()); }); // Transit program_counter to destination when should_jump, otherwise by // delta 1. let next_program_counter = select::expr( should_jump, - from_bytes::expr(&destination.cells), + dest.valid_value(), cb.curr.state.program_counter.expr() + 1.expr(), ); @@ -75,7 +76,7 @@ impl ExecutionGadget for JumpiGadget { Self { same_context, - destination, + dest, phase2_condition, is_condition_zero, } @@ -92,19 +93,10 @@ impl ExecutionGadget for JumpiGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [destination, condition] = - [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].stack_value()); + let [destination, condition] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); let condition = region.word_rlc(condition); - self.destination.assign( - region, - offset, - Some( - destination.to_le_bytes()[..N_BYTES_PROGRAM_COUNTER] - .try_into() - .unwrap(), - ), - )?; + self.dest.assign(region, offset, destination)?; self.phase2_condition.assign(region, offset, condition)?; self.is_condition_zero .assign_value(region, offset, condition)?; @@ -174,4 +166,18 @@ mod test { test_ok(rand_range(1 << 11..0x5fff), Word::zero()); test_ok(rand_range(1 << 11..0x5fff), rand_word()); } + + #[test] + fn jumpi_gadget_with_zero_cond_and_overflow_dest() { + let bytecode = bytecode! { + PUSH32(Word::MAX) + PUSH32(Word::zero()) + JUMPI + }; + + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .run(); + } } diff --git a/zkevm-circuits/src/evm_circuit/execution/logs.rs b/zkevm-circuits/src/evm_circuit/execution/logs.rs index b82c3704e2..bf2f0d17ba 100644 --- a/zkevm-circuits/src/evm_circuit/execution/logs.rs +++ b/zkevm-circuits/src/evm_circuit/execution/logs.rs @@ -6,7 +6,7 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{ - ConstraintBuilder, StepStateTransition, + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::{Delta, To}, }, memory_gadget::{MemoryAddressGadget, MemoryExpansionGadget}, @@ -14,7 +14,7 @@ use crate::{ }, witness::{Block, Call, ExecStep, Transaction}, }, - table::{CallContextFieldTag, RwTableTag, TxLogFieldTag}, + table::{CallContextFieldTag, TxLogFieldTag}, util::{build_tx_log_expression, Expr}, }; use array_init::array_init; @@ -46,7 +46,7 @@ impl ExecutionGadget for LogGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::LOG; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let mstart = cb.query_cell_phase2(); let msize = cb.query_word_rlc(); @@ -151,8 +151,8 @@ impl ExecutionGadget for LogGadget { ); }); - let gas_cost = GasCost::LOG.as_u64().expr() - + GasCost::LOG.as_u64().expr() * topic_count.clone() + let gas_cost = GasCost::LOG.expr() + + GasCost::LOG.expr() * topic_count.clone() + 8.expr() * memory_address.length() + memory_expansion.gas_cost(); // State transition @@ -194,9 +194,7 @@ impl ExecutionGadget for LogGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [memory_start, msize] = - [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].stack_value()); - + let [memory_start, msize] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); let memory_address = self .memory_address .assign(region, offset, memory_start, msize)?; @@ -205,40 +203,45 @@ impl ExecutionGadget for LogGadget { self.memory_expansion .assign(region, offset, step.memory_word_size(), [memory_address])?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let topic_count = opcode.postfix().expect("opcode with postfix") as usize; assert!(topic_count <= 4); - let is_persistent = call.is_persistent as u64; - let mut topic_stack_entry = if topic_count > 0 { - step.rw_indices[6 + call.is_persistent as usize] - } else { - // if topic_count == 0, this value will be no used anymore - (RwTableTag::Stack, 0usize) - }; - + let is_persistent = call.is_persistent as usize; + let mut topics = (0..topic_count).map(|topic| { + // We compute the index of the correct read-write record from + // bus-mapping/src/evm/opcodes/logs.rs::gen_log_step + // It takes 6 + is_persistent reads or writes to reach the topic stack write section. + // Each topic takes at least 1 stack read. They take an additional tx log write if the + // call is persistent. + block + .get_rws(step, 6 + is_persistent + topic * (1 + is_persistent)) + .stack_value() + }); for i in 0..4 { - let mut topic = region.word_rlc(U256::zero()); - if i < topic_count { - topic = region.word_rlc(block.rws[topic_stack_entry].stack_value()); - self.topic_selectors[i].assign(region, offset, Value::known(F::one()))?; - topic_stack_entry.1 += 1; - } else { - self.topic_selectors[i].assign(region, offset, Value::known(F::zero()))?; - } - self.phase2_topics[i].assign(region, offset, topic)?; + let topic = topics.next(); + self.topic_selectors[i].assign( + region, + offset, + Value::known(F::from(topic.is_some().into())), + )?; + self.phase2_topics[i].assign( + region, + offset, + region.word_rlc(topic.unwrap_or_default()), + )?; } self.contract_address.assign( region, offset, Value::known( - call.callee_address + call.address .to_scalar() .expect("unexpected Address -> Scalar conversion failure"), ), )?; - + let is_persistent = call.is_persistent as u64; self.is_static_call .assign(region, offset, Value::known(F::from(call.is_static as u64)))?; self.is_persistent @@ -358,7 +361,7 @@ mod test { code.push(32, Word::from(mstart)); code.write_op(cur_op_code); if is_persistent { - code.write_op(OpcodeId::STOP); + code.op_stop(); } else { // make current call failed with false persistent code.write_op(OpcodeId::INVALID(0xfe)); @@ -415,7 +418,7 @@ mod test { code.push(32, Word::from(mstart)); code.write_op(cur_op_code); - code.write_op(OpcodeId::STOP); + code.op_stop(); code_prepare.append(&code); CircuitTestBuilder::new_from_test_ctx( @@ -430,9 +433,7 @@ mod test { // prepare memory data let mut code = Bytecode::default(); for (i, d) in data.chunks(32).enumerate() { - code.push(32, Word::from_big_endian(d)); - code.push(32, Word::from(offset + i * 32)); - code.write_op(OpcodeId::MSTORE); + code.op_mstore(offset + i * 32, Word::from_big_endian(d)); } code } diff --git a/zkevm-circuits/src/evm_circuit/execution/memory.rs b/zkevm-circuits/src/evm_circuit/execution/memory.rs index 8dfa64e01d..2bf7999a44 100644 --- a/zkevm-circuits/src/evm_circuit/execution/memory.rs +++ b/zkevm-circuits/src/evm_circuit/execution/memory.rs @@ -6,7 +6,7 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{ - ConstraintBuilder, StepStateTransition, + EVMConstraintBuilder, StepStateTransition, Transition::{Delta, To}, }, from_bytes, @@ -36,7 +36,7 @@ impl ExecutionGadget for MemoryGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::MEMORY; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); // In successful case the address must be in 5 bytes @@ -128,11 +128,10 @@ impl ExecutionGadget for MemoryGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); // Inputs/Outputs - let [address, value] = - [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].stack_value()); + let [address, value] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); self.address.assign( region, offset, @@ -165,7 +164,7 @@ impl ExecutionGadget for MemoryGadget { region, offset, step.memory_word_size(), - [address.as_u64() + if is_mstore8 == F::one() { 1 } else { 32 }], + [address.as_u64() + if is_mstore8 == F::ONE { 1 } else { 32 }], )?; Ok(()) @@ -192,7 +191,7 @@ mod test { }; let gas_limit = - GasCost::TX.as_u64() + OpcodeId::PUSH32.as_u64() + OpcodeId::PUSH32.as_u64() + gas_cost; + GasCost::TX + OpcodeId::PUSH32.as_u64() + OpcodeId::PUSH32.as_u64() + gas_cost; let ctx = TestContext::<2, 1>::new( None, @@ -250,7 +249,7 @@ mod test { + 31; let memory_size = memory_address / 32; - GasCost::FASTEST.as_u64() + 3 * memory_size + memory_size * memory_size / 512 + GasCost::FASTEST + 3 * memory_size + memory_size * memory_size / 512 }; for opcode in [OpcodeId::MSTORE, OpcodeId::MLOAD, OpcodeId::MSTORE8] { diff --git a/zkevm-circuits/src/evm_circuit/execution/msize.rs b/zkevm-circuits/src/evm_circuit/execution/msize.rs index 3428f0a3e7..37d04e91fe 100644 --- a/zkevm-circuits/src/evm_circuit/execution/msize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/msize.rs @@ -5,7 +5,10 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, + Transition::Delta, + }, from_bytes, CachedRegion, RandomLinearCombination, }, witness::{Block, Call, ExecStep, Transaction}, @@ -27,7 +30,7 @@ impl ExecutionGadget for MsizeGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::MSIZE; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let value = cb.query_word_rlc(); // memory_size is limited to 64 bits so we only consider 8 bytes @@ -67,11 +70,8 @@ impl ExecutionGadget for MsizeGadget { step: &ExecStep, ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - self.value.assign( - region, - offset, - Some((step.memory_size as u64).to_le_bytes()), - )?; + self.value + .assign(region, offset, Some((step.memory_size).to_le_bytes()))?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs b/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs index bb0bf1a971..8804392410 100644 --- a/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs @@ -5,7 +5,10 @@ use crate::{ util::{ self, common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, + Transition::Delta, + }, math_gadget::{IsZeroGadget, LtWordGadget, MulAddWordsGadget}, select, sum, CachedRegion, }, @@ -40,7 +43,7 @@ impl ExecutionGadget for MulDivModGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::MUL_DIV_MOD; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let is_mul = (OpcodeId::DIV.expr() - opcode.expr()) @@ -118,9 +121,8 @@ impl ExecutionGadget for MulDivModGadget { step: &ExecStep, ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let indices = [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]]; - let [pop1, pop2, push] = indices.map(|idx| block.rws[idx].stack_value()); - let (a, b, c, d) = match step.opcode.unwrap() { + let [pop1, pop2, push] = [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); + let (a, b, c, d) = match step.opcode().unwrap() { OpcodeId::MUL => (pop1, pop2, U256::from(0), push), OpcodeId::DIV => (push, pop2, pop1 - push * pop2, pop1), OpcodeId::MOD => ( diff --git a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs index c31abab8d0..3986b16d9d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs @@ -5,7 +5,10 @@ use crate::{ util::{ self, common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, + Transition::Delta, + }, math_gadget::{IsZeroGadget, LtWordGadget, ModGadget, MulAddWords512Gadget}, sum, CachedRegion, }, @@ -41,7 +44,7 @@ impl ExecutionGadget for MulModGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::MULMOD; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let a = cb.query_word_rlc(); @@ -113,9 +116,7 @@ impl ExecutionGadget for MulModGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [r, n, b, a] = [3, 2, 1, 0] - .map(|idx| step.rw_indices[idx]) - .map(|idx| block.rws[idx].stack_value()); + let [r, n, b, a] = [3, 2, 1, 0].map(|index| block.get_rws(step, index).stack_value()); self.words[0].assign(region, offset, Some(a.to_le_bytes()))?; self.words[1].assign(region, offset, Some(b.to_le_bytes()))?; self.words[2].assign(region, offset, Some(n.to_le_bytes()))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/not.rs b/zkevm-circuits/src/evm_circuit/execution/not.rs index 5590f0e6bd..37ddcc3f1b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/not.rs +++ b/zkevm-circuits/src/evm_circuit/execution/not.rs @@ -5,7 +5,7 @@ use crate::{ table::{FixedTableTag, Lookup}, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, Word, }, witness::{Block, Call, ExecStep, Transaction}, @@ -27,7 +27,7 @@ impl ExecutionGadget for NotGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::NOT; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let input = cb.query_word_rlc(); @@ -74,8 +74,7 @@ impl ExecutionGadget for NotGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [input, output] = - [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].stack_value()); + let [input, output] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); self.input .assign(region, offset, Some(input.to_le_bytes()))?; self.output diff --git a/zkevm-circuits/src/evm_circuit/execution/origin.rs b/zkevm-circuits/src/evm_circuit/execution/origin.rs index be2c3edefa..f53d0e2099 100644 --- a/zkevm-circuits/src/evm_circuit/execution/origin.rs +++ b/zkevm-circuits/src/evm_circuit/execution/origin.rs @@ -5,7 +5,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, from_bytes, CachedRegion, Cell, RandomLinearCombination, }, witness::{Block, Call, ExecStep, Transaction}, @@ -29,7 +29,7 @@ impl ExecutionGadget for OriginGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ORIGIN; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let origin = cb.query_word_rlc::(); // Lookup in call_ctx the TxId @@ -72,7 +72,7 @@ impl ExecutionGadget for OriginGadget { _: &Call, step: &ExecStep, ) -> Result<(), Error> { - let origin = block.rws[step.rw_indices[1]].stack_value(); + let origin = block.get_rws(step, 1).stack_value(); // Assing TxId. self.tx_id diff --git a/zkevm-circuits/src/evm_circuit/execution/pc.rs b/zkevm-circuits/src/evm_circuit/execution/pc.rs index 14fc0cee9f..905ae21d5c 100644 --- a/zkevm-circuits/src/evm_circuit/execution/pc.rs +++ b/zkevm-circuits/src/evm_circuit/execution/pc.rs @@ -5,7 +5,10 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, + Transition::Delta, + }, from_bytes, CachedRegion, RandomLinearCombination, }, witness::{Block, Call, ExecStep, Transaction}, @@ -27,7 +30,7 @@ impl ExecutionGadget for PcGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::PC; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let value = cb.query_word_rlc(); // program_counter is limited to 64 bits so we only consider 8 bytes @@ -69,7 +72,7 @@ impl ExecutionGadget for PcGadget { self.same_context.assign_exec_step(region, offset, step)?; self.value - .assign(region, offset, Some(step.program_counter.to_le_bytes()))?; + .assign(region, offset, Some(step.pc.to_le_bytes()))?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/pop.rs b/zkevm-circuits/src/evm_circuit/execution/pop.rs index 3b92858ce4..93103a4ae3 100644 --- a/zkevm-circuits/src/evm_circuit/execution/pop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/pop.rs @@ -4,7 +4,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, @@ -26,7 +26,7 @@ impl ExecutionGadget for PopGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::POP; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let phase2_value = cb.query_cell_phase2(); // Pop the value from the stack @@ -60,7 +60,7 @@ impl ExecutionGadget for PopGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); self.phase2_value .assign(region, offset, region.word_rlc(value))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/push.rs b/zkevm-circuits/src/evm_circuit/execution/push.rs index 7970c50eae..75c1add43c 100644 --- a/zkevm-circuits/src/evm_circuit/execution/push.rs +++ b/zkevm-circuits/src/evm_circuit/execution/push.rs @@ -4,7 +4,10 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, + Transition::Delta, + }, sum, CachedRegion, Cell, Word, }, witness::{Block, Call, ExecStep, Transaction}, @@ -27,7 +30,7 @@ impl ExecutionGadget for PushGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::PUSH; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let value = cb.query_word_rlc(); @@ -123,9 +126,9 @@ impl ExecutionGadget for PushGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); self.value .assign(region, offset, Some(value.to_le_bytes()))?; @@ -157,7 +160,7 @@ mod test { for b in bytes { bytecode.write(*b, false); } - bytecode.write_op(OpcodeId::STOP); + bytecode.op_stop(); CircuitTestBuilder::new_from_test_ctx( TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), diff --git a/zkevm-circuits/src/evm_circuit/execution/return_revert.rs b/zkevm-circuits/src/evm_circuit/execution/return_revert.rs index d417a3590f..f5dbefa402 100644 --- a/zkevm-circuits/src/evm_circuit/execution/return_revert.rs +++ b/zkevm-circuits/src/evm_circuit/execution/return_revert.rs @@ -6,7 +6,7 @@ use crate::{ util::{ common_gadget::RestoreContextGadget, constraint_builder::{ - ConstraintBuilder, ReversionInfo, StepStateTransition, + ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition::{Delta, To}, }, math_gadget::{IsZeroGadget, MinMaxGadget}, @@ -27,6 +27,7 @@ pub(crate) struct ReturnRevertGadget { opcode: Cell, range: MemoryAddressGadget, + deployed_code_rlc: Cell, is_success: Cell, restore_context: RestoreContextGadget, @@ -51,7 +52,7 @@ impl ExecutionGadget for ReturnRevertGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::RETURN_REVERT; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); cb.opcode_lookup(opcode.expr(), 1.expr()); @@ -94,11 +95,12 @@ impl ExecutionGadget for ReturnRevertGadget { let is_contract_deployment = is_create.clone() * is_success.expr() * not::expr(copy_rw_increase_is_zero.expr()); - let (caller_id, address, reversion_info, code_hash) = + let (caller_id, address, reversion_info, code_hash, deployed_code_rlc) = cb.condition(is_contract_deployment.clone(), |cb| { // We don't need to place any additional constraints on code_hash because the // copy circuit enforces that it is the hash of the bytes in the copy lookup. let code_hash = cb.query_cell_phase2(); + let deployed_code_rlc = cb.query_cell_phase2(); cb.copy_table_lookup( cb.curr.state.call_id.expr(), CopyDataType::Memory.expr(), @@ -108,7 +110,7 @@ impl ExecutionGadget for ReturnRevertGadget { range.address(), 0.expr(), range.length(), - 0.expr(), + deployed_code_rlc.expr(), copy_rw_increase.expr(), ); @@ -127,7 +129,13 @@ impl ExecutionGadget for ReturnRevertGadget { Some(&mut reversion_info), ); - (caller_id, address, reversion_info, code_hash) + ( + caller_id, + address, + reversion_info, + code_hash, + deployed_code_rlc, + ) }); // Case B in the specs. @@ -218,6 +226,7 @@ impl ExecutionGadget for ReturnRevertGadget { Self { opcode, range, + deployed_code_rlc, is_success, copy_length, copy_rw_increase, @@ -245,18 +254,21 @@ impl ExecutionGadget for ReturnRevertGadget { self.opcode.assign( region, offset, - Value::known(F::from(step.opcode.unwrap().as_u64())), + Value::known(F::from(step.opcode().unwrap().as_u64())), )?; - let [memory_offset, length] = [0, 1].map(|i| block.rws[step.rw_indices[i]].stack_value()); + let [memory_offset, length] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); let range = self.range.assign(region, offset, memory_offset, length)?; self.memory_expansion .assign(region, offset, step.memory_word_size(), [range])?; - self.is_success - .assign(region, offset, Value::known(call.is_success.into()))?; + self.is_success.assign( + region, + offset, + Value::known(F::from(call.is_success as u64)), + )?; - if !call.is_root && !call.is_create { + if !call.is_root && !call.is_create() { for (cell, value) in [ (&self.return_data_length, call.return_data_length.into()), (&self.return_data_offset, call.return_data_offset.into()), @@ -272,10 +284,15 @@ impl ExecutionGadget for ReturnRevertGadget { )?; } - if call.is_create && call.is_success { + if call.is_create() && call.is_success { let values: Vec<_> = (3..3 + length.as_usize()) - .map(|i| block.rws[step.rw_indices[i]].memory_value()) + .map(|index| block.get_rws(step, index).memory_value()) .collect(); + self.deployed_code_rlc.assign( + region, + offset, + region.keccak_rlc(&values.iter().rev().cloned().collect::>()), + )?; let mut code_hash = CodeDB::hash(&values).to_fixed_bytes(); code_hash.reverse(); self.code_hash.assign( @@ -285,7 +302,7 @@ impl ExecutionGadget for ReturnRevertGadget { )?; } - let copy_rw_increase = if call.is_create && call.is_success { + let copy_rw_increase = if call.is_create() && call.is_success { length.as_u64() } else if !call.is_root { 2 * std::cmp::min(call.return_data_length, length.as_u64()) @@ -297,7 +314,7 @@ impl ExecutionGadget for ReturnRevertGadget { self.copy_rw_increase_is_zero .assign(region, offset, F::from(copy_rw_increase))?; - let is_contract_deployment = call.is_create && call.is_success && !length.is_zero(); + let is_contract_deployment = call.is_create() && call.is_success && !length.is_zero(); if !call.is_root { let rw_counter_offset = 3 + if is_contract_deployment { 5 + length.as_u64() @@ -323,7 +340,7 @@ impl ExecutionGadget for ReturnRevertGadget { self.address.assign( region, offset, - Value::known(call.callee_address.to_scalar().unwrap()), + Value::known(call.address.to_scalar().unwrap()), )?; self.reversion_info.assign( @@ -344,7 +361,7 @@ mod test { address, bytecode, evm_types::OpcodeId, geth_types::{Account, GethData}, - Address, Bytecode, ToWord, Word, U256, + Address, Bytecode, Bytes, ToWord, Word, U256, U64, }; use itertools::Itertools; use mock::{eth, TestContext, MOCK_ACCOUNTS}; @@ -352,16 +369,16 @@ mod test { const CALLEE_ADDRESS: Address = Address::repeat_byte(0xff); const CALLER_ADDRESS: Address = Address::repeat_byte(0x34); - fn callee_bytecode(is_return: bool, offset: u64, length: u64) -> Bytecode { - let memory_bytes = [0x60; 10]; + fn callee_bytecode(is_return: bool, offset: u128, length: u64) -> Bytecode { + let memory_bytes = [0x60; 6]; let memory_address = 0; let memory_value = Word::from_big_endian(&memory_bytes); let mut code = bytecode! { - PUSH10(memory_value) + PUSH6(memory_value) PUSH1(memory_address) MSTORE PUSH2(length) - PUSH2(32u64 - u64::try_from(memory_bytes.len()).unwrap() + offset) + PUSH17(Word::from(offset) + 32 - memory_bytes.len()) }; code.write_op(if is_return { OpcodeId::RETURN @@ -417,13 +434,13 @@ mod test { let callee = Account { address: CALLEE_ADDRESS, code: callee_bytecode(*is_return, *callee_offset, *callee_length).into(), - nonce: Word::one(), + nonce: U64::one(), ..Default::default() }; let caller = Account { address: CALLER_ADDRESS, code: caller_bytecode(*caller_offset, *caller_length).into(), - nonce: Word::one(), + nonce: U64::one(), ..Default::default() }; @@ -493,21 +510,17 @@ mod test { CREATE }; - let caller = Account { - address: CALLER_ADDRESS, - code: root_code.into(), - nonce: Word::one(), - balance: eth(10), - ..Default::default() - }; - let ctx = TestContext::<2, 1>::new( None, |accs| { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(eth(10)); - accs[1].account(&caller); + accs[1] + .address(CALLER_ADDRESS) + .code::(root_code.into()) + .nonce(1) + .balance(eth(10)); }, |mut txs, accs| { txs[0] @@ -547,7 +560,7 @@ mod test { let caller = Account { address: CALLER_ADDRESS, code: root_code.into(), - nonce: Word::one(), + nonce: U64::one(), balance: eth(10), ..Default::default() }; @@ -602,9 +615,7 @@ mod test { .chain(0u8..((32 - initializer.len() % 32) as u8)) .collect(); for (index, word) in code_creator.chunks(32).enumerate() { - bytecode.push(32, Word::from_big_endian(word)); - bytecode.push(32, Word::from(index * 32)); - bytecode.write_op(OpcodeId::MSTORE); + bytecode.op_mstore(index * 32, Word::from_big_endian(word)); } bytecode.append(&bytecode! { PUSH3(0x123456) // salt @@ -617,10 +628,8 @@ mod test { PUSH1(0) // dest offset RETURNDATACOPY }); - - let block: GethData = TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode.clone()) - .unwrap() - .into(); + let test_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode.clone()).unwrap(); + let block: GethData = test_ctx.clone().into(); // collect return opcode, retrieve next step, assure both contract create // successfully @@ -651,7 +660,17 @@ mod test { .iter() .for_each(|size| assert_eq!(size, &Word::zero())); - let text_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(); - CircuitTestBuilder::new_from_test_ctx(text_ctx).run(); + CircuitTestBuilder::new_from_test_ctx(test_ctx).run(); + } + + #[test] + fn test_return_overflow_offset_and_zero_length() { + for is_return in [true, false] { + let code = callee_bytecode(is_return, u128::MAX, 0); + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(), + ) + .run(); + } } } diff --git a/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs b/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs index 4a8a91e957..aed699e68a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs @@ -6,7 +6,7 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{ - ConstraintBuilder, StepStateTransition, + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::{Delta, To}, }, from_bytes, @@ -57,7 +57,7 @@ impl ExecutionGadget for ReturnDataCopyGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::RETURNDATACOPY; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let dest_offset = cb.query_cell_phase2(); @@ -176,7 +176,7 @@ impl ExecutionGadget for ReturnDataCopyGadget { self.same_context.assign_exec_step(region, offset, step)?; let [dest_offset, data_offset, size] = - [0, 1, 2].map(|i| block.rws[step.rw_indices[i as usize]].stack_value()); + [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); self.data_offset.assign( region, @@ -194,7 +194,7 @@ impl ExecutionGadget for ReturnDataCopyGadget { (5, CallContextFieldTag::LastCalleeReturnDataLength), ] .map(|(i, tag)| { - let rw = block.rws[step.rw_indices[i as usize]]; + let rw = block.get_rws(step, i); assert_eq!(rw.field_tag(), Some(tag as u64)); rw.call_context_value() }); @@ -269,49 +269,40 @@ impl ExecutionGadget for ReturnDataCopyGadget { mod test { use crate::{evm_circuit::test::rand_bytes, test_util::CircuitTestBuilder}; use bus_mapping::circuit_input_builder::CircuitsParams; - use eth_types::{bytecode, ToWord, Word}; - use mock::test_ctx::TestContext; + use eth_types::{bytecode, Word}; + use mock::{generate_mock_call_bytecode, test_ctx::TestContext, MockCallBytecodeParams}; fn test_ok_internal( return_data_offset: usize, return_data_size: usize, - dest_offset: usize, - offset: usize, size: usize, + offset: usize, + dest_offset: Word, ) { let (addr_a, addr_b) = (mock::MOCK_ACCOUNTS[0], mock::MOCK_ACCOUNTS[1]); - let pushdata = rand_bytes(32); let return_offset = std::cmp::max((return_data_offset + return_data_size) as i64 - 32, 0) as usize; let code_b = bytecode! { - PUSH32(Word::from_big_endian(&pushdata)) - PUSH32(return_offset) - MSTORE - - PUSH32(return_data_size) - PUSH32(return_data_offset) - RETURN + .op_mstore(return_offset, Word::from_big_endian(&rand_bytes(32))) + .op_return(return_data_offset, return_data_size) STOP }; // code A calls code B. - let code_a = bytecode! { - // call ADDR_B. - PUSH32(return_data_size) // retLength - PUSH32(return_data_offset) // retOffset - PUSH1(0x00) // argsLength - PUSH1(0x00) // argsOffset - PUSH1(0x00) // value - PUSH32(addr_b.to_word()) // addr - PUSH32(0x1_0000) // gas - CALL + let instruction = bytecode! { PUSH32(size) // size PUSH32(offset) // offset PUSH32(dest_offset) // dest_offset RETURNDATACOPY - STOP }; + let code_a = generate_mock_call_bytecode(MockCallBytecodeParams { + address: addr_b, + return_data_offset, + return_data_size, + instructions_after_call: instruction, + ..MockCallBytecodeParams::default() + }); let ctx = TestContext::<3, 1>::new( None, @@ -339,38 +330,43 @@ mod test { #[test] fn returndatacopy_gadget_do_nothing() { - test_ok_internal(0x00, 0x02, 0x10, 0x00, 0x00); + test_ok_internal(0, 2, 0, 0, 0x10.into()); } #[test] fn returndatacopy_gadget_simple() { - test_ok_internal(0x00, 0x02, 0x10, 0x00, 0x02); + test_ok_internal(0, 2, 2, 0, 0x10.into()); } #[test] fn returndatacopy_gadget_large() { - test_ok_internal(0x00, 0x20, 0x20, 0x00, 0x20); + test_ok_internal(0, 0x20, 0x20, 0, 0x20.into()); } #[test] fn returndatacopy_gadget_large_partial() { - test_ok_internal(0x00, 0x20, 0x20, 0x10, 0x10); + test_ok_internal(0, 0x20, 0x10, 0x10, 0x20.into()); } #[test] fn returndatacopy_gadget_zero_length() { - test_ok_internal(0x00, 0x00, 0x20, 0x00, 0x00); + test_ok_internal(0, 0, 0, 0, 0x20.into()); } #[test] fn returndatacopy_gadget_long_length() { // rlc value matters only if length > 255, i.e., size.cells.len() > 1 - test_ok_internal(0x00, 0x200, 0x20, 0x00, 0x150); + test_ok_internal(0, 0x200, 0x150, 0, 0x20.into()); } #[test] fn returndatacopy_gadget_big_offset() { // rlc value matters only if length > 255, i.e., size.cells.len() > 1 - test_ok_internal(0x200, 0x200, 0x200, 0x00, 0x150); + test_ok_internal(0x200, 0x200, 0x150, 0, 0x200.into()); + } + + #[test] + fn returndatacopy_gadget_overflow_offset_and_zero_length() { + test_ok_internal(0, 0x20, 0, 0x20, Word::MAX); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs b/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs index b7e1d41cd5..60aaafa0f0 100644 --- a/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs @@ -5,7 +5,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, from_bytes, CachedRegion, RandomLinearCombination, }, witness::{Block, Call, ExecStep, Transaction}, @@ -28,7 +28,7 @@ impl ExecutionGadget for ReturnDataSizeGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::RETURNDATASIZE; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); // Add lookup constraint in the call context for the returndatasize field. @@ -69,7 +69,7 @@ impl ExecutionGadget for ReturnDataSizeGadget { step: &ExecStep, ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let return_data_size = block.rws[step.rw_indices[1]].stack_value(); + let return_data_size = block.get_rws(step, 1).stack_value(); self.return_data_size.assign( region, offset, @@ -87,38 +87,28 @@ impl ExecutionGadget for ReturnDataSizeGadget { #[cfg(test)] mod test { use crate::{evm_circuit::test::rand_bytes, test_util::CircuitTestBuilder}; - use eth_types::{bytecode, ToWord, Word}; - use mock::test_ctx::TestContext; + use eth_types::{bytecode, Word}; + use mock::{generate_mock_call_bytecode, test_ctx::TestContext, MockCallBytecodeParams}; fn test_ok_internal(return_data_offset: usize, return_data_size: usize) { let (addr_a, addr_b) = (mock::MOCK_ACCOUNTS[0], mock::MOCK_ACCOUNTS[1]); - let pushdata = rand_bytes(32); let code_b = bytecode! { - PUSH32(Word::from_big_endian(&pushdata)) - PUSH1(0) - MSTORE - - PUSH32(return_data_size) - PUSH1(return_data_offset) - RETURN + .op_mstore(0, Word::from_big_endian(&rand_bytes(32))) + .op_return(return_data_offset, return_data_size) STOP }; - // code A calls code B. - let code_a = bytecode! { - // call ADDR_B. - PUSH32(return_data_size) // retLength - PUSH1(return_data_offset) // retOffset - PUSH1(0x00) // argsLength - PUSH1(0x00) // argsOffset - PUSH1(0x00) // value - PUSH32(addr_b.to_word()) // addr - PUSH32(0x1_0000) // gas - CALL + let instruction = bytecode! { RETURNDATASIZE - STOP }; + let code_a = generate_mock_call_bytecode(MockCallBytecodeParams { + address: addr_b, + return_data_offset, + return_data_size, + instructions_after_call: instruction, + ..MockCallBytecodeParams::default() + }); let ctx = TestContext::<3, 1>::new( None, diff --git a/zkevm-circuits/src/evm_circuit/execution/sar.rs b/zkevm-circuits/src/evm_circuit/execution/sar.rs index ed339be385..87dbd318a5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sar.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sar.rs @@ -6,7 +6,10 @@ use crate::{ table::{FixedTableTag, Lookup}, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, + Transition::Delta, + }, from_bytes, math_gadget::{IsEqualGadget, IsZeroGadget, LtGadget}, select, sum, CachedRegion, Cell, Word, @@ -71,7 +74,7 @@ impl ExecutionGadget for SarGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::SAR; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let shift = cb.query_word_rlc(); @@ -263,8 +266,7 @@ impl ExecutionGadget for SarGadget { step: &ExecStep, ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let indices = [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]]; - let [shift, a, b] = indices.map(|idx| block.rws[idx].stack_value()); + let [shift, a, b] = [0, 1, 2].map(|idx| block.get_rws(step, idx).stack_value()); self.shift .assign(region, offset, Some(shift.to_le_bytes()))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs b/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs index ac4682f492..7398ced5ed 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs @@ -4,7 +4,10 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, + Transition::Delta, + }, math_gadget::{AbsWordGadget, IsZeroGadget, LtGadget, LtWordGadget, MulAddWordsGadget}, select, sum, CachedRegion, }, @@ -36,7 +39,7 @@ impl ExecutionGadget for SignedDivModGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::SDIV_SMOD; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let is_sdiv = (OpcodeId::SMOD.expr() - opcode.expr()) * F::from(2).invert().unwrap(); @@ -147,14 +150,13 @@ impl ExecutionGadget for SignedDivModGadget { step: &ExecStep, ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let indices = [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]]; - let [pop1, pop2, push] = indices.map(|idx| block.rws[idx].stack_value()); + let [pop1, pop2, push] = [0, 1, 2].map(|idx| block.get_rws(step, idx).stack_value()); let pop1_abs = get_abs(pop1); let pop2_abs = get_abs(pop2); let push_abs = get_abs(push); let is_pop1_neg = is_neg(pop1); let is_pop2_neg = is_neg(pop2); - let (quotient, divisor, remainder, dividend) = match step.opcode.unwrap() { + let (quotient, divisor, remainder, dividend) = match step.opcode().unwrap() { OpcodeId::SDIV => ( push, pop2, diff --git a/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs b/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs index 8c0863492e..b170e88360 100644 --- a/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs +++ b/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs @@ -4,7 +4,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, @@ -28,7 +28,7 @@ impl ExecutionGadget for SelfbalanceGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::SELFBALANCE; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let callee_address = cb.call_context(None, CallContextFieldTag::CalleeAddress); let phase2_self_balance = cb.query_cell_phase2(); @@ -72,13 +72,13 @@ impl ExecutionGadget for SelfbalanceGadget { region, offset, Value::known( - call.callee_address + call.address .to_scalar() .expect("unexpected Address -> Scalar conversion failure"), ), )?; - let self_balance = block.rws[step.rw_indices[2]].stack_value(); + let self_balance = block.get_rws(step, 2).stack_value(); self.phase2_self_balance .assign(region, offset, region.word_rlc(self_balance))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/sha3.rs b/zkevm-circuits/src/evm_circuit/execution/sha3.rs index f85fd1127e..736b961dfa 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sha3.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sha3.rs @@ -8,7 +8,9 @@ use crate::evm_circuit::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition, + }, memory_gadget::{MemoryAddressGadget, MemoryCopierGasGadget, MemoryExpansionGadget}, rlc, CachedRegion, Cell, Word, }, @@ -33,7 +35,7 @@ impl ExecutionGadget for Sha3Gadget { const NAME: &'static str = "SHA3"; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let offset = cb.query_cell_phase2(); @@ -112,8 +114,7 @@ impl ExecutionGadget for Sha3Gadget { self.same_context.assign_exec_step(region, offset, step)?; let [memory_offset, size, sha3_output] = - [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] - .map(|idx| block.rws[idx].stack_value()); + [0, 1, 2].map(|idx| block.get_rws(step, idx).stack_value()); let memory_address = self .memory_address .assign(region, offset, memory_offset, size)?; @@ -130,7 +131,7 @@ impl ExecutionGadget for Sha3Gadget { )?; let values: Vec = (3..3 + (size.low_u64() as usize)) - .map(|i| block.rws[step.rw_indices[i]].memory_value()) + .map(|i| block.get_rws(step, i).memory_value()) .collect(); let rlc_acc = region @@ -146,12 +147,8 @@ impl ExecutionGadget for Sha3Gadget { step.memory_word_size(), [memory_address], )?; - self.memory_copier_gas.assign( - region, - offset, - size.as_u64(), - memory_expansion_gas_cost as u64, - )?; + self.memory_copier_gas + .assign(region, offset, size.as_u64(), memory_expansion_gas_cost)?; Ok(()) } @@ -160,14 +157,12 @@ impl ExecutionGadget for Sha3Gadget { #[cfg(test)] mod tests { use crate::test_util::CircuitTestBuilder; - use bus_mapping::{ - circuit_input_builder::CircuitsParams, - evm::{gen_sha3_code, MemoryKind}, - }; + use bus_mapping::{circuit_input_builder::CircuitsParams, evm::Sha3CodeGen}; + use eth_types::{bytecode, U256}; use mock::TestContext; - fn test_ok(offset: usize, size: usize, mem_kind: MemoryKind) { - let (code, _) = gen_sha3_code(offset, size, mem_kind); + fn test_ok(mut gen: Sha3CodeGen) { + let (code, _) = gen.gen_sha3_code(); CircuitTestBuilder::new_from_test_ctx( TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(), ) @@ -180,22 +175,36 @@ mod tests { #[test] fn sha3_gadget_zero_length() { - test_ok(0x20, 0x00, MemoryKind::MoreThanSize); + test_ok(Sha3CodeGen::mem_gt_size(0x20, 0x00)); } #[test] fn sha3_gadget_simple() { - test_ok(0x00, 0x08, MemoryKind::Empty); - test_ok(0x10, 0x10, MemoryKind::LessThanSize); - test_ok(0x24, 0x16, MemoryKind::EqualToSize); - test_ok(0x32, 0x78, MemoryKind::MoreThanSize); + test_ok(Sha3CodeGen::mem_empty(0x00, 0x08)); + test_ok(Sha3CodeGen::mem_lt_size(0x10, 0x10)); + test_ok(Sha3CodeGen::mem_eq_size(0x24, 0x16)); + test_ok(Sha3CodeGen::mem_gt_size(0x32, 0x78)); } #[test] fn sha3_gadget_large() { - test_ok(0x101, 0x202, MemoryKind::Empty); - test_ok(0x202, 0x303, MemoryKind::LessThanSize); - test_ok(0x303, 0x404, MemoryKind::EqualToSize); - test_ok(0x404, 0x505, MemoryKind::MoreThanSize); + test_ok(Sha3CodeGen::mem_empty(0x101, 0x202)); + test_ok(Sha3CodeGen::mem_lt_size(0x202, 0x303)); + test_ok(Sha3CodeGen::mem_eq_size(0x303, 0x404)); + test_ok(Sha3CodeGen::mem_gt_size(0x404, 0x505)); + } + + #[test] + fn sha3_gadget_overflow_offset_and_zero_size() { + let bytecode = bytecode! { + PUSH1(0) + PUSH32(U256::MAX) + SHA3 + }; + + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .run(); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs index dbecca2252..798a1101fe 100644 --- a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs +++ b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs @@ -6,7 +6,10 @@ use crate::{ util::{ self, common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, + Transition::Delta, + }, from_bytes, math_gadget::{IsZeroGadget, LtWordGadget, MulAddWordsGadget}, sum, CachedRegion, Cell, @@ -51,7 +54,7 @@ impl ExecutionGadget for ShlShrGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::SHL_SHR; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let is_shl = OpcodeId::SHR.expr() - opcode.expr(); let is_shr = 1.expr() - is_shl.expr(); @@ -160,8 +163,7 @@ impl ExecutionGadget for ShlShrGadget { step: &ExecStep, ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let indices = [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]]; - let [pop1, pop2, push] = indices.map(|idx| block.rws[idx].stack_value()); + let [pop1, pop2, push] = [0, 1, 2].map(|idx| block.get_rws(step, idx).stack_value()); let shf0 = u64::from(pop1.to_le_bytes()[0]); let shf_lt256 = pop1 .to_le_bytes() @@ -177,7 +179,7 @@ impl ExecutionGadget for ShlShrGadget { U256::from(0) }; - let (quotient, remainder, dividend) = match step.opcode.unwrap() { + let (quotient, remainder, dividend) = match step.opcode().unwrap() { OpcodeId::SHL => (pop2, U256::from(0), push), OpcodeId::SHR => (push, pop2 - push * divisor, pop2), _ => unreachable!(), diff --git a/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs b/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs index 069e3ad740..8ee8357385 100644 --- a/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs +++ b/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs @@ -4,7 +4,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, from_bytes, math_gadget::{ComparisonGadget, IsEqualGadget, LtGadget}, select, CachedRegion, Cell, Word, @@ -39,7 +39,7 @@ impl ExecutionGadget for SignedComparatorGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::SCMP; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let a = cb.query_word_rlc(); @@ -153,7 +153,7 @@ impl ExecutionGadget for SignedComparatorGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); // SLT opcode is the default check in the SCMP gadget. Swap rw for SGT. self.is_sgt.assign( @@ -163,11 +163,11 @@ impl ExecutionGadget for SignedComparatorGadget { F::from(OpcodeId::SGT.as_u8() as u64), )?; let indices = if opcode == OpcodeId::SGT { - [step.rw_indices[1], step.rw_indices[0]] + [1, 0] } else { - [step.rw_indices[0], step.rw_indices[1]] + [0, 1] }; - let [a, b] = indices.map(|idx| block.rws[idx].stack_value()); + let [a, b] = indices.map(|idx| block.get_rws(step, idx).stack_value()); let a_le_bytes = a.to_le_bytes(); let b_le_bytes = b.to_le_bytes(); @@ -206,7 +206,7 @@ impl ExecutionGadget for SignedComparatorGadget { self.a_lt_b.assign( region, offset, - Value::known(if a < b { F::one() } else { F::zero() }), + Value::known(if a < b { F::ONE } else { F::ZERO }), )?; self.a.assign(region, offset, Some(a_le_bytes))?; @@ -230,7 +230,7 @@ mod test { bytecode.push(32, a); bytecode.write_op(opcode); } - bytecode.write_op(OpcodeId::STOP); + bytecode.op_stop(); CircuitTestBuilder::new_from_test_ctx( TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), diff --git a/zkevm-circuits/src/evm_circuit/execution/signextend.rs b/zkevm-circuits/src/evm_circuit/execution/signextend.rs index 0b4f28f6a2..ba16143688 100644 --- a/zkevm-circuits/src/evm_circuit/execution/signextend.rs +++ b/zkevm-circuits/src/evm_circuit/execution/signextend.rs @@ -6,7 +6,10 @@ use crate::{ util::{ and, common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, + Transition::Delta, + }, math_gadget::{IsEqualGadget, IsZeroGadget}, rlc, select, sum, CachedRegion, Cell, Word, }, @@ -35,7 +38,7 @@ impl ExecutionGadget for SignextendGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::SIGNEXTEND; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let index = cb.query_word_rlc(); let value = cb.query_word_rlc(); let sign_byte = cb.query_cell(); @@ -163,8 +166,8 @@ impl ExecutionGadget for SignextendGadget { self.same_context.assign_exec_step(region, offset, step)?; // Inputs/Outputs - let index = block.rws[step.rw_indices[0]].stack_value().to_le_bytes(); - let value = block.rws[step.rw_indices[1]].stack_value().to_le_bytes(); + let index = block.get_rws(step, 0).stack_value().to_le_bytes(); + let value = block.get_rws(step, 1).stack_value().to_le_bytes(); self.index.assign(region, offset, Some(index))?; self.value.assign(region, offset, Some(value))?; @@ -192,7 +195,7 @@ impl ExecutionGadget for SignextendGadget { // Set the sign byte let mut sign = 0u64; - if index[0] < 31 && msb_sum_zero == F::one() { + if index[0] < 31 && msb_sum_zero == F::ONE { sign = (value[index[0] as usize] >> 7) as u64; } self.sign_byte diff --git a/zkevm-circuits/src/evm_circuit/execution/sload.rs b/zkevm-circuits/src/evm_circuit/execution/sload.rs index 4f9622bd33..32759f9aa1 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sload.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sload.rs @@ -5,7 +5,7 @@ use crate::{ util::{ common_gadget::{SameContextGadget, SloadGasGadget}, constraint_builder::{ - ConstraintBuilder, ReversionInfo, StepStateTransition, Transition::Delta, + EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition::Delta, }, CachedRegion, Cell, }, @@ -34,7 +34,7 @@ impl ExecutionGadget for SloadGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::SLOAD; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let tx_id = cb.call_context(None, CallContextFieldTag::TxId); @@ -112,24 +112,23 @@ impl ExecutionGadget for SloadGadget { region, offset, Value::known( - call.callee_address + call.address .to_scalar() .expect("unexpected Address -> Scalar conversion failure"), ), )?; - - let [key, value] = - [step.rw_indices[4], step.rw_indices[6]].map(|idx| block.rws[idx].stack_value()); + let key = block.get_rws(step, 4).stack_value(); + let value = block.get_rws(step, 6).stack_value(); self.phase2_key .assign(region, offset, region.word_rlc(key))?; self.phase2_value .assign(region, offset, region.word_rlc(value))?; - let (_, committed_value) = block.rws[step.rw_indices[5]].aux_pair(); + let (_, committed_value) = block.get_rws(step, 5).aux_pair(); self.phase2_committed_value .assign(region, offset, region.word_rlc(committed_value))?; - let (_, is_warm) = block.rws[step.rw_indices[7]].tx_access_list_value_pair(); + let (_, is_warm) = block.get_rws(step, 7).tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/sstore.rs b/zkevm-circuits/src/evm_circuit/execution/sstore.rs index 8aee917e5c..fd0a90b558 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sstore.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sstore.rs @@ -6,7 +6,8 @@ use crate::{ util::{ common_gadget::{SameContextGadget, SstoreGasGadget}, constraint_builder::{ - ConstraintBuilder, ReversionInfo, StepStateTransition, Transition::Delta, + ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, + Transition::Delta, }, math_gadget::{IsEqualGadget, IsZeroGadget, LtGadget}, not, CachedRegion, Cell, @@ -47,7 +48,7 @@ impl ExecutionGadget for SstoreGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::SSTORE; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let tx_id = cb.call_context(None, CallContextFieldTag::TxId); @@ -92,7 +93,7 @@ impl ExecutionGadget for SstoreGadget { // Constrain for SSTORE reentrancy sentry. let sufficient_gas_sentry = LtGadget::construct( cb, - GasCost::SSTORE_SENTRY.0.expr(), + GasCost::SSTORE_SENTRY.expr(), cb.curr.state.gas_left.expr(), ); cb.require_equal( @@ -177,37 +178,37 @@ impl ExecutionGadget for SstoreGadget { region, offset, Value::known( - call.callee_address + call.address .to_scalar() .expect("unexpected Address -> Scalar conversion failure"), ), )?; - let [key, value] = - [step.rw_indices[5], step.rw_indices[6]].map(|idx| block.rws[idx].stack_value()); + let key = block.get_rws(step, 5).stack_value(); + let value = block.get_rws(step, 6).stack_value(); self.phase2_key .assign(region, offset, region.word_rlc(key))?; self.phase2_value .assign(region, offset, region.word_rlc(value))?; - let (_, value_prev, _, original_value) = block.rws[step.rw_indices[7]].storage_value_aux(); + let (_, value_prev, _, original_value) = block.get_rws(step, 7).storage_value_aux(); self.phase2_value_prev .assign(region, offset, region.word_rlc(value_prev))?; self.phase2_original_value .assign(region, offset, region.word_rlc(original_value))?; - let (_, is_warm) = block.rws[step.rw_indices[8]].tx_access_list_value_pair(); + let (_, is_warm) = block.get_rws(step, 8).tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; - let (tx_refund, tx_refund_prev) = block.rws[step.rw_indices[9]].tx_refund_value_pair(); + let (tx_refund, tx_refund_prev) = block.get_rws(step, 9).tx_refund_value_pair(); self.tx_refund_prev .assign(region, offset, Value::known(F::from(tx_refund_prev)))?; self.sufficient_gas_sentry.assign_value( region, offset, - Value::known(F::from(GasCost::SSTORE_SENTRY.0)), + Value::known(F::from(GasCost::SSTORE_SENTRY)), Value::known(F::from(step.gas_left)), )?; @@ -244,7 +245,7 @@ pub(crate) struct SstoreTxRefundGadget { impl SstoreTxRefundGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, tx_refund_old: Cell, value: Cell, value_prev: Cell, @@ -396,11 +397,11 @@ fn calc_expected_tx_refund( if !original_value.is_zero() { if value_prev.is_zero() { // recreate slot (2.2.1.1) - tx_refund_new -= GasCost::SSTORE_CLEARS_SCHEDULE.as_u64() + tx_refund_new -= GasCost::SSTORE_CLEARS_SCHEDULE } if value.is_zero() { // delete slot (2.2.1.2) - tx_refund_new += GasCost::SSTORE_CLEARS_SCHEDULE.as_u64() + tx_refund_new += GasCost::SSTORE_CLEARS_SCHEDULE } } @@ -408,10 +409,10 @@ fn calc_expected_tx_refund( if original_value == value { if original_value.is_zero() { // reset to original inexistent slot (2.2.2.1) - tx_refund_new += GasCost::SSTORE_SET.as_u64() - GasCost::WARM_ACCESS.as_u64(); + tx_refund_new += GasCost::SSTORE_SET - GasCost::WARM_ACCESS; } else { // reset to original existing slot (2.2.2.2) - tx_refund_new += GasCost::SSTORE_RESET.as_u64() - GasCost::WARM_ACCESS.as_u64(); + tx_refund_new += GasCost::SSTORE_RESET - GasCost::WARM_ACCESS; } } } diff --git a/zkevm-circuits/src/evm_circuit/execution/stop.rs b/zkevm-circuits/src/evm_circuit/execution/stop.rs index d58be6cf9d..5b635381c6 100644 --- a/zkevm-circuits/src/evm_circuit/execution/stop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/stop.rs @@ -5,7 +5,7 @@ use crate::{ util::{ common_gadget::RestoreContextGadget, constraint_builder::{ - ConstraintBuilder, StepStateTransition, + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::{Delta, Same}, }, math_gadget::IsZeroGadget, @@ -17,7 +17,7 @@ use crate::{ util::Expr, }; use bus_mapping::evm::OpcodeId; -use eth_types::Field; +use eth_types::{Field, ToWord}; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] @@ -33,7 +33,7 @@ impl ExecutionGadget for StopGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::STOP; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let code_length = cb.query_cell(); cb.bytecode_length(cb.curr.state.code_hash.expr(), code_length.expr()); let is_out_of_range = IsZeroGadget::construct( @@ -105,7 +105,7 @@ impl ExecutionGadget for StopGadget { ) -> Result<(), Error> { let code = block .bytecodes - .get(&call.code_hash) + .get(&call.code_hash.to_word()) .expect("could not find current environment's bytecode"); self.code_length.assign( region, @@ -116,10 +116,10 @@ impl ExecutionGadget for StopGadget { self.is_out_of_range.assign( region, offset, - F::from(code.bytes.len() as u64) - F::from(step.program_counter), + F::from(code.bytes.len() as u64) - F::from(step.pc), )?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/swap.rs b/zkevm-circuits/src/evm_circuit/execution/swap.rs index 55eb6f5556..7238aa5833 100644 --- a/zkevm-circuits/src/evm_circuit/execution/swap.rs +++ b/zkevm-circuits/src/evm_circuit/execution/swap.rs @@ -4,7 +4,7 @@ use crate::{ step::ExecutionState, util::{ common_gadget::SameContextGadget, - constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, @@ -25,7 +25,7 @@ impl ExecutionGadget for SwapGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::SWAP; - fn configure(cb: &mut ConstraintBuilder) -> Self { + fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let phase2_values = [cb.query_cell_phase2(), cb.query_cell_phase2()]; @@ -70,8 +70,8 @@ impl ExecutionGadget for SwapGadget { self.same_context.assign_exec_step(region, offset, step)?; for (cell, value) in self.phase2_values.iter().zip( - [step.rw_indices[0], step.rw_indices[1]] - .map(|idx| block.rws[idx].stack_value()) + [0, 1] + .map(|index| block.get_rws(step, index).stack_value()) .iter(), ) { cell.assign(region, offset, region.word_rlc(*value))?; @@ -94,7 +94,7 @@ mod test { PUSH32(lhs) }; for _ in 0..n - 1 { - bytecode.write_op(OpcodeId::DUP1); + bytecode.op_dup1(); } bytecode.append(&bytecode! { PUSH32(rhs) diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index a2262d21e9..e21530bb9c 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -16,16 +16,16 @@ pub const MAX_STEP_HEIGHT: usize = 21; pub(crate) const STEP_STATE_HEIGHT: usize = 1; /// Number of Advice Phase2 columns in the EVM circuit -pub(crate) const N_PHASE2_COLUMNS: usize = 4; +pub const N_PHASE2_COLUMNS: usize = 4; /// Number of Advice Phase1 columns in the EVM circuit -pub(crate) const N_PHASE1_COLUMNS: usize = +pub const N_PHASE1_COLUMNS: usize = STEP_WIDTH - EVM_LOOKUP_COLS - N_PHASE2_COLUMNS - N_COPY_COLUMNS - N_BYTE_LOOKUPS; // Number of copy columns -pub(crate) const N_COPY_COLUMNS: usize = 2; +pub const N_COPY_COLUMNS: usize = 2; -pub(crate) const N_BYTE_LOOKUPS: usize = 24; +pub const N_BYTE_LOOKUPS: usize = 24; /// Amount of lookup columns in the EVM circuit dedicated to lookups. pub(crate) const EVM_LOOKUP_COLS: usize = FIXED_TABLE_LOOKUPS @@ -38,7 +38,7 @@ pub(crate) const EVM_LOOKUP_COLS: usize = FIXED_TABLE_LOOKUPS + EXP_TABLE_LOOKUPS; /// Lookups done per row. -pub(crate) const LOOKUP_CONFIG: &[(Table, usize)] = &[ +pub const LOOKUP_CONFIG: &[(Table, usize)] = &[ (Table::Fixed, FIXED_TABLE_LOOKUPS), (Table::Tx, TX_TABLE_LOOKUPS), (Table::Rw, RW_TABLE_LOOKUPS), diff --git a/zkevm-circuits/src/evm_circuit/step.rs b/zkevm-circuits/src/evm_circuit/step.rs index f5bbfd110b..17af7a0531 100644 --- a/zkevm-circuits/src/evm_circuit/step.rs +++ b/zkevm-circuits/src/evm_circuit/step.rs @@ -7,8 +7,12 @@ use crate::{ }, util::Expr, }; -use bus_mapping::evm::OpcodeId; -use eth_types::Field; +use bus_mapping::{ + circuit_input_builder::ExecState, + error::{DepthError, ExecError, InsufficientBalanceError, NonceUintOverflowError, OogError}, + evm::OpcodeId, +}; +use eth_types::{evm_unimplemented, Field, ToWord}; use halo2_proofs::{ circuit::Value, plonk::{Advice, Column, ConstraintSystem, Error, Expression}, @@ -121,6 +125,168 @@ impl Display for ExecutionState { write!(f, "{:?}", self) } } +impl From<&ExecError> for ExecutionState { + fn from(error: &ExecError) -> Self { + match error { + ExecError::InvalidOpcode => ExecutionState::ErrorInvalidOpcode, + ExecError::StackOverflow | ExecError::StackUnderflow => ExecutionState::ErrorStack, + ExecError::WriteProtection => ExecutionState::ErrorWriteProtection, + ExecError::Depth(depth_error) => match depth_error { + DepthError::Call => ExecutionState::CALL_OP, + DepthError::Create => ExecutionState::CREATE, + DepthError::Create2 => ExecutionState::CREATE2, + }, + ExecError::InsufficientBalance(insufficient_balance_err) => { + match insufficient_balance_err { + InsufficientBalanceError::Call => ExecutionState::CALL_OP, + InsufficientBalanceError::Create => ExecutionState::CREATE, + InsufficientBalanceError::Create2 => ExecutionState::CREATE2, + } + } + ExecError::NonceUintOverflow(nonce_overflow_err) => match nonce_overflow_err { + NonceUintOverflowError::Create => ExecutionState::CREATE, + NonceUintOverflowError::Create2 => ExecutionState::CREATE2, + }, + ExecError::ContractAddressCollision => ExecutionState::CREATE2, + ExecError::InvalidCreationCode => ExecutionState::ErrorInvalidCreationCode, + ExecError::InvalidJump => ExecutionState::ErrorInvalidJump, + ExecError::ReturnDataOutOfBounds => ExecutionState::ErrorReturnDataOutOfBound, + ExecError::CodeStoreOutOfGas => ExecutionState::ErrorOutOfGasCodeStore, + ExecError::MaxCodeSizeExceeded => ExecutionState::ErrorMaxCodeSizeExceeded, + ExecError::OutOfGas(oog_error) => match oog_error { + OogError::Constant => ExecutionState::ErrorOutOfGasConstant, + OogError::StaticMemoryExpansion => { + ExecutionState::ErrorOutOfGasStaticMemoryExpansion + } + OogError::DynamicMemoryExpansion => { + ExecutionState::ErrorOutOfGasDynamicMemoryExpansion + } + OogError::MemoryCopy => ExecutionState::ErrorOutOfGasMemoryCopy, + OogError::AccountAccess => ExecutionState::ErrorOutOfGasAccountAccess, + OogError::CodeStore => ExecutionState::ErrorOutOfGasCodeStore, + OogError::Log => ExecutionState::ErrorOutOfGasLOG, + OogError::Exp => ExecutionState::ErrorOutOfGasEXP, + OogError::Sha3 => ExecutionState::ErrorOutOfGasSHA3, + OogError::Call => ExecutionState::ErrorOutOfGasCall, + OogError::SloadSstore => ExecutionState::ErrorOutOfGasSloadSstore, + OogError::Create2 => ExecutionState::ErrorOutOfGasCREATE2, + OogError::SelfDestruct => ExecutionState::ErrorOutOfGasSELFDESTRUCT, + }, + } + } +} +impl From<&ExecStep> for ExecutionState { + fn from(step: &ExecStep) -> Self { + if let Some(error) = step.error.as_ref() { + return error.into(); + } + match step.exec_state { + ExecState::Op(op) => { + if op.is_dup() { + return ExecutionState::DUP; + } + if op.is_push() { + return ExecutionState::PUSH; + } + if op.is_swap() { + return ExecutionState::SWAP; + } + if op.is_log() { + return ExecutionState::LOG; + } + + macro_rules! dummy { + ($name:expr) => {{ + evm_unimplemented!("{:?} is implemented with DummyGadget", $name); + $name + }}; + } + + match op { + OpcodeId::ADD | OpcodeId::SUB => ExecutionState::ADD_SUB, + OpcodeId::ADDMOD => ExecutionState::ADDMOD, + OpcodeId::ADDRESS => ExecutionState::ADDRESS, + OpcodeId::BALANCE => ExecutionState::BALANCE, + OpcodeId::MUL | OpcodeId::DIV | OpcodeId::MOD => ExecutionState::MUL_DIV_MOD, + OpcodeId::MULMOD => ExecutionState::MULMOD, + OpcodeId::SDIV | OpcodeId::SMOD => ExecutionState::SDIV_SMOD, + OpcodeId::EQ | OpcodeId::LT | OpcodeId::GT => ExecutionState::CMP, + OpcodeId::SLT | OpcodeId::SGT => ExecutionState::SCMP, + OpcodeId::SIGNEXTEND => ExecutionState::SIGNEXTEND, + OpcodeId::STOP => ExecutionState::STOP, + OpcodeId::AND => ExecutionState::BITWISE, + OpcodeId::XOR => ExecutionState::BITWISE, + OpcodeId::OR => ExecutionState::BITWISE, + OpcodeId::NOT => ExecutionState::NOT, + OpcodeId::EXP => ExecutionState::EXP, + OpcodeId::POP => ExecutionState::POP, + OpcodeId::PUSH32 => ExecutionState::PUSH, + OpcodeId::BYTE => ExecutionState::BYTE, + OpcodeId::MLOAD => ExecutionState::MEMORY, + OpcodeId::MSTORE => ExecutionState::MEMORY, + OpcodeId::MSTORE8 => ExecutionState::MEMORY, + OpcodeId::JUMPDEST => ExecutionState::JUMPDEST, + OpcodeId::JUMP => ExecutionState::JUMP, + OpcodeId::JUMPI => ExecutionState::JUMPI, + OpcodeId::GASPRICE => ExecutionState::GASPRICE, + OpcodeId::PC => ExecutionState::PC, + OpcodeId::MSIZE => ExecutionState::MSIZE, + OpcodeId::CALLER => ExecutionState::CALLER, + OpcodeId::CALLVALUE => ExecutionState::CALLVALUE, + OpcodeId::EXTCODEHASH => ExecutionState::EXTCODEHASH, + OpcodeId::EXTCODESIZE => ExecutionState::EXTCODESIZE, + OpcodeId::BLOCKHASH => ExecutionState::BLOCKHASH, + OpcodeId::TIMESTAMP | OpcodeId::NUMBER | OpcodeId::GASLIMIT => { + ExecutionState::BLOCKCTXU64 + } + OpcodeId::COINBASE => ExecutionState::BLOCKCTXU160, + OpcodeId::DIFFICULTY | OpcodeId::BASEFEE => ExecutionState::BLOCKCTXU256, + OpcodeId::GAS => ExecutionState::GAS, + OpcodeId::SAR => ExecutionState::SAR, + OpcodeId::SELFBALANCE => ExecutionState::SELFBALANCE, + OpcodeId::SHA3 => ExecutionState::SHA3, + OpcodeId::SHL | OpcodeId::SHR => ExecutionState::SHL_SHR, + OpcodeId::SLOAD => ExecutionState::SLOAD, + OpcodeId::SSTORE => ExecutionState::SSTORE, + OpcodeId::CALLDATASIZE => ExecutionState::CALLDATASIZE, + OpcodeId::CALLDATACOPY => ExecutionState::CALLDATACOPY, + OpcodeId::CHAINID => ExecutionState::CHAINID, + OpcodeId::ISZERO => ExecutionState::ISZERO, + OpcodeId::CALL + | OpcodeId::CALLCODE + | OpcodeId::DELEGATECALL + | OpcodeId::STATICCALL => ExecutionState::CALL_OP, + OpcodeId::ORIGIN => ExecutionState::ORIGIN, + OpcodeId::CODECOPY => ExecutionState::CODECOPY, + OpcodeId::CALLDATALOAD => ExecutionState::CALLDATALOAD, + OpcodeId::CODESIZE => ExecutionState::CODESIZE, + OpcodeId::EXTCODECOPY => ExecutionState::EXTCODECOPY, + OpcodeId::RETURN | OpcodeId::REVERT => ExecutionState::RETURN_REVERT, + OpcodeId::RETURNDATASIZE => ExecutionState::RETURNDATASIZE, + OpcodeId::RETURNDATACOPY => ExecutionState::RETURNDATACOPY, + // dummy ops + OpcodeId::CREATE => dummy!(ExecutionState::CREATE), + OpcodeId::CREATE2 => dummy!(ExecutionState::CREATE2), + OpcodeId::SELFDESTRUCT => dummy!(ExecutionState::SELFDESTRUCT), + _ => unimplemented!("unimplemented opcode {:?}", op), + } + } + ExecState::BeginTx => ExecutionState::BeginTx, + ExecState::EndTx => ExecutionState::EndTx, + ExecState::EndBlock => ExecutionState::EndBlock, + } + } +} + +pub trait HasExecutionState { + fn execution_state(&self) -> ExecutionState; +} + +impl HasExecutionState for ExecStep { + fn execution_state(&self) -> ExecutionState { + ExecutionState::from(self) + } +} impl ExecutionState { pub(crate) const fn as_u64(&self) -> u64 { @@ -137,9 +303,6 @@ impl ExecutionState { Self::ErrorInvalidOpcode | Self::ErrorStack | Self::ErrorWriteProtection - | Self::ErrorDepth - | Self::ErrorInsufficientBalance - | Self::ErrorContractAddressCollision | Self::ErrorInvalidCreationCode | Self::ErrorMaxCodeSizeExceeded | Self::ErrorInvalidJump @@ -166,7 +329,7 @@ impl ExecutionState { || self.halts_in_exception() } - pub(crate) fn responsible_opcodes(&self) -> Vec { + pub fn responsible_opcodes(&self) -> Vec { if matches!(self, Self::ErrorStack) { return OpcodeId::valid_opcodes() .into_iter() @@ -337,7 +500,7 @@ impl ExecutionState { /// Enum of Responsible opcode mapping to execution state. #[derive(Debug)] -pub(crate) enum ResponsibleOp { +pub enum ResponsibleOp { /// Raw opcode Op(OpcodeId), /// Corresponding to ExecutionState::ErrorStack @@ -352,7 +515,7 @@ impl From for ResponsibleOp { } impl ResponsibleOp { - pub(crate) fn opcode(&self) -> OpcodeId { + pub fn opcode(&self) -> OpcodeId { *match self { ResponsibleOp::Op(opcode) => opcode, ResponsibleOp::InvalidStackPtr(opcode, _) => opcode, @@ -442,17 +605,13 @@ impl DynamicSelectorHalf { self.target_odd.assign( region, offset, - Value::known(if odd { F::one() } else { F::zero() }), + Value::known(if odd { F::ONE } else { F::ZERO }), )?; for (index, cell) in self.target_pairs.iter().enumerate() { cell.assign( region, offset, - Value::known(if index == pair_index { - F::one() - } else { - F::zero() - }), + Value::known(if index == pair_index { F::ONE } else { F::ZERO }), )?; } Ok(()) @@ -556,35 +715,31 @@ impl Step { ) -> Result<(), Error> { self.state .execution_state - .assign(region, offset, step.execution_state as usize)?; - self.state.rw_counter.assign( - region, - offset, - Value::known(F::from(step.rw_counter as u64)), - )?; + .assign(region, offset, step.execution_state() as usize)?; + self.state + .rw_counter + .assign(region, offset, Value::known(F::from(step.rwc.into())))?; self.state .call_id - .assign(region, offset, Value::known(F::from(call.id as u64)))?; + .assign(region, offset, Value::known(F::from(call.call_id as u64)))?; self.state .is_root .assign(region, offset, Value::known(F::from(call.is_root as u64)))?; self.state.is_create.assign( region, offset, - Value::known(F::from(call.is_create as u64)), + Value::known(F::from(call.is_create() as u64)), )?; self.state .code_hash - .assign(region, offset, region.word_rlc(call.code_hash))?; - self.state.program_counter.assign( - region, - offset, - Value::known(F::from(step.program_counter as u64)), - )?; + .assign(region, offset, region.word_rlc(call.code_hash.to_word()))?; + self.state + .program_counter + .assign(region, offset, Value::known(F::from(step.pc)))?; self.state.stack_pointer.assign( region, offset, - Value::known(F::from(step.stack_pointer as u64)), + Value::known(F::from(step.stack_pointer())), )?; self.state .gas_left diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index c9eab33f0a..071941daad 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -1,4 +1,3 @@ -pub use crate::table::TxContextFieldTag; use crate::{ evm_circuit::step::{ExecutionState, ResponsibleOp}, impl_expr, @@ -35,37 +34,37 @@ impl FixedTableTag { pub fn build(&self) -> Box> { let tag = F::from(*self as u64); match self { - Self::Zero => Box::new((0..1).map(move |_| [tag, F::zero(), F::zero(), F::zero()])), + Self::Zero => Box::new((0..1).map(move |_| [tag, F::ZERO, F::ZERO, F::ZERO])), Self::Range5 => { - Box::new((0..5).map(move |value| [tag, F::from(value), F::zero(), F::zero()])) + Box::new((0..5).map(move |value| [tag, F::from(value), F::ZERO, F::ZERO])) } Self::Range16 => { - Box::new((0..16).map(move |value| [tag, F::from(value), F::zero(), F::zero()])) + Box::new((0..16).map(move |value| [tag, F::from(value), F::ZERO, F::ZERO])) } Self::Range32 => { - Box::new((0..32).map(move |value| [tag, F::from(value), F::zero(), F::zero()])) + Box::new((0..32).map(move |value| [tag, F::from(value), F::ZERO, F::ZERO])) } Self::Range64 => { - Box::new((0..64).map(move |value| [tag, F::from(value), F::zero(), F::zero()])) + Box::new((0..64).map(move |value| [tag, F::from(value), F::ZERO, F::ZERO])) } Self::Range128 => { - Box::new((0..128).map(move |value| [tag, F::from(value), F::zero(), F::zero()])) + Box::new((0..128).map(move |value| [tag, F::from(value), F::ZERO, F::ZERO])) } Self::Range256 => { - Box::new((0..256).map(move |value| [tag, F::from(value), F::zero(), F::zero()])) + Box::new((0..256).map(move |value| [tag, F::from(value), F::ZERO, F::ZERO])) } Self::Range512 => { - Box::new((0..512).map(move |value| [tag, F::from(value), F::zero(), F::zero()])) + Box::new((0..512).map(move |value| [tag, F::from(value), F::ZERO, F::ZERO])) } Self::Range1024 => { - Box::new((0..1024).map(move |value| [tag, F::from(value), F::zero(), F::zero()])) + Box::new((0..1024).map(move |value| [tag, F::from(value), F::ZERO, F::ZERO])) } Self::SignByte => Box::new((0..256).map(move |value| { [ tag, F::from(value), F::from((value >> 7) * 0xFFu64), - F::zero(), + F::ZERO, ] })), Self::BitwiseAnd => Box::new((0..256).flat_map(move |lhs| { @@ -82,7 +81,7 @@ impl FixedTableTag { execution_state.responsible_opcodes().into_iter().map( move |responsible_opcode| { let (op, aux) = match responsible_opcode { - ResponsibleOp::Op(op) => (op, F::zero()), + ResponsibleOp::Op(op) => (op, F::ZERO), ResponsibleOp::InvalidStackPtr(op, stack_ptr) => { (op, F::from(u64::from(stack_ptr))) } @@ -107,13 +106,13 @@ impl FixedTableTag { })), Self::ConstantGasCost => Box::new( OpcodeId::iter() - .filter(move |opcode| opcode.constant_gas_cost().0 > 0) + .filter(move |opcode| opcode.constant_gas_cost() > 0) .map(move |opcode| { [ tag, F::from(opcode.as_u64()), - F::from(opcode.constant_gas_cost().0), - F::zero(), + F::from(opcode.constant_gas_cost()), + F::ZERO, ] }), ), @@ -122,7 +121,7 @@ impl FixedTableTag { } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, EnumIter)] -pub(crate) enum Table { +pub enum Table { Fixed, Tx, Rw, diff --git a/zkevm-circuits/src/evm_circuit/test.rs b/zkevm-circuits/src/evm_circuit/test.rs new file mode 100644 index 0000000000..b282deb3b1 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/test.rs @@ -0,0 +1,36 @@ +#![allow(unused_imports)] +pub use super::EvmCircuit; +use crate::evm_circuit::{detect_fixed_table_tags, witness::Block}; + +use eth_types::{Field, Word}; +use rand::{ + distributions::uniform::{SampleRange, SampleUniform}, + random, thread_rng, Rng, +}; + +pub(crate) fn rand_range(range: R) -> T +where + T: SampleUniform, + R: SampleRange, +{ + thread_rng().gen_range(range) +} + +pub(crate) fn rand_bytes(n: usize) -> Vec { + (0..n).map(|_| random()).collect() +} + +pub(crate) fn rand_bytes_array() -> [u8; N] { + [(); N].map(|_| random()) +} + +pub(crate) fn rand_word() -> Word { + Word::from_big_endian(&rand_bytes_array::<32>()) +} + +impl EvmCircuit { + pub fn get_test_cicuit_from_block(block: Block) -> Self { + let fixed_table_tags = detect_fixed_table_tags(&block); + EvmCircuit::::new_dev(block, fixed_table_tags) + } +} diff --git a/zkevm-circuits/src/evm_circuit/util.rs b/zkevm-circuits/src/evm_circuit/util.rs index 1d29c97722..f8136f55db 100644 --- a/zkevm-circuits/src/evm_circuit/util.rs +++ b/zkevm-circuits/src/evm_circuit/util.rs @@ -5,14 +5,12 @@ use crate::{ }, table::Table, }, - table::RwTableTag, util::{query_expression, Challenges, Expr}, witness::{Block, ExecStep, Rw, RwMap}, }; use bus_mapping::state_db::CodeDB; use eth_types::{Address, Field, ToLittleEndian, ToWord, U256}; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{AssignedCell, Region, Value}, plonk::{Advice, Assigned, Column, ConstraintSystem, Error, Expression, VirtualCells}, poly::Rotation, @@ -87,7 +85,6 @@ impl Expr for &Cell { self.expression.clone() } } - pub struct CachedRegion<'r, 'b, F: Field> { region: &'r mut Region<'b, F>, advice: Vec>, @@ -108,7 +105,7 @@ impl<'r, 'b, F: Field> CachedRegion<'r, 'b, F> { ) -> Self { Self { region, - advice: vec![vec![F::zero(); height]; advice_columns.len()], + advice: vec![vec![F::ZERO; height]; advice_columns.len()], challenges, width_start: advice_columns[0].index(), height_start, @@ -196,10 +193,23 @@ impl<'r, 'b, F: Field> CachedRegion<'r, 'b, F> { .evm_word() .map(|r| rlc::value(&n.to_le_bytes(), r)) } + + pub fn keccak_rlc(&self, le_bytes: &[u8]) -> Value { + self.challenges + .keccak_input() + .map(|r| rlc::value(le_bytes, r)) + } + pub fn empty_code_hash_rlc(&self) -> Value { self.word_rlc(CodeDB::empty_code_hash().to_word()) } + pub fn code_hash(&self, n: U256) -> Value { + self.challenges + .evm_word() + .map(|r| rlc::value(&n.to_le_bytes(), r)) + } + /// Constrains a cell to have a constant value. /// /// Returns an error if the cell is in a column where equality has not been @@ -278,7 +288,7 @@ pub(crate) enum CellType { impl CellType { // The phase that given `Expression` becomes evaluateable. - fn expr_phase(expr: &Expression) -> u8 { + fn expr_phase(expr: &Expression) -> u8 { use Expression::*; match expr { Challenge(challenge) => challenge.phase() + 1, @@ -290,7 +300,7 @@ impl CellType { } /// Return the storage phase of phase - pub(crate) fn storage_for_phase(phase: u8) -> CellType { + pub(crate) fn storage_for_phase(phase: u8) -> CellType { match phase { 0 => CellType::StoragePhase1, 1 => CellType::StoragePhase2, @@ -299,8 +309,8 @@ impl CellType { } /// Return the storage cell of the expression - pub(crate) fn storage_for_expr(expr: &Expression) -> CellType { - Self::storage_for_phase::(Self::expr_phase::(expr)) + pub(crate) fn storage_for_expr(expr: &Expression) -> CellType { + Self::storage_for_phase(Self::expr_phase::(expr)) } } @@ -507,7 +517,7 @@ pub(crate) type MemoryAddress = RandomLinearCombination>(bytes: &[E]) -> Expression { debug_assert!( @@ -515,7 +525,7 @@ pub(crate) mod from_bytes { "Too many bytes to compose an integer in field" ); let mut value = 0.expr(); - let mut multiplier = F::one(); + let mut multiplier = F::ONE; for byte in bytes.iter() { value = value + byte.expr() * multiplier; multiplier *= F::from(256); @@ -523,13 +533,13 @@ pub(crate) mod from_bytes { value } - pub(crate) fn value(bytes: &[u8]) -> F { + pub(crate) fn value(bytes: &[u8]) -> F { debug_assert!( bytes.len() <= MAX_N_BYTES_INTEGER, "Too many bytes to compose an integer in field" ); - let mut value = F::zero(); - let mut multiplier = F::one(); + let mut value = F::ZERO; + let mut multiplier = F::ONE; for byte in bytes.iter() { value += F::from(*byte as u64) * multiplier; multiplier *= F::from(256); @@ -567,7 +577,7 @@ pub(crate) mod rlc { if !values.is_empty() { generic(values, randomness) } else { - F::zero() + F::ZERO } } @@ -584,13 +594,13 @@ pub(crate) mod rlc { } } -/// Returns 2**by as FieldExt -pub(crate) fn pow_of_two(by: usize) -> F { - F::from(2).pow(&[by as u64, 0, 0, 0]) +/// Returns 2**by as Field +pub(crate) fn pow_of_two(by: usize) -> F { + F::from(2).pow([by as u64, 0, 0, 0]) } /// Returns 2**by as Expression -pub(crate) fn pow_of_two_expr(by: usize) -> Expression { +pub(crate) fn pow_of_two_expr(by: usize) -> Expression { Expression::Constant(pow_of_two(by)) } @@ -628,7 +638,7 @@ pub(crate) fn is_precompiled(address: &Address) -> bool { /// Helper struct to read rw operations from a step sequentially. pub(crate) struct StepRws<'a> { rws: &'a RwMap, - rw_indices: &'a Vec<(RwTableTag, usize)>, + step: &'a ExecStep, offset: usize, } @@ -637,7 +647,7 @@ impl<'a> StepRws<'a> { pub(crate) fn new(block: &'a Block, step: &'a ExecStep) -> Self { Self { rws: &block.rws, - rw_indices: &step.rw_indices, + step, offset: 0, } } @@ -647,7 +657,7 @@ impl<'a> StepRws<'a> { } /// Return the next rw operation from the step. pub(crate) fn next(&mut self) -> Rw { - let rw = self.rws[self.rw_indices[self.offset]]; + let rw = self.rws[self.step.rw_index(self.offset)]; self.offset += 1; rw } diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index 2914907b9a..854eb40bba 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -1,6 +1,7 @@ use super::{ + constraint_builder::ConstrainBuilderCommon, from_bytes, - math_gadget::{IsEqualGadget, IsZeroGadget}, + math_gadget::{IsEqualGadget, IsZeroGadget, LtGadget}, memory_gadget::{MemoryAddressGadget, MemoryExpansionGadget}, CachedRegion, }; @@ -11,7 +12,7 @@ use crate::{ table::{FixedTableTag, Lookup}, util::{ constraint_builder::{ - ConstraintBuilder, ReversionInfo, StepStateTransition, + EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition::{Delta, Same, To}, }, math_gadget::{AddWordsGadget, RangeCheckGadget}, @@ -40,7 +41,7 @@ pub(crate) struct SameContextGadget { impl SameContextGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, opcode: Cell, step_state_transition: StepStateTransition, ) -> Self { @@ -75,15 +76,12 @@ impl SameContextGadget { offset: usize, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; - self.sufficient_gas_left.assign( - region, - offset, - F::from((step.gas_left - step.gas_cost) as u64), - )?; + self.sufficient_gas_left + .assign(region, offset, F::from(step.gas_left - step.gas_cost))?; Ok(()) } @@ -105,7 +103,7 @@ pub(crate) struct RestoreContextGadget { impl RestoreContextGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, is_success: Expression, // Expression for the number of rw lookups that occur after this gadget is constructed. subsequent_rw_lookups: Expression, @@ -224,8 +222,7 @@ impl RestoreContextGadget { [U256::zero(); 9] } else { [0, 1, 2, 3, 4, 5, 6, 7, 8] - .map(|i| step.rw_indices[i + rw_offset]) - .map(|idx| block.rws[idx].call_context_value()) + .map(|i| block.get_rws(step, i + rw_offset).call_context_value()) }; for (cell, value) in [ @@ -268,7 +265,7 @@ impl UpdateBalanceGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, address: Expression, updates: Vec>, reversion_info: Option<&mut ReversionInfo>, @@ -363,7 +360,7 @@ pub(crate) struct TransferWithGasFeeGadget { impl TransferWithGasFeeGadget { #[allow(clippy::too_many_arguments)] pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, sender_address: Expression, receiver_address: Expression, receiver_exists: Expression, @@ -494,7 +491,7 @@ pub(crate) struct TransferGadget { impl TransferGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, sender_address: Expression, receiver_address: Expression, receiver_exists: Expression, @@ -597,7 +594,7 @@ pub(crate) struct CommonCallGadget { impl CommonCallGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, is_call: Expression, is_callcode: Expression, is_delegatecall: Expression, @@ -775,14 +772,14 @@ impl CommonCallGadget is_empty_account: bool, ) -> Result { let gas_cost = if is_warm_prev { - GasCost::WARM_ACCESS.as_u64() + GasCost::WARM_ACCESS } else { - GasCost::COLD_ACCOUNT_ACCESS.as_u64() + GasCost::COLD_ACCOUNT_ACCESS } + if has_value { - GasCost::CALL_WITH_VALUE.as_u64() + GasCost::CALL_WITH_VALUE // Only CALL opcode could invoke transfer to make empty account into non-empty. + if is_call && is_empty_account { - GasCost::NEW_ACCOUNT.as_u64() + GasCost::NEW_ACCOUNT } else { 0 } @@ -801,7 +798,7 @@ pub(crate) struct SloadGasGadget { } impl SloadGasGadget { - pub(crate) fn construct(_cb: &mut ConstraintBuilder, is_warm: Expression) -> Self { + pub(crate) fn construct(_cb: &mut EVMConstraintBuilder, is_warm: Expression) -> Self { let gas_cost = select::expr( is_warm.expr(), GasCost::WARM_ACCESS.expr(), @@ -831,7 +828,7 @@ pub(crate) struct SstoreGasGadget { impl SstoreGasGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, value: Cell, value_prev: Cell, original_value: Cell, @@ -911,13 +908,11 @@ impl SstoreGasGadget { } pub(crate) fn cal_sload_gas_cost_for_assignment(is_warm: bool) -> u64 { - let gas_cost = if is_warm { + if is_warm { GasCost::WARM_ACCESS } else { GasCost::COLD_SLOAD - }; - - gas_cost.0 + } } pub(crate) fn cal_sstore_gas_cost_for_assignment( @@ -938,9 +933,9 @@ pub(crate) fn cal_sstore_gas_cost_for_assignment( GasCost::WARM_ACCESS }; if is_warm { - warm_case_gas.0 + warm_case_gas } else { - warm_case_gas.0 + GasCost::COLD_SLOAD.0 + warm_case_gas + GasCost::COLD_SLOAD } } @@ -952,7 +947,7 @@ pub(crate) struct CommonErrorGadget { impl CommonErrorGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, opcode: Expression, rw_counter_delta: Expression, ) -> Self { @@ -1039,3 +1034,121 @@ impl CommonErrorGadget { Ok(1u64) } } + +/// Check if the passed in word is within the specified byte range +/// (not overflow) and less than a maximum cap. +#[derive(Clone, Debug)] +pub(crate) struct WordByteCapGadget { + word: WordByteRangeGadget, + lt_cap: LtGadget, +} + +impl WordByteCapGadget { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder, cap: Expression) -> Self { + let word = WordByteRangeGadget::construct(cb); + let value = select::expr(word.overflow(), cap.expr(), word.valid_value()); + let lt_cap = LtGadget::construct(cb, value, cap); + + Self { word, lt_cap } + } + + /// Return true if within the specified byte range (not overflow), false if + /// overflow. No matter whether it is less than the cap. + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + original: U256, + cap: F, + ) -> Result { + let not_overflow = self.word.assign(region, offset, original)?; + + let value = if not_overflow { + let mut bytes = [0; 32]; + bytes[0..VALID_BYTES].copy_from_slice(&original.to_le_bytes()[0..VALID_BYTES]); + F::from_repr(bytes).unwrap() + } else { + cap + }; + + self.lt_cap.assign(region, offset, value, cap)?; + + Ok(not_overflow) + } + + pub(crate) fn lt_cap(&self) -> Expression { + self.lt_cap.expr() + } + + pub(crate) fn original_word(&self) -> Expression { + self.word.original_word() + } + + pub(crate) fn overflow(&self) -> Expression { + self.word.overflow() + } + + pub(crate) fn valid_value(&self) -> Expression { + self.word.valid_value() + } + + pub(crate) fn not_overflow(&self) -> Expression { + self.word.not_overflow() + } +} + +/// Check if the passed in word is within the specified byte range (not overflow). +#[derive(Clone, Debug)] +pub(crate) struct WordByteRangeGadget { + original: Word, + not_overflow: IsZeroGadget, +} + +impl WordByteRangeGadget { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder) -> Self { + debug_assert!(VALID_BYTES < 32); + + let original = cb.query_word_rlc(); + let not_overflow = IsZeroGadget::construct(cb, sum::expr(&original.cells[VALID_BYTES..])); + + Self { + original, + not_overflow, + } + } + + /// Return true if within the range, false if overflow. + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + original: U256, + ) -> Result { + self.original + .assign(region, offset, Some(original.to_le_bytes()))?; + + let overflow_hi = original.to_le_bytes()[VALID_BYTES..] + .iter() + .fold(0, |acc, val| acc + u64::from(*val)); + self.not_overflow + .assign(region, offset, F::from(overflow_hi))?; + + Ok(overflow_hi == 0) + } + + pub(crate) fn original_word(&self) -> Expression { + self.original.expr() + } + + pub(crate) fn overflow(&self) -> Expression { + not::expr(self.not_overflow()) + } + + pub(crate) fn valid_value(&self) -> Expression { + from_bytes::expr(&self.original.cells[..VALID_BYTES]) + } + + pub(crate) fn not_overflow(&self) -> Expression { + self.not_overflow.expr() + } +} diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index da85efa7c7..93ea3d51ca 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -6,12 +6,12 @@ use crate::{ util::{Cell, RandomLinearCombination, Word}, }, table::{ - AccountFieldTag, BytecodeFieldTag, CallContextFieldTag, RwTableTag, TxContextFieldTag, - TxLogFieldTag, TxReceiptFieldTag, + AccountFieldTag, BytecodeFieldTag, CallContextFieldTag, TxContextFieldTag, TxLogFieldTag, + TxReceiptFieldTag, }, util::{build_tx_log_expression, Challenges, Expr}, }; -use bus_mapping::state_db::EMPTY_CODE_HASH_LE; +use bus_mapping::{operation::Target, state_db::EMPTY_CODE_HASH_LE}; use eth_types::Field; use gadgets::util::not; use halo2_proofs::{ @@ -88,7 +88,7 @@ impl StepStateTransition { /// ReversionInfo counts `rw_counter` of reversion for gadgets, by tracking how /// many reversions that have been used. Gadgets should call -/// [`ConstraintBuilder::reversion_info`] to get [`ReversionInfo`] with +/// [`EVMConstraintBuilder::reversion_info`] to get [`ReversionInfo`] with /// `reversible_write_counter` initialized at current tracking one if no /// `call_id` is specified, then pass it as mutable reference when doing state /// write. @@ -141,40 +141,22 @@ impl ReversionInfo { } } -#[derive(Default)] -pub struct BaseConstraintBuilder { - pub constraints: Vec<(&'static str, Expression)>, - pub max_degree: usize, - pub condition: Option>, -} - -impl BaseConstraintBuilder { - pub(crate) fn new(max_degree: usize) -> Self { - BaseConstraintBuilder { - constraints: Vec::new(), - max_degree, - condition: None, - } - } +pub(crate) trait ConstrainBuilderCommon { + fn add_constraint(&mut self, name: &'static str, constraint: Expression); - pub(crate) fn require_zero(&mut self, name: &'static str, constraint: Expression) { + fn require_zero(&mut self, name: &'static str, constraint: Expression) { self.add_constraint(name, constraint); } - pub(crate) fn require_equal( - &mut self, - name: &'static str, - lhs: Expression, - rhs: Expression, - ) { + fn require_equal(&mut self, name: &'static str, lhs: Expression, rhs: Expression) { self.add_constraint(name, lhs - rhs); } - pub(crate) fn require_boolean(&mut self, name: &'static str, value: Expression) { + fn require_boolean(&mut self, name: &'static str, value: Expression) { self.add_constraint(name, value.clone() * (1.expr() - value)); } - pub(crate) fn require_in_set( + fn require_in_set( &mut self, name: &'static str, value: Expression, @@ -187,6 +169,40 @@ impl BaseConstraintBuilder { ); } + fn add_constraints(&mut self, constraints: Vec<(&'static str, Expression)>) { + for (name, constraint) in constraints { + self.add_constraint(name, constraint); + } + } +} + +#[derive(Default)] +pub struct BaseConstraintBuilder { + pub constraints: Vec<(&'static str, Expression)>, + pub max_degree: usize, + pub condition: Option>, +} + +impl ConstrainBuilderCommon for BaseConstraintBuilder { + fn add_constraint(&mut self, name: &'static str, constraint: Expression) { + let constraint = match &self.condition { + Some(condition) => condition.clone() * constraint, + None => constraint, + }; + self.validate_degree(constraint.degree(), name); + self.constraints.push((name, constraint)); + } +} + +impl BaseConstraintBuilder { + pub(crate) fn new(max_degree: usize) -> Self { + BaseConstraintBuilder { + constraints: Vec::new(), + max_degree, + condition: None, + } + } + pub(crate) fn condition( &mut self, condition: Expression, @@ -202,21 +218,6 @@ impl BaseConstraintBuilder { ret } - pub(crate) fn add_constraints(&mut self, constraints: Vec<(&'static str, Expression)>) { - for (name, constraint) in constraints { - self.add_constraint(name, constraint); - } - } - - pub(crate) fn add_constraint(&mut self, name: &'static str, constraint: Expression) { - let constraint = match &self.condition { - Some(condition) => condition.clone() * constraint, - None => constraint, - }; - self.validate_degree(constraint.degree(), name); - self.constraints.push((name, constraint)); - } - pub(crate) fn validate_degree(&self, degree: usize, name: &'static str) { if self.max_degree > 0 { debug_assert!( @@ -263,7 +264,7 @@ pub(crate) struct Constraints { pub(crate) not_step_last: Vec<(&'static str, Expression)>, } -pub(crate) struct ConstraintBuilder<'a, F> { +pub(crate) struct EVMConstraintBuilder<'a, F> { pub max_degree: usize, pub(crate) curr: Step, pub(crate) next: Step, @@ -280,7 +281,20 @@ pub(crate) struct ConstraintBuilder<'a, F> { stored_expressions: Vec>, } -impl<'a, F: Field> ConstraintBuilder<'a, F> { +impl<'a, F: Field> ConstrainBuilderCommon for EVMConstraintBuilder<'a, F> { + fn add_constraint(&mut self, name: &'static str, constraint: Expression) { + let constraint = self.split_expression( + name, + constraint * self.condition_expr(), + MAX_DEGREE - IMPLICIT_DEGREE, + ); + + self.validate_degree(constraint.degree(), name); + self.push_constraint(name, constraint); + } +} + +impl<'a, F: Field> EVMConstraintBuilder<'a, F> { pub(crate) fn new( curr: Step, next: Step, @@ -443,38 +457,6 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { self.word_rlc((*EMPTY_CODE_HASH_LE).map(|byte| byte.expr())) } - // Common - - pub(crate) fn require_zero(&mut self, name: &'static str, constraint: Expression) { - self.add_constraint(name, constraint); - } - - pub(crate) fn require_equal( - &mut self, - name: &'static str, - lhs: Expression, - rhs: Expression, - ) { - self.add_constraint(name, lhs - rhs); - } - - pub(crate) fn require_boolean(&mut self, name: &'static str, value: Expression) { - self.add_constraint(name, value.clone() * (1.expr() - value)); - } - - pub(crate) fn require_in_set( - &mut self, - name: &'static str, - value: Expression, - set: Vec>, - ) { - self.add_constraint( - name, - set.iter() - .fold(1.expr(), |acc, item| acc * (value.clone() - item.clone())), - ); - } - pub(crate) fn require_next_state(&mut self, execution_state: ExecutionState) { let next_state = self.next.execution_state_selector([execution_state]); self.add_constraint( @@ -695,7 +677,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { name: &str, counter: Expression, is_write: Expression, - tag: RwTableTag, + tag: Target, values: RwValues, ) { let name = format!("rw lookup {}", name); @@ -716,7 +698,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { &mut self, name: &'static str, is_write: Expression, - tag: RwTableTag, + tag: Target, values: RwValues, ) { self.rw_lookup_with_counter( @@ -744,7 +726,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { fn reversible_write( &mut self, name: &'static str, - tag: RwTableTag, + tag: Target, values: RwValues, reversion_info: Option<&mut ReversionInfo>, ) { @@ -787,7 +769,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { ) { self.reversible_write( "TxAccessListAccount write", - RwTableTag::TxAccessListAccount, + Target::TxAccessListAccount, RwValues::new( tx_id, account_address, @@ -811,7 +793,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { self.rw_lookup( "account access list read", false.expr(), - RwTableTag::TxAccessListAccount, + Target::TxAccessListAccount, RwValues::new( tx_id, account_address, @@ -836,7 +818,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { ) { self.reversible_write( "TxAccessListAccountStorage write", - RwTableTag::TxAccessListAccountStorage, + Target::TxAccessListAccountStorage, RwValues::new( tx_id, account_address, @@ -861,7 +843,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { self.rw_lookup( "TxAccessListAccountStorage read", false.expr(), - RwTableTag::TxAccessListAccountStorage, + Target::TxAccessListAccountStorage, RwValues::new( tx_id, account_address, @@ -881,7 +863,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { self.rw_lookup( "TxRefund read", false.expr(), - RwTableTag::TxRefund, + Target::TxRefund, RwValues::new( tx_id, 0.expr(), @@ -904,7 +886,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { ) { self.reversible_write( "TxRefund write", - RwTableTag::TxRefund, + Target::TxRefund, RwValues::new( tx_id, 0.expr(), @@ -930,7 +912,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { self.rw_lookup( "Account read", false.expr(), - RwTableTag::Account, + Target::Account, RwValues::new( 0.expr(), account_address, @@ -954,7 +936,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { ) { self.reversible_write( "Account write", - RwTableTag::Account, + Target::Account, RwValues::new( 0.expr(), account_address, @@ -982,7 +964,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { self.rw_lookup( "account_storage_read", false.expr(), - RwTableTag::AccountStorage, + Target::Storage, RwValues::new( tx_id, account_address, @@ -1009,7 +991,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { ) { self.reversible_write( "AccountStorage write", - RwTableTag::AccountStorage, + Target::Storage, RwValues::new( tx_id, account_address, @@ -1060,7 +1042,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { self.rw_lookup( "CallContext lookup", is_write, - RwTableTag::CallContext, + Target::CallContext, RwValues::new( call_id.unwrap_or_else(|| self.curr.state.call_id.expr()), 0.expr(), @@ -1135,7 +1117,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { self.rw_lookup( "Stack lookup", is_write, - RwTableTag::Stack, + Target::Stack, RwValues::new( self.curr.state.call_id.expr(), self.curr.state.stack_pointer.expr() + stack_pointer_offset, @@ -1161,7 +1143,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { self.rw_lookup( "Memory lookup", is_write, - RwTableTag::Memory, + Target::Memory, RwValues::new( call_id.unwrap_or_else(|| self.curr.state.call_id.expr()), memory_address, @@ -1186,7 +1168,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { self.rw_lookup( "log data lookup", 1.expr(), - RwTableTag::TxLog, + Target::TxLog, RwValues::new( tx_id, build_tx_log_expression(index, field_tag.expr(), log_id), @@ -1212,7 +1194,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { self.rw_lookup( "tx receipt lookup", is_write, - RwTableTag::TxReceipt, + Target::TxReceipt, RwValues::new( tx_id, 0.expr(), @@ -1233,7 +1215,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { "Start lookup", counter, 0.expr(), - RwTableTag::Start, + Target::Start, RwValues { id: 0.expr(), address: 0.expr(), @@ -1378,23 +1360,6 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { ret } - pub(crate) fn add_constraints(&mut self, constraints: Vec<(&'static str, Expression)>) { - for (name, constraint) in constraints { - self.add_constraint(name, constraint); - } - } - - pub(crate) fn add_constraint(&mut self, name: &'static str, constraint: Expression) { - let constraint = self.split_expression( - name, - constraint * self.condition_expr(), - MAX_DEGREE - IMPLICIT_DEGREE, - ); - - self.validate_degree(constraint.degree(), name); - self.push_constraint(name, constraint); - } - /// TODO: Doc fn constraint_at_location( &mut self, diff --git a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs index e2a1948ca3..eef04dbace 100644 --- a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs +++ b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs @@ -1,7 +1,7 @@ use crate::evm_circuit::{ step::ExecutionState, table::Table, - util::{constraint_builder::ConstraintBuilder, CellType}, + util::{constraint_builder::EVMConstraintBuilder, CellType}, }; use eth_types::Field; use itertools::Itertools; @@ -12,7 +12,7 @@ type ColumnSize = (usize, usize, usize); /// Instrument captures metrics during the compilation of a circuit. #[derive(Clone, Debug, Default)] -pub(crate) struct Instrument { +pub struct Instrument { // States -> Cell Types -> (width, height, num_cells) states: Vec<(ExecutionState, StepSize)>, } @@ -20,10 +20,10 @@ pub(crate) struct Instrument { impl Instrument { /// Collects `CellManager` stats from a compiled EVMCircuit in order to /// extract metrics. - pub(crate) fn on_gadget_built<'a, F: Field>( + pub(crate) fn on_gadget_built( &mut self, execution_state: ExecutionState, - cb: &ConstraintBuilder<'a, F>, + cb: &EVMConstraintBuilder, ) { let sizes = cb .curr @@ -38,7 +38,7 @@ impl Instrument { /// Dissasembles the instrumentation data and returns a collection of /// `ExecStateReport`s. One for each EVM `ExecutionState`. - pub(crate) fn analyze(&self) -> Vec { + pub fn analyze(&self) -> Vec { let mut report_collection = vec![]; for (state, sizes) in &self.states { // Create a state report @@ -111,20 +111,20 @@ impl Instrument { /// Struct which contains a Cost/ColumnType report for a particular EVM /// `ExecutionStep`. #[derive(Clone, Debug, Default)] -pub(crate) struct ExecStateReport { - pub(crate) state: ExecutionState, - pub(crate) storage_1: StateReportRow, - pub(crate) storage_2: StateReportRow, - pub(crate) storage_perm: StateReportRow, - pub(crate) byte_lookup: StateReportRow, - pub(crate) fixed_table: StateReportRow, - pub(crate) tx_table: StateReportRow, - pub(crate) rw_table: StateReportRow, - pub(crate) bytecode_table: StateReportRow, - pub(crate) block_table: StateReportRow, - pub(crate) copy_table: StateReportRow, - pub(crate) keccak_table: StateReportRow, - pub(crate) exp_table: StateReportRow, +pub struct ExecStateReport { + pub state: ExecutionState, + pub storage_1: StateReportRow, + pub storage_2: StateReportRow, + pub storage_perm: StateReportRow, + pub byte_lookup: StateReportRow, + pub fixed_table: StateReportRow, + pub tx_table: StateReportRow, + pub rw_table: StateReportRow, + pub bytecode_table: StateReportRow, + pub block_table: StateReportRow, + pub copy_table: StateReportRow, + pub keccak_table: StateReportRow, + pub exp_table: StateReportRow, } impl From for ExecStateReport { @@ -148,17 +148,17 @@ impl From<&ExecutionState> for ExecStateReport { /// Struct that contains all of the measurament values required to evaluate the /// costs of a particular `ColumnType` of an `ExecStateReport` #[derive(Debug, Clone, Default)] -pub(crate) struct StateReportRow { +pub struct StateReportRow { // Given a rigion of x columns and y rows, we have x * y cells available for computation. - pub(crate) available_cells: usize, + pub available_cells: usize, // The cells not used in the computation in the x*y region. These are the wasted cells. - pub(crate) unused_cells: usize, + pub unused_cells: usize, // The cells used in the computation in the x*y region. - pub(crate) used_cells: usize, + pub used_cells: usize, // The largest y within all the `CellType`. - pub(crate) top_height: usize, + pub top_height: usize, // If we fully utilize y, how large is the x really needed? - pub(crate) used_columns: usize, + pub used_columns: usize, // The percentage of cells used in computation in the x * y region. - pub(crate) utilization: f64, + pub utilization: f64, } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/abs_word.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/abs_word.rs index d92ca58822..e7bbe5dc8e 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/abs_word.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/abs_word.rs @@ -1,6 +1,10 @@ use crate::{ evm_circuit::util::{ - self, constraint_builder::ConstraintBuilder, from_bytes, math_gadget::*, CachedRegion, + self, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + from_bytes, + math_gadget::*, + CachedRegion, }, util::Expr, }; @@ -23,7 +27,7 @@ pub(crate) struct AbsWordGadget { } impl AbsWordGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder) -> Self { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder) -> Self { let x = cb.query_word_rlc(); let x_abs = cb.query_word_rlc(); let sum = cb.query_word_rlc(); @@ -100,6 +104,7 @@ impl AbsWordGadget { #[cfg(test)] mod tests { use super::{super::test_util::*, *}; + use crate::evm_circuit::util::constraint_builder::ConstrainBuilderCommon; use eth_types::{Word, U256}; use halo2_proofs::{halo2curves::bn256::Fr, plonk::Error}; @@ -110,7 +115,7 @@ mod tests { } impl MathGadgetContainer for AbsWordGadgetContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let absword_gadget = AbsWordGadget::::construct(cb); cb.require_equal( "is_neg is correct", diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs index 87673f208b..704fd60fef 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs @@ -1,7 +1,8 @@ use crate::{ evm_circuit::util::{ - self, constraint_builder::ConstraintBuilder, from_bytes, pow_of_two_expr, split_u256, sum, - CachedRegion, Cell, + self, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + from_bytes, pow_of_two_expr, split_u256, sum, CachedRegion, Cell, }, util::Expr, }; @@ -22,7 +23,7 @@ impl AddWordsGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, addends: [util::Word; N_ADDENDS], sum: util::Word, ) -> Self { @@ -167,7 +168,7 @@ mod tests { impl MathGadgetContainer for AddWordsTestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let addends = [(); N_ADDENDS].map(|_| cb.query_word_rlc()); let sum = cb.query_word_rlc(); let addwords_gadget = AddWordsGadget::::construct( diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/batched_is_zero.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/batched_is_zero.rs index 4d1817933b..02c514843a 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/batched_is_zero.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/batched_is_zero.rs @@ -1,6 +1,7 @@ use crate::{ evm_circuit::util::{ - constraint_builder::ConstraintBuilder, transpose_val_ret, CachedRegion, Cell, CellType, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + transpose_val_ret, CachedRegion, Cell, CellType, }, util::Expr, }; @@ -17,14 +18,14 @@ pub struct BatchedIsZeroGadget { } impl BatchedIsZeroGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder, values: [Expression; N]) -> Self { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder, values: [Expression; N]) -> Self { let max_values_phase = values .iter() .map(CellType::expr_phase) .max() .expect("BatchedIsZeroGadget needs at least one expression"); - let cell_type = CellType::storage_for_phase::(max_values_phase); + let cell_type = CellType::storage_for_phase(max_values_phase); let is_zero = cb.query_bool_with_type(cell_type); let nonempty_witness = cb.query_cell_with_type(cell_type); @@ -62,9 +63,9 @@ impl BatchedIsZeroGadget { if let Some(inverse) = values.iter().find_map(|value| Option::from(value.invert())) { self.nonempty_witness .assign(region, offset, Value::known(inverse))?; - F::zero() + F::ZERO } else { - F::one() + F::ONE }; self.is_zero.assign(region, offset, Value::known(is_zero))?; @@ -97,7 +98,7 @@ mod tests { } impl MathGadgetContainer for IsZeroGadgetTestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let nums = [(); N].map(|_| cb.query_cell()); let z_gadget = BatchedIsZeroGadget::::construct( cb, diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/binary_number.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/binary_number.rs index 6484ab4a6e..d872bfc6b7 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/binary_number.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/binary_number.rs @@ -5,7 +5,10 @@ use halo2_proofs::{ plonk::{Error, Expression}, }; -use crate::evm_circuit::util::{constraint_builder::ConstraintBuilder, CachedRegion, Cell}; +use crate::evm_circuit::util::{ + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + CachedRegion, Cell, +}; #[derive(Clone, Debug)] pub struct BinaryNumberGadget { @@ -13,7 +16,7 @@ pub struct BinaryNumberGadget { } impl BinaryNumberGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder, value: Expression) -> Self { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder, value: Expression) -> Self { let bits = array_init::array_init(|_| cb.query_bool()); // the binary representation of value must be correct. diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs index db1b81ffaf..e57dbaade3 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs @@ -1,7 +1,10 @@ use crate::{ evm_circuit::{ param::N_BYTES_WORD, - util::{constraint_builder::ConstraintBuilder, sum, CachedRegion, Cell}, + util::{ + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + sum, CachedRegion, Cell, + }, }, util::Expr, }; @@ -25,7 +28,7 @@ pub(crate) struct ByteSizeGadget { impl ByteSizeGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, values: [Expression; N_BYTES_WORD], ) -> Self { let most_significant_nonzero_byte_index = [(); N_BYTES_WORD + 1].map(|()| cb.query_bool()); @@ -74,7 +77,7 @@ impl ByteSizeGadget { byte_index.assign( region, offset, - Value::known(if i == byte_size { F::one() } else { F::zero() }), + Value::known(if i == byte_size { F::ONE } else { F::ZERO }), )?; } if byte_size > 0 { @@ -92,7 +95,7 @@ impl ByteSizeGadget { self.most_significant_nonzero_byte_inverse.assign( region, offset, - Value::known(F::zero()), + Value::known(F::ZERO), )?; } Ok(()) @@ -123,7 +126,7 @@ mod tests { } impl MathGadgetContainer for ByteSizeGadgetContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let value_rlc = cb.query_word_rlc(); let bytesize_gadget = ByteSizeGadget::::construct( cb, diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs index 758484a65b..1a6f657bf1 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs @@ -1,6 +1,6 @@ use crate::{ evm_circuit::util::{ - self, constraint_builder::ConstraintBuilder, from_bytes, math_gadget::*, select, + self, constraint_builder::EVMConstraintBuilder, from_bytes, math_gadget::*, select, CachedRegion, }, util::Expr, @@ -19,7 +19,7 @@ pub(crate) struct CmpWordsGadget { impl CmpWordsGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, a: &util::Word, b: &util::Word, ) -> Self { @@ -86,6 +86,7 @@ impl CmpWordsGadget { #[cfg(test)] mod tests { use super::{test_util::*, *}; + use crate::evm_circuit::util::constraint_builder::ConstrainBuilderCommon; use eth_types::Word; use halo2_proofs::{halo2curves::bn256::Fr, plonk::Error}; @@ -100,7 +101,7 @@ mod tests { impl MathGadgetContainer for CmpWordGadgetTestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let a = cb.query_word_rlc(); let b = cb.query_word_rlc(); let cmp_gadget = CmpWordsGadget::::construct(cb, &a, &b); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs index fccd872772..5568ec54c4 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs @@ -1,5 +1,5 @@ use crate::evm_circuit::util::{ - constraint_builder::ConstraintBuilder, math_gadget::*, sum, CachedRegion, + constraint_builder::EVMConstraintBuilder, math_gadget::*, sum, CachedRegion, }; use eth_types::Field; use halo2_proofs::plonk::{Error, Expression}; @@ -17,7 +17,7 @@ pub struct ComparisonGadget { impl ComparisonGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, lhs: Expression, rhs: Expression, ) -> Self { @@ -51,7 +51,7 @@ impl ComparisonGadget { #[cfg(test)] mod tests { use super::{test_util::*, *}; - use crate::evm_circuit::util::Cell; + use crate::evm_circuit::util::{constraint_builder::ConstrainBuilderCommon, Cell}; use eth_types::*; use halo2_proofs::{halo2curves::bn256::Fr, plonk::Error}; @@ -66,7 +66,7 @@ mod tests { impl MathGadgetContainer for ComparisonTestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let a = cb.query_cell(); let b = cb.query_cell(); let cmp_gadget = ComparisonGadget::::construct(cb, a.expr(), b.expr()); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs index 824c035ef2..838cc585c2 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs @@ -1,7 +1,8 @@ use crate::{ evm_circuit::util::{ - constraint_builder::ConstraintBuilder, math_gadget::*, transpose_val_ret, CachedRegion, - Cell, CellType, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + math_gadget::*, + transpose_val_ret, CachedRegion, Cell, CellType, }, util::Expr, }; @@ -26,7 +27,7 @@ pub struct ConstantDivisionGadget { impl ConstantDivisionGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, numerator: Expression, denominator: u64, ) -> Self { @@ -125,7 +126,7 @@ mod tests { > MathGadgetContainer for ConstantDivisionTestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let a = cb.query_cell(); let constdiv_gadget = ConstantDivisionGadget::::construct(cb, a.expr(), DENOMINATOR); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs index 1b5bb41128..1c472181c6 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs @@ -1,5 +1,5 @@ use crate::evm_circuit::util::{ - constraint_builder::ConstraintBuilder, math_gadget::*, transpose_val_ret, CachedRegion, + constraint_builder::EVMConstraintBuilder, math_gadget::*, transpose_val_ret, CachedRegion, }; use eth_types::Field; use halo2_proofs::{ @@ -15,7 +15,7 @@ pub struct IsEqualGadget { impl IsEqualGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, lhs: Expression, rhs: Expression, ) -> Self { @@ -55,7 +55,9 @@ impl IsEqualGadget { #[cfg(test)] mod tests { use super::{test_util::*, *}; - use crate::evm_circuit::util::{CachedRegion, Cell}; + use crate::evm_circuit::util::{ + constraint_builder::ConstrainBuilderCommon, CachedRegion, Cell, + }; use eth_types::*; use halo2_proofs::{halo2curves::bn256::Fr, plonk::Error}; @@ -68,7 +70,7 @@ mod tests { } impl MathGadgetContainer for IsEqualGadgetTestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let a = cb.query_cell(); let b = cb.query_cell(); let eq_gadget = IsEqualGadget::::construct(cb, a.expr(), b.expr()); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero.rs index 1f0de17ca3..9f7d3f252f 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero.rs @@ -1,6 +1,7 @@ use crate::{ evm_circuit::util::{ - constraint_builder::ConstraintBuilder, transpose_val_ret, CachedRegion, Cell, CellType, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + transpose_val_ret, CachedRegion, Cell, CellType, }, util::Expr, }; @@ -18,7 +19,7 @@ pub struct IsZeroGadget { } impl IsZeroGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder, value: Expression) -> Self { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder, value: Expression) -> Self { let inverse = cb.query_cell_with_type(CellType::storage_for_expr(&value)); let is_zero = 1.expr() - (value.clone() * inverse.expr()); @@ -45,12 +46,12 @@ impl IsZeroGadget { offset: usize, value: F, ) -> Result { - let inverse = value.invert().unwrap_or(F::zero()); + let inverse = value.invert().unwrap_or(F::ZERO); self.inverse.assign(region, offset, Value::known(inverse))?; Ok(if value.is_zero().into() { - F::one() + F::ONE } else { - F::zero() + F::ZERO }) } @@ -66,8 +67,13 @@ impl IsZeroGadget { #[cfg(test)] mod tests { + use crate::{ + evm_circuit::util::{constraint_builder::ConstrainBuilderCommon, Cell}, + util::Expr, + }; + use super::{super::test_util::*, *}; - use crate::{evm_circuit::util::Cell, util::Expr}; + use eth_types::{ToScalar, Word}; use halo2_proofs::{halo2curves::bn256::Fr, plonk::Error}; @@ -79,7 +85,7 @@ mod tests { } impl MathGadgetContainer for IsZeroGadgetTestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let n = cb.query_cell(); let z_gadget = IsZeroGadget::::construct(cb, n.expr()); cb.require_equal("Input is zero", z_gadget.expr(), 1.expr()); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs index e43af2a23f..51366b5480 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::util::{ - constraint_builder::ConstraintBuilder, from_bytes, pow_of_two, transpose_val_ret, - CachedRegion, Cell, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + from_bytes, pow_of_two, transpose_val_ret, CachedRegion, Cell, }, util::Expr, }; @@ -30,7 +30,7 @@ pub struct LtGadget { impl LtGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, lhs: Expression, rhs: Expression, ) -> Self { @@ -64,11 +64,11 @@ impl LtGadget { self.lt.assign( region, offset, - Value::known(if lt { F::one() } else { F::zero() }), + Value::known(if lt { F::ONE } else { F::ZERO }), )?; // Set the bytes of diff - let diff = (lhs - rhs) + (if lt { self.range } else { F::zero() }); + let diff = (lhs - rhs) + (if lt { self.range } else { F::ZERO }); let diff_bytes = diff.to_repr(); for (idx, diff) in self.diff.iter().enumerate() { diff.assign( @@ -78,7 +78,7 @@ impl LtGadget { )?; } - Ok((if lt { F::one() } else { F::zero() }, diff_bytes.to_vec())) + Ok((if lt { F::ONE } else { F::ZERO }, diff_bytes.to_vec())) } pub(crate) fn diff_bytes(&self) -> Vec> { @@ -115,7 +115,7 @@ mod tests { } impl MathGadgetContainer for LtGadgetTestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let a = cb.query_cell(); let b = cb.query_cell(); let lt_gadget = LtGadget::::construct(cb, a.expr(), b.expr()); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs index 04f674c97a..601d9a88e7 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs @@ -1,5 +1,5 @@ use crate::evm_circuit::util::{ - self, constraint_builder::ConstraintBuilder, from_bytes, math_gadget::*, split_u256, + self, constraint_builder::EVMConstraintBuilder, from_bytes, math_gadget::*, split_u256, CachedRegion, }; use eth_types::{Field, Word}; @@ -15,7 +15,7 @@ pub struct LtWordGadget { impl LtWordGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, lhs: &util::Word, rhs: &util::Word, ) -> Self { @@ -67,6 +67,8 @@ impl LtWordGadget { #[cfg(test)] mod tests { + use crate::evm_circuit::util::constraint_builder::ConstrainBuilderCommon; + use super::{test_util::*, *}; use eth_types::*; use halo2_proofs::{halo2curves::bn256::Fr, plonk::Error}; @@ -80,7 +82,7 @@ mod tests { } impl MathGadgetContainer for LtWordTestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let a = cb.query_word_rlc(); let b = cb.query_word_rlc(); let ltword_gadget = LtWordGadget::::construct(cb, &a, &b); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs index a5bb9ffdfb..e17f9c7b39 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs @@ -1,5 +1,6 @@ use crate::evm_circuit::util::{ - constraint_builder::ConstraintBuilder, math_gadget::*, select, transpose_val_ret, CachedRegion, + constraint_builder::EVMConstraintBuilder, math_gadget::*, select, transpose_val_ret, + CachedRegion, }; use eth_types::Field; use halo2_proofs::{ @@ -18,7 +19,7 @@ pub struct MinMaxGadget { impl MinMaxGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, lhs: Expression, rhs: Expression, ) -> Self { @@ -69,7 +70,7 @@ impl MinMaxGadget { #[cfg(test)] mod tests { use super::{test_util::*, *}; - use crate::evm_circuit::util::Cell; + use crate::evm_circuit::util::{constraint_builder::ConstrainBuilderCommon, Cell}; use eth_types::{ToScalar, Word}; use halo2_proofs::{halo2curves::bn256::Fr, plonk::Error}; @@ -84,7 +85,7 @@ mod tests { impl MathGadgetContainer for MinMaxTestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let a = cb.query_cell(); let b = cb.query_cell(); let minmax_gadget = MinMaxGadget::::construct(cb, a.expr(), b.expr()); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs index 2d03e1c63e..d4b65f20b0 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs @@ -1,6 +1,9 @@ use crate::{ evm_circuit::util::{ - self, constraint_builder::ConstraintBuilder, math_gadget::*, sum, CachedRegion, + self, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + math_gadget::*, + sum, CachedRegion, }, util::Expr, }; @@ -27,7 +30,7 @@ pub(crate) struct ModGadget { lt: LtWordGadget, } impl ModGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder, words: [&util::Word; 3]) -> Self { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder, words: [&util::Word; 3]) -> Self { let (a, n, r) = (words[0], words[1], words[2]); let k = cb.query_word_rlc(); let a_or_zero = cb.query_word_rlc(); @@ -113,7 +116,7 @@ mod tests { } impl MathGadgetContainer for ModGadgetTestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let a = cb.query_word_rlc(); let n = cb.query_word_rlc(); let r = cb.query_word_rlc(); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs index 4d3abd35fd..1a212906a7 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs @@ -1,7 +1,8 @@ use crate::{ evm_circuit::util::{ - self, constraint_builder::ConstraintBuilder, from_bytes, pow_of_two_expr, split_u256, - split_u256_limb64, CachedRegion, Cell, + self, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + from_bytes, pow_of_two_expr, split_u256, split_u256_limb64, CachedRegion, Cell, }, util::Expr, }; @@ -24,7 +25,7 @@ use halo2_proofs::{ /// t2 = a0 * b2 + a2 * b0 + a1 * b1, contribute to above 128 bit /// t3 = a0 * b3 + a3 * b0 + a2 * b1 + a1 * b2, contribute to above 192 bit /// -/// so t0 ~ t1 include all contributions to the low 256-bit of product, with +/// so t0 ~ t3 include all contributions to the low 256-bit of product, with /// a maximum 68-bit radix (the part higher than 256-bit), denoted as carry_hi /// Similarly, we define carry_lo as the radix of contributions to the low /// 128-bit of the product. @@ -32,8 +33,8 @@ use halo2_proofs::{ /// allocate 9 bytes for them each /// /// Finally we just prove: -/// t0 + t1 * 2^64 = + carry_lo -/// t2 + t3 * 2^64 + carry_lo = + carry_hi +/// t0 + t1 * 2^64 = + carry_lo * 2^128 +/// t2 + t3 * 2^64 + carry_lo = + carry_hi * 2^128 /// /// Last, we sum the parts that are higher than 256-bit in the multiplication /// into overflow @@ -49,7 +50,7 @@ pub(crate) struct MulAddWordsGadget { } impl MulAddWordsGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder, words: [&util::Word; 4]) -> Self { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder, words: [&util::Word; 4]) -> Self { let (a, b, c, d) = (words[0], words[1], words[2], words[3]); let carry_lo = cb.query_bytes(); let carry_hi = cb.query_bytes(); @@ -165,7 +166,7 @@ mod tests { } impl MathGadgetContainer for MulAddGadgetContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let a = cb.query_word_rlc(); let b = cb.query_word_rlc(); let c = cb.query_word_rlc(); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs index 5a362b0012..7548cb8ada 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs @@ -1,7 +1,8 @@ use crate::{ evm_circuit::util::{ - self, constraint_builder::ConstraintBuilder, from_bytes, pow_of_two_expr, split_u256, - split_u256_limb64, CachedRegion, Cell, + self, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + from_bytes, pow_of_two_expr, split_u256, split_u256_limb64, CachedRegion, Cell, }, util::Expr, }; @@ -50,7 +51,7 @@ impl MulAddWords512Gadget { /// The words argument is: a, b, d, e /// Addend is the optional c. pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, words: [&util::Word; 4], addend: Option<&util::Word>, ) -> Self { @@ -210,7 +211,7 @@ mod tests { } impl MathGadgetContainer for MulAddWords512GadgetContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let a = cb.query_word_rlc(); let b = cb.query_word_rlc(); let d = cb.query_word_rlc(); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs index 60acbd250d..1c5ce03e1e 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs @@ -1,7 +1,8 @@ use crate::{ evm_circuit::util::{ - self, constraint_builder::ConstraintBuilder, from_bytes, pow_of_two_expr, split_u256, - CachedRegion, + self, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + from_bytes, pow_of_two_expr, split_u256, CachedRegion, }, util::Expr, }; @@ -22,7 +23,7 @@ pub(crate) struct MulWordByU64Gadget { impl MulWordByU64Gadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, multiplicand: util::Word, multiplier: Expression, ) -> Self { @@ -106,7 +107,7 @@ mod tests { } impl MathGadgetContainer for MulWordByU64TestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let a = cb.query_word_rlc(); let b = cb.query_cell(); let product = cb.query_word_rlc(); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/pair_select.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/pair_select.rs index c525bb438e..ab1c98084d 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/pair_select.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/pair_select.rs @@ -1,5 +1,8 @@ use crate::{ - evm_circuit::util::{constraint_builder::ConstraintBuilder, CachedRegion, Cell}, + evm_circuit::util::{ + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + CachedRegion, Cell, + }, util::Expr, }; use eth_types::Field; @@ -22,7 +25,7 @@ pub struct PairSelectGadget { impl PairSelectGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, value: Expression, a: Expression, b: Expression, @@ -50,15 +53,17 @@ impl PairSelectGadget { a: F, _b: F, ) -> Result<(F, F), Error> { - let is_a = if value == a { F::one() } else { F::zero() }; + let is_a = if value == a { F::ONE } else { F::ZERO }; self.is_a.assign(region, offset, Value::known(is_a))?; - Ok((is_a, F::one() - is_a)) + Ok((is_a, F::ONE - is_a)) } } #[cfg(test)] mod tests { + use crate::evm_circuit::util::constraint_builder::ConstrainBuilderCommon; + use super::{super::test_util::*, *}; use eth_types::*; use halo2_proofs::{halo2curves::bn256::Fr, plonk::Error}; @@ -75,7 +80,7 @@ mod tests { impl MathGadgetContainer for PairSelectionTestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let v = cb.query_cell(); let a = cb.query_cell(); let b = cb.query_cell(); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs index 7e9ccf5282..989011c68a 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs @@ -1,5 +1,6 @@ use crate::evm_circuit::util::{ - constraint_builder::ConstraintBuilder, from_bytes, CachedRegion, Cell, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + from_bytes, CachedRegion, Cell, }; use eth_types::Field; use halo2_proofs::{ @@ -15,7 +16,7 @@ pub struct RangeCheckGadget { } impl RangeCheckGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder, value: Expression) -> Self { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder, value: Expression) -> Self { let parts = cb.query_bytes(); // Require that the reconstructed value from the parts equals the @@ -60,7 +61,7 @@ mod tests { impl MathGadgetContainer for RangeCheckTestContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let a = cb.query_cell(); let range_check_gadget = RangeCheckGadget::::construct(cb, a.expr()); RangeCheckTestContainer { diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs index d3932caccc..6a5a2154c1 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs @@ -7,7 +7,10 @@ use halo2_proofs::{ use crate::evm_circuit::{ param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_U64, N_BYTES_WORD}, - util::{constraint_builder::ConstraintBuilder, CachedRegion, Cell, RandomLinearCombination}, + util::{ + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + CachedRegion, Cell, RandomLinearCombination, + }, }; use super::IsZeroGadget; @@ -26,7 +29,7 @@ pub struct RlpU64Gadget { impl RlpU64Gadget { /// Configure and construct a gadget for RLP-encoding of a U64 value. - fn construct(cb: &mut ConstraintBuilder) -> Self { + fn construct(cb: &mut EVMConstraintBuilder) -> Self { let value_rlc = cb.query_keccak_rlc(); let is_most_significant_byte = array_init::array_init(|_| cb.query_bool()); @@ -43,10 +46,15 @@ impl RlpU64Gadget { .map(|(byte, indicator)| byte.expr() * indicator.expr()), ); let most_significant_byte_is_zero = IsZeroGadget::construct(cb, most_significant_byte); + let is_lt_128 = cb.query_bool(); let value = expr_from_bytes(&value_rlc.cells); cb.condition(most_significant_byte_is_zero.expr(), |cb| { cb.require_zero("if most significant byte is 0, value is 0", value.clone()); + cb.require_zero( + "if most significant byte is 0, value is less than 128", + 1.expr() - is_lt_128.expr(), + ); }); for (i, is_most_significant) in is_most_significant_byte.iter().enumerate() { @@ -64,10 +72,18 @@ impl RlpU64Gadget { }); } - let is_lt_128 = cb.query_bool(); - cb.condition(is_lt_128.expr(), |cb| { - cb.range_lookup(value, 128); - }); + // If is_lt_128, then value < 128, checked by a lookup. + + // Otherwise, then value >= 128, checked as follows: + // - Either the first byte is not the most significant, and there is a more significant one; + // - Or the first byte is the most significant, and it is >= 128. value ∈ [128, 256) (value + // - 128) ∈ [0, 128) + let byte_128 = value_rlc.cells[0].expr() - 128.expr(); + let is_first = is_most_significant_byte[0].expr(); + let byte_128_or_zero = byte_128 * is_first; + + let value_lt_128 = select::expr(is_lt_128.expr(), value, byte_128_or_zero); + cb.range_lookup(value_lt_128, 128); Self { value_rlc, @@ -143,7 +159,7 @@ impl RlpU64Gadget { } /// RLC for the RLP-encoding of the U64 value. - fn rlp_rlc(&self, cb: &ConstraintBuilder) -> Expression { + fn rlp_rlc(&self, cb: &EVMConstraintBuilder) -> Expression { select::expr( and::expr([ self.is_lt_128.expr(), @@ -155,7 +171,7 @@ impl RlpU64Gadget { ) } - fn challenge_power_rlp_length(&self, cb: &ConstraintBuilder) -> Expression { + fn challenge_power_rlp_length(&self, cb: &EVMConstraintBuilder) -> Expression { cb.challenges().keccak_input() * select::expr( self.is_lt_128.expr(), @@ -164,7 +180,7 @@ impl RlpU64Gadget { ) } - fn challenge_power_n_bytes(&self, cb: &ConstraintBuilder) -> Expression { + fn challenge_power_n_bytes(&self, cb: &EVMConstraintBuilder) -> Expression { select::expr( self.most_significant_byte_is_zero.expr(), 1.expr(), @@ -196,7 +212,7 @@ pub struct ContractCreateGadget { impl ContractCreateGadget { /// Configure and construct the gadget. - pub(crate) fn construct(cb: &mut ConstraintBuilder) -> Self { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder) -> Self { let caller_address = cb.query_keccak_rlc(); let nonce = RlpU64Gadget::construct(cb); let code_hash = array_init::array_init(|_| cb.query_byte()); @@ -254,7 +270,7 @@ impl ContractCreateGadget { } /// Code hash word RLC. - pub(crate) fn code_hash_word_rlc(&self, cb: &ConstraintBuilder) -> Expression { + pub(crate) fn code_hash_word_rlc(&self, cb: &EVMConstraintBuilder) -> Expression { cb.word_rlc::( self.code_hash .iter() @@ -266,7 +282,7 @@ impl ContractCreateGadget { } /// Code hash keccak RLC. - pub(crate) fn code_hash_keccak_rlc(&self, cb: &ConstraintBuilder) -> Expression { + pub(crate) fn code_hash_keccak_rlc(&self, cb: &EVMConstraintBuilder) -> Expression { cb.keccak_rlc::( self.code_hash .iter() @@ -308,7 +324,7 @@ impl ContractCreateGadget { } /// RLC for the input data. - pub(crate) fn input_rlc(&self, cb: &ConstraintBuilder) -> Expression { + pub(crate) fn input_rlc(&self, cb: &EVMConstraintBuilder) -> Expression { let challenges = cb.challenges().keccak_powers_of_randomness::<21>(); let challenge_power_20 = challenges[19].clone(); if IS_CREATE2 { @@ -344,7 +360,10 @@ mod test { use gadgets::util::{not, Expr}; use halo2_proofs::halo2curves::bn256::Fr; - use crate::evm_circuit::util::{constraint_builder::ConstraintBuilder, CachedRegion, Cell}; + use crate::evm_circuit::util::{ + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + CachedRegion, Cell, + }; #[derive(Clone)] struct ContractCreateGadgetContainer { @@ -357,7 +376,7 @@ mod test { impl MathGadgetContainer for ContractCreateGadgetContainer { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self { + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { let create_gadget = ContractCreateGadget::construct(cb); let input_len_expected = cb.query_cell(); let create_input_rlc_expected = array_init::array_init(|_| cb.query_byte()); @@ -431,7 +450,7 @@ mod test { .assign(region, offset, Value::known(F::from(input_len)))?; if IS_CREATE2 { for c in self.create_input_rlc_expected.iter() { - c.assign(region, offset, Value::known(F::zero()))?; + c.assign(region, offset, Value::known(F::ZERO))?; } for (c, v) in self.create2_input_rlc_expected.iter().zip( [ @@ -453,7 +472,7 @@ mod test { c.assign(region, offset, Value::known(F::from(v as u64)))?; } for c in self.create2_input_rlc_expected.iter() { - c.assign(region, offset, Value::known(F::zero()))?; + c.assign(region, offset, Value::known(F::ZERO))?; } } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs index 169b879112..0541bc0fc1 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs @@ -8,7 +8,7 @@ use crate::{ step::{ExecutionState, Step}, table::{FixedTableTag, Table}, util::{ - constraint_builder::ConstraintBuilder, rlc, CachedRegion, CellType, Expr, + constraint_builder::EVMConstraintBuilder, rlc, CachedRegion, CellType, Expr, StoredExpression, LOOKUP_CONFIG, }, Advice, Column, Fixed, @@ -41,11 +41,11 @@ pub(crate) const WORD_SIGNED_MAX: Word = U256([u64::MAX, u64::MAX, u64::MAX, i64 pub(crate) const WORD_SIGNED_MIN: Word = U256([0, 0, 0, i64::MIN as _]); pub(crate) fn generate_power_of_randomness(randomness: F) -> Vec { - (1..32).map(|exp| randomness.pow(&[exp, 0, 0, 0])).collect() + (1..32).map(|exp| randomness.pow([exp, 0, 0, 0])).collect() } pub(crate) trait MathGadgetContainer: Clone { - fn configure_gadget_container(cb: &mut ConstraintBuilder) -> Self + fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self where Self: Sized; @@ -90,6 +90,7 @@ impl UnitTestMathGadgetBaseCircuit { impl> Circuit for UnitTestMathGadgetBaseCircuit { type Config = (UnitTestMathGadgetBaseCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { UnitTestMathGadgetBaseCircuit { @@ -125,7 +126,7 @@ impl> Circuit for UnitTestMathGadgetBaseC let step_curr = Step::new(meta, advices, 0, false); let step_next = Step::new(meta, advices, MAX_STEP_HEIGHT, true); - let mut cb = ConstraintBuilder::new( + let mut cb = EVMConstraintBuilder::new( step_curr.clone(), step_next, &challenges_exprs, @@ -214,7 +215,7 @@ impl> Circuit for UnitTestMathGadgetBaseC layouter.assign_region( || "fixed table", |mut region| { - for (offset, row) in std::iter::once([F::zero(); 4]) + for (offset, row) in std::iter::once([F::ZERO; 4]) .chain( FixedTableTag::iter() .filter(|t| { diff --git a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs index f637a3c072..adb3016fe4 100644 --- a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs @@ -1,9 +1,9 @@ -use super::CachedRegion; +use super::{constraint_builder::ConstrainBuilderCommon, CachedRegion}; use crate::{ evm_circuit::{ param::{N_BYTES_GAS, N_BYTES_MEMORY_ADDRESS, N_BYTES_MEMORY_WORD_SIZE}, util::{ - constraint_builder::ConstraintBuilder, + constraint_builder::EVMConstraintBuilder, from_bytes, math_gadget::{ConstantDivisionGadget, IsZeroGadget, MinMaxGadget, RangeCheckGadget}, select, sum, Cell, CellType, MemoryAddress, @@ -71,7 +71,7 @@ pub(crate) struct MemoryAddressGadget { impl MemoryAddressGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, memory_offset: Cell, memory_length: MemoryAddress, ) -> Self { @@ -169,7 +169,7 @@ pub(crate) struct MemoryWordSizeGadget { } impl MemoryWordSizeGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder, address: Expression) -> Self { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder, address: Expression) -> Self { let memory_word_size = ConstantDivisionGadget::construct(cb, address + 31.expr(), 32); Self { memory_word_size } @@ -216,7 +216,10 @@ impl /// Output ranges: /// - `next_memory_word_size < 256**MAX_MEMORY_SIZE_IN_BYTES` /// - `gas_cost <= GAS_MEM*256**MAX_MEMORY_SIZE_IN_BYTES + 256**MAX_QUAD_COST_IN_BYTES` - pub(crate) fn construct(cb: &mut ConstraintBuilder, addresses: [Expression; N]) -> Self { + pub(crate) fn construct( + cb: &mut EVMConstraintBuilder, + addresses: [Expression; N], + ) -> Self { // Calculate the memory size of the memory access // `address_memory_word_size < 256**MAX_MEMORY_SIZE_IN_BYTES` let memory_word_sizes = @@ -243,12 +246,12 @@ impl let curr_quad_memory_cost = ConstantDivisionGadget::construct( cb, curr_memory_word_size.clone() * curr_memory_word_size.clone(), - GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR.as_u64(), + GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR, ); let next_quad_memory_cost = ConstantDivisionGadget::construct( cb, next_memory_word_size.clone() * next_memory_word_size.clone(), - GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR.as_u64(), + GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR, ); // Calculate the gas cost for the memory expansion. @@ -295,7 +298,7 @@ impl .collect::, _>>()?; // Calculate the next memory size - let mut next_memory_word_size = curr_memory_word_size as u64; + let mut next_memory_word_size = curr_memory_word_size; for (max_memory_word_sizes, memory_word_size) in self .max_memory_word_sizes .iter() @@ -304,7 +307,7 @@ impl let (_, max) = max_memory_word_sizes.assign( region, offset, - F::from(next_memory_word_size as u64), + F::from(next_memory_word_size), F::from(*memory_word_size), )?; next_memory_word_size = max.get_lower_128() as u64; @@ -323,8 +326,8 @@ impl )?; // Calculate the gas cost for the expansian - let memory_cost = GasCost::MEMORY_EXPANSION_LINEAR_COEFF.as_u64() - * (next_memory_word_size - curr_memory_word_size as u64) + let memory_cost = GasCost::MEMORY_EXPANSION_LINEAR_COEFF + * (next_memory_word_size - curr_memory_word_size) + (next_quad_memory_cost - curr_quad_memory_cost) as u64; // Return the new memory size and the memory expansion gas cost @@ -337,13 +340,13 @@ impl /// This gas cost is the difference between the next and current memory costs: /// `memory_cost = Gmem * memory_size + floor(memory_size * memory_size / 512)` #[derive(Clone, Debug)] -pub(crate) struct MemoryCopierGasGadget { +pub(crate) struct MemoryCopierGasGadget { word_size: MemoryWordSizeGadget, gas_cost: Expression, gas_cost_range_check: RangeCheckGadget, } -impl MemoryCopierGasGadget { +impl MemoryCopierGasGadget { pub const WORD_SIZE: u64 = 32u64; /// Input requirements: @@ -353,7 +356,7 @@ impl MemoryCopierGasGadget { /// - `next_memory_size < 256**MAX_MEMORY_SIZE_IN_BYTES` /// - `gas_cost <= GAS_MEM*256**MAX_MEMORY_SIZE_IN_BYTES + 256**MAX_QUAD_COST_IN_BYTES` pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, num_bytes: Expression, memory_expansion_gas_cost: Expression, ) -> Self { @@ -382,7 +385,7 @@ impl MemoryCopierGasGadget { memory_expansion_gas_cost: u64, ) -> Result { let word_size = self.word_size.assign(region, offset, num_bytes)?; - let gas_cost = word_size * GAS_COPY.as_u64() + memory_expansion_gas_cost; + let gas_cost = word_size * GAS_COPY + memory_expansion_gas_cost; self.gas_cost_range_check .assign(region, offset, F::from(gas_cost))?; // Return the memory copier gas cost @@ -414,7 +417,7 @@ impl BufferReaderGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut EVMConstraintBuilder, addr_start: Expression, addr_end: Expression, ) -> Self { @@ -506,7 +509,7 @@ impl // assign bound_dist and bound_dist_is_zero let oob = addr_start + idx as u64 >= addr_end; let bound_dist = if oob { - F::zero() + F::ZERO } else { F::from(addr_end - addr_start - idx as u64) }; diff --git a/zkevm-circuits/src/exp_circuit.rs b/zkevm-circuits/src/exp_circuit.rs index 5355f4051b..4da5979a0f 100644 --- a/zkevm-circuits/src/exp_circuit.rs +++ b/zkevm-circuits/src/exp_circuit.rs @@ -1,7 +1,19 @@ //! Exponentiation verification circuit. -use std::{marker::PhantomData, ops::Add}; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +mod dev; +pub(crate) mod param; +#[cfg(any(feature = "test", test))] +mod test; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +pub use dev::ExpCircuit as TestExpCircuit; +use crate::{ + evm_circuit::util::constraint_builder::BaseConstraintBuilder, + table::{ExpTable, LookupTable}, + util::{Challenges, SubCircuit, SubCircuitConfig}, + witness, +}; use bus_mapping::circuit_input_builder::{ExpEvent, ExpStep}; use eth_types::{Field, ToScalar, U256}; use gadgets::{ @@ -14,26 +26,9 @@ use halo2_proofs::{ poly::Rotation, }; -use crate::{ - evm_circuit::util::constraint_builder::BaseConstraintBuilder, - table::{ExpTable, LookupTable}, - util::{Challenges, SubCircuit, SubCircuitConfig}, - witness, -}; - -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -use halo2_proofs::{circuit::SimpleFloorPlanner, plonk::Circuit}; - -/// The number of rows assigned for each step in an exponentiation trace. -pub const OFFSET_INCREMENT: usize = 7usize; -/// The number of rows required for the exponentiation table within the circuit -/// for each step. -pub const ROWS_PER_STEP: usize = 4usize; -/// The gate "verify all but the last step" at constraint "`base_limb[i]` is the -/// same across all steps" uses rotation 10 in `exp_table.base_limb` which is -/// enabled with `q_usable`, which in turn is enabled in all steps. This means -/// this circuit requires these extra rows after the last enabled `q_usable`. -const UNUSABLE_EXP_ROWS: usize = 10usize; +use crate::evm_circuit::util::constraint_builder::ConstrainBuilderCommon; +use param::*; +use std::{marker::PhantomData, ops::Add}; /// Layout for the Exponentiation circuit. #[derive(Clone, Debug)] @@ -387,14 +382,14 @@ impl ExpCircuitConfig { || format!("exp_circuit: {:?}: {}", self.exp_table.is_step, offset), self.exp_table.is_step, *offset, - || Value::known(F::one()), + || Value::known(F::ONE), )?; for i in 1..OFFSET_INCREMENT { region.assign_fixed( || format!("exp_circuit: {:?}: {}", self.exp_table.is_step, *offset + i), self.exp_table.is_step, *offset + i, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; } // mul_chip has 7 rows, exp_table has 4 rows. So we increment the offset by @@ -454,14 +449,14 @@ impl ExpCircuitConfig { || format!("unused rows: {}", offset + i), *column, offset + i, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; } region.assign_fixed( || format!("unused rows: {}", offset + i), self.exp_table.is_step, offset + i, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; } @@ -501,6 +496,29 @@ impl ExpCircuit { impl SubCircuit for ExpCircuit { type Config = ExpCircuitConfig; + fn unusable_rows() -> usize { + // Column base_limb of ExpTable is queried at 8 distinct rotations at + // - Rotation(0) + // - Rotation(1) + // - Rotation(2) + // - Rotation(3) + // - Rotation(7) + // - Rotation(8) + // - Rotation(9) + // - Rotation(10) + // Also column col2 and col3 of are queried at 8 distinct rotations at + // - Rotation(0) + // - Rotation(1) + // - Rotation(2) + // - Rotation(3) + // - Rotation(4) + // - Rotation(5) + // - Rotation(6) + // - Rotation(9) + // so returns 11 unusable rows. + 11 + } + fn new_from_block(block: &witness::Block) -> Self { // Hardcoded to pass unit tests for now. In the future, insert: // "block.circuits_params.max_exp_rows" @@ -528,185 +546,3 @@ impl SubCircuit for ExpCircuit { config.assign_exp_events(layouter, &self.exp_events, self.max_exp_rows) } } - -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -impl Circuit for ExpCircuit { - type Config = (ExpCircuitConfig, Challenges); - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let exp_table = ExpTable::construct(meta); - let challenges = Challenges::construct(meta); - (ExpCircuitConfig::new(meta, exp_table), challenges) - } - - fn synthesize( - &self, - (config, challenges): Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), halo2_proofs::plonk::Error> { - let challenges = challenges.values(&mut layouter); - self.synthesize_sub(&config, &challenges, &mut layouter) - } -} - -#[cfg(any(feature = "test", test))] -/// Dev helpers -pub mod dev { - use super::*; - use eth_types::Field; - use halo2_proofs::dev::MockProver; - - use crate::evm_circuit::witness::Block; - - /// Test exponentiation circuit with the provided block witness - pub fn test_exp_circuit(k: u32, block: Block) { - let circuit = ExpCircuit::::new( - block.exp_events.clone(), - block.circuits_params.max_exp_steps, - ); - let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); - prover.assert_satisfied_par() - } -} - -#[cfg(test)] -mod tests { - use bus_mapping::{ - circuit_input_builder::{CircuitInputBuilder, CircuitsParams}, - evm::OpcodeId, - mock::BlockData, - }; - use eth_types::{bytecode, geth_types::GethData, Bytecode, Word}; - use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; - use mock::TestContext; - - use crate::{ - evm_circuit::witness::block_convert, - exp_circuit::{dev::test_exp_circuit, ExpCircuit}, - }; - - fn gen_code_single(base: Word, exponent: Word) -> Bytecode { - bytecode! { - PUSH32(exponent) - PUSH32(base) - EXP - STOP - } - } - - fn gen_code_multiple(args: Vec<(Word, Word)>) -> Bytecode { - let mut code = Bytecode::default(); - for (base, exponent) in args.into_iter() { - code.push(32, exponent); - code.push(32, base); - code.write_op(OpcodeId::EXP); - } - code.write_op(OpcodeId::STOP); - code - } - - fn gen_data(code: Bytecode) -> CircuitInputBuilder { - let test_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(); - let block: GethData = test_ctx.into(); - let mut builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); - builder - .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); - builder - } - - fn test_ok(base: Word, exponent: Word, k: Option) { - let code = gen_code_single(base, exponent); - let builder = gen_data(code); - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - test_exp_circuit(k.unwrap_or(18), block); - } - - fn test_ok_multiple(args: Vec<(Word, Word)>) { - let code = gen_code_multiple(args); - let builder = gen_data(code); - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - test_exp_circuit(20, block); - } - - #[test] - fn exp_circuit_single() { - test_ok(2.into(), 2.into(), None); - test_ok(3.into(), 7.into(), None); - test_ok(5.into(), 11.into(), None); - test_ok(7.into(), 13.into(), None); - test_ok(11.into(), 17.into(), None); - test_ok(13.into(), 23.into(), None); - test_ok(29.into(), 43.into(), None); - test_ok(41.into(), 259.into(), None); - } - - #[test] - fn exp_circuit_big() { - test_ok( - 2.into(), - Word::from_str_radix("0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE", 16).unwrap(), - Some(20), - ); - } - - #[test] - fn exp_circuit_multiple() { - test_ok_multiple(vec![ - (3.into(), 7.into()), - (5.into(), 11.into()), - (7.into(), 13.into()), - (11.into(), 17.into()), - (13.into(), 23.into()), - (29.into(), 43.into()), - (41.into(), 259.into()), - ]); - } - - #[test] - fn variadic_size_check() { - let k = 20; - // Empty - let block: GethData = TestContext::<0, 0>::new(None, |_| {}, |_, _| {}, |b, _| b) - .unwrap() - .into(); - let mut builder = - BlockData::new_from_geth_data_with_params(block.clone(), CircuitsParams::default()) - .new_circuit_input_builder(); - builder - .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - let circuit = ExpCircuit::::new( - block.exp_events.clone(), - block.circuits_params.max_exp_steps, - ); - let prover1 = MockProver::::run(k, &circuit, vec![]).unwrap(); - - // Non-empty - let code = bytecode! { - PUSH32(8) - PUSH32(10) - EXP - PUSH32(3) - PUSH32(5) - EXP - STOP - }; - let builder = gen_data(code); - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); - let circuit = ExpCircuit::::new( - block.exp_events.clone(), - block.circuits_params.max_exp_steps, - ); - let prover2 = MockProver::::run(k, &circuit, vec![]).unwrap(); - - assert_eq!(prover1.fixed(), prover2.fixed()); - assert_eq!(prover1.permutation(), prover2.permutation()); - } -} diff --git a/zkevm-circuits/src/exp_circuit/dev.rs b/zkevm-circuits/src/exp_circuit/dev.rs new file mode 100644 index 0000000000..ebb5cb4d31 --- /dev/null +++ b/zkevm-circuits/src/exp_circuit/dev.rs @@ -0,0 +1,37 @@ +pub use super::ExpCircuit; + +use crate::{ + exp_circuit::ExpCircuitConfig, + table::ExpTable, + util::{Challenges, SubCircuit, SubCircuitConfig}, +}; +use eth_types::Field; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner}, + plonk::{Circuit, ConstraintSystem, Error}, +}; + +impl Circuit for ExpCircuit { + type Config = (ExpCircuitConfig, Challenges); + type FloorPlanner = SimpleFloorPlanner; + type Params = (); + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let exp_table = ExpTable::construct(meta); + let challenges = Challenges::construct(meta); + (ExpCircuitConfig::new(meta, exp_table), challenges) + } + + fn synthesize( + &self, + (config, challenges): Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenges = challenges.values(&mut layouter); + self.synthesize_sub(&config, &challenges, &mut layouter) + } +} diff --git a/zkevm-circuits/src/exp_circuit/param.rs b/zkevm-circuits/src/exp_circuit/param.rs new file mode 100644 index 0000000000..f5c42a14ce --- /dev/null +++ b/zkevm-circuits/src/exp_circuit/param.rs @@ -0,0 +1,10 @@ +/// The number of rows assigned for each step in an exponentiation trace. +pub(crate) const OFFSET_INCREMENT: usize = 7usize; +/// The number of rows required for the exponentiation table within the circuit +/// for each step. +pub(crate) const ROWS_PER_STEP: usize = 4usize; +/// The gate "verify all but the last step" at constraint "`base_limb[i]` is the +/// same across all steps" uses rotation 10 in `exp_table.base_limb` which is +/// enabled with `q_usable`, which in turn is enabled in all steps. This means +/// this circuit requires these extra rows after the last enabled `q_usable`. +pub(crate) const UNUSABLE_EXP_ROWS: usize = 10usize; diff --git a/zkevm-circuits/src/exp_circuit/test.rs b/zkevm-circuits/src/exp_circuit/test.rs new file mode 100644 index 0000000000..2b5012458d --- /dev/null +++ b/zkevm-circuits/src/exp_circuit/test.rs @@ -0,0 +1,152 @@ +#![allow(unused_imports)] +use crate::{ + evm_circuit::witness::{block_convert, Block}, + exp_circuit::ExpCircuit, + util::{unusable_rows, SubCircuit}, +}; +use bus_mapping::{ + circuit_input_builder::{CircuitInputBuilder, CircuitsParams}, + evm::OpcodeId, + mock::BlockData, +}; +use eth_types::{bytecode, geth_types::GethData, Bytecode, Field, Word}; +use halo2_proofs::{ + circuit::SimpleFloorPlanner, dev::MockProver, halo2curves::bn256::Fr, plonk::Circuit, +}; +use mock::TestContext; + +#[test] +fn exp_circuit_unusable_rows() { + assert_eq!( + ExpCircuit::::unusable_rows(), + unusable_rows::>(()), + ) +} + +/// Test exponentiation circuit with the provided block witness +pub fn test_exp_circuit(k: u32, block: Block) { + let circuit = ExpCircuit::::new( + block.exp_events.clone(), + block.circuits_params.max_exp_steps, + ); + let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); + prover.assert_satisfied_par() +} + +fn gen_code_single(base: Word, exponent: Word) -> Bytecode { + bytecode! { + PUSH32(exponent) + PUSH32(base) + EXP + STOP + } +} + +fn gen_code_multiple(args: Vec<(Word, Word)>) -> Bytecode { + let mut code = Bytecode::default(); + for (base, exponent) in args.into_iter() { + code.op_exp(base, exponent); + } + code.op_stop(); + code +} + +fn gen_data(code: Bytecode) -> CircuitInputBuilder { + let test_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(); + let block: GethData = test_ctx.into(); + let mut builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); + builder + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); + builder +} + +fn test_ok(base: Word, exponent: Word, k: Option) { + let code = gen_code_single(base, exponent); + let builder = gen_data(code); + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + test_exp_circuit(k.unwrap_or(18), block); +} + +fn test_ok_multiple(args: Vec<(Word, Word)>) { + let code = gen_code_multiple(args); + let builder = gen_data(code); + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + test_exp_circuit(20, block); +} + +#[test] +fn exp_circuit_single() { + test_ok(2.into(), 2.into(), None); + test_ok(3.into(), 7.into(), None); + test_ok(5.into(), 11.into(), None); + test_ok(7.into(), 13.into(), None); + test_ok(11.into(), 17.into(), None); + test_ok(13.into(), 23.into(), None); + test_ok(29.into(), 43.into(), None); + test_ok(41.into(), 259.into(), None); +} + +#[test] +fn exp_circuit_big() { + test_ok( + 2.into(), + Word::from_str_radix("0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE", 16).unwrap(), + Some(20), + ); +} + +#[test] +fn exp_circuit_multiple() { + test_ok_multiple(vec![ + (3.into(), 7.into()), + (5.into(), 11.into()), + (7.into(), 13.into()), + (11.into(), 17.into()), + (13.into(), 23.into()), + (29.into(), 43.into()), + (41.into(), 259.into()), + ]); +} + +#[test] +fn variadic_size_check() { + let k = 20; + // Empty + let block: GethData = TestContext::<0, 0>::new(None, |_| {}, |_, _| {}, |b, _| b) + .unwrap() + .into(); + let mut builder = + BlockData::new_from_geth_data_with_params(block.clone(), CircuitsParams::default()) + .new_circuit_input_builder(); + builder + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + let circuit = ExpCircuit::::new( + block.exp_events.clone(), + block.circuits_params.max_exp_steps, + ); + let prover1 = MockProver::::run(k, &circuit, vec![]).unwrap(); + + // Non-empty + let code = bytecode! { + PUSH32(8) + PUSH32(10) + EXP + PUSH32(3) + PUSH32(5) + EXP + STOP + }; + let builder = gen_data(code); + let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + let circuit = ExpCircuit::::new( + block.exp_events.clone(), + block.circuits_params.max_exp_steps, + ); + let prover2 = MockProver::::run(k, &circuit, vec![]).unwrap(); + + assert_eq!(prover1.fixed(), prover2.fixed()); + assert_eq!(prover1.permutation(), prover2.permutation()); +} diff --git a/zkevm-circuits/src/keccak_circuit.rs b/zkevm-circuits/src/keccak_circuit.rs index 7d814bb58b..029ec196a6 100644 --- a/zkevm-circuits/src/keccak_circuit.rs +++ b/zkevm-circuits/src/keccak_circuit.rs @@ -4,17 +4,33 @@ mod cell_manager; pub mod keccak_packed_multi; mod param; mod table; -#[cfg(test)] -mod test; /// Util mod util; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +mod dev; +#[cfg(any(feature = "test", test))] +mod test; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +pub use dev::KeccakCircuit as TestKeccakCircuit; + use std::marker::PhantomData; pub use KeccakCircuitConfig as KeccakConfig; -use self::{cell_manager::*, keccak_packed_multi::*, param::*, table::*, util::*}; +use self::{ + cell_manager::*, + keccak_packed_multi::{keccak_unusable_rows, multi_keccak, KeccakRow}, + param::*, + table::*, + util::*, +}; use crate::{ - evm_circuit::util::constraint_builder::BaseConstraintBuilder, + evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, + keccak_circuit::keccak_packed_multi::{ + decode, get_num_bits_per_absorb_lookup, get_num_bits_per_base_chi_lookup, + get_num_bits_per_rho_pi_lookup, get_num_bits_per_theta_c_lookup, get_num_rows_per_round, + split, split_uniform, transform, transform_to, Part, + }, table::{KeccakTable, LookupTable}, util::{Challenges, SubCircuit, SubCircuitConfig}, witness, @@ -26,10 +42,7 @@ use halo2_proofs::{ plonk::{Column, ConstraintSystem, Error, Expression, Fixed, TableColumn, VirtualCells}, poly::Rotation, }; -use log::info; - -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -use halo2_proofs::{circuit::SimpleFloorPlanner, plonk::Circuit}; +use log::debug; /// KeccakConfig #[derive(Clone, Debug)] @@ -165,9 +178,9 @@ impl SubCircuitConfig for KeccakCircuitConfig { decode::expr(absorb_res), absorb_result.expr(), ); - info!("- Post absorb:"); - info!("Lookups: {}", lookup_counter); - info!("Columns: {}", cell_manager.get_width()); + debug!("- Post absorb:"); + debug!("Lookups: {}", lookup_counter); + debug!("Columns: {}", cell_manager.get_width()); total_lookup_counter += lookup_counter; // Process inputs. @@ -207,9 +220,9 @@ impl SubCircuitConfig for KeccakCircuitConfig { for _ in input_bytes.iter() { is_paddings.push(cell_manager.query_cell(meta)); } - info!("- Post padding:"); - info!("Lookups: {}", lookup_counter); - info!("Columns: {}", cell_manager.get_width()); + debug!("- Post padding:"); + debug!("Lookups: {}", lookup_counter); + debug!("Columns: {}", cell_manager.get_width()); total_lookup_counter += lookup_counter; // Theta @@ -265,9 +278,9 @@ impl SubCircuitConfig for KeccakCircuitConfig { } } s = os.clone(); - info!("- Post theta:"); - info!("Lookups: {}", lookup_counter); - info!("Columns: {}", cell_manager.get_width()); + debug!("- Post theta:"); + debug!("Lookups: {}", lookup_counter); + debug!("Columns: {}", cell_manager.get_width()); total_lookup_counter += lookup_counter; // Rho/Pi @@ -353,9 +366,9 @@ impl SubCircuitConfig for KeccakCircuitConfig { }); lookup_counter += 1; } - info!("- Post rho/pi:"); - info!("Lookups: {}", lookup_counter); - info!("Columns: {}", cell_manager.get_width()); + debug!("- Post rho/pi:"); + debug!("Lookups: {}", lookup_counter); + debug!("Columns: {}", cell_manager.get_width()); total_lookup_counter += lookup_counter; // Chi @@ -433,9 +446,9 @@ impl SubCircuitConfig for KeccakCircuitConfig { cb.require_equal("next row check", s[i][j].clone(), s_next[i][j].clone()); } } - info!("- Post chi:"); - info!("Lookups: {}", lookup_counter); - info!("Columns: {}", cell_manager.get_width()); + debug!("- Post chi:"); + debug!("Lookups: {}", lookup_counter); + debug!("Columns: {}", cell_manager.get_width()); total_lookup_counter += lookup_counter; let mut lookup_counter = 0; @@ -473,9 +486,9 @@ impl SubCircuitConfig for KeccakCircuitConfig { .unwrap(), true, ); - info!("- Post squeeze:"); - info!("Lookups: {}", lookup_counter); - info!("Columns: {}", cell_manager.get_width()); + debug!("- Post squeeze:"); + debug!("Lookups: {}", lookup_counter); + debug!("Columns: {}", cell_manager.get_width()); total_lookup_counter += lookup_counter; // The round constraints that we've been building up till now @@ -810,21 +823,21 @@ impl SubCircuitConfig for KeccakCircuitConfig { meta.annotate_lookup_column(col, || format!("KECCAK_pack_table_{}", idx)) }); - info!("Degree: {}", meta.degree()); - info!("Minimum rows: {}", meta.minimum_rows()); - info!("Total Lookups: {}", total_lookup_counter); - info!("Total Columns: {}", cell_manager.get_width()); - info!("num unused cells: {}", cell_manager.get_num_unused_cells()); - info!("part_size absorb: {}", get_num_bits_per_absorb_lookup()); - info!("part_size theta: {}", get_num_bits_per_theta_c_lookup()); - info!( + debug!("Degree: {}", meta.degree()); + debug!("Minimum rows: {}", meta.minimum_rows()); + debug!("Total Lookups: {}", total_lookup_counter); + debug!("Total Columns: {}", cell_manager.get_width()); + debug!("num unused cells: {}", cell_manager.get_num_unused_cells()); + debug!("part_size absorb: {}", get_num_bits_per_absorb_lookup()); + debug!("part_size theta: {}", get_num_bits_per_theta_c_lookup()); + debug!( "part_size theta c: {}", get_num_bits_per_lookup(THETA_C_LOOKUP_RANGE) ); - info!("part_size theta t: {}", get_num_bits_per_lookup(4)); - info!("part_size rho/pi: {}", get_num_bits_per_rho_pi_lookup()); - info!("part_size chi base: {}", get_num_bits_per_base_chi_lookup()); - info!( + debug!("part_size theta t: {}", get_num_bits_per_lookup(4)); + debug!("part_size rho/pi: {}", get_num_bits_per_rho_pi_lookup()); + debug!("part_size chi base: {}", get_num_bits_per_base_chi_lookup()); + debug!( "uniform part sizes: {:?}", target_part_sizes(get_num_bits_per_theta_c_lookup()) ); @@ -877,16 +890,20 @@ impl KeccakCircuitConfig { ) -> Result<(), Error> { // Fixed selectors for (name, column, value) in &[ - ("q_enable", self.q_enable, F::from(row.q_enable)), - ("q_first", self.q_first, F::from(offset == 0)), - ("q_round", self.q_round, F::from(row.q_round)), - ("q_round_last", self.q_round_last, F::from(row.q_round_last)), - ("q_absorb", self.q_absorb, F::from(row.q_absorb)), - ("q_padding", self.q_padding, F::from(row.q_padding)), + ("q_enable", self.q_enable, F::from(row.q_enable as u64)), + ("q_first", self.q_first, F::from((offset == 0) as u64)), + ("q_round", self.q_round, F::from(row.q_round as u64)), + ( + "q_round_last", + self.q_round_last, + F::from(row.q_round_last as u64), + ), + ("q_absorb", self.q_absorb, F::from(row.q_absorb as u64)), + ("q_padding", self.q_padding, F::from(row.q_padding as u64)), ( "q_padding_last", self.q_padding_last, - F::from(row.q_padding_last), + F::from(row.q_padding_last as u64), ), ] { region.assign_fixed( @@ -901,7 +918,7 @@ impl KeccakCircuitConfig { region, offset, [ - Value::known(F::from(row.is_final)), + Value::known(F::from(row.is_final as u64)), row.data_rlc, Value::known(F::from(row.length as u64)), row.hash_rlc, @@ -969,6 +986,10 @@ pub struct KeccakCircuit { impl SubCircuit for KeccakCircuit { type Config = KeccakCircuitConfig; + fn unusable_rows() -> usize { + keccak_unusable_rows() + } + /// The `block.circuits_params.keccak_padding` parmeter, when enabled, sets /// up the circuit to support a fixed number of permutations/keccak_f's, /// independently of the permutations required by `inputs`. @@ -1005,42 +1026,6 @@ impl SubCircuit for KeccakCircuit { } } -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -impl Circuit for KeccakCircuit { - type Config = (KeccakCircuitConfig, Challenges); - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let keccak_table = KeccakTable::construct(meta); - let challenges = Challenges::construct(meta); - - let config = { - let challenges = challenges.exprs(meta); - KeccakCircuitConfig::new( - meta, - KeccakCircuitConfigArgs { - keccak_table, - challenges, - }, - ) - }; - (config, challenges) - } - - fn synthesize( - &self, - (config, challenges): Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let challenges = challenges.values(&mut layouter); - self.synthesize_sub(&config, &challenges, &mut layouter) - } -} - impl KeccakCircuit { /// Creates a new circuit instance pub fn new(num_rows: usize, inputs: Vec>) -> Self { diff --git a/zkevm-circuits/src/keccak_circuit/cell_manager.rs b/zkevm-circuits/src/keccak_circuit/cell_manager.rs index b01d2a7c5f..0706ac93a7 100644 --- a/zkevm-circuits/src/keccak_circuit/cell_manager.rs +++ b/zkevm-circuits/src/keccak_circuit/cell_manager.rs @@ -1,4 +1,4 @@ -use crate::keccak_circuit::{util::extract_field, KeccakRegion}; +use crate::keccak_circuit::util::extract_field; use eth_types::Field; use gadgets::util::Expr; use halo2_proofs::{ @@ -7,6 +7,8 @@ use halo2_proofs::{ poly::Rotation, }; +use super::keccak_packed_multi::KeccakRegion; + #[derive(Clone, Debug)] pub(crate) struct Cell { pub(crate) expression: Expression, @@ -59,11 +61,7 @@ impl Cell { } pub(crate) fn assign(&self, region: &mut KeccakRegion, offset: i32, value: F) { - region.assign( - self.column_idx, - ((offset as i32) + self.rotation) as usize, - value, - ); + region.assign(self.column_idx, (offset + self.rotation) as usize, value); } pub(crate) fn assign_value(&self, region: &mut KeccakRegion, offset: i32, value: Value) { @@ -73,11 +71,7 @@ impl Cell { // this shouldn't be needed. let value_f = extract_field(value); - region.assign( - self.column_idx, - ((offset as i32) + self.rotation) as usize, - value_f, - ); + region.assign(self.column_idx, (offset + self.rotation) as usize, value_f); } } diff --git a/zkevm-circuits/src/keccak_circuit/dev.rs b/zkevm-circuits/src/keccak_circuit/dev.rs new file mode 100644 index 0000000000..12709bc41e --- /dev/null +++ b/zkevm-circuits/src/keccak_circuit/dev.rs @@ -0,0 +1,48 @@ +pub use super::KeccakCircuit; + +use crate::{ + keccak_circuit::{KeccakCircuitConfig, KeccakCircuitConfigArgs}, + table::KeccakTable, + util::{Challenges, SubCircuit, SubCircuitConfig}, +}; +use eth_types::Field; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner}, + plonk::{Circuit, ConstraintSystem, Error}, +}; + +impl Circuit for KeccakCircuit { + type Config = (KeccakCircuitConfig, Challenges); + type FloorPlanner = SimpleFloorPlanner; + type Params = (); + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let keccak_table = KeccakTable::construct(meta); + let challenges = Challenges::construct(meta); + + let config = { + let challenges = challenges.exprs(meta); + KeccakCircuitConfig::new( + meta, + KeccakCircuitConfigArgs { + keccak_table, + challenges, + }, + ) + }; + (config, challenges) + } + + fn synthesize( + &self, + (config, challenges): Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenges = challenges.values(&mut layouter); + self.synthesize_sub(&config, &challenges, &mut layouter) + } +} diff --git a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs index e5db4b1a32..3c1e93999d 100644 --- a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs +++ b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs @@ -10,11 +10,19 @@ use std::{env::var, vec}; pub(crate) fn get_num_rows_per_round() -> usize { var("KECCAK_ROWS") - .unwrap_or_else(|_| "12".to_string()) + .unwrap_or_else(|_| format!("{DEFAULT_KECCAK_ROWS}")) .parse() .expect("Cannot parse KECCAK_ROWS env var as usize") } +pub(crate) fn keccak_unusable_rows() -> usize { + const UNUSABLE_ROWS_BY_KECCAK_ROWS: [usize; 24] = [ + 53, 67, 63, 59, 45, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59, 57, 71, 89, 107, 107, 107, + 107, 107, + ]; + UNUSABLE_ROWS_BY_KECCAK_ROWS[get_num_rows_per_round() - NUM_BYTES_PER_WORD - 1] +} + pub(crate) fn get_num_bits_per_absorb_lookup() -> usize { get_num_bits_per_lookup(ABSORB_LOOKUP_RANGE) } @@ -94,7 +102,7 @@ impl KeccakRegion { } let row = &mut self.rows[offset]; while column >= row.len() { - row.push(F::zero()); + row.push(F::ZERO); } row[column] = value; } @@ -114,7 +122,7 @@ pub(crate) mod decode { } pub(crate) fn value(parts: Vec>) -> F { - parts.iter().rev().fold(F::zero(), |acc, part| { + parts.iter().rev().fold(F::ZERO, |acc, part| { acc * F::from(1u64 << (BIT_COUNT * part.num_bits)) + part.value }) } @@ -124,7 +132,7 @@ pub(crate) mod decode { pub(crate) mod split { use super::{decode, CellManager, KeccakRegion, Part, PartValue}; use crate::{ - evm_circuit::util::constraint_builder::BaseConstraintBuilder, + evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, keccak_circuit::util::{pack, pack_part, unpack, WordParts}, util::Expr, }; @@ -187,7 +195,7 @@ pub(crate) mod split { pub(crate) mod split_uniform { use super::{decode, target_part_sizes, Cell, CellManager, KeccakRegion, Part, PartValue}; use crate::{ - evm_circuit::util::constraint_builder::BaseConstraintBuilder, + evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, keccak_circuit::{ param::BIT_COUNT, util::{pack, pack_part, rotate, rotate_rev, unpack, WordParts}, @@ -483,7 +491,7 @@ pub(crate) fn keccak( challenges: Challenges>, ) { let mut bits = into_bits(bytes); - let mut s = [[F::zero(); 5]; 5]; + let mut s = [[F::ZERO; 5]; 5]; let absorb_positions = get_absorb_positions(); let num_bytes_in_last_block = bytes.len() % RATE; let two = F::from(2u64); @@ -496,7 +504,7 @@ pub(crate) fn keccak( bits.push(1); let mut length = 0usize; - let mut data_rlc = Value::known(F::zero()); + let mut data_rlc = Value::known(F::ZERO); let chunks = bits.chunks(RATE_IN_BITS); let num_chunks = chunks.len(); for (idx, chunk) in chunks.enumerate() { @@ -520,7 +528,7 @@ pub(crate) fn keccak( let mut cell_managers = Vec::new(); let mut regions = Vec::new(); - let mut hash_rlc = Value::known(F::zero()); + let mut hash_rlc = Value::known(F::ZERO); let mut round_lengths = Vec::new(); let mut round_data_rlcs = Vec::new(); for round in 0..NUM_ROUNDS + 1 { @@ -573,7 +581,7 @@ pub(crate) fn keccak( transform::value(&mut cell_manager, &mut region, packed, false, |v| *v, true); cell_manager.start_region(); let mut is_paddings = Vec::new(); - let mut data_rlcs = vec![Value::known(F::zero()); get_num_rows_per_round()]; + let mut data_rlcs = vec![Value::known(F::ZERO); get_num_rows_per_round()]; for _ in input_bytes.iter() { is_paddings.push(cell_manager.query_cell_value()); } @@ -588,7 +596,7 @@ pub(crate) fn keccak( false }; paddings.push(padding); - is_padding.assign(&mut region, 0, if padding { F::one() } else { F::zero() }); + is_padding.assign(&mut region, 0, if padding { F::ONE } else { F::ZERO }); } data_rlcs[NUM_BYTES_PER_WORD] = data_rlc; // Start at 0 or forward the previous value. @@ -630,7 +638,7 @@ pub(crate) fn keccak( bc.push(bc_norm); } cell_manager.start_region(); - let mut os = [[F::zero(); 5]; 5]; + let mut os = [[F::ZERO; 5]; 5]; for i in 0..5 { let t = decode::value(bc[(i + 4) % 5].clone()) + decode::value(rotate(bc[(i + 1) % 5].clone(), 1, part_size)); @@ -692,7 +700,7 @@ pub(crate) fn keccak( // Chi let part_size_base = get_num_bits_per_base_chi_lookup(); let three_packed = pack::(&vec![3u8; part_size_base]); - let mut os = [[F::zero(); 5]; 5]; + let mut os = [[F::ZERO; 5]; 5]; for j in 0..5 { for i in 0..5 { let mut s_parts = Vec::new(); @@ -750,7 +758,7 @@ pub(crate) fn keccak( .evm_word() .map(|challenge_value| rlc::value(&hash_bytes_le, challenge_value)) } else { - Value::known(F::zero()) + Value::known(F::ZERO) }; // The words to squeeze out @@ -833,11 +841,11 @@ pub(crate) fn multi_keccak( q_round_last: false, q_padding: false, q_padding_last: false, - round_cst: F::zero(), + round_cst: F::ZERO, is_final: false, length: 0usize, - data_rlc: Value::known(F::zero()), - hash_rlc: Value::known(F::zero()), + data_rlc: Value::known(F::ZERO), + hash_rlc: Value::known(F::ZERO), cell_values: Vec::new(), }); } diff --git a/zkevm-circuits/src/keccak_circuit/param.rs b/zkevm-circuits/src/keccak_circuit/param.rs index 121fe9ab36..847c10e8e6 100644 --- a/zkevm-circuits/src/keccak_circuit/param.rs +++ b/zkevm-circuits/src/keccak_circuit/param.rs @@ -1,4 +1,5 @@ pub(crate) const MAX_DEGREE: usize = 9; +pub(crate) const DEFAULT_KECCAK_ROWS: usize = 12; pub(crate) const ABSORB_LOOKUP_RANGE: usize = 3; pub(crate) const THETA_C_LOOKUP_RANGE: usize = 6; pub(crate) const RHO_PI_LOOKUP_RANGE: usize = 4; diff --git a/zkevm-circuits/src/keccak_circuit/table.rs b/zkevm-circuits/src/keccak_circuit/table.rs index 74c5959f51..5ab2fc5e23 100644 --- a/zkevm-circuits/src/keccak_circuit/table.rs +++ b/zkevm-circuits/src/keccak_circuit/table.rs @@ -246,6 +246,7 @@ mod tests { impl Circuit for TableTestCircuit { type Config = [TableColumn; 2]; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { self.clone() diff --git a/zkevm-circuits/src/keccak_circuit/test.rs b/zkevm-circuits/src/keccak_circuit/test.rs index 70f0213e9d..9711f978d5 100644 --- a/zkevm-circuits/src/keccak_circuit/test.rs +++ b/zkevm-circuits/src/keccak_circuit/test.rs @@ -1,11 +1,33 @@ -use crate::keccak_circuit::KeccakCircuit; +#![allow(unused_imports)] +use super::*; +use crate::util::unusable_rows; use eth_types::Field; -use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner}, + dev::MockProver, + halo2curves::bn256::Fr, + plonk::{Circuit, ConstraintSystem, Error}, +}; use log::error; use std::iter::zip; use super::util::{target_part_sizes, target_part_sizes_rot, WordParts}; +// This needs to be tested independent since it sets the environment variable +// which might affect other tests. +#[ignore] +#[test] +fn serial_keccak_circuit_unusable_rows() { + for keccak_rows in NUM_BYTES_PER_WORD + 1..=32 { + std::env::set_var("KECCAK_ROWS", format!("{keccak_rows}")); + assert_eq!( + KeccakCircuit::::unusable_rows(), + unusable_rows::>(()), + ) + } + std::env::set_var("KECCAK_ROWS", format!("{DEFAULT_KECCAK_ROWS}")); +} + fn verify(k: u32, inputs: Vec>, success: bool) { let circuit = KeccakCircuit::new(2usize.pow(k), inputs); diff --git a/zkevm-circuits/src/keccak_circuit/util.rs b/zkevm-circuits/src/keccak_circuit/util.rs index d94ab8d41e..48b819046e 100644 --- a/zkevm-circuits/src/keccak_circuit/util.rs +++ b/zkevm-circuits/src/keccak_circuit/util.rs @@ -1,8 +1,8 @@ //! Utility traits, functions used in the crate. -use super::param::*; +use super::{keccak_packed_multi::keccak_unusable_rows, param::*}; use eth_types::{Field, ToScalar, Word}; -use halo2_proofs::{circuit::Value, halo2curves::FieldExt}; +use halo2_proofs::circuit::Value; use std::env::var; /// Description of which bits (positions) a part contains @@ -122,7 +122,7 @@ pub(crate) fn pack_with_base(bits: &[u8], base: usize) -> F { let base = F::from(base as u64); bits.iter() .rev() - .fold(F::zero(), |acc, &bit| acc * base + F::from(bit as u64)) + .fold(F::ZERO, |acc, &bit| acc * base + F::from(bit as u64)) } /// Decodes the bits using the position data found in the part info @@ -224,7 +224,7 @@ pub(crate) fn get_num_bits_per_lookup(range: usize) -> usize { // Implementation of the above without environment dependency. pub(crate) fn get_num_bits_per_lookup_impl(range: usize, log_height: usize) -> usize { - let num_unusable_rows = 31; + let num_unusable_rows = keccak_unusable_rows(); let height = 2usize.pow(log_height as u32); let mut num_bits = 1; while range.pow(num_bits + 1) + num_unusable_rows <= height { @@ -233,8 +233,8 @@ pub(crate) fn get_num_bits_per_lookup_impl(range: usize, log_height: usize) -> u num_bits as usize } -pub(crate) fn extract_field(value: Value) -> F { - let mut field = F::zero(); +pub(crate) fn extract_field(value: Value) -> F { + let mut field = F::ZERO; let _ = value.map(|f| { field = f; f @@ -280,7 +280,7 @@ pub(crate) mod to_bytes { let mut bytes = Vec::new(); for byte_bits in bits.chunks(8) { let mut value = 0.expr(); - let mut multiplier = F::one(); + let mut multiplier = F::ONE; for byte in byte_bits.iter() { value = value + byte.expr() * multiplier; multiplier *= F::from(2); diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index c68a4a9e58..4f8d20fcb8 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -33,9 +33,6 @@ pub mod table; #[cfg(any(feature = "test", test))] pub mod test_util; -#[cfg(any(feature = "test", test))] -mod stats; - pub mod tx_circuit; pub mod util; pub mod witness; diff --git a/zkevm-circuits/src/mpt_circuit.rs b/zkevm-circuits/src/mpt_circuit.rs index 317bd59a6d..a813123445 100644 --- a/zkevm-circuits/src/mpt_circuit.rs +++ b/zkevm-circuits/src/mpt_circuit.rs @@ -6,10 +6,14 @@ use gadgets::{ }; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, - plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, + plonk::{ + Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, SecondPhase, + VirtualCells, + }, poly::Rotation, }; +use itertools::Itertools; use std::{convert::TryInto, env::var}; mod account_leaf; @@ -26,15 +30,18 @@ mod witness_row; use self::{ account_leaf::AccountLeafConfig, helpers::{key_memory, RLPItemView}, - witness_row::{ExtensionBranchRowType, Node}, + rlp_gadgets::decode_rlp, + witness_row::{AccountRowType, ExtensionBranchRowType, Node, StartRowType, StorageRowType}, }; use crate::{ assign, assignf, circuit, - circuit_tools::{ - cell_manager::CellManager, constraint_builder::ConstraintBuilder, memory::Memory, - }, + circuit_tools::{cached_region::CachedRegion, cell_manager::CellManager, memory::Memory}, + evm_circuit::table::Table, mpt_circuit::{ - helpers::{main_memory, parent_memory, MPTConstraintBuilder, MainRLPGadget}, + helpers::{ + main_memory, parent_memory, MPTConstraintBuilder, MainRLPGadget, MptCellType, FIXED, + KECCAK, PHASE_TWO, + }, start::StartConfig, storage_leaf::StorageLeafConfig, }, @@ -44,6 +51,17 @@ use crate::{ use extension_branch::ExtensionBranchConfig; use param::HASH_WIDTH; +#[derive(Debug, Eq, PartialEq)] +pub(crate) enum MPTRegion { + Default, + RLP, + Start, + Branch, + Account, + Storage, + Count, +} + /// State machine config. #[derive(Clone, Debug)] pub struct StateMachineConfig { @@ -82,6 +100,28 @@ impl StateMachineConfig { self.is_storage, ] } + + pub(crate) fn step_constraints( + &self, + meta: &mut VirtualCells<'_, F>, + cb: &mut MPTConstraintBuilder, + height: usize, + ) { + circuit!([meta, cb], { + // Because the state machine state is this height, we're already querying cells + // at all of these rotations, so may as well keep things simple. + // State selectors are already enforced to be boolean on each row. + let mut sum = 0.expr(); + for rot in 1..height { + for state_selector in self.state_selectors() { + sum = sum + a!(state_selector, rot); + } + } + require!(sum => 0); + // It should not be necessary to force the next row to have a state enabled + // because we never use relative offsets between state machine states. + }) + } } /// Merkle Patricia Trie context @@ -89,16 +129,14 @@ impl StateMachineConfig { pub struct MPTContext { pub(crate) mpt_table: MptTable, pub(crate) rlp_item: MainRLPGadget, - pub(crate) challenges: Challenges>, - pub(crate) memory: Memory, - pub(crate) r: Expression, + pub(crate) memory: Memory, } impl MPTContext { pub(crate) fn rlp_item( &self, meta: &mut VirtualCells, - cb: &mut ConstraintBuilder, + cb: &mut MPTConstraintBuilder, idx: usize, ) -> RLPItemView { // TODO(Brecht): Add RLP limitations like max num bytes @@ -108,7 +146,7 @@ impl MPTContext { pub(crate) fn nibbles( &self, meta: &mut VirtualCells, - cb: &mut ConstraintBuilder, + cb: &mut MPTConstraintBuilder, idx: usize, ) -> RLPItemView { self.rlp_item.create_view(meta, cb, idx, true) @@ -118,17 +156,16 @@ impl MPTContext { /// Merkle Patricia Trie config. #[derive(Clone)] pub struct MPTConfig { + pub(crate) mpt_table: MptTable, pub(crate) q_enable: Column, pub(crate) q_first: Column, pub(crate) q_last: Column, - pub(crate) rlp_columns: Vec>, - pub(crate) managed_columns: Vec>, - pub(crate) memory: Memory, + pub(crate) memory: Memory, keccak_table: KeccakTable, - fixed_table: [Column; 3], + fixed_table: [Column; 6], + phase_two_table: [Column; 3], rlp_item: MainRLPGadget, state_machine: StateMachineConfig, - pub(crate) mpt_table: MptTable, cb: MPTConstraintBuilder, } @@ -137,8 +174,6 @@ pub struct MPTConfig { pub enum FixedTableTag { /// All zero lookup data Disabled, - /// Power of randomness: [1, r], [2, r^2],... - RMult, /// 0 - 15 Range16, /// 0 - 255 @@ -147,22 +182,35 @@ pub enum FixedTableTag { RangeKeyLen256, /// For checking there are 0s after the RLP stream ends RangeKeyLen16, + /// Extesion key odd key + ExtOddKey, + /// RLP decoding + RLP, + /// TEMP + LERMult, } impl_expr!(FixedTableTag); +/// Enumerator to determine the type of row in the fixed table. +#[derive(Clone, Copy, Debug)] +pub enum PhaseTwoTableTag { + /// Multiplier for keccak challenge + BERMult, + // reserve for more phase two lookups... +} + +impl_expr!(PhaseTwoTableTag); + #[derive(Default)] pub(crate) struct MPTState { - pub(crate) r: F, - pub(crate) memory: Memory, + pub(crate) memory: Memory, } impl MPTState { - fn new(memory: &Memory, r: F) -> Self { + fn new(memory: &Memory) -> Self { Self { - r, memory: memory.clone(), - ..Default::default() } } } @@ -180,14 +228,18 @@ impl MPTConfig { let mpt_table = MptTable::construct(meta); - let fixed_table: [Column; 3] = (0..3) + let fixed_table: [Column; 6] = (0..6) .map(|_| meta.fixed_column()) .collect::>() .try_into() .unwrap(); - let rlp_columns = (0..50).map(|_| meta.advice_column()).collect::>(); - let managed_columns = (0..15).map(|_| meta.advice_column()).collect::>(); + let phase_two_table: [Column; 3] = (0..3) + .map(|_| meta.advice_column_in(SecondPhase)) + .collect::>() + .try_into() + .unwrap(); + let memory_columns = (0..5).map(|_| meta.advice_column()).collect::>(); let mut state_machine = StateMachineConfig::construct(meta); @@ -201,51 +253,88 @@ impl MPTConfig { memory.allocate(meta, main_memory()); let mut ctx = MPTContext { - mpt_table: mpt_table.clone(), + mpt_table, rlp_item: rlp_item.clone(), - challenges: challenges.clone(), - r: challenges.keccak_input(), memory: memory.clone(), }; - let mut cb = MPTConstraintBuilder::new(33 + 10, None); + let rlp_cm = CellManager::new( + meta, + // Type, #cols, phase, permutable + vec![ + (MptCellType::StoragePhase1, 50, 0, false), + (MptCellType::StoragePhase2, 5, 1, false), + (MptCellType::StoragePhase3, 5, 2, false), + (MptCellType::Lookup(Table::Fixed), 4, 2, false), + (MptCellType::Lookup(Table::Exp), 2, 2, false), + ], + 0, + 1, + ); + let state_cm = CellManager::new( + meta, + // Type, #cols, phase, permutable + vec![ + (MptCellType::StoragePhase1, 20, 0, false), + (MptCellType::StoragePhase2, 5, 1, false), + (MptCellType::StoragePhase3, 5, 2, false), + (MptCellType::LookupByte, 4, 0, false), + (MptCellType::Lookup(Table::Fixed), 3, 2, false), + (MptCellType::Lookup(Table::Keccak), 1, 2, false), + (MptCellType::Lookup(Table::Exp), 2, 2, false), + ], + 0, + 50, + ); + + let le_r = 123456.expr(); + let mut cb = MPTConstraintBuilder::new(50, Some(challenges.clone()), None, le_r.expr()); meta.create_gate("MPT", |meta| { - circuit!([meta, cb.base], { + circuit!([meta, cb], { // Populate lookup tables - require!(@"keccak" => >::advice_columns(&keccak_table).iter().map(|table| a!(table)).collect()); - require!(@"fixed" => fixed_table.iter().map(|table| f!(table)).collect()); + require!(@KECCAK => >::advice_columns(&keccak_table).iter().map(|table| a!(table)).collect()); + require!(@FIXED => fixed_table.iter().map(|table| f!(table)).collect()); + require!(@PHASE_TWO => phase_two_table.iter().map(|table| a!(table)).collect()); ifx!{f!(q_enable) => { // RLP item decoding unit - let mut cell_manager = CellManager::new(meta, &rlp_columns); - cell_manager.reset(1); - cb.base.set_cell_manager(cell_manager); - rlp_item = MainRLPGadget::construct(&mut cb, &ctx.r); + cb.base.set_cell_manager(rlp_cm.clone()); + cb.base.push_region(MPTRegion::RLP as usize); + rlp_item = MainRLPGadget::construct(&mut cb); + cb.base.pop_region(); ctx.rlp_item = rlp_item.clone(); // Main MPT circuit - let cell_manager = CellManager::new(meta, &managed_columns); - cb.base.set_cell_manager(cell_manager); - // State machine - // TODO(Brecht): state machine constraints - // Always start and end with the start state + cb.base.set_cell_manager(state_cm.clone()); ifx! {f!(q_first) + f!(q_last) => { require!(a!(state_machine.is_start) => true); }}; // Main state machine matchx! { a!(state_machine.is_start) => { + state_machine.step_constraints(meta, &mut cb, StartRowType::Count as usize); + cb.base.push_region(MPTRegion::Start as usize); state_machine.start_config = StartConfig::configure(meta, &mut cb, ctx.clone()); + cb.base.pop_region(); }, a!(state_machine.is_branch) => { + state_machine.step_constraints(meta, &mut cb, ExtensionBranchRowType::Count as usize); + cb.base.push_region(MPTRegion::Branch as usize); state_machine.branch_config = ExtensionBranchConfig::configure(meta, &mut cb, ctx.clone()); + cb.base.pop_region(); }, a!(state_machine.is_account) => { + state_machine.step_constraints(meta, &mut cb, AccountRowType::Count as usize); + cb.base.push_region(MPTRegion::Account as usize); state_machine.account_config = AccountLeafConfig::configure(meta, &mut cb, ctx.clone()); + cb.base.pop_region(); }, a!(state_machine.is_storage) => { + state_machine.step_constraints(meta, &mut cb, StorageRowType::Count as usize); + cb.base.push_region(MPTRegion::Storage as usize); state_machine.storage_config = StorageLeafConfig::configure(meta, &mut cb, ctx.clone()); + cb.base.pop_region(); }, _ => (), }; @@ -255,40 +344,63 @@ impl MPTConfig { }} // Memory banks - ctx.memory.generate_constraints(&mut cb.base, f!(q_first)); + ctx.memory.build_constraints(&mut cb.base, f!(q_first)); }} }); - cb.base.generate_constraints() + cb.base.build_constraints() }); + // Region for spliting dynamic lookup + // currently not working :( + cb.base.push_region(MPTRegion::Count as usize); + let disable_lookups: usize = var("DISABLE_LOOKUPS") .unwrap_or_else(|_| "0".to_string()) .parse() .expect("Cannot parse DISABLE_LOOKUPS env var as usize"); if disable_lookups == 0 { - cb.base.generate_lookups( + cb.base.build_celltype_lookups( meta, - &[ - vec!["fixed".to_string() /* , "keccak".to_string() */], - ctx.memory.tags(), - ] - .concat(), + vec![rlp_cm, state_cm], + vec![ + (MptCellType::Lookup(Table::Keccak), &keccak_table), + (MptCellType::Lookup(Table::Fixed), &fixed_table), + (MptCellType::Lookup(Table::Exp), &phase_two_table), + ], + ); + cb.base.build_dynamic_lookups( + meta, + &[vec![FIXED, KECCAK], ctx.memory.tags()].concat(), + vec![(MptCellType::Lookup(Table::Fixed), &fixed_table)], ); } else if disable_lookups == 1 { - cb.base.generate_lookups( + cb.base.build_dynamic_lookups( meta, - &[vec!["keccak".to_string()], ctx.memory.tags()].concat(), + &[vec![KECCAK], ctx.memory.tags()].concat(), + vec![(MptCellType::Lookup(Table::Fixed), &fixed_table)], ); } else if disable_lookups == 2 { - cb.base.generate_lookups(meta, &ctx.memory.tags()); + cb.base.build_dynamic_lookups( + meta, + &ctx.memory.tags(), + vec![(MptCellType::Lookup(Table::Fixed), &fixed_table)], + ); } else if disable_lookups == 3 { - cb.base - .generate_lookups(meta, &vec!["fixed".to_string(), "keccak".to_string()]); + cb.base.build_dynamic_lookups( + meta, + &[FIXED, KECCAK], + vec![(MptCellType::Lookup(Table::Fixed), &fixed_table)], + ); } else if disable_lookups == 4 { - cb.base.generate_lookups(meta, &vec!["keccak".to_string()]); + cb.base.build_dynamic_lookups( + meta, + &[KECCAK], + vec![(MptCellType::Lookup(Table::Fixed), &fixed_table)], + ); } + println!("degree: {}", meta.degree()); println!("num lookups: {}", meta.lookups().len()); println!("num advices: {}", meta.num_advice_columns()); println!("num fixed: {}", meta.num_fixed_columns()); @@ -298,11 +410,10 @@ impl MPTConfig { q_enable, q_first, q_last, - rlp_columns, - managed_columns, memory, keccak_table, fixed_table, + phase_two_table, state_machine, rlp_item, mpt_table, @@ -320,80 +431,102 @@ impl MPTConfig { let mut height = 0; let mut memory = self.memory.clone(); - let mut r = F::zero(); - challenges.keccak_input().map(|v| r = v); - layouter.assign_region( || "MPT", |mut region| { - let mut pv = MPTState::new(&self.memory, r); + + let le_r = F::from(123456u64); + let mut be_r = F::ZERO; + challenges.keccak_input().map(|v| be_r = v); + + let mut pv = MPTState::new(&self.memory); memory.clear_witness_data(); let mut offset = 0; for node in nodes.iter() { + //println!("offset: {}", offset); + let mut cached_region = CachedRegion::new( + &mut region, + challenges, + le_r, + be_r, + ); + // Assign bytes let mut rlp_values = Vec::new(); + // Decompose RLP for (idx, bytes) in node.values.iter().enumerate() { + cached_region.push_region(offset + idx, MPTRegion::RLP as usize); let is_nibbles = node.extension_branch.is_some() && idx == ExtensionBranchRowType::KeyC as usize; let rlp_value = self.rlp_item.assign( - &mut region, + &mut cached_region, offset + idx, bytes, - r, is_nibbles, )?; rlp_values.push(rlp_value); + cached_region.pop_region(); } // Assign nodes if node.start.is_some() { - // println!("{}: start", offset); - assign!(region, (self.state_machine.is_start, offset) => true.scalar())?; + //println!("{}: start", offset); + cached_region.push_region(offset, MPTRegion::Start as usize); + assign!(cached_region, (self.state_machine.is_start, offset) => "is_start", true.scalar())?; self.state_machine.start_config.assign( - &mut region, + &mut cached_region, self, &mut pv, offset, node, &rlp_values, )?; + cached_region.pop_region(); } else if node.extension_branch.is_some() { - // println!("{}: branch", offset); - assign!(region, (self.state_machine.is_branch, offset) => true.scalar())?; + //println!("{}: branch", offset); + cached_region.push_region(offset, MPTRegion::Branch as usize); + assign!(cached_region, (self.state_machine.is_branch, offset) => "is_branch", true.scalar())?; self.state_machine.branch_config.assign( - &mut region, + &mut cached_region, self, &mut pv, offset, node, &rlp_values, )?; - } else if node.storage.is_some() { - // println!("{}: storage", offset); - assign!(region, (self.state_machine.is_storage, offset) => true.scalar())?; - self.state_machine.storage_config.assign( - &mut region, + cached_region.pop_region(); + } else if node.account.is_some() { + //println!("{}: account", offset); + cached_region.push_region(offset, MPTRegion::Account as usize); + assign!(cached_region, (self.state_machine.is_account, offset) => "is_account", true.scalar())?; + self.state_machine.account_config.assign( + &mut cached_region, self, &mut pv, offset, node, &rlp_values, )?; - } else if node.account.is_some() { - // println!("{}: account", offset); - assign!(region, (self.state_machine.is_account, offset) => true.scalar())?; - self.state_machine.account_config.assign( - &mut region, + cached_region.pop_region(); + } else if node.storage.is_some() { + //println!("{}: storage", offset); + cached_region.push_region(offset, MPTRegion::Storage as usize); + assign!(cached_region, (self.state_machine.is_storage, offset) => "is_storage", true.scalar())?; + self.state_machine.storage_config.assign( + &mut cached_region, self, &mut pv, offset, node, &rlp_values, )?; + cached_region.pop_region(); } - + cached_region.push_region(offset, MPTRegion::Count as usize); + cached_region.assign_stored_expressions(&self.cb.base)?; + cached_region.pop_region(); offset += node.values.len(); } @@ -415,14 +548,7 @@ impl MPTConfig { Ok(()) } - fn load_fixed_table( - &self, - layouter: &mut impl Layouter, - challenges: &Challenges>, - ) -> Result<(), Error> { - let mut r = F::zero(); - challenges.keccak_input().map(|v| r = v); - + fn load_fixed_table(&self, layouter: &mut impl Layouter) -> Result<(), Error> { layouter.assign_region( || "fixed table", |mut region| { @@ -435,9 +561,10 @@ impl MPTConfig { offset += 1; // Mult table - let mut mult = F::one(); + let r = F::from(123456u64); + let mut mult = F::ONE; for ind in 0..(2 * HASH_WIDTH + 1) { - assignf!(region, (self.fixed_table[0], offset) => FixedTableTag::RMult.scalar())?; + assignf!(region, (self.fixed_table[0], offset) => FixedTableTag::LERMult.scalar())?; assignf!(region, (self.fixed_table[1], offset) => ind.scalar())?; assignf!(region, (self.fixed_table[2], offset) => mult)?; mult *= r; @@ -471,8 +598,7 @@ impl MPTConfig { for n in -max_length..=max_length { let range = if n <= 0 && range == 256 { 1 } else { range }; for idx in 0..range { - let v = F::from(n.unsigned_abs() as u64) - * if n.is_negative() { -F::one() } else { F::one() }; + let v = n.scalar(); assignf!(region, (self.fixed_table[0], offset) => tag.scalar())?; assignf!(region, (self.fixed_table[1], offset) => idx.scalar())?; assignf!(region, (self.fixed_table[2], offset) => v)?; @@ -481,6 +607,59 @@ impl MPTConfig { } } + // Compact encoding of the extension key, find out if the key is odd or not. + // Even - The full byte is simply 0. + assignf!(region, (self.fixed_table[0], offset) => FixedTableTag::ExtOddKey.scalar())?; + assignf!(region, (self.fixed_table[1], offset) => 0.scalar())?; + assignf!(region, (self.fixed_table[2], offset) => false.scalar())?; + offset += 1; + // Odd - First nibble is 1, the second nibble can be any value. + for idx in 0..16 { + assignf!(region, (self.fixed_table[0], offset) => FixedTableTag::ExtOddKey.scalar())?; + assignf!(region, (self.fixed_table[1], offset) => ((0b1_0000) + idx).scalar())?; + assignf!(region, (self.fixed_table[2], offset) => true.scalar())?; + offset += 1; + } + + // RLP + for byte in 0..255 { + let (is_list, is_short, is_long, is_very_long) = decode_rlp(byte); + assignf!(region, (self.fixed_table[0], offset) => FixedTableTag::RLP.scalar())?; + assignf!(region, (self.fixed_table[1], offset) => byte.scalar())?; + assignf!(region, (self.fixed_table[2], offset) => is_list.scalar())?; + assignf!(region, (self.fixed_table[3], offset) => is_short.scalar())?; + assignf!(region, (self.fixed_table[4], offset) => is_long.scalar())?; + assignf!(region, (self.fixed_table[5], offset) => is_very_long.scalar())?; + offset += 1; + } + + Ok(()) + }, + ) + } + + fn load_phase_two_table( + &self, + layouter: &mut impl Layouter, + challenges: &Challenges>, + ) -> Result<(), Error> { + layouter.assign_region( + || "phase two table", + |mut region| { + // let le_r = F::from(123456u64); + // let be_r = r + F::ONE; + let mut be_r = F::ZERO; + challenges.keccak_input().map(|k| be_r = k); + + let mut offset = 0; + let mut mult = F::ONE; + for ind in 0..(2 * HASH_WIDTH + 1) { + assign!(region, (self.phase_two_table[0], offset) => PhaseTwoTableTag::BERMult.scalar())?; + assign!(region, (self.phase_two_table[1], offset) => ind.scalar())?; + assign!(region, (self.phase_two_table[2], offset) => mult)?; + mult *= be_r; + offset += 1; + } Ok(()) }, ) @@ -497,31 +676,16 @@ struct MPTCircuit { impl Circuit for MPTCircuit { type Config = (MPTConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - // let challenges = Challenges::construct(meta); - // let challenges_expr = challenges.exprs(meta); - - let r = 2u64; - let challenges = Challenges::mock( - Value::known(F::from(r)), - Value::known(F::from(r)), - Value::known(F::from(r)), - ); - let challenges_expr = Challenges::mock(r.expr(), r.expr(), r.expr()); - - let keccak_table = KeccakTable::construct(meta); - // let randomness: F = 123456789.scalar(); - // Use a mock randomness instead of the randomness derived from the challange - // (either from mock or real prover) to help debugging assignments. - // let power_of_randomness: [Expression; HASH_WIDTH] = array::from_fn(|i| { - // Expression::Constant(randomness.pow(&[1 + i as u64, 0, 0, 0])) - //}); let challenges = Challenges::construct(meta); + let challenges_expr = challenges.exprs(meta); + let keccak_table = KeccakTable::construct(meta); ( MPTConfig::configure(meta, challenges_expr, keccak_table), challenges, @@ -530,17 +694,13 @@ impl Circuit for MPTCircuit { fn synthesize( &self, - (config, challenges): Self::Config, + (config, _challenges): Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - // let challenges = challenges.values(&mut layouter); - - let r = self.randomness; - let challenges = Challenges::mock(Value::known(r), Value::known(r), Value::known(r)); - - config.load_fixed_table(&mut layouter, &challenges)?; + let challenges = _challenges.values(&mut layouter); + config.load_fixed_table(&mut layouter)?; + config.load_phase_two_table(&mut layouter, &challenges)?; config.assign(&mut layouter, &self.nodes, &challenges)?; - config .keccak_table .dev_load(&mut layouter, &self.keccak_data, &challenges, false)?; @@ -561,9 +721,7 @@ mod tests { #[test] fn test_mpt() { - // for debugging: let path = "src/mpt_circuit/tests"; - // let path = "tests"; let files = fs::read_dir(path).unwrap(); files .filter_map(Result::ok) @@ -583,7 +741,7 @@ mod tests { let reader = std::io::BufReader::new(file.unwrap()); let w: Vec> = serde_json::from_reader(reader).unwrap(); - let randomness: Fr = 2.scalar(); + let randomness: Fr = 123456.scalar(); let mut keccak_data = vec![]; let mut witness_rows = vec![]; diff --git a/zkevm-circuits/src/mpt_circuit/account_leaf.rs b/zkevm-circuits/src/mpt_circuit/account_leaf.rs index 9916e307a8..838d6211ea 100644 --- a/zkevm-circuits/src/mpt_circuit/account_leaf.rs +++ b/zkevm-circuits/src/mpt_circuit/account_leaf.rs @@ -1,7 +1,7 @@ use eth_types::Field; use gadgets::util::{pow, Scalar}; use halo2_proofs::{ - circuit::{Region, Value}, + circuit::Value, plonk::{Error, VirtualCells}, poly::Rotation, }; @@ -15,14 +15,15 @@ use super::{ use crate::{ circuit, circuit_tools::{ + cached_region::{CachedRegion, ChallengeSet}, cell_manager::Cell, - constraint_builder::{RLCChainable, RLCable, RLCableValue}, + constraint_builder::{RLCChainable2, RLCable, RLCableValue}, gadgets::IsEqualGadget, }, mpt_circuit::{ helpers::{ key_memory, main_memory, num_nibbles, parent_memory, DriftedGadget, Indexable, - IsEmptyTreeGadget, KeyData, MPTConstraintBuilder, ParentData, WrongGadget, + IsEmptyTreeGadget, KeyData, MPTConstraintBuilder, ParentData, WrongGadget, KECCAK, }, param::{KEY_LEN_IN_NIBBLES, RLP_LIST_LONG, RLP_LONG}, MPTConfig, MPTContext, MPTState, @@ -56,8 +57,6 @@ impl AccountLeafConfig { cb: &mut MPTConstraintBuilder, ctx: MPTContext, ) -> Self { - let r = ctx.r.clone(); - cb.base .cell_manager .as_mut() @@ -65,38 +64,34 @@ impl AccountLeafConfig { .reset(AccountRowType::Count as usize); let mut config = AccountLeafConfig::default(); - circuit!([meta, cb.base], { + circuit!([meta, cb], { let key_items = [ - ctx.rlp_item(meta, &mut cb.base, AccountRowType::KeyS as usize), - ctx.rlp_item(meta, &mut cb.base, AccountRowType::KeyC as usize), + ctx.rlp_item(meta, cb, AccountRowType::KeyS as usize), + ctx.rlp_item(meta, cb, AccountRowType::KeyC as usize), ]; - config.value_rlp_bytes = [cb.base.query_bytes(), cb.base.query_bytes()]; - config.value_list_rlp_bytes = [cb.base.query_bytes(), cb.base.query_bytes()]; + config.value_rlp_bytes = [cb.query_bytes(), cb.query_bytes()]; + config.value_list_rlp_bytes = [cb.query_bytes(), cb.query_bytes()]; let nonce_items = [ - ctx.rlp_item(meta, &mut cb.base, AccountRowType::NonceS as usize), - ctx.rlp_item(meta, &mut cb.base, AccountRowType::NonceC as usize), + ctx.rlp_item(meta, cb, AccountRowType::NonceS as usize), + ctx.rlp_item(meta, cb, AccountRowType::NonceC as usize), ]; let balance_items = [ - ctx.rlp_item(meta, &mut cb.base, AccountRowType::BalanceS as usize), - ctx.rlp_item(meta, &mut cb.base, AccountRowType::BalanceC as usize), + ctx.rlp_item(meta, cb, AccountRowType::BalanceS as usize), + ctx.rlp_item(meta, cb, AccountRowType::BalanceC as usize), ]; let storage_items = [ - ctx.rlp_item(meta, &mut cb.base, AccountRowType::StorageS as usize), - ctx.rlp_item(meta, &mut cb.base, AccountRowType::StorageC as usize), + ctx.rlp_item(meta, cb, AccountRowType::StorageS as usize), + ctx.rlp_item(meta, cb, AccountRowType::StorageC as usize), ]; let codehash_items = [ - ctx.rlp_item(meta, &mut cb.base, AccountRowType::CodehashS as usize), - ctx.rlp_item(meta, &mut cb.base, AccountRowType::CodehashC as usize), + ctx.rlp_item(meta, cb, AccountRowType::CodehashS as usize), + ctx.rlp_item(meta, cb, AccountRowType::CodehashC as usize), ]; - let drifted_bytes = ctx.rlp_item(meta, &mut cb.base, AccountRowType::Drifted as usize); - let wrong_bytes = ctx.rlp_item(meta, &mut cb.base, AccountRowType::Wrong as usize); + let drifted_bytes = ctx.rlp_item(meta, cb, AccountRowType::Drifted as usize); + let wrong_bytes = ctx.rlp_item(meta, cb, AccountRowType::Wrong as usize); - config.main_data = MainData::load( - "main storage", - &mut cb.base, - &ctx.memory[main_memory()], - 0.expr(), - ); + config.main_data = + MainData::load("main storage", cb, &ctx.memory[main_memory()], 0.expr()); // Don't allow an account node to follow an account node require!(config.main_data.is_below_account => false); @@ -107,70 +102,74 @@ impl AccountLeafConfig { let mut storage_rlc = vec![0.expr(); 2]; let mut codehash_rlc = vec![0.expr(); 2]; let mut leaf_no_key_rlc = vec![0.expr(); 2]; + let mut leaf_no_key_rlc_mult = vec![0.expr(); 2]; for is_s in [true, false] { // Key data let key_data = &mut config.key_data[is_s.idx()]; - *key_data = KeyData::load(&mut cb.base, &ctx.memory[key_memory(is_s)], 0.expr()); + *key_data = KeyData::load(cb, &ctx.memory[key_memory(is_s)], 0.expr()); // Parent data let parent_data = &mut config.parent_data[is_s.idx()]; *parent_data = ParentData::load( "account load", - &mut cb.base, + cb, &ctx.memory[parent_memory(is_s)], 0.expr(), ); // Placeholder leaf checks config.is_in_empty_trie[is_s.idx()] = - IsEmptyTreeGadget::construct(&mut cb.base, parent_data.rlc.expr(), &r); + IsEmptyTreeGadget::construct(cb, parent_data.rlc.expr(), &cb.le_r.expr()); // Calculate the key RLC let rlp_key = &mut config.rlp_key[is_s.idx()]; - *rlp_key = ListKeyGadget::construct(&mut cb.base, &key_items[is_s.idx()]); + *rlp_key = ListKeyGadget::construct(cb, &key_items[is_s.idx()]); // Storage root and codehash are always 32-byte hashes. require!(storage_items[is_s.idx()].len() => HASH_WIDTH); require!(codehash_items[is_s.idx()].len() => HASH_WIDTH); - // Multiplier after list and key - let mult = rlp_key.rlp_list.rlp_mult(&r) * key_items[is_s.idx()].mult(); - let nonce_rlp_rlc; let balance_rlp_rlc; let storage_rlp_rlc; let codehash_rlp_rlc; - (nonce_rlc[is_s.idx()], nonce_rlp_rlc) = nonce_items[is_s.idx()].rlc(); - (balance_rlc[is_s.idx()], balance_rlp_rlc) = balance_items[is_s.idx()].rlc(); - (storage_rlc[is_s.idx()], storage_rlp_rlc) = storage_items[is_s.idx()].rlc(); - (codehash_rlc[is_s.idx()], codehash_rlp_rlc) = codehash_items[is_s.idx()].rlc(); + (nonce_rlc[is_s.idx()], nonce_rlp_rlc) = nonce_items[is_s.idx()].rlc2(); + (balance_rlc[is_s.idx()], balance_rlp_rlc) = balance_items[is_s.idx()].rlc2(); + (storage_rlc[is_s.idx()], storage_rlp_rlc) = storage_items[is_s.idx()].rlc2(); + (codehash_rlc[is_s.idx()], codehash_rlp_rlc) = codehash_items[is_s.idx()].rlc2(); // Calculate the leaf RLC + let keccak_r = &cb.be_r; let value_rlp_bytes = config.value_rlp_bytes[is_s.idx()].to_expr_vec(); let value_list_rlp_bytes = config.value_list_rlp_bytes[is_s.idx()].to_expr_vec(); - leaf_no_key_rlc[is_s.idx()] = (value_rlp_bytes.rlc(&r), pow::expr(r.expr(), 2)) - .rlc_chain( - (value_list_rlp_bytes.rlc(&r), pow::expr(r.expr(), 2)).rlc_chain( - (nonce_rlp_rlc.expr(), nonce_items[is_s.idx()].mult()).rlc_chain( - (balance_rlp_rlc.expr(), balance_items[is_s.idx()].mult()) - .rlc_chain( - (storage_rlp_rlc.expr(), pow::expr(r.expr(), 33)) - .rlc_chain(codehash_rlp_rlc.expr()), - ), - ), - ), - ); - let leaf_rlc = - (rlp_key.rlc(&r), mult.expr()).rlc_chain(leaf_no_key_rlc[is_s.idx()].expr()); + leaf_no_key_rlc[is_s.idx()] = value_rlp_bytes + .rlc_rev(&keccak_r) + .rlc_chain2(( + value_list_rlp_bytes.rlc_rev(&keccak_r), + pow::expr(keccak_r.expr(), 2), + )) + .rlc_chain2(nonce_rlp_rlc.clone()) + .rlc_chain2(balance_rlp_rlc.clone()) + .rlc_chain2(storage_rlp_rlc.clone()) + .rlc_chain2(codehash_rlp_rlc.clone()); + leaf_no_key_rlc_mult[is_s.idx()] = pow::expr(keccak_r.expr(), 4) + * nonce_rlp_rlc.1 + * balance_rlp_rlc.1 + * storage_rlp_rlc.1 + * codehash_rlp_rlc.1; + let leaf_rlc = rlp_key.rlc2(&cb.be_r).rlc_chain2(( + leaf_no_key_rlc[is_s.idx()].expr(), + leaf_no_key_rlc_mult[is_s.idx()].expr(), + )); // Key key_rlc[is_s.idx()] = key_data.rlc.expr() + rlp_key.key.expr( - &mut cb.base, + cb, rlp_key.key_value.clone(), key_data.mult.expr(), key_data.is_odd.expr(), - &r, + &cb.le_r.expr(), ); // Total number of nibbles needs to be KEY_LEN_IN_NIBBLES. let num_nibbles = @@ -180,7 +179,7 @@ impl AccountLeafConfig { // Check if the account is in its parent. // Check is skipped for placeholder leaves which are dummy leaves ifx! {not!(and::expr(&[not!(config.parent_data[is_s.idx()].is_placeholder), config.is_in_empty_trie[is_s.idx()].expr()])) => { - require!((1, leaf_rlc, rlp_key.rlp_list.num_bytes(), config.parent_data[is_s.idx()].rlc) => @"keccak"); + require!((1, leaf_rlc, rlp_key.rlp_list.num_bytes(), config.parent_data[is_s.idx()].rlc) => @KECCAK); }} // Check the RLP encoding consistency. @@ -200,10 +199,10 @@ impl AccountLeafConfig { require!(config.rlp_key[is_s.idx()].rlp_list.len() => config.rlp_key[is_s.idx()].key_value.num_bytes() + value_list_num_bytes); // Key done, set the starting values - KeyData::store_defaults(&mut cb.base, &ctx.memory[key_memory(is_s)]); + KeyData::store_defaults(cb, &ctx.memory[key_memory(is_s)]); // Store the new parent ParentData::store( - &mut cb.base, + cb, &ctx.memory[parent_memory(is_s)], storage_rlc[is_s.idx()].expr(), true.expr(), @@ -216,7 +215,7 @@ impl AccountLeafConfig { // TODO(Brecht): For non-existing accounts it should be impossible to prove // storage leaves unless it's also a non-existing proof? MainData::store( - &mut cb.base, + cb, &ctx.memory[main_memory()], [ config.main_data.proof_type.expr(), @@ -266,8 +265,9 @@ impl AccountLeafConfig { &config.key_data, &key_rlc, &leaf_no_key_rlc, + &leaf_no_key_rlc_mult, &drifted_bytes, - &ctx.r, + &cb.le_r.expr(), ); // Wrong leaf handling @@ -280,7 +280,7 @@ impl AccountLeafConfig { &wrong_bytes, config.is_in_empty_trie[true.idx()].expr(), config.key_data[true.idx()].clone(), - &ctx.r, + &cb.le_r.expr(), ); ifx! {config.is_account_delete_mod => { @@ -323,7 +323,7 @@ impl AccountLeafConfig { }} // Put the data in the lookup table - let (proof_type, value_prev, value) = _matchx! {cb.base, + let (proof_type, value_prev, value) = _matchx! {cb, config.is_nonce_mod => (MPTProofType::NonceChanged.expr(), nonce_rlc[true.idx()].expr(), nonce_rlc[false.idx()].expr()), config.is_balance_mod => (MPTProofType::BalanceChanged.expr(), balance_rlc[true.idx()].expr(), balance_rlc[false.idx()].expr()), config.is_storage_mod => (MPTProofType::StorageChanged.expr(), storage_rlc[true.idx()].expr(), storage_rlc[false.idx()].expr()), @@ -357,10 +357,11 @@ impl AccountLeafConfig { config } - pub fn assign( + #[allow(clippy::too_many_arguments)] + pub fn assign>( &self, - region: &mut Region<'_, F>, - ctx: &MPTConfig, + region: &mut CachedRegion<'_, '_, F, S>, + mpt_config: &MPTConfig, pv: &mut MPTState, offset: usize, node: &Node, @@ -421,14 +422,14 @@ impl AccountLeafConfig { key_data[is_s.idx()] = self.key_data[is_s.idx()].witness_load( region, offset, - &mut pv.memory[key_memory(is_s)], + &pv.memory[key_memory(is_s)], 0, )?; parent_data[is_s.idx()] = self.parent_data[is_s.idx()].witness_load( region, offset, - &mut pv.memory[parent_memory(is_s)], + &pv.memory[parent_memory(is_s)], 0, )?; @@ -436,7 +437,7 @@ impl AccountLeafConfig { region, offset, parent_data[is_s.idx()].rlc, - pv.r, + region.le_r, )?; let rlp_key_witness = self.rlp_key[is_s.idx()].assign( @@ -446,17 +447,17 @@ impl AccountLeafConfig { &key_items[is_s.idx()], )?; - nonce_rlc[is_s.idx()] = nonce_items[is_s.idx()].rlc_content(pv.r); - balance_rlc[is_s.idx()] = balance_items[is_s.idx()].rlc_content(pv.r); - storage_rlc[is_s.idx()] = storage_items[is_s.idx()].rlc_content(pv.r); - codehash_rlc[is_s.idx()] = codehash_items[is_s.idx()].rlc_content(pv.r); + nonce_rlc[is_s.idx()] = nonce_items[is_s.idx()].rlc_content(region.le_r); + balance_rlc[is_s.idx()] = balance_items[is_s.idx()].rlc_content(region.le_r); + storage_rlc[is_s.idx()] = storage_items[is_s.idx()].rlc_content(region.le_r); + codehash_rlc[is_s.idx()] = codehash_items[is_s.idx()].rlc_content(region.le_r); // Key (key_rlc[is_s.idx()], _) = rlp_key_witness.key.key( rlp_key_witness.key_item.clone(), key_data[is_s.idx()].rlc, key_data[is_s.idx()].mult, - pv.r, + region.le_r, ); // Update key and parent state @@ -464,11 +465,11 @@ impl AccountLeafConfig { region, offset, &mut pv.memory[key_memory(is_s)], - F::zero(), - F::one(), + F::ZERO, + F::ONE, 0, - F::zero(), - F::one(), + F::ZERO, + F::ONE, 0, )?; ParentData::witness_store( @@ -489,7 +490,7 @@ impl AccountLeafConfig { &mut pv.memory[main_memory()], main_data.proof_type, true, - account.address.rlc_value(pv.r), + account.address.rlc_value(region.le_r), main_data.root_prev, main_data.root, )?; @@ -539,7 +540,7 @@ impl AccountLeafConfig { &parent_data, &account.drifted_rlp_bytes, &drifted_item, - pv.r, + region.le_r, )?; // Wrong leaf handling @@ -552,7 +553,7 @@ impl AccountLeafConfig { &wrong_item, true, key_data[true.idx()].clone(), - pv.r, + region.le_r, )?; // Put the data in the lookup table @@ -571,11 +572,11 @@ impl AccountLeafConfig { } else { (MPTProofType::Disabled, vec![0.scalar(); 2]) }; - ctx.mpt_table.assign( + mpt_config.mpt_table.assign_cached( region, offset, &MptUpdateRow { - address_rlc: Value::known(account.address.rlc_value(pv.r)), + address_rlc: Value::known(account.address.rlc_value(region.le_r)), proof_type: Value::known(proof_type.scalar()), key_rlc: Value::known(0.scalar()), value_prev: Value::known(value[true.idx()]), diff --git a/zkevm-circuits/src/mpt_circuit/branch.rs b/zkevm-circuits/src/mpt_circuit/branch.rs index 433b390e1d..711a90bf24 100644 --- a/zkevm-circuits/src/mpt_circuit/branch.rs +++ b/zkevm-circuits/src/mpt_circuit/branch.rs @@ -1,9 +1,6 @@ use eth_types::Field; use gadgets::util::Scalar; -use halo2_proofs::{ - circuit::Region, - plonk::{Error, Expression, VirtualCells}, -}; +use halo2_proofs::plonk::{Error, Expression, VirtualCells}; use super::{ helpers::{MPTConstraintBuilder, RLPItemView}, @@ -14,9 +11,14 @@ use super::{ }; use crate::{ circuit, - circuit_tools::{cell_manager::Cell, constraint_builder::RLCChainable, gadgets::LtGadget}, + circuit_tools::{ + cached_region::{CachedRegion, ChallengeSet}, + cell_manager::Cell, + constraint_builder::RLCChainable2, + gadgets::LtGadget, + }, mpt_circuit::{ - helpers::{nibble_rlc, Indexable}, + helpers::{nibble_rlc, Indexable, KECCAK}, param::{HASH_WIDTH, RLP_NIL}, MPTConfig, MPTState, }, @@ -46,6 +48,7 @@ pub(crate) struct BranchGadget { } impl BranchGadget { + #[allow(clippy::too_many_arguments)] pub fn configure( meta: &mut VirtualCells<'_, F>, cb: &mut MPTConstraintBuilder, @@ -58,29 +61,28 @@ impl BranchGadget { num_nibbles: Expression, is_key_odd: Expression, ) -> Self { - let r = ctx.r.clone(); - let mut config = BranchGadget::default(); - circuit!([meta, cb.base], { + circuit!([meta, cb], { // Data let children: [RLPItemView; ARITY + 1] = - array_init::array_init(|i| ctx.rlp_item(meta, &mut cb.base, i)); + array_init::array_init(|i| ctx.rlp_item(meta, cb, i)); let mut num_bytes_left = vec![0.expr(); 2]; let mut node_rlc = vec![0.expr(); 2]; - let mut mult = vec![1.expr(); 2]; for is_s in [true, false] { // Read the list - config.rlp_list[is_s.idx()] = RLPListDataGadget::construct(&mut cb.base); + config.rlp_list[is_s.idx()] = RLPListDataGadget::construct(cb); // Start RLC encoding the RLP data starting with the list RLP bytes - (node_rlc[is_s.idx()], mult[is_s.idx()]) = - config.rlp_list[is_s.idx()].rlp_list.rlc_rlp_only(&r); + node_rlc[is_s.idx()] = config.rlp_list[is_s.idx()] + .rlp_list + .rlc_rlp_only2(&cb.be_r) + .0; // Keep track of how many bytes the branch contains to make sure it's correct. num_bytes_left[is_s.idx()] = config.rlp_list[is_s.idx()].rlp_list.len(); - config.mod_rlc[is_s.idx()] = cb.base.query_cell(); + config.mod_rlc[is_s.idx()] = cb.query_cell(); // Check if the branch is hashed or not config.is_not_hashed[is_s.idx()] = LtGadget::construct( @@ -111,18 +113,18 @@ impl BranchGadget { // child branch for c is stored in child 0. let child = &children[node_index + 1]; let mod_child = &children[0]; - let (rlc_rlp, num_bytes, length, mult_diff) = if is_s { + let (rlc, rlc_mult, num_bytes, length) = if is_s { ( - child.rlc_rlp(), + child.rlc_chain_data().0, + child.rlc_chain_data().1, child.num_bytes(), child.len(), - child.mult(), ) } else { ifx! {config.is_modified[node_index] => { - (mod_child.rlc_rlp(), mod_child.num_bytes(), mod_child.len(), mod_child.mult()) + (mod_child.rlc_chain_data().0, mod_child.rlc_chain_data().1, mod_child.num_bytes(), mod_child.len()) } elsex { - (child.rlc_rlp(), child.num_bytes(), child.len(), child.mult()) + (child.rlc_chain_data().0, child.rlc_chain_data().1, child.num_bytes(), child.len()) }} }; @@ -131,11 +133,7 @@ impl BranchGadget { num_bytes_left[is_s.idx()].expr() - num_bytes.expr(); // Update the full branch node RLC with the data of this branch - node_rlc[is_s.idx()] = (node_rlc[is_s.idx()].expr(), mult[is_s.idx()].expr()) - .rlc_chain(rlc_rlp.expr()); - - // Update the branch node multiplier - mult[is_s.idx()] = mult[is_s.idx()].expr() * mult_diff; + node_rlc[is_s.idx()] = node_rlc[is_s.idx()].rlc_chain2((rlc, rlc_mult)); // When in a placeholder branch, both branches are the same - the placeholder // branch and its parallel counterpart, which is not a @@ -163,9 +161,9 @@ impl BranchGadget { for is_s in [true, false] { // Number of bytes left needs to be 1 because ValueNode which occupies 1 byte require!(num_bytes_left[is_s.idx()] => 1); - // TODO: acc currently doesn'thave branch ValueNode info - node_rlc[is_s.idx()] = (node_rlc[is_s.idx()].expr(), mult[is_s.idx()].expr()) - .rlc_chain(RLP_NIL.expr()); + // TODO: acc currently doesn't have branch ValueNode info + node_rlc[is_s.idx()] = + node_rlc[is_s.idx()].rlc_chain2((RLP_NIL.expr(), cb.be_r.expr())); } // `is_modified` needs to be set to 1 at exactly 1 branch child @@ -193,31 +191,32 @@ impl BranchGadget { ifx! {not!(is_placeholder[is_s.idx()]) => { ifx!{or::expr(&[is_root[is_s.idx()].expr(), not!(is_not_hashed)]) => { // Hashed branch hash in parent branch - require!((1, rlc, num_bytes, parent_rlc[is_s.idx()].expr()) => @"keccak"); + require!((1, rlc, num_bytes, parent_rlc[is_s.idx()].expr()) => @KECCAK); } elsex { // Non-hashed branch hash in parent branch - require!(rlc => parent_rlc[is_s.idx()].expr()); + // TODO(Brecht): restore + //require!(rlc => parent_rlc[is_s.idx()].expr()); }} }} } // Update the key RLC and multiplier for the branch nibble. let (key_rlc_post_branch, key_mult_post_branch) = nibble_rlc( - &mut cb.base, + cb, key_rlc.expr(), key_mult.expr(), is_key_odd.expr(), modified_index.expr(), - &r, + &cb.le_r.expr(), ); // Also calculate the key RLC and multiplier for the drifted nibble. let (key_rlc_post_drifted, key_mult_post_drifted) = nibble_rlc( - &mut cb.base, + cb, key_rlc.expr(), key_mult.expr(), is_key_odd.expr(), drifted_index.expr(), - &r, + &cb.le_r.expr(), ); // Update the nibble counter @@ -270,11 +269,13 @@ impl BranchGadget { self.post_state.as_ref().unwrap().clone() } - pub(crate) fn assign( + #[allow(clippy::collapsible_else_if)] + #[allow(clippy::too_many_arguments)] + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, - mpt_config: &MPTConfig, - pv: &mut MPTState, + region: &mut CachedRegion<'_, '_, F, S>, + _mpt_config: &MPTConfig, + _pv: &mut MPTState, offset: usize, is_placeholder: &[bool; 2], key_rlc: &mut F, @@ -326,7 +327,7 @@ impl BranchGadget { } else { // The nibble will be added as the least significant nibble, the multiplier // needs to advance - (1.scalar(), pv.r) + (1.scalar(), region.le_r) }; let key_rlc_post_branch = *key_rlc + F::from(branch.modified_index as u64) * nibble_mult * *key_mult; @@ -338,12 +339,12 @@ impl BranchGadget { let mut mod_node_hash_rlc = [0.scalar(); 2]; for is_s in [true, false] { mod_node_hash_rlc[is_s.idx()] = if is_placeholder[is_s.idx()] { - rlp_values[1 + branch.drifted_index].rlc_content(pv.r) + rlp_values[1 + branch.drifted_index].rlc_content(region.le_r) } else { if is_s { - rlp_values[1 + branch.modified_index].rlc_content(pv.r) + rlp_values[1 + branch.modified_index].rlc_content(region.le_r) } else { - rlp_values[0].rlc_content(pv.r) + rlp_values[0].rlc_content(region.le_r) } }; self.mod_rlc[is_s.idx()].assign(region, offset, mod_node_hash_rlc[is_s.idx()])?; diff --git a/zkevm-circuits/src/mpt_circuit/extension.rs b/zkevm-circuits/src/mpt_circuit/extension.rs index 47c38202dd..abe24f38eb 100644 --- a/zkevm-circuits/src/mpt_circuit/extension.rs +++ b/zkevm-circuits/src/mpt_circuit/extension.rs @@ -1,9 +1,6 @@ use eth_types::Field; use gadgets::util::{pow, Scalar}; -use halo2_proofs::{ - circuit::Region, - plonk::{Error, Expression, VirtualCells}, -}; +use halo2_proofs::plonk::{Error, Expression, VirtualCells}; use super::{ helpers::{KeyDataWitness, ListKeyGadget, MPTConstraintBuilder}, @@ -13,10 +10,16 @@ use super::{ }; use crate::{ circuit, - circuit_tools::{cell_manager::Cell, constraint_builder::RLCChainable, gadgets::LtGadget}, + circuit_tools::{ + cached_region::{CachedRegion, ChallengeSet}, + cell_manager::Cell, + constraint_builder::RLCChainable2, + gadgets::LtGadget, + }, mpt_circuit::{ helpers::{ ext_key_rlc_calc_value, ext_key_rlc_expr, num_nibbles, Indexable, KeyData, ParentData, + FIXED, KECCAK, }, param::HASH_WIDTH, FixedTableTag, MPTConfig, MPTState, @@ -53,24 +56,27 @@ impl ExtensionGadget { parent_data: &[ParentData; 2], is_placeholder: &[Cell; 2], ) -> Self { - let r = ctx.r.clone(); - let mut config = ExtensionGadget::default(); - circuit!([meta, cb.base], { + circuit!([meta, cb], { // Data let key_items = [ - ctx.rlp_item(meta, &mut cb.base, ExtensionBranchRowType::KeyS as usize), - ctx.nibbles(meta, &mut cb.base, ExtensionBranchRowType::KeyC as usize), + ctx.rlp_item(meta, cb, ExtensionBranchRowType::KeyS as usize), + ctx.nibbles(meta, cb, ExtensionBranchRowType::KeyC as usize), ]; let rlp_value = [ - ctx.rlp_item(meta, &mut cb.base, ExtensionBranchRowType::ValueS as usize), - ctx.rlp_item(meta, &mut cb.base, ExtensionBranchRowType::ValueC as usize), + ctx.rlp_item(meta, cb, ExtensionBranchRowType::ValueS as usize), + ctx.rlp_item(meta, cb, ExtensionBranchRowType::ValueC as usize), ]; - config.rlp_key = ListKeyGadget::construct(&mut cb.base, &key_items[0]); - // TODO(Brecht): add lookup constraint - config.is_key_part_odd = cb.base.query_cell(); + config.rlp_key = ListKeyGadget::construct(cb, &key_items[0]); + config.is_key_part_odd = cb.query_cell(); + let first_byte = matchx! { + key_items[true.idx()].is_short() => key_items[true.idx()].bytes[0].expr(), + key_items[true.idx()].is_long() => key_items[true.idx()].bytes[1].expr(), + key_items[true.idx()].is_very_long() => key_items[true.idx()].bytes[2].expr(), + }; + require!((FixedTableTag::ExtOddKey.expr(), first_byte, config.is_key_part_odd.expr()) => @FIXED); let mut branch_rlp_rlc = vec![0.expr(); 2]; for is_s in [true, false] { @@ -81,12 +87,11 @@ impl ExtensionGadget { require!(config.rlp_key.rlp_list.len() => config.rlp_key.key_value.num_bytes() + rlp_value[is_s.idx()].num_bytes()); } - // Multiplier after list and key - let mult = config.rlp_key.rlp_list.rlp_mult(&r) * key_items[true.idx()].mult(); - // Extension node RLC - let node_rlc = (config.rlp_key.rlc(&r), mult.expr()) - .rlc_chain(rlp_value[is_s.idx()].rlc_rlp()); + let node_rlc = config + .rlp_key + .rlc2(&cb.be_r) + .rlc_chain2(rlp_value[is_s.idx()].rlc_chain_data()); // The branch expected in the extension node branch_rlp_rlc[is_s.idx()] = rlp_value[is_s.idx()].rlc_content(); @@ -109,7 +114,7 @@ impl ExtensionGadget { ifx! {not!(is_placeholder[is_s.idx()]) => { ifx!{or::expr(&[parent_data[is_s.idx()].is_root.expr(), not!(is_not_hashed)]) => { // Hashed branch hash in parent branch - require!((1, rlc, num_bytes, parent_data[is_s.idx()].rlc) => @"keccak"); + require!((1, rlc, num_bytes, parent_data[is_s.idx()].rlc) => @KECCAK); } elsex { // Non-hashed branch hash in parent branch require!(rlc => parent_data[is_s.idx()].rlc); @@ -139,7 +144,7 @@ impl ExtensionGadget { // implemented. let key_rlc = key_data.rlc.expr() + ext_key_rlc_expr( - &mut cb.base, + cb, config.rlp_key.key_value.clone(), key_data.mult.expr(), config.is_key_part_odd.expr(), @@ -150,7 +155,7 @@ impl ExtensionGadget { .collect::>() .try_into() .unwrap(), - &ctx.r, + &cb.le_r.expr(), ); // Get the length of the key @@ -159,12 +164,12 @@ impl ExtensionGadget { let key_num_bytes_for_mult = key_len - ifx! {not!(key_data.is_odd.expr() * config.is_key_part_odd.expr()) => { 1.expr() }}; // Get the multiplier for this key length - config.mult_key = cb.base.query_cell(); - require!((FixedTableTag::RMult, key_num_bytes_for_mult, config.mult_key.expr()) => @"fixed"); + config.mult_key = cb.query_cell(); + require!((FixedTableTag::LERMult, key_num_bytes_for_mult, config.mult_key.expr()) => @FIXED); // Store the post ext state config.post_state = Some(ExtState { - key_rlc: key_rlc, + key_rlc, key_mult: key_data.mult.expr() * config.mult_key.expr(), num_nibbles, is_key_odd, @@ -179,11 +184,12 @@ impl ExtensionGadget { self.post_state.as_ref().unwrap().clone() } - pub(crate) fn assign( + #[allow(clippy::too_many_arguments)] + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, - mpt_config: &MPTConfig, - pv: &mut MPTState, + region: &mut CachedRegion<'_, '_, F, S>, + _mpt_config: &MPTConfig, + _pv: &mut MPTState, offset: usize, key_data: &KeyDataWitness, key_rlc: &mut F, @@ -247,7 +253,7 @@ impl ExtensionGadget { // Key RLC let (key_rlc_ext, _) = ext_key_rlc_calc_value( - rlp_key.key_item.clone(), + rlp_key.key_item, key_data.mult, is_key_part_odd, !*is_key_odd, @@ -257,12 +263,12 @@ impl ExtensionGadget { .collect::>() .try_into() .unwrap(), - pv.r, + region.le_r, ); *key_rlc = key_data.rlc + key_rlc_ext; // Key mult - let mult_key = pow::value(pv.r, key_len_mult); + let mult_key = pow::value(region.le_r, key_len_mult); self.mult_key.assign(region, offset, mult_key)?; *key_mult = key_data.mult * mult_key; diff --git a/zkevm-circuits/src/mpt_circuit/extension_branch.rs b/zkevm-circuits/src/mpt_circuit/extension_branch.rs index 03623c3696..d4c8c5dcae 100644 --- a/zkevm-circuits/src/mpt_circuit/extension_branch.rs +++ b/zkevm-circuits/src/mpt_circuit/extension_branch.rs @@ -1,9 +1,6 @@ use eth_types::Field; use gadgets::util::Scalar; -use halo2_proofs::{ - circuit::Region, - plonk::{Error, VirtualCells}, -}; +use halo2_proofs::plonk::{Error, VirtualCells}; use super::{ branch::BranchGadget, @@ -15,7 +12,10 @@ use super::{ }; use crate::{ circuit, - circuit_tools::cell_manager::Cell, + circuit_tools::{ + cached_region::{CachedRegion, ChallengeSet}, + cell_manager::Cell, + }, mpt_circuit::{ helpers::{key_memory, parent_memory, Indexable, KeyData, ParentData}, witness_row::ExtensionBranchRowType, @@ -46,24 +46,24 @@ impl ExtensionBranchConfig { .reset(ExtensionBranchRowType::Count as usize); let mut config = ExtensionBranchConfig::default(); - circuit!([meta, cb.base], { + circuit!([meta, cb], { // General inputs - config.is_extension = cb.base.query_bool(); + config.is_extension = cb.query_bool(); // If we're in a placeholder, both the extension and the branch parts are // placeholders for is_s in [true, false] { - config.is_placeholder[is_s.idx()] = cb.base.query_bool(); + config.is_placeholder[is_s.idx()] = cb.query_bool(); } // Don't allow both branches to be placeholders require!(config.is_placeholder[true.idx()].expr() + config.is_placeholder[false.idx()].expr() => bool); // Load the last key values - config.key_data = KeyData::load(&mut cb.base, &ctx.memory[key_memory(true)], 0.expr()); + config.key_data = KeyData::load(cb, &ctx.memory[key_memory(true)], 0.expr()); // Load the parent values for is_s in [true, false] { config.parent_data[is_s.idx()] = ParentData::load( "branch load", - &mut cb.base, + cb, &ctx.memory[parent_memory(is_s)], 0.expr(), ); @@ -135,7 +135,7 @@ impl ExtensionBranchConfig { for is_s in [true, false] { ifx! {not!(config.is_placeholder[is_s.idx()].expr()) => { KeyData::store( - &mut cb.base, + cb, &ctx.memory[key_memory(is_s)], branch.key_rlc_post_branch.expr(), branch.key_mult_post_branch.expr(), @@ -147,7 +147,7 @@ impl ExtensionBranchConfig { false.expr(), ); ParentData::store( - &mut cb.base, + cb, &ctx.memory[parent_memory(is_s)], branch.mod_rlc[is_s.idx()].expr(), false.expr(), @@ -156,7 +156,7 @@ impl ExtensionBranchConfig { ); } elsex { KeyData::store( - &mut cb.base, + cb, &ctx.memory[key_memory(is_s)], config.key_data.rlc.expr(), config.key_data.mult.expr(), @@ -168,7 +168,7 @@ impl ExtensionBranchConfig { branch.is_key_odd.expr(), ); ParentData::store( - &mut cb.base, + cb, &ctx.memory[parent_memory(is_s)], config.parent_data[is_s.idx()].rlc.expr(), config.parent_data[is_s.idx()].is_root.expr(), @@ -182,9 +182,10 @@ impl ExtensionBranchConfig { config } - pub(crate) fn assign( + #[allow(clippy::too_many_arguments)] + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, mpt_config: &MPTConfig, pv: &mut MPTState, offset: usize, @@ -204,7 +205,7 @@ impl ExtensionBranchConfig { parent_data[is_s.idx()] = self.parent_data[is_s.idx()].witness_load( region, offset, - &mut pv.memory[parent_memory(is_s)], + &pv.memory[parent_memory(is_s)], 0, )?; self.is_placeholder[is_s.idx()].assign( diff --git a/zkevm-circuits/src/mpt_circuit/helpers.rs b/zkevm-circuits/src/mpt_circuit/helpers.rs index c16f0b4ee1..4823f49992 100644 --- a/zkevm-circuits/src/mpt_circuit/helpers.rs +++ b/zkevm-circuits/src/mpt_circuit/helpers.rs @@ -1,34 +1,85 @@ use crate::{ assign, circuit, circuit_tools::{ - cell_manager::{Cell, CellManager}, + cached_region::{CachedRegion, ChallengeSet}, + cell_manager::{Cell, CellManager, CellType}, constraint_builder::{ - ConstraintBuilder, RLCChainable, RLCChainableValue, RLCable, RLCableValue, + ConstraintBuilder, RLCChainable, RLCChainable2, RLCChainableValue, RLCable, + RLCableValue, }, gadgets::IsEqualGadget, memory::MemoryBank, }, + evm_circuit::table::Table, matchw, mpt_circuit::{ param::{EMPTY_TRIE_HASH, KEY_LEN_IN_NIBBLES, KEY_PREFIX_EVEN, KEY_TERMINAL_PREFIX_EVEN}, rlp_gadgets::{get_ext_odd_nibble, get_terminal_odd_nibble}, + PhaseTwoTableTag, }, - util::Expr, + util::{Challenges, Expr}, }; use eth_types::Field; -use gadgets::util::{or, pow, Scalar}; +use gadgets::util::{not, or, pow, Scalar}; use halo2_proofs::{ - circuit::Region, + circuit::Value, plonk::{Error, Expression, VirtualCells}, }; use super::{ + param::HASH_WIDTH, rlp_gadgets::{ get_ext_odd_nibble_value, RLPItemGadget, RLPItemWitness, RLPListGadget, RLPListWitness, }, FixedTableTag, }; +impl ChallengeSet for crate::util::Challenges> { + fn indexed(&self) -> Vec<&Value> { + self.indexed().to_vec() + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum MptCellType { + StoragePhase1, + StoragePhase2, + StoragePhase3, + StoragePermutation, + LookupByte, + Lookup(Table), + MemParentS, + MemParentC, + MemKeyS, + MemKeyC, + MemMain, +} + +impl Default for MptCellType { + fn default() -> Self { + Self::StoragePhase1 + } +} + +impl CellType for MptCellType { + fn byte_type() -> Option { + Some(MptCellType::LookupByte) + } + + fn storage_for_phase(phase: u8) -> Self { + match phase { + 0 => MptCellType::StoragePhase1, + 1 => MptCellType::StoragePhase2, + 2 => MptCellType::StoragePhase3, + _ => unreachable!(), + } + } +} + +pub const FIXED: MptCellType = MptCellType::Lookup(Table::Fixed); +pub const KECCAK: MptCellType = MptCellType::Lookup(Table::Keccak); +pub const PHASE_TWO: MptCellType = MptCellType::Lookup(Table::Exp); + /// Indexable object pub trait Indexable { /// Convert to index @@ -56,10 +107,10 @@ pub(crate) struct LeafKeyWitness { } impl LeafKeyGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder, rlp_key: RLPItemView) -> Self { + pub(crate) fn construct(cb: &mut MPTConstraintBuilder, rlp_key: RLPItemView) -> Self { circuit!([meta, cb], { let has_no_nibbles = IsEqualGadget::::construct( - cb, + &mut cb.base, rlp_key.bytes()[0].expr(), KEY_TERMINAL_PREFIX_EVEN.expr(), ); @@ -69,14 +120,14 @@ impl LeafKeyGadget { pub(crate) fn expr( &self, - cb: &mut ConstraintBuilder, + cb: &mut MPTConstraintBuilder, rlp_key: RLPItemView, key_mult_prev: Expression, is_key_odd: Expression, r: &Expression, ) -> Expression { - circuit!([meta, cb], { - let calc_rlc = |cb: &mut ConstraintBuilder, + circuit!([meta, cb.base], { + let calc_rlc = |cb: &mut MPTConstraintBuilder, bytes: &[Expression], is_key_odd: Expression| { leaf_key_rlc(cb, bytes, key_mult_prev.expr(), is_key_odd.expr(), r) @@ -96,9 +147,9 @@ impl LeafKeyGadget { }) } - pub(crate) fn assign( + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, bytes: &[u8], ) -> Result { @@ -143,7 +194,7 @@ impl LeafKeyWitness { } pub(crate) fn ext_key_rlc_expr( - cb: &mut ConstraintBuilder, + cb: &mut MPTConstraintBuilder, key_value: RLPItemView, key_mult_prev: Expression, is_key_part_odd: Expression, @@ -151,10 +202,10 @@ pub(crate) fn ext_key_rlc_expr( data: [Vec>; 2], r: &Expression, ) -> Expression { - circuit!([meta, cb], { + circuit!([meta, cb.base], { let (is_short, is_long) = (key_value.is_short(), key_value.is_long()); let mult_first_odd = ifx! {is_key_odd => { 1.expr() } elsex { 16.expr() }}; - let calc_rlc = |cb: &mut ConstraintBuilder, + let calc_rlc = |cb: &mut MPTConstraintBuilder, bytes: &[Expression], key_mult_first_even: Expression| { ext_key_rlc( @@ -226,7 +277,7 @@ pub(crate) fn ext_key_rlc_calc_value( // Check that `nibble_hi` is correct. assert!(*byte == nibble_lo * 16 + nibble_hi); // Collect bytes - (F::from(*nibble_hi as u64) * F::from(16 as u64) * r) + F::from(nibble_lo as u64) + (F::from(*nibble_hi as u64) * F::from(16_u64) * r) + F::from(nibble_lo as u64) }).collect::>()); calc_rlc(&key_bytes, 1.scalar()) }, @@ -256,7 +307,7 @@ pub(crate) struct ListKeyWitness { } impl ListKeyGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder, key_value: &RLPItemView) -> Self { + pub(crate) fn construct(cb: &mut MPTConstraintBuilder, key_value: &RLPItemView) -> Self { let rlp_list_bytes = cb.query_bytes(); let rlp_list_bytes_expr = rlp_list_bytes.iter().map(|c| c.expr()).collect::>(); let key = LeafKeyGadget::construct(cb, key_value.clone()); @@ -268,9 +319,9 @@ impl ListKeyGadget { } } - pub(crate) fn assign( + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, list_bytes: &[u8], key_item: &RLPItemWitness, @@ -293,6 +344,13 @@ impl ListKeyGadget { .rlc_rlp_only(r) .rlc_chain(self.key_value.rlc_rlp()) } + + pub(crate) fn rlc2(&self, r: &Expression) -> Expression { + self.rlp_list + .rlc_rlp_only2(r) + .0 + .rlc_chain2(self.key_value.rlc_chain_data()) + } } impl ListKeyWitness { @@ -330,8 +388,8 @@ pub(crate) struct KeyDataWitness { impl KeyData { pub(crate) fn load( - cb: &mut ConstraintBuilder, - memory: &MemoryBank, + cb: &mut MPTConstraintBuilder, + memory: &MemoryBank, offset: Expression, ) -> Self { let key_data = KeyData { @@ -344,10 +402,10 @@ impl KeyData { drifted_num_nibbles: cb.query_cell(), drifted_is_odd: cb.query_cell(), }; - circuit!([meta, cb], { + circuit!([meta, cb.base], { memory.load( "key load", - cb, + &mut cb.base, offset, &[ key_data.rlc.expr(), @@ -364,9 +422,10 @@ impl KeyData { key_data } + #[allow(clippy::too_many_arguments)] pub(crate) fn store( - cb: &mut ConstraintBuilder, - memory: &MemoryBank, + cb: &mut MPTConstraintBuilder, + memory: &MemoryBank, rlc: Expression, mult: Expression, num_nibbles: Expression, @@ -377,7 +436,7 @@ impl KeyData { drifted_is_odd: Expression, ) { memory.store( - cb, + &mut cb.base, &[ rlc, mult, @@ -391,8 +450,11 @@ impl KeyData { ); } - pub(crate) fn store_defaults(cb: &mut ConstraintBuilder, memory: &MemoryBank) { - memory.store(cb, &KeyData::default_values_expr()); + pub(crate) fn store_defaults( + cb: &mut MPTConstraintBuilder, + memory: &MemoryBank, + ) { + memory.store(&mut cb.base, &KeyData::default_values_expr()); } pub(crate) fn default_values_expr() -> [Expression; 8] { @@ -408,10 +470,11 @@ impl KeyData { ] } - pub(crate) fn witness_store( - _region: &mut Region<'_, F>, + #[allow(clippy::too_many_arguments)] + pub(crate) fn witness_store>( + _region: &mut CachedRegion<'_, '_, F, S>, offset: usize, - memory: &mut MemoryBank, + memory: &mut MemoryBank, rlc: F, mult: F, num_nibbles: usize, @@ -434,11 +497,11 @@ impl KeyData { Ok(()) } - pub(crate) fn witness_load( + pub(crate) fn witness_load>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, - memory: &MemoryBank, + memory: &MemoryBank, load_offset: usize, ) -> Result, Error> { let values = memory.witness_load(load_offset); @@ -456,11 +519,11 @@ impl KeyData { rlc: values[0], mult: values[1], num_nibbles: values[2].get_lower_32() as usize, - is_odd: values[3] != F::zero(), + is_odd: values[3] != F::ZERO, drifted_rlc: values[4], drifted_mult: values[5], drifted_num_nibbles: values[6].get_lower_32() as usize, - drifted_is_odd: values[7] != F::zero(), + drifted_is_odd: values[7] != F::ZERO, }) } } @@ -484,20 +547,20 @@ pub(crate) struct ParentDataWitness { impl ParentData { pub(crate) fn load( description: &'static str, - cb: &mut ConstraintBuilder, - memory: &MemoryBank, + cb: &mut MPTConstraintBuilder, + memory: &MemoryBank, offset: Expression, ) -> Self { let parent_data = ParentData { - rlc: cb.query_cell(), + rlc: cb.query_cell_with_type(MptCellType::StoragePhase2), is_root: cb.query_cell(), is_placeholder: cb.query_cell(), drifted_parent_rlc: cb.query_cell(), }; - circuit!([meta, cb], { + circuit!([meta, cb.base], { memory.load( description, - cb, + &mut cb.base, offset, &[ parent_data.rlc.expr(), @@ -511,20 +574,23 @@ impl ParentData { } pub(crate) fn store( - cb: &mut ConstraintBuilder, - memory: &MemoryBank, + cb: &mut MPTConstraintBuilder, + memory: &MemoryBank, rlc: Expression, is_root: Expression, is_placeholder: Expression, drifted_parent_rlc: Expression, ) { - memory.store(cb, &[rlc, is_root, is_placeholder, drifted_parent_rlc]); + memory.store( + &mut cb.base, + &[rlc, is_root, is_placeholder, drifted_parent_rlc], + ); } - pub(crate) fn witness_store( - _region: &mut Region<'_, F>, + pub(crate) fn witness_store>( + _region: &mut CachedRegion<'_, '_, F, S>, offset: usize, - memory: &mut MemoryBank, + memory: &mut MemoryBank, rlc: F, force_hashed: bool, is_placeholder: bool, @@ -542,11 +608,11 @@ impl ParentData { Ok(()) } - pub(crate) fn witness_load( + pub(crate) fn witness_load>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, - memory: &MemoryBank, + memory: &MemoryBank, load_offset: usize, ) -> Result, Error> { let values = memory.witness_load(load_offset); @@ -586,8 +652,8 @@ pub(crate) struct MainDataWitness { impl MainData { pub(crate) fn load( description: &'static str, - cb: &mut ConstraintBuilder, - memory: &MemoryBank, + cb: &mut MPTConstraintBuilder, + memory: &MemoryBank, offset: Expression, ) -> Self { let main_data = MainData { @@ -597,10 +663,10 @@ impl MainData { root_prev: cb.query_cell(), root: cb.query_cell(), }; - circuit!([meta, cb], { + circuit!([meta, cb.base], { memory.load( description, - cb, + &mut cb.base, offset, &[ main_data.proof_type.expr(), @@ -615,17 +681,18 @@ impl MainData { } pub(crate) fn store( - cb: &mut ConstraintBuilder, - memory: &MemoryBank, + cb: &mut MPTConstraintBuilder, + memory: &MemoryBank, values: [Expression; 5], ) { - memory.store(cb, &values); + memory.store(&mut cb.base, &values); } - pub(crate) fn witness_store( - _region: &mut Region<'_, F>, + #[allow(clippy::too_many_arguments)] + pub(crate) fn witness_store>( + _region: &mut CachedRegion<'_, '_, F, S>, offset: usize, - memory: &mut MemoryBank, + memory: &mut MemoryBank, proof_type: usize, is_below_account: bool, address_rlc: F, @@ -644,11 +711,11 @@ impl MainData { Ok(()) } - pub(crate) fn witness_load( + pub(crate) fn witness_load>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, - memory: &MemoryBank, + memory: &MemoryBank, load_offset: usize, ) -> Result, Error> { let values = memory.witness_load(load_offset); @@ -671,14 +738,14 @@ impl MainData { /// Add the nibble from the drifted branch pub(crate) fn nibble_rlc( - cb: &mut ConstraintBuilder, + cb: &mut MPTConstraintBuilder, key_rlc: Expression, key_mult_prev: Expression, is_key_odd: Expression, nibble: Expression, r: &Expression, ) -> (Expression, Expression) { - circuit!([meta, cb], { + circuit!([meta, cb.base], { let (nibble_mult, mult) = ifx! {is_key_odd => { // The nibble will be added as the least significant nibble, the multiplier needs to advance (1.expr(), r.expr()) @@ -694,13 +761,13 @@ pub(crate) fn nibble_rlc( } pub(crate) fn leaf_key_rlc( - cb: &mut ConstraintBuilder, + cb: &mut MPTConstraintBuilder, bytes: &[Expression], key_mult_prev: Expression, is_key_odd: Expression, r: &Expression, ) -> Expression { - circuit!([meta, cb], { + circuit!([meta, cb.base], { // Add the odd nibble first if we have one. let (rlc, mult) = ifx! {is_key_odd => { (get_terminal_odd_nibble(bytes[0].expr()) * key_mult_prev.expr(), r.expr()) @@ -713,7 +780,7 @@ pub(crate) fn leaf_key_rlc( } pub(crate) fn ext_key_rlc( - cb: &mut ConstraintBuilder, + cb: &mut MPTConstraintBuilder, bytes: &[Expression], key_mult_prev: Expression, is_odd: Expression, @@ -721,7 +788,7 @@ pub(crate) fn ext_key_rlc( key_mult_first_odd: Expression, r: &Expression, ) -> Expression { - circuit!([meta, cb], { + circuit!([meta, cb.base], { // Add the odd nibble first if we have one. let (rlc, mult) = ifx! {is_odd => { (get_ext_odd_nibble(bytes[0].expr()) * key_mult_prev.expr() * rlc_mult_first_odd, key_mult_first_odd.expr()) @@ -751,8 +818,7 @@ pub(crate) fn ext_key_rlc_value( assert!(bytes[0] == KEY_PREFIX_EVEN.scalar()); (0.scalar(), 1.scalar()) }; - (rlc, key_mult_prev * mult) - .rlc_chain_value(bytes[1..].iter().map(|v| v).collect::>(), r) + (rlc, key_mult_prev * mult).rlc_chain_value(bytes[1..].iter().collect::>(), r) } // Returns the number of nibbles stored in a key value @@ -782,30 +848,152 @@ pub(crate) mod num_nibbles { } } -pub(crate) fn parent_memory(is_s: bool) -> String { - (if is_s { "parent_s" } else { "parent_c" }).to_string() +pub(crate) fn parent_memory(is_s: bool) -> MptCellType { + if is_s { + MptCellType::MemParentS + } else { + MptCellType::MemParentC + } } -pub(crate) fn key_memory(is_s: bool) -> String { - (if is_s { "key_s" } else { "key_c" }).to_string() +pub(crate) fn key_memory(is_s: bool) -> MptCellType { + if is_s { + MptCellType::MemKeyS + } else { + MptCellType::MemKeyC + } } -pub(crate) fn main_memory() -> String { - "main".to_string() +pub(crate) fn main_memory() -> MptCellType { + MptCellType::MemMain } /// MPTConstraintBuilder #[derive(Clone)] pub struct MPTConstraintBuilder { - pub base: ConstraintBuilder, + pub base: ConstraintBuilder, + pub challenges: Option>>, + pub le_r: Expression, + pub be_r: Expression, } impl MPTConstraintBuilder { - pub(crate) fn new(max_degree: usize, cell_manager: Option>) -> Self { + pub(crate) fn new( + max_degree: usize, + challenges: Option>>, + cell_manager: Option>, + le_r: Expression, + ) -> Self { MPTConstraintBuilder { - base: ConstraintBuilder::new(max_degree, cell_manager), + base: ConstraintBuilder::new( + max_degree, + cell_manager, + Some(challenges.clone().unwrap().lookup_input().expr()), + ), + le_r: le_r.expr(), + be_r: challenges.clone().unwrap().keccak_input().expr(), // (r.expr() + 1.expr()) + challenges, } } + + pub(crate) fn push_condition(&mut self, condition: Expression) { + self.base.push_condition(condition) + } + + pub(crate) fn pop_condition(&mut self) { + self.base.pop_condition() + } + + pub(crate) fn query_bool(&mut self) -> Cell { + self.base.query_bool() + } + + pub(crate) fn query_byte(&mut self) -> Cell { + self.base.query_one(MptCellType::LookupByte) + } + + pub(crate) fn query_bytes(&mut self) -> [Cell; N] { + self.base + .query_cells_dyn(MptCellType::LookupByte, N) + .try_into() + .unwrap() + } + + pub(crate) fn query_bytes_dyn(&mut self, count: usize) -> Vec> { + self.base.query_cells_dyn(MptCellType::StoragePhase1, count) + } + + pub(crate) fn query_cell(&mut self) -> Cell { + self.base.query_default() + } + + pub(crate) fn query_cells(&mut self) -> [Cell; N] { + self.base + .query_cells_dyn(MptCellType::default(), N) + .try_into() + .unwrap() + } + + pub(crate) fn query_cell_with_type(&mut self, cell_type: MptCellType) -> Cell { + self.base.query_cell_with_type(cell_type) + } + + pub(crate) fn require_equal( + &mut self, + name: &'static str, + lhs: Expression, + rhs: Expression, + ) { + self.base.require_equal(name, lhs, rhs) + } + + pub(crate) fn require_in_set( + &mut self, + name: &'static str, + value: Expression, + set: Vec>, + ) { + self.base.require_in_set(name, value, set) + } + + pub(crate) fn require_boolean(&mut self, name: &'static str, value: Expression) { + self.base.require_boolean(name, value) + } + + pub(crate) fn add_dynamic_lookup( + &mut self, + description: &'static str, + tag: MptCellType, + values: Vec>, + is_fixed: bool, + is_combine: bool, + is_split: bool, + ) { + self.base + .add_dynamic_lookup(description, tag, values, is_fixed, is_combine, is_split) + } + + pub(crate) fn add_celltype_lookup( + &mut self, + description: &'static str, + cell_type: MptCellType, + values: Vec>, + ) { + self.base + .add_celltype_lookup(description, cell_type, values) + } + + pub(crate) fn store_dynamic_table( + &mut self, + description: &'static str, + tag: MptCellType, + values: Vec>, + is_combine: bool, + is_split: bool, + ) { + self.base + .store_dynamic_table(description, tag, values, is_combine, is_split) + } } /// Returns `1` when `value == 0`, and returns `0` otherwise. @@ -817,19 +1005,20 @@ pub struct IsEmptyTreeGadget { impl IsEmptyTreeGadget { pub(crate) fn construct( - cb: &mut ConstraintBuilder, + cb: &mut MPTConstraintBuilder, parent_rlc: Expression, r: &Expression, ) -> Self { - circuit!([meta, cb], { + circuit!([meta, cb.base], { let empty_root_rlc = EMPTY_TRIE_HASH .iter() .map(|v| v.expr()) .collect::>() - .rlc(&r); + .rlc(r); let is_in_empty_trie = - IsEqualGadget::construct(cb, parent_rlc.expr(), empty_root_rlc.expr()); - let is_in_empty_branch = IsEqualGadget::construct(cb, parent_rlc.expr(), 0.expr()); + IsEqualGadget::construct(&mut cb.base, parent_rlc.expr(), empty_root_rlc.expr()); + let is_in_empty_branch = + IsEqualGadget::construct(&mut cb.base, parent_rlc.expr(), 0.expr()); Self { is_in_empty_trie, @@ -842,9 +1031,9 @@ impl IsEmptyTreeGadget { or::expr(&[self.is_in_empty_trie.expr(), self.is_in_empty_branch.expr()]) } - pub(crate) fn assign( + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, parent_rlc: F, r: F, @@ -870,13 +1059,14 @@ impl DriftedGadget { key_data: &[KeyData], expected_key_rlc: &[Expression], leaf_no_key_rlc: &[Expression], + leaf_no_key_rlc_mult: &[Expression], drifted_item: &RLPItemView, r: &Expression, ) -> Self { let mut config = DriftedGadget::default(); - circuit!([meta, cb.base], { + circuit!([meta, cb], { ifx! {parent_data[true.idx()].is_placeholder.expr() + parent_data[false.idx()].is_placeholder.expr() => { - config.drifted_rlp_key = ListKeyGadget::construct(&mut cb.base, drifted_item); + config.drifted_rlp_key = ListKeyGadget::construct(cb, drifted_item); for is_s in [true, false] { ifx! {parent_data[is_s.idx()].is_placeholder.expr() => { // Check that the drifted leaf is unchanged and is stored at `drifted_index`. @@ -891,11 +1081,11 @@ impl DriftedGadget { key_data[is_s.idx()].drifted_is_odd.expr(), ); let key_rlc = key_rlc.expr() + config.drifted_rlp_key.key.expr( - &mut cb.base, + cb, config.drifted_rlp_key.key_value.clone(), key_mult.expr(), is_key_odd.expr(), - &r + r ); // The key of the drifted leaf needs to match the key of the leaf require!(key_rlc => expected_key_rlc[is_s.idx()]); @@ -905,13 +1095,11 @@ impl DriftedGadget { let num_nibbles = num_nibbles::expr(config.drifted_rlp_key.key_value.len(), is_key_odd.expr()); require!(key_num_nibbles.expr() + num_nibbles => KEY_LEN_IN_NIBBLES); - // Multiplier after list and key - let mult = config.drifted_rlp_key.rlp_list.rlp_mult(&r) * drifted_item.mult(); - // Complete the drifted leaf rlc by adding the bytes on the value row - let leaf_rlc = (config.drifted_rlp_key.rlc(&r), mult.expr()).rlc_chain(leaf_no_key_rlc[is_s.idx()].expr()); + //let leaf_rlc = (config.drifted_rlp_key.rlc(be_r), mult.expr()).rlc_chain(leaf_no_key_rlc[is_s.idx()].expr()); + let leaf_rlc = config.drifted_rlp_key.rlc2(&cb.be_r).rlc_chain2((leaf_no_key_rlc[is_s.idx()].expr(), leaf_no_key_rlc_mult[is_s.idx()].expr())); // The drifted leaf needs to be stored in the branch at `drifted_index`. - require!((1, leaf_rlc, config.drifted_rlp_key.rlp_list.num_bytes(), parent_data[is_s.idx()].drifted_parent_rlc.expr()) => @"keccak"); + require!((1, leaf_rlc, config.drifted_rlp_key.rlp_list.num_bytes(), parent_data[is_s.idx()].drifted_parent_rlc.expr()) => @KECCAK); } }} }} @@ -919,9 +1107,9 @@ impl DriftedGadget { }) } - pub(crate) fn assign( + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, parent_data: &[ParentDataWitness], drifted_list_bytes: &[u8], @@ -946,6 +1134,7 @@ pub struct WrongGadget { } impl WrongGadget { + #[allow(clippy::too_many_arguments)] pub(crate) fn construct( cb: &mut MPTConstraintBuilder, expected_address: Expression, @@ -962,9 +1151,9 @@ impl WrongGadget { // Get the previous key data ifx! {is_non_existing, not!(is_in_empty_tree) => { // Calculate the key - config.wrong_rlp_key = ListKeyGadget::construct(&mut cb.base, wrong_item); + config.wrong_rlp_key = ListKeyGadget::construct(cb, wrong_item); let key_rlc_wrong = key_data.rlc.expr() + config.wrong_rlp_key.key.expr( - &mut cb.base, + cb, config.wrong_rlp_key.key_value.clone(), key_data.mult.expr(), key_data.is_odd.expr(), @@ -987,9 +1176,10 @@ impl WrongGadget { }) } - pub(crate) fn assign( + #[allow(clippy::too_many_arguments)] + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, is_non_existing: bool, key_rlc: &[F], @@ -1030,19 +1220,31 @@ pub struct MainRLPGadget { rlp: RLPItemGadget, num_bytes: Cell, len: Cell, - mult_diff: Cell, + mult_inv: Cell, + mult_diff_be: Cell, + mult_diff_le: Cell, rlc_content: Cell, rlc_rlp: Cell, tag: Cell, } impl MainRLPGadget { - pub(crate) fn construct(cb: &mut MPTConstraintBuilder, r: &Expression) -> Self { - let mut config = MainRLPGadget::default(); - config.bytes = cb.base.query_cells::<34>().to_vec(); - circuit!([meta, cb.base], { + pub(crate) fn construct(cb: &mut MPTConstraintBuilder) -> Self { + circuit!([meta, cb], { + let mut config = MainRLPGadget { + bytes: cb.query_cells::<34>().to_vec(), + rlp: RLPItemGadget::default(), + num_bytes: cb.query_cell(), + len: cb.query_cell(), + mult_inv: cb.query_cell(), + mult_diff_be: cb.query_cell_with_type(MptCellType::StoragePhase2), + mult_diff_le: cb.query_cell(), // TODO: remove + rlc_content: cb.query_cell(), + rlc_rlp: cb.query_cell_with_type(MptCellType::StoragePhase2), + tag: cb.query_cell(), + }; config.rlp = RLPItemGadget::construct( - &mut cb.base, + cb, &config .bytes .iter() @@ -1050,40 +1252,40 @@ impl MainRLPGadget { .collect::>(), ); - config.num_bytes = cb.base.query_cell(); require!(config.num_bytes => config.rlp.num_bytes()); - config.len = cb.base.query_cell(); require!(config.len => config.rlp.len()); - config.rlc_content = cb.base.query_cell(); - require!(config.rlc_content => config.rlp.rlc_content(r)); - config.rlc_rlp = cb.base.query_cell(); - require!(config.rlc_rlp => config.rlp.rlc_rlp(&mut cb.base, r)); - config.mult_diff = cb.base.query_cell(); - let mult_diff = config.mult_diff.expr(); - require!((FixedTableTag::RMult, config.rlp.num_bytes(), mult_diff) => @format!("fixed")); + require!(config.rlc_content => config.rlp.rlc_content(&cb.le_r)); + require!(config.rlc_rlp => config.rlp.rlc_rlp2(cb) * config.mult_inv.expr()); - // "free" input that needs to be constrained externally! - config.tag = cb.base.query_cell(); + require!(config.mult_inv.expr() * pow::expr(cb.be_r.expr(), HASH_WIDTH + 2) => config.mult_diff_be.expr()); + require!((FixedTableTag::LERMult, config.rlp.num_bytes(), config.mult_diff_le.expr()) => @FIXED); + require!((PhaseTwoTableTag::BERMult, config.rlp.num_bytes(), config.mult_diff_be.expr()) => @PHASE_TWO); // Range/zero checks // These range checks ensure that the value in the RLP columns are all byte // value. These lookups also enforce the byte value to be zero when // the byte index >= num_bytes. // TODO(Brecht): do 2 bytes/lookup when circuit height >= 2**21 + // We enable dynamic lookups because otherwise these lookup would require a lot of extra + // cells. for (idx, byte) in config.bytes.iter().enumerate() { - require!((config.tag.expr(), byte.expr(), config.num_bytes.expr() - idx.expr()) => @"fixed"); + require!( + format!("byte {:?}", byte.identifier()), + vec![config.tag.expr(), byte.expr(), config.num_bytes.expr() - idx.expr()] + // is_fixed, is_combine, is_split + => @FIXED, true, true, false + ); } config }) } - pub(crate) fn assign( + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, bytes: &[u8], - r: F, is_nibbles: bool, ) -> Result { // Assign the bytes @@ -1094,19 +1296,32 @@ impl MainRLPGadget { // Decode the RLP item let rlp_witness = self.rlp.assign(region, offset, bytes)?; + // Compute the denominator needed for BE + let mult_inv = pow::value(region.be_r, HASH_WIDTH + 2 - rlp_witness.num_bytes()) + .invert() + .unwrap_or(F::ZERO); + // Store RLP properties for easy access self.num_bytes .assign(region, offset, rlp_witness.num_bytes().scalar())?; self.len .assign(region, offset, rlp_witness.len().scalar())?; - - self.mult_diff - .assign(region, offset, pow::value(r, rlp_witness.num_bytes()))?; - self.rlc_content - .assign(region, offset, rlp_witness.rlc_content(r))?; + .assign(region, offset, rlp_witness.rlc_content(region.le_r))?; self.rlc_rlp - .assign(region, offset, rlp_witness.rlc_rlp(r))?; + .assign(region, offset, rlp_witness.rlc_rlp2(region.be_r) * mult_inv)?; + + self.mult_inv.assign(region, offset, mult_inv)?; + self.mult_diff_be.assign( + region, + offset, + pow::value(region.be_r, rlp_witness.num_bytes()), + )?; + self.mult_diff_le.assign( + region, + offset, + pow::value(region.le_r, rlp_witness.num_bytes()), + )?; assign!(region, self.tag, offset => self.tag(is_nibbles).scalar())?; @@ -1116,17 +1331,17 @@ impl MainRLPGadget { pub(crate) fn create_view( &self, meta: &mut VirtualCells, - cb: &mut ConstraintBuilder, + cb: &mut MPTConstraintBuilder, rot: usize, is_nibbles: bool, ) -> RLPItemView { - circuit!([meta, cb], { + circuit!([meta, cb.base], { require!(self.tag.rot(meta, rot) => self.tag(is_nibbles).expr()); }); RLPItemView { num_bytes: Some(self.num_bytes.rot(meta, rot)), len: Some(self.len.rot(meta, rot)), - mult_diff: Some(self.mult_diff.rot(meta, rot)), + mult_diff_be: Some(self.mult_diff_be.rot(meta, rot)), rlc_content: Some(self.rlc_content.rot(meta, rot)), rlc_rlp: Some(self.rlc_rlp.rot(meta, rot)), bytes: self.bytes.iter().map(|byte| byte.rot(meta, rot)).collect(), @@ -1150,7 +1365,7 @@ pub struct RLPItemView { pub(crate) bytes: Vec>, pub(crate) num_bytes: Option>, pub(crate) len: Option>, - pub(crate) mult_diff: Option>, + pub(crate) mult_diff_be: Option>, pub(crate) rlc_content: Option>, pub(crate) rlc_rlp: Option>, pub(crate) is_short: Option>, @@ -1167,7 +1382,7 @@ impl RLPItemView { } pub(crate) fn mult(&self) -> Expression { - self.mult_diff.clone().unwrap() + self.mult_diff_be.clone().unwrap() } pub(crate) fn rlc_content(&self) -> Expression { @@ -1182,6 +1397,14 @@ impl RLPItemView { (self.rlc_content(), self.rlc_rlp()) } + pub(crate) fn rlc2(&self) -> (Expression, (Expression, Expression)) { + (self.rlc_content(), self.rlc_chain_data()) + } + + pub(crate) fn rlc_chain_data(&self) -> (Expression, Expression) { + (self.rlc_rlp(), self.mult()) + } + pub(crate) fn bytes(&self) -> Vec> { self.bytes.clone() } @@ -1193,4 +1416,8 @@ impl RLPItemView { pub(crate) fn is_long(&self) -> Expression { self.is_long.clone().unwrap() } + + pub(crate) fn is_very_long(&self) -> Expression { + not::expr(self.is_short() + self.is_long()) + } } diff --git a/zkevm-circuits/src/mpt_circuit/rlp_gadgets.rs b/zkevm-circuits/src/mpt_circuit/rlp_gadgets.rs index a289de9968..5e2fa35b48 100644 --- a/zkevm-circuits/src/mpt_circuit/rlp_gadgets.rs +++ b/zkevm-circuits/src/mpt_circuit/rlp_gadgets.rs @@ -1,21 +1,61 @@ use crate::{ _cb, circuit, circuit_tools::{ + cached_region::{CachedRegion, ChallengeSet}, cell_manager::Cell, constraint_builder::{ConstraintBuilder, RLCable, RLCableValue}, }, matchw, - mpt_circuit::param::{RLP_LIST_LONG, RLP_LIST_SHORT, RLP_SHORT}, + mpt_circuit::{ + helpers::FIXED, + param::{RLP_LIST_LONG, RLP_LIST_SHORT, RLP_SHORT}, + FixedTableTag, + }, util::Expr, }; use eth_types::Field; use gadgets::util::{not, pow, Scalar}; -use halo2_proofs::{ - circuit::Region, - plonk::{Error, Expression}, +use halo2_proofs::plonk::{Error, Expression}; +use itertools::Itertools; + +use super::{ + helpers::MPTConstraintBuilder, + param::{KEY_PREFIX_ODD, KEY_TERMINAL_PREFIX_ODD, RLP_LONG}, }; -use super::param::{KEY_PREFIX_ODD, KEY_TERMINAL_PREFIX_ODD, RLP_LONG}; +// Decodes the first byte of an RLP data stream to return (is_list, is_short, is_long, is_very_long) +pub(crate) fn decode_rlp(byte: u8) -> (bool, bool, bool, bool) { + if byte < RLP_LIST_SHORT { + const RLP_SHORT_INCLUSIVE: u8 = RLP_SHORT - 1; + const RLP_LONG_EXCLUSIVE: u8 = RLP_LONG + 1; + const RLP_VALUE_MAX: u8 = RLP_LIST_SHORT - 1; + + let mut is_short = false; + let mut is_long = false; + let mut is_very_long = false; + match byte { + 0..=RLP_SHORT_INCLUSIVE => is_short = true, + RLP_SHORT..=RLP_LONG => is_long = true, + RLP_LONG_EXCLUSIVE..=RLP_VALUE_MAX => is_very_long = true, + _ => unreachable!(), + } + (false, is_short, is_long, is_very_long) + } else { + const RLP_LIST_LONG_1: u8 = RLP_LIST_LONG + 1; + const RLP_LIST_LONG_2: u8 = RLP_LIST_LONG + 2; + + let mut is_short = false; + let mut is_long = false; + let mut is_very_long = false; + match byte { + RLP_LIST_SHORT..=RLP_LIST_LONG => is_short = true, + RLP_LIST_LONG_1 => is_long = true, + RLP_LIST_LONG_2 => is_very_long = true, + _ => (), + } + (true, is_short, is_long, is_very_long) + } +} #[derive(Clone, Debug, Default)] pub(crate) struct RLPListGadget { @@ -36,42 +76,47 @@ pub(crate) struct RLPListWitness { } impl RLPListGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder, bytes: &[Expression]) -> Self { - // TODO(Brecht): add lookup - RLPListGadget { - is_short: cb.query_cell(), - is_long: cb.query_cell(), - is_very_long: cb.query_cell(), - is_string: cb.query_cell(), - bytes: bytes.to_vec(), - } + pub(crate) fn construct(cb: &mut MPTConstraintBuilder, bytes: &[Expression]) -> Self { + circuit!([meta, cb], { + let is_short = cb.query_cell(); + let is_long = cb.query_cell(); + let is_very_long = cb.query_cell(); + let is_string = cb.query_cell(); + + require!(vec![ + FixedTableTag::RLP.expr(), + bytes[0].expr(), + not!(is_string), + is_short.expr(), + is_long.expr(), + is_very_long.expr(), + ] => @FIXED + ); + + RLPListGadget { + is_short, + is_long, + is_very_long, + is_string, + bytes: bytes.to_vec(), + } + }) } - pub(crate) fn assign( + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, bytes: &[u8], ) -> Result { - const RLP_LIST_LONG_1: u8 = RLP_LIST_LONG + 1; - const RLP_LIST_LONG_2: u8 = RLP_LIST_LONG + 2; - - let mut is_short = false; - let mut is_long = false; - let mut is_very_long = false; - let mut is_string = false; - match bytes[0] { - RLP_LIST_SHORT..=RLP_LIST_LONG => is_short = true, - RLP_LIST_LONG_1 => is_long = true, - RLP_LIST_LONG_2 => is_very_long = true, - _ => is_string = true, - } + let (is_list, is_short, is_long, is_very_long) = decode_rlp(bytes[0]); + let is_string = !is_list; - self.is_short.assign(region, offset, F::from(is_short))?; - self.is_long.assign(region, offset, F::from(is_long))?; + self.is_short.assign(region, offset, is_short.scalar())?; + self.is_long.assign(region, offset, is_long.scalar())?; self.is_very_long - .assign(region, offset, F::from(is_very_long))?; - self.is_string.assign(region, offset, F::from(is_string))?; + .assign(region, offset, is_very_long.scalar())?; + self.is_string.assign(region, offset, is_string.scalar())?; Ok(RLPListWitness { is_short, @@ -134,6 +179,11 @@ impl RLPListGadget { self.bytes.rlc(r) } + /// Returns the rlc of all the list data provided + pub(crate) fn rlc_rlp2(&self, r: &Expression) -> Expression { + self.bytes.rlc_rev(r) + } + /// Returns the rlc of only the RLP bytes pub(crate) fn rlc_rlp_only(&self, r: &Expression) -> (Expression, Expression) { circuit!([meta, _cb!()], { @@ -145,6 +195,16 @@ impl RLPListGadget { }) } + pub(crate) fn rlc_rlp_only2(&self, r: &Expression) -> (Expression, Expression) { + circuit!([meta, _cb!()], { + matchx! { + self.is_short() => (self.bytes[..1].rlc_rev(r), pow::expr(r.expr(), 1)), + self.is_long() => (self.bytes[..2].rlc_rev(r), pow::expr(r.expr(), 2)), + self.is_very_long() => (self.bytes[..3].rlc_rev(r), pow::expr(r.expr(), 3)), + } + }) + } + pub(crate) fn rlp_mult(&self, r: &Expression) -> Expression { self.rlc_rlp_only(r).1 } @@ -202,6 +262,12 @@ impl RLPListWitness { self.bytes.rlc_value(r) } + /// Returns the rlc of the complete list value and the complete list + /// (including RLP bytes) + pub(crate) fn rlc_rlp2(&self, r: F) -> F { + self.bytes.iter().cloned().rev().collect_vec().rlc_value(r) + } + /// Returns the rlc of the RLP bytes pub(crate) fn rlc_rlp_only(&self, r: F) -> (F, F) { matchw! { @@ -219,7 +285,7 @@ pub(crate) struct RLPListDataGadget { } impl RLPListDataGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder) -> Self { + pub(crate) fn construct(cb: &mut MPTConstraintBuilder) -> Self { let rlp_list_bytes = cb.query_bytes(); let rlp_list_bytes_expr = rlp_list_bytes.iter().map(|c| c.expr()).collect::>(); RLPListDataGadget { @@ -228,9 +294,9 @@ impl RLPListDataGadget { } } - pub(crate) fn assign( + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, list_bytes: &[u8], ) -> Result { @@ -264,43 +330,46 @@ pub(crate) struct RLPValueWitness { } impl RLPValueGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder, bytes: &[Expression]) -> Self { - // TODO(Brecht): add lookup - RLPValueGadget { - is_short: cb.query_cell(), - is_long: cb.query_cell(), - is_very_long: cb.query_cell(), - is_list: cb.query_cell(), - bytes: bytes.to_vec(), - } + pub(crate) fn construct(cb: &mut MPTConstraintBuilder, bytes: &[Expression]) -> Self { + circuit!([meta, cb], { + let is_short = cb.query_cell(); + let is_long = cb.query_cell(); + let is_very_long = cb.query_cell(); + let is_list = cb.query_cell(); + + require!(vec![ + FixedTableTag::RLP.expr(), + bytes[0].expr(), + is_list.expr(), + is_short.expr(), + is_long.expr(), + is_very_long.expr(), + ] => @FIXED + ); + + RLPValueGadget { + is_short, + is_long, + is_very_long, + is_list, + bytes: bytes.to_vec(), + } + }) } - pub(crate) fn assign( + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, bytes: &[u8], ) -> Result { - const RLP_SHORT_INCLUSIVE: u8 = RLP_SHORT - 1; - const RLP_LONG_EXCLUSIVE: u8 = RLP_LONG + 1; - const RLP_VALUE_MAX: u8 = RLP_LIST_SHORT - 1; - - let mut is_short = false; - let mut is_long = false; - let mut is_very_long = false; - let mut is_list = false; - match bytes[0] { - 0..=RLP_SHORT_INCLUSIVE => is_short = true, - RLP_SHORT..=RLP_LONG => is_long = true, - RLP_LONG_EXCLUSIVE..=RLP_VALUE_MAX => is_very_long = true, - _ => is_list = true, - } + let (is_list, is_short, is_long, is_very_long) = decode_rlp(bytes[0]); - self.is_short.assign(region, offset, F::from(is_short))?; - self.is_long.assign(region, offset, F::from(is_long))?; + self.is_short.assign(region, offset, is_short.scalar())?; + self.is_long.assign(region, offset, is_long.scalar())?; self.is_very_long - .assign(region, offset, F::from(is_very_long))?; - self.is_list.assign(region, offset, F::from(is_list))?; + .assign(region, offset, is_very_long.scalar())?; + self.is_list.assign(region, offset, is_list.scalar())?; Ok(RLPValueWitness { is_short, @@ -372,14 +441,44 @@ impl RLPValueGadget { } /// RLC data - pub(crate) fn rlc(&self, r: &Expression) -> (Expression, Expression) { - (self.rlc_value(r), self.rlc_rlp(r)) + pub(crate) fn rlc( + &self, + r: &Expression, + keccak_r: &Expression, + ) -> (Expression, Expression) { + (self.rlc_value(r), self.rlc_rlp(keccak_r)) } pub(crate) fn rlc_rlp(&self, r: &Expression) -> Expression { self.bytes.rlc(r) } + /// RLC data + pub(crate) fn rlc2( + &self, + r: &Expression, + keccak_r: &Expression, + ) -> (Expression, Expression) { + (self.rlc_value(r), self.rlc_rlp_only2(keccak_r).0) + } + + pub(crate) fn rlc_rlp2(&self, r: &Expression) -> Expression { + self.bytes.rlc_rev(r) + } + + pub(crate) fn rlc_rlp_only2(&self, r: &Expression) -> (Expression, Expression) { + circuit!([meta, _cb!()], { + matchx! { + self.is_short() => (self.bytes[..1].rlc_rev(r), pow::expr(r.expr(), 1)), + self.is_long() => (self.bytes[..1].rlc_rev(r), pow::expr(r.expr(), 1)), + self.is_very_long() => { + unreachablex!(); + (0.expr(), 0.expr()) + }, + } + }) + } + pub(crate) fn rlc_value(&self, r: &Expression) -> Expression { circuit!([meta, _cb!()], { matchx! { @@ -455,6 +554,10 @@ impl RLPValueWitness { self.bytes.rlc_value(r) } + pub(crate) fn rlc_rlp2(&self, r: F) -> F { + self.bytes.iter().cloned().rev().collect_vec().rlc_value(r) + } + pub(crate) fn rlc_value(&self, r: F) -> F { matchw! { self.is_short() => { @@ -558,16 +661,16 @@ pub(crate) struct RLPItemWitness { } impl RLPItemGadget { - pub(crate) fn construct(cb: &mut ConstraintBuilder, bytes: &[Expression]) -> Self { + pub(crate) fn construct(cb: &mut MPTConstraintBuilder, bytes: &[Expression]) -> Self { RLPItemGadget { value: RLPValueGadget::construct(cb, bytes), list: RLPListGadget::construct(cb, bytes), } } - pub(crate) fn assign( + pub(crate) fn assign>( &self, - region: &mut Region<'_, F>, + region: &mut CachedRegion<'_, '_, F, S>, offset: usize, bytes: &[u8], ) -> Result { @@ -643,15 +746,20 @@ impl RLPItemGadget { }) } - pub(crate) fn rlc_rlp( - &self, - cb: &mut ConstraintBuilder, - r: &Expression, - ) -> Expression { + pub(crate) fn rlc_rlp(&self, cb: &mut MPTConstraintBuilder) -> Expression { circuit!([meta, cb], { matchx! { - self.value.is_string() => self.value.rlc_rlp(r), - self.list.is_list() => self.list.rlc_rlp(r), + self.value.is_string() => self.value.rlc_rlp(&cb.be_r), + self.list.is_list() => self.list.rlc_rlp(&cb.be_r), + } + }) + } + + pub(crate) fn rlc_rlp2(&self, cb: &mut MPTConstraintBuilder) -> Expression { + circuit!([meta, cb], { + matchx! { + self.value.is_string() => self.value.rlc_rlp2(&cb.be_r), + self.list.is_list() => self.list.rlc_rlp2(&cb.be_r), } }) } @@ -720,4 +828,11 @@ impl RLPItemWitness { self.list.is_list() => self.list.rlc_rlp(r), } } + + pub(crate) fn rlc_rlp2(&self, r: F) -> F { + matchw! { + self.value.is_string() => self.value.rlc_rlp2(r), + self.list.is_list() => self.list.rlc_rlp2(r), + } + } } diff --git a/zkevm-circuits/src/mpt_circuit/start.rs b/zkevm-circuits/src/mpt_circuit/start.rs index 86109fdbdf..b77d276990 100644 --- a/zkevm-circuits/src/mpt_circuit/start.rs +++ b/zkevm-circuits/src/mpt_circuit/start.rs @@ -5,7 +5,10 @@ use super::{ }; use crate::{ circuit, - circuit_tools::cell_manager::Cell, + circuit_tools::{ + cached_region::{CachedRegion, ChallengeSet}, + cell_manager::Cell, + }, mpt_circuit::{ helpers::{ key_memory, main_memory, parent_memory, KeyData, MPTConstraintBuilder, MainData, @@ -16,10 +19,7 @@ use crate::{ }; use eth_types::Field; use gadgets::util::Scalar; -use halo2_proofs::{ - circuit::Region, - plonk::{Error, VirtualCells}, -}; +use halo2_proofs::plonk::{Error, VirtualCells}; #[derive(Clone, Debug, Default)] pub(crate) struct StartConfig { @@ -39,13 +39,13 @@ impl StartConfig { .reset(StartRowType::Count as usize); let mut config = StartConfig::default(); - circuit!([meta, cb.base], { + circuit!([meta, cb], { let root_items = [ - ctx.rlp_item(meta, &mut cb.base, StartRowType::RootS as usize), - ctx.rlp_item(meta, &mut cb.base, StartRowType::RootC as usize), + ctx.rlp_item(meta, cb, StartRowType::RootS as usize), + ctx.rlp_item(meta, cb, StartRowType::RootC as usize), ]; - config.proof_type = cb.base.query_cell(); + config.proof_type = cb.query_cell(); let mut root = vec![0.expr(); 2]; for is_s in [true, false] { @@ -53,7 +53,7 @@ impl StartConfig { } MainData::store( - &mut cb.base, + cb, &ctx.memory[main_memory()], [ config.proof_type.expr(), @@ -66,24 +66,25 @@ impl StartConfig { for is_s in [true, false] { ParentData::store( - &mut cb.base, + cb, &ctx.memory[parent_memory(is_s)], root[is_s.idx()].expr(), true.expr(), false.expr(), root[is_s.idx()].expr(), ); - KeyData::store_defaults(&mut cb.base, &ctx.memory[key_memory(is_s)]); + KeyData::store_defaults(cb, &ctx.memory[key_memory(is_s)]); } }); config } - pub fn assign( + #[allow(clippy::too_many_arguments)] + pub fn assign>( &self, - region: &mut Region<'_, F>, - ctx: &MPTConfig, + region: &mut CachedRegion<'_, '_, F, S>, + _mpt_config: &MPTConfig, pv: &mut MPTState, offset: usize, node: &Node, @@ -101,8 +102,7 @@ impl StartConfig { let mut root = vec![0.scalar(); 2]; for is_s in [true, false] { - root[is_s.idx()] = rlp_values[is_s.idx()].rlc_content(pv.r); - // println!("root {}: {:?}", is_s, root[is_s.idx()]); + root[is_s.idx()] = rlp_values[is_s.idx()].rlc_content(region.le_r); } MainData::witness_store( @@ -130,11 +130,11 @@ impl StartConfig { region, offset, &mut pv.memory[key_memory(is_s)], - F::zero(), - F::one(), + F::ZERO, + F::ONE, 0, - F::zero(), - F::one(), + F::ZERO, + F::ONE, 0, )?; } diff --git a/zkevm-circuits/src/mpt_circuit/storage_leaf.rs b/zkevm-circuits/src/mpt_circuit/storage_leaf.rs index 6b82441244..84f447caa1 100644 --- a/zkevm-circuits/src/mpt_circuit/storage_leaf.rs +++ b/zkevm-circuits/src/mpt_circuit/storage_leaf.rs @@ -1,7 +1,7 @@ use eth_types::Field; use gadgets::util::Scalar; use halo2_proofs::{ - circuit::{Region, Value}, + circuit::Value, plonk::{Error, VirtualCells}, poly::Rotation, }; @@ -9,14 +9,15 @@ use halo2_proofs::{ use crate::{ circuit, circuit_tools::{ + cached_region::{CachedRegion, ChallengeSet}, cell_manager::Cell, - constraint_builder::RLCChainable, + constraint_builder::RLCChainable2, gadgets::{IsEqualGadget, LtGadget}, }, mpt_circuit::{ helpers::{ key_memory, main_memory, num_nibbles, parent_memory, DriftedGadget, IsEmptyTreeGadget, - KeyData, MPTConstraintBuilder, MainData, ParentData, ParentDataWitness, + KeyData, MPTConstraintBuilder, MainData, ParentData, ParentDataWitness, KECCAK, }, param::KEY_LEN_IN_NIBBLES, MPTConfig, MPTContext, MPTState, @@ -54,8 +55,6 @@ impl StorageLeafConfig { cb: &mut MPTConstraintBuilder, ctx: MPTContext, ) -> Self { - let r = ctx.r.clone(); - cb.base .cell_manager .as_mut() @@ -63,25 +62,21 @@ impl StorageLeafConfig { .reset(StorageRowType::Count as usize); let mut config = StorageLeafConfig::default(); - circuit!([meta, cb.base], { + circuit!([meta, cb], { let key_items = [ - ctx.rlp_item(meta, &mut cb.base, StorageRowType::KeyS as usize), - ctx.rlp_item(meta, &mut cb.base, StorageRowType::KeyC as usize), + ctx.rlp_item(meta, cb, StorageRowType::KeyS as usize), + ctx.rlp_item(meta, cb, StorageRowType::KeyC as usize), ]; config.value_rlp_bytes = [cb.base.query_bytes(), cb.base.query_bytes()]; let value_item = [ - ctx.rlp_item(meta, &mut cb.base, StorageRowType::ValueS as usize), - ctx.rlp_item(meta, &mut cb.base, StorageRowType::ValueC as usize), + ctx.rlp_item(meta, cb, StorageRowType::ValueS as usize), + ctx.rlp_item(meta, cb, StorageRowType::ValueC as usize), ]; - let drifted_item = ctx.rlp_item(meta, &mut cb.base, StorageRowType::Drifted as usize); - let wrong_item = ctx.rlp_item(meta, &mut cb.base, StorageRowType::Wrong as usize); + let drifted_item = ctx.rlp_item(meta, cb, StorageRowType::Drifted as usize); + let wrong_item = ctx.rlp_item(meta, cb, StorageRowType::Wrong as usize); - config.main_data = MainData::load( - "main storage", - &mut cb.base, - &ctx.memory[main_memory()], - 0.expr(), - ); + config.main_data = + MainData::load("main storage", cb, &ctx.memory[main_memory()], 0.expr()); // Storage leaves always need to be below accounts require!(config.main_data.is_below_account => true); @@ -89,64 +84,64 @@ impl StorageLeafConfig { let mut key_rlc = vec![0.expr(); 2]; let mut value_rlc = vec![0.expr(); 2]; let mut value_rlp_rlc = vec![0.expr(); 2]; + let mut value_rlp_rlc_mult = vec![0.expr(); 2]; for is_s in [true, false] { // Parent data let parent_data = &mut config.parent_data[is_s.idx()]; - *parent_data = ParentData::load( - "leaf load", - &mut cb.base, - &ctx.memory[parent_memory(is_s)], - 0.expr(), - ); + *parent_data = + ParentData::load("leaf load", cb, &ctx.memory[parent_memory(is_s)], 0.expr()); // Key data let key_data = &mut config.key_data[is_s.idx()]; - *key_data = KeyData::load(&mut cb.base, &ctx.memory[key_memory(is_s)], 0.expr()); + *key_data = KeyData::load(cb, &ctx.memory[key_memory(is_s)], 0.expr()); // Placeholder leaf checks config.is_in_empty_trie[is_s.idx()] = - IsEmptyTreeGadget::construct(&mut cb.base, parent_data.rlc.expr(), &r); + IsEmptyTreeGadget::construct(cb, parent_data.rlc.expr(), &cb.le_r.expr()); let is_placeholder_leaf = config.is_in_empty_trie[is_s.idx()].expr(); let rlp_key = &mut config.rlp_key[is_s.idx()]; - *rlp_key = ListKeyGadget::construct(&mut cb.base, &key_items[is_s.idx()]); + *rlp_key = ListKeyGadget::construct(cb, &key_items[is_s.idx()]); config.rlp_value[is_s.idx()] = RLPValueGadget::construct( - &mut cb.base, + cb, &config.value_rlp_bytes[is_s.idx()] .iter() .map(|c| c.expr()) .collect::>(), ); - // Multiplier after list and key - let mult = rlp_key.rlp_list.rlp_mult(&r) * key_items[is_s.idx()].mult(); - // Because the storage value is an rlp encoded string inside another rlp encoded // string (leaves are always encoded as [key, value], with // `value` here containing a single stored value) the stored // value is either stored directly in the RLP encoded string if short, or stored // wrapped inside another RLP encoded string if long. - (value_rlc[is_s.idx()], value_rlp_rlc[is_s.idx()]) = ifx! {config.rlp_value[is_s.idx()].is_short() => { - config.rlp_value[is_s.idx()].rlc(&r) + let rlp_value = config.rlp_value[is_s.idx()].rlc_value(&cb.le_r); + let rlp_value_rlc_mult = config.rlp_value[is_s.idx()].rlc_rlp_only2(&cb.be_r); + ( + value_rlc[is_s.idx()], + value_rlp_rlc[is_s.idx()], + value_rlp_rlc_mult[is_s.idx()], + ) = ifx! {config.rlp_value[is_s.idx()].is_short() => { + (rlp_value, rlp_value_rlc_mult.0.expr(), rlp_value_rlc_mult.1.expr()) } elsex { let value_rlc = value_item[is_s.idx()].rlc_content(); - let value_rlp_rlc = (config.rlp_value[is_s.idx()].rlc_rlp(&r), r.expr()).rlc_chain( - value_item[is_s.idx()].rlc_rlp() - ); + let value_rlp_rlc = rlp_value_rlc_mult.0.rlc_chain2(value_item[is_s.idx()].rlc_chain_data()); require!(config.rlp_value[is_s.idx()].num_bytes() => value_item[is_s.idx()].num_bytes() + 1.expr()); - (value_rlc, value_rlp_rlc) + (value_rlc, value_rlp_rlc, rlp_value_rlc_mult.1 * value_item[is_s.idx()].mult()) }}; - let leaf_rlc = - (rlp_key.rlc(&r), mult.expr()).rlc_chain(value_rlp_rlc[is_s.idx()].expr()); + let leaf_rlc = rlp_key.rlc2(&cb.be_r).rlc_chain2(( + value_rlp_rlc[is_s.idx()].expr(), + value_rlp_rlc_mult[is_s.idx()].expr(), + )); // Key key_rlc[is_s.idx()] = key_data.rlc.expr() + rlp_key.key.expr( - &mut cb.base, + cb, rlp_key.key_value.clone(), key_data.mult.expr(), key_data.is_odd.expr(), - &r, + &cb.le_r.expr(), ); // Total number of nibbles needs to be KEY_LEN_IN_NIBBLES let num_nibbles = @@ -168,18 +163,19 @@ impl StorageLeafConfig { config.is_not_hashed[is_s.idx()] = LtGadget::construct(&mut cb.base, rlp_key.rlp_list.num_bytes(), 32.expr()); ifx!{or::expr(&[parent_data.is_root.expr(), not!(config.is_not_hashed[is_s.idx()])]) => { // Hashed branch hash in parent branch - require!((1, leaf_rlc, rlp_key.rlp_list.num_bytes(), parent_data.rlc) => @"keccak"); + require!((1, leaf_rlc, rlp_key.rlp_list.num_bytes(), parent_data.rlc) => @KECCAK); } elsex { // Non-hashed branch hash in parent branch - require!(leaf_rlc => parent_data.rlc); + // TODO(Brecht): restore + //require!(leaf_rlc => parent_data.rlc); }} }} // Key done, set the default values - KeyData::store_defaults(&mut cb.base, &ctx.memory[key_memory(is_s)]); + KeyData::store_defaults(cb, &ctx.memory[key_memory(is_s)]); // Store the new parent ParentData::store( - &mut cb.base, + cb, &ctx.memory[parent_memory(is_s)], 0.expr(), true.expr(), @@ -207,8 +203,9 @@ impl StorageLeafConfig { &config.key_data, &key_rlc, &value_rlp_rlc, + &value_rlp_rlc_mult, &drifted_item, - &ctx.r, + &cb.le_r.expr(), ); // Wrong leaf handling @@ -221,7 +218,7 @@ impl StorageLeafConfig { &wrong_item, config.is_in_empty_trie[true.idx()].expr(), config.key_data[true.idx()].clone(), - &ctx.r, + &cb.le_r.expr(), ); // For non-existing proofs the tree needs to remain the same @@ -257,10 +254,11 @@ impl StorageLeafConfig { config } - pub fn assign( + #[allow(clippy::too_many_arguments)] + pub fn assign>( &self, - region: &mut Region<'_, F>, - ctx: &MPTConfig, + region: &mut CachedRegion<'_, '_, F, S>, + mpt_config: &MPTConfig, pv: &mut MPTState, offset: usize, node: &Node, @@ -291,7 +289,7 @@ impl StorageLeafConfig { parent_data[is_s.idx()] = self.parent_data[is_s.idx()].witness_load( region, offset, - &mut pv.memory[parent_memory(is_s)], + &pv.memory[parent_memory(is_s)], 0, )?; @@ -312,18 +310,18 @@ impl StorageLeafConfig { key_data[is_s.idx()] = self.key_data[is_s.idx()].witness_load( region, offset, - &mut pv.memory[key_memory(is_s)], + &pv.memory[key_memory(is_s)], 0, )?; KeyData::witness_store( region, offset, &mut pv.memory[key_memory(is_s)], - F::zero(), - F::one(), + F::ZERO, + F::ONE, 0, - F::zero(), - F::one(), + F::ZERO, + F::ONE, 0, )?; @@ -332,7 +330,7 @@ impl StorageLeafConfig { rlp_key_witness.key_item.clone(), key_data[is_s.idx()].rlc, key_data[is_s.idx()].mult, - pv.r, + region.le_r, ); // Value @@ -348,26 +346,26 @@ impl StorageLeafConfig { &storage.value_rlp_bytes[is_s.idx()], )?; value_rlc[is_s.idx()] = if value_witness.is_short() { - value_witness.rlc_value(pv.r) + value_witness.rlc_value(region.le_r) } else { - value_item[is_s.idx()].rlc_content(pv.r) + value_item[is_s.idx()].rlc_content(region.le_r) }; ParentData::witness_store( region, offset, &mut pv.memory[parent_memory(is_s)], - F::zero(), + F::ZERO, true, false, - F::zero(), + F::ZERO, )?; self.is_in_empty_trie[is_s.idx()].assign( region, offset, parent_data[is_s.idx()].rlc, - pv.r, + region.le_r, )?; } @@ -391,7 +389,7 @@ impl StorageLeafConfig { &parent_data, &storage.drifted_rlp_bytes, &drifted_item, - pv.r, + region.le_r, )?; // Wrong leaf handling @@ -404,7 +402,7 @@ impl StorageLeafConfig { &wrong_item, false, key_data[true.idx()].clone(), - pv.r, + region.le_r, )?; // Put the data in the lookup table @@ -415,7 +413,7 @@ impl StorageLeafConfig { } else { MPTProofType::Disabled }; - ctx.mpt_table.assign( + mpt_config.mpt_table.assign_cached( region, offset, &MptUpdateRow { diff --git a/zkevm-circuits/src/mpt_circuit/witness_row.rs b/zkevm-circuits/src/mpt_circuit/witness_row.rs index cfcbc39767..59ec44bb3f 100644 --- a/zkevm-circuits/src/mpt_circuit/witness_row.rs +++ b/zkevm-circuits/src/mpt_circuit/witness_row.rs @@ -243,6 +243,7 @@ impl MptWitnessRow { } // TODO(Brecht): Do all of this on the MPT proof generation side +#[allow(clippy::collapsible_else_if)] pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec { let mut key_rlp_bytes = Vec::new(); for (_, row) in witness @@ -274,7 +275,6 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec } if row.get_type() == MptWitnessRowType::BranchChild { - // println!("- {:?}", row.bytes); let mut child_s_bytes = row.bytes[0..34].to_owned(); if child_s_bytes[1] == 160 { child_s_bytes[0] = 0; @@ -301,13 +301,11 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec row.bytes[68..].to_owned(), ] .concat(); - // println!("+ {:?}", row.bytes); } if row.get_type() == MptWitnessRowType::ExtensionNodeS || row.get_type() == MptWitnessRowType::ExtensionNodeC { - // println!("- {:?}", row.bytes); let mut value_bytes = row.bytes[34..68].to_owned(); if value_bytes[1] == 160 { value_bytes[0] = 0; @@ -323,7 +321,6 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec row.bytes[68..].to_owned(), ] .concat(); - // println!("+ {:?}", row.bytes); } // Separate the list rlp bytes from the key bytes @@ -347,9 +344,7 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec // Currently the list rlp bytes are dropped for non-key row, restore them here if key_bytes[0] < RLP_LIST_SHORT && row.get_type() != MptWitnessRowType::ExtensionNodeS { - for idx in 0..key_rlp_bytes.len() { - key_bytes[idx] = key_rlp_bytes[idx]; - } + key_bytes[..key_rlp_bytes.len()].copy_from_slice(&key_rlp_bytes[..]); } const RLP_LIST_LONG_1: u8 = RLP_LIST_LONG + 1; @@ -357,12 +352,12 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec let mut is_short = false; let mut is_long = false; let mut is_very_long = false; - let mut is_string = false; + let mut _is_string = false; match key_bytes[0] { RLP_LIST_SHORT..=RLP_LIST_LONG => is_short = true, RLP_LIST_LONG_1 => is_long = true, RLP_LIST_LONG_2 => is_very_long = true, - _ => is_string = true, + _ => _is_string = true, } let num_rlp_bytes = if is_short { @@ -374,14 +369,10 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec } else { if row.get_type() == MptWitnessRowType::ExtensionNodeS { 0 - } else if is_string { - unreachable!() } else { unreachable!() } }; - - // println!("bytes: {:?}", key_bytes); row.rlp_bytes = key_bytes[..num_rlp_bytes].to_vec(); for byte in key_bytes[..num_rlp_bytes].iter_mut() { *byte = 0; @@ -394,9 +385,6 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec { key_rlp_bytes = row.rlp_bytes.clone(); } - - // println!("list : {:?}", row.rlp_bytes); - // println!("key : {:?}", row.bytes); } // Separate the RLP bytes and shift the value bytes to the start of the row @@ -486,7 +474,6 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec .collect::>(); let mut offset = 0; while offset < witness.len() { - // println!("offset: {}", offset); let mut new_proof = offset == 0; if offset > 0 { let row_prev = witness[offset - 1].clone(); @@ -514,17 +501,18 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec node_rows[StartRowType::RootC as usize] = new_row.c(); let start_node = StartNode { - proof_type: new_row.proof_type.clone(), + proof_type: new_row.proof_type, }; - let mut node = Node::default(); - node.start = Some(start_node); - node.values = node_rows; - nodes.push(node); + nodes.push(Node { + start: Some(start_node), + values: node_rows, + ..Default::default() + }); } if witness[offset].get_type() == MptWitnessRowType::InitBranch { let row_init = witness[offset].to_owned(); - let is_placeholder = row_init.is_placeholder.clone(); + let is_placeholder = row_init.is_placeholder; let is_extension = row_init.is_extension; let modified_index = row_init.modified_index; let mut drifted_index = row_init.drifted_index; @@ -543,9 +531,7 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec let ext_list_rlp_bytes = witness[offset + 17].rlp_bytes.to_owned(); let mut node_rows = vec![Vec::new(); ExtensionBranchRowType::Count as usize]; - for idx in 0..ARITY + 1 { - node_rows[idx] = child_bytes[idx].clone(); - } + node_rows[..(ARITY + 1)].clone_from_slice(&child_bytes[..(ARITY + 1)]); node_rows[ExtensionBranchRowType::KeyS as usize] = witness[offset + 17].s(); node_rows[ExtensionBranchRowType::ValueS as usize] = witness[offset + 17].c(); node_rows[ExtensionBranchRowType::KeyC as usize] = witness[offset + 18].s(); @@ -564,12 +550,13 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec list_rlp_bytes: branch_list_rlp_bytes, }, }; - let mut node = Node::default(); - node.extension_branch = Some(extension_branch_node); - node.values = node_rows; - nodes.push(node); + nodes.push(Node { + extension_branch: Some(extension_branch_node), + values: node_rows, + ..Default::default() + }); } else if witness[offset].get_type() == MptWitnessRowType::StorageLeafSKey { - let row_key = [&witness[offset + 0], &witness[offset + 2]]; + let row_key = [&witness[offset], &witness[offset + 2]]; let row_value = [&witness[offset + 1], &witness[offset + 3]]; let row_drifted = &witness[offset + 4]; let row_wrong = &witness[offset + 5]; @@ -600,10 +587,11 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec drifted_rlp_bytes, wrong_rlp_bytes, }; - let mut node = Node::default(); - node.storage = Some(storage_node); - node.values = node_rows; - nodes.push(node); + nodes.push(Node { + storage: Some(storage_node), + values: node_rows, + ..Default::default() + }); } else if witness[offset].get_type() == MptWitnessRowType::AccountLeafKeyS { let key_s = witness[offset].to_owned(); let key_c = witness[offset + 1].to_owned(); @@ -650,10 +638,11 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec drifted_rlp_bytes, wrong_rlp_bytes, }; - let mut node = Node::default(); - node.account = Some(account_node); - node.values = node_rows; - nodes.push(node); + nodes.push(Node { + account: Some(account_node), + values: node_rows, + ..Default::default() + }); } } @@ -661,10 +650,11 @@ pub(crate) fn prepare_witness(witness: &mut [MptWitnessRow]) -> Vec let start_node = StartNode { proof_type: MPTProofType::Disabled, }; - let mut node = Node::default(); - node.start = Some(start_node); - node.values = vec![vec![0; 34]; StartRowType::Count as usize]; - nodes.push(node); + nodes.push(Node { + start: Some(start_node), + values: vec![vec![0; 34]; StartRowType::Count as usize], + ..Default::default() + }); nodes } diff --git a/zkevm-circuits/src/pi_circuit.rs b/zkevm-circuits/src/pi_circuit.rs index 0fea2abcb2..cebe069cba 100644 --- a/zkevm-circuits/src/pi_circuit.rs +++ b/zkevm-circuits/src/pi_circuit.rs @@ -1,14 +1,21 @@ //! Public Input Circuit implementation +mod param; -use std::marker::PhantomData; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +mod dev; +#[cfg(any(feature = "test", test))] +mod test; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +pub use dev::PiCircuit as TestPiCircuit; use eth_types::{ geth_types::{BlockConstants, Transaction}, sign_types::SignData, - Address, BigEndianHash, Field, ToBigEndian, ToLittleEndian, ToScalar, Word, H256, + Address, BigEndianHash, Field, Keccak, ToBigEndian, ToLittleEndian, ToScalar, Word, H256, }; use halo2_proofs::plonk::{Instance, SecondPhase}; -use keccak256::plain::Keccak; +use param::*; +use std::marker::PhantomData; use crate::{ table::{BlockTable, LookupTable, TxFieldTag, TxTable}, @@ -29,12 +36,6 @@ use halo2_proofs::{ #[cfg(any(feature = "test", test, feature = "test-circuits"))] use halo2_proofs::{circuit::SimpleFloorPlanner, plonk::Circuit}; -/// Fixed by the spec -const BLOCK_LEN: usize = 7 + 256; -const EXTRA_LEN: usize = 2; -const ZERO_BYTE_GAS_COST: u64 = 4; -const NONZERO_BYTE_GAS_COST: u64 = 16; - /// Values of the block table (as in the spec) #[derive(Clone, Default, Debug)] pub struct BlockValues { @@ -51,12 +52,12 @@ pub struct BlockValues { /// Values of the tx table (as in the spec) #[derive(Default, Debug, Clone)] pub struct TxValues { - nonce: Word, - gas: Word, // gas limit + nonce: u64, + gas: u64, // gas limit gas_price: Word, from_addr: Address, to_addr: Address, - is_create: u64, + is_create: bool, value: Word, call_data_len: u64, call_data_gas_cost: u64, @@ -139,21 +140,15 @@ impl PublicData { let mut msg_hash_le = [0u8; 32]; msg_hash_le.copy_from_slice(sign_data.msg_hash.to_bytes().as_slice()); tx_vals.push(TxValues { - nonce: tx.nonce, + nonce: tx.nonce.as_u64(), gas_price: tx.gas_price, - gas: tx.gas_limit, + gas: tx.gas_limit.as_u64(), from_addr: tx.from, - to_addr: tx.to.unwrap_or_else(Address::zero), - is_create: (tx.to.is_none() as u64), + to_addr: tx.to_or_zero(), + is_create: tx.is_create(), value: tx.value, - call_data_len: tx.call_data.0.len() as u64, - call_data_gas_cost: tx.call_data.0.iter().fold(0, |acc, byte| { - acc + if *byte == 0 { - ZERO_BYTE_GAS_COST - } else { - NONZERO_BYTE_GAS_COST - } - }), + call_data_len: tx.call_data.len() as u64, + call_data_gas_cost: tx.call_data_gas_cost(), tx_sign_hash: msg_hash_le, }); } @@ -579,13 +574,13 @@ impl PiCircuitConfig { || "tx_id", self.tx_table.tx_id, offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; region.assign_advice( || "tx_id_inv", self.tx_id_inv, offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; region.assign_fixed( || "tag", @@ -597,31 +592,31 @@ impl PiCircuitConfig { || "index", self.tx_table.index, offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; region.assign_advice( || "tx_value", self.tx_table.value, offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; region.assign_advice( || "tx_value_inv", self.tx_value_inv, offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; region.assign_advice( || "is_final", self.is_final, offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; region.assign_advice( || "gas_cost", self.calldata_gas_cost, offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; Ok(()) } @@ -642,14 +637,14 @@ impl PiCircuitConfig { // tx_id_inv = (tag - CallDataLength)^(-1) let tx_id_inv = if tag != TxFieldTag::CallDataLength { let x = F::from(tag as u64) - F::from(TxFieldTag::CallDataLength as u64); - x.invert().unwrap_or(F::zero()) + x.invert().unwrap_or(F::ZERO) } else { - F::zero() + F::ZERO }; let tag = F::from(tag as u64); let index = F::from(index as u64); let tx_value = tx_value; - let tx_value_inv = tx_value.invert().unwrap_or(F::zero()); + let tx_value_inv = tx_value.invert().unwrap_or(F::ZERO); self.q_tx_table.enable(region, offset)?; @@ -737,14 +732,14 @@ impl PiCircuitConfig { raw_pi_vals: &mut [F], ) -> Result<(), Error> { let tx_id = F::from(tx_id as u64); - let tx_id_inv = tx_id.invert().unwrap_or(F::zero()); + let tx_id_inv = tx_id.invert().unwrap_or(F::ZERO); let tx_id_diff = F::from(tx_id_next as u64) - tx_id; - let tx_id_diff_inv = tx_id_diff.invert().unwrap_or(F::zero()); + let tx_id_diff_inv = tx_id_diff.invert().unwrap_or(F::ZERO); let tag = F::from(TxFieldTag::CallData as u64); let index = F::from(index as u64); let tx_value = tx_value; - let tx_value_inv = tx_value.invert().unwrap_or(F::zero()); - let is_final = if is_final { F::one() } else { F::zero() }; + let tx_value_inv = tx_value.invert().unwrap_or(F::ZERO); + let is_final = if is_final { F::ONE } else { F::ZERO }; // Assign vals to raw_public_inputs column let tx_table_len = TX_LEN * self.max_txs + 1; @@ -843,15 +838,15 @@ impl PiCircuitConfig { || "zero", self.block_table.value, offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; region.assign_advice( || "zero", self.raw_public_inputs, offset, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; - raw_pi_vals[offset] = F::zero(); + raw_pi_vals[offset] = F::ZERO; offset += 1; // coinbase @@ -1139,6 +1134,16 @@ impl PiCircuit { impl SubCircuit for PiCircuit { type Config = PiCircuitConfig; + fn unusable_rows() -> usize { + // Column raw_public_inputs is queried at 4 distinct rotations at + // - Rotation::cur() + // - Rotation(BLOCK_LEN + 1 + EXTRA_LEN) + // - Rotation(BLOCK_LEN + 1 + EXTRA_LEN + max_txs * TX_LEN + 1) + // - Rotation(BLOCK_LEN + 1 + EXTRA_LEN + 2 * (max_txs * TX_LEN + 1)) + // so returns 7 unusable rows. + 7 + } + fn new_from_block(block: &witness::Block) -> Self { let public_data = PublicData { chain_id: block.context.chain_id, @@ -1202,7 +1207,7 @@ impl SubCircuit for PiCircuit { let rlc_rpi = rlc_rpi_col .iter() .rev() - .fold(F::zero(), |acc, val| acc * self.rand_rpi + val); + .fold(F::ZERO, |acc, val| acc * self.rand_rpi + val); // let block_hash = public_data // .eth_block @@ -1271,7 +1276,7 @@ impl SubCircuit for PiCircuit { region.name_column(|| "Public_Inputs", config.pi); let circuit_len = config.circuit_len(); - let mut raw_pi_vals = vec![F::zero(); circuit_len]; + let mut raw_pi_vals = vec![F::ZERO; circuit_len]; // Assign block table let block_values = self.public_data.get_block_table_values(); @@ -1304,7 +1309,7 @@ impl SubCircuit for PiCircuit { 0, TxFieldTag::Null, 0, - F::zero(), + F::ZERO, &mut raw_pi_vals, )?; offset += 1; @@ -1313,11 +1318,8 @@ impl SubCircuit for PiCircuit { let tx = if i < txs.len() { &txs[i] } else { &tx_default }; for (tag, value) in &[ - ( - TxFieldTag::Nonce, - rlc(tx.nonce.to_le_bytes(), self.randomness), - ), - (TxFieldTag::Gas, rlc(tx.gas.to_le_bytes(), self.randomness)), + (TxFieldTag::Nonce, F::from(tx.nonce)), + (TxFieldTag::Gas, F::from(tx.gas)), ( TxFieldTag::GasPrice, rlc(tx.gas_price.to_le_bytes(), self.randomness), @@ -1330,7 +1332,7 @@ impl SubCircuit for PiCircuit { TxFieldTag::CalleeAddress, tx.to_addr.to_scalar().expect("tx.to too big"), ), - (TxFieldTag::IsCreate, F::from(tx.is_create)), + (TxFieldTag::IsCreate, F::from(tx.is_create as u64)), ( TxFieldTag::Value, rlc(tx.value.to_le_bytes(), self.randomness), @@ -1362,7 +1364,7 @@ impl SubCircuit for PiCircuit { let txs = self.public_data.txs(); for (i, tx) in self.public_data.txs().iter().enumerate() { let call_data_length = tx.call_data.0.len(); - let mut gas_cost = F::zero(); + let mut gas_cost = F::ZERO; for (index, byte) in tx.call_data.0.iter().enumerate() { assert!(calldata_count < config.max_calldata); let is_final = index == call_data_length - 1; @@ -1389,7 +1391,7 @@ impl SubCircuit for PiCircuit { &mut region, offset, i + 1, - tx_id_next as usize, + tx_id_next, index, F::from(*byte as u64), is_final, @@ -1407,9 +1409,9 @@ impl SubCircuit for PiCircuit { 0, // tx_id 0, 0, - F::zero(), + F::ZERO, false, - F::zero(), + F::ZERO, &mut raw_pi_vals, )?; offset += 1; @@ -1442,92 +1444,6 @@ impl SubCircuit for PiCircuit { } } -// We define the PiTestCircuit as a wrapper over PiCircuit extended to take the -// generic const parameters MAX_TXS and MAX_CALLDATA. This is necessary because -// the trait Circuit requires an implementation of `configure` that doesn't take -// any circuit parameters, and the PiCircuit defines gates that use rotations -// that depend on MAX_TXS and MAX_CALLDATA, so these two values are required -// during the configuration. -/// Test Circuit for PiCircuit -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -#[derive(Default, Clone)] -pub struct PiTestCircuit( - pub PiCircuit, -); - -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -impl SubCircuit - for PiTestCircuit -{ - type Config = PiCircuitConfig; - - fn new_from_block(block: &witness::Block) -> Self { - assert_eq!(block.circuits_params.max_txs, MAX_TXS); - assert_eq!(block.circuits_params.max_calldata, MAX_CALLDATA); - - Self(PiCircuit::new_from_block(block)) - } - - fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { - assert_eq!(block.circuits_params.max_txs, MAX_TXS); - assert_eq!(block.circuits_params.max_calldata, MAX_CALLDATA); - - PiCircuit::min_num_rows_block(block) - } - - /// Compute the public inputs for this circuit. - fn instance(&self) -> Vec> { - self.0.instance() - } - - fn synthesize_sub( - &self, - _config: &Self::Config, - _challenges: &Challenges>, - _layouter: &mut impl Layouter, - ) -> Result<(), Error> { - panic!("use PiCircuit for embedding instead"); - } -} - -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -impl Circuit - for PiTestCircuit -{ - type Config = (PiCircuitConfig, Challenges); - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let block_table = BlockTable::construct(meta); - let tx_table = TxTable::construct(meta); - ( - PiCircuitConfig::new( - meta, - PiCircuitConfigArgs { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, - block_table, - tx_table, - }, - ), - Challenges::construct(meta), - ) - } - - fn synthesize( - &self, - (config, challenges): Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let challenges = challenges.values(&mut layouter); - self.0.synthesize_sub(&config, &challenges, &mut layouter) - } -} - /// Compute the raw_public_inputs column from the verifier's perspective. fn raw_public_inputs_col( max_txs: usize, @@ -1541,11 +1457,11 @@ fn raw_public_inputs_col( let mut offset = 0; let mut result = - vec![F::zero(); BLOCK_LEN + 1 + EXTRA_LEN + 3 * (TX_LEN * max_txs + 1) + max_calldata]; + vec![F::ZERO; BLOCK_LEN + 1 + EXTRA_LEN + 3 * (TX_LEN * max_txs + 1) + max_calldata]; // Insert Block Values // zero row - result[offset] = F::zero(); + result[offset] = F::ZERO; offset += 1; // coinbase result[offset] = block.coinbase.to_scalar().unwrap(); @@ -1592,9 +1508,9 @@ fn raw_public_inputs_col( let value_offset = index_offset + tx_table_len; // Insert zero row - result[id_offset + offset] = F::zero(); - result[index_offset + offset] = F::zero(); - result[value_offset + offset] = F::zero(); + result[id_offset + offset] = F::ZERO; + result[index_offset + offset] = F::ZERO; + result[value_offset + offset] = F::ZERO; offset += 1; @@ -1602,19 +1518,19 @@ fn raw_public_inputs_col( let tx = if i < txs.len() { &txs[i] } else { &tx_default }; for val in &[ - rlc(tx.nonce.to_le_bytes(), randomness), - rlc(tx.gas.to_le_bytes(), randomness), + F::from(tx.nonce), + F::from(tx.gas), rlc(tx.gas_price.to_le_bytes(), randomness), tx.from_addr.to_scalar().expect("tx.from too big"), tx.to_addr.to_scalar().expect("tx.to too big"), - F::from(tx.is_create), + F::from(tx.is_create as u64), rlc(tx.value.to_le_bytes(), randomness), F::from(tx.call_data_len), F::from(tx.call_data_gas_cost), rlc(tx.tx_sign_hash, randomness), ] { result[id_offset + offset] = F::from((i + 1) as u64); - result[index_offset + offset] = F::zero(); + result[index_offset + offset] = F::ZERO; result[value_offset + offset] = *val; offset += 1; @@ -1631,7 +1547,7 @@ fn raw_public_inputs_col( } } for _ in calldata_count..max_calldata { - result[value_offset + offset] = F::zero(); + result[value_offset + offset] = F::ZERO; offset += 1; } @@ -1655,133 +1571,3 @@ pub fn gen_rand_rpi( let rand_rpi = Word::from(keccak.digest().as_slice()) % F::MODULUS; rand_rpi.to_scalar().expect("rand_rpi.to_scalar") } - -#[cfg(test)] -mod pi_circuit_test { - use super::*; - use halo2_proofs::{ - dev::{MockProver, VerifyFailure}, - halo2curves::bn256::Fr, - }; - use mock::{CORRECT_MOCK_TXS, MOCK_CHAIN_ID}; - use pretty_assertions::assert_eq; - use rand::SeedableRng; - use rand_chacha::ChaCha20Rng; - - fn run( - k: u32, - public_data: PublicData, - ) -> Result<(), Vec> { - let mut rng = ChaCha20Rng::seed_from_u64(2); - let randomness = F::random(&mut rng); - let rand_rpi = F::random(&mut rng); - let mut public_data = public_data; - public_data.chain_id = *MOCK_CHAIN_ID; - - let circuit = PiTestCircuit::(PiCircuit::new( - MAX_TXS, - MAX_CALLDATA, - randomness, - rand_rpi, - public_data, - )); - let public_inputs = circuit.0.instance(); - - let prover = match MockProver::run(k, &circuit, public_inputs) { - Ok(prover) => prover, - Err(e) => panic!("{:#?}", e), - }; - prover.verify() - } - - #[test] - fn test_default_pi() { - const MAX_TXS: usize = 2; - const MAX_CALLDATA: usize = 8; - let public_data = PublicData::default(); - - let k = 17; - assert_eq!(run::(k, public_data), Ok(())); - } - - #[test] - fn test_simple_pi() { - const MAX_TXS: usize = 8; - const MAX_CALLDATA: usize = 200; - - let mut public_data = PublicData::default(); - - let n_tx = 4; - for i in 0..n_tx { - public_data - .transactions - .push(CORRECT_MOCK_TXS[i].clone().into()); - } - - let k = 17; - assert_eq!(run::(k, public_data), Ok(())); - } - - fn run_size_check( - public_data: [PublicData; 2], - ) { - let mut rng = ChaCha20Rng::seed_from_u64(2); - let randomness = F::random(&mut rng); - let rand_rpi = F::random(&mut rng); - - let circuit = PiTestCircuit::(PiCircuit::new( - MAX_TXS, - MAX_CALLDATA, - randomness, - rand_rpi, - public_data[0].clone(), - )); - let public_inputs = circuit.0.instance(); - let prover1 = MockProver::run(20, &circuit, public_inputs).unwrap(); - - let circuit2 = PiTestCircuit::(PiCircuit::new( - MAX_TXS, - MAX_CALLDATA, - randomness, - rand_rpi, - public_data[1].clone(), - )); - let public_inputs = circuit2.0.instance(); - let prover2 = MockProver::run(20, &circuit, public_inputs).unwrap(); - - assert_eq!(prover1.fixed(), prover2.fixed()); - assert_eq!(prover1.permutation(), prover2.permutation()); - } - - #[test] - fn variadic_size_check() { - const MAX_TXS: usize = 8; - const MAX_CALLDATA: usize = 200; - - let mut pub_dat_1 = PublicData { - chain_id: *MOCK_CHAIN_ID, - ..Default::default() - }; - - let n_tx = 2; - for i in 0..n_tx { - pub_dat_1 - .transactions - .push(CORRECT_MOCK_TXS[i].clone().into()); - } - - let mut pub_dat_2 = PublicData { - chain_id: *MOCK_CHAIN_ID, - ..Default::default() - }; - - let n_tx = 4; - for i in 0..n_tx { - pub_dat_2 - .transactions - .push(CORRECT_MOCK_TXS[i].clone().into()); - } - - run_size_check::([pub_dat_1, pub_dat_2]); - } -} diff --git a/zkevm-circuits/src/pi_circuit/dev.rs b/zkevm-circuits/src/pi_circuit/dev.rs new file mode 100644 index 0000000000..2ca0345ecb --- /dev/null +++ b/zkevm-circuits/src/pi_circuit/dev.rs @@ -0,0 +1,58 @@ +pub use super::PiCircuit; +use super::*; + +/// Public Input Circuit configuration parameters +#[derive(Default)] +pub struct PiCircuitParams { + /// Max Txs + pub max_txs: usize, + /// Max Calldata + pub max_calldata: usize, +} + +impl Circuit for PiCircuit { + type Config = (PiCircuitConfig, Challenges); + type FloorPlanner = SimpleFloorPlanner; + type Params = PiCircuitParams; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn params(&self) -> Self::Params { + PiCircuitParams { + max_txs: self.max_txs, + max_calldata: self.max_calldata, + } + } + + fn configure_with_params(meta: &mut ConstraintSystem, params: Self::Params) -> Self::Config { + let block_table = BlockTable::construct(meta); + let tx_table = TxTable::construct(meta); + ( + PiCircuitConfig::new( + meta, + PiCircuitConfigArgs { + max_txs: params.max_txs, + max_calldata: params.max_calldata, + block_table, + tx_table, + }, + ), + Challenges::construct(meta), + ) + } + + fn configure(_meta: &mut ConstraintSystem) -> Self::Config { + unreachable!(); + } + + fn synthesize( + &self, + (config, challenges): Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenges = challenges.values(&mut layouter); + self.synthesize_sub(&config, &challenges, &mut layouter) + } +} diff --git a/zkevm-circuits/src/pi_circuit/param.rs b/zkevm-circuits/src/pi_circuit/param.rs new file mode 100644 index 0000000000..8c05677fdd --- /dev/null +++ b/zkevm-circuits/src/pi_circuit/param.rs @@ -0,0 +1,5 @@ +/// Fixed by the spec +pub(super) const BLOCK_LEN: usize = 7 + 256; +pub(super) const EXTRA_LEN: usize = 2; +pub(super) const ZERO_BYTE_GAS_COST: u64 = 4; +pub(super) const NONZERO_BYTE_GAS_COST: u64 = 16; diff --git a/zkevm-circuits/src/pi_circuit/test.rs b/zkevm-circuits/src/pi_circuit/test.rs new file mode 100644 index 0000000000..5b6db6499b --- /dev/null +++ b/zkevm-circuits/src/pi_circuit/test.rs @@ -0,0 +1,132 @@ +#![allow(unused_imports)] +use super::{dev::*, *}; +use crate::util::unusable_rows; +use halo2_proofs::{ + dev::{MockProver, VerifyFailure}, + halo2curves::bn256::Fr, +}; +use mock::{CORRECT_MOCK_TXS, MOCK_CHAIN_ID}; +use rand::SeedableRng; +use rand_chacha::ChaCha20Rng; + +#[test] +fn pi_circuit_unusable_rows() { + assert_eq!( + PiCircuit::::unusable_rows(), + unusable_rows::>(PiCircuitParams { + max_txs: 2, + max_calldata: 8, + }), + ) +} + +fn run( + k: u32, + max_txs: usize, + max_calldata: usize, + public_data: PublicData, +) -> Result<(), Vec> { + let mut rng = ChaCha20Rng::seed_from_u64(2); + let randomness = F::random(&mut rng); + let rand_rpi = F::random(&mut rng); + let mut public_data = public_data; + public_data.chain_id = *MOCK_CHAIN_ID; + + let circuit = PiCircuit::::new(max_txs, max_calldata, randomness, rand_rpi, public_data); + let public_inputs = circuit.instance(); + + let prover = match MockProver::run(k, &circuit, public_inputs) { + Ok(prover) => prover, + Err(e) => panic!("{:#?}", e), + }; + prover.verify() +} + +#[test] +fn test_default_pi() { + let max_txs = 2; + let max_calldata = 8; + let public_data = PublicData::default(); + + let k = 17; + assert_eq!(run::(k, max_txs, max_calldata, public_data), Ok(())); +} + +#[test] +fn test_simple_pi() { + let max_txs = 8; + let max_calldata = 200; + + let mut public_data = PublicData::default(); + + let n_tx = 4; + for i in 0..n_tx { + public_data + .transactions + .push(CORRECT_MOCK_TXS[i].clone().into()); + } + + let k = 17; + assert_eq!(run::(k, max_txs, max_calldata, public_data), Ok(())); +} + +fn run_size_check(max_txs: usize, max_calldata: usize, public_data: [PublicData; 2]) { + let mut rng = ChaCha20Rng::seed_from_u64(2); + let randomness = F::random(&mut rng); + let rand_rpi = F::random(&mut rng); + + let circuit = PiCircuit::::new( + max_txs, + max_calldata, + randomness, + rand_rpi, + public_data[0].clone(), + ); + let public_inputs = circuit.instance(); + let prover1 = MockProver::run(20, &circuit, public_inputs).unwrap(); + + let circuit2 = PiCircuit::new( + max_txs, + max_calldata, + randomness, + rand_rpi, + public_data[1].clone(), + ); + let public_inputs = circuit2.instance(); + let prover2 = MockProver::run(20, &circuit, public_inputs).unwrap(); + + assert_eq!(prover1.fixed(), prover2.fixed()); + assert_eq!(prover1.permutation(), prover2.permutation()); +} + +#[test] +fn variadic_size_check() { + let max_txs = 8; + let max_calldata = 200; + + let mut pub_dat_1 = PublicData { + chain_id: *MOCK_CHAIN_ID, + ..Default::default() + }; + + let n_tx = 2; + for i in 0..n_tx { + pub_dat_1 + .transactions + .push(CORRECT_MOCK_TXS[i].clone().into()); + } + + let mut pub_dat_2 = PublicData { + chain_id: *MOCK_CHAIN_ID, + ..Default::default() + }; + + let n_tx = 4; + for i in 0..n_tx { + pub_dat_2 + .transactions + .push(CORRECT_MOCK_TXS[i].clone().into()); + } + + run_size_check::(max_txs, max_calldata, [pub_dat_1, pub_dat_2]); +} diff --git a/zkevm-circuits/src/root_circuit.rs b/zkevm-circuits/src/root_circuit.rs index 9eb0b35af2..d990f68b65 100644 --- a/zkevm-circuits/src/root_circuit.rs +++ b/zkevm-circuits/src/root_circuit.rs @@ -1,6 +1,7 @@ //! The Root circuit implementation. +use eth_types::Field; use halo2_proofs::{ - arithmetic::Field, + arithmetic::Field as Halo2Field, circuit::{Layouter, SimpleFloorPlanner, Value}, halo2curves::serde::SerdeObject, plonk::{Circuit, ConstraintSystem, Error}, @@ -13,6 +14,11 @@ use std::iter; mod aggregation; +#[cfg(any(feature = "test", test))] +mod test; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +pub use self::RootCircuit as TestRootCircuit; + pub use aggregation::{ aggregate, AggregationConfig, EccChip, Halo2Loader, KzgAs, KzgDk, KzgSvk, PlonkSuccinctVerifier, PlonkVerifier, PoseidonTranscript, Snark, SnarkWitness, BITS, LIMBS, @@ -34,6 +40,7 @@ impl<'a, M: MultiMillerLoop> RootCircuit<'a, M> where M::G1Affine: SerdeObject, M::G2Affine: SerdeObject, + M::Scalar: Field, { /// Create a `RootCircuit` with accumulator computed given a `SuperCircuit` /// proof and its instance. Returns `None` if given proof is invalid. @@ -45,7 +52,7 @@ where ) -> Result { let num_instances = super_circuit_protocol.num_instance.iter().sum::() + 4 * LIMBS; let instance = { - let mut instance = Ok(vec![M::Scalar::zero(); num_instances]); + let mut instance = Ok(vec![M::Scalar::ZERO; num_instances]); super_circuit_instances .as_ref() .zip(super_circuit_proof.as_ref()) @@ -97,15 +104,19 @@ where } } -impl<'a, M: MultiMillerLoop> Circuit for RootCircuit<'a, M> { +impl<'a, M: MultiMillerLoop> Circuit for RootCircuit<'a, M> +where + M::Scalar: Field, +{ type Config = AggregationConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self { svk: self.svk, snark: self.snark.without_witnesses(), - instance: vec![M::Scalar::zero(); self.instance.len()], + instance: vec![M::Scalar::ZERO; self.instance.len()], } } @@ -137,90 +148,3 @@ impl<'a, M: MultiMillerLoop> Circuit for RootCircuit<'a, M> { Ok(()) } } - -#[cfg(test)] -mod test { - use crate::{ - root_circuit::{compile, Config, PoseidonTranscript, RootCircuit}, - super_circuit::{super_circuit_tests::block_1tx, SuperCircuit}, - }; - use bus_mapping::circuit_input_builder::CircuitsParams; - use halo2_proofs::{ - circuit::Value, - dev::MockProver, - halo2curves::bn256::Bn256, - plonk::{create_proof, keygen_pk, keygen_vk}, - poly::kzg::{ - commitment::{KZGCommitmentScheme, ParamsKZG}, - multiopen::ProverGWC, - }, - }; - use itertools::Itertools; - use rand::rngs::OsRng; - - #[ignore = "Due to high memory requirement"] - #[test] - fn test_root_circuit() { - let (params, protocol, proof, instance) = { - // Preprocess - const MAX_TXS: usize = 1; - const MAX_CALLDATA: usize = 32; - const TEST_MOCK_RANDOMNESS: u64 = 0x100; - let circuits_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, - max_rws: 256, - max_copy_rows: 256, - max_exp_steps: 256, - max_bytecode: 512, - max_evm_rows: 0, - max_keccak_rows: 0, - }; - let (k, circuit, instance, _) = - SuperCircuit::<_, MAX_TXS, MAX_CALLDATA, TEST_MOCK_RANDOMNESS>::build( - block_1tx(), - circuits_params, - ) - .unwrap(); - let params = ParamsKZG::::setup(k, OsRng); - let pk = keygen_pk(¶ms, keygen_vk(¶ms, &circuit).unwrap(), &circuit).unwrap(); - let protocol = compile( - ¶ms, - pk.get_vk(), - Config::kzg() - .with_num_instance(instance.iter().map(|instance| instance.len()).collect()), - ); - - // Create proof - let proof = { - let mut transcript = PoseidonTranscript::new(Vec::new()); - create_proof::, ProverGWC<_>, _, _, _, _>( - ¶ms, - &pk, - &[circuit], - &[&instance.iter().map(Vec::as_slice).collect_vec()], - OsRng, - &mut transcript, - ) - .unwrap(); - transcript.finalize() - }; - - (params, protocol, proof, instance) - }; - - let root_circuit = RootCircuit::new( - ¶ms, - &protocol, - Value::known(&instance), - Value::known(&proof), - ) - .unwrap(); - assert_eq!( - MockProver::run(26, &root_circuit, root_circuit.instance()) - .unwrap() - .verify_par(), - Ok(()) - ); - } -} diff --git a/zkevm-circuits/src/root_circuit/aggregation.rs b/zkevm-circuits/src/root_circuit/aggregation.rs index b3321808e6..5e51ad5d9f 100644 --- a/zkevm-circuits/src/root_circuit/aggregation.rs +++ b/zkevm-circuits/src/root_circuit/aggregation.rs @@ -1,8 +1,9 @@ +use eth_types::Field; use halo2_proofs::{ - arithmetic::Field, circuit::{AssignedCell, Layouter, SimpleFloorPlanner, Value}, halo2curves::{ - group::prime::PrimeCurveAffine, pairing::Engine, serde::SerdeObject, CurveAffine, + ff::Field as Halo2Field, group::prime::PrimeCurveAffine, pairing::Engine, + serde::SerdeObject, CurveAffine, }, plonk::{Circuit, ConstraintSystem, Error}, poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}, @@ -23,7 +24,7 @@ use snark_verifier::{ AccumulationDecider, AccumulationScheme, AccumulationSchemeProver, }, system::halo2::transcript, - util::arithmetic::{fe_to_limbs, FieldExt, MultiMillerLoop}, + util::arithmetic::{fe_to_limbs, MultiMillerLoop}, verifier::{self, plonk::PlonkProtocol, SnarkVerifier}, }; use std::{io, iter, rc::Rc}; @@ -178,12 +179,12 @@ impl AggregationConfig { } /// Returns `MainGate`. - pub fn main_gate(&self) -> MainGate { + pub fn main_gate(&self) -> MainGate { MainGate::new(self.main_gate_config.clone()) } /// Returns `RangeChip`. - pub fn range_chip(&self) -> RangeChip { + pub fn range_chip(&self) -> RangeChip { RangeChip::new(self.range_config.clone()) } @@ -196,7 +197,7 @@ impl AggregationConfig { } /// Load fixed lookup table for `RangeChip` - pub fn load_table(&self, layouter: &mut impl Layouter) -> Result<(), Error> { + pub fn load_table(&self, layouter: &mut impl Layouter) -> Result<(), Error> { self.range_chip().load_table(layouter) } @@ -215,7 +216,10 @@ impl AggregationConfig { Vec>, ), Error, - > { + > + where + M::Scalar: Field, + { type PoseidonTranscript<'a, C, S> = transcript::halo2::PoseidonTranscript>, S, T, RATE, R_F, R_P>; let snarks = snarks.into_iter().collect_vec(); @@ -311,6 +315,7 @@ pub fn aggregate<'a, M: MultiMillerLoop>( where M::G1Affine: SerdeObject, M::G2Affine: SerdeObject, + M::Scalar: Field, { let svk = KzgSvk::::new(params.get_g()[0]); let dk = KzgDk::new(svk, params.g2(), params.s_g2()); @@ -385,6 +390,7 @@ impl<'a, M: MultiMillerLoop> TestAggregationCircuit<'a, M> where M::G1Affine: SerdeObject, M::G2Affine: SerdeObject, + M::Scalar: Field, { /// Create an Aggregation circuit with aggregated accumulator computed. /// Returns `None` if any given snark is invalid. @@ -434,9 +440,13 @@ where } } -impl<'a, M: MultiMillerLoop> Circuit for TestAggregationCircuit<'a, M> { +impl<'a, M: MultiMillerLoop> Circuit for TestAggregationCircuit<'a, M> +where + M::Scalar: Field, +{ type Config = AggregationConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self { @@ -446,7 +456,7 @@ impl<'a, M: MultiMillerLoop> Circuit for TestAggregationCircuit<'a, M .iter() .map(SnarkWitness::without_witnesses) .collect(), - instances: vec![M::Scalar::zero(); self.instances.len()], + instances: vec![M::Scalar::ZERO; self.instances.len()], } } @@ -484,8 +494,9 @@ impl<'a, M: MultiMillerLoop> Circuit for TestAggregationCircuit<'a, M #[cfg(test)] pub mod test { use crate::root_circuit::{PoseidonTranscript, Snark, TestAggregationCircuit}; + use eth_types::Field; use halo2_proofs::{ - arithmetic::FieldExt, + arithmetic::Field as Halo2Field, circuit::{floor_planner::V1, Layouter, Value}, dev::{FailureLocation, MockProver, VerifyFailure}, halo2curves::{ @@ -550,7 +561,7 @@ pub mod test { impl StandardPlonkConfig { /// Configure for `StandardPlonk` - pub fn configure(meta: &mut ConstraintSystem) -> Self { + pub fn configure(meta: &mut ConstraintSystem) -> Self { let [w_l, w_r, w_o] = [(); 3].map(|_| meta.advice_column()); let [q_l, q_r, q_o, q_m, q_c] = [(); 5].map(|_| meta.fixed_column()); let pi = meta.instance_column(); @@ -584,7 +595,7 @@ pub mod test { #[derive(Clone, Copy)] pub struct StandardPlonk(F); - impl StandardPlonk { + impl StandardPlonk { /// Create a `StandardPlonk` with random instnace. pub fn rand(mut rng: R) -> Self { Self(F::from(rng.next_u32() as u64)) @@ -596,9 +607,10 @@ pub mod test { } } - impl Circuit for StandardPlonk { + impl Circuit for StandardPlonk { type Config = StandardPlonkConfig; type FloorPlanner = V1; + type Params = (); fn without_witnesses(&self) -> Self { *self @@ -622,7 +634,7 @@ pub mod test { // Assign some non-zero values to make sure the advice/fixed columns have // non-identity commitments. let a = region.assign_advice(|| "", w_l, 0, || Value::known(self.0))?; - region.assign_fixed(|| "", q_l, 0, || Value::known(-F::one()))?; + region.assign_fixed(|| "", q_l, 0, || Value::known(-F::ONE))?; a.copy_advice(|| "", &mut region, w_r, 1)?; a.copy_advice(|| "", &mut region, w_o, 2)?; region.assign_advice(|| "", w_l, 3, || Value::known(-F::from(5)))?; @@ -642,7 +654,7 @@ pub mod test { ) -> Vec> { // Preprocess let (pk, protocol) = { - let standard_plonk = StandardPlonk(Fr::zero()); + let standard_plonk = StandardPlonk(Fr::ZERO); let vk = keygen_vk(params, &standard_plonk).unwrap(); let pk = keygen_pk(params, vk, &standard_plonk).unwrap(); let protocol = compile( @@ -711,7 +723,7 @@ pub mod test { .unwrap(); let mut instances = aggregation.instances(); // Change the propagated inner snark's instance - instances[0][0] += Fr::one(); + instances[0][0] += Fr::ONE; // Then expect the verification to fail assert_eq!( MockProver::run(21, &aggregation, instances) diff --git a/zkevm-circuits/src/root_circuit/test.rs b/zkevm-circuits/src/root_circuit/test.rs new file mode 100644 index 0000000000..0f372fe629 --- /dev/null +++ b/zkevm-circuits/src/root_circuit/test.rs @@ -0,0 +1,77 @@ +#![allow(unused_imports)] +pub use super::*; +use crate::super_circuit::{test::block_1tx, SuperCircuit}; +use bus_mapping::circuit_input_builder::CircuitsParams; +use halo2_proofs::{ + circuit::Value, + dev::MockProver, + halo2curves::bn256::Bn256, + plonk::{create_proof, keygen_pk, keygen_vk}, + poly::kzg::{ + commitment::{KZGCommitmentScheme, ParamsKZG}, + multiopen::ProverGWC, + }, +}; +use itertools::Itertools; +use rand::rngs::OsRng; + +#[ignore = "Due to high memory requirement"] +#[test] +fn test_root_circuit() { + let (params, protocol, proof, instance) = { + // Preprocess + const TEST_MOCK_RANDOMNESS: u64 = 0x100; + let circuits_params = CircuitsParams { + max_txs: 1, + max_calldata: 32, + max_rws: 256, + max_copy_rows: 256, + max_exp_steps: 256, + max_bytecode: 512, + max_evm_rows: 0, + max_keccak_rows: 0, + }; + let (k, circuit, instance, _) = + SuperCircuit::<_>::build(block_1tx(), circuits_params, TEST_MOCK_RANDOMNESS.into()) + .unwrap(); + let params = ParamsKZG::::setup(k, OsRng); + let pk = keygen_pk(¶ms, keygen_vk(¶ms, &circuit).unwrap(), &circuit).unwrap(); + let protocol = compile( + ¶ms, + pk.get_vk(), + Config::kzg() + .with_num_instance(instance.iter().map(|instance| instance.len()).collect()), + ); + + // Create proof + let proof = { + let mut transcript = PoseidonTranscript::new(Vec::new()); + create_proof::, ProverGWC<_>, _, _, _, _>( + ¶ms, + &pk, + &[circuit], + &[&instance.iter().map(Vec::as_slice).collect_vec()], + OsRng, + &mut transcript, + ) + .unwrap(); + transcript.finalize() + }; + + (params, protocol, proof, instance) + }; + + let root_circuit = RootCircuit::new( + ¶ms, + &protocol, + Value::known(&instance), + Value::known(&proof), + ) + .unwrap(); + assert_eq!( + MockProver::run(26, &root_circuit, root_circuit.instance()) + .unwrap() + .verify_par(), + Ok(()) + ); +} diff --git a/zkevm-circuits/src/state_circuit.rs b/zkevm-circuits/src/state_circuit.rs index 22639d32da..baa02a75dc 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -3,13 +3,24 @@ mod constraint_builder; mod lexicographic_ordering; mod lookups; mod multiple_precision_integer; +mod param; mod random_linear_combination; -#[cfg(test)] + +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +mod dev; +#[cfg(any(feature = "test", test))] mod test; +use bus_mapping::operation::Target; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +pub use dev::StateCircuit as TestStateCircuit; +use self::{ + constraint_builder::{MptUpdateTableQueries, RwTableQueries}, + lexicographic_ordering::LimbIndex, +}; use crate::{ evm_circuit::{param::N_BYTES_WORD, util::rlc}, - table::{AccountFieldTag, LookupTable, MPTProofType, MptTable, RwTable, RwTableTag}, + table::{AccountFieldTag, LookupTable, MPTProofType, MptTable, RwTable}, util::{Challenges, Expr, SubCircuit, SubCircuitConfig}, witness::{self, MptUpdates, Rw, RwMap}, }; @@ -29,22 +40,12 @@ use halo2_proofs::{ use lexicographic_ordering::Config as LexicographicOrderingConfig; use lookups::{Chip as LookupsChip, Config as LookupsConfig, Queries as LookupsQueries}; use multiple_precision_integer::{Chip as MpiChip, Config as MpiConfig, Queries as MpiQueries}; +use param::*; use random_linear_combination::{Chip as RlcChip, Config as RlcConfig, Queries as RlcQueries}; -#[cfg(test)] -use std::collections::HashMap; use std::marker::PhantomData; -use self::{ - constraint_builder::{MptUpdateTableQueries, RwTableQueries}, - lexicographic_ordering::LimbIndex, -}; - #[cfg(any(feature = "test", test, feature = "test-circuits"))] -use halo2_proofs::{circuit::SimpleFloorPlanner, plonk::Circuit}; - -const N_LIMBS_RW_COUNTER: usize = 2; -const N_LIMBS_ACCOUNT_ADDRESS: usize = 10; -const N_LIMBS_ID: usize = 2; +use std::collections::HashMap; /// Config for StateCircuit #[derive(Clone)] @@ -227,7 +228,7 @@ impl StateCircuitConfig { || "selector", self.selector, offset, - || Value::known(F::one()), + || Value::known(F::ONE), )?; tag_chip.assign(region, offset, &row.tag())?; @@ -262,7 +263,7 @@ impl StateCircuitConfig { || "not_first_access", self.not_first_access, offset, - || Value::known(if is_first_access { F::zero() } else { F::one() }), + || Value::known(if is_first_access { F::ZERO } else { F::ONE }), )?; if is_first_access { @@ -275,13 +276,8 @@ impl StateCircuitConfig { assert_eq!(state_root, old_root); state_root = new_root; } - if matches!(row.tag(), RwTableTag::CallContext) && !row.is_write() { - assert_eq!( - row.value_assignment(randomness), - F::zero(), - "{:?}", - row - ); + if matches!(row.tag(), Target::CallContext) && !row.is_write() { + assert_eq!(row.value_assignment(randomness), F::ZERO, "{:?}", row); } state_root }); @@ -398,7 +394,7 @@ impl StateCircuitConfig { /// Keys for sorting the rows of the state circuit #[derive(Clone, Copy)] pub struct SortKeysConfig { - tag: BinaryNumberConfig, + tag: BinaryNumberConfig, id: MpiConfig, address: MpiConfig, field_tag: Column, @@ -427,8 +423,8 @@ pub struct StateCircuit { pub rows: Vec, updates: MptUpdates, pub(crate) n_rows: usize, - #[cfg(test)] - overrides: HashMap<(test::AdviceColumn, isize), F>, + #[cfg(any(feature = "test", test, feature = "test-circuits"))] + overrides: HashMap<(dev::AdviceColumn, isize), F>, _marker: PhantomData, } @@ -441,7 +437,7 @@ impl StateCircuit { rows, updates, n_rows, - #[cfg(test)] + #[cfg(any(feature = "test", test, feature = "test-circuits"))] overrides: HashMap::new(), _marker: PhantomData::default(), } @@ -455,6 +451,12 @@ impl SubCircuit for StateCircuit { Self::new(block.rws.clone(), block.circuits_params.max_rws) } + fn unusable_rows() -> usize { + // No column queried at more than 3 distinct rotations, so returns 6 as + // minimum unusable rows. + 6 + } + /// Return the minimum number of rows required to prove the block fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { ( @@ -494,7 +496,7 @@ impl SubCircuit for StateCircuit { self.n_rows, randomness, )?; - #[cfg(test)] + #[cfg(any(feature = "test", test, feature = "test-circuits"))] { let padding_length = RwMap::padding_len(self.rows.len(), self.n_rows); for ((column, row_offset), &f) in &self.overrides { @@ -522,51 +524,6 @@ impl SubCircuit for StateCircuit { } } -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -impl Circuit for StateCircuit -where - F: Field, -{ - type Config = (StateCircuitConfig, Challenges); - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let rw_table = RwTable::construct(meta); - let mpt_table = MptTable::construct(meta); - let challenges = Challenges::construct(meta); - - let config = { - let challenges = challenges.exprs(meta); - StateCircuitConfig::new( - meta, - StateCircuitConfigArgs { - rw_table, - mpt_table, - challenges, - }, - ) - }; - - (config, challenges) - } - - fn synthesize( - &self, - (config, challenges): Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let challenges = challenges.values(&mut layouter); - config - .mpt_table - .load(&mut layouter, &self.updates, challenges.evm_word())?; - self.synthesize_sub(&config, &challenges, &mut layouter) - } -} - fn queries(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig) -> Queries { let first_different_limb = c.lexicographic_ordering.first_different_limb; let final_bits_sum = meta.query_advice(first_different_limb.bits[3], Rotation::cur()) @@ -635,40 +592,3 @@ fn queries(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig) state_root_prev: meta.query_advice(c.state_root, Rotation::prev()), } } - -#[cfg(test)] -mod state_circuit_stats { - use crate::{ - evm_circuit::step::ExecutionState, - stats::{bytecode_prefix_op_big_rws, print_circuit_stats_by_states}, - }; - - /// Prints the stats of State circuit per execution state. See - /// `print_circuit_stats_by_states` for more details. - /// - /// Run with: - /// `cargo test -p zkevm-circuits --release --all-features - /// get_state_states_stats -- --nocapture --ignored` - #[ignore] - #[test] - pub fn get_state_states_stats() { - print_circuit_stats_by_states( - |state| { - // TODO: Enable CREATE/CREATE2 once they are supported - !matches!( - state, - ExecutionState::ErrorInvalidOpcode - | ExecutionState::CREATE - | ExecutionState::CREATE2 - | ExecutionState::SELFDESTRUCT - ) - }, - bytecode_prefix_op_big_rws, - |block, _, step_index| { - let step = &block.txs[0].steps()[step_index]; - let step_next = &block.txs[0].steps()[step_index + 1]; - step_next.rwc.0 - step.rwc.0 - }, - ); - } -} diff --git a/zkevm-circuits/src/state_circuit/constraint_builder.rs b/zkevm-circuits/src/state_circuit/constraint_builder.rs index fe718ad21c..58a5e66169 100644 --- a/zkevm-circuits/src/state_circuit/constraint_builder.rs +++ b/zkevm-circuits/src/state_circuit/constraint_builder.rs @@ -1,16 +1,16 @@ use super::{ lookups::Queries as LookupsQueries, multiple_precision_integer::Queries as MpiQueries, - random_linear_combination::Queries as RlcQueries, N_LIMBS_ACCOUNT_ADDRESS, N_LIMBS_ID, - N_LIMBS_RW_COUNTER, + param::*, random_linear_combination::Queries as RlcQueries, }; use crate::{ evm_circuit::{ param::N_BYTES_WORD, util::{math_gadget::generate_lagrange_base_polynomial, not}, }, - table::{AccountFieldTag, MPTProofType, RwTableTag}, + table::{AccountFieldTag, MPTProofType}, util::Expr, }; +use bus_mapping::operation::Target; use eth_types::Field; use gadgets::binary_number::BinaryNumberConfig; use halo2_proofs::plonk::Expression; @@ -102,35 +102,34 @@ impl ConstraintBuilder { pub fn build(&mut self, q: &Queries) { self.build_general_constraints(q); - self.condition(q.tag_matches(RwTableTag::Start), |cb| { + self.condition(q.tag_matches(Target::Start), |cb| { cb.build_start_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::Memory), |cb| { + self.condition(q.tag_matches(Target::Memory), |cb| { cb.build_memory_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::Stack), |cb| { + self.condition(q.tag_matches(Target::Stack), |cb| { cb.build_stack_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::AccountStorage), |cb| { + self.condition(q.tag_matches(Target::Storage), |cb| { cb.build_account_storage_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::TxAccessListAccount), |cb| { + self.condition(q.tag_matches(Target::TxAccessListAccount), |cb| { cb.build_tx_access_list_account_constraints(q) }); - self.condition( - q.tag_matches(RwTableTag::TxAccessListAccountStorage), - |cb| cb.build_tx_access_list_account_storage_constraints(q), - ); - self.condition(q.tag_matches(RwTableTag::TxRefund), |cb| { + self.condition(q.tag_matches(Target::TxAccessListAccountStorage), |cb| { + cb.build_tx_access_list_account_storage_constraints(q) + }); + self.condition(q.tag_matches(Target::TxRefund), |cb| { cb.build_tx_refund_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::Account), |cb| { + self.condition(q.tag_matches(Target::Account), |cb| { cb.build_account_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::CallContext), |cb| { + self.condition(q.tag_matches(Target::CallContext), |cb| { cb.build_call_context_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::TxLog), |cb| { + self.condition(q.tag_matches(Target::TxLog), |cb| { cb.build_tx_log_constraints(q) }); } @@ -400,11 +399,6 @@ impl ConstraintBuilder { }); // 7.2. `initial value` is 0 self.require_zero("initial TxRefund value is 0", q.initial_value()); - // 7.3. First access for a set of all keys are 0 if READ - self.require_zero( - "first access for a set of all keys are 0 if READ", - q.first_access() * q.is_read() * q.value(), - ); } fn build_account_constraints(&mut self, q: &Queries) { @@ -623,8 +617,8 @@ impl Queries { self.mpt_proof_type.clone() } - fn tag_matches(&self, tag: RwTableTag) -> Expression { - BinaryNumberConfig::::value_equals_expr(tag, self.tag_bits.clone()) + fn tag_matches(&self, tag: Target) -> Expression { + BinaryNumberConfig::::value_equals_expr(tag, self.tag_bits.clone()) } fn first_access(&self) -> Expression { @@ -676,7 +670,7 @@ fn from_digits(digits: &[Expression], base: Expression) -> Expre digits .iter() .rev() - .fold(Expression::Constant(F::zero()), |result, digit| { + .fold(Expression::Constant(F::ZERO), |result, digit| { digit.clone() + result * base.clone() }) } diff --git a/zkevm-circuits/src/state_circuit/dev.rs b/zkevm-circuits/src/state_circuit/dev.rs new file mode 100644 index 0000000000..83769a9057 --- /dev/null +++ b/zkevm-circuits/src/state_circuit/dev.rs @@ -0,0 +1,120 @@ +pub use super::StateCircuit; + +use crate::{ + state_circuit::{StateCircuitConfig, StateCircuitConfigArgs}, + table::{MptTable, RwTable}, + util::{Challenges, SubCircuit, SubCircuitConfig}, +}; +use eth_types::Field; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner}, + plonk::{Advice, Circuit, Column, ConstraintSystem, Error}, +}; + +impl Circuit for StateCircuit +where + F: Field, +{ + type Config = (StateCircuitConfig, Challenges); + type FloorPlanner = SimpleFloorPlanner; + type Params = (); + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let rw_table = RwTable::construct(meta); + let mpt_table = MptTable::construct(meta); + let challenges = Challenges::construct(meta); + + let config = { + let challenges = challenges.exprs(meta); + StateCircuitConfig::new( + meta, + StateCircuitConfigArgs { + rw_table, + mpt_table, + challenges, + }, + ) + }; + + (config, challenges) + } + + fn synthesize( + &self, + (config, challenges): Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenges = challenges.values(&mut layouter); + config + .mpt_table + .load(&mut layouter, &self.updates, challenges.evm_word())?; + self.synthesize_sub(&config, &challenges, &mut layouter) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub enum AdviceColumn { + IsWrite, + Address, + AddressLimb0, + AddressLimb1, + StorageKey, + StorageKeyByte0, + StorageKeyByte1, + Value, + ValuePrev, + RwCounter, + RwCounterLimb0, + RwCounterLimb1, + Tag, + TagBit0, + TagBit1, + TagBit2, + TagBit3, + LimbIndexBit0, // most significant bit + LimbIndexBit1, + LimbIndexBit2, + LimbIndexBit3, + LimbIndexBit4, // least significant bit + InitialValue, + IsZero, // committed_value and value are 0 + // NonEmptyWitness is the BatchedIsZero chip witness that contains the + // inverse of the non-zero value if any in [committed_value, value] + NonEmptyWitness, +} + +impl AdviceColumn { + pub fn value(&self, config: &StateCircuitConfig) -> Column { + match self { + Self::IsWrite => config.rw_table.is_write, + Self::Address => config.rw_table.address, + Self::AddressLimb0 => config.sort_keys.address.limbs[0], + Self::AddressLimb1 => config.sort_keys.address.limbs[1], + Self::StorageKey => config.rw_table.storage_key, + Self::StorageKeyByte0 => config.sort_keys.storage_key.bytes[0], + Self::StorageKeyByte1 => config.sort_keys.storage_key.bytes[1], + Self::Value => config.rw_table.value, + Self::ValuePrev => config.rw_table.value_prev, + Self::RwCounter => config.rw_table.rw_counter, + Self::RwCounterLimb0 => config.sort_keys.rw_counter.limbs[0], + Self::RwCounterLimb1 => config.sort_keys.rw_counter.limbs[1], + Self::Tag => config.rw_table.tag, + Self::TagBit0 => config.sort_keys.tag.bits[0], + Self::TagBit1 => config.sort_keys.tag.bits[1], + Self::TagBit2 => config.sort_keys.tag.bits[2], + Self::TagBit3 => config.sort_keys.tag.bits[3], + Self::LimbIndexBit0 => config.lexicographic_ordering.first_different_limb.bits[0], + Self::LimbIndexBit1 => config.lexicographic_ordering.first_different_limb.bits[1], + Self::LimbIndexBit2 => config.lexicographic_ordering.first_different_limb.bits[2], + Self::LimbIndexBit3 => config.lexicographic_ordering.first_different_limb.bits[3], + Self::LimbIndexBit4 => config.lexicographic_ordering.first_different_limb.bits[4], + Self::InitialValue => config.initial_value, + Self::IsZero => config.is_non_exist.is_zero, + Self::NonEmptyWitness => config.is_non_exist.nonempty_witness, + } + } +} diff --git a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs index cb84cab754..bea63c2d5c 100644 --- a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs +++ b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs @@ -1,4 +1,4 @@ -use super::{lookups, SortKeysConfig, N_LIMBS_ACCOUNT_ADDRESS, N_LIMBS_ID, N_LIMBS_RW_COUNTER}; +use super::{lookups, param::*, SortKeysConfig}; use crate::{evm_circuit::param::N_BYTES_WORD, impl_expr, util::Expr, witness::Rw}; use eth_types::{Field, ToBigEndian}; use gadgets::binary_number::{AsBits, BinaryNumberChip, BinaryNumberConfig}; @@ -196,7 +196,7 @@ impl Config { || "upper_limb_difference", self.selector, offset, - || Value::known(F::one()), + || Value::known(F::ONE), )?; let cur_be_limbs = rw_to_be_limbs(cur); diff --git a/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs b/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs index fcd71f1bdf..2a8d963baa 100644 --- a/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs +++ b/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs @@ -1,4 +1,4 @@ -use super::{lookups, N_LIMBS_ACCOUNT_ADDRESS, N_LIMBS_RW_COUNTER}; +use super::{lookups, param::*}; use crate::util::Expr; use eth_types::{Address, Field}; use halo2_proofs::{ diff --git a/zkevm-circuits/src/state_circuit/param.rs b/zkevm-circuits/src/state_circuit/param.rs new file mode 100644 index 0000000000..fb30860c12 --- /dev/null +++ b/zkevm-circuits/src/state_circuit/param.rs @@ -0,0 +1,3 @@ +pub(super) const N_LIMBS_RW_COUNTER: usize = 2; +pub(super) const N_LIMBS_ACCOUNT_ADDRESS: usize = 10; +pub(super) const N_LIMBS_ID: usize = 2; diff --git a/zkevm-circuits/src/state_circuit/test.rs b/zkevm-circuits/src/state_circuit/test.rs index a8e273fcf6..8656bab364 100644 --- a/zkevm-circuits/src/state_circuit/test.rs +++ b/zkevm-circuits/src/state_circuit/test.rs @@ -1,7 +1,8 @@ -use super::{StateCircuit, StateCircuitConfig}; +#![allow(unused_imports)] +pub use super::{dev::*, *}; use crate::{ - table::{AccountFieldTag, CallContextFieldTag, RwTableTag, TxLogFieldTag, TxReceiptFieldTag}, - util::SubCircuit, + table::{AccountFieldTag, CallContextFieldTag, TxLogFieldTag, TxReceiptFieldTag}, + util::{unusable_rows, SubCircuit}, witness::{MptUpdates, Rw, RwMap}, }; use bus_mapping::operation::{ @@ -15,6 +16,7 @@ use eth_types::{ use gadgets::binary_number::AsBits; use halo2_proofs::{ arithmetic::Field as Halo2Field, + circuit::SimpleFloorPlanner, dev::{MockProver, VerifyFailure}, halo2curves::bn256::{Bn256, Fr}, plonk::{keygen_vk, Advice, Circuit, Column, ConstraintSystem}, @@ -26,67 +28,12 @@ use strum::IntoEnumIterator; const N_ROWS: usize = 1 << 16; -#[derive(Hash, Eq, PartialEq, Clone, Debug)] -pub enum AdviceColumn { - IsWrite, - Address, - AddressLimb0, - AddressLimb1, - StorageKey, - StorageKeyByte0, - StorageKeyByte1, - Value, - ValuePrev, - RwCounter, - RwCounterLimb0, - RwCounterLimb1, - Tag, - TagBit0, - TagBit1, - TagBit2, - TagBit3, - LimbIndexBit0, // most significant bit - LimbIndexBit1, - LimbIndexBit2, - LimbIndexBit3, - LimbIndexBit4, // least significant bit - InitialValue, - IsZero, // committed_value and value are 0 - // NonEmptyWitness is the BatchedIsZero chip witness that contains the - // inverse of the non-zero value if any in [committed_value, value] - NonEmptyWitness, -} - -impl AdviceColumn { - pub fn value(&self, config: &StateCircuitConfig) -> Column { - match self { - Self::IsWrite => config.rw_table.is_write, - Self::Address => config.rw_table.address, - Self::AddressLimb0 => config.sort_keys.address.limbs[0], - Self::AddressLimb1 => config.sort_keys.address.limbs[1], - Self::StorageKey => config.rw_table.storage_key, - Self::StorageKeyByte0 => config.sort_keys.storage_key.bytes[0], - Self::StorageKeyByte1 => config.sort_keys.storage_key.bytes[1], - Self::Value => config.rw_table.value, - Self::ValuePrev => config.rw_table.value_prev, - Self::RwCounter => config.rw_table.rw_counter, - Self::RwCounterLimb0 => config.sort_keys.rw_counter.limbs[0], - Self::RwCounterLimb1 => config.sort_keys.rw_counter.limbs[1], - Self::Tag => config.rw_table.tag, - Self::TagBit0 => config.sort_keys.tag.bits[0], - Self::TagBit1 => config.sort_keys.tag.bits[1], - Self::TagBit2 => config.sort_keys.tag.bits[2], - Self::TagBit3 => config.sort_keys.tag.bits[3], - Self::LimbIndexBit0 => config.lexicographic_ordering.first_different_limb.bits[0], - Self::LimbIndexBit1 => config.lexicographic_ordering.first_different_limb.bits[1], - Self::LimbIndexBit2 => config.lexicographic_ordering.first_different_limb.bits[2], - Self::LimbIndexBit3 => config.lexicographic_ordering.first_different_limb.bits[3], - Self::LimbIndexBit4 => config.lexicographic_ordering.first_different_limb.bits[4], - Self::InitialValue => config.initial_value, - Self::IsZero => config.is_non_exist.is_zero, - Self::NonEmptyWitness => config.is_non_exist.nonempty_witness, - } - } +#[test] +fn state_circuit_unusable_rows() { + assert_eq!( + StateCircuit::::unusable_rows(), + unusable_rows::>(()), + ) } fn test_state_circuit_ok( @@ -435,7 +382,7 @@ fn address_limb_mismatch() { value: U256::zero(), value_prev: U256::zero(), }]; - let overrides = HashMap::from([((AdviceColumn::AddressLimb0, 0), Fr::zero())]); + let overrides = HashMap::from([((AdviceColumn::AddressLimb0, 0), Fr::ZERO)]); let result = verify_with_overrides(rows, overrides); @@ -454,7 +401,7 @@ fn address_limb_out_of_range() { }]; let overrides = HashMap::from([ ((AdviceColumn::AddressLimb0, 0), Fr::from(1 << 16)), - ((AdviceColumn::AddressLimb1, 0), Fr::zero()), + ((AdviceColumn::AddressLimb1, 0), Fr::ZERO), ]); let result = verify_with_overrides(rows, overrides); @@ -474,7 +421,7 @@ fn storage_key_mismatch() { tx_id: 4, committed_value: U256::from(34), }]; - let overrides = HashMap::from([((AdviceColumn::StorageKeyByte1, 0), Fr::one())]); + let overrides = HashMap::from([((AdviceColumn::StorageKeyByte1, 0), Fr::ONE)]); let result = verify_with_overrides(rows, overrides); @@ -495,7 +442,7 @@ fn storage_key_byte_out_of_range() { }]; let overrides = HashMap::from([ ((AdviceColumn::StorageKeyByte0, 0), Fr::from(0xcafeu64)), - ((AdviceColumn::StorageKeyByte1, 0), Fr::zero()), + ((AdviceColumn::StorageKeyByte1, 0), Fr::ZERO), ]); // This will trigger two errors: an RLC encoding error and the "fit into u8", we @@ -709,8 +656,8 @@ fn lexicographic_ordering_previous_limb_differences_nonzero() { // limb difference between the two rows here is still 1, so no additional // overrides are needed. let overrides = HashMap::from([ - ((AdviceColumn::LimbIndexBit1, 1), Fr::one()), - ((AdviceColumn::LimbIndexBit2, 1), Fr::one()), + ((AdviceColumn::LimbIndexBit1, 1), Fr::ONE), + ((AdviceColumn::LimbIndexBit2, 1), Fr::ONE), ]); let result = verify_with_overrides(rows, overrides); @@ -759,7 +706,7 @@ fn skipped_start_rw_counter() { // The original assignment is 1 << 16. Fr::from((1 << 16) + 1), ), - ((AdviceColumn::RwCounterLimb0, -1), Fr::one()), + ((AdviceColumn::RwCounterLimb0, -1), Fr::ONE), ]); let result = prover(vec![], overrides).verify_at_rows(N_ROWS - 1..N_ROWS, N_ROWS - 1..N_ROWS); @@ -793,7 +740,7 @@ fn bad_initial_memory_value() { let overrides = HashMap::from([ ((AdviceColumn::Value, 0), v), ((AdviceColumn::ValuePrev, 0), v), - ((AdviceColumn::IsZero, 0), Fr::zero()), + ((AdviceColumn::IsZero, 0), Fr::ZERO), ((AdviceColumn::NonEmptyWitness, 0), v.invert().unwrap()), ((AdviceColumn::InitialValue, 0), v), ]); @@ -877,14 +824,12 @@ fn invalid_stack_address_change() { #[test] fn invalid_tags() { let first_row_offset = -isize::try_from(N_ROWS).unwrap(); - let tags: BTreeSet = RwTableTag::iter().map(|x| x as usize).collect(); + let tags: BTreeSet = Target::iter().map(|x| x as usize).collect(); for i in 0..16 { if tags.contains(&i) { continue; } - let bits: [Fr; 4] = i - .as_bits() - .map(|bit| if bit { Fr::one() } else { Fr::zero() }); + let bits: [Fr; 4] = i.as_bits().map(|bit| if bit { Fr::ONE } else { Fr::ZERO }); let overrides = HashMap::from([ ((AdviceColumn::TagBit0, first_row_offset), bits[0]), ((AdviceColumn::TagBit1, first_row_offset), bits[1]), @@ -956,7 +901,7 @@ fn bad_initial_tx_refund_value() { ((AdviceColumn::IsWrite, 0), Fr::from(1)), ((AdviceColumn::Value, 0), v), ((AdviceColumn::ValuePrev, 0), v), - ((AdviceColumn::IsZero, 0), Fr::zero()), + ((AdviceColumn::IsZero, 0), Fr::ZERO), ((AdviceColumn::NonEmptyWitness, 0), v.invert().unwrap()), ((AdviceColumn::InitialValue, 0), v), ]); diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 64060c11a7..cb5deac7f7 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -50,6 +50,9 @@ //! - [x] Tx Circuit //! - [ ] MPT Circuit +#[cfg(any(feature = "test", test))] +pub(crate) mod test; + use crate::{ bytecode_circuit::circuit::{ BytecodeCircuit, BytecodeCircuitConfig, BytecodeCircuitConfigArgs, @@ -95,17 +98,17 @@ pub struct SuperCircuitConfig { } /// Circuit configuration arguments -pub struct SuperCircuitConfigArgs { +pub struct SuperCircuitConfigArgs { /// Max txs pub max_txs: usize, /// Max calldata pub max_calldata: usize, /// Mock randomness - pub mock_randomness: u64, + pub mock_randomness: F, } impl SubCircuitConfig for SuperCircuitConfig { - type ConfigArgs = SuperCircuitConfigArgs; + type ConfigArgs = SuperCircuitConfigArgs; /// Configure SuperCircuitConfig fn new( @@ -128,9 +131,8 @@ impl SubCircuitConfig for SuperCircuitConfig { // Use a mock randomness instead of the randomness derived from the challange // (either from mock or real prover) to help debugging assignments. - let power_of_randomness: [Expression; 31] = array::from_fn(|i| { - Expression::Constant(F::from(mock_randomness).pow(&[1 + i as u64, 0, 0, 0])) - }); + let power_of_randomness: [Expression; 31] = + array::from_fn(|i| Expression::Constant(mock_randomness.pow([1 + i as u64, 0, 0, 0]))); let challenges = Challenges::mock( power_of_randomness[0].clone(), @@ -222,12 +224,7 @@ impl SubCircuitConfig for SuperCircuitConfig { /// The Super Circuit contains all the zkEVM circuits #[derive(Clone, Default, Debug)] -pub struct SuperCircuit< - F: Field, - const MAX_TXS: usize, - const MAX_CALLDATA: usize, - const MOCK_RANDOMNESS: u64, -> { +pub struct SuperCircuit { /// EVM Circuit pub evm_circuit: EvmCircuit, /// State Circuit @@ -244,15 +241,16 @@ pub struct SuperCircuit< pub exp_circuit: ExpCircuit, /// Keccak Circuit pub keccak_circuit: KeccakCircuit, + /// Circuits Parameters + pub circuits_params: CircuitsParams, + /// Mock randomness + pub mock_randomness: F, } -impl - SuperCircuit -{ +impl SuperCircuit { /// Return the number of rows required to verify a given block pub fn get_num_rows_required(block: &Block) -> usize { let num_rows_evm_circuit = EvmCircuit::::get_num_rows_required(block); - assert_eq!(block.circuits_params.max_txs, MAX_TXS); let num_rows_tx_circuit = TxCircuitConfig::::get_num_rows_required(block.circuits_params.max_txs); num_rows_evm_circuit.max(num_rows_tx_circuit) @@ -262,11 +260,23 @@ impl - SubCircuit for SuperCircuit -{ +impl SubCircuit for SuperCircuit { type Config = SuperCircuitConfig; + fn unusable_rows() -> usize { + itertools::max([ + EvmCircuit::::unusable_rows(), + StateCircuit::::unusable_rows(), + TxCircuit::::unusable_rows(), + PiCircuit::::unusable_rows(), + BytecodeCircuit::::unusable_rows(), + CopyCircuit::::unusable_rows(), + ExpCircuit::::unusable_rows(), + KeccakCircuit::::unusable_rows(), + ]) + .unwrap() + } + fn new_from_block(block: &Block) -> Self { let evm_circuit = EvmCircuit::new_from_block(block); let state_circuit = StateCircuit::new_from_block(block); @@ -277,7 +287,7 @@ impl { + SuperCircuit::<_> { evm_circuit, state_circuit, tx_circuit, @@ -286,6 +296,8 @@ impl - Circuit for SuperCircuit -{ +/// Super Circuit configuration parameters +#[derive(Default)] +pub struct SuperCircuitParams { + max_txs: usize, + max_calldata: usize, + mock_randomness: F, +} + +impl Circuit for SuperCircuit { type Config = SuperCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = SuperCircuitParams; fn without_witnesses(&self) -> Self { Self::default() } - fn configure(meta: &mut ConstraintSystem) -> Self::Config { + fn params(&self) -> Self::Params { + SuperCircuitParams { + max_txs: self.circuits_params.max_txs, + max_calldata: self.circuits_params.max_calldata, + mock_randomness: self.mock_randomness, + } + } + + fn configure_with_params(meta: &mut ConstraintSystem, params: Self::Params) -> Self::Config { Self::Config::new( meta, SuperCircuitConfigArgs { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, - mock_randomness: MOCK_RANDOMNESS, + max_txs: params.max_txs, + max_calldata: params.max_calldata, + mock_randomness: params.mock_randomness, }, ) } + fn configure(_meta: &mut ConstraintSystem) -> Self::Config { + unreachable!(); + } + fn synthesize( &self, config: Self::Config, @@ -401,9 +432,7 @@ impl - SuperCircuit -{ +impl SuperCircuit { /// From the witness data, generate a SuperCircuit instance with all of the /// sub-circuits filled with their corresponding witnesses. /// @@ -413,6 +442,7 @@ impl Result<(u32, Self, Vec>, CircuitInputBuilder), bus_mapping::Error> { let block_data = BlockData::new_from_geth_data_with_params(geth_data.clone(), circuits_params); @@ -421,7 +451,7 @@ impl Result<(u32, Self, Vec>), bus_mapping::Error> { let mut block = block_convert(&builder.block, &builder.code_db).unwrap(); - block.randomness = F::from(MOCK_RANDOMNESS); - assert_eq!(block.circuits_params.max_txs, MAX_TXS); - assert_eq!(block.circuits_params.max_calldata, MAX_CALLDATA); + block.randomness = mock_randomness; - const NUM_BLINDING_ROWS: usize = 64; let (_, rows_needed) = Self::min_num_rows_block(&block); - let k = log2_ceil(NUM_BLINDING_ROWS + rows_needed); + let k = log2_ceil(Self::unusable_rows() + rows_needed); log::debug!("super circuit uses k = {}", k); - let circuit = - SuperCircuit::<_, MAX_TXS, MAX_CALLDATA, MOCK_RANDOMNESS>::new_from_block(&block); + let circuit = SuperCircuit::new_from_block(&block); let instance = circuit.instance(); Ok((k, circuit, instance)) } } - -#[cfg(test)] -pub(crate) mod super_circuit_tests { - use super::*; - use ethers_signers::{LocalWallet, Signer}; - use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; - use log::error; - use mock::{TestContext, MOCK_CHAIN_ID}; - use rand::SeedableRng; - use rand_chacha::ChaCha20Rng; - use std::collections::HashMap; - - use eth_types::{address, bytecode, geth_types::GethData, Word}; - - #[test] - fn super_circuit_degree() { - let mut cs = ConstraintSystem::::default(); - SuperCircuit::<_, 1, 32, 0x100>::configure(&mut cs); - log::info!("super circuit degree: {}", cs.degree()); - log::info!("super circuit minimum_rows: {}", cs.minimum_rows()); - assert!(cs.degree() <= 9); - } - - fn test_super_circuit< - const MAX_TXS: usize, - const MAX_CALLDATA: usize, - const MOCK_RANDOMNESS: u64, - >( - block: GethData, - circuits_params: CircuitsParams, - ) { - let (k, circuit, instance, _) = - SuperCircuit::::build( - block, - circuits_params, - ) - .unwrap(); - let prover = MockProver::run(k, &circuit, instance).unwrap(); - let res = prover.verify_par(); - if let Err(err) = res { - error!("Verification failures: {:#?}", err); - panic!("Failed verification"); - } - } - - pub(crate) fn block_1tx() -> GethData { - let mut rng = ChaCha20Rng::seed_from_u64(2); - - let chain_id = (*MOCK_CHAIN_ID).as_u64(); - - let bytecode = bytecode! { - GAS - STOP - }; - - let wallet_a = LocalWallet::new(&mut rng).with_chain_id(chain_id); - - let addr_a = wallet_a.address(); - let addr_b = address!("0x000000000000000000000000000000000000BBBB"); - - let mut wallets = HashMap::new(); - wallets.insert(wallet_a.address(), wallet_a); - - let mut block: GethData = TestContext::<2, 1>::new( - None, - |accs| { - accs[0] - .address(addr_b) - .balance(Word::from(1u64 << 20)) - .code(bytecode); - accs[1].address(addr_a).balance(Word::from(1u64 << 20)); - }, - |mut txs, accs| { - txs[0] - .from(accs[1].address) - .to(accs[0].address) - .gas(Word::from(1_000_000u64)); - }, - |block, _tx| block.number(0xcafeu64), - ) - .unwrap() - .into(); - block.sign(&wallets); - block - } - - fn block_2tx() -> GethData { - let mut rng = ChaCha20Rng::seed_from_u64(2); - - let chain_id = (*MOCK_CHAIN_ID).as_u64(); - - let bytecode = bytecode! { - GAS - STOP - }; - - let wallet_a = LocalWallet::new(&mut rng).with_chain_id(chain_id); - - let addr_a = wallet_a.address(); - let addr_b = address!("0x000000000000000000000000000000000000BBBB"); - - let mut wallets = HashMap::new(); - wallets.insert(wallet_a.address(), wallet_a); - - let mut block: GethData = TestContext::<2, 2>::new( - None, - |accs| { - accs[0] - .address(addr_b) - .balance(Word::from(1u64 << 20)) - .code(bytecode); - accs[1].address(addr_a).balance(Word::from(1u64 << 20)); - }, - |mut txs, accs| { - txs[0] - .from(accs[1].address) - .to(accs[0].address) - .gas(Word::from(1_000_000u64)); - txs[1] - .from(accs[1].address) - .to(accs[0].address) - .gas(Word::from(1_000_000u64)); - }, - |block, _tx| block.number(0xcafeu64), - ) - .unwrap() - .into(); - block.sign(&wallets); - block - } - - const TEST_MOCK_RANDOMNESS: u64 = 0x100; - - // High memory usage test. Run in serial with: - // `cargo test [...] serial_ -- --ignored --test-threads 1` - #[ignore] - #[test] - fn serial_test_super_circuit_1tx_1max_tx() { - let block = block_1tx(); - const MAX_TXS: usize = 1; - const MAX_CALLDATA: usize = 32; - let circuits_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, - max_rws: 256, - max_copy_rows: 256, - max_exp_steps: 256, - max_bytecode: 512, - max_evm_rows: 0, - max_keccak_rows: 0, - }; - test_super_circuit::(block, circuits_params); - } - #[ignore] - #[test] - fn serial_test_super_circuit_1tx_2max_tx() { - let block = block_1tx(); - const MAX_TXS: usize = 2; - const MAX_CALLDATA: usize = 32; - let circuits_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, - max_rws: 256, - max_copy_rows: 256, - max_exp_steps: 256, - max_bytecode: 512, - max_evm_rows: 0, - max_keccak_rows: 0, - }; - test_super_circuit::(block, circuits_params); - } - #[ignore] - #[test] - fn serial_test_super_circuit_2tx_2max_tx() { - let block = block_2tx(); - const MAX_TXS: usize = 2; - const MAX_CALLDATA: usize = 32; - let circuits_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, - max_rws: 256, - max_copy_rows: 256, - max_exp_steps: 256, - max_bytecode: 512, - max_evm_rows: 0, - max_keccak_rows: 0, - }; - test_super_circuit::(block, circuits_params); - } -} diff --git a/zkevm-circuits/src/super_circuit/test.rs b/zkevm-circuits/src/super_circuit/test.rs new file mode 100644 index 0000000000..697da5cb0c --- /dev/null +++ b/zkevm-circuits/src/super_circuit/test.rs @@ -0,0 +1,174 @@ +pub use super::*; +use ethers_signers::{LocalWallet, Signer}; +use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; +use log::error; +use mock::{TestContext, MOCK_CHAIN_ID}; +use rand::SeedableRng; +use rand_chacha::ChaCha20Rng; +use std::collections::HashMap; + +use eth_types::{address, bytecode, geth_types::GethData, Word}; + +#[test] +fn super_circuit_degree() { + let mut cs = ConstraintSystem::::default(); + let params = SuperCircuitParams { + max_txs: 1, + max_calldata: 32, + mock_randomness: Fr::from(0x100), + }; + SuperCircuit::configure_with_params(&mut cs, params); + log::info!("super circuit degree: {}", cs.degree()); + log::info!("super circuit minimum_rows: {}", cs.minimum_rows()); + assert!(cs.degree() <= 9); +} + +fn test_super_circuit(block: GethData, circuits_params: CircuitsParams, mock_randomness: Fr) { + let (k, circuit, instance, _) = + SuperCircuit::::build(block, circuits_params, mock_randomness).unwrap(); + let prover = MockProver::run(k, &circuit, instance).unwrap(); + let res = prover.verify_par(); + if let Err(err) = res { + error!("Verification failures: {:#?}", err); + panic!("Failed verification"); + } +} + +pub(crate) fn block_1tx() -> GethData { + let mut rng = ChaCha20Rng::seed_from_u64(2); + + let chain_id = (*MOCK_CHAIN_ID).as_u64(); + + let bytecode = bytecode! { + GAS + STOP + }; + + let wallet_a = LocalWallet::new(&mut rng).with_chain_id(chain_id); + + let addr_a = wallet_a.address(); + let addr_b = address!("0x000000000000000000000000000000000000BBBB"); + + let mut wallets = HashMap::new(); + wallets.insert(wallet_a.address(), wallet_a); + + let mut block: GethData = TestContext::<2, 1>::new( + None, + |accs| { + accs[0] + .address(addr_b) + .balance(Word::from(1u64 << 20)) + .code(bytecode); + accs[1].address(addr_a).balance(Word::from(1u64 << 20)); + }, + |mut txs, accs| { + txs[0] + .from(accs[1].address) + .to(accs[0].address) + .gas(Word::from(1_000_000u64)); + }, + |block, _tx| block.number(0xcafeu64), + ) + .unwrap() + .into(); + block.sign(&wallets); + block +} + +fn block_2tx() -> GethData { + let mut rng = ChaCha20Rng::seed_from_u64(2); + + let chain_id = (*MOCK_CHAIN_ID).as_u64(); + + let bytecode = bytecode! { + GAS + STOP + }; + + let wallet_a = LocalWallet::new(&mut rng).with_chain_id(chain_id); + + let addr_a = wallet_a.address(); + let addr_b = address!("0x000000000000000000000000000000000000BBBB"); + + let mut wallets = HashMap::new(); + wallets.insert(wallet_a.address(), wallet_a); + + let mut block: GethData = TestContext::<2, 2>::new( + None, + |accs| { + accs[0] + .address(addr_b) + .balance(Word::from(1u64 << 20)) + .code(bytecode); + accs[1].address(addr_a).balance(Word::from(1u64 << 20)); + }, + |mut txs, accs| { + txs[0] + .from(accs[1].address) + .to(accs[0].address) + .gas(Word::from(1_000_000u64)); + txs[1] + .from(accs[1].address) + .to(accs[0].address) + .gas(Word::from(1_000_000u64)); + }, + |block, _tx| block.number(0xcafeu64), + ) + .unwrap() + .into(); + block.sign(&wallets); + block +} + +const TEST_MOCK_RANDOMNESS: u64 = 0x100; + +// High memory usage test. Run in serial with: +// `cargo test [...] serial_ -- --ignored --test-threads 1` +#[ignore] +#[test] +fn serial_test_super_circuit_1tx_1max_tx() { + let block = block_1tx(); + let circuits_params = CircuitsParams { + max_txs: 1, + max_calldata: 32, + max_rws: 256, + max_copy_rows: 256, + max_exp_steps: 256, + max_bytecode: 512, + max_evm_rows: 0, + max_keccak_rows: 0, + }; + test_super_circuit(block, circuits_params, Fr::from(TEST_MOCK_RANDOMNESS)); +} +#[ignore] +#[test] +fn serial_test_super_circuit_1tx_2max_tx() { + let block = block_1tx(); + let circuits_params = CircuitsParams { + max_txs: 2, + max_calldata: 32, + max_rws: 256, + max_copy_rows: 256, + max_exp_steps: 256, + max_bytecode: 512, + max_evm_rows: 0, + max_keccak_rows: 0, + }; + test_super_circuit(block, circuits_params, Fr::from(TEST_MOCK_RANDOMNESS)); +} +#[ignore] +#[test] +fn serial_test_super_circuit_2tx_2max_tx() { + let block = block_2tx(); + let circuits_params = CircuitsParams { + max_txs: 2, + max_calldata: 32, + max_rws: 256, + max_copy_rows: 256, + max_exp_steps: 256, + max_bytecode: 512, + max_evm_rows: 0, + max_keccak_rows: 0, + }; + test_super_circuit(block, circuits_params, Fr::from(TEST_MOCK_RANDOMNESS)); +} diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 3431d1e8c8..8ae4b2d90a 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -1,35 +1,59 @@ //! Table definitions used cross-circuits use crate::{ - circuit, - circuit_tools::constraint_builder::ConstraintBuilder, - copy_circuit::number_or_hash_to_field, + copy_circuit::util::number_or_hash_to_field, evm_circuit::util::rlc, - exp_circuit::{OFFSET_INCREMENT, ROWS_PER_STEP}, impl_expr, util::{build_tx_log_address, Challenges}, witness::{ Block, BlockContext, Bytecode, MptUpdateRow, MptUpdates, Rw, RwMap, RwRow, Transaction, }, }; -use bus_mapping::circuit_input_builder::{CopyDataType, CopyEvent, CopyStep, ExpEvent}; +use bus_mapping::circuit_input_builder::{CopyDataType, CopyEvent, CopyStep}; use core::iter::once; -use eth_types::{Field, ToLittleEndian, ToScalar, Word, U256}; +use eth_types::{Field, Keccak, ToLittleEndian, ToScalar, Word, U256}; use gadgets::{ binary_number::{BinaryNumberChip, BinaryNumberConfig}, util::{split_u256, split_u256_limb64}, }; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Layouter, Region, Value}, plonk::{Advice, Column, ConstraintSystem, Error, *}, poly::Rotation, }; use itertools::Itertools; -use keccak256::plain::Keccak; use std::array; use strum_macros::{EnumCount, EnumIter}; +/// block table +pub(crate) mod block_table; +/// bytecode table +pub(crate) mod bytecode_table; +/// copy Table +pub(crate) mod copy_table; +/// exp(exponentiation) table +pub(crate) mod exp_table; +/// keccak table +pub(crate) mod keccak_table; +/// mpt table +pub(crate) mod mpt_table; +/// rw table +pub(crate) mod rw_table; +/// tx table +pub(crate) mod tx_table; + +pub(crate) use block_table::{BlockContextFieldTag, BlockTable}; +pub(crate) use bytecode_table::{BytecodeFieldTag, BytecodeTable}; +pub(crate) use copy_table::CopyTable; +pub(crate) use exp_table::ExpTable; +pub(crate) use keccak_table::KeccakTable; + +pub(crate) use mpt_table::{MPTProofType, MptTable}; +pub(crate) use rw_table::RwTable; +pub(crate) use tx_table::{ + TxContextFieldTag, TxFieldTag, TxLogFieldTag, TxReceiptFieldTag, TxTable, +}; + /// Trait used to define lookup tables pub trait LookupTable { /// Returns the list of ALL the table columns following the table order. @@ -90,246 +114,6 @@ impl> + Copy, const W: usize> LookupTable for [ } } -/// Tag used to identify each field in the transaction in a row of the -/// transaction table. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum TxFieldTag { - /// Unused tag - Null = 0, - /// Nonce - Nonce, - /// Gas - Gas, - /// GasPrice - GasPrice, - /// CallerAddress - CallerAddress, - /// CalleeAddress - CalleeAddress, - /// IsCreate - IsCreate, - /// Value - Value, - /// CallDataLength - CallDataLength, - /// Gas cost for transaction call data (4 for byte == 0, 16 otherwise) - CallDataGasCost, - /// TxSignHash: Hash of the transaction without the signature, used for - /// signing. - TxSignHash, - /// CallData - CallData, -} -impl_expr!(TxFieldTag); - -/// Alias for TxFieldTag used by EVM Circuit -pub type TxContextFieldTag = TxFieldTag; - -/// Table that contains the fields of all Transactions in a block -#[derive(Clone, Debug)] -pub struct TxTable { - /// Tx ID - pub tx_id: Column, - /// Tag (TxContextFieldTag) - pub tag: Column, - /// Index for Tag = CallData - pub index: Column, - /// Value - pub value: Column, -} - -impl TxTable { - /// Construct a new TxTable - pub fn construct(meta: &mut ConstraintSystem) -> Self { - Self { - tx_id: meta.advice_column(), - tag: meta.fixed_column(), - index: meta.advice_column(), - value: meta.advice_column_in(SecondPhase), - } - } - - /// Assign the `TxTable` from a list of block `Transaction`s, followig the - /// same layout that the Tx Circuit uses. - pub fn load( - &self, - layouter: &mut impl Layouter, - txs: &[Transaction], - max_txs: usize, - max_calldata: usize, - challenges: &Challenges>, - ) -> Result<(), Error> { - assert!( - txs.len() <= max_txs, - "txs.len() <= max_txs: txs.len()={}, max_txs={}", - txs.len(), - max_txs - ); - let sum_txs_calldata = txs.iter().map(|tx| tx.call_data.len()).sum(); - assert!( - sum_txs_calldata <= max_calldata, - "sum_txs_calldata <= max_calldata: sum_txs_calldata={}, max_calldata={}", - sum_txs_calldata, - max_calldata, - ); - - fn assign_row( - region: &mut Region<'_, F>, - offset: usize, - advice_columns: &[Column], - tag: &Column, - row: &[Value; 4], - msg: &str, - ) -> Result<(), Error> { - for (index, column) in advice_columns.iter().enumerate() { - region.assign_advice( - || format!("tx table {} row {}", msg, offset), - *column, - offset, - || row[if index > 0 { index + 1 } else { index }], - )?; - } - region.assign_fixed( - || format!("tx table {} row {}", msg, offset), - *tag, - offset, - || row[1], - )?; - Ok(()) - } - - layouter.assign_region( - || "tx table", - |mut region| { - let mut offset = 0; - let advice_columns = [self.tx_id, self.index, self.value]; - assign_row( - &mut region, - offset, - &advice_columns, - &self.tag, - &[(); 4].map(|_| Value::known(F::zero())), - "all-zero", - )?; - offset += 1; - - // Tx Table contains an initial region that has a size parametrized by max_txs - // with all the tx data except for calldata, and then a second - // region that has a size parametrized by max_calldata with all - // the tx calldata. This is required to achieve a constant fixed column tag - // regardless of the number of input txs or the calldata size of each tx. - let mut calldata_assignments: Vec<[Value; 4]> = Vec::new(); - // Assign Tx data (all tx fields except for calldata) - let padding_txs: Vec<_> = (txs.len()..max_txs) - .map(|i| Transaction { - id: i + 1, - ..Default::default() - }) - .collect(); - for tx in txs.iter().chain(padding_txs.iter()) { - let [tx_data, tx_calldata] = tx.table_assignments(*challenges); - for row in tx_data { - assign_row(&mut region, offset, &advice_columns, &self.tag, &row, "")?; - offset += 1; - } - calldata_assignments.extend(tx_calldata.iter()); - } - // Assign Tx calldata - let padding_calldata = (sum_txs_calldata..max_calldata).map(|_| { - [ - Value::known(F::zero()), - Value::known(F::from(TxContextFieldTag::CallData as u64)), - Value::known(F::zero()), - Value::known(F::zero()), - ] - }); - for row in calldata_assignments.into_iter().chain(padding_calldata) { - assign_row(&mut region, offset, &advice_columns, &self.tag, &row, "")?; - offset += 1; - } - Ok(()) - }, - ) - } -} - -impl LookupTable for TxTable { - fn columns(&self) -> Vec> { - vec![ - self.tx_id.into(), - self.tag.into(), - self.index.into(), - self.value.into(), - ] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("tx_id"), - String::from("tag"), - String::from("index"), - String::from("value"), - ] - } - - fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { - vec![ - meta.query_advice(self.tx_id, Rotation::cur()), - meta.query_fixed(self.tag, Rotation::cur()), - meta.query_advice(self.index, Rotation::cur()), - meta.query_advice(self.value, Rotation::cur()), - ] - } -} - -/// Tag to identify the operation type in a RwTable row -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIter)] -pub enum RwTableTag { - /// Start (used for padding) - Start = 1, - /// Stack operation - Stack, - /// Memory operation - Memory, - /// Account Storage operation - AccountStorage, - /// Tx Access List Account operation - TxAccessListAccount, - /// Tx Access List Account Storage operation - TxAccessListAccountStorage, - /// Tx Refund operation - TxRefund, - /// Account operation - Account, - /// Call Context operation - CallContext, - /// Tx Log operation - TxLog, - /// Tx Receipt operation - TxReceipt, -} -impl_expr!(RwTableTag); - -impl RwTableTag { - /// Returns true if the RwTable operation is reversible - pub fn is_reversible(self) -> bool { - matches!( - self, - RwTableTag::TxAccessListAccount - | RwTableTag::TxAccessListAccountStorage - | RwTableTag::TxRefund - | RwTableTag::Account - | RwTableTag::AccountStorage - ) - } -} - -impl From for usize { - fn from(t: RwTableTag) -> Self { - t as usize - } -} - /// Tag for an AccountField in RwTable #[derive(Clone, Copy, Debug, EnumIter, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum AccountFieldTag { @@ -344,30 +128,6 @@ pub enum AccountFieldTag { } impl_expr!(AccountFieldTag); -/// Tag for a TxLogField in RwTable -#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter)] -pub enum TxLogFieldTag { - /// Address field - Address = 1, - /// Topic field - Topic, - /// Data field - Data, -} -impl_expr!(TxLogFieldTag); - -/// Tag for a TxReceiptField in RwTable -#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter, EnumCount)] -pub enum TxReceiptFieldTag { - /// Tx result - PostStateOrStatus = 1, - /// CumulativeGasUsed in the tx - CumulativeGasUsed, - /// Number of logs in the tx - LogLength, -} -impl_expr!(TxReceiptFieldTag); - /// Tag for a CallContextField in RwTable #[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter)] pub enum CallContextFieldTag { @@ -425,1146 +185,3 @@ pub enum CallContextFieldTag { ReversibleWriteCounter, } impl_expr!(CallContextFieldTag); - -/// The RwTable shared between EVM Circuit and State Circuit, which contains -/// traces of the EVM state operations. -#[derive(Clone, Copy, Debug)] -pub struct RwTable { - /// Read Write Counter - pub rw_counter: Column, - /// Is Write - pub is_write: Column, - /// Tag - pub tag: Column, - /// Key1 (Id) - pub id: Column, - /// Key2 (Address) - pub address: Column, - /// Key3 (FieldTag) - pub field_tag: Column, - /// Key3 (StorageKey) - pub storage_key: Column, - /// Value - pub value: Column, - /// Value Previous - pub value_prev: Column, - /// Aux1 - pub aux1: Column, - /// Aux2 (Committed Value) - pub aux2: Column, -} - -impl LookupTable for RwTable { - fn columns(&self) -> Vec> { - vec![ - self.rw_counter.into(), - self.is_write.into(), - self.tag.into(), - self.id.into(), - self.address.into(), - self.field_tag.into(), - self.storage_key.into(), - self.value.into(), - self.value_prev.into(), - self.aux1.into(), - self.aux2.into(), - ] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("rw_counter"), - String::from("is_write"), - String::from("tag"), - String::from("id"), - String::from("address"), - String::from("field_tag"), - String::from("storage_key"), - String::from("value"), - String::from("value_prev"), - String::from("aux1"), - String::from("aux2"), - ] - } -} -impl RwTable { - /// Construct a new RwTable - pub fn construct(meta: &mut ConstraintSystem) -> Self { - Self { - rw_counter: meta.advice_column(), - is_write: meta.advice_column(), - tag: meta.advice_column(), - id: meta.advice_column(), - address: meta.advice_column(), - field_tag: meta.advice_column(), - storage_key: meta.advice_column_in(SecondPhase), - value: meta.advice_column_in(SecondPhase), - value_prev: meta.advice_column_in(SecondPhase), - // It seems that aux1 for the moment is not using randomness - // TODO check in a future review - aux1: meta.advice_column_in(SecondPhase), - aux2: meta.advice_column_in(SecondPhase), - } - } - fn assign( - &self, - region: &mut Region<'_, F>, - offset: usize, - row: &RwRow>, - ) -> Result<(), Error> { - for (column, value) in [ - (self.rw_counter, row.rw_counter), - (self.is_write, row.is_write), - (self.tag, row.tag), - (self.id, row.id), - (self.address, row.address), - (self.field_tag, row.field_tag), - (self.storage_key, row.storage_key), - (self.value, row.value), - (self.value_prev, row.value_prev), - (self.aux1, row.aux1), - (self.aux2, row.aux2), - ] { - region.assign_advice(|| "assign rw row on rw table", column, offset, || value)?; - } - Ok(()) - } - - /// Assign the `RwTable` from a `RwMap`, following the same - /// table layout that the State Circuit uses. - pub fn load( - &self, - layouter: &mut impl Layouter, - rws: &[Rw], - n_rows: usize, - challenges: Value, - ) -> Result<(), Error> { - layouter.assign_region( - || "rw table", - |mut region| self.load_with_region(&mut region, rws, n_rows, challenges), - ) - } - - pub(crate) fn load_with_region( - &self, - region: &mut Region<'_, F>, - rws: &[Rw], - n_rows: usize, - challenges: Value, - ) -> Result<(), Error> { - let (rows, _) = RwMap::table_assignments_prepad(rws, n_rows); - for (offset, row) in rows.iter().enumerate() { - self.assign(region, offset, &row.table_assignment(challenges))?; - } - Ok(()) - } -} - -/// The types of proofs in the MPT table -#[derive(Clone, Copy, Debug)] -pub enum MPTProofType { - /// Disabled - Disabled, - /// Nonce updated - NonceChanged = AccountFieldTag::Nonce as isize, - /// Balance updated - BalanceChanged = AccountFieldTag::Balance as isize, - /// Code hash exists - CodeHashExists = AccountFieldTag::CodeHash as isize, - /// Account destroyed - AccountDestructed, - /// Account does not exist - AccountDoesNotExist, - /// Storage updated - StorageChanged, - /// Storage does not exist - StorageDoesNotExist, -} -impl_expr!(MPTProofType); - -impl From for MPTProofType { - fn from(tag: AccountFieldTag) -> Self { - match tag { - AccountFieldTag::Nonce => Self::NonceChanged, - AccountFieldTag::Balance => Self::BalanceChanged, - AccountFieldTag::CodeHash => Self::CodeHashExists, - AccountFieldTag::NonExisting => Self::AccountDoesNotExist, - } - } -} - -/// The MptTable shared between MPT Circuit and State Circuit -#[derive(Clone, Copy, Debug)] -pub struct MptTable { - /// Account address - pub address_rlc: Column, - /// Proof type - pub proof_type: Column, - /// Storage address - pub key_rlc: Column, - /// Old value - pub value_prev: Column, - /// New value - pub value: Column, - /// Previous MPT root - pub root_prev: Column, - /// New MPT root - pub root: Column, -} - -impl LookupTable for MptTable { - fn columns(&self) -> Vec> { - vec![ - self.address_rlc, - self.proof_type, - self.key_rlc, - self.value_prev, - self.value, - self.root_prev, - self.root, - ] - .into_iter() - .map(|col| col.into()) - .collect::>>() - } - - fn annotations(&self) -> Vec { - vec![ - String::from("address"), - String::from("proof_type"), - String::from("storage_key"), - String::from("old_value"), - String::from("new_value"), - String::from("old_root"), - String::from("new_root"), - ] - } -} - -impl MptTable { - /// Construct a new MptTable - pub(crate) fn construct(meta: &mut ConstraintSystem) -> Self { - // TODO(Brecht): everything except address and proof type needs to be - // advice_column_in(SecondPhase) - Self { - address_rlc: meta.advice_column(), - proof_type: meta.advice_column(), - key_rlc: meta.advice_column(), - value_prev: meta.advice_column(), - value: meta.advice_column(), - root_prev: meta.advice_column(), - root: meta.advice_column(), - } - } - - pub(crate) fn constrain( - &self, - meta: &mut VirtualCells<'_, F>, - cb: &mut ConstraintBuilder, - address_rlc: Expression, - proof_type: Expression, - key_rlc: Expression, - value_prev: Expression, - value: Expression, - root_prev: Expression, - root: Expression, - ) { - circuit!([meta, cb], { - require!(a!(self.proof_type) => proof_type); - require!(a!(self.address_rlc) => address_rlc); - require!(a!(self.key_rlc) => key_rlc); - require!(a!(self.value_prev) => value_prev); - require!(a!(self.value) => value); - require!(a!(self.root_prev) => root_prev); - require!(a!(self.root) => root); - }) - } - - pub(crate) fn assign( - &self, - region: &mut Region<'_, F>, - offset: usize, - row: &MptUpdateRow>, - ) -> Result<(), Error> { - for (column, value) in >::advice_columns(self) - .iter() - .zip_eq(row.values()) - { - region.assign_advice(|| "assign mpt table row value", *column, offset, || value)?; - } - Ok(()) - } - - pub(crate) fn load( - &self, - layouter: &mut impl Layouter, - updates: &MptUpdates, - randomness: Value, - ) -> Result<(), Error> { - layouter.assign_region( - || "mpt table", - |mut region| self.load_with_region(&mut region, updates, randomness), - ) - } - - pub(crate) fn load_with_region( - &self, - region: &mut Region<'_, F>, - updates: &MptUpdates, - randomness: Value, - ) -> Result<(), Error> { - for (offset, row) in updates.table_assignments(randomness).iter().enumerate() { - self.assign(region, offset, row)?; - } - Ok(()) - } -} - -/// Tag to identify the field in a Bytecode Table row -#[derive(Clone, Copy, Debug)] -pub enum BytecodeFieldTag { - /// Header field - Header, - /// Byte field - Byte, -} -impl_expr!(BytecodeFieldTag); - -/// Table with Bytecode indexed by its Code Hash -#[derive(Clone, Debug)] -pub struct BytecodeTable { - /// Code Hash - pub code_hash: Column, - /// Tag - pub tag: Column, - /// Index - pub index: Column, - /// Is Code is true when the byte is not an argument to a PUSH* instruction. - pub is_code: Column, - /// Value - pub value: Column, -} - -impl BytecodeTable { - /// Construct a new BytecodeTable - pub fn construct(meta: &mut ConstraintSystem) -> Self { - let [tag, index, is_code, value] = array::from_fn(|_| meta.advice_column()); - let code_hash = meta.advice_column_in(SecondPhase); - Self { - code_hash, - tag, - index, - is_code, - value, - } - } - - /// Assign the `BytecodeTable` from a list of bytecodes, followig the same - /// table layout that the Bytecode Circuit uses. - pub fn load<'a, F: Field>( - &self, - layouter: &mut impl Layouter, - bytecodes: impl IntoIterator + Clone, - challenges: &Challenges>, - ) -> Result<(), Error> { - layouter.assign_region( - || "bytecode table", - |mut region| { - let mut offset = 0; - for column in >::advice_columns(self) { - region.assign_advice( - || "bytecode table all-zero row", - column, - offset, - || Value::known(F::zero()), - )?; - } - offset += 1; - - let bytecode_table_columns = - >::advice_columns(self); - for bytecode in bytecodes.clone() { - for row in bytecode.table_assignments(challenges) { - for (&column, value) in bytecode_table_columns.iter().zip_eq(row) { - region.assign_advice( - || format!("bytecode table row {}", offset), - column, - offset, - || value, - )?; - } - offset += 1; - } - } - Ok(()) - }, - ) - } -} - -impl LookupTable for BytecodeTable { - fn columns(&self) -> Vec> { - vec![ - self.code_hash.into(), - self.tag.into(), - self.index.into(), - self.is_code.into(), - self.value.into(), - ] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("code_hash"), - String::from("tag"), - String::from("index"), - String::from("is_code"), - String::from("value"), - ] - } -} - -/// Tag to identify the field in a Block Table row -// Keep the sequence consistent with OpcodeId for scalar -#[derive(Clone, Copy, Debug)] -pub enum BlockContextFieldTag { - /// Coinbase field - Coinbase = 1, - /// Timestamp field - Timestamp, - /// Number field - Number, - /// Difficulty field - Difficulty, - /// Gas Limit field - GasLimit, - /// Base Fee field - BaseFee = 8, - /// Block Hash field - BlockHash, - /// Chain ID field. Although this is not a field in the block header, we - /// add it here for convenience. - ChainId, -} -impl_expr!(BlockContextFieldTag); - -/// Table with Block header fields -#[derive(Clone, Debug)] -pub struct BlockTable { - /// Tag - pub tag: Column, - /// Index - pub index: Column, - /// Value - pub value: Column, -} - -impl BlockTable { - /// Construct a new BlockTable - pub fn construct(meta: &mut ConstraintSystem) -> Self { - Self { - tag: meta.advice_column(), - index: meta.advice_column(), - value: meta.advice_column_in(SecondPhase), - } - } - - /// Assign the `BlockTable` from a `BlockContext`. - pub fn load( - &self, - layouter: &mut impl Layouter, - block: &BlockContext, - randomness: Value, - ) -> Result<(), Error> { - layouter.assign_region( - || "block table", - |mut region| { - let mut offset = 0; - for column in >::advice_columns(self) { - region.assign_advice( - || "block table all-zero row", - column, - offset, - || Value::known(F::zero()), - )?; - } - offset += 1; - - let block_table_columns = >::advice_columns(self); - for row in block.table_assignments(randomness) { - for (&column, value) in block_table_columns.iter().zip_eq(row) { - region.assign_advice( - || format!("block table row {}", offset), - column, - offset, - || value, - )?; - } - offset += 1; - } - - Ok(()) - }, - ) - } -} - -impl LookupTable for BlockTable { - fn columns(&self) -> Vec> { - vec![self.tag.into(), self.index.into(), self.value.into()] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("tag"), - String::from("index"), - String::from("value"), - ] - } -} - -/// Keccak Table, used to verify keccak hashing from RLC'ed input. -#[derive(Clone, Debug)] -pub struct KeccakTable { - /// True when the row is enabled - pub is_enabled: Column, - /// Byte array input as `RLC(reversed(input))` - pub input_rlc: Column, // RLC of input bytes - /// Byte array input length - pub input_len: Column, - /// RLC of the hash result - pub output_rlc: Column, // RLC of hash of input bytes -} - -impl LookupTable for KeccakTable { - fn columns(&self) -> Vec> { - vec![ - self.is_enabled.into(), - self.input_rlc.into(), - self.input_len.into(), - self.output_rlc.into(), - ] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("is_enabled"), - String::from("input_rlc"), - String::from("input_len"), - String::from("output_rlc"), - ] - } -} - -impl KeccakTable { - /// Construct a new KeccakTable - pub fn construct(meta: &mut ConstraintSystem) -> Self { - Self { - is_enabled: meta.advice_column(), - input_rlc: meta.advice_column_in(SecondPhase), - input_len: meta.advice_column(), - output_rlc: meta.advice_column_in(SecondPhase), - } - } - - /// Generate the keccak table assignments from a byte array input. - pub fn assignments( - input: &[u8], - challenges: &Challenges>, - is_big_endian: bool, - ) -> Vec<[Value; 4]> { - let bytes = if is_big_endian { - input.iter().cloned().rev().collect::>() - } else { - input.to_owned() - }; - let input_rlc = challenges - .keccak_input() - .map(|challenge| rlc::value(&bytes, challenge)); - let input_len = F::from(input.len() as u64); - let mut keccak = Keccak::default(); - keccak.update(input); - let output = keccak.digest(); - // println!("keccak hash bytes: {:?}", - // Word::from_big_endian(output.as_slice()).to_le_bytes()); - let output_rlc = challenges.evm_word().map(|challenge| { - // println!("keccak output challenge: {:?}", challenge); - rlc::value( - &if is_big_endian { - Word::from_big_endian(output.as_slice()) - } else { - Word::from_little_endian(output.as_slice()) - } - .to_le_bytes(), - challenge, - ) - }); - - vec![[ - Value::known(F::one()), - input_rlc, - Value::known(input_len), - output_rlc, - ]] - } - - /// Assign a table row for keccak table - pub fn assign_row( - &self, - region: &mut Region, - offset: usize, - values: [Value; 4], - ) -> Result<(), Error> { - for (&column, value) in >::advice_columns(self) - .iter() - .zip(values.iter()) - { - region.assign_advice(|| format!("assign {}", offset), column, offset, || *value)?; - } - Ok(()) - } - - /// Provide this function for the case that we want to consume a keccak - /// table but without running the full keccak circuit - pub fn dev_load<'a, F: Field>( - &self, - layouter: &mut impl Layouter, - inputs: impl IntoIterator> + Clone, - challenges: &Challenges>, - is_big_endian: bool, - ) -> Result<(), Error> { - layouter.assign_region( - || "keccak table", - |mut region| { - let mut offset = 0; - for column in >::advice_columns(self) { - region.assign_advice( - || "keccak table all-zero row", - column, - offset, - || Value::known(F::zero()), - )?; - } - offset += 1; - - let keccak_table_columns = >::advice_columns(self); - for input in inputs.clone() { - for row in Self::assignments(input, challenges, is_big_endian) { - // let mut column_index = 0; - for (&column, value) in keccak_table_columns.iter().zip_eq(row) { - region.assign_advice( - || format!("keccak table row {}", offset), - column, - offset, - || value, - )?; - // println!("keccak: {:?}", value); - } - offset += 1; - } - } - Ok(()) - }, - ) - } - - /// returns matchings between the circuit columns passed as parameters and - /// the table collumns - pub fn match_columns( - &self, - value_rlc: Column, - length: Column, - code_hash: Column, - ) -> Vec<(Column, Column)> { - vec![ - (value_rlc, self.input_rlc), - (length, self.input_len), - (code_hash, self.output_rlc), - ] - } -} - -/// Copy Table, used to verify copies of byte chunks between Memory, Bytecode, -/// TxLogs and TxCallData. -#[derive(Clone, Copy, Debug)] -pub struct CopyTable { - /// Whether the row is the first read-write pair for a copy event. - pub is_first: Column, - /// The relevant ID for the read-write row, represented as a random linear - /// combination. The ID may be one of the below: - /// 1. Call ID/Caller ID for CopyDataType::Memory - /// 2. RLC encoding of bytecode hash for CopyDataType::Bytecode - /// 3. Transaction ID for CopyDataType::TxCalldata, CopyDataType::TxLog - pub id: Column, - /// The source/destination address for this copy step. Can be memory - /// address, byte index in the bytecode, tx call data, and tx log data. - pub addr: Column, - /// The end of the source buffer for the copy event. Any data read from an - /// address greater than or equal to this value will be 0. - pub src_addr_end: Column, - /// The number of bytes left to be copied. - pub bytes_left: Column, - /// An accumulator value in the RLC representation. This is used for - /// specific purposes, for instance, when `tag == CopyDataType::RlcAcc`. - /// Having an additional column for the `rlc_acc` simplifies the lookup - /// to copy table. - pub rlc_acc: Column, - /// The associated read-write counter for this row. - pub rw_counter: Column, - /// Decrementing counter denoting reverse read-write counter. - pub rwc_inc_left: Column, - /// Binary chip to constrain the copy table conditionally depending on the - /// current row's tag, whether it is Bytecode, Memory, TxCalldata or - /// TxLog. - pub tag: BinaryNumberConfig, -} - -type CopyTableRow = [(Value, &'static str); 8]; -type CopyCircuitRow = [(Value, &'static str); 4]; - -impl CopyTable { - /// Construct a new CopyTable - pub fn construct(meta: &mut ConstraintSystem, q_enable: Column) -> Self { - Self { - is_first: meta.advice_column(), - id: meta.advice_column_in(SecondPhase), - tag: BinaryNumberChip::configure(meta, q_enable, None), - addr: meta.advice_column(), - src_addr_end: meta.advice_column(), - bytes_left: meta.advice_column(), - rlc_acc: meta.advice_column_in(SecondPhase), - rw_counter: meta.advice_column(), - rwc_inc_left: meta.advice_column(), - } - } - - /// Generate the copy table and copy circuit assignments from a copy event. - pub fn assignments( - copy_event: &CopyEvent, - challenges: Challenges>, - ) -> Vec<(CopyDataType, CopyTableRow, CopyCircuitRow)> { - let mut assignments = Vec::new(); - // rlc_acc - let rlc_acc = if copy_event.dst_type == CopyDataType::RlcAcc { - let values = copy_event - .bytes - .iter() - .map(|(value, _)| *value) - .collect::>(); - challenges - .keccak_input() - .map(|keccak_input| rlc::value(values.iter().rev(), keccak_input)) - } else { - Value::known(F::zero()) - }; - let mut value_acc = Value::known(F::zero()); - for (step_idx, (is_read_step, copy_step)) in copy_event - .bytes - .iter() - .flat_map(|(value, is_code)| { - let read_step = CopyStep { - value: *value, - is_code: if copy_event.src_type == CopyDataType::Bytecode { - Some(*is_code) - } else { - None - }, - }; - let write_step = CopyStep { - value: *value, - is_code: if copy_event.dst_type == CopyDataType::Bytecode { - Some(*is_code) - } else { - None - }, - }; - once((true, read_step)).chain(once((false, write_step))) - }) - .enumerate() - { - // is_first - let is_first = Value::known(if step_idx == 0 { F::one() } else { F::zero() }); - // is last - let is_last = if step_idx == copy_event.bytes.len() * 2 - 1 { - Value::known(F::one()) - } else { - Value::known(F::zero()) - }; - - // id - let id = if is_read_step { - number_or_hash_to_field(©_event.src_id, challenges.evm_word()) - } else { - number_or_hash_to_field(©_event.dst_id, challenges.evm_word()) - }; - - // tag binary bumber chip - let tag = if is_read_step { - copy_event.src_type - } else { - copy_event.dst_type - }; - - // addr - let copy_step_addr: u64 = - if is_read_step { - copy_event.src_addr - } else { - copy_event.dst_addr - } + (u64::try_from(step_idx).unwrap() - if is_read_step { 0 } else { 1 }) / 2u64; - - let addr = if tag == CopyDataType::TxLog { - Value::known( - build_tx_log_address( - copy_step_addr, - TxLogFieldTag::Data, - copy_event.log_id.unwrap(), - ) - .to_scalar() - .unwrap(), - ) - } else { - Value::known(F::from(copy_step_addr)) - }; - - // bytes_left - let bytes_left = u64::try_from(copy_event.bytes.len() * 2 - step_idx).unwrap() / 2; - // value - let value = if copy_event.dst_type == CopyDataType::RlcAcc { - if is_read_step { - Value::known(F::from(copy_step.value as u64)) - } else { - value_acc = value_acc * challenges.keccak_input() - + Value::known(F::from(copy_step.value as u64)); - value_acc - } - } else { - Value::known(F::from(copy_step.value as u64)) - }; - // is_pad - let is_pad = Value::known(F::from( - is_read_step && copy_step_addr >= copy_event.src_addr_end, - )); - - // is_code - let is_code = Value::known(copy_step.is_code.map_or(F::zero(), |v| F::from(v))); - - assignments.push(( - tag, - [ - (is_first, "is_first"), - (id, "id"), - (addr, "addr"), - ( - Value::known(F::from(copy_event.src_addr_end)), - "src_addr_end", - ), - (Value::known(F::from(bytes_left)), "bytes_left"), - (rlc_acc, "rlc_acc"), - ( - Value::known(F::from(copy_event.rw_counter(step_idx))), - "rw_counter", - ), - ( - Value::known(F::from(copy_event.rw_counter_increase_left(step_idx))), - "rwc_inc_left", - ), - ], - [ - (is_last, "is_last"), - (value, "value"), - (is_pad, "is_pad"), - (is_code, "is_code"), - ], - )); - } - assignments - } - - /// Assign the `CopyTable` from a `Block`. - pub fn load( - &self, - layouter: &mut impl Layouter, - block: &Block, - challenges: &Challenges>, - ) -> Result<(), Error> { - layouter.assign_region( - || "copy table", - |mut region| { - let mut offset = 0; - for column in >::advice_columns(self) { - region.assign_advice( - || "copy table all-zero row", - column, - offset, - || Value::known(F::zero()), - )?; - } - offset += 1; - - let tag_chip = BinaryNumberChip::construct(self.tag); - let copy_table_columns = >::advice_columns(self); - for copy_event in block.copy_events.iter() { - for (tag, row, _) in Self::assignments(copy_event, *challenges) { - for (&column, (value, label)) in copy_table_columns.iter().zip_eq(row) { - region.assign_advice( - || format!("{} at row: {}", label, offset), - column, - offset, - || value, - )?; - } - tag_chip.assign(&mut region, offset, &tag)?; - offset += 1; - } - } - - Ok(()) - }, - ) - } -} - -impl LookupTable for CopyTable { - fn columns(&self) -> Vec> { - vec![ - self.is_first.into(), - self.id.into(), - self.addr.into(), - self.src_addr_end.into(), - self.bytes_left.into(), - self.rlc_acc.into(), - self.rw_counter.into(), - self.rwc_inc_left.into(), - ] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("is_first"), - String::from("id"), - String::from("addr"), - String::from("src_addr_end"), - String::from("bytes_left"), - String::from("rlc_acc"), - String::from("rw_counter"), - String::from("rwc_inc_left"), - ] - } - - fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { - vec![ - meta.query_advice(self.is_first, Rotation::cur()), - meta.query_advice(self.id, Rotation::cur()), // src_id - self.tag.value(Rotation::cur())(meta), // src_tag - meta.query_advice(self.id, Rotation::next()), // dst_id - self.tag.value(Rotation::next())(meta), // dst_tag - meta.query_advice(self.addr, Rotation::cur()), // src_addr - meta.query_advice(self.src_addr_end, Rotation::cur()), // src_addr_end - meta.query_advice(self.addr, Rotation::next()), // dst_addr - meta.query_advice(self.bytes_left, Rotation::cur()), // length - meta.query_advice(self.rlc_acc, Rotation::cur()), // rlc_acc - meta.query_advice(self.rw_counter, Rotation::cur()), // rw_counter - meta.query_advice(self.rwc_inc_left, Rotation::cur()), // rwc_inc_left - ] - } -} - -/// Lookup table within the Exponentiation circuit. -#[derive(Clone, Copy, Debug)] -pub struct ExpTable { - /// Whether the row is the start of a step. - pub is_step: Column, - /// An identifier for every exponentiation trace, at the moment this is the - /// read-write counter at the time of the lookups done to the - /// exponentiation table. - pub identifier: Column, - /// Whether this row is the last row in the exponentiation operation's - /// trace. - pub is_last: Column, - /// The integer base of the exponentiation. - pub base_limb: Column, - /// The integer exponent of the exponentiation. - pub exponent_lo_hi: Column, - /// The intermediate result of exponentiation by squaring. - pub exponentiation_lo_hi: Column, -} - -impl ExpTable { - /// Construct the Exponentiation table. - pub fn construct(meta: &mut ConstraintSystem) -> Self { - Self { - is_step: meta.fixed_column(), - identifier: meta.advice_column(), - is_last: meta.advice_column(), - base_limb: meta.advice_column(), - exponent_lo_hi: meta.advice_column(), - exponentiation_lo_hi: meta.advice_column(), - } - } - - /// Given an exponentiation event and randomness, get assignments to the - /// exponentiation table. - pub fn assignments(exp_event: &ExpEvent) -> Vec<[F; 5]> { - let mut assignments = Vec::new(); - let base_limbs = split_u256_limb64(&exp_event.base); - let identifier = F::from(exp_event.identifier as u64); - let mut exponent = exp_event.exponent; - for (step_idx, exp_step) in exp_event.steps.iter().rev().enumerate() { - let is_last = if step_idx == exp_event.steps.len() - 1 { - F::one() - } else { - F::zero() - }; - let (exp_lo, exp_hi) = split_u256(&exp_step.d); - let (exponent_lo, exponent_hi) = split_u256(&exponent); - - // row 1 - assignments.push([ - identifier, - is_last, - base_limbs[0].as_u64().into(), - exponent_lo - .to_scalar() - .expect("exponent should fit to scalar"), - exp_lo - .to_scalar() - .expect("exponentiation lo should fit to scalar"), - ]); - // row 2 - assignments.push([ - identifier, - F::zero(), - base_limbs[1].as_u64().into(), - exponent_hi - .to_scalar() - .expect("exponent hi should fit to scalar"), - exp_hi - .to_scalar() - .expect("exponentiation hi should fit to scalar"), - ]); - // row 3 - assignments.push([ - identifier, - F::zero(), - base_limbs[2].as_u64().into(), - F::zero(), - F::zero(), - ]); - // row 4 - assignments.push([ - identifier, - F::zero(), - base_limbs[3].as_u64().into(), - F::zero(), - F::zero(), - ]); - for _ in ROWS_PER_STEP..OFFSET_INCREMENT { - assignments.push([F::zero(), F::zero(), F::zero(), F::zero(), F::zero()]); - } - - // update intermediate exponent. - let (exponent_div2, remainder) = exponent.div_mod(U256::from(2)); - if remainder.is_zero() { - // exponent is even - exponent = exponent_div2; - } else { - // exponent is odd - exponent = exponent - 1; - } - } - assignments - } - - /// Assign witness data from a block to the exponentiation table. - pub fn load( - &self, - layouter: &mut impl Layouter, - block: &Block, - ) -> Result<(), Error> { - layouter.assign_region( - || "exponentiation table", - |mut region| { - let mut offset = 0; - let exp_table_columns = >::advice_columns(self); - for exp_event in block.exp_events.iter() { - for row in Self::assignments::(exp_event) { - for (&column, value) in exp_table_columns.iter().zip_eq(row) { - region.assign_advice( - || format!("exponentiation table row {}", offset), - column, - offset, - || Value::known(value), - )?; - } - let is_step = if offset % OFFSET_INCREMENT == 0 { - F::one() - } else { - F::zero() - }; - region.assign_fixed( - || format!("exponentiation table row {}", offset), - self.is_step, - offset, - || Value::known(is_step), - )?; - offset += 1; - } - } - - // pad an empty row - let row = [F::from_u128(0); 5]; - for (column, value) in exp_table_columns.iter().zip_eq(row) { - region.assign_advice( - || format!("exponentiation table row {}", offset), - *column, - offset, - || Value::known(value), - )?; - } - - Ok(()) - }, - ) - } -} - -impl LookupTable for ExpTable { - fn columns(&self) -> Vec> { - vec![ - self.is_step.into(), - self.identifier.into(), - self.is_last.into(), - self.base_limb.into(), - self.exponent_lo_hi.into(), - self.exponentiation_lo_hi.into(), - ] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("is_step"), - String::from("identifier"), - String::from("is_last"), - String::from("base_limb"), - String::from("exponent_lo_hi"), - String::from("exponentiation_lo_hi"), - ] - } - - fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { - vec![ - meta.query_fixed(self.is_step, Rotation::cur()), - meta.query_advice(self.identifier, Rotation::cur()), - meta.query_advice(self.is_last, Rotation::cur()), - meta.query_advice(self.base_limb, Rotation::cur()), - meta.query_advice(self.base_limb, Rotation::next()), - meta.query_advice(self.base_limb, Rotation(2)), - meta.query_advice(self.base_limb, Rotation(3)), - meta.query_advice(self.exponent_lo_hi, Rotation::cur()), - meta.query_advice(self.exponent_lo_hi, Rotation::next()), - meta.query_advice(self.exponentiation_lo_hi, Rotation::cur()), - meta.query_advice(self.exponentiation_lo_hi, Rotation::next()), - ] - } -} diff --git a/zkevm-circuits/src/table/block_table.rs b/zkevm-circuits/src/table/block_table.rs new file mode 100644 index 0000000000..443e993673 --- /dev/null +++ b/zkevm-circuits/src/table/block_table.rs @@ -0,0 +1,100 @@ +use super::*; + +/// Tag to identify the field in a Block Table row +// Keep the sequence consistent with OpcodeId for scalar +#[derive(Clone, Copy, Debug)] +pub enum BlockContextFieldTag { + /// Coinbase field + Coinbase = 1, + /// Timestamp field + Timestamp, + /// Number field + Number, + /// Difficulty field + Difficulty, + /// Gas Limit field + GasLimit, + /// Base Fee field + BaseFee = 8, + /// Block Hash field + BlockHash, + /// Chain ID field. Although this is not a field in the block header, we + /// add it here for convenience. + ChainId, +} +impl_expr!(BlockContextFieldTag); + +/// Table with Block header fields +#[derive(Clone, Debug)] +pub struct BlockTable { + /// Tag + pub tag: Column, + /// Index + pub index: Column, + /// Value + pub value: Column, +} + +impl BlockTable { + /// Construct a new BlockTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + tag: meta.advice_column(), + index: meta.advice_column(), + value: meta.advice_column_in(SecondPhase), + } + } + + /// Assign the `BlockTable` from a `BlockContext`. + pub fn load( + &self, + layouter: &mut impl Layouter, + block: &BlockContext, + randomness: Value, + ) -> Result<(), Error> { + layouter.assign_region( + || "block table", + |mut region| { + let mut offset = 0; + for column in >::advice_columns(self) { + region.assign_advice( + || "block table all-zero row", + column, + offset, + || Value::known(F::ZERO), + )?; + } + offset += 1; + + let block_table_columns = >::advice_columns(self); + for row in block.table_assignments(randomness) { + for (&column, value) in block_table_columns.iter().zip_eq(row) { + region.assign_advice( + || format!("block table row {}", offset), + column, + offset, + || value, + )?; + } + offset += 1; + } + + Ok(()) + }, + ) + } +} + +impl LookupTable for BlockTable { + fn columns(&self) -> Vec> { + vec![self.tag.into(), self.index.into(), self.value.into()] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("tag"), + String::from("index"), + String::from("value"), + ] + } +} diff --git a/zkevm-circuits/src/table/bytecode_table.rs b/zkevm-circuits/src/table/bytecode_table.rs new file mode 100644 index 0000000000..2b6a8a5d27 --- /dev/null +++ b/zkevm-circuits/src/table/bytecode_table.rs @@ -0,0 +1,105 @@ +use super::*; + +/// Tag to identify the field in a Bytecode Table row +#[derive(Clone, Copy, Debug)] +pub enum BytecodeFieldTag { + /// Header field + Header, + /// Byte field + Byte, +} +impl_expr!(BytecodeFieldTag); + +/// Table with Bytecode indexed by its Code Hash +#[derive(Clone, Debug)] +pub struct BytecodeTable { + /// Code Hash + pub code_hash: Column, + /// Tag + pub tag: Column, + /// Index + pub index: Column, + /// Is Code is true when the byte is not an argument to a PUSH* instruction. + pub is_code: Column, + /// Value + pub value: Column, +} + +impl BytecodeTable { + /// Construct a new BytecodeTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + let [tag, index, is_code, value] = array::from_fn(|_| meta.advice_column()); + let code_hash = meta.advice_column_in(SecondPhase); + Self { + code_hash, + tag, + index, + is_code, + value, + } + } + + /// Assign the `BytecodeTable` from a list of bytecodes, followig the same + /// table layout that the Bytecode Circuit uses. + pub fn load<'a, F: Field>( + &self, + layouter: &mut impl Layouter, + bytecodes: impl IntoIterator + Clone, + challenges: &Challenges>, + ) -> Result<(), Error> { + layouter.assign_region( + || "bytecode table", + |mut region| { + let mut offset = 0; + for column in >::advice_columns(self) { + region.assign_advice( + || "bytecode table all-zero row", + column, + offset, + || Value::known(F::ZERO), + )?; + } + offset += 1; + + let bytecode_table_columns = + >::advice_columns(self); + for bytecode in bytecodes.clone() { + for row in bytecode.table_assignments(challenges) { + for (&column, value) in bytecode_table_columns.iter().zip_eq(row) { + region.assign_advice( + || format!("bytecode table row {}", offset), + column, + offset, + || value, + )?; + } + offset += 1; + } + } + Ok(()) + }, + ) + } +} + +impl LookupTable for BytecodeTable { + fn columns(&self) -> Vec> { + vec![ + self.code_hash.into(), + self.tag.into(), + self.index.into(), + self.is_code.into(), + self.value.into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("code_hash"), + String::from("tag"), + String::from("index"), + String::from("is_code"), + String::from("value"), + ] + } +} diff --git a/zkevm-circuits/src/table/copy_table.rs b/zkevm-circuits/src/table/copy_table.rs new file mode 100644 index 0000000000..a48761229d --- /dev/null +++ b/zkevm-circuits/src/table/copy_table.rs @@ -0,0 +1,287 @@ +use super::*; + +type CopyTableRow = [(Value, &'static str); 8]; +type CopyCircuitRow = [(Value, &'static str); 5]; + +/// Copy Table, used to verify copies of byte chunks between Memory, Bytecode, +/// TxLogs and TxCallData. +#[derive(Clone, Copy, Debug)] +pub struct CopyTable { + /// Whether the row is the first read-write pair for a copy event. + pub is_first: Column, + /// The relevant ID for the read-write row, represented as a random linear + /// combination. The ID may be one of the below: + /// 1. Call ID/Caller ID for CopyDataType::Memory + /// 2. RLC encoding of bytecode hash for CopyDataType::Bytecode + /// 3. Transaction ID for CopyDataType::TxCalldata, CopyDataType::TxLog + pub id: Column, + /// The source/destination address for this copy step. Can be memory + /// address, byte index in the bytecode, tx call data, and tx log data. + pub addr: Column, + /// The end of the source buffer for the copy event. Any data read from an + /// address greater than or equal to this value will be 0. + pub src_addr_end: Column, + /// The number of bytes left to be copied. + pub bytes_left: Column, + /// An accumulator value in the RLC representation. This is used for + /// specific purposes, for instance, when `tag == CopyDataType::RlcAcc`. + /// Having an additional column for the `rlc_acc` simplifies the lookup + /// to copy table. + pub rlc_acc: Column, + /// The associated read-write counter for this row. + pub rw_counter: Column, + /// Decrementing counter denoting reverse read-write counter. + pub rwc_inc_left: Column, + /// Binary chip to constrain the copy table conditionally depending on the + /// current row's tag, whether it is Bytecode, Memory, TxCalldata or + /// TxLog. + pub tag: BinaryNumberConfig, +} + +impl CopyTable { + /// Construct a new CopyTable + pub fn construct(meta: &mut ConstraintSystem, q_enable: Column) -> Self { + Self { + is_first: meta.advice_column(), + id: meta.advice_column_in(SecondPhase), + tag: BinaryNumberChip::configure(meta, q_enable, None), + addr: meta.advice_column(), + src_addr_end: meta.advice_column(), + bytes_left: meta.advice_column(), + rlc_acc: meta.advice_column_in(SecondPhase), + rw_counter: meta.advice_column(), + rwc_inc_left: meta.advice_column(), + } + } + + /// Generate the copy table and copy circuit assignments from a copy event. + pub fn assignments( + copy_event: &CopyEvent, + challenges: Challenges>, + ) -> Vec<(CopyDataType, CopyTableRow, CopyCircuitRow)> { + let mut assignments = Vec::new(); + // rlc_acc + let rlc_acc = { + let values = copy_event + .bytes + .iter() + .map(|(value, _)| *value) + .collect::>(); + challenges + .keccak_input() + .map(|keccak_input| rlc::value(values.iter().rev(), keccak_input)) + }; + let mut value_acc = Value::known(F::ZERO); + for (step_idx, (is_read_step, copy_step)) in copy_event + .bytes + .iter() + .flat_map(|(value, is_code)| { + let read_step = CopyStep { + value: *value, + is_code: if copy_event.src_type == CopyDataType::Bytecode { + Some(*is_code) + } else { + None + }, + }; + let write_step = CopyStep { + value: *value, + is_code: if copy_event.dst_type == CopyDataType::Bytecode { + Some(*is_code) + } else { + None + }, + }; + once((true, read_step)).chain(once((false, write_step))) + }) + .enumerate() + { + // is_first + let is_first = Value::known(if step_idx == 0 { F::ONE } else { F::ZERO }); + // is last + let is_last = if step_idx == copy_event.bytes.len() * 2 - 1 { + Value::known(F::ONE) + } else { + Value::known(F::ZERO) + }; + + // id + let id = if is_read_step { + number_or_hash_to_field(©_event.src_id, challenges.evm_word()) + } else { + number_or_hash_to_field(©_event.dst_id, challenges.evm_word()) + }; + + // tag binary bumber chip + let tag = if is_read_step { + copy_event.src_type + } else { + copy_event.dst_type + }; + + // addr + let copy_step_addr: u64 = + if is_read_step { + copy_event.src_addr + } else { + copy_event.dst_addr + } + (u64::try_from(step_idx).unwrap() - if is_read_step { 0 } else { 1 }) / 2u64; + + let addr = if tag == CopyDataType::TxLog { + Value::known( + build_tx_log_address( + copy_step_addr, + TxLogFieldTag::Data, + copy_event.log_id.unwrap(), + ) + .to_scalar() + .unwrap(), + ) + } else { + Value::known(F::from(copy_step_addr)) + }; + + // bytes_left + let bytes_left = u64::try_from(copy_event.bytes.len() * 2 - step_idx).unwrap() / 2; + // value + let value = Value::known(F::from(copy_step.value as u64)); + // value_acc + if is_read_step { + value_acc = value_acc * challenges.keccak_input() + value; + } + // is_pad + let is_pad = Value::known(F::from( + (is_read_step && copy_step_addr >= copy_event.src_addr_end) as u64, + )); + + // is_code + let is_code = Value::known(copy_step.is_code.map_or(F::ZERO, |v| F::from(v as u64))); + + assignments.push(( + tag, + [ + (is_first, "is_first"), + (id, "id"), + (addr, "addr"), + ( + Value::known(F::from(copy_event.src_addr_end)), + "src_addr_end", + ), + (Value::known(F::from(bytes_left)), "bytes_left"), + ( + match (copy_event.src_type, copy_event.dst_type) { + (CopyDataType::Memory, CopyDataType::Bytecode) => rlc_acc, + (_, CopyDataType::RlcAcc) => rlc_acc, + _ => Value::known(F::ZERO), + }, + "rlc_acc", + ), + ( + Value::known(F::from(copy_event.rw_counter(step_idx))), + "rw_counter", + ), + ( + Value::known(F::from(copy_event.rw_counter_increase_left(step_idx))), + "rwc_inc_left", + ), + ], + [ + (is_last, "is_last"), + (value, "value"), + (value_acc, "value_acc"), + (is_pad, "is_pad"), + (is_code, "is_code"), + ], + )); + } + assignments + } + + /// Assign the `CopyTable` from a `Block`. + pub fn load( + &self, + layouter: &mut impl Layouter, + block: &Block, + challenges: &Challenges>, + ) -> Result<(), Error> { + layouter.assign_region( + || "copy table", + |mut region| { + let mut offset = 0; + for column in >::advice_columns(self) { + region.assign_advice( + || "copy table all-zero row", + column, + offset, + || Value::known(F::ZERO), + )?; + } + offset += 1; + + let tag_chip = BinaryNumberChip::construct(self.tag); + let copy_table_columns = >::advice_columns(self); + for copy_event in block.copy_events.iter() { + for (tag, row, _) in Self::assignments(copy_event, *challenges) { + for (&column, (value, label)) in copy_table_columns.iter().zip_eq(row) { + region.assign_advice( + || format!("{} at row: {}", label, offset), + column, + offset, + || value, + )?; + } + tag_chip.assign(&mut region, offset, &tag)?; + offset += 1; + } + } + + Ok(()) + }, + ) + } +} + +impl LookupTable for CopyTable { + fn columns(&self) -> Vec> { + vec![ + self.is_first.into(), + self.id.into(), + self.addr.into(), + self.src_addr_end.into(), + self.bytes_left.into(), + self.rlc_acc.into(), + self.rw_counter.into(), + self.rwc_inc_left.into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("is_first"), + String::from("id"), + String::from("addr"), + String::from("src_addr_end"), + String::from("bytes_left"), + String::from("rlc_acc"), + String::from("rw_counter"), + String::from("rwc_inc_left"), + ] + } + + fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { + vec![ + meta.query_advice(self.is_first, Rotation::cur()), + meta.query_advice(self.id, Rotation::cur()), // src_id + self.tag.value(Rotation::cur())(meta), // src_tag + meta.query_advice(self.id, Rotation::next()), // dst_id + self.tag.value(Rotation::next())(meta), // dst_tag + meta.query_advice(self.addr, Rotation::cur()), // src_addr + meta.query_advice(self.src_addr_end, Rotation::cur()), // src_addr_end + meta.query_advice(self.addr, Rotation::next()), // dst_addr + meta.query_advice(self.bytes_left, Rotation::cur()), // length + meta.query_advice(self.rlc_acc, Rotation::cur()), // rlc_acc + meta.query_advice(self.rw_counter, Rotation::cur()), // rw_counter + meta.query_advice(self.rwc_inc_left, Rotation::cur()), // rwc_inc_left + ] + } +} diff --git a/zkevm-circuits/src/table/exp_table.rs b/zkevm-circuits/src/table/exp_table.rs new file mode 100644 index 0000000000..c2ee645e53 --- /dev/null +++ b/zkevm-circuits/src/table/exp_table.rs @@ -0,0 +1,207 @@ +use super::*; + +use crate::{ + exp_circuit::param::{OFFSET_INCREMENT, ROWS_PER_STEP}, + table::LookupTable, + witness::Block, +}; +use bus_mapping::circuit_input_builder::ExpEvent; + +/// Lookup table within the Exponentiation circuit. +#[derive(Clone, Copy, Debug)] +pub struct ExpTable { + /// Whether the row is the start of a step. + pub is_step: Column, + /// An identifier for every exponentiation trace, at the moment this is the + /// read-write counter at the time of the lookups done to the + /// exponentiation table. + pub identifier: Column, + /// Whether this row is the last row in the exponentiation operation's + /// trace. + pub is_last: Column, + /// The integer base of the exponentiation. + pub base_limb: Column, + /// The integer exponent of the exponentiation. + pub exponent_lo_hi: Column, + /// The intermediate result of exponentiation by squaring. + pub exponentiation_lo_hi: Column, +} + +impl ExpTable { + /// Construct the Exponentiation table. + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + is_step: meta.fixed_column(), + identifier: meta.advice_column(), + is_last: meta.advice_column(), + base_limb: meta.advice_column(), + exponent_lo_hi: meta.advice_column(), + exponentiation_lo_hi: meta.advice_column(), + } + } + + /// Given an exponentiation event and randomness, get assignments to the + /// exponentiation table. + pub fn assignments(exp_event: &ExpEvent) -> Vec<[F; 5]> { + let mut assignments = Vec::new(); + let base_limbs = split_u256_limb64(&exp_event.base); + let identifier = F::from(exp_event.identifier as u64); + let mut exponent = exp_event.exponent; + for (step_idx, exp_step) in exp_event.steps.iter().rev().enumerate() { + let is_last = if step_idx == exp_event.steps.len() - 1 { + F::ONE + } else { + F::ZERO + }; + let (exp_lo, exp_hi) = split_u256(&exp_step.d); + let (exponent_lo, exponent_hi) = split_u256(&exponent); + + // row 1 + assignments.push([ + identifier, + is_last, + base_limbs[0].as_u64().into(), + exponent_lo + .to_scalar() + .expect("exponent should fit to scalar"), + exp_lo + .to_scalar() + .expect("exponentiation lo should fit to scalar"), + ]); + // row 2 + assignments.push([ + identifier, + F::ZERO, + base_limbs[1].as_u64().into(), + exponent_hi + .to_scalar() + .expect("exponent hi should fit to scalar"), + exp_hi + .to_scalar() + .expect("exponentiation hi should fit to scalar"), + ]); + // row 3 + assignments.push([ + identifier, + F::ZERO, + base_limbs[2].as_u64().into(), + F::ZERO, + F::ZERO, + ]); + // row 4 + assignments.push([ + identifier, + F::ZERO, + base_limbs[3].as_u64().into(), + F::ZERO, + F::ZERO, + ]); + for _ in ROWS_PER_STEP..OFFSET_INCREMENT { + assignments.push([F::ZERO, F::ZERO, F::ZERO, F::ZERO, F::ZERO]); + } + + // update intermediate exponent. + let (exponent_div2, remainder) = exponent.div_mod(U256::from(2)); + if remainder.is_zero() { + // exponent is even + exponent = exponent_div2; + } else { + // exponent is odd + exponent = exponent - 1; + } + } + assignments + } + + /// Assign witness data from a block to the exponentiation table. + pub fn load( + &self, + layouter: &mut impl Layouter, + block: &Block, + ) -> Result<(), Error> { + layouter.assign_region( + || "exponentiation table", + |mut region| { + let mut offset = 0; + let exp_table_columns = >::advice_columns(self); + for exp_event in block.exp_events.iter() { + for row in Self::assignments::(exp_event) { + for (&column, value) in exp_table_columns.iter().zip_eq(row) { + region.assign_advice( + || format!("exponentiation table row {}", offset), + column, + offset, + || Value::known(value), + )?; + } + let is_step = if offset % OFFSET_INCREMENT == 0 { + F::ONE + } else { + F::ZERO + }; + region.assign_fixed( + || format!("exponentiation table row {}", offset), + self.is_step, + offset, + || Value::known(is_step), + )?; + offset += 1; + } + } + + // pad an empty row + let row = [F::from_u128(0); 5]; + for (column, value) in exp_table_columns.iter().zip_eq(row) { + region.assign_advice( + || format!("exponentiation table row {}", offset), + *column, + offset, + || Value::known(value), + )?; + } + + Ok(()) + }, + ) + } +} + +impl LookupTable for ExpTable { + fn columns(&self) -> Vec> { + vec![ + self.is_step.into(), + self.identifier.into(), + self.is_last.into(), + self.base_limb.into(), + self.exponent_lo_hi.into(), + self.exponentiation_lo_hi.into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("is_step"), + String::from("identifier"), + String::from("is_last"), + String::from("base_limb"), + String::from("exponent_lo_hi"), + String::from("exponentiation_lo_hi"), + ] + } + + fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { + vec![ + meta.query_fixed(self.is_step, Rotation::cur()), + meta.query_advice(self.identifier, Rotation::cur()), + meta.query_advice(self.is_last, Rotation::cur()), + meta.query_advice(self.base_limb, Rotation::cur()), + meta.query_advice(self.base_limb, Rotation::next()), + meta.query_advice(self.base_limb, Rotation(2)), + meta.query_advice(self.base_limb, Rotation(3)), + meta.query_advice(self.exponent_lo_hi, Rotation::cur()), + meta.query_advice(self.exponent_lo_hi, Rotation::next()), + meta.query_advice(self.exponentiation_lo_hi, Rotation::cur()), + meta.query_advice(self.exponentiation_lo_hi, Rotation::next()), + ] + } +} diff --git a/zkevm-circuits/src/table/keccak_table.rs b/zkevm-circuits/src/table/keccak_table.rs new file mode 100644 index 0000000000..fda652720c --- /dev/null +++ b/zkevm-circuits/src/table/keccak_table.rs @@ -0,0 +1,160 @@ +use super::*; + +/// Keccak Table, used to verify keccak hashing from RLC'ed input. +#[derive(Clone, Debug)] +pub struct KeccakTable { + /// True when the row is enabled + pub is_enabled: Column, + /// Byte array input as `RLC(reversed(input))` + pub input_rlc: Column, // RLC of input bytes + /// Byte array input length + pub input_len: Column, + /// RLC of the hash result + pub output_rlc: Column, // RLC of hash of input bytes +} + +impl LookupTable for KeccakTable { + fn columns(&self) -> Vec> { + vec![ + self.is_enabled.into(), + self.input_rlc.into(), + self.input_len.into(), + self.output_rlc.into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("is_enabled"), + String::from("input_rlc"), + String::from("input_len"), + String::from("output_rlc"), + ] + } +} + +impl KeccakTable { + /// Construct a new KeccakTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + is_enabled: meta.advice_column(), + input_rlc: meta.advice_column_in(SecondPhase), + input_len: meta.advice_column(), + output_rlc: meta.advice_column_in(SecondPhase), + } + } + + /// Generate the keccak table assignments from a byte array input. + pub fn assignments( + input: &[u8], + challenges: &Challenges>, + is_big_endian: bool, + ) -> Vec<[Value; 4]> { + // let bytes = if is_big_endian { + // input.iter().cloned().rev().collect::>() + // } else { + // input.to_owned() + // }; + let le_r = F::from(123456u64); + let input_rlc = challenges + .keccak_input() + .map(|challenge| rlc::value(input.iter().rev(), challenge /* le_r + F::ONE */)); + let input_len = F::from(input.len() as u64); + let mut keccak = Keccak::default(); + keccak.update(input); + let output = keccak.digest(); + let output_rlc = challenges.evm_word().map(|_challenge| { + rlc::value( + &if is_big_endian { + Word::from_big_endian(output.as_slice()) + } else { + Word::from_little_endian(output.as_slice()) + } + .to_le_bytes(), + // challenge + le_r, + ) + }); + + vec![[ + Value::known(F::ONE), + input_rlc, + Value::known(input_len), + output_rlc, + ]] + } + + /// Assign a table row for keccak table + pub fn assign_row( + &self, + region: &mut Region, + offset: usize, + values: [Value; 4], + ) -> Result<(), Error> { + for (&column, value) in >::advice_columns(self) + .iter() + .zip(values.iter()) + { + region.assign_advice(|| format!("assign {}", offset), column, offset, || *value)?; + } + Ok(()) + } + + /// Provide this function for the case that we want to consume a keccak + /// table but without running the full keccak circuit + pub fn dev_load<'a, F: Field>( + &self, + layouter: &mut impl Layouter, + inputs: impl IntoIterator> + Clone, + challenges: &Challenges>, + is_big_endian: bool, + ) -> Result<(), Error> { + layouter.assign_region( + || "keccak table", + |mut region| { + let mut offset = 0; + for column in >::advice_columns(self) { + region.assign_advice( + || "keccak table all-zero row", + column, + offset, + || Value::known(F::ZERO), + )?; + } + offset += 1; + + let keccak_table_columns = >::advice_columns(self); + for input in inputs.clone() { + for row in Self::assignments(input, challenges, is_big_endian) { + // let mut column_index = 0; + for (&column, value) in keccak_table_columns.iter().zip_eq(row) { + region.assign_advice( + || format!("keccak table row {}", offset), + column, + offset, + || value, + )?; + } + offset += 1; + } + } + Ok(()) + }, + ) + } + + /// returns matchings between the circuit columns passed as parameters and + /// the table collumns + pub fn match_columns( + &self, + value_rlc: Column, + length: Column, + code_hash: Column, + ) -> Vec<(Column, Column)> { + vec![ + (value_rlc, self.input_rlc), + (length, self.input_len), + (code_hash, self.output_rlc), + ] + } +} diff --git a/zkevm-circuits/src/table/mpt_table.rs b/zkevm-circuits/src/table/mpt_table.rs new file mode 100644 index 0000000000..576d890aae --- /dev/null +++ b/zkevm-circuits/src/table/mpt_table.rs @@ -0,0 +1,185 @@ +use super::*; +use crate::{ + circuit, + circuit_tools::{ + cached_region::{CachedRegion, ChallengeSet}, + cell_manager::CellType, + constraint_builder::ConstraintBuilder, + }, +}; + +/// The types of proofs in the MPT table +#[derive(Clone, Copy, Debug)] +pub enum MPTProofType { + /// Disabled + Disabled, + /// Nonce updated + NonceChanged = AccountFieldTag::Nonce as isize, + /// Balance updated + BalanceChanged = AccountFieldTag::Balance as isize, + /// Code hash exists + CodeHashExists = AccountFieldTag::CodeHash as isize, + /// Account destroyed + AccountDestructed, + /// Account does not exist + AccountDoesNotExist, + /// Storage updated + StorageChanged, + /// Storage does not exist + StorageDoesNotExist, +} +impl_expr!(MPTProofType); + +impl From for MPTProofType { + fn from(tag: AccountFieldTag) -> Self { + match tag { + AccountFieldTag::Nonce => Self::NonceChanged, + AccountFieldTag::Balance => Self::BalanceChanged, + AccountFieldTag::CodeHash => Self::CodeHashExists, + AccountFieldTag::NonExisting => Self::AccountDoesNotExist, + } + } +} + +/// The MptTable shared between MPT Circuit and State Circuit +#[derive(Clone, Copy, Debug)] +pub struct MptTable { + /// Account address + pub address_rlc: Column, + /// Proof type + pub proof_type: Column, + /// Storage address + pub key_rlc: Column, + /// Old value + pub value_prev: Column, + /// New value + pub value: Column, + /// Previous MPT root + pub root_prev: Column, + /// New MPT root + pub root: Column, +} + +impl LookupTable for MptTable { + fn columns(&self) -> Vec> { + vec![ + self.address_rlc, + self.proof_type, + self.key_rlc, + self.value_prev, + self.value, + self.root_prev, + self.root, + ] + .into_iter() + .map(|col| col.into()) + .collect::>>() + } + + fn annotations(&self) -> Vec { + vec![ + String::from("address"), + String::from("proof_type"), + String::from("storage_key"), + String::from("old_value"), + String::from("new_value"), + String::from("old_root"), + String::from("new_root"), + ] + } +} + +impl MptTable { + /// Construct a new MptTable + pub(crate) fn construct(meta: &mut ConstraintSystem) -> Self { + // TODO(Brecht): everything except address and proof type needs to be + // advice_column_in(SecondPhase) + Self { + address_rlc: meta.advice_column_in(SecondPhase), + proof_type: meta.advice_column(), + key_rlc: meta.advice_column_in(SecondPhase), + value_prev: meta.advice_column_in(SecondPhase), + value: meta.advice_column_in(SecondPhase), + root_prev: meta.advice_column_in(SecondPhase), + root: meta.advice_column_in(SecondPhase), + } + } + + #[allow(clippy::too_many_arguments)] + pub(crate) fn constrain( + &self, + meta: &mut VirtualCells<'_, F>, + cb: &mut ConstraintBuilder, + address_rlc: Expression, + proof_type: Expression, + key_rlc: Expression, + value_prev: Expression, + value: Expression, + root_prev: Expression, + root: Expression, + ) { + circuit!([meta, cb], { + require!(a!(self.proof_type) => proof_type); + require!(a!(self.address_rlc) => address_rlc); + require!(a!(self.key_rlc) => key_rlc); + require!(a!(self.value_prev) => value_prev); + require!(a!(self.value) => value); + require!(a!(self.root_prev) => root_prev); + require!(a!(self.root) => root); + }) + } + + pub(crate) fn assign( + &self, + region: &mut Region<'_, F>, + offset: usize, + row: &MptUpdateRow>, + ) -> Result<(), Error> { + for (column, value) in >::advice_columns(self) + .iter() + .zip_eq(row.values()) + { + region.assign_advice(|| "assign mpt table row value", *column, offset, || value)?; + } + Ok(()) + } + + pub(crate) fn assign_cached>( + &self, + region: &mut CachedRegion<'_, '_, F, S>, + offset: usize, + row: &MptUpdateRow>, + ) -> Result<(), Error> { + for (column, value) in >::advice_columns(self) + .iter() + .zip_eq(row.values()) + { + region.assign_advice(|| "assign mpt table row value", *column, offset, || value)?; + } + Ok(()) + } + + pub(crate) fn load( + &self, + layouter: &mut impl Layouter, + updates: &MptUpdates, + randomness: Value, + ) -> Result<(), Error> { + layouter.assign_region( + || "mpt table", + |mut region| self.load_with_region(&mut region, updates, randomness), + ) + } + + pub(crate) fn load_with_region( + &self, + region: &mut Region<'_, F>, + updates: &MptUpdates, + randomness: Value, + ) -> Result<(), Error> { + for (offset, row) in updates.table_assignments(randomness).iter().enumerate() { + self.assign(region, offset, row)?; + } + Ok(()) + } +} diff --git a/zkevm-circuits/src/table/rw_table.rs b/zkevm-circuits/src/table/rw_table.rs new file mode 100644 index 0000000000..ca56b1f89b --- /dev/null +++ b/zkevm-circuits/src/table/rw_table.rs @@ -0,0 +1,135 @@ +use super::*; + +/// The RwTable shared between EVM Circuit and State Circuit, which contains +/// traces of the EVM state operations. +#[derive(Clone, Copy, Debug)] +pub struct RwTable { + /// Read Write Counter + pub rw_counter: Column, + /// Is Write + pub is_write: Column, + /// Tag + pub tag: Column, + /// Key1 (Id) + pub id: Column, + /// Key2 (Address) + pub address: Column, + /// Key3 (FieldTag) + pub field_tag: Column, + /// Key3 (StorageKey) + pub storage_key: Column, + /// Value + pub value: Column, + /// Value Previous + pub value_prev: Column, + /// Aux1 + pub aux1: Column, + /// Aux2 (Committed Value) + pub aux2: Column, +} + +impl LookupTable for RwTable { + fn columns(&self) -> Vec> { + vec![ + self.rw_counter.into(), + self.is_write.into(), + self.tag.into(), + self.id.into(), + self.address.into(), + self.field_tag.into(), + self.storage_key.into(), + self.value.into(), + self.value_prev.into(), + self.aux1.into(), + self.aux2.into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("rw_counter"), + String::from("is_write"), + String::from("tag"), + String::from("id"), + String::from("address"), + String::from("field_tag"), + String::from("storage_key"), + String::from("value"), + String::from("value_prev"), + String::from("aux1"), + String::from("aux2"), + ] + } +} +impl RwTable { + /// Construct a new RwTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + rw_counter: meta.advice_column(), + is_write: meta.advice_column(), + tag: meta.advice_column(), + id: meta.advice_column(), + address: meta.advice_column(), + field_tag: meta.advice_column(), + storage_key: meta.advice_column_in(SecondPhase), + value: meta.advice_column_in(SecondPhase), + value_prev: meta.advice_column_in(SecondPhase), + // It seems that aux1 for the moment is not using randomness + // TODO check in a future review + aux1: meta.advice_column_in(SecondPhase), + aux2: meta.advice_column_in(SecondPhase), + } + } + fn assign( + &self, + region: &mut Region<'_, F>, + offset: usize, + row: &RwRow>, + ) -> Result<(), Error> { + for (column, value) in [ + (self.rw_counter, row.rw_counter), + (self.is_write, row.is_write), + (self.tag, row.tag), + (self.id, row.id), + (self.address, row.address), + (self.field_tag, row.field_tag), + (self.storage_key, row.storage_key), + (self.value, row.value), + (self.value_prev, row.value_prev), + (self.aux1, row.aux1), + (self.aux2, row.aux2), + ] { + region.assign_advice(|| "assign rw row on rw table", column, offset, || value)?; + } + Ok(()) + } + + /// Assign the `RwTable` from a `RwMap`, following the same + /// table layout that the State Circuit uses. + pub fn load( + &self, + layouter: &mut impl Layouter, + rws: &[Rw], + n_rows: usize, + challenges: Value, + ) -> Result<(), Error> { + layouter.assign_region( + || "rw table", + |mut region| self.load_with_region(&mut region, rws, n_rows, challenges), + ) + } + + pub(crate) fn load_with_region( + &self, + region: &mut Region<'_, F>, + rws: &[Rw], + n_rows: usize, + challenges: Value, + ) -> Result<(), Error> { + let (rows, _) = RwMap::table_assignments_prepad(rws, n_rows); + for (offset, row) in rows.iter().enumerate() { + self.assign(region, offset, &row.table_assignment(challenges))?; + } + Ok(()) + } +} diff --git a/zkevm-circuits/src/table/tx_table.rs b/zkevm-circuits/src/table/tx_table.rs new file mode 100644 index 0000000000..017007a614 --- /dev/null +++ b/zkevm-circuits/src/table/tx_table.rs @@ -0,0 +1,217 @@ +use super::*; + +/// Tag used to identify each field in the transaction in a row of the +/// transaction table. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TxFieldTag { + /// Unused tag + Null = 0, + /// Nonce + Nonce, + /// Gas + Gas, + /// GasPrice + GasPrice, + /// CallerAddress + CallerAddress, + /// CalleeAddress + CalleeAddress, + /// IsCreate + IsCreate, + /// Value + Value, + /// CallDataLength + CallDataLength, + /// Gas cost for transaction call data (4 for byte == 0, 16 otherwise) + CallDataGasCost, + /// TxSignHash: Hash of the transaction without the signature, used for + /// signing. + TxSignHash, + /// CallData + CallData, +} +impl_expr!(TxFieldTag); + +/// Tag for a TxLogField in RwTable +#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter)] +pub enum TxLogFieldTag { + /// Address field + Address = 1, + /// Topic field + Topic, + /// Data field + Data, +} +impl_expr!(TxLogFieldTag); + +/// Tag for a TxReceiptField in RwTable +#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter, EnumCount)] +pub enum TxReceiptFieldTag { + /// Tx result + PostStateOrStatus = 1, + /// CumulativeGasUsed in the tx + CumulativeGasUsed, + /// Number of logs in the tx + LogLength, +} +impl_expr!(TxReceiptFieldTag); + +/// Alias for TxFieldTag used by EVM Circuit +pub type TxContextFieldTag = TxFieldTag; + +/// Table that contains the fields of all Transactions in a block +#[derive(Clone, Debug)] +pub struct TxTable { + /// Tx ID + pub tx_id: Column, + /// Tag (TxContextFieldTag) + pub tag: Column, + /// Index for Tag = CallData + pub index: Column, + /// Value + pub value: Column, +} + +impl TxTable { + /// Construct a new TxTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + tx_id: meta.advice_column(), + tag: meta.fixed_column(), + index: meta.advice_column(), + value: meta.advice_column_in(SecondPhase), + } + } + + /// Assign the `TxTable` from a list of block `Transaction`s, followig the + /// same layout that the Tx Circuit uses. + pub fn load( + &self, + layouter: &mut impl Layouter, + txs: &[Transaction], + max_txs: usize, + max_calldata: usize, + challenges: &Challenges>, + ) -> Result<(), Error> { + assert!( + txs.len() <= max_txs, + "txs.len() <= max_txs: txs.len()={}, max_txs={}", + txs.len(), + max_txs + ); + let sum_txs_calldata = txs.iter().map(|tx| tx.call_data.len()).sum(); + assert!( + sum_txs_calldata <= max_calldata, + "sum_txs_calldata <= max_calldata: sum_txs_calldata={}, max_calldata={}", + sum_txs_calldata, + max_calldata, + ); + + fn assign_row( + region: &mut Region<'_, F>, + offset: usize, + advice_columns: &[Column], + tag: &Column, + row: &[Value; 4], + msg: &str, + ) -> Result<(), Error> { + for (index, column) in advice_columns.iter().enumerate() { + region.assign_advice( + || format!("tx table {} row {}", msg, offset), + *column, + offset, + || row[if index > 0 { index + 1 } else { index }], + )?; + } + region.assign_fixed( + || format!("tx table {} row {}", msg, offset), + *tag, + offset, + || row[1], + )?; + Ok(()) + } + + layouter.assign_region( + || "tx table", + |mut region| { + let mut offset = 0; + let advice_columns = [self.tx_id, self.index, self.value]; + assign_row( + &mut region, + offset, + &advice_columns, + &self.tag, + &[(); 4].map(|_| Value::known(F::ZERO)), + "all-zero", + )?; + offset += 1; + + // Tx Table contains an initial region that has a size parametrized by max_txs + // with all the tx data except for calldata, and then a second + // region that has a size parametrized by max_calldata with all + // the tx calldata. This is required to achieve a constant fixed column tag + // regardless of the number of input txs or the calldata size of each tx. + let mut calldata_assignments: Vec<[Value; 4]> = Vec::new(); + // Assign Tx data (all tx fields except for calldata) + let padding_txs: Vec<_> = (txs.len()..max_txs) + .map(|i| Transaction { + id: i + 1, + ..Default::default() + }) + .collect(); + for tx in txs.iter().chain(padding_txs.iter()) { + let [tx_data, tx_calldata] = tx.table_assignments(*challenges); + for row in tx_data { + assign_row(&mut region, offset, &advice_columns, &self.tag, &row, "")?; + offset += 1; + } + calldata_assignments.extend(tx_calldata.iter()); + } + // Assign Tx calldata + let padding_calldata = (sum_txs_calldata..max_calldata).map(|_| { + [ + Value::known(F::ZERO), + Value::known(F::from(TxContextFieldTag::CallData as u64)), + Value::known(F::ZERO), + Value::known(F::ZERO), + ] + }); + for row in calldata_assignments.into_iter().chain(padding_calldata) { + assign_row(&mut region, offset, &advice_columns, &self.tag, &row, "")?; + offset += 1; + } + Ok(()) + }, + ) + } +} + +impl LookupTable for TxTable { + fn columns(&self) -> Vec> { + vec![ + self.tx_id.into(), + self.tag.into(), + self.index.into(), + self.value.into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("tx_id"), + String::from("tag"), + String::from("index"), + String::from("value"), + ] + } + + fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { + vec![ + meta.query_advice(self.tx_id, Rotation::cur()), + meta.query_fixed(self.tag, Rotation::cur()), + meta.query_advice(self.index, Rotation::cur()), + meta.query_advice(self.value, Rotation::cur()), + ] + } +} diff --git a/zkevm-circuits/src/test_util.rs b/zkevm-circuits/src/test_util.rs index 4e3d17e337..99ba2dfd56 100644 --- a/zkevm-circuits/src/test_util.rs +++ b/zkevm-circuits/src/test_util.rs @@ -8,7 +8,9 @@ use crate::{ }; use bus_mapping::{circuit_input_builder::CircuitsParams, mock::BlockData}; use eth_types::geth_types::GethData; +use std::cmp; +use crate::util::log2_ceil; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; use mock::TestContext; @@ -19,6 +21,8 @@ fn init_env_logger() { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("error")).init(); } +const NUM_BLINDING_ROWS: usize = 64; + #[allow(clippy::type_complexity)] /// Struct used to easily generate tests for EVM &| State circuits being able to /// customize all of the steps involved in the testing itself. @@ -220,18 +224,18 @@ impl CircuitTestBuilder { // TODO: use randomness as one of the circuit public input, since randomness in // state circuit and evm circuit must be same { + let rows_needed = StateCircuit::::min_num_rows_block(&block).1; + let k = cmp::max(log2_ceil(rows_needed + NUM_BLINDING_ROWS), 18); let state_circuit = StateCircuit::::new(block.rws, params.max_rws); let instance = state_circuit.instance(); - let prover = MockProver::::run(18, &state_circuit, instance).unwrap(); + let prover = MockProver::::run(k, &state_circuit, instance).unwrap(); // Skip verification of Start rows to accelerate testing let non_start_rows_len = state_circuit .rows .iter() .filter(|rw| !matches!(rw, Rw::Start { .. })) .count(); - let rows = (params.max_rws - non_start_rows_len..params.max_rws) - .into_iter() - .collect(); + let rows = (params.max_rws - non_start_rows_len..params.max_rws).collect(); self.state_checks.as_ref()(prover, &rows, &rows); } diff --git a/zkevm-circuits/src/tx_circuit.rs b/zkevm-circuits/src/tx_circuit.rs index 9c70baa8d1..ffd60993aa 100644 --- a/zkevm-circuits/src/tx_circuit.rs +++ b/zkevm-circuits/src/tx_circuit.rs @@ -6,14 +6,19 @@ pub mod sign_verify; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +mod dev; +#[cfg(any(feature = "test", test))] +mod test; +#[cfg(any(feature = "test", test, feature = "test-circuits"))] +pub use dev::TxCircuit as TestTxCircuit; + use crate::{ table::{KeccakTable, TxFieldTag, TxTable}, util::{random_linear_combine_word as rlc, Challenges, SubCircuit, SubCircuitConfig}, witness, }; -use eth_types::{ - geth_types::Transaction, sign_types::SignData, Address, Field, ToLittleEndian, ToScalar, -}; +use eth_types::{geth_types::Transaction, sign_types::SignData, Field, ToLittleEndian, ToScalar}; use halo2_proofs::{ circuit::{AssignedCell, Layouter, Region, Value}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed}, @@ -23,20 +28,6 @@ use log::error; use sign_verify::{AssignedSignatureVerify, SignVerifyChip, SignVerifyConfig}; use std::marker::PhantomData; -pub use halo2_proofs::halo2curves::{ - group::{ - ff::{Field as GroupField, PrimeField}, - prime::PrimeCurveAffine, - Curve, Group, GroupEncoding, - }, - secp256k1::{self, Secp256k1Affine, Secp256k1Compressed}, -}; - -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -use bus_mapping::circuit_input_builder::keccak_inputs_tx_circuit; -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -use halo2_proofs::{circuit::SimpleFloorPlanner, plonk::Circuit}; - /// Number of static fields per tx: [nonce, gas, gas_price, /// caller_address, callee_address, is_create, value, call_data_length, /// call_data_gas_cost, tx_sign_hash]. @@ -198,7 +189,7 @@ impl TxCircuit { 0, TxFieldTag::Null, 0, - Value::known(F::zero()), + Value::known(F::ZERO), )?; offset += 1; // Assign al Tx fields except for call data @@ -211,12 +202,7 @@ impl TxCircuit { }; for (tag, value) in [ - ( - TxFieldTag::Nonce, - challenges - .evm_word() - .map(|challenge| rlc(tx.nonce.to_le_bytes(), challenge)), - ), + (TxFieldTag::Nonce, Value::known(F::from(tx.nonce.as_u64()))), ( TxFieldTag::Gas, Value::known(F::from(tx.gas_limit.as_u64())), @@ -233,16 +219,11 @@ impl TxCircuit { ), ( TxFieldTag::CalleeAddress, - Value::known( - tx.to - .unwrap_or_else(Address::zero) - .to_scalar() - .expect("tx.to too big"), - ), + Value::known(tx.to_or_zero().to_scalar().expect("tx.to too big")), ), ( TxFieldTag::IsCreate, - Value::known(F::from(tx.to.is_none() as u64)), + Value::known(F::from(tx.is_create() as u64)), ), ( TxFieldTag::Value, @@ -256,12 +237,7 @@ impl TxCircuit { ), ( TxFieldTag::CallDataGasCost, - Value::known(F::from( - tx.call_data - .0 - .iter() - .fold(0, |acc, byte| acc + if *byte == 0 { 4 } else { 16 }), - )), + Value::known(F::from(tx.call_data_gas_cost())), ), ( TxFieldTag::TxSignHash, @@ -312,7 +288,7 @@ impl TxCircuit { 0, // tx_id TxFieldTag::CallData, 0, - Value::known(F::zero()), + Value::known(F::ZERO), )?; offset += 1; } @@ -325,6 +301,12 @@ impl TxCircuit { impl SubCircuit for TxCircuit { type Config = TxCircuitConfig; + fn unusable_rows() -> usize { + // No column queried at more than 3 distinct rotations, so returns 6 as + // minimum unusable rows. + 6 + } + fn new_from_block(block: &witness::Block) -> Self { Self::new( block.circuits_params.max_txs, @@ -386,164 +368,3 @@ impl SubCircuit for TxCircuit { vec![vec![]] } } - -#[cfg(any(feature = "test", test, feature = "test-circuits"))] -impl Circuit for TxCircuit { - type Config = (TxCircuitConfig, Challenges); - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let tx_table = TxTable::construct(meta); - let keccak_table = KeccakTable::construct(meta); - let challenges = Challenges::construct(meta); - - let config = { - let challenges = challenges.exprs(meta); - TxCircuitConfig::new( - meta, - TxCircuitConfigArgs { - tx_table, - keccak_table, - challenges, - }, - ) - }; - - (config, challenges) - } - - fn synthesize( - &self, - (config, challenges): Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let challenges = challenges.values(&mut layouter); - - config.keccak_table.dev_load( - &mut layouter, - &keccak_inputs_tx_circuit(&self.txs[..], self.chain_id).map_err(|e| { - error!("keccak_inputs_tx_circuit error: {:?}", e); - Error::Synthesis - })?, - &challenges, - true, - )?; - self.synthesize_sub(&config, &challenges, &mut layouter) - } -} - -#[cfg(test)] -mod tx_circuit_tests { - use super::*; - use crate::util::log2_ceil; - use eth_types::address; - use halo2_proofs::{ - dev::{MockProver, VerifyFailure}, - halo2curves::bn256::Fr, - }; - use mock::AddrOrWallet; - use pretty_assertions::assert_eq; - - const NUM_BLINDING_ROWS: usize = 64; - - fn run( - txs: Vec, - chain_id: u64, - max_txs: usize, - max_calldata: usize, - ) -> Result<(), Vec> { - let k = log2_ceil(NUM_BLINDING_ROWS + TxCircuit::::min_num_rows(max_txs, max_calldata)); - // SignVerifyChip -> ECDSAChip -> MainGate instance column - let circuit = TxCircuit::::new(max_txs, max_calldata, chain_id, txs); - - let prover = match MockProver::run(k, &circuit, vec![vec![]]) { - Ok(prover) => prover, - Err(e) => panic!("{:#?}", e), - }; - prover.verify() - } - - #[test] - fn tx_circuit_2tx_2max_tx() { - const NUM_TXS: usize = 2; - const MAX_TXS: usize = 2; - const MAX_CALLDATA: usize = 32; - - assert_eq!( - run::( - mock::CORRECT_MOCK_TXS[..NUM_TXS] - .iter() - .map(|tx| Transaction::from(tx.clone())) - .collect_vec(), - mock::MOCK_CHAIN_ID.as_u64(), - MAX_TXS, - MAX_CALLDATA - ), - Ok(()) - ); - } - - #[test] - fn tx_circuit_1tx_1max_tx() { - const MAX_TXS: usize = 1; - const MAX_CALLDATA: usize = 32; - - let chain_id: u64 = mock::MOCK_CHAIN_ID.as_u64(); - - let tx: Transaction = mock::CORRECT_MOCK_TXS[0].clone().into(); - - assert_eq!(run::(vec![tx], chain_id, MAX_TXS, MAX_CALLDATA), Ok(())); - } - - #[test] - fn tx_circuit_1tx_2max_tx() { - const MAX_TXS: usize = 2; - const MAX_CALLDATA: usize = 32; - - let chain_id: u64 = mock::MOCK_CHAIN_ID.as_u64(); - - let tx: Transaction = mock::CORRECT_MOCK_TXS[0].clone().into(); - - assert_eq!(run::(vec![tx], chain_id, MAX_TXS, MAX_CALLDATA), Ok(())); - } - - #[test] - fn tx_circuit_bad_address() { - const MAX_TXS: usize = 1; - const MAX_CALLDATA: usize = 32; - - let mut tx = mock::CORRECT_MOCK_TXS[0].clone(); - // This address doesn't correspond to the account that signed this tx. - tx.from = AddrOrWallet::from(address!("0x1230000000000000000000000000000000000456")); - - assert!(run::( - vec![tx.into()], - mock::MOCK_CHAIN_ID.as_u64(), - MAX_TXS, - MAX_CALLDATA - ) - .is_err(),); - } - - #[test] - fn variadic_size_check() { - const MAX_TXS: usize = 2; - const MAX_CALLDATA: usize = 32; - - let chain_id: u64 = mock::MOCK_CHAIN_ID.as_u64(); - let tx1: Transaction = mock::CORRECT_MOCK_TXS[0].clone().into(); - let tx2: Transaction = mock::CORRECT_MOCK_TXS[1].clone().into(); - let circuit = TxCircuit::::new(MAX_TXS, MAX_CALLDATA, chain_id, vec![tx1.clone()]); - let prover1 = MockProver::::run(20, &circuit, vec![vec![]]).unwrap(); - - let circuit = TxCircuit::::new(MAX_TXS, MAX_CALLDATA, chain_id, vec![tx1, tx2]); - let prover2 = MockProver::::run(20, &circuit, vec![vec![]]).unwrap(); - - assert_eq!(prover1.fixed(), prover2.fixed()); - assert_eq!(prover1.permutation(), prover2.permutation()); - } -} diff --git a/zkevm-circuits/src/tx_circuit/dev.rs b/zkevm-circuits/src/tx_circuit/dev.rs new file mode 100644 index 0000000000..a286d554a6 --- /dev/null +++ b/zkevm-circuits/src/tx_circuit/dev.rs @@ -0,0 +1,63 @@ +pub use super::TxCircuit; + +use crate::{ + table::{KeccakTable, TxTable}, + tx_circuit::{TxCircuitConfig, TxCircuitConfigArgs}, + util::{Challenges, SubCircuit, SubCircuitConfig}, +}; +use bus_mapping::circuit_input_builder::keccak_inputs_tx_circuit; +use eth_types::Field; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner}, + plonk::{Circuit, ConstraintSystem, Error}, +}; +use log::error; + +impl Circuit for TxCircuit { + type Config = (TxCircuitConfig, Challenges); + type FloorPlanner = SimpleFloorPlanner; + type Params = (); + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let tx_table = TxTable::construct(meta); + let keccak_table = KeccakTable::construct(meta); + let challenges = Challenges::construct(meta); + + let config = { + let challenges = challenges.exprs(meta); + TxCircuitConfig::new( + meta, + TxCircuitConfigArgs { + tx_table, + keccak_table, + challenges, + }, + ) + }; + + (config, challenges) + } + + fn synthesize( + &self, + (config, challenges): Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenges = challenges.values(&mut layouter); + + config.keccak_table.dev_load( + &mut layouter, + &keccak_inputs_tx_circuit(&self.txs[..], self.chain_id).map_err(|e| { + error!("keccak_inputs_tx_circuit error: {:?}", e); + Error::Synthesis + })?, + &challenges, + true, + )?; + self.synthesize_sub(&config, &challenges, &mut layouter) + } +} diff --git a/zkevm-circuits/src/tx_circuit/sign_verify.rs b/zkevm-circuits/src/tx_circuit/sign_verify.rs index e011e9f325..b69b5624a9 100644 --- a/zkevm-circuits/src/tx_circuit/sign_verify.rs +++ b/zkevm-circuits/src/tx_circuit/sign_verify.rs @@ -12,14 +12,15 @@ use crate::{ use ecc::{maingate, EccConfig, GeneralEccChip}; use ecdsa::ecdsa::{AssignedEcdsaSig, AssignedPublicKey, EcdsaChip}; use eth_types::{ - self, + self, keccak256, sign_types::{pk_bytes_le, pk_bytes_swap_endianness, SignData}, Field, }; use halo2_proofs::{ - arithmetic::{CurveAffine, FieldExt}, + arithmetic::CurveAffine, circuit::{AssignedCell, Cell, Layouter, Value}, halo2curves::{ + ff::PrimeField, group::{Curve, Group}, secp256k1, secp256k1::Secp256k1Affine, @@ -32,7 +33,6 @@ use rand::SeedableRng; use rand_chacha::ChaCha20Rng; use itertools::Itertools; -use keccak256::plain::Keccak; use log::error; use maingate::{ AssignedValue, MainGate, MainGateConfig, MainGateInstructions, RangeChip, RangeConfig, @@ -67,7 +67,7 @@ impl SignVerifyChip { ::CurveExt::random(&mut rng).to_affine(); Self { aux_generator, - window_size: 2, + window_size: 4, max_verif, _marker: PhantomData, } @@ -86,7 +86,7 @@ impl SignVerifyChip { // region. TODO: Figure out a way to get these numbers automatically. let rows_range_chip_table = 295188; let rows_ecc_chip_aux = 226; - let rows_ecdsa_chip_verification = 140360; + let rows_ecdsa_chip_verification = 104471; let rows_signature_address_verify = 76; std::cmp::max( rows_range_chip_table, @@ -100,7 +100,7 @@ impl Default for SignVerifyChip { fn default() -> Self { Self { aux_generator: Secp256k1Affine::default(), - window_size: 1, + window_size: 4, max_verif: 0, _marker: PhantomData::default(), } @@ -314,7 +314,7 @@ pub(crate) struct AssignedSignatureVerify { // Return an array of bytes that corresponds to the little endian representation // of the integer, adding the constraints to verify the correctness of the // conversion (byte range check included). -fn integer_to_bytes_le( +fn integer_to_bytes_le( ctx: &mut RegionCtx<'_, F>, range_chip: &RangeChip, int: &AssignedInteger, @@ -352,7 +352,7 @@ impl SignVerifyChip { ecc_chip: &mut GeneralEccChip, ) -> Result<(), Error> { ecc_chip.assign_aux_generator(ctx, Value::known(self.aux_generator))?; - ecc_chip.assign_aux(ctx, self.window_size, 1)?; + ecc_chip.assign_aux(ctx, self.window_size, 2)?; Ok(()) } @@ -426,15 +426,15 @@ impl SignVerifyChip { challenge: Value, inputs_le: impl IntoIterator>, ) -> Result, Error> { - let zero = chips.main_gate.assign_constant(ctx, F::zero())?; + let zero = chips.main_gate.assign_constant(ctx, F::ZERO)?; let columns = config.main_gate_config.advices(); let inputs_le = inputs_le.into_iter().collect_vec(); - let inputs_be = iter::repeat_with(|| Term::assigned(zero.cell(), Value::known(F::zero()))) + let inputs_be = iter::repeat_with(|| Term::assigned(zero.cell(), Value::known(F::ZERO))) .take(Integer::next_multiple_of(&inputs_le.len(), &columns.len()) - inputs_le.len()) .chain(inputs_le.into_iter().rev()) .collect_vec(); - let mut rlc = Value::known(F::zero()); + let mut rlc = Value::known(F::ZERO); for (chunk_idx, chunk) in inputs_be.chunks_exact(columns.len()).enumerate() { ctx.enable(q_rlc)?; let assigned_rlc = ctx.assign_advice(|| "{name}_rlc[{chunk_idx}]", config.rlc, rlc)?; @@ -450,9 +450,7 @@ impl SignVerifyChip { } rlc = iter::once(rlc) .chain(chunk.iter().map(|term| term.value())) - .fold(Value::known(F::zero()), |acc, input| { - acc * challenge + input - }); + .fold(Value::known(F::ZERO), |acc, input| acc * challenge + input); ctx.next(); } @@ -507,29 +505,22 @@ impl SignVerifyChip { let pk_le = pk_bytes_le(&sign_data.pk); let pk_be = pk_bytes_swap_endianness(&pk_le); let pk_hash = (!padding) - .then(|| { - let mut keccak = Keccak::default(); - keccak.update(&pk_be); - let hash: [_; 32] = keccak.digest().try_into().expect("vec to array of size 32"); - hash - }) + .then(|| keccak256(&pk_be)) .unwrap_or_default() .map(|byte| Value::known(F::from(byte as u64))); let pk_hash_hi = pk_hash[..12].to_vec(); // Ref. spec SignVerifyChip 2. Verify that the first 20 bytes of the // pub_key_hash equal the address let (address, pk_hash_lo) = { - let powers_of_256 = - iter::successors(Some(F::one()), |coeff| Some(F::from(256) * coeff)) - .take(20) - .collect_vec(); + let powers_of_256 = iter::successors(Some(F::ONE), |coeff| Some(F::from(256) * coeff)) + .take(20) + .collect_vec(); let terms = pk_hash[12..] .iter() .zip(powers_of_256.into_iter().rev()) .map(|(byte, coeff)| maingate::Term::Unassigned(*byte, coeff)) .collect_vec(); - let (address, pk_hash_lo) = - main_gate.decompose(ctx, &terms, F::zero(), |_, _| Ok(()))?; + let (address, pk_hash_lo) = main_gate.decompose(ctx, &terms, F::ZERO, |_, _| Ok(()))?; ( address, @@ -545,7 +536,7 @@ impl SignVerifyChip { // Ref. spec SignVerifyChip 3. Verify that the signed message in the ecdsa_chip // with RLC encoding corresponds to msg_hash_rlc let msg_hash_rlc = { - let zero = main_gate.assign_constant(ctx, F::zero())?; + let zero = main_gate.assign_constant(ctx, F::ZERO)?; let assigned_msg_hash_le = assigned_ecdsa .msg_hash_le .iter() @@ -700,7 +691,7 @@ impl SignVerifyChip { fn pub_key_hash_to_address(pk_hash: &[u8]) -> F { pk_hash[32 - 20..] .iter() - .fold(F::zero(), |acc, b| acc * F::from(256) + F::from(*b as u64)) + .fold(F::ZERO, |acc, b| acc * F::from(256) + F::from(*b as u64)) } #[cfg(test)] @@ -756,6 +747,7 @@ mod sign_verify_tests { impl Circuit for TestCircuitSignVerify { type Config = TestCircuitSignVerifyConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -798,7 +790,7 @@ mod sign_verify_tests { let circuit = TestCircuitSignVerify:: { sign_verify: SignVerifyChip { aux_generator, - window_size: 2, + window_size: 4, max_verif, _marker: PhantomData, }, diff --git a/zkevm-circuits/src/tx_circuit/test.rs b/zkevm-circuits/src/tx_circuit/test.rs new file mode 100644 index 0000000000..102b32c11d --- /dev/null +++ b/zkevm-circuits/src/tx_circuit/test.rs @@ -0,0 +1,116 @@ +#![allow(unused_imports)] +use super::*; +use crate::util::{log2_ceil, unusable_rows}; +use eth_types::address; +use halo2_proofs::{ + dev::{MockProver, VerifyFailure}, + halo2curves::bn256::Fr, +}; +use mock::AddrOrWallet; + +#[test] +fn tx_circuit_unusable_rows() { + assert_eq!( + TxCircuit::::unusable_rows(), + unusable_rows::>(()), + ) +} + +fn run( + txs: Vec, + chain_id: u64, + max_txs: usize, + max_calldata: usize, +) -> Result<(), Vec> { + let k = log2_ceil( + TxCircuit::::unusable_rows() + TxCircuit::::min_num_rows(max_txs, max_calldata), + ); + // SignVerifyChip -> ECDSAChip -> MainGate instance column + let circuit = TxCircuit::::new(max_txs, max_calldata, chain_id, txs); + + let prover = match MockProver::run(k, &circuit, vec![vec![]]) { + Ok(prover) => prover, + Err(e) => panic!("{:#?}", e), + }; + prover.verify() +} + +#[test] +fn tx_circuit_2tx_2max_tx() { + const NUM_TXS: usize = 2; + const MAX_TXS: usize = 2; + const MAX_CALLDATA: usize = 32; + + assert_eq!( + run::( + mock::CORRECT_MOCK_TXS[..NUM_TXS] + .iter() + .map(|tx| Transaction::from(tx.clone())) + .collect_vec(), + mock::MOCK_CHAIN_ID.as_u64(), + MAX_TXS, + MAX_CALLDATA + ), + Ok(()) + ); +} + +#[test] +fn tx_circuit_1tx_1max_tx() { + const MAX_TXS: usize = 1; + const MAX_CALLDATA: usize = 32; + + let chain_id: u64 = mock::MOCK_CHAIN_ID.as_u64(); + + let tx: Transaction = mock::CORRECT_MOCK_TXS[0].clone().into(); + + assert_eq!(run::(vec![tx], chain_id, MAX_TXS, MAX_CALLDATA), Ok(())); +} + +#[test] +fn tx_circuit_1tx_2max_tx() { + const MAX_TXS: usize = 2; + const MAX_CALLDATA: usize = 32; + + let chain_id: u64 = mock::MOCK_CHAIN_ID.as_u64(); + + let tx: Transaction = mock::CORRECT_MOCK_TXS[0].clone().into(); + + assert_eq!(run::(vec![tx], chain_id, MAX_TXS, MAX_CALLDATA), Ok(())); +} + +#[test] +fn tx_circuit_bad_address() { + const MAX_TXS: usize = 1; + const MAX_CALLDATA: usize = 32; + + let mut tx = mock::CORRECT_MOCK_TXS[0].clone(); + // This address doesn't correspond to the account that signed this tx. + tx.from = AddrOrWallet::from(address!("0x1230000000000000000000000000000000000456")); + + assert!(run::( + vec![tx.into()], + mock::MOCK_CHAIN_ID.as_u64(), + MAX_TXS, + MAX_CALLDATA + ) + .is_err(),); +} + +#[test] +fn variadic_size_check() { + const MAX_TXS: usize = 2; + const MAX_CALLDATA: usize = 32; + + let chain_id: u64 = mock::MOCK_CHAIN_ID.as_u64(); + let tx1: Transaction = mock::CORRECT_MOCK_TXS[0].clone().into(); + let tx2: Transaction = mock::CORRECT_MOCK_TXS[1].clone().into(); + let circuit = TxCircuit::::new(MAX_TXS, MAX_CALLDATA, chain_id, vec![tx1.clone()]); + let prover1 = MockProver::::run(20, &circuit, vec![vec![]]).unwrap(); + + let circuit = TxCircuit::::new(MAX_TXS, MAX_CALLDATA, chain_id, vec![tx1, tx2]); + let prover2 = MockProver::::run(20, &circuit, vec![vec![]]).unwrap(); + + assert_eq!(prover1.fixed(), prover2.fixed()); + assert_eq!(prover1.permutation(), prover2.permutation()); +} diff --git a/zkevm-circuits/src/util.rs b/zkevm-circuits/src/util.rs index d893756b21..4603df4a21 100644 --- a/zkevm-circuits/src/util.rs +++ b/zkevm-circuits/src/util.rs @@ -3,13 +3,13 @@ use bus_mapping::evm::OpcodeId; use halo2_proofs::{ circuit::{Layouter, Value}, plonk::{ - Challenge, ConstraintSystem, Error, Expression, FirstPhase, SecondPhase, VirtualCells, + Challenge, Circuit, ConstraintSystem, Error, Expression, FirstPhase, SecondPhase, + VirtualCells, }, }; -use keccak256::plain::Keccak; use crate::{evm_circuit::util::rlc, table::TxLogFieldTag, witness}; -use eth_types::{Field, ToAddress, Word}; +use eth_types::{keccak256, Field, ToAddress, Word}; pub use ethers_core::types::{Address, U256}; pub use gadgets::util::Expr; @@ -157,6 +157,10 @@ pub trait SubCircuit { /// Configuration of the SubCircuit. type Config: SubCircuitConfig; + /// Returns number of unusable rows of the SubCircuit, which should be + /// `meta.blinding_factors() + 1`. + fn unusable_rows() -> usize; + /// Create a new SubCircuit from a witness Block fn new_from_block(block: &witness::Block) -> Self; @@ -195,9 +199,7 @@ pub fn log2_ceil(n: usize) -> u32 { } pub(crate) fn keccak(msg: &[u8]) -> Word { - let mut keccak = Keccak::default(); - keccak.update(msg); - Word::from_big_endian(keccak.digest().as_slice()) + Word::from_big_endian(keccak256(msg).as_slice()) } pub(crate) fn is_push(byte: u8) -> bool { @@ -211,3 +213,22 @@ pub(crate) fn get_push_size(byte: u8) -> u64 { 0u64 } } + +/// Returns number of unusable rows of the Circuit. +/// The minimum unusable rows of a circuit is currently 6, where +/// - 3 comes from minimum number of distinct queries to permutation argument witness column +/// - 1 comes from queries at x_3 during multiopen +/// - 1 comes as slight defense against off-by-one errors +/// - 1 comes from reservation for last row for grand-product boundray check, hence not copy-able or +/// lookup-able. Note this 1 is not considered in [`ConstraintSystem::blinding_factors`], so below +/// we need to add an extra 1. +/// +/// For circuit with column queried at more than 3 distinct rotation, we can +/// calculate the unusable rows as (x - 3) + 6 where x is the number of distinct +/// rotation. +pub(crate) fn unusable_rows>(params: C::Params) -> usize { + let mut cs = ConstraintSystem::default(); + C::configure_with_params(&mut cs, params); + + cs.blinding_factors() + 1 +} diff --git a/zkevm-circuits/src/util/cell_manager.rs b/zkevm-circuits/src/util/cell_manager.rs new file mode 100644 index 0000000000..97a7ee493f --- /dev/null +++ b/zkevm-circuits/src/util/cell_manager.rs @@ -0,0 +1,8 @@ +trait CellManagerStrategy { + fn query_top_aligned_cell(&mut self, cell_type: CellType) -> Cell; + fn query_cell(&mut self, cell_type: CellType) -> Cell; + fn query_cells(&mut self, cell_type: CellType, count: usize) -> Vec>; + fn columns(&self) -> &[CellColumn]; + fn get_width(&self) -> usize; + fn get_height(&self) -> usize; +} diff --git a/zkevm-circuits/src/witness.rs b/zkevm-circuits/src/witness.rs index 22bca9affd..dde790dd39 100644 --- a/zkevm-circuits/src/witness.rs +++ b/zkevm-circuits/src/witness.rs @@ -6,13 +6,11 @@ mod block; pub use block::{block_convert, Block, BlockContext}; mod bytecode; pub use bytecode::Bytecode; -mod call; -pub use call::Call; mod mpt; pub use mpt::{MptUpdate, MptUpdateRow, MptUpdates}; mod rw; +pub use bus_mapping::circuit_input_builder::ExecStep; pub use rw::{Rw, RwMap, RwRow}; -mod step; -pub use step::ExecStep; mod tx; +pub use bus_mapping::circuit_input_builder::Call; pub use tx::Transaction; diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index a6a025da60..e3c4de3764 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -1,9 +1,11 @@ use std::collections::HashMap; -#[cfg(any(feature = "test", test))] -use crate::evm_circuit::{detect_fixed_table_tags, EvmCircuit}; - -use crate::{evm_circuit::util::rlc, table::BlockContextFieldTag}; +use crate::{ + evm_circuit::{detect_fixed_table_tags, util::rlc, EvmCircuit}, + exp_circuit::param::OFFSET_INCREMENT, + table::BlockContextFieldTag, + util::{log2_ceil, SubCircuit}, +}; use bus_mapping::{ circuit_input_builder::{self, CircuitsParams, CopyEvent, ExpEvent}, Error, @@ -11,7 +13,7 @@ use bus_mapping::{ use eth_types::{Address, Field, ToLittleEndian, ToScalar, Word}; use halo2_proofs::circuit::Value; -use super::{step::step_convert, tx::tx_convert, Bytecode, ExecStep, RwMap, Transaction}; +use super::{tx::tx_convert, Bytecode, ExecStep, Rw, RwMap, Transaction}; // TODO: Remove fields that are duplicated in`eth_block` /// Block is the struct used by all circuits, which contains all the needed @@ -58,22 +60,19 @@ impl Block { for (tx_idx, tx) in self.txs.iter().enumerate() { println!("tx {}", tx_idx); for step in &tx.steps { - println!(" step {:?} rwc: {}", step.execution_state, step.rw_counter); - for rw_ref in &step.rw_indices { - println!(" - {:?}", self.rws[*rw_ref]); + println!(" step {:?} rwc: {}", step.exec_state, step.rwc.0); + for rw_idx in 0..step.bus_mapping_instance.len() { + println!(" - {:?}", self.get_rws(step, rw_idx)); } } } } -} -#[cfg(feature = "test")] -use crate::exp_circuit::OFFSET_INCREMENT; -#[cfg(feature = "test")] -use crate::util::log2_ceil; + /// Get a read-write record + pub(crate) fn get_rws(&self, step: &ExecStep, index: usize) -> Rw { + self.rws[step.rw_index(index)] + } -#[cfg(feature = "test")] -impl Block { /// Obtains the expected Circuit degree needed in order to be able to test /// the EvmCircuit with this block without needing to configure the /// `ConstraintSystem`. @@ -101,8 +100,6 @@ impl Block { .map(|e| e.steps.len() * OFFSET_INCREMENT) .sum(); - const NUM_BLINDING_ROWS: usize = 64; - let rows_needed: usize = itertools::max([ num_rows_required_for_execution_steps, num_rows_required_for_rw_table, @@ -115,7 +112,7 @@ impl Block { ]) .unwrap(); - let k = log2_ceil(NUM_BLINDING_ROWS + rows_needed); + let k = log2_ceil(EvmCircuit::::unusable_rows() + rows_needed); log::debug!( "num_rows_requred_for rw_table={}, fixed_table={}, bytecode_table={}, \ copy_table={}, keccak_table={}, tx_table={}, exp_table={}", @@ -160,39 +157,39 @@ impl BlockContext { vec![ [ Value::known(F::from(BlockContextFieldTag::Coinbase as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), Value::known(self.coinbase.to_scalar().unwrap()), ], [ Value::known(F::from(BlockContextFieldTag::Timestamp as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), Value::known(self.timestamp.to_scalar().unwrap()), ], [ Value::known(F::from(BlockContextFieldTag::Number as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), Value::known(self.number.to_scalar().unwrap()), ], [ Value::known(F::from(BlockContextFieldTag::Difficulty as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), randomness .map(|randomness| rlc::value(&self.difficulty.to_le_bytes(), randomness)), ], [ Value::known(F::from(BlockContextFieldTag::GasLimit as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), Value::known(F::from(self.gas_limit)), ], [ Value::known(F::from(BlockContextFieldTag::BaseFee as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), randomness .map(|randomness| rlc::value(&self.base_fee.to_le_bytes(), randomness)), ], [ Value::known(F::from(BlockContextFieldTag::ChainId as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), randomness .map(|randomness| rlc::value(&self.chain_id.to_le_bytes(), randomness)), ], @@ -250,8 +247,8 @@ pub fn block_convert( .enumerate() .map(|(idx, tx)| tx_convert(tx, idx + 1)) .collect(), - end_block_not_last: step_convert(&block.block_steps.end_block_not_last), - end_block_last: step_convert(&block.block_steps.end_block_last), + end_block_not_last: block.block_steps.end_block_not_last.clone(), + end_block_last: block.block_steps.end_block_last.clone(), bytecodes: code_db .0 .values() diff --git a/zkevm-circuits/src/witness/bytecode.rs b/zkevm-circuits/src/witness/bytecode.rs index 75345c45b2..4e945dc380 100644 --- a/zkevm-circuits/src/witness/bytecode.rs +++ b/zkevm-circuits/src/witness/bytecode.rs @@ -35,8 +35,8 @@ impl Bytecode { rows.push([ hash, Value::known(F::from(BytecodeFieldTag::Header as u64)), - Value::known(F::zero()), - Value::known(F::zero()), + Value::known(F::ZERO), + Value::known(F::ZERO), Value::known(F::from(self.bytes.len() as u64)), ]); diff --git a/zkevm-circuits/src/witness/call.rs b/zkevm-circuits/src/witness/call.rs deleted file mode 100644 index c3161721b0..0000000000 --- a/zkevm-circuits/src/witness/call.rs +++ /dev/null @@ -1,42 +0,0 @@ -use eth_types::{Address, Word}; - -/// Call in transactions. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct Call { - /// The unique identifier of call in the whole proof, using the - /// `rw_counter` at the call step. - pub id: usize, - /// Indicate if the call is the root call - pub is_root: bool, - /// Indicate if the call is a create call - pub is_create: bool, - /// The identifier of current executed bytecode - pub code_hash: Word, - /// The `rw_counter` at the end of reversion of a call if it has - /// `is_persistent == false` - pub rw_counter_end_of_reversion: usize, - /// The call index of caller - pub caller_id: usize, - /// The depth in the call stack - pub depth: usize, - /// The caller address - pub caller_address: Address, - /// The callee address - pub callee_address: Address, - /// The call data offset in the memory - pub call_data_offset: u64, - /// The length of call data - pub call_data_length: u64, - /// The return data offset in the memory - pub return_data_offset: u64, - /// The length of return data - pub return_data_length: u64, - /// The ether amount of the transaction - pub value: Word, - /// Indicate if this call halts successfully. - pub is_success: bool, - /// Indicate if this call and all its caller have `is_success == true` - pub is_persistent: bool, - /// Indicate if it's a static call - pub is_static: bool, -} diff --git a/zkevm-circuits/src/witness/mpt.rs b/zkevm-circuits/src/witness/mpt.rs index c6f8134e47..922a1f1e76 100644 --- a/zkevm-circuits/src/witness/mpt.rs +++ b/zkevm-circuits/src/witness/mpt.rs @@ -196,7 +196,7 @@ impl Key { } fn storage_key(&self, randomness: F) -> F { match self { - Self::Account { .. } => F::zero(), + Self::Account { .. } => F::ZERO, Self::AccountStorage { storage_key, .. } => { rlc::value(&storage_key.to_le_bytes(), randomness) } diff --git a/zkevm-circuits/src/witness/rw.rs b/zkevm-circuits/src/witness/rw.rs index 18f98e0704..a01bf1e7ea 100644 --- a/zkevm-circuits/src/witness/rw.rs +++ b/zkevm-circuits/src/witness/rw.rs @@ -1,14 +1,17 @@ #![allow(missing_docs)] use std::collections::HashMap; -use bus_mapping::operation::{self, AccountField, CallContextField, TxLogField, TxReceiptField}; +use bus_mapping::{ + exec_trace::OperationRef, + operation::{self, AccountField, CallContextField, Target, TxLogField, TxReceiptField}, +}; use eth_types::{Address, Field, ToAddress, ToLittleEndian, ToScalar, Word, U256}; use halo2_proofs::{circuit::Value, halo2curves::bn256::Fr}; use itertools::Itertools; use crate::{ evm_circuit::util::rlc, - table::{AccountFieldTag, CallContextFieldTag, RwTableTag, TxLogFieldTag, TxReceiptFieldTag}, + table::{AccountFieldTag, CallContextFieldTag, TxLogFieldTag, TxReceiptFieldTag}, util::build_tx_log_address, }; @@ -16,22 +19,31 @@ use super::MptUpdates; /// Rw constainer for a witness block #[derive(Debug, Default, Clone)] -pub struct RwMap(pub HashMap>); +pub struct RwMap(pub HashMap>); + +impl std::ops::Index<(Target, usize)> for RwMap { + type Output = Rw; + + fn index(&self, (tag, idx): (Target, usize)) -> &Self::Output { + &self.0.get(&tag).unwrap()[idx] + } +} -impl std::ops::Index<(RwTableTag, usize)> for RwMap { +impl std::ops::Index for RwMap { type Output = Rw; - fn index(&self, (tag, idx): (RwTableTag, usize)) -> &Self::Output { + fn index(&self, OperationRef(tag, idx): OperationRef) -> &Self::Output { &self.0.get(&tag).unwrap()[idx] } } + impl RwMap { /// Check rw_counter is continuous and starting from 1 pub fn check_rw_counter_sanity(&self) { for (idx, rw_counter) in self .0 .iter() - .filter(|(tag, _rs)| !matches!(tag, RwTableTag::Start)) + .filter(|(tag, _rs)| !matches!(tag, Target::Start)) .flat_map(|(_tag, rs)| rs) .map(|r| r.rw_counter()) .sorted() @@ -281,7 +293,7 @@ impl RwRow { values .iter() .rev() - .fold(F::zero(), |acc, value| acc * randomness + value) + .fold(F::ZERO, |acc, value| acc * randomness + value) } pub(crate) fn rlc_value(&self, randomness: Value) -> Value { @@ -392,14 +404,14 @@ impl Rw { tag: F::from(self.tag() as u64), id: F::from(self.id().unwrap_or_default() as u64), address: self.address().unwrap_or_default().to_scalar().unwrap(), - field_tag: F::from(self.field_tag().unwrap_or_default() as u64), + field_tag: F::from(self.field_tag().unwrap_or_default()), storage_key: rlc::value( &self.storage_key().unwrap_or_default().to_le_bytes(), randomness, ), value: self.value_assignment(randomness), value_prev: self.value_prev_assignment(randomness).unwrap_or_default(), - aux1: F::zero(), // only used for AccountStorage::tx_id, which moved to key1. + aux1: F::ZERO, // only used for AccountStorage::tx_id, which moved to key1. aux2: self .committed_value_assignment(randomness) .unwrap_or_default(), @@ -413,7 +425,7 @@ impl Rw { tag: Value::known(F::from(self.tag() as u64)), id: Value::known(F::from(self.id().unwrap_or_default() as u64)), address: Value::known(self.address().unwrap_or_default().to_scalar().unwrap()), - field_tag: Value::known(F::from(self.field_tag().unwrap_or_default() as u64)), + field_tag: Value::known(F::from(self.field_tag().unwrap_or_default())), storage_key: randomness.map(|randomness| { rlc::value( &self.storage_key().unwrap_or_default().to_le_bytes(), @@ -423,8 +435,8 @@ impl Rw { value: randomness.map(|randomness| self.value_assignment(randomness)), value_prev: randomness .map(|randomness| self.value_prev_assignment(randomness).unwrap_or_default()), - aux1: Value::known(F::zero()), /* only used for AccountStorage::tx_id, which moved to - * key1. */ + aux1: Value::known(F::ZERO), /* only used for AccountStorage::tx_id, which moved to + * key1. */ aux2: randomness.map(|randomness| { self.committed_value_assignment(randomness) .unwrap_or_default() @@ -464,19 +476,19 @@ impl Rw { } } - pub(crate) fn tag(&self) -> RwTableTag { + pub(crate) fn tag(&self) -> Target { match self { - Self::Start { .. } => RwTableTag::Start, - Self::Memory { .. } => RwTableTag::Memory, - Self::Stack { .. } => RwTableTag::Stack, - Self::AccountStorage { .. } => RwTableTag::AccountStorage, - Self::TxAccessListAccount { .. } => RwTableTag::TxAccessListAccount, - Self::TxAccessListAccountStorage { .. } => RwTableTag::TxAccessListAccountStorage, - Self::TxRefund { .. } => RwTableTag::TxRefund, - Self::Account { .. } => RwTableTag::Account, - Self::CallContext { .. } => RwTableTag::CallContext, - Self::TxLog { .. } => RwTableTag::TxLog, - Self::TxReceipt { .. } => RwTableTag::TxReceipt, + Self::Start { .. } => Target::Start, + Self::Memory { .. } => Target::Memory, + Self::Stack { .. } => Target::Stack, + Self::AccountStorage { .. } => Target::Storage, + Self::TxAccessListAccount { .. } => Target::TxAccessListAccount, + Self::TxAccessListAccountStorage { .. } => Target::TxAccessListAccountStorage, + Self::TxRefund { .. } => Target::TxRefund, + Self::Account { .. } => Target::Account, + Self::CallContext { .. } => Target::CallContext, + Self::TxLog { .. } => Target::TxLog, + Self::TxReceipt { .. } => Target::TxReceipt, } } @@ -563,7 +575,7 @@ impl Rw { pub(crate) fn value_assignment(&self, randomness: F) -> F { match self { - Self::Start { .. } => F::zero(), + Self::Start { .. } => F::ZERO, Self::CallContext { field_tag, value, .. } => { @@ -648,7 +660,7 @@ impl From<&operation::OperationContainer> for RwMap { let mut rws = HashMap::default(); rws.insert( - RwTableTag::Start, + Target::Start, container .start .iter() @@ -658,7 +670,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::TxAccessListAccount, + Target::TxAccessListAccount, container .tx_access_list_account .iter() @@ -673,7 +685,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::TxAccessListAccountStorage, + Target::TxAccessListAccountStorage, container .tx_access_list_account_storage .iter() @@ -689,7 +701,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::TxRefund, + Target::TxRefund, container .tx_refund .iter() @@ -703,7 +715,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::Account, + Target::Account, container .account .iter() @@ -722,7 +734,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::AccountStorage, + Target::Storage, container .storage .iter() @@ -739,7 +751,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::CallContext, + Target::CallContext, container .call_context .iter() @@ -787,7 +799,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::Stack, + Target::Stack, container .stack .iter() @@ -801,7 +813,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::Memory, + Target::Memory, container .memory .iter() @@ -817,7 +829,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::TxLog, + Target::TxLog, container .tx_log .iter() @@ -837,7 +849,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::TxReceipt, + Target::TxReceipt, container .tx_receipt .iter() diff --git a/zkevm-circuits/src/witness/step.rs b/zkevm-circuits/src/witness/step.rs deleted file mode 100644 index bcc8e3bd37..0000000000 --- a/zkevm-circuits/src/witness/step.rs +++ /dev/null @@ -1,243 +0,0 @@ -use bus_mapping::{ - circuit_input_builder, - error::{ExecError, OogError}, - evm::OpcodeId, - operation, -}; -use eth_types::evm_unimplemented; - -use crate::{ - evm_circuit::{ - param::{N_BYTES_WORD, STACK_CAPACITY}, - step::ExecutionState, - }, - table::RwTableTag, -}; - -/// Step executed in a transaction -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct ExecStep { - /// The index in the Transaction calls - pub call_index: usize, - /// The indices in the RW trace incurred in this step - pub rw_indices: Vec<(RwTableTag, usize)>, - /// Number of rw operations performed via a copy event in this step. - pub copy_rw_counter_delta: u64, - /// The execution state for the step - pub execution_state: ExecutionState, - /// The Read/Write counter before the step - pub rw_counter: usize, - /// The program counter - pub program_counter: u64, - /// The stack pointer - pub stack_pointer: usize, - /// The amount of gas left - pub gas_left: u64, - /// The gas cost in this step - pub gas_cost: u64, - /// The memory size in bytes - pub memory_size: u64, - /// The counter for reversible writes at the beginning of the step - pub reversible_write_counter: usize, - /// The number of reversible writes from this step - pub reversible_write_counter_delta: usize, - /// The counter for log index within tx - pub log_id: usize, - /// The opcode corresponds to the step - pub opcode: Option, -} - -impl ExecStep { - /// The memory size in word **before** this step - pub fn memory_word_size(&self) -> u64 { - // EVM always pads the memory size to word size - // https://github.com/ethereum/go-ethereum/blob/master/core/vm/interpreter.go#L212-L216 - // Thus, the memory size must be a multiple of 32 bytes. - assert_eq!(self.memory_size % N_BYTES_WORD as u64, 0); - self.memory_size / N_BYTES_WORD as u64 - } -} - -impl From<&ExecError> for ExecutionState { - fn from(error: &ExecError) -> Self { - match error { - ExecError::InvalidOpcode => ExecutionState::ErrorInvalidOpcode, - ExecError::StackOverflow | ExecError::StackUnderflow => ExecutionState::ErrorStack, - ExecError::WriteProtection => ExecutionState::ErrorWriteProtection, - ExecError::Depth => ExecutionState::ErrorDepth, - ExecError::InsufficientBalance => ExecutionState::ErrorInsufficientBalance, - ExecError::ContractAddressCollision => ExecutionState::ErrorContractAddressCollision, - ExecError::InvalidCreationCode => ExecutionState::ErrorInvalidCreationCode, - ExecError::InvalidJump => ExecutionState::ErrorInvalidJump, - ExecError::ReturnDataOutOfBounds => ExecutionState::ErrorReturnDataOutOfBound, - ExecError::CodeStoreOutOfGas => ExecutionState::ErrorOutOfGasCodeStore, - ExecError::MaxCodeSizeExceeded => ExecutionState::ErrorMaxCodeSizeExceeded, - ExecError::OutOfGas(oog_error) => match oog_error { - OogError::Constant => ExecutionState::ErrorOutOfGasConstant, - OogError::StaticMemoryExpansion => { - ExecutionState::ErrorOutOfGasStaticMemoryExpansion - } - OogError::DynamicMemoryExpansion => { - ExecutionState::ErrorOutOfGasDynamicMemoryExpansion - } - OogError::MemoryCopy => ExecutionState::ErrorOutOfGasMemoryCopy, - OogError::AccountAccess => ExecutionState::ErrorOutOfGasAccountAccess, - OogError::CodeStore => ExecutionState::ErrorOutOfGasCodeStore, - OogError::Log => ExecutionState::ErrorOutOfGasLOG, - OogError::Exp => ExecutionState::ErrorOutOfGasEXP, - OogError::Sha3 => ExecutionState::ErrorOutOfGasSHA3, - OogError::ExtCodeCopy => ExecutionState::ErrorOutOfGasEXTCODECOPY, - OogError::Call => ExecutionState::ErrorOutOfGasCall, - OogError::SloadSstore => ExecutionState::ErrorOutOfGasSloadSstore, - OogError::Create2 => ExecutionState::ErrorOutOfGasCREATE2, - OogError::SelfDestruct => ExecutionState::ErrorOutOfGasSELFDESTRUCT, - }, - } - } -} - -impl From<&circuit_input_builder::ExecStep> for ExecutionState { - fn from(step: &circuit_input_builder::ExecStep) -> Self { - if let Some(error) = step.error.as_ref() { - return error.into(); - } - match step.exec_state { - circuit_input_builder::ExecState::Op(op) => { - if op.is_dup() { - return ExecutionState::DUP; - } - if op.is_push() { - return ExecutionState::PUSH; - } - if op.is_swap() { - return ExecutionState::SWAP; - } - if op.is_log() { - return ExecutionState::LOG; - } - - macro_rules! dummy { - ($name:expr) => {{ - evm_unimplemented!("{:?} is implemented with DummyGadget", $name); - $name - }}; - } - - match op { - OpcodeId::ADD | OpcodeId::SUB => ExecutionState::ADD_SUB, - OpcodeId::ADDMOD => ExecutionState::ADDMOD, - OpcodeId::ADDRESS => ExecutionState::ADDRESS, - OpcodeId::BALANCE => ExecutionState::BALANCE, - OpcodeId::MUL | OpcodeId::DIV | OpcodeId::MOD => ExecutionState::MUL_DIV_MOD, - OpcodeId::MULMOD => ExecutionState::MULMOD, - OpcodeId::SDIV | OpcodeId::SMOD => ExecutionState::SDIV_SMOD, - OpcodeId::EQ | OpcodeId::LT | OpcodeId::GT => ExecutionState::CMP, - OpcodeId::SLT | OpcodeId::SGT => ExecutionState::SCMP, - OpcodeId::SIGNEXTEND => ExecutionState::SIGNEXTEND, - OpcodeId::STOP => ExecutionState::STOP, - OpcodeId::AND => ExecutionState::BITWISE, - OpcodeId::XOR => ExecutionState::BITWISE, - OpcodeId::OR => ExecutionState::BITWISE, - OpcodeId::NOT => ExecutionState::NOT, - OpcodeId::EXP => ExecutionState::EXP, - OpcodeId::POP => ExecutionState::POP, - OpcodeId::PUSH32 => ExecutionState::PUSH, - OpcodeId::BYTE => ExecutionState::BYTE, - OpcodeId::MLOAD => ExecutionState::MEMORY, - OpcodeId::MSTORE => ExecutionState::MEMORY, - OpcodeId::MSTORE8 => ExecutionState::MEMORY, - OpcodeId::JUMPDEST => ExecutionState::JUMPDEST, - OpcodeId::JUMP => ExecutionState::JUMP, - OpcodeId::JUMPI => ExecutionState::JUMPI, - OpcodeId::GASPRICE => ExecutionState::GASPRICE, - OpcodeId::PC => ExecutionState::PC, - OpcodeId::MSIZE => ExecutionState::MSIZE, - OpcodeId::CALLER => ExecutionState::CALLER, - OpcodeId::CALLVALUE => ExecutionState::CALLVALUE, - OpcodeId::EXTCODEHASH => ExecutionState::EXTCODEHASH, - OpcodeId::EXTCODESIZE => ExecutionState::EXTCODESIZE, - OpcodeId::BLOCKHASH => ExecutionState::BLOCKHASH, - OpcodeId::TIMESTAMP | OpcodeId::NUMBER | OpcodeId::GASLIMIT => { - ExecutionState::BLOCKCTXU64 - } - OpcodeId::COINBASE => ExecutionState::BLOCKCTXU160, - OpcodeId::DIFFICULTY | OpcodeId::BASEFEE => ExecutionState::BLOCKCTXU256, - OpcodeId::GAS => ExecutionState::GAS, - OpcodeId::SAR => ExecutionState::SAR, - OpcodeId::SELFBALANCE => ExecutionState::SELFBALANCE, - OpcodeId::SHA3 => ExecutionState::SHA3, - OpcodeId::SHL | OpcodeId::SHR => ExecutionState::SHL_SHR, - OpcodeId::SLOAD => ExecutionState::SLOAD, - OpcodeId::SSTORE => ExecutionState::SSTORE, - OpcodeId::CALLDATASIZE => ExecutionState::CALLDATASIZE, - OpcodeId::CALLDATACOPY => ExecutionState::CALLDATACOPY, - OpcodeId::CHAINID => ExecutionState::CHAINID, - OpcodeId::ISZERO => ExecutionState::ISZERO, - OpcodeId::CALL - | OpcodeId::CALLCODE - | OpcodeId::DELEGATECALL - | OpcodeId::STATICCALL => ExecutionState::CALL_OP, - OpcodeId::ORIGIN => ExecutionState::ORIGIN, - OpcodeId::CODECOPY => ExecutionState::CODECOPY, - OpcodeId::CALLDATALOAD => ExecutionState::CALLDATALOAD, - OpcodeId::CODESIZE => ExecutionState::CODESIZE, - OpcodeId::RETURN | OpcodeId::REVERT => ExecutionState::RETURN_REVERT, - OpcodeId::RETURNDATASIZE => ExecutionState::RETURNDATASIZE, - OpcodeId::RETURNDATACOPY => ExecutionState::RETURNDATACOPY, - // dummy ops - OpcodeId::EXTCODECOPY => dummy!(ExecutionState::EXTCODECOPY), - OpcodeId::CREATE => dummy!(ExecutionState::CREATE), - OpcodeId::CREATE2 => dummy!(ExecutionState::CREATE2), - OpcodeId::SELFDESTRUCT => dummy!(ExecutionState::SELFDESTRUCT), - _ => unimplemented!("unimplemented opcode {:?}", op), - } - } - circuit_input_builder::ExecState::BeginTx => ExecutionState::BeginTx, - circuit_input_builder::ExecState::EndTx => ExecutionState::EndTx, - circuit_input_builder::ExecState::EndBlock => ExecutionState::EndBlock, - } - } -} - -pub(super) fn step_convert(step: &circuit_input_builder::ExecStep) -> ExecStep { - ExecStep { - call_index: step.call_index, - rw_indices: step - .bus_mapping_instance - .iter() - .map(|x| { - let tag = match x.target() { - operation::Target::Memory => RwTableTag::Memory, - operation::Target::Stack => RwTableTag::Stack, - operation::Target::Storage => RwTableTag::AccountStorage, - operation::Target::TxAccessListAccount => RwTableTag::TxAccessListAccount, - operation::Target::TxAccessListAccountStorage => { - RwTableTag::TxAccessListAccountStorage - } - operation::Target::TxRefund => RwTableTag::TxRefund, - operation::Target::Account => RwTableTag::Account, - operation::Target::CallContext => RwTableTag::CallContext, - operation::Target::TxReceipt => RwTableTag::TxReceipt, - operation::Target::TxLog => RwTableTag::TxLog, - operation::Target::Start => RwTableTag::Start, - }; - (tag, x.as_usize()) - }) - .collect(), - copy_rw_counter_delta: step.copy_rw_counter_delta, - execution_state: ExecutionState::from(step), - rw_counter: usize::from(step.rwc), - program_counter: usize::from(step.pc) as u64, - stack_pointer: STACK_CAPACITY - step.stack_size, - gas_left: step.gas_left.0, - gas_cost: step.gas_cost.as_u64(), - opcode: match step.exec_state { - circuit_input_builder::ExecState::Op(op) => Some(op), - _ => None, - }, - memory_size: step.memory_size as u64, - reversible_write_counter: step.reversible_write_counter, - reversible_write_counter_delta: step.reversible_write_counter_delta, - log_id: step.log_id, - } -} diff --git a/zkevm-circuits/src/witness/tx.rs b/zkevm-circuits/src/witness/tx.rs index 60f514e123..38be201d3f 100644 --- a/zkevm-circuits/src/witness/tx.rs +++ b/zkevm-circuits/src/witness/tx.rs @@ -1,13 +1,13 @@ use bus_mapping::circuit_input_builder; -use eth_types::{Address, Field, ToLittleEndian, ToScalar, ToWord, Word}; +use eth_types::{Address, Field, ToLittleEndian, ToScalar, Word}; use halo2_proofs::circuit::Value; use crate::{evm_circuit::util::rlc, table::TxContextFieldTag, util::Challenges}; -use super::{step::step_convert, Call, ExecStep}; +use super::{Call, ExecStep}; /// Transaction in a witness block -#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[derive(Debug, Default, Clone)] pub struct Transaction { /// The transaction identifier in the block pub id: usize, @@ -48,19 +48,19 @@ impl Transaction { [ Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::Nonce as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), Value::known(F::from(self.nonce)), ], [ Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::Gas as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), Value::known(F::from(self.gas)), ], [ Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::GasPrice as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), challenges .evm_word() .map(|challenge| rlc::value(&self.gas_price.to_le_bytes(), challenge)), @@ -68,25 +68,25 @@ impl Transaction { [ Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::CallerAddress as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), Value::known(self.caller_address.to_scalar().unwrap()), ], [ Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::CalleeAddress as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), Value::known(self.callee_address.to_scalar().unwrap()), ], [ Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::IsCreate as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), Value::known(F::from(self.is_create as u64)), ], [ Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::Value as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), challenges .evm_word() .map(|challenge| rlc::value(&self.value.to_le_bytes(), challenge)), @@ -94,13 +94,13 @@ impl Transaction { [ Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::CallDataLength as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), Value::known(F::from(self.call_data_length as u64)), ], [ Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::CallDataGasCost as u64)), - Value::known(F::zero()), + Value::known(F::ZERO), Value::known(F::from(self.call_data_gas_cost)), ], ]; @@ -124,42 +124,17 @@ impl Transaction { pub(super) fn tx_convert(tx: &circuit_input_builder::Transaction, id: usize) -> Transaction { Transaction { id, - nonce: tx.nonce, - gas: tx.gas, - gas_price: tx.gas_price, - caller_address: tx.from, - callee_address: tx.to, + nonce: tx.tx.nonce.as_u64(), + gas: tx.gas(), + gas_price: tx.tx.gas_price, + caller_address: tx.tx.from, + callee_address: tx.tx.to_or_contract_addr(), is_create: tx.is_create(), - value: tx.value, - call_data: tx.input.clone(), - call_data_length: tx.input.len(), - call_data_gas_cost: tx - .input - .iter() - .fold(0, |acc, byte| acc + if *byte == 0 { 4 } else { 16 }), - calls: tx - .calls() - .iter() - .map(|call| Call { - id: call.call_id, - is_root: call.is_root, - is_create: call.is_create(), - code_hash: call.code_hash.to_word(), - rw_counter_end_of_reversion: call.rw_counter_end_of_reversion, - caller_id: call.caller_id, - depth: call.depth, - caller_address: call.caller_address, - callee_address: call.address, - call_data_offset: call.call_data_offset, - call_data_length: call.call_data_length, - return_data_offset: call.return_data_offset, - return_data_length: call.return_data_length, - value: call.value, - is_success: call.is_success, - is_persistent: call.is_persistent, - is_static: call.is_static, - }) - .collect(), - steps: tx.steps().iter().map(step_convert).collect(), + value: tx.tx.value, + call_data: tx.tx.call_data.to_vec(), + call_data_length: tx.tx.call_data.len(), + call_data_gas_cost: tx.tx.call_data_gas_cost(), + calls: tx.calls().to_vec(), + steps: tx.steps().to_vec(), } } diff --git a/zkevm-circuits/tests/prover_error.rs b/zkevm-circuits/tests/prover_error.rs index d55ca439d0..ef70f98246 100644 --- a/zkevm-circuits/tests/prover_error.rs +++ b/zkevm-circuits/tests/prover_error.rs @@ -1,8 +1,8 @@ // This file is intended to be used with fixtures generated from zkevm-chain. // Copy the `errors/` directory into the zkevm-circuits git root // as `block` and run via `cargo test -p zkevm-circuits --features test -// prover_error -- --nocapture --ignored`. Change any constant variables like -// `MAX_TXS` to suit your needs. +// prover_error -- --nocapture --ignored`. Change any circuit parameters like +// `max_txs` to suit your needs. use bus_mapping::{circuit_input_builder::CircuitsParams, mock::BlockData}; use env_logger::Env; use eth_types::{ @@ -36,14 +36,12 @@ fn load_json(path: &str) -> Value { #[ignore] fn prover_error() { // change any of these values to your needs - const MAX_TXS: usize = 1; - const MAX_CALLDATA: usize = 256; const MOCK_RANDOMNESS: u64 = 0x100; let k = 19; let chain_id = Word::from(99); let circuit_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, + max_txs: 1, + max_calldata: 256, max_rws: 16388, ..Default::default() }; @@ -104,8 +102,7 @@ fn prover_error() { block.randomness = Fr::from(MOCK_RANDOMNESS); block }; - let circuit = - SuperCircuit::<_, MAX_TXS, MAX_CALLDATA, MOCK_RANDOMNESS>::new_from_block(&block_witness); + let circuit = SuperCircuit::new_from_block(&block_witness); let res = MockProver::run(k, &circuit, circuit.instance()) .expect("MockProver::run") .verify_par();